(ns auto-ap.views.components.invoice-table (:require [re-frame.core :as re-frame] [auto-ap.subs :as subs] [auto-ap.views.utils :refer [date->str dispatch-event delayed-dispatch nf]] [auto-ap.views.components.paginator :refer [paginator]] [auto-ap.views.components.sorter :refer [sorted-column]] [auto-ap.views.components.dropdown :refer [drop-down drop-down-contents]] [auto-ap.events :as events] [reagent.core :as reagent] [clojure.string :as str] [cljs-time.format :as format] [goog.string :as gstring] [goog.i18n.NumberFormat.Format]) ) ;; TODO graphql schema enforcement ;; TODO postgres constraints for data integrity ;; TODO performance ;; TODO refactor graphql (re-frame/reg-event-fx ::toggle-checks (fn [{:keys [db] } [_ invoice-id]] {:db (update-in db [::visible-checks] (fn [i] (if (= i invoice-id) nil invoice-id) ))})) (re-frame/reg-sub ::visible-checks (fn [db] (::visible-checks db))) (re-frame/reg-event-fx ::toggle-expense-accounts (fn [{:keys [db] } [_ invoice-id]] (println "toggling") {:db (update-in db [::visible-expense-accounts] (fn [i] (if (= i invoice-id) nil invoice-id) ))})) (re-frame/reg-sub ::visible-expense-accounts (fn [db] (::visible-expense-accounts db))) (defn query [params] {:venia/queries [[:invoice_page (assoc params :client-id (:id @(re-frame/subscribe [::subs/client]))) [[:invoices [:id :total :outstanding-balance :invoice-number :date :status [:vendor [:name :id]] [:expense_accounts [:amount :id :expense_account_id :location [:expense_account [:id :name :location [:parent [:id :name]]]]]] [:client [:name :id :locations]] [:payments [:amount :id [:payment [:id :status :amount :s3_url :check_number [:transaction [:post_date]]]]]]]] :total :start :end]]]}) (defn invoice-table [{:keys [id invoice-page status on-params-change vendors params check-boxes checked on-check-changed on-edit-invoice on-void-invoice on-unvoid-invoice expense-event]}] (let [visible-checks @(re-frame/subscribe [::visible-checks]) visible-expense-accounts @(re-frame/subscribe [::visible-expense-accounts]) selected-client @(re-frame/subscribe [::subs/client]) opc (fn [p] (on-params-change (merge @params p)))] (fn [{:keys [id invoice-page status on-params-change vendors checked params]}] (let [{:keys [sort-by asc]} @params {:keys [invoices start end count total]} @invoice-page visible-checks @(re-frame/subscribe [::visible-checks]) visible-expense-accounts @(re-frame/subscribe [::visible-expense-accounts]) selected-client @(re-frame/subscribe [::subs/client]) percentage-size (if selected-client "%50%" "33%") ] [:div [paginator {:start start :end end :count count :total total :on-change (fn [p ] (on-params-change (merge @params p) ))}] "Showing " (inc start) "-" end "/" total [:table.table.is-fullwidth [:thead [:tr (when check-boxes [:th {:style {"width" "20px"}}]) (when-not selected-client [sorted-column {:on-sort opc :style {:width percentage-size :cursor "pointer"} :sort-key "client" :sort-by sort-by :asc asc} "Client"]) [sorted-column {:on-sort opc :style {:width percentage-size :cursor "pointer"} :sort-key "vendor" :sort-by sort-by :asc asc} "Vendor"] [sorted-column {:on-sort opc :style {:width percentage-size :cursor "pointer"} :sort-key "invoice-number" :sort-by sort-by :asc asc} "Invoice #"] [sorted-column {:on-sort opc :style {:width "8em" :cursor "pointer"} :sort-key "date" :sort-by sort-by :asc asc} "Date"] [sorted-column {:on-sort opc :style {:width "8em" :cursor "pointer"} :sort-key "location" :sort-by sort-by :asc asc} "Location"] [sorted-column {:on-sort opc :style {:width "8em" :cursor "pointer"} :sort-key "total" :class "has-text-right" :sort-by sort-by :asc asc} "Amount"] [sorted-column {:on-sort opc :style {:width "10em" :cursor "pointer"} :sort-key "outstanding-balance" :class "has-text-right" :sort-by sort-by :asc asc} "Outstanding"] [:th { :style {:width "20rem" :cursor "pointer"} } ""]]] [:tbody (if (:loading @status) [:tr [:td {:col-span 5} [:i.fa.fa-spin.fa-spinner]]] (for [{:keys [client payments expense-accounts invoice-number date total outstanding-balance id vendor] :as i} (:invoices @invoice-page)] ^{:key id} [:tr {:class (:class i)} (when check-boxes [:td [:input.checkbox {:type "checkbox" :checked (if (get checked id) "checked" "") :on-change (fn [x e] (when on-check-changed (on-check-changed id i)))} ]]) (when-not selected-client [:td (:name client)]) [:td (:name vendor)] [:td invoice-number] [:td (date->str date) ] [:td (str/join ", " (set (map :location expense-accounts)))] [:td.has-text-right (nf total )] [:td.has-text-right (nf outstanding-balance )] [:td.expandable [:div.buttons (when (seq expense-accounts) [drop-down {:id [::expense-accounts id ] :header [:a.button.badge {:data-badge (str (clojure.core/count expense-accounts)) :aria-haspopup true :on-click (dispatch-event [::events/toggle-menu [::expense-accounts id]]) :tab-index "0" } "Accounts"]} [drop-down-contents [:div (for [e expense-accounts] ^{:key (:id e)} [:span.dropdown-item (:name (:expense-account e)) " " (gstring/format "$%.2f" (:amount e) ) ]) [:hr.dropdown-divider] (when expense-event [:a.dropdown-item.is-primary {:on-click (dispatch-event (conj expense-event i))} "Change"])]]]) [:span {:style {:margin-left "1em"}}] (when (and on-edit-invoice (not= ":voided" (:status i))) [:a.button {:on-click (fn [] (on-edit-invoice i))} [:span.icon [:i.fa.fa-pencil]]]) (when (and on-void-invoice (= (:outstanding-balance i) (:total i)) (not= ":voided" (:status i))) [:a.button {:on-click (fn [] (on-void-invoice i))} [:span.icon [:span.icon-bin-2 {:style {:font-weight "400"}}]]]) (when (and on-unvoid-invoice (= ":voided" (:status i))) [:a.button {:on-click (fn [] (on-unvoid-invoice i))} [:span [:span.icon [:i.fa.fa-undo]]]]) (when (seq payments) [drop-down {:id [::payments id] :header [:button.button.badge {:data-badge (str (clojure.core/count payments)) :aria-haspopup true :tab-index "0" :on-click (dispatch-event [::events/toggle-menu [::payments id]]) } "Payments"]} [:div (for [payment payments] (if (:s3-url (:payment payment)) ^{:key (:id payment)} [:a.dropdown-item {:href (:s3-url (: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))))))]))]])]]]))]]]))))