From 2ea6ca576f2975d29629fc24519cc3569732f819 Mon Sep 17 00:00:00 2001 From: Bryce Covert Date: Thu, 23 Aug 2018 18:28:35 -0700 Subject: [PATCH] check printing --- src/clj/auto_ap/datomic.clj | 4 + src/clj/auto_ap/datomic/bank_accounts.clj | 36 +++ src/clj/auto_ap/datomic/clients.clj | 13 + src/clj/auto_ap/datomic/invoices.clj | 17 +- src/clj/auto_ap/graphql.clj | 19 +- src/clj/auto_ap/graphql/checks.clj | 280 +++++++++++++++- src/clj/auto_ap/routes/checks.clj | 302 +----------------- .../auto_ap/views/pages/unpaid_invoices.cljs | 6 +- 8 files changed, 362 insertions(+), 315 deletions(-) create mode 100644 src/clj/auto_ap/datomic/bank_accounts.clj diff --git a/src/clj/auto_ap/datomic.clj b/src/clj/auto_ap/datomic.clj index 5e81049f..986884a2 100644 --- a/src/clj/auto_ap/datomic.clj +++ b/src/clj/auto_ap/datomic.clj @@ -364,6 +364,10 @@ :db/valueType :db.type/ref :db/cardinality :db.cardinality/one :db/doc "The bank account that was used to pay"} + {:db/ident :payment/invoices + :db/valueType :db.type/ref + :db/cardinality :db.cardinality/many + :db/doc "Any invoices this payment is related to"} ;; enums {:db/ident :payment-status/pending} diff --git a/src/clj/auto_ap/datomic/bank_accounts.clj b/src/clj/auto_ap/datomic/bank_accounts.clj new file mode 100644 index 00000000..dfe04a77 --- /dev/null +++ b/src/clj/auto_ap/datomic/bank_accounts.clj @@ -0,0 +1,36 @@ +(ns auto-ap.datomic.bank-accounts + (:require [datomic.api :as d] + [auto-ap.datomic :refer [uri]] + [clj-time.coerce :as c] + [clojure.set :refer [rename-keys]])) + +(defn add-arg [query name value where & rest] + (let [query (-> query + (update :args conj value) + (update-in [:query :in] conj name) + (update-in [:query :where] conj where))] + (reduce #(update-in %1 [:query :where] conj %2) query rest))) + +(def default-read '(pull ?e [*])) + +(defn <-datomic [x] + (->> x + (map #(update % :bank-account/type :db/ident)) + )) + + + +(defn get-by-id [id] + (->> + (d/query (-> {:query {:find [default-read] + :in ['$] + :where []} + :args [(d/db (d/connect uri))]} + (add-arg '?e (cond-> id (string? id) Long/parseLong) ['?e]))) + (map first) + (<-datomic) + (first))) + + + + diff --git a/src/clj/auto_ap/datomic/clients.clj b/src/clj/auto_ap/datomic/clients.clj index 54662d65..8ae770b8 100644 --- a/src/clj/auto_ap/datomic/clients.clj +++ b/src/clj/auto_ap/datomic/clients.clj @@ -14,3 +14,16 @@ (map (fn [ba] (update ba :bank-account/type :db/ident )) bas))))))) + +(defn get-by-id [id] + (->> + (d/query (-> {:query {:find ['(pull ?e [*])] + :in ['$ '?e] + :where [['?e]]} + :args [(d/db (d/connect uri)) (cond-> id (string? id) Long/parseLong)]} + )) + (first) + (first) + #_(map first) + + #_(first))) diff --git a/src/clj/auto_ap/datomic/invoices.clj b/src/clj/auto_ap/datomic/invoices.clj index 44b5a013..dc4a7efa 100644 --- a/src/clj/auto_ap/datomic/invoices.clj +++ b/src/clj/auto_ap/datomic/invoices.clj @@ -19,7 +19,7 @@ (defn <-datomic [x] (->> x - (map first) + (map #(update % :invoice/date c/from-date)) (map #(update % :invoice/status :db/ident)) @@ -42,6 +42,7 @@ '[?c :client/original-id ?original-id]) (:status args) (add-arg '?status (keyword "invoice-status" (:status args)) '[?e :invoice/status ?status]))) + (map first) (<-datomic))) (defn sort-fn [args] @@ -75,9 +76,23 @@ :where []} :args [(d/db (d/connect uri))]} (add-arg '?e (cond-> id (string? id) Long/parseLong) ['?e]))) + (map first) (<-datomic) (first))) +(defn get-multi [ids] + (->> + (d/query {:query {:find [[default-read '...]] + :in ['$ ['?e '...]] + :where [['?e]]} + :args [(d/db (d/connect uri)) + (mapv #(cond-> % (string? %) Long/parseLong) + ids)] } + + + ) + (<-datomic))) + (defn find-conflicting [{:keys [:invoice/invoice-number :invoice/vendor :invoice/client]}] (->> (d/query (cond-> {:query {:find [default-read] diff --git a/src/clj/auto_ap/graphql.clj b/src/clj/auto_ap/graphql.clj index 9500e66d..e54bfbc6 100644 --- a/src/clj/auto_ap/graphql.clj +++ b/src/clj/auto_ap/graphql.clj @@ -18,7 +18,6 @@ [auto-ap.datomic.vendors :as d-vendors] [auto-ap.db.users :as users] [auto-ap.db.checks :as checks] - [auto-ap.routes.checks :as rchecks] [auto-ap.graphql.users :as gq-users] [auto-ap.graphql.checks :as gq-checks] [auto-ap.graphql.expense-accounts :as expense-accounts] @@ -291,9 +290,9 @@ :mutations {:print_checks {:type :check_result :args {:invoice_payments {:type '(list :invoice_payment_amount)} - :bank_account_id {:type 'Int} + :bank_account_id {:type 'String} :type {:type 'String} - :company_id {:type 'Int}} + :client_id {:type 'String}} :resolve :mutation/print-checks} :add_handwritten_check {:type :check_result @@ -454,14 +453,14 @@ (defn print-checks [context args value] - (assert-can-see-company (:id context) (:company_id args)) + (assert-can-see-company (:id context) (:client_id args)) (->graphql - (rchecks/print-checks (map (fn [i] {:invoice-id (:invoice_id i) - :amount (:amount i)}) - (:invoice_payments args)) - (:company_id args) - (:bank_account_id args) - (:type args)))) + (gq-checks/print-checks (map (fn [i] {:invoice-id (:invoice_id i) + :amount (:amount i)}) + (:invoice_payments args)) + (:client_id args) + (:bank_account_id args) + (:type args)))) diff --git a/src/clj/auto_ap/graphql/checks.clj b/src/clj/auto_ap/graphql/checks.clj index aa66a14f..5b9e1875 100644 --- a/src/clj/auto_ap/graphql/checks.clj +++ b/src/clj/auto_ap/graphql/checks.clj @@ -8,13 +8,289 @@ [auto-ap.db.invoices-checks :as invoices-checks] [auto-ap.db.checks :as checks] [auto-ap.datomic.checks :as d-checks] + [auto-ap.datomic.invoices :as d-invoices] + [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.db.invoices-checks :as invoices-checks] [auto-ap.db.vendors :as vendors] [auto-ap.db.invoices :as invoices] + [auto-ap.db.utils :refer [query]] [auto-ap.datomic :refer [uri]] [auto-ap.utils :refer [by]] + [auto-ap.numeric :refer [num->words]] + [config.core :refer [env]] [auto-ap.db.companies :as companies] - [auto-ap.time :refer [parse normal-date iso-date]])) + [auto-ap.time :refer [parse normal-date iso-date local-now]] + [amazonica.aws.s3 :as s3] + [clojure.string :as str] + [clj-pdf.core :as pdf] + [clj-time.format :as f] + [clj-time.core :as time] + [clojure.java.io :as io]) + (:import [java.text DecimalFormat] + [java.util UUID] + [java.io ByteArrayOutputStream])) + +(def parser (f/formatter "MM/dd/YYYY")) +(defn date->str [t] + (f/unparse parser t)) + +(defn distribute [nums] + (let [sum (reduce + 0 nums)] + (map #(* 100 (/ % sum)) nums))) + +(defn split-memo [memo] + (str/join "\n" + (reverse + (take 6 + (concat + (reduce + (fn [[line & rest ] word] + (let [line (or line "")] + (if (> (+ (count line) (count word)) 50) + (concat [word line] rest) + (concat [(str line " " word)] + rest)))) + [] + (str/split memo #" ")) + (repeat "")))))) + +(defn make-check-pdf [check] + (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 + 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] + [:cell {:colspan 3 } [:paragraph {:leading 14} name "\n" street1 "\n" (str city ", " state " " zip)] ]) + (let [{{:keys [name acct]} :bank} company] + [:cell {:colspan 7 :align :center} [:paragraph {:style :bold} name] [:paragraph {:size 8 :leading 8} acct]]) + [:cell {:colspan 2 :size 13} + check]] + + [[:cell {:colspan 9}] + [:cell {:colspan 3 :leading -10} date]] + [[:cell {:colspan 12 :size 14}] + ] + + [[:cell {:size 13 :leading 13} "PAY"] + [:cell {:size 8 :leading 8 } "TO THE ORDER OF"] + [:cell {:colspan 7} (if (seq print-as) + print-as + vendor-name)] + [:cell {:colspan 3} amount]] + + [[:cell {}] + [:cell {:colspan 8} (str " -- " word-amount " " (str/join "" (take (max + 2 + (- 97 + (count word-amount))) + (repeat "-")))) + [:line {:line-width 0.15 :color [50 50 50]}]] + [:cell {:colspan 3}]] + + + [[: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) + [:image { :top-margin 90 :xscale 0.30 :yscale 0.30 :align :center} + + (:signature-file company)] + [:spacer])]] + + #_[ + #_[:cell {:colspan 5} #_memo ] + #_[:cell {:colspan 6}]] + + [[: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")]]] + + [[:cell {:colspan 12 :leading 18} [:spacer]]] + + [[:cell] + (into + [:cell {:colspan 9}] + (let [{:keys [name] + {:keys [street1 city state zip bank]} :address} company] + (list + [:paragraph " " name] + [:paragraph " " street1] + [:paragraph " " city ", " state " " zip] + ))) + [:cell {:colspan 2 :size 13} + check]] + + [[:cell {:colspan 12 :leading 74} [:spacer]]] + + [[:cell] + [:cell {:colspan 5} [:paragraph + " " vendor-name "\n" + " " (:street1 (:address vendor)) "\n" + " " (:city (:address vendor)) ", " (:state (:address vendor)) " " (:zip (:address vendor))]] + [:cell {:align :right} + "Paid to:\n" + "Amount:\n" + "Date:\n"] + + [:cell {:colspan 5} + [:paragraph paid-to] + [:paragraph amount] + [:paragraph date]]] + + [[:cell {:colspan 3} "Memo:"] + [:cell {:colspan 9} memo]] + + [[:cell {:colspan 12} [:spacer]]] + [[:cell {:colspan 12} [:spacer]]] + [[:cell {:colspan 12} [:spacer]]] + [[:cell {:colspan 12} [:spacer]]] + + [[:cell {:colspan 5}] + [:cell {:align :right :colspan 2} + "Check:\n" + "Vendor:\n" + "Bank Account:\n" + "Paid To:\n" + "Amount:\n" + "Date:\n"] + + [:cell {:colspan 5} + [:paragraph check] + [:paragraph vendor-name] + [:paragraph (:name (:bank company))] + [:paragraph paid-to] + [:paragraph amount] + [:paragraph date]]] + [[:cell {:colspan 3} "Memo:"] + [:cell {:colspan 9} memo]] + ])] + output-stream) + (.toByteArray output-stream))) + +(defn make-pdfs [checks] + (loop [[check & checks] checks] + (when check + (s3/put-object :bucket-name (:data-bucket env) + :key (:s3-key check) + :input-stream (-> check + :pdf-data + make-check-pdf + (io/make-input-stream {})) + :metadata {:content-type "application/pdf"}) + (recur checks)))) + +(defn merge-pdfs [keys] + (let [merged-pdf-stream (java.io.ByteArrayOutputStream.) + uuid (str (UUID/randomUUID))] + (apply pdf/collate (concat [{:size :letter} merged-pdf-stream] (->> keys + (map #(s3/get-object (:data-bucket env) %)) + (map :input-stream)))) + (s3/put-object :bucket-name (:data-bucket env) + :key (str "merged-checks/" uuid ".pdf") + :input-stream (io/make-input-stream (.toByteArray merged-pdf-stream) {}) + :metadata {:content-length (count (.toByteArray merged-pdf-stream)) + :content-type "application/pdf"}) + (str "http://" (:data-bucket env) ".s3-website-us-east-1.amazonaws.com/merged-checks/" uuid ".pdf"))) + + + +(defmulti check-for-invoices (fn [invoices vendor-id vendors company bank-account type index invoice-amounts] + type)) + +(defmethod check-for-invoices "check" [invoices vendor-id vendors company 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)))] + + {: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)})) + +(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 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)})) + +(defn print-checks [invoice-payments client-id bank-account-id type] + + (let [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)]) + doall)] + + (clojure.pprint/pprint checks) + @(d/transact (d/connect uri) (map second 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)})) (defn get-payment-page [context args value] (let [args (assoc args :id (:id context)) @@ -55,7 +331,7 @@ (let [check (d-checks/get-by-id id)] (assert (or (= :payment-status/pending (:payment/status check)) (#{:payment-type/cash :payment-type/debit} (:payment/type check)))) - (assert-can-see-company (:id context) (:company-id check)) + (assert-can-see-company (:id context) (:client-id check)) (let [removing-payments (mapcat (fn [x] (let [invoice (:invoice-payment/invoice x) new-balance (+ (:invoice/outstanding-balance invoice) diff --git a/src/clj/auto_ap/routes/checks.clj b/src/clj/auto_ap/routes/checks.clj index e35e9f96..d8c3c422 100644 --- a/src/clj/auto_ap/routes/checks.clj +++ b/src/clj/auto_ap/routes/checks.clj @@ -1,305 +1,9 @@ (ns auto-ap.routes.checks - (:require [auto-ap.db.companies :as companies] - [auto-ap.db.vendors :as vendors] - [auto-ap.db.invoices :as invoices] - [auto-ap.graphql.utils :refer [assert-can-see-company]] - [auto-ap.utils :refer [by]] - [auto-ap.numeric :refer [num->words]] - [auto-ap.db.checks :as checks] - [auto-ap.db.invoices-checks :as invoices-checks] - [auto-ap.db.utils :refer [query]] - [auto-ap.parse :as parse] - [amazonica.aws.s3 :as s3] + (:require [hiccup.core :refer [html]] [auto-ap.routes.utils :refer [wrap-secure]] - [auto-ap.time :refer [local-now]] - [config.core :refer [env]] - [compojure.core :refer [GET POST context defroutes - wrap-routes]] - [clojure.string :as str] - [clj-pdf.core :as pdf] - [clj-time.format :as f] - [clj-time.core :as time] - [clojure.java.io :as io]) - (:import [java.text DecimalFormat] - [java.util UUID] - [java.io ByteArrayOutputStream])) - -(def parser (f/formatter "MM/dd/YYYY")) -(defn date->str [t] - (f/unparse parser t)) - -(defn distribute [nums] - (let [sum (reduce + 0 nums)] - (map #(* 100 (/ % sum)) nums))) - -(defn split-memo [memo] - (str/join "\n" - (reverse - (take 6 - (concat - (reduce - (fn [[line & rest ] word] - (let [line (or line "")] - (if (> (+ (count line) (count word)) 50) - (concat [word line] rest) - (concat [(str line " " word)] - rest)))) - [] - (str/split memo #" ")) - (repeat "")))))) - -(defn make-check-pdf [check] - (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 - 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] - [:cell {:colspan 3 } [:paragraph {:leading 14} name "\n" street1 "\n" (str city ", " state " " zip)] ]) - (let [{{:keys [name acct]} :bank} company] - [:cell {:colspan 7 :align :center} [:paragraph {:style :bold} name] [:paragraph {:size 8 :leading 8} acct]]) - [:cell {:colspan 2 :size 13} - check]] - - [[:cell {:colspan 9}] - [:cell {:colspan 3 :leading -10} date]] - [[:cell {:colspan 12 :size 14}] - ] - - [[:cell {:size 13 :leading 13} "PAY"] - [:cell {:size 8 :leading 8 } "TO THE ORDER OF"] - [:cell {:colspan 7} (if (seq print-as) - print-as - vendor-name)] - [:cell {:colspan 3} amount]] - - [[:cell {}] - [:cell {:colspan 8} (str " -- " word-amount " " (str/join "" (take (max - 2 - (- 97 - (count word-amount))) - (repeat "-")))) - [:line {:line-width 0.15 :color [50 50 50]}]] - [:cell {:colspan 3}]] - - - [[: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) - [:image { :top-margin 90 :xscale 0.30 :yscale 0.30 :align :center} - - (:signature-file company)] - [:spacer])]] - - #_[ - #_[:cell {:colspan 5} #_memo ] - #_[:cell {:colspan 6}]] - - [[: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")]]] - - [[:cell {:colspan 12 :leading 18} [:spacer]]] - - [[:cell] - (into - [:cell {:colspan 9}] - (let [{:keys [name] - {:keys [street1 city state zip bank]} :address} company] - (list - [:paragraph " " name] - [:paragraph " " street1] - [:paragraph " " city ", " state " " zip] - ))) - [:cell {:colspan 2 :size 13} - check]] - - [[:cell {:colspan 12 :leading 74} [:spacer]]] - - [[:cell] - [:cell {:colspan 5} [:paragraph - " " vendor-name "\n" - " " (:street1 (:address vendor)) "\n" - " " (:city (:address vendor)) ", " (:state (:address vendor)) " " (:zip (:address vendor))]] - [:cell {:align :right} - "Paid to:\n" - "Amount:\n" - "Date:\n"] - - [:cell {:colspan 5} - [:paragraph paid-to] - [:paragraph amount] - [:paragraph date]]] - - [[:cell {:colspan 3} "Memo:"] - [:cell {:colspan 9} memo]] - - [[:cell {:colspan 12} [:spacer]]] - [[:cell {:colspan 12} [:spacer]]] - [[:cell {:colspan 12} [:spacer]]] - [[:cell {:colspan 12} [:spacer]]] - - [[:cell {:colspan 5}] - [:cell {:align :right :colspan 2} - "Check:\n" - "Vendor:\n" - "Bank Account:\n" - "Paid To:\n" - "Amount:\n" - "Date:\n"] - - [:cell {:colspan 5} - [:paragraph check] - [:paragraph vendor-name] - [:paragraph (:name (:bank company))] - [:paragraph paid-to] - [:paragraph amount] - [:paragraph date]]] - [[:cell {:colspan 3} "Memo:"] - [:cell {:colspan 9} memo]] - ])] - output-stream) - (.toByteArray output-stream))) - -(defn make-pdfs [checks] - (loop [[check & checks] checks] - (when check - (s3/put-object :bucket-name (:data-bucket env) - :key (:s3-key check) - :input-stream (-> check - :pdf-data - make-check-pdf - (io/make-input-stream {})) - :metadata {:content-type "application/pdf"}) - (recur checks)))) - -(defn merge-pdfs [keys] - (let [merged-pdf-stream (java.io.ByteArrayOutputStream.) - uuid (str (UUID/randomUUID))] - (apply pdf/collate (concat [{:size :letter} merged-pdf-stream] (->> keys - (map #(s3/get-object (:data-bucket env) %)) - (map :input-stream)))) - (s3/put-object :bucket-name (:data-bucket env) - :key (str "merged-checks/" uuid ".pdf") - :input-stream (io/make-input-stream (.toByteArray merged-pdf-stream) {}) - :metadata {:content-length (count (.toByteArray merged-pdf-stream)) - :content-type "application/pdf"}) - (str "http://" (:data-bucket env) ".s3-website-us-east-1.amazonaws.com/merged-checks/" uuid ".pdf"))) - - - -(defmulti check-for-invoices (fn [invoices vendor-id vendors company bank-account type index invoice-amounts] - type)) - -(defmethod check-for-invoices "check" [invoices vendor-id vendors company 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-number i) "(" (invoice-amounts (:id i)) ")")) - invoices)))] - {:s3-uuid uuid - :s3-key (str "checks/" uuid ".pdf") - :s3-url (str "http://" (:data-bucket env) ".s3-website-us-east-1.amazonaws.com/checks/" uuid ".pdf") - :check-number (+ index (:check-number bank-account)) - :type "check" - :bank-account-id (:id bank-account) - :amount (reduce + 0 (map (comp invoice-amounts :id) invoices)) - :memo memo - :vendor-id (:id vendor) - :company-id (:id company) - :date (time/now) - :pdf-data {:vendor vendor - :paid-to (:name vendor) - :amount (reduce + 0 (map (comp invoice-amounts :id) invoices)) - :check (str (+ index (: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-name bank-account) - :acct (:bank-code bank-account) - :routing (:routing bank-account) - :acct-number (:number bank-account)}}} - - :invoices (map :id invoices)})) - -(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 (:id bank-account) - :amount (reduce + 0 (map (comp invoice-amounts :id) invoices)) - :status "cleared" - :memo "Debit" - :vendor-id (:id vendor) - :company-id (:id company) - :date (time/now) - :invoices (map :id invoices)})) - -(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 (:id bank-account) - :amount (reduce + 0 (map (comp invoice-amounts :id) invoices)) - :status "cleared" - :memo "Cash" - :vendor-id (:id vendor) - :company-id (:id company) - :date (time/now) - :invoices (map :id invoices)})) - -(defn print-checks [invoice-payments company-id bank-account-id type] - (let [invoices (invoices/get-multi (map :invoice-id invoice-payments)) - company (companies/get-by-id company-id) - vendors (by :id (vendors/get-all)) - invoice-amounts (by :invoice-id :amount invoice-payments) - invoices-grouped-by-vendor (group-by :vendor-id invoices) - bank-account (first (filter #(= (:id %) bank-account-id) (:bank-accounts company))) - checks (-> (for [[[vendor-id invoices] index] (map vector invoices-grouped-by-vendor (range))] - [invoices (checks/insert! (check-for-invoices invoices vendor-id vendors company bank-account type index invoice-amounts))]) - doall) - invoice-checks (invoices-checks/insert-multi! - (mapcat - (fn [[invoices check]] - (map - (fn [i] - {:invoice-id (:id i) - :check-id (:id check) - :amount (invoice-amounts (:id i))}) - invoices)) - checks)) - updated-company (if (= type "check" ) - (update company :bank-accounts - (fn [bas] - (map (fn [ba] - (if (= bank-account-id (:id ba)) - (update ba :check-number + (count checks)) - ba)) - bas))) - company)] - - (when (= type "check") - (make-pdfs (map second checks)) - (companies/upsert company-id updated-company)) - (doseq [{:keys [invoice-id amount]} invoice-payments] - (invoices/apply-payment invoice-id amount)) - {:invoices (invoices/get-multi (map :id (mapcat first checks))) - :pdf-url (if (= type "check") - (merge-pdfs (map (comp :s3-key second) checks)) - nil)})) - - +[compojure.core :refer [GET POST context defroutes + wrap-routes]])) (defroutes routes (wrap-routes (context "/checks" []) diff --git a/src/cljs/auto_ap/views/pages/unpaid_invoices.cljs b/src/cljs/auto_ap/views/pages/unpaid_invoices.cljs index de41b5b9..177dd67e 100644 --- a/src/cljs/auto_ap/views/pages/unpaid_invoices.cljs +++ b/src/cljs/auto_ap/views/pages/unpaid_invoices.cljs @@ -149,7 +149,7 @@ (assoc-in i f v) i)))))) -(defn print-checks-query [invoice-payments bank-account-id type company-id] +(defn print-checks-query [invoice-payments bank-account-id type client-id] {:venia/operation {:operation/type :mutation :operation/name "PrintChecks"} @@ -157,8 +157,8 @@ {:invoice_payments invoice-payments :type type :bank_account_id bank-account-id - :company_id company-id} - [[:invoices [:id :outstanding-balance [:checks [:amount [:check [:amount :s3_url :check_number ]]]]]] + :client_id client-id} + [[:invoices [:id :outstanding-balance [:payments [:amount [:payment [:amount :s3_url :check_number ]]]]]] :pdf_url]]]}) (re-frame/reg-event-fx