242 lines
12 KiB
Clojure
242 lines
12 KiB
Clojure
(ns auto-ap.views.components.invoice-table
|
|
(:require [auto-ap.events :as events]
|
|
[auto-ap.routes :as routes]
|
|
[auto-ap.subs :as subs]
|
|
[auto-ap.views.components.buttons :as buttons]
|
|
[auto-ap.views.components.dropdown
|
|
:refer
|
|
[drop-down drop-down-contents]]
|
|
[auto-ap.views.components.grid :as grid]
|
|
[auto-ap.views.pages.invoices.form :as form]
|
|
[auto-ap.views.pages.invoices.common :refer [invoice-read]]
|
|
[auto-ap.views.utils :refer [date->str dispatch-event nf days-until]]
|
|
[bidi.bidi :as bidi]
|
|
[cemerick.url :as url]
|
|
[cljs-time.core :as t]
|
|
[clojure.string :as str]
|
|
[goog.string :as gstring]
|
|
[re-frame.core :as re-frame]
|
|
[auto-ap.views.components.expense-accounts-dialog :as expense-accounts-dialog]
|
|
[auto-ap.views.pages.data-page :as data-page]))
|
|
|
|
(defn query [params]
|
|
{:venia/queries [[:invoice_page
|
|
{
|
|
:start (:start params 0)
|
|
:sort (:sort params)
|
|
:per-page (:per-page params)
|
|
|
|
:vendor-id (:id (:vendor params))
|
|
:date-range (:date-range params)
|
|
:due-range (:due-range params)
|
|
:amount-gte (:amount-gte (:amount-range params))
|
|
:amount-lte (:amount-lte (:amount-range params))
|
|
:location (:location params)
|
|
:unresolved (:unresolved params)
|
|
:invoice-number-like (:invoice-number-like params)
|
|
:client-id (:id @(re-frame/subscribe [::subs/client]))
|
|
:import-status (:import-status params)
|
|
:status (condp = @(re-frame/subscribe [::subs/active-page])
|
|
:invoices nil
|
|
:import-invoices nil
|
|
:unpaid-invoices :unpaid
|
|
:paid-invoices :paid
|
|
:voided-invoices :voided)}
|
|
[[:invoices [:id :total :outstanding-balance :invoice-number :date :due :status :client-identifier :scheduled-payment
|
|
[:vendor [:name :id]]
|
|
[:expense_accounts [:amount :id :location
|
|
[:account [:id ]]]]
|
|
[:client [:name :id :locations]]
|
|
[:payments [:amount :id [:payment [:id :status :amount :s3_url :check_number
|
|
[:transaction [:post_date]]]]]]]]
|
|
:outstanding
|
|
:total
|
|
:start
|
|
:end]]]})
|
|
|
|
|
|
|
|
(re-frame/reg-event-fx
|
|
::void-invoice
|
|
(fn [{:keys [db]} [_ {id :id}]]
|
|
{:graphql
|
|
{:token (-> db :user)
|
|
:owns-state {:multi ::void
|
|
:which id}
|
|
:query-obj {:venia/operation {:operation/type :mutation
|
|
:operation/name "VoidInvoice"}
|
|
|
|
:venia/queries [{:query/data [:void-invoice
|
|
{:invoice-id id}
|
|
invoice-read]}]}
|
|
:on-success (fn [result]
|
|
[::invoice-updated (assoc (:void-invoice result)
|
|
:class "live-removed")])}}))
|
|
|
|
(re-frame/reg-event-fx
|
|
::unvoid-invoice
|
|
(fn [{:keys [db]} [_ {id :id}]]
|
|
{:graphql
|
|
{:token (-> db :user)
|
|
:owns-state {:multi ::unvoid
|
|
:which id}
|
|
:query-obj {:venia/operation {:operation/type :mutation
|
|
:operation/name "UnvoidInvoice"}
|
|
|
|
:venia/queries [{:query/data [:unvoid-invoice
|
|
{:invoice-id id}
|
|
invoice-read]}]}
|
|
:on-success (fn [result]
|
|
[::invoice-updated (:unvoid-invoice result)])}}))
|
|
|
|
(re-frame/reg-event-fx
|
|
::invoice-updated
|
|
(fn [{:keys [db]} [_ invoice]]
|
|
{:db db}))
|
|
|
|
(defn row [{:keys [invoice check-boxes selected-client overrides checkable? expense-event actions]}]
|
|
(let [{:keys [client payments expense-accounts invoice-number date due total outstanding-balance id vendor] :as i} invoice
|
|
accounts-by-id @(re-frame/subscribe [::subs/accounts-by-id client])
|
|
account->name #(:name (accounts-by-id (:id %)))]
|
|
[grid/row {:class (:class i) :id id :checkable? checkable? :entity invoice}
|
|
(when-not selected-client
|
|
[grid/cell {}
|
|
(if-let [client-override (:client overrides)]
|
|
(client-override i)
|
|
(:name client))])
|
|
[grid/cell {} (:name vendor)]
|
|
[grid/cell {} invoice-number]
|
|
[grid/cell {} (date->str date) ]
|
|
[grid/cell {}
|
|
(when due
|
|
(let [due-in (days-until due)]
|
|
(if (> due-in 0)
|
|
[:span.has-text-success due-in " days"]
|
|
[:span.has-text-danger due-in " days"])
|
|
))]
|
|
[grid/cell {} (str/join ", " (set (map :location expense-accounts)))]
|
|
|
|
[grid/cell {:class "has-text-right"} (nf total )]
|
|
[grid/cell {:class "has-text-right"} (nf outstanding-balance )]
|
|
[grid/button-cell {}
|
|
[:div.buttons
|
|
(when (seq expense-accounts)
|
|
[drop-down {:id [::expense-accounts id ]
|
|
:header [buttons/sl-icon {:class "badge"
|
|
:event [::events/toggle-menu [::expense-accounts id]]
|
|
:data-badge (str (clojure.core/count expense-accounts))
|
|
:icon "icon-navigation-menu"}]}
|
|
[drop-down-contents
|
|
[:div
|
|
(for [e expense-accounts]
|
|
^{:key (:id e)}
|
|
[:span.dropdown-item (account->name (:account e)) " " (gstring/format "$%.2f" (:amount e) ) ])
|
|
|
|
(when (get actions :expense-accounts)
|
|
[:<>
|
|
|
|
[:hr.dropdown-divider]
|
|
|
|
[:a.dropdown-item.is-primary {:on-click (dispatch-event [::expense-accounts-dialog/show i])} "Change"]])]]])
|
|
[:span {:style {:margin-left "1em"}}]
|
|
(when (seq payments)
|
|
[:<>
|
|
[drop-down {:id [::payments id]
|
|
:header [buttons/sl-icon {:class "badge"
|
|
:event [::events/toggle-menu [::payments id]]
|
|
:data-badge (str (clojure.core/count payments))
|
|
:icon "icon-accounting-bill"}]}
|
|
[:div
|
|
(for [payment payments]
|
|
(if (:check-number (:payment payment))
|
|
^{:key (:id payment)}
|
|
[:a.dropdown-item {:href (str (bidi/path-for routes/routes :payments )
|
|
"?"
|
|
(url/map->query {:check-number-like (:check-number (:payment payment))}))
|
|
:target "_new"}
|
|
[:i.fa.fa-money-check]
|
|
[:span.icon [:i.fa.fa-money]]
|
|
(str " " (:check-number (:payment payment)) " (" (gstring/format "$%.2f" (:amount payment) ) ")")]
|
|
|
|
^{:key (:id payment)}
|
|
[:span.dropdown-item [:span.icon {:class [(when (= :cleared (:status (:payment payment)))
|
|
"has-text-success")]}
|
|
[:i.fa.fa-money]] (str " " (:check-number (:payment payment)) " (" (gstring/format "$%.2f" (:amount payment) ) ") "
|
|
(when (= :cleared (:status (:payment payment)))
|
|
(str " - " (:post-date (:transaction (:payment payment))))))]))]]
|
|
[:span {:style {:margin-right "1em"}}]])
|
|
(when (and (get actions :edit)
|
|
(not= ":voided" (:status i)))
|
|
[buttons/fa-icon {:icon "fa-pencil"
|
|
:event [::form/editing i]}])
|
|
(when (and (get actions :void)
|
|
(= (:outstanding-balance i) (:total i)) (not= ":voided" (:status i)))
|
|
[buttons/sl-icon {:icon "icon-bin-2"
|
|
:event [::void-invoice i]}])
|
|
(when (and (get actions :void)
|
|
(= ":voided" (:status i)))
|
|
[buttons/fa-icon {:icon "fa-undo"
|
|
:event [::unvoid-invoice i]}])]]]))
|
|
|
|
(defn invoice-table [{:keys [id check-boxes overrides actions data-page checkable-fn]}]
|
|
(let [selected-client @(re-frame/subscribe [::subs/client])
|
|
{:keys [data status table-params]} @(re-frame/subscribe [::data-page/page data-page])
|
|
|
|
selected-client @(re-frame/subscribe [::subs/client])
|
|
is-loading? (= :loading (:state status))
|
|
is-sorted-by-vendor? (and (= "vendor" (:sort-key (first (:sort table-params))))
|
|
(not is-loading?)
|
|
(or (apply <= (map (comp :name :vendor) (:data data)))
|
|
(apply >= (map (comp :name :vendor) (:data data)))))
|
|
|
|
[invoice-groups] (if is-sorted-by-vendor?
|
|
(reduce
|
|
(fn [[acc last-vendor] invoice]
|
|
(if (not= (:id (:vendor invoice))
|
|
last-vendor)
|
|
[(update-in acc [(clojure.core/count acc)] #(conj (or % []) invoice))
|
|
(:id (:vendor invoice))]
|
|
[(update-in acc [(dec (clojure.core/count acc))] #(conj (or % []) invoice))
|
|
(:id (:vendor invoice))]))
|
|
[[] nil]
|
|
(:data data))
|
|
[[(:data data)]])]
|
|
[grid/grid {:data-page data-page
|
|
:check-boxes? check-boxes
|
|
:column-count (if selected-client 8 9)}
|
|
[grid/controls data
|
|
[:div.level-item
|
|
"Outstanding " (nf (:outstanding data))]]
|
|
(for [invoices invoice-groups]
|
|
^{:key (or (:id (first invoices)) "init")}
|
|
[grid/table {:fullwidth true}
|
|
[grid/header {}
|
|
[grid/row {}
|
|
(when-not selected-client
|
|
[grid/sortable-header-cell {:sort-key "client" :sort-name "Client"} "Client"])
|
|
[grid/sortable-header-cell {:sort-key "vendor" :sort-name "Vendor"}
|
|
(if is-sorted-by-vendor?
|
|
(:name (:vendor (first invoices)))
|
|
"Vendor")]
|
|
|
|
[grid/sortable-header-cell {:sort-key "invoice-number" :sort-name "Invoice Number"} "Invoice #"]
|
|
[grid/sortable-header-cell {:sort-key "date" :sort-name "Date" :style {:width "8em"}} "Date"]
|
|
[grid/sortable-header-cell {:sort-key "due" :sort-name "Due" :style {:width "8em"}} "Due"]
|
|
[grid/sortable-header-cell {:sort-key "location" :sort-name "Location" :style {:width "5em"}} "Loc"]
|
|
[grid/sortable-header-cell {:sort-key "total" :sort-name "Total" :style {:width "8em"} :class "has-text-right"} "Total"]
|
|
|
|
[grid/sortable-header-cell {:sort-key "outstanding-balance" :sort-name "Outstanding" :style {:width "10em"} :class "has-text-right"} "Outstanding"]
|
|
[grid/header-cell {:style {:width "14rem" }}]
|
|
]]
|
|
|
|
[grid/body
|
|
(for [{:keys [client payments expense-accounts invoice-number date due total outstanding-balance id vendor] :as i} invoices]
|
|
^{:key id}
|
|
[row {:invoice i
|
|
:selected-client selected-client
|
|
:checkable? (if checkable-fn
|
|
(checkable-fn i)
|
|
true)
|
|
:actions actions
|
|
:overrides overrides}])]])]))
|