diff --git a/src/clj/auto_ap/graphql.clj b/src/clj/auto_ap/graphql.clj index 87b49db3..f56463c5 100644 --- a/src/clj/auto_ap/graphql.clj +++ b/src/clj/auto_ap/graphql.clj @@ -387,8 +387,7 @@ :resolve :mutation/print-checks} :add_handwritten_check {:type :check_result - :args {:invoice_id {:type :id} - :amount {:type 'Float} + :args {:invoice_payments {:type '(list :invoice_payment_amount)} :date {:type 'String} :check_number {:type 'Int} :bank_account_id {:type :id}} diff --git a/src/clj/auto_ap/graphql/checks.clj b/src/clj/auto_ap/graphql/checks.clj index beaec621..a9f7ba99 100644 --- a/src/clj/auto_ap/graphql/checks.clj +++ b/src/clj/auto_ap/graphql/checks.clj @@ -330,24 +330,30 @@ :end (+ (:start args 0) (count payments))}])) (defn add-handwritten-check [context args value] - (let [invoice (d-invoices/get-by-id (:invoice_id args)) + (let [invoices (d-invoices/get-multi (map :invoice_id (:invoice_payments args))) bank-account-id (:bank_account_id args) bank-account (d-bank-accounts/get-by-id bank-account-id) - _ (assert-can-see-client (:id context) (:invoice/client invoice)) - base-payment (base-payment [invoice] (:invoice/vendor invoice) - (:invoice/client invoice) - bank-account :payment-type/check 0 {(:invoice_id args) (:amount args)})] + _ (doseq [invoice invoices] + (assert-can-see-client (:id context) (:invoice/client invoice))) + invoice-payment-lookup (by :invoice_id :amount (:invoice_payments args)) + base-payment (base-payment invoices + (:invoice/vendor (first invoices)) + (:invoice/client (first invoices)) + bank-account + :payment-type/check + 0 + invoice-payment-lookup)] + @(d/transact (d/connect uri) (into [(assoc base-payment - :payment/type :payment-type/check - :payment/status :payment-status/pending - :payment/check-number (:check_number args) - :payment/date (c/to-date (parse (:date args) iso-date)) - :payment/amount (:amount args))] - (invoice-payments [invoice] {(:invoice_id args) (:amount args)}))) + :payment/type :payment-type/check + :payment/status :payment-status/pending + :payment/check-number (:check_number args) + :payment/date (c/to-date (parse (:date args) iso-date)))] + (invoice-payments invoices invoice-payment-lookup))) (->graphql {:s3-url nil - :invoices [(d-invoices/get-by-id (:invoice_id args))]}))) + :invoices (d-invoices/get-multi (map :invoice_id (:invoice_payments args)))}))) (defn void-check [context {id :payment_id} value] diff --git a/src/cljs/auto_ap/views/pages/unpaid_invoices.cljs b/src/cljs/auto_ap/views/pages/unpaid_invoices.cljs index 03d708b0..f5053993 100644 --- a/src/cljs/auto_ap/views/pages/unpaid_invoices.cljs +++ b/src/cljs/auto_ap/views/pages/unpaid_invoices.cljs @@ -1,7 +1,7 @@ (ns auto-ap.views.pages.unpaid-invoices (:require [re-frame.core :as re-frame] [reagent.core :as r] - [clojure.string :as str] + [clojure.string :as str :refer [blank?]] [clojure.spec.alpha :as s] [cljs-time.core :as c] [goog.string :as gstring] @@ -35,6 +35,19 @@ [:expense_accounts [:amount :id :expense_account_id :location [:expense_account [:id :name [:parent [:id :name]]]]]]]) + +(defn does-amount-exceed-outstanding? [amount outstanding-balance] + (let [amount (js/parseFloat amount) + outstanding-balance (js/parseFloat outstanding-balance)] + (or (and (> outstanding-balance 0) + (> amount outstanding-balance)) + (and (> outstanding-balance 0) + (<= amount 0)) + (and (< outstanding-balance 0) + (< amount outstanding-balance)) + (and (< outstanding-balance 0) + (>= amount 0))))) + (re-frame/reg-sub ::invoice-page (fn [db] @@ -126,18 +139,16 @@ ::handwrite-checks (fn [{:keys [db]} _] (let [{:keys [checked invoices]} (get-in db [::invoice-page]) - invoice (->> invoices - (filter (comp checked :id)) - first) - ] + invoices (->> checked + vals + (map #(assoc % :amount (:outstanding-balance %))))] {:dispatch [::events/modal-status ::handwrite-checks {:visible? true}] :db (-> db (forms/stop-form ::new-invoice) (update-in [::invoice-page :print-checks-shown?] #(not %) ) (assoc-in [::handwrite-checks] {:bank-account-id (:id (first @(re-frame/subscribe [::subs/bank-accounts]))) - :amount (:outstanding-balance invoice) - :invoice invoice } ))}))) + :invoices invoices } ))}))) (re-frame/reg-event-db ::cancel-advanced-print @@ -163,6 +174,15 @@ (if (= which (:id i)) (assoc-in i f v) i)))))) +(re-frame/reg-event-db + ::edit-handwritten-payment + (fn [db [_ which f v]] + (update-in db [::handwrite-checks :invoices] + (fn [is] + (for [i is] + (if (= which (:id i)) + (assoc-in i f v) + i)))))) (defn print-checks-query [invoice-payments bank-account-id type client-id] {:venia/operation {:operation/type :mutation @@ -310,7 +330,7 @@ (re-frame/reg-event-fx ::save-and-print-invoice (fn [{:keys [db]} [_ bank-account-id type ]] - (println bank-account-id type) + (when @(re-frame/subscribe [::can-submit-edit-invoice]) (let [{:keys [data]} @(re-frame/subscribe [::forms/form ::new-invoice])] {:db (forms/loading db ::new-invoice) @@ -385,7 +405,9 @@ (re-frame/reg-event-fx ::handwrite-checks-save (fn [{:keys [db]} _] - (let [{:keys [date invoice amount check-number bank-account-id]} @(re-frame/subscribe [::handwrite-checks])] + (let [{:keys [date invoices check-number bank-account-id]} @(re-frame/subscribe [::handwrite-checks]) + invoice-amounts (by :id (comp js/parseFloat :amount) invoices)] + {:graphql {:token (-> db :user) :query-obj {:venia/operation {:operation/type :mutation @@ -393,13 +415,22 @@ :venia/queries [{:query/data [:add-handwritten-check {:date date - :amount amount + :invoice_payments (map (fn [x] + {:invoice-id (:id x) + :amount (invoice-amounts (:id x))}) + invoices) :check-number check-number :bank-account-id bank-account-id - :invoice_id (:id invoice) } [[:invoices invoice-read]]]}]} - :on-success [::handwrite-checks-succeeded]}}))) + :on-success [::handwrite-checks-succeeded] + :on-error [::handwrite-checks-failed]}}))) + +(re-frame/reg-event-fx + ::handwrite-checks-failed + (fn [{:keys [db]} [_ result]] + + {:dispatch [::events/modal-failed ::handwrite-checks (:message (first result))]})) (re-frame/reg-event-fx ::handwrite-checks-succeeded @@ -510,17 +541,7 @@ (seq (filter (fn [{:keys [outstanding-balance amount]}] - (let [amount (js/parseFloat amount) - outstanding-balance (js/parseFloat outstanding-balance)] - (println amount outstanding-balance) - (or (and (> outstanding-balance 0) - (> amount outstanding-balance)) - (and (> outstanding-balance 0) - (<= amount 0)) - (and (< outstanding-balance 0) - (< amount outstanding-balance)) - (and (< outstanding-balance 0) - (>= amount 0))))) + (does-amount-exceed-outstanding? amount outstanding-balance )) invoices)) "disabled" @@ -570,7 +591,7 @@ ::can-submit-edit-invoice :<- [::forms/form ::new-invoice] (fn [{:keys [data status]} _] - (println (s/explain-data ::invoice/invoice data)) + (let [min-total (if (= (:total (:original data)) (:outstanding-balance (:original data))) nil (- (:total (:original data)) (:outstanding-balance (:original data))))] @@ -582,15 +603,25 @@ (defn handwrite-checks-modal [] (let [{:keys [checked]} @(re-frame/subscribe [::invoice-page]) - {:keys [invoice] :as handwrite-checks} @(re-frame/subscribe [::handwrite-checks]) + {:keys [invoices] :as handwrite-checks} @(re-frame/subscribe [::handwrite-checks]) change-event [::events/change-form [::handwrite-checks]] current-client @(re-frame/subscribe [::subs/client])] + [action-modal {:id ::handwrite-checks :title "Handwrite Check" :action-text "Save" :save-event [::handwrite-checks-save] - #_#_:can-submit? (s/valid? ::invoice/invoice data)} + :can-submit? (cond (seq (filter + (fn [{:keys [outstanding-balance amount]}] + (does-amount-exceed-outstanding? amount outstanding-balance )) + invoices)) + false + + :else + (and (not (blank? (:check-number handwrite-checks))) + (not (blank? (:date handwrite-checks))))) + } [horizontal-field [:label.label "Pay using"] [:span.select @@ -602,18 +633,9 @@ (for [{:keys [id number name]} (->> current-client :bank-accounts (filter #(= (:type %) :check)) (sort-by :sort-order))] ^{:key id} [:option {:value id} name])]]]] - [horizontal-field - [:label.label "Paid amount"] - [:div.field.has-addons.is-extended - [:p.control [:a.button.is-static "$"]] - [:p.control - [bind-field - [:input.input {:type "number" - :field [:amount] - :event change-event - :subscription handwrite-checks - :spec ::invoice/total - :step "0.01"}]]]]] + + + [horizontal-field [:label.label "Date"] @@ -640,7 +662,29 @@ :field [:check-number] :event change-event #_#_:spec ::check/date - :subscription handwrite-checks}]]]])) + :subscription handwrite-checks}]]] + [:table.table.is-fullwidth + [:thead + [:tr + [:th "Invoice ID"] + [:th {:style {"width" "10em"}} "Payment"]]] + [:tbody + (for [{:keys [payment outstanding-balance invoice-number id] :as i} invoices] + ^{:key id} + [:tr + [:td invoice-number] + + [:td [:div.field.has-addons.is-extended + [:p.control [:a.button.is-static "$"]] + [:p.control + [bind-field + [:input.input {:type "number" + :field :amount + :event [::edit-handwritten-payment id] + :subscription i + :value payment + #_#_:max outstanding-balance + :step "0.01"}]]]]]])]]])) (re-frame/reg-event-fx ::change-new-invoice-client @@ -829,7 +873,7 @@ ^{:key (str id "-check")} [:a.dropdown-item {:on-click (dispatch-event [::save-and-print-invoice id :check])} "Print checks from " name] ^{:key (str id "-debit")} [:a.dropdown-item {:on-click (dispatch-event [::save-and-print-invoice id :debit])} "Debit from " name]))))]]]) [:div.column - [:button.button.is-medium.is-primary.is-fullwidth {:disabled (if (doto @(re-frame/subscribe [::can-submit-edit-invoice]) println) + [:button.button.is-medium.is-primary.is-fullwidth {:disabled (if @(re-frame/subscribe [::can-submit-edit-invoice]) "" "disabled") :class (str @(re-frame/subscribe [::forms/loading-class ::new-invoice]) @@ -922,7 +966,7 @@ ^{:key (str id "-debit")} [:a.dropdown-item {:on-click (dispatch-event [::print-checks id :debit])} "Debit from " name]))) ^{:key "advanced-divider"} [:hr.dropdown-divider] - (when (= 1 (count checked-invoices)) + (when (= 1 (count (set (map (comp :id :vendor) (vals checked-invoices))))) ^{:key "handwritten"} [:a.dropdown-item {:on-click (dispatch-event [::handwrite-checks])} "Handwritten Check..."]) ^{:key "advanced"} [:a.dropdown-item {:on-click (dispatch-event [::advanced-print-checks])} "Advanced..."])]])]] [:div.is-pulled-right