diff --git a/src/clj/auto_ap/datomic/checks.clj b/src/clj/auto_ap/datomic/checks.clj index 393c80eb..ba32b9bc 100644 --- a/src/clj/auto_ap/datomic/checks.clj +++ b/src/clj/auto_ap/datomic/checks.clj @@ -29,7 +29,8 @@ {:payment/type [:db/ident]}])) (defn raw-graphql [args] - (let [query (cond-> {:query {:find [default-read] + (let [check-number-like (try (Long/parseLong (:check-number-like args)) (catch Exception e nil)) + query (cond-> {:query {:find [default-read] :in ['$] :where ['[?e :payment/client]]} :args [(d/db (d/connect uri))]} @@ -54,7 +55,10 @@ (:amount args) (add-arg '?amount (:amount args) '[?e :payment/amount ?amount]) (:status args) (add-arg '?status (:status args) - '[?e :payment/status ?status]))] + '[?e :payment/status ?status]) + check-number-like (add-arg '?check-number-like check-number-like + '[?e :payment/check-number ?check-number-like] + #_'[(.contains ^String ?check-number ?check-number-like)]))] (->> (d/query query) diff --git a/src/clj/auto_ap/datomic/migrate.clj b/src/clj/auto_ap/datomic/migrate.clj index 7235faa7..74aed9bd 100644 --- a/src/clj/auto_ap/datomic/migrate.clj +++ b/src/clj/auto_ap/datomic/migrate.clj @@ -35,6 +35,18 @@ :code [[:db/add e a (-> (d/entity db e) a (+ amount))]] })}]] ) +(defn fix-pay-function [conn] + [[{:db/ident :pay + :db/doc "Data function that increments value of attribute a by amount." + :db/fn (d/function '{:lang "clojure" + :params [db e amount] + :code (let [current-outstanding-balance (-> (d/entity db e) :invoice/outstanding-balance) + new-outstanding-balance (- current-outstanding-balance amount)] + [[:db/add e :invoice/outstanding-balance new-outstanding-balance] + [:db/add e :invoice/status (if (< -0.001 new-outstanding-balance 0.001) + :invoice-status/paid + :invoice-status/unpaid)]])})}]] ) + (defn -main [& args] (println "Creating database...") (d/create-database uri) @@ -43,6 +55,7 @@ conn (d/connect uri) norms-map {:auto-ap/base-schema {:txes auto-ap.datomic/base-schema} :auto-ap/functions {:txes-fn 'auto-ap.datomic.migrate/functions :requires [:auto-ap/base-schema]} + :auto-ap/fx-pay-function-2 {:txes-fn 'auto-ap.datomic.migrate/fix-pay-function :requires [:auto-ap/functions]} :auto-ap/migrate-vendors {:txes-fn 'auto-ap.datomic/migrate-vendors :requires [:auto-ap/base-schema]} :auto-ap/migrate-clients {:txes-fn 'auto-ap.datomic/migrate-clients :requires [:auto-ap/migrate-vendors]} :auto-ap/migrate-users {:txes-fn 'auto-ap.datomic/migrate-users :requires [:auto-ap/migrate-clients]} diff --git a/src/clj/auto_ap/graphql.clj b/src/clj/auto_ap/graphql.clj index 53f4b013..f56463c5 100644 --- a/src/clj/auto_ap/graphql.clj +++ b/src/clj/auto_ap/graphql.clj @@ -266,6 +266,7 @@ :payment_page {:type '(list :payment_page) :args {:client_id {:type :id} :vendor_id {:type :id} + :check_number_like {:type 'String} :start {:type 'Int} :sort_by {:type 'String} :asc {:type 'Boolean}} @@ -386,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 69c1fd8e..a9f7ba99 100644 --- a/src/clj/auto_ap/graphql/checks.clj +++ b/src/clj/auto_ap/graphql/checks.clj @@ -10,7 +10,7 @@ [auto-ap.datomic.vendors :as d-vendors] [auto-ap.datomic.clients :as d-clients] [auto-ap.datomic.bank-accounts :as d-bank-accounts] - [auto-ap.datomic :refer [uri]] + [auto-ap.datomic :refer [uri remove-nils]] [auto-ap.utils :refer [by]] [auto-ap.numeric :refer [num->words]] [config.core :refer [env]] @@ -181,6 +181,7 @@ (edn/read-string) + make-check-pdf (io/make-input-stream {})) :metadata {:content-type "application/pdf"}) @@ -228,29 +229,32 @@ (map (fn [i] (str (:invoice/invoice-number i) "(" (invoice-amounts (:db/id i)) ")")) invoices))) - payment (assoc (base-payment invoices vendor client bank-account type index invoice-amounts) - :payment/s3-uuid uuid - :payment/s3-key (str "checks/" uuid ".pdf") - :payment/s3-url (str "http://" (:data-bucket env) ".s3-website-us-east-1.amazonaws.com/checks/" uuid ".pdf") - :payment/check-number (+ index (:bank-account/check-number bank-account)) - :payment/type :payment-type/check - :payment/memo memo - :payment/status :payment-status/pending - :payment/pdf-data (pr-str {:vendor vendor - :paid-to (or (:vendor/paid-to vendor) (:vendor/name vendor)) - :amount (reduce + 0 (map (comp invoice-amounts :db/id) invoices)) - :check (str (+ index (:bank-account/check-number bank-account))) - :memo memo - :date (date->str (local-now)) - :client client - :bank-account bank-account - #_#_:client {:name (:name client) - :address (:address client) - :signature-file (:signature-file client) - :bank {:name (:bank-account/bank-name bank-account) - :acct (:bank-account/bank-code bank-account) - :routing (:bank-account/routing bank-account) - :acct-number (:bank-account/number bank-account)}}}))] + base-payment (base-payment invoices vendor client bank-account type index invoice-amounts) + payment + (remove-nils + (assoc base-payment + :payment/s3-uuid (when (> (:payment/amount base-payment) 0) uuid) + :payment/s3-key (when (> (:payment/amount base-payment) 0) (str "checks/" uuid ".pdf")) + :payment/s3-url (when (> (:payment/amount base-payment) 0) (str "http://" (:data-bucket env) ".s3-website-us-east-1.amazonaws.com/checks/" uuid ".pdf")) + :payment/check-number (+ index (:bank-account/check-number bank-account)) + :payment/type :payment-type/check + :payment/memo memo + :payment/status :payment-status/pending + :payment/pdf-data (pr-str {:vendor vendor + :paid-to (or (:vendor/paid-to vendor) (:vendor/name vendor)) + :amount (reduce + 0 (map (comp invoice-amounts :db/id) invoices)) + :check (str (+ index (:bank-account/check-number bank-account))) + :memo memo + :date (date->str (local-now)) + :client client + :bank-account bank-account + #_#_:client {:name (:name client) + :address (:address client) + :signature-file (:signature-file client) + :bank {:name (:bank-account/bank-name bank-account) + :acct (:bank-account/bank-code bank-account) + :routing (:bank-account/routing bank-account) + :acct-number (:bank-account/number bank-account)}}})))] (-> [] (conj payment) @@ -301,7 +305,10 @@ (conj checks [:inc (:db/id bank-account) :bank-account/check-number (count invoices-grouped-by-vendor)]) checks)] (when (= type :payment-type/check) - (make-pdfs (filter #(= :payment-type/check (:payment/type %)) checks))) + (make-pdfs (filter #(and (= :payment-type/check (:payment/type %)) + (> (:payment/amount %) 0.0) + ) + checks))) @(d/transact (d/connect uri) checks) @@ -323,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] @@ -355,9 +368,9 @@ [[:db.fn/retractEntity (:db/id x)] {:db/id (:db/id invoice) :invoice/outstanding-balance new-balance - :invoice/status (if (> new-balance 0) - :invoice-status/unpaid - (:invoice/status invoice))}])) + :invoice/status (if (< -0.001 new-balance 0.001) + (:invoice/status invoice) + :invoice-status/unpaid)}])) (:payment/invoices check)) updated-payment {:db/id id :payment/amount 0.0 diff --git a/src/clj/auto_ap/routes/invoices.clj b/src/clj/auto_ap/routes/invoices.clj index 61a4efc0..ac54ac60 100644 --- a/src/clj/auto_ap/routes/invoices.clj +++ b/src/clj/auto_ap/routes/invoices.clj @@ -22,14 +22,25 @@ n)))) (defn assoc-client-code [i] - (-> i - (assoc :client-code (first (str/split (:location i) #"-" ))) - (assoc :default-location (second (str/split (:location i) #"-" ))))) + (let [[client-code default-location] (str/split (:location i) #"-" )] + (cond-> i + client-code (assoc :client-code client-code) + default-location (assoc :default-location default-location) + (not client-code) (update :errors conj {:info "No client code found"}) + (not default-location) (update :errors conj {:info "No default location found"})))) -(defn parse-client [{:keys [client-code client]} clients] +(defn parse-client [{:keys [client-code client default-location]} clients] (if-let [id (:db/id (or (clients client-code) (clients client)))] - id + (do + (println "FOUND CLIENT" (or (clients client-code) + (clients client))) + (when (not ((set (:client/locations (or (clients client-code) + (clients client)))) + default-location)) + (throw (Exception. (str "Location '" default-location "' not found for client '" client-code "'."))) + ) + id) (throw (Exception. (str "Client code '" client-code "' and client named '" client "' not found."))))) (defn parse-invoice-number [{:keys [invoice-number]}] @@ -105,6 +116,8 @@ (map (parse-or-error :invoice-number parse-invoice-number)) (map (parse-or-error :total parse-amount)) (map (parse-or-error :date parse-date)))] + + rows)) (defn invoice-rows->transaction [rows] @@ -229,6 +242,7 @@ :bank-account-id (:db/id (all-bank-accounts bank-account-code))}))))] (manual-import raw-transactions) + {:status 200 :body (pr-str {:imported (count raw-transactions) @@ -272,10 +286,11 @@ set) inserted-rows @(d/transact (d/connect uri) (invoice-rows->transaction (:new grouped-rows)))] + {:status 200 :body (pr-str {:imported (count (:new grouped-rows)) :already-imported (count (:exists grouped-rows)) :vendors-not-found vendors-not-found - :errors (:error grouped-rows)}) + :errors (map #(dissoc % :date) (:error grouped-rows))}) :headers {"Content-Type" "application/edn"}})))) wrap-secure)) diff --git a/src/cljs/auto_ap/forms.cljs b/src/cljs/auto_ap/forms.cljs index c94b3495..80fcea71 100644 --- a/src/cljs/auto_ap/forms.cljs +++ b/src/cljs/auto_ap/forms.cljs @@ -56,9 +56,11 @@ (re-frame/reg-event-db ::save-error (fn [db [_ form result]] + (-> db (assoc-in [::forms form :status] :error) - (assoc-in [::forms form :error] (:message (first result)))))) + (assoc-in [::forms form :error] (or (:message (first result)) + result))))) (defn side-bar-form [{:keys [form]} children] [:div [:a.delete.is-pulled-right {:on-click (dispatch-event [::form-closing form])}] [:div children]]) @@ -67,3 +69,8 @@ (-> db (assoc-in [::forms id :status] :loading) (assoc-in [::forms id :error] nil))) + +(defn save-succeeded [db id] + (-> db + (assoc-in [::forms id :status] nil) + (assoc-in [::forms id :error] nil))) diff --git a/src/cljs/auto_ap/views/pages/admin/excel_import.cljs b/src/cljs/auto_ap/views/pages/admin/excel_import.cljs index 3ed862a3..a33f9d16 100644 --- a/src/cljs/auto_ap/views/pages/admin/excel_import.cljs +++ b/src/cljs/auto_ap/views/pages/admin/excel_import.cljs @@ -6,6 +6,7 @@ [auto-ap.events :as all-events] [auto-ap.events.admin.clients :as events] [auto-ap.entities.clients :as entity] + [auto-ap.forms :as forms] [auto-ap.views.components.layouts :refer [side-bar-layout]] [auto-ap.views.components.admin.side-bar :refer [admin-side-bar]] @@ -31,39 +32,36 @@ (fn [db [_ field v]] (assoc-in db (into [::excel-import] field) v))) -(re-frame/reg-event-db - ::edit - (fn [db [_ field v]] - (assoc db ::excel-import nil ) - db)) + (re-frame/reg-event-fx ::save - (fn [{:keys [db]}] - (let [excel-import (::excel-import db)] - {:http {:token (:user db) + [(forms/in-form ::excel-import)] + (fn [{{excel-import-data :data :as excel-import-form} :db}] + (let [user @(re-frame/subscribe [::subs/token])] + {:db (-> excel-import-form + (assoc :status :loading) + (assoc :error nil)) + :http {:token user :method :post - :body (pr-str excel-import) + :body (pr-str excel-import-data) :headers {"Content-Type" "application/edn"} :uri (str "/api/invoices/upload-integreat") :on-success [::save-complete] - :on-error [::save-error]} - :db (-> db - (assoc-in [::excel-import :rows] nil) - (assoc-in [::excel-import :saving?] true))}))) + :on-error [::forms/save-error ::excel-import]}}))) (re-frame/reg-event-fx ::save-complete (fn [{:keys [db]} [_ rows]] - {:dispatch [::edit nil] - :db + {:db (-> db - (assoc-in [::excel-import :rows] rows) - (assoc-in [::excel-import :saving?] false))})) + (forms/save-succeeded ::excel-import) + (assoc-in [::excel-import :rows] rows))})) (re-frame/reg-event-fx ::save-error (fn [{:keys [db]}] + (println "ERROR") {:dispatch [::change [:error] true] :db (-> db (assoc-in [::excel-import :rows] nil) @@ -82,14 +80,20 @@ (fn [{:keys [db]}] (let [excel-import (::excel-import db)] (println (::expense-accounts db)) - {:https {:requests (map (fn [v] - {:token (:user db) - :method :post - :body (pr-str {:name v :default-expense-account (-> db ::expense-accounts (get v) :default-expense-account) }) - :headers {"Content-Type" "application/edn"} - :uri (str "/api/vendors/")}) - (doto (get-in db [::excel-import :create-vendors]) - println)) + {:graphql {:token (:user db) + :query-obj {:venia/operation {:operation/type :mutation + :operation/name "UpsertVendor"} + + :venia/queries (map (fn [v ] + {:query/data [:upsert-vendor + {:vendor {:name v :default-expense-account (-> db ::expense-accounts (get v) :default-expense-account)}} + [:id :name :default-expense-account + [:primary-contact [:name :phone :email :id]] + [:secondary-contact [:id :name :phone :email]] + :print-as :invoice-reminder-schedule :code + [:address [:street1 :street2 :city :state :zip]]]]}) + + (get-in db [::excel-import :create-vendors]))} :on-success [::create-vendor-complete] :on-error [::create-vendor-error]} :db (-> db @@ -112,9 +116,12 @@ :or {create-vendors #{}} :as excel-import-data} @(re-frame/subscribe [::excel-import]) data @(re-frame/subscribe [::expense-accounts]) + form @(re-frame/subscribe [::forms/form ::excel-import]) + chooseable-expense-accounts @(re-frame/subscribe [::subs/chooseable-expense-accounts]) change-event [::all-events/change-form [::expense-accounts]]] + (println form) [:div [:h1.title "Import Invoices from Integreat Excel"] (when (seq vendors-not-found) @@ -161,13 +168,13 @@ [:textarea.textarea {:rows "20" :field :excel-rows :type "text" - :event ::change - :subscription excel-import-data}]] + :event [::forms/change ::excel-import] + :subscription (:data form)}]] [:button.button.is-large.is-pulled-right.is-primary {:on-click (dispatch-event [::save]) - :class (when (:saving? excel-import-data) - "is-loading") - :disabled (when (:saving? excel-import-data) "disabled")} "Import"] + :class (str @(re-frame/subscribe [::forms/loading-class ::excel-import]) + (when (:error form) " animated shake")) + :disabled (when (= :saving (:status form)) "disabled")} "Import"] [:div.is-clearfix] [:div.is-clearfix diff --git a/src/cljs/auto_ap/views/pages/checks.cljs b/src/cljs/auto_ap/views/pages/checks.cljs index 9bd8f794..cad7dac5 100644 --- a/src/cljs/auto_ap/views/pages/checks.cljs +++ b/src/cljs/auto_ap/views/pages/checks.cljs @@ -40,7 +40,9 @@ (assoc-in [::params] params)) :graphql {:token (-> cofx :db :user) :query-obj {:venia/queries [[:payment_page - (assoc params :client-id (:id @(re-frame/subscribe [::subs/client]))) + (-> params + (assoc :client-id (:id @(re-frame/subscribe [::subs/client]))) + (dissoc :check-number-like-current)) [[:payments [:id :status :amount :type :check_number :s3_url :date [:vendor [:name :id]] [:client [:name :id]]]] :total :start @@ -96,6 +98,27 @@ (assoc-in updated [::params :vendor-id] value)) updated)))) +(re-frame/reg-event-fx + ::check-number-like-current-changed + (fn [{:keys [db]} [_ params check-like]] + {:db (assoc-in db [::params :check-number-like-current] check-like ) + :dispatch-debounce {:event [::check-number-like-settled check-like] + :time 500 + :key ::check-number-like}})) + +(re-frame/reg-event-fx + ::check-number-like-settled + (fn [{:keys [db]} [_ check-like]] + {:dispatch [::params-change (assoc (::params db) :check-number-like check-like :start 0) ]})) + +(defn check-number-filter [] + (let [{:keys [check-number-like-current] :as params} @(re-frame/subscribe [::params])] + [:div.field + [:div.control [:input.input {:placeholder "10001" + :value check-number-like-current + :on-change (fn [x] + (re-frame/dispatch [::check-number-like-current-changed params (.. x -target -value) ]))} ]]])) + (defn check-table [{:keys [id payment-page status on-params-change vendors params check-boxes checked on-check-changed expense-event]}] (let [#_#_state (reagent/atom (or @params {})) selected-client @(re-frame/subscribe [::subs/client]) @@ -211,8 +234,12 @@ (defn checks-page [] [side-bar-layout {:side-bar [:div - [:p.menu-label "Vendor"] + [:p.menu-label "Vendor"] [:div [vendor-filter {:on-change-event [::change-selected-vendor] :value (:vendor-filter @(re-frame/subscribe [::payment-page])) - :vendors @(re-frame/subscribe [::subs/vendors])}]]] + :vendors @(re-frame/subscribe [::subs/vendors])}]] + + [:p.menu-label "Check #"] + [:div [check-number-filter]] + ] :main [checks-content]}]) diff --git a/src/cljs/auto_ap/views/pages/unpaid_invoices.cljs b/src/cljs/auto_ap/views/pages/unpaid_invoices.cljs index 311f2d2b..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 @@ -508,7 +539,10 @@ :disabled (cond printing? "disabled" - (seq (filter #(> (js/parseFloat (:amount %)) (js/parseFloat (:outstanding-balance %))) invoices)) + (seq (filter + (fn [{:keys [outstanding-balance amount]}] + (does-amount-exceed-outstanding? amount outstanding-balance )) + invoices)) "disabled" :else @@ -557,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))))] @@ -569,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 @@ -589,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"] @@ -627,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 @@ -816,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]) @@ -847,7 +904,7 @@ {:db (assoc-in db [::params :invoice-number-like-current] invoice-like ) :dispatch-debounce {:event [::invoice-number-like-settled invoice-like] :time 500 - :key ::invoice-nuber-like}})) + :key ::invoice-number-like}})) (re-frame/reg-event-fx ::invoice-number-like-settled @@ -872,7 +929,7 @@ [drop-down {:header [:button.button.is-success {:aria-haspopup true :on-click (dispatch-event [::events/toggle-menu ::print-checks ]) :disabled (if (and (seq checked-invoices) - (->> checked-invoices + #_(->> checked-invoices vals (group-by #(get-in % [:vendor :id])) (reduce-kv (fn [negative? _ invoices] @@ -909,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