diff --git a/src/clj/auto_ap/datomic.clj b/src/clj/auto_ap/datomic.clj index 986884a2..7931b34f 100644 --- a/src/clj/auto_ap/datomic.clj +++ b/src/clj/auto_ap/datomic.clj @@ -34,6 +34,24 @@ result nil))) +(def functions + [{:db/ident :pay + :db/doc "Data function that increments value of attribute a by amount." + :db/fn #db/fn {: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 (> new-outstanding-balance 0) + :invoice-status/unpaid + :invoice-status/paid)]])}} + {:db/ident :inc + :db/doc "Data function that increments value of attribute a by amount." + :db/fn #db/fn {:lang "clojure" + :params [db e a amount] + :code [[:db/add e a + (-> (d/entity db e) a (+ amount))]]}}] ) + (def vendor-schema [{:db/ident :vendor/original-id :db/valueType :db.type/long @@ -512,7 +530,7 @@ (defn create-schema [] (->> - (concat address-schema contact-schema vendor-schema client-schema bank-account-schema invoice-schema invoice-expense-account-schema payment-schema invoice-payment-schema transaction-schema user-schema) + (concat address-schema contact-schema vendor-schema client-schema bank-account-schema invoice-schema invoice-expense-account-schema payment-schema invoice-payment-schema transaction-schema user-schema functions) (d/transact (d/connect uri)))) diff --git a/src/clj/auto_ap/datomic/checks.clj b/src/clj/auto_ap/datomic/checks.clj index f621dc73..f8949683 100644 --- a/src/clj/auto_ap/datomic/checks.clj +++ b/src/clj/auto_ap/datomic/checks.clj @@ -9,6 +9,7 @@ (map first) (map #(update % :payment/date c/from-date)) (map #(update % :payment/status :db/ident)) + (map #(update % :payment/type :db/ident)) (map #(rename-keys % {:invoice-payment/_payment :payment/invoices})))) (defn add-arg [query name value where & rest] @@ -22,12 +23,13 @@ {:invoice-payment/_payment [* {:invoice-payment/invoice [*]}]} {:payment/client [:client/name :db/id]} {:payment/vendor [:vendor/name :db/id]} - {:payment/status [:db/ident]}])) + {:payment/status [:db/ident]} + {:payment/type [:db/ident]}])) (defn raw-graphql [args] (let [query (cond-> {:query {:find [default-read] :in ['$] - :where ['[?e :payment/original-id]]} + :where ['[?e :payment/client]]} :args [(d/db (d/connect uri))]} (:client-id args) (add-arg '?client-id (cond-> (:client-id args) (string? (:client-id args)) Long/parseLong ) diff --git a/src/clj/auto_ap/graphql.clj b/src/clj/auto_ap/graphql.clj index e54bfbc6..9df33cbe 100644 --- a/src/clj/auto_ap/graphql.clj +++ b/src/clj/auto_ap/graphql.clj @@ -58,7 +58,7 @@ :bank_account {:fields {:id {:type 'String} - :type {:type 'String} + :type {:type :bank_account_type} :number {:type 'String} :check_number {:type 'Int} :name {:type 'String} @@ -110,7 +110,7 @@ }} :payment {:fields {:id {:type 'String} - :type {:type 'String} + :type {:type :payment_type} :amount {:type 'String} :vendor {:type :vendor} :client {:type :client} @@ -287,11 +287,16 @@ :date {:type 'String} :total {:type 'Float}}}} + :enums {:payment_type {:values [{:enum-value :check} + {:enum-value :cash} + {:enum-value :debit}]} + :bank_account_type {:values [{:enum-value :check} + {:enum-value :cash}]}} :mutations {:print_checks {:type :check_result :args {:invoice_payments {:type '(list :invoice_payment_amount)} :bank_account_id {:type 'String} - :type {:type 'String} + :type {:type :payment_type} :client_id {:type 'String}} :resolve :mutation/print-checks} diff --git a/src/clj/auto_ap/graphql/checks.clj b/src/clj/auto_ap/graphql/checks.clj index 5b9e1875..a3420f88 100644 --- a/src/clj/auto_ap/graphql/checks.clj +++ b/src/clj/auto_ap/graphql/checks.clj @@ -1,6 +1,7 @@ (ns auto-ap.graphql.checks (:require [auto-ap.graphql.utils :refer [->graphql <-graphql assert-can-see-company]] [datomic.api :as d] + [clojure.edn :as edn] [com.walmartlabs.lacinia :refer [execute]] [com.walmartlabs.lacinia.executor :as executor] @@ -26,6 +27,7 @@ [clojure.string :as str] [clj-pdf.core :as pdf] [clj-time.format :as f] + [clj-time.coerce :as c] [clj-time.core :as time] [clojure.java.io :as io]) (:import [java.text DecimalFormat] @@ -61,16 +63,17 @@ (let [output-stream (ByteArrayOutputStream.)] (pdf/pdf [{:left-margin 25 :right-margin 0 :top-margin 0 :bottom-margin 0 :size :letter} - (let [{:keys [paid-to company check date amount memo] {print-as :print-as vendor-name :name :as vendor} :vendor} check + (let [{:keys [paid-to client check date amount memo] {print-as :vendor/print-as vendor-name :vendor/name :as vendor} :vendor} check + _ (println "VENDOR" vendor) df (DecimalFormat. "#,###.00") word-amount (num->words amount) amount (str "--" (.format df amount) "--")] [:table {:num-cols 12 :border false :leading 11 :widths (distribute [2 3 3 3 3 3 3 3 3 2 2 2])} - [(let [{:keys [name bank] {:keys [street1 street2 city state zip ]} :address} company] + [(let [{:keys [name bank] {:keys [street1 street2 city state zip ]} :address} client] [:cell {:colspan 3 } [:paragraph {:leading 14} name "\n" street1 "\n" (str city ", " state " " zip)] ]) - (let [{{:keys [name acct]} :bank} company] + (let [{{:keys [name acct]} :bank} client] [:cell {:colspan 7 :align :center} [:paragraph {:style :bold} name] [:paragraph {:size 8 :leading 8} acct]]) [:cell {:colspan 2 :size 13} check]] @@ -100,10 +103,10 @@ [[:cell {:size 9 :leading 11.5} "\n\n\n\n\nMEMO"] [:cell {:colspan 5 :leading 11.5} (split-memo memo) [:line {:line-width 0.15 :color [50 50 50]}]] - [:cell {:colspan 6 } (if (:signature-file company) + [:cell {:colspan 6 } (if (:signature-file client) [:image { :top-margin 90 :xscale 0.30 :yscale 0.30 :align :center} - (:signature-file company)] + (:signature-file client)] [:spacer])]] #_[ @@ -112,7 +115,7 @@ [[:cell {:colspan 2}] [:cell {:colspan 10 :leading 30} - [:phrase {:size 18 :ttf-name "public/micrenc.ttf"} (str "c" check "c a" (:routing (:bank company)) "a " (:acct-number (:bank company)) "c")]]] + [:phrase {:size 18 :ttf-name "public/micrenc.ttf"} (str "c" check "c a" (:routing (:bank client)) "a " (:acct-number (:bank client)) "c")]]] [[:cell {:colspan 12 :leading 18} [:spacer]]] @@ -120,7 +123,7 @@ (into [:cell {:colspan 9}] (let [{:keys [name] - {:keys [street1 city state zip bank]} :address} company] + {:keys [street1 city state zip bank]} :address} client] (list [:paragraph " " name] [:paragraph " " street1] @@ -134,8 +137,8 @@ [[:cell] [:cell {:colspan 5} [:paragraph " " vendor-name "\n" - " " (:street1 (:address vendor)) "\n" - " " (:city (:address vendor)) ", " (:state (:address vendor)) " " (:zip (:address vendor))]] + " " (:address/street1 (:vendor/address vendor)) "\n" + " " (:address/city (:vendor/address vendor)) ", " (:address/state (:vendor/address vendor)) " " (:address/zip (:vendor/address vendor))]] [:cell {:align :right} "Paid to:\n" "Amount:\n" @@ -166,7 +169,7 @@ [:cell {:colspan 5} [:paragraph check] [:paragraph vendor-name] - [:paragraph (:name (:bank company))] + [:paragraph (:name (:bank client))] [:paragraph paid-to] [:paragraph amount] [:paragraph date]]] @@ -180,9 +183,12 @@ (loop [[check & checks] checks] (when check (s3/put-object :bucket-name (:data-bucket env) - :key (:s3-key check) + :key (:payment/s3-key check) :input-stream (-> check - :pdf-data + :payment/pdf-data + (edn/read-string) + + make-check-pdf (io/make-input-stream {})) :metadata {:content-type "application/pdf"}) @@ -203,94 +209,104 @@ -(defmulti check-for-invoices (fn [invoices vendor-id vendors company bank-account type index invoice-amounts] +(defmulti invoices->entities (fn [invoices vendor-id client bank-account type index invoice-amounts] type)) +(defn invoice-payments [invoices invoice-amounts] + (->> (for [invoice invoices + :let [invoice-amount (invoice-amounts (:db/id invoice))]] + [{:invoice-payment/payment (-> invoice :invoice/vendor :db/id) + :invoice-payment/amount invoice-amount + :invoice-payment/invoice (:db/id invoice)} + [:pay (:db/id invoice) invoice-amount]]) + (reduce into []))) -(defmethod check-for-invoices "check" [invoices vendor-id vendors company bank-account type index invoice-amounts] +(defn base-payment [invoices vendor-id client bank-account type index invoice-amounts] + {:db/id (str vendor-id) + :payment/bank-account (:db/id bank-account) + :payment/amount (reduce + 0 (map (comp invoice-amounts :db/id) invoices)) + :payment/vendor vendor-id + :payment/client (:db/id client) + :payment/date (c/to-date (time/now)) + :payment/invoices (map :db/id invoices)}) + +(defmethod invoices->entities :payment-type/check [invoices vendor client bank-account type index invoice-amounts] (let [uuid (str (UUID/randomUUID)) - vendor (vendors vendor-id) - - memo (str "Invoice #'s: " (str/join ", " (map (fn [i] (str (:invoice/invoice-number i) "(" (invoice-amounts (:db/id i)) ")")) - invoices)))] + 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 {: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)}}}))] - {: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/bank-account (:db/id bank-account) - :payment/amount (reduce + 0 (map (comp invoice-amounts :db/id) invoices)) - :payment/memo memo - :payment/vendor (:db/id vendor) - :payment/client (:db/id company) - :payment/date (time/now) - #_#_:payment/pdf-data {: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)) - :company {:name (:name company) - :address (:address company) - :signature-file (:signature-file company) - :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)}}} - - :payment/invoices (map :db/id invoices)})) + (-> [] + (conj payment) + (conj [:inc (:db/id bank-account) :bank-account/check-number 1]) + (into (invoice-payments invoices invoice-amounts))))) -(defmethod check-for-invoices "debit" [invoices vendor-id vendors company bank-account type index invoice-amounts] - (let [vendor (vendors vendor-id)] - {:type "debit" - :bank-account-id (:db/id bank-account) - :amount (reduce + 0 (map (comp invoice-amounts :db/id) invoices)) - :status "cleared" - :memo "Debit" - :vendor-id (:db/id vendor) - :client-id (:db/id company) - :date (time/now) - :invoices (map :db/id invoices)})) +(defmethod invoices->entities :payment-type/debit [invoices vendor client bank-account type index invoice-amounts] + (let [payment (assoc (base-payment invoices vendor client bank-account type index invoice-amounts) + :payment/type :payment-type/debit + :payment/memo "Debit" + :payment/status :payment-status/cleared)] + (-> [] + (conj payment) + (into (invoice-payments invoices invoice-amounts))))) -(defmethod check-for-invoices "cash" [invoices vendor-id vendors company bank-account type index invoice-amounts] - (let [vendor (vendors vendor-id)] - {:type "cash" - :bank-account-id (:db/id bank-account) - :amount (reduce + 0 (map (comp invoice-amounts :db/id) invoices)) - :status "cleared" - :memo "Cash" - :vendor-id (:db/id vendor) - :client-id (:db/id company) - :date (time/now) - :invoices (map :db/id invoices)})) +(defmethod invoices->entities :payment-type/cash [invoices vendor client bank-account type index invoice-amounts] + (let [payment (assoc (base-payment invoices vendor client bank-account type index invoice-amounts) + :payment/type :payment-type/cash + :payment/memo "Cash" + :payment/status :payment-status/cleared)] + (-> [] + (conj payment) + (into (invoice-payments invoices invoice-amounts))))) (defn print-checks [invoice-payments client-id bank-account-id type] - - (let [client-id (Long/parseLong client-id) + (let [type (keyword "payment-type" (name type)) + client-id (Long/parseLong client-id) bank-account-id (Long/parseLong bank-account-id) invoices (d-invoices/get-multi (map :invoice-id invoice-payments)) client (d-clients/get-by-id client-id) vendors (by :db/id (d-vendors/get-graphql {})) + invoice-amounts (by (comp #(Long/parseLong %) :invoice-id) :amount invoice-payments) invoices-grouped-by-vendor (group-by (comp :db/id :invoice/vendor) invoices) bank-account (d-bank-accounts/get-by-id bank-account-id) - checks (-> (for [[[vendor-id invoices] index] (map vector invoices-grouped-by-vendor (range))] - [invoices (check-for-invoices invoices vendor-id vendors client bank-account type index invoice-amounts)]) + checks (->> (for [[[vendor-id invoices] index] (map vector invoices-grouped-by-vendor (range))] + (invoices->entities invoices (vendors vendor-id) client bank-account type index invoice-amounts)) + (reduce into []) doall)] - (clojure.pprint/pprint checks) - @(d/transact (d/connect uri) (map second checks)) + (when (= type :payment-type/check) + (make-pdfs (filter #(= :payment-type/check (:payment/type %)) checks))) + @(d/transact (d/connect uri) checks) - {:invoices [] #_(invoices/get-multi (map :id (mapcat first checks))) - :pdf-url nil #_(if (= type "check") - (merge-pdfs (map (comp :s3-key second) checks)) - nil)})) + {:invoices [] + :pdf-url (if (= type :payment-type/check) + (merge-pdfs (filter identity (map :payment/s3-key checks))) + nil)})) (defn get-payment-page [context args value] (let [args (assoc args :id (:id context)) diff --git a/src/cljs/auto_ap/views/pages/checks.cljs b/src/cljs/auto_ap/views/pages/checks.cljs index 432b2b19..180f4726 100644 --- a/src/cljs/auto_ap/views/pages/checks.cljs +++ b/src/cljs/auto_ap/views/pages/checks.cljs @@ -155,8 +155,8 @@ [:td (:name client)]) [:td (:name vendor)] [:td (cond - (= "cash" type) "Cash" - (= "debit" type) "Debit" + (= :cash type) "Cash" + (= :debit type) "Debit" :else check-number)] [:td (date->str date) ] [:td (gstring/format "$%.2f" amount )] diff --git a/src/cljs/auto_ap/views/pages/unpaid_invoices.cljs b/src/cljs/auto_ap/views/pages/unpaid_invoices.cljs index 177dd67e..d80c4172 100644 --- a/src/cljs/auto_ap/views/pages/unpaid_invoices.cljs +++ b/src/cljs/auto_ap/views/pages/unpaid_invoices.cljs @@ -164,6 +164,7 @@ (re-frame/reg-event-fx ::print-checks (fn [{:keys [db]} [_ bank-account-id type]] + (println type) (let [invoice-amounts (by :id :outstanding-balance (get-in db [::invoice-page :invoices]))] {:db (-> db @@ -203,7 +204,13 @@ :amount (invoice-amounts (:id x))}) (get-in db [::advanced-print-checks :invoices])) bank-account-id - (or type "check") + (cond (= type :check) + :check + (= type :cash) + :cash + + :else + :check) (:company db)) :on-success [::checks-created]}}))) @@ -809,11 +816,11 @@ [:div.dropdown-content (list (for [{:keys [id number name type]} (:bank-accounts current-company)] - (if (= "cash" type) - ^{:key id} [:a.dropdown-item {:on-click (dispatch-event [::print-checks id "cash"])} "With cash"] + (if (= :cash type) + ^{:key id} [:a.dropdown-item {:on-click (dispatch-event [::print-checks id :cash])} "With cash"] (list - ^{:key (str id "-check")} [:a.dropdown-item {:on-click (dispatch-event [::print-checks id "check"])} "Print checks from " name] - ^{:key (str id "-debit")} [:a.dropdown-item {:on-click (dispatch-event [::print-checks id "debit"])} "Debit from " name]))) + ^{:key (str id "-check")} [:a.dropdown-item {:on-click (dispatch-event [::print-checks id :check])} "Print checks from " name] + ^{: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))