This commit is contained in:
2025-10-07 21:54:21 -07:00
16 changed files with 607 additions and 191 deletions

View File

@@ -241,7 +241,7 @@
([db client-id ^java.util.Date end ^java.util.Date start]
(for [running-balance-set (account-sets db client-id)
:let [{:keys [account-id client-id location current-balance debits credits count]}
:let [{:keys [account-id client-id location current-balance debits credits count sample]}
(->> running-balance-set
(filter (fn until-end [^Line l]
(let [^java.util.Date d (.-date l)]
@@ -259,13 +259,15 @@
:current-balance (.-running-balance l)
:account-id (.-account-id l)
:client-id (.-client-id l)
:location (.-location l)))
:location (.-location l)
:sample (.-id l)))
(transient {:count 0
:debits 0.0
:credits 0.0
:current-balance 0.0})))]
:when client-id]
[client-id account-id location debits credits current-balance count])))
(do
[client-id account-id location debits credits current-balance count sample]))))
(comment

File diff suppressed because one or more lines are too long

View File

@@ -1,29 +1,27 @@
(ns auto-ap.graphql.checks
(:require
[amazonica.aws.s3 :as s3]
[auto-ap.datomic :refer [conn remove-nils pull-many audit-transact]]
[auto-ap.datomic :refer [audit-transact conn pull-many remove-nils]]
[auto-ap.datomic.accounts :as a]
[auto-ap.datomic.bank-accounts :as d-bank-accounts]
[auto-ap.datomic.checks :as d-checks]
[auto-ap.datomic.clients :as d-clients]
[auto-ap.datomic.invoices :as d-invoices]
[auto-ap.datomic.transactions :as d-transactions]
[auto-ap.logging :as alog]
[auto-ap.datomic.vendors :as d-vendors]
[auto-ap.graphql.utils
:refer [->graphql
<-graphql
attach-tracing-resolvers
assert-admin
assert-can-see-client
assert-failure
assert-not-locked
:refer [->graphql <-graphql assert-admin assert-can-see-client
assert-failure assert-not-locked attach-tracing-resolvers
enum->keyword]]
[auto-ap.logging :as alog]
[auto-ap.numeric :refer [num->words]]
[auto-ap.time :refer [iso-date local-now parse]]
[auto-ap.solr :as solr]
[auto-ap.time :refer [iso-date parse]]
[auto-ap.time :as atime]
[auto-ap.utils :refer [by dollars-0?]]
[clj-pdf.core :as pdf]
[clj-time.coerce :as c]
[clj-time.coerce :as coerce]
[clj-time.core :as time]
[clj-time.format :as f]
[clojure.edn :as edn]
@@ -31,12 +29,9 @@
[clojure.set :as set]
[clojure.string :as str]
[com.brunobonacci.mulog :as mu]
[com.walmartlabs.lacinia.util :refer [attach-resolvers]]
[config.core :refer [env]]
[clj-time.coerce :as coerce]
[datomic.api :as dc]
[digest]
[auto-ap.solr :as solr])
[digest])
(:import
(java.io ByteArrayOutputStream)
(java.text DecimalFormat)
@@ -215,7 +210,7 @@
(str "https://" (:data-bucket env) "/merged-checks/" uuid ".pdf")))
#_{:clj-kondo/ignore [:unused-binding]}
(defmulti invoices->entities (fn [invoices vendor-id client bank-account type index invoice-amounts]
(defmulti invoices->entities (fn [invoices vendor-id client bank-account type index invoice-amounts date]
type))
(defn invoice-payments [invoices invoice-amounts]
(->> (for [invoice invoices
@@ -228,16 +223,16 @@
[:pay (:db/id invoice) invoice-amount]])
(reduce into [])))
(defn base-payment [invoices vendor client bank-account _ _ invoice-amounts]
(defn base-payment [invoices vendor client bank-account _ _ invoice-amounts date]
{:db/id (str (:db/id vendor))
:payment/bank-account (:db/id bank-account)
:payment/amount (reduce + 0 (map (comp invoice-amounts :db/id) invoices))
:payment/vendor (:db/id vendor)
:payment/client (:db/id client)
:payment/date (c/to-date (time/now))
:payment/date (c/to-date (or date (time/now)))
:payment/invoices (map :db/id invoices)})
(defmethod invoices->entities :payment-type/check [invoices vendor client bank-account type index invoice-amounts]
(defmethod invoices->entities :payment-type/check [invoices vendor client bank-account type index invoice-amounts date]
(when (<= (->> invoices
(map (comp invoice-amounts :db/id))
(reduce + 0.0))
@@ -245,51 +240,52 @@
(throw (ex-info "The selected invoices do not have an outstanding balance."
{:validation-error "The selected invoices do not have an outstanding balance."})))
(let [uuid (str (UUID/randomUUID))
date (or date (time/now))
memo (str "Invoice #'s: "
(str/join ", "
(map (fn [i]
(str (:invoice/invoice-number i) "(" (invoice-amounts (:db/id i)) ")"))
invoices)))
base-payment (base-payment invoices vendor client bank-account type index invoice-amounts)
base-payment (base-payment invoices vendor client bank-account type index invoice-amounts date)
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 "https://" (:data-bucket env) "/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 (dissoc client :client/bank-accounts :client/square-integration-status :client/ezcater-locations :client/locked-until :client/emails :client/square-auth-token :client/square-locations)
:bank-account (dissoc bank-account :bank-account/start-date :bank-account/integration-status)
#_#_: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)}}})))]
(cond-> (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 "https://" (:data-bucket env) "/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 date)
:client (dissoc client :client/bank-accounts :client/square-integration-status :client/ezcater-locations :client/locked-until :client/emails :client/square-auth-token :client/square-locations)
:bank-account (dissoc bank-account :bank-account/start-date :bank-account/integration-status)
#_#_: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)
(into (invoice-payments invoices invoice-amounts)))))
(defmethod invoices->entities :payment-type/debit [invoices vendor client bank-account type index invoice-amounts]
(defmethod invoices->entities :payment-type/debit [invoices vendor client bank-account type index invoice-amounts date]
(when (<= (->> invoices
(map (comp invoice-amounts :db/id))
(reduce + 0.0))
0.001)
(throw (ex-info "The selected invoices do not have an outstanding balance."
{:validation-error "The selected invoices do not have an outstanding balance."})))
(let [payment (assoc (base-payment invoices vendor client bank-account type index invoice-amounts)
(let [payment (assoc (base-payment invoices vendor client bank-account type index invoice-amounts date)
:payment/type :payment-type/debit
:payment/memo (str "Debit Invoice #'s: "
(str/join ", "
@@ -310,14 +306,14 @@
(throw (ex-info "The selected invoices do not have an outstanding balance."
{:validation-error "The selected invoices do not have an outstanding balance."}))))
(defmethod invoices->entities :payment-type/credit [invoices vendor client bank-account type index invoice-amounts]
(defmethod invoices->entities :payment-type/credit [invoices vendor client bank-account type index invoice-amounts date]
(when (>= (->> invoices
(map (comp invoice-amounts :db/id))
(reduce + 0.0))
0.001)
(throw (ex-info "The selected invoices do not have an outstanding balance."
{:validation-error "The selected invoices do not have an outstanding balance."})))
(let [payment (assoc (base-payment invoices vendor client bank-account type index invoice-amounts)
(let [payment (assoc (base-payment invoices vendor client bank-account type index invoice-amounts date)
:payment/type :payment-type/credit
:payment/memo (str "Debit Invoice #'s: "
(str/join ", "
@@ -329,14 +325,14 @@
(conj payment)
(into (invoice-payments invoices invoice-amounts)))))
(defmethod invoices->entities :payment-type/cash [invoices vendor client bank-account type index invoice-amounts]
(defmethod invoices->entities :payment-type/cash [invoices vendor client bank-account type index invoice-amounts date]
(when (<= (->> invoices
(map (comp invoice-amounts :db/id))
(reduce + 0.0))
0.001)
(throw (ex-info "The selected invoices do not have an outstanding balance."
{:validation-error "The selected invoices do not have an outstanding balance."})))
(let [base-payment (base-payment invoices vendor client bank-account type index invoice-amounts)
(let [base-payment (base-payment invoices vendor client bank-account type index invoice-amounts date)
transaction-id (str (UUID/randomUUID))
memo (str "Cash Invoice #'s: "
(str/join ", "
@@ -381,50 +377,51 @@
:client-id client-id
:invoices (map :invoice/invoice-number invoices)}))))
(defn print-checks-internal [invoice-payments client-id bank-account-id type id]
(let [type (keyword "payment-type" (name type))
invoices (d-invoices/get-multi (map :invoice-id invoice-payments))
client (d-clients/get-by-id client-id)
invoice-amounts (by :invoice-id :amount invoice-payments)
invoices-grouped-by-vendor (group-by (comp :db/id :invoice/vendor) invoices)
vendors (->> (pull-many (dc/db conn) d-vendors/default-read (keys invoices-grouped-by-vendor))
(by :db/id))
bank-account (d-bank-accounts/get-by-id bank-account-id)
_ (validate-belonging client-id invoices bank-account)
_ (when (and (nil? (:bank-account/check-number bank-account))
(= type :payment-type/check))
(let [message (str "The bank account " (:bank-account/name bank-account) " does not have a starting check number. Please ask the integreat staff to initialize it.")]
(throw (ex-info message
{:validation-error message}))))
checks (mu/trace ::build-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))
checks (if (= type :payment-type/check)
(conj checks [:plus (:db/id bank-account) :bank-account/check-number (count invoices-grouped-by-vendor)])
checks)]
(when (= type :payment-type/check)
(mu/trace ::making-pdfs [:checks checks]
(make-pdfs (filter #(and (= :payment-type/check (:payment/type %))
(> (:payment/amount %) 0.0))
checks))))
(let [result (audit-transact (map #(if (map? %)
(dissoc % :payment/pdf-data)
%) checks) id)]
(try
(doseq [[_ i] (:tempids result)]
(solr/touch-with-ledger i))
(catch Exception e
(alog/error ::cant-save-solr
:error e))))
{:invoices (d-invoices/get-multi (map :invoice-id invoice-payments))
:pdf-url (if (= type :payment-type/check)
(mu/trace ::merge-pdfs
[]
(merge-pdfs (filter identity (map :payment/s3-key checks))))
nil)}))
(defn print-checks-internal
([invoice-payments client-id bank-account-id type id date]
(let [type (keyword "payment-type" (name type))
invoices (d-invoices/get-multi (map :invoice-id invoice-payments))
client (d-clients/get-by-id client-id)
invoice-amounts (by :invoice-id :amount invoice-payments)
invoices-grouped-by-vendor (group-by (comp :db/id :invoice/vendor) invoices)
vendors (->> (pull-many (dc/db conn) d-vendors/default-read (keys invoices-grouped-by-vendor))
(by :db/id))
bank-account (d-bank-accounts/get-by-id bank-account-id)
_ (validate-belonging client-id invoices bank-account)
_ (when (and (nil? (:bank-account/check-number bank-account))
(= type :payment-type/check))
(let [message (str "The bank account " (:bank-account/name bank-account) " does not have a starting check number. Please ask the integreat staff to initialize it.")]
(throw (ex-info message
{:validation-error message}))))
checks (mu/trace ::build-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 date))
(reduce into [])
doall))
checks (if (= type :payment-type/check)
(conj checks [:plus (:db/id bank-account) :bank-account/check-number (count invoices-grouped-by-vendor)])
checks)]
(when (= type :payment-type/check)
(mu/trace ::making-pdfs [:checks checks]
(make-pdfs (filter #(and (= :payment-type/check (:payment/type %))
(> (:payment/amount %) 0.0))
checks))))
(let [result (audit-transact (map #(if (map? %)
(dissoc % :payment/pdf-data)
%) checks) id)]
(try
(doseq [[_ i] (:tempids result)]
(solr/touch-with-ledger i))
(catch Exception e
(alog/error ::cant-save-solr
:error e))))
{:invoices (d-invoices/get-multi (map :invoice-id invoice-payments))
:pdf-url (if (= type :payment-type/check)
(mu/trace ::merge-pdfs
[]
(merge-pdfs (filter identity (map :payment/s3-key checks))))
nil)})))
(defn get-payment-page [context args _]
(let [[payments checks-count] (d-checks/get-graphql (-> args
@@ -474,14 +471,15 @@
bank-account
:payment-type/check
0
invoice-payment-lookup)]
invoice-payment-lookup
(parse (:date args) iso-date))]
(let [result (audit-transact
(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/date (c/to-date (parse (:date args) iso-date)))]
(invoice-payments invoices invoice-payment-lookup))
(:id context))]
(doseq [[_ i] (:tempids result)]
@@ -586,7 +584,8 @@
(:client_id args)
(:bank_account_id args)
(:type args)
(:id context))))
(:id context)
(time/now))))
(defn pay-invoices-from-balance [context {invoices :invoices
client-id :client_id} _]

View File

@@ -212,7 +212,8 @@
client_id
bank-account-id
type
(:id context))
(:id context)
(time/now))
u/->graphql))))
(defn edit-invoice [context {{:keys [id due invoice_number vendor_id total date expense_accounts scheduled_payment] :as in} :invoice} _]

View File

@@ -75,8 +75,10 @@
(assoc :per-page Integer/MAX_VALUE)
(d-transactions/raw-graphql-ids )
:ids)
specific-ids (d-transactions/filter-ids (:ids args))]
(into (set ids) specific-ids)))
specific-ids (d-transactions/filter-ids (seq (:ids args)))]
(if (seq (:ids args))
(set specific-ids)
(set ids))))
(defn all-ids-not-locked [all-ids]
(->> all-ids
@@ -188,10 +190,12 @@
(audit-transact-batch
(map (fn [t]
(let [locations (client->locations (-> t :transaction/client :db/id))]
[:upsert-transaction (cond-> t
(:approval_status args) (assoc :transaction/approval-status (enum->keyword (:approval_status args) "transaction-approval-status"))
(:vendor args) (assoc :transaction/vendor (:vendor args))
(seq (:accounts args)) (assoc :transaction/accounts (maybe-code-accounts t (:accounts args) locations)))]))
(doto
[:upsert-transaction (cond-> t
(:approval_status args) (assoc :transaction/approval-status (enum->keyword (:approval_status args) "transaction-approval-status"))
(:vendor args) (assoc :transaction/vendor (:vendor args))
(seq (:accounts args)) (assoc :transaction/accounts (maybe-code-accounts t (:accounts args) locations)))]
clojure.pprint/pprint)))
transactions)
(:id context))
{:message (str "Successfully coded " (count all-ids) " transactions.")}))

View File

@@ -62,7 +62,7 @@
:input-stream (io/input-stream filename)
:metadata {:content-type "application/pdf"
:content-length (.length (io/file filename))})
(let [imports (->> (parse/parse-file filename filename)
(let [imports (->> (parse/parse-file filename filename :allow-glimpse? false)
(map #(assoc %
:source-url (str "https://" (:data-bucket env)
"/"

View File

@@ -34,14 +34,14 @@
([text full-text template]
(when (and template
(or (not (:multi-match? template))
(re-find (:multi-match? template) text )))
(re-find (:multi-match? template) text)))
[(->> template
:extract
(reduce-kv
(fn [result k v]
(let [value (some-> (or (first (map second (re-seq v text)))
(first (map second (re-seq v full-text))))
str/trim )
str/trim)
[value-parser parser-params] (-> template :parser k)]
(assoc result k (try
(u/parse-value value-parser parser-params value)
@@ -61,14 +61,20 @@
(extract-template text)))
(defmulti parse-file (fn [_ filename] (.toLowerCase (last (str/split filename #"\." )))))
(defmulti parse-file
"Parses a file based on its extension. Accepts options as additional arguments.
Options:
- :allow-glimpse? (default false) - If true, allows parsing a glimpse of the file."
(fn [_ filename & opts]
(.toLowerCase (last (str/split filename #"\.")))))
(defn invoke-glimpse2 [f]
(let [result (slurp (:payload (lambda/invoke {:client-config {:request-timeout 120000
:socket-timeout 120000}} {:function-name "glimpse2" :payload
(json/write-str
(alog/peek ::x {"url" (str "https://" "data.prod.app.integreatconsult.com" "/" f ) }))
})))]
(json/write-str
(alog/peek ::x {"url" (str "https://" "data.prod.app.integreatconsult.com" "/" f)}))})))]
(alog/info ::glimpse2-payload :payload result)
(-> result
@@ -81,46 +87,46 @@
(s3/put-object {:bucket-name "data.prod.app.integreatconsult.com"
:key tmp-key
:input-stream f}))
is (invoke-glimpse2 tmp-key) ]
is (invoke-glimpse2 tmp-key)]
(alog/peek ::glimpse2-result is)
(for [i is]
{:date (u/parse-value :clj-time "yyyy-MM-dd" (str/trim (get i "date")))
:customer-identifier (get i "customer_identifier")
:account-number (not-empty (get i "account_number"))
:vendor-search (get i "vendor_identifier")
:vendor-code (-> (vendors/best-match (get i "vendor_identifier") )
:vendor-code (-> (vendors/best-match (get i "vendor_identifier"))
(get "label"))
:total (get i "total")
:invoice-number (get i "invoice_number")
:template "None found - defaulting to ChatGPT"})
)
:template "None found - defaulting to ChatGPT"}))
(catch Exception e
(alog/warn ::glimpse2-not-work :error e)
nil)))
(defmethod parse-file
"pdf"
[file _]
[file _ & {:keys [allow-glimpse?] :or {allow-glimpse? false}}]
(or
(-> (sh/sh "pdftotext" "-layout" file "-")
:out
parse)
(alog/peek ::glimpse2-result (glimpse2 file))))
(and allow-glimpse? (alog/peek ::glimpse2-result (glimpse2 file)))))
(defmethod parse-file
"csv"
[file filename]
[file filename & _]
(csv/parse-file file filename))
(defmethod parse-file
"xls"
[file filename]
[file filename & _]
(excel/parse-file file filename))
(defmethod parse-file
"xlsx"
[file filename]
[file filename & _]
(excel/parse-file file filename))
(defn best-match
@@ -147,17 +153,17 @@
(sort-by second)
first)
word-set (set (filter (complement str/blank?) (str/split (.toLowerCase invoice-client-name) #"[\s:\-]" )))
word-set (set (filter (complement str/blank?) (str/split (.toLowerCase invoice-client-name) #"[\s:\-]")))
client-word-match (->> clients
(map
(fn [{:keys [:client/matches :client/name] :as client :or {matches []}}]
(let [client-words (-> #{}
(into
(mapcat
(fn [match] (str/split (.toLowerCase match) #"\s" ))
(fn [match] (str/split (.toLowerCase match) #"\s"))
matches))
(into
(str/split (.toLowerCase name) #"\s" )))]
(str/split (.toLowerCase name) #"\s")))]
[client (count (set/intersection client-words word-set))])))
(filter (fn [[_ c]] (> c 0)))
(sort-by (fn [[_ c]] c))
@@ -184,14 +190,14 @@
(mapcat (fn [{:keys [:location-match/location :location-match/matches]}]
(map (fn [match] [location match]) matches)))
(filter (fn [[_ match]]
(re-find (re-pattern (str "(?i)" match)) text)) )
(re-find (re-pattern (str "(?i)" match)) text)))
first
first)
(->> client
:client/location-matches
(mapcat (fn [{:keys [:location-match/location :location-match/matches]}]
(map (fn [match] [location match]) matches)))
(filter (fn [[_ match]] (re-find (re-pattern (str "(?i)" match)) full-text)) )
(filter (fn [[_ match]] (re-find (re-pattern (str "(?i)" match)) full-text)))
first
first)
(:client/default-location client)

View File

@@ -1,7 +1,9 @@
(ns auto-ap.parse.templates
(:require [auto-ap.parse.util :as u]
[auto-ap.logging :as alog]
[clojure.string :as str]))
[clj-time.core :as time]
[clojure.string :as str]
[auto-ap.time :as atime]))
(def pdf-templates
@@ -13,7 +15,7 @@
:date #"\s+([0-9]+/[0-9]+/[0-9]+)"
:invoice-number #"\s+[0-9]+/[0-9]+/[0-9]+\s+([0-9]+)"}
:parser {:date [:clj-time "MM/dd/yyyy"]}}
{:vendor "Gstar Seafood"
:keywords [#"G Star Seafood"]
:extract {:total #"Total\s{2,}([\d\-,]+\.\d{2,2}+)"
@@ -42,10 +44,10 @@
:total #"INVOICE TOTAL\s+([0-9.]+)"}
:parser {:date [:clj-time "MM/dd/yy"]}
:multi #"\f\f"}
;; IMPACT PAPER
{:vendor "Impact Paper & Ink LTD"
{:vendor "Impact Paper & Ink LTD"
:keywords [#"650-692-5598"]
:extract {:total #"Total Amount\s+\$([\d\.\,\-]+)"
:account-number #"CUST. #\n.*?/\d{4,}\s+(.*?)\n"
@@ -94,7 +96,7 @@
:total #"Total Invoice\s+([\-]?[0-9.]+)"}
:parser {:date [:clj-time "MM/dd/yy"]
:total [:trim-commas-and-negate nil]}}
{:vendor "Ben E. Keith"
:keywords [#"BEN E. KEITH"]
:extract {:date #"Customer No Mo Day Yr.*?\n.*?\d{5,}\s{2,}(\d+\s+\d+\s+\d+)"
@@ -104,7 +106,7 @@
:parser {:date [:month-day-year nil]
:total [:trim-commas-and-negate nil]}}
;; SOUTHBAY FRESH
;; SOUTHBAY FRESH
{:vendor "Southbay Fresh Produce"
:keywords [#"(SOUTH BAY FRESH PRODUCE|SOUTH BAY PRODUCE)"]
:extract {:date #"^([0-9]+/[0-9]+/[0-9]+)"
@@ -115,25 +117,25 @@
:multi #"\n"
:multi-match? #"^[0-9]+/[0-9]+/[0-9]+\s+INV "}
;; DON VITO
;; DON VITO
{:vendor "Don Vito Ozuna Food Corp"
:keywords [#"408-465-2010"]
:extract {:date #"([0-9]+/[0-9]+/[0-9]+)"
:customer-identifier #"Bill To.*?\n(.*?)\s{2,}"
:invoice-number #"(?:[0-9]+/[0-9]+/[0-9]+)\s{2,}(\d+)"
:total #"Please remit payment to\s{2,}\$([\-0-9.]+)"}
:parser {:date [:clj-time "MM/dd/yyyy"]} }
:parser {:date [:clj-time "MM/dd/yyyy"]}}
;; DON VITO STATEMENT
{:vendor "Don Vito Ozuna Food Corp"
{:vendor "Don Vito Ozuna Food Corp"
:keywords [#"Don Vito Ozuna Food Corp.*?\n.*?Statement"]
:extract {:date #"([0-9]+/[0-9]+/[0-9]+)"
:customer-identifier #"To:.*?\n\s*(.*)?\s{2,}"
:invoice-number #"INV #(\d+)"
:total #"Amount \$([\d\-\.]+?)\.\s{2,}"}
:parser {:date [:clj-time "MM/dd/yyyy"]}
:multi #"\n"
:multi-match? #"\d+/\d+/\d+.*?INV"}
:multi #"\n"
:multi-match? #"\d+/\d+/\d+.*?INV"}
;; PFG - LEDYARD
{:vendor "Performance Food Group - LEDYARD"
@@ -221,8 +223,8 @@
:total [:trim-commas nil]}
:multi (. java.util.regex.Pattern (compile (-> \formfeed str) java.util.regex.Pattern/CASE_INSENSITIVE))
:multi-match? #"(Total\s+[0-9\.]+|Total Order)"}
;; AUTO-CHLOR
;; AUTO-CHLOR
{:vendor "Auto-Chlor"
:keywords [#"AUTO-CHLOR"]
:extract {:date #"DATE : ([0-9]+/[0-9]+/[0-9]+)"
@@ -254,7 +256,7 @@
:multi #"\n"
:multi-match? #"^\s+.*?\d{6,}.*?\$"}
;; C & L
;; C & L
{:vendor "C&L Produce"
:keywords [#"440 Franklin Street"]
:extract {:date #"([0-9]+/[0-9]+/[0-9]+)"
@@ -368,7 +370,7 @@
:total [:trim-commas nil]}}
;; Breakthru Bev
;; Breakthru Bev
{:vendor "Wine Warehouse"
:keywords [#"BREAKTHRU BEVERAGE"]
:extract {:date #"Invoice Date:\s+([0-9]+/[0-9]+/[0-9]+)"
@@ -377,7 +379,7 @@
:account-number #"Customer #:\s+(\d+)"}
:parser {:date [:clj-time "MM/dd/yyyy"]
:total [:trim-commas nil]}}
;; THE WATER PROS
{:vendor "The Water Pros"
:keywords [#"The Water Pros, Inc"]
@@ -418,7 +420,7 @@
:parser {:date [:clj-time "MM/dd/yyyy"]
:total [:trim-commas nil]}}
;; PACIFIC SEAFOOD
;; PACIFIC SEAFOOD
{:vendor "Pacific Seafood"
:keywords [#"(pacseafood|PACIFIC FRESH)"]
:extract {:date #"DATE(?:.*\n.*(?=([0-9]+/[0-9]+/[0-9]+)))([0-9]+/[0-9]+/[0-9]+)"
@@ -490,7 +492,7 @@
:parser {:date [:clj-time "MM/dd/yyyy"]
:total [:trim-commas-and-negate nil]}}
;; A&B
;; A&B
{:vendor "A&B Produce"
:keywords [#"ABProduce"]
:extract {:date #"^\s+([0-9]+/[0-9]+/[0-9]+)"
@@ -623,7 +625,7 @@
:parser {:date [:clj-time "MM/dd/yy"]
:total [:trim-commas nil]}}
;; JFC
;; JFC
{:vendor "JFC International"
:keywords [#"48490 MILMONT DRIVE"]
:extract {:date #"([0-9]+/[0-9]+/[0-9]+)"
@@ -701,6 +703,29 @@
:total [:trim-commas-and-negate nil]}
:multi #"\n"
:multi-match? #"^\s*Invoice\s{2,}"}
;; DAY LEE
{:vendor "Day Lee Foods"
:keywords [#"DAY-LEE FOODS"]
:extract {:date #"([0-9]+/[0-9]+/[0-9]+)"
:customer-identifier #"9720\n\n(.*?)\n"
:invoice-number #"^\s*(\d+)\s"
:total #"\$([\d\-,]+)$"}
:parser {:date [:clj-time "MM/dd/yyyy"]
:total [:trim-commas-and-negate nil]}
:multi #"\n"
:multi-match? #"RI"}
;; NA sales
{:vendor "N.A. Sales Company, Inc"
:keywords [#"2695 McCone Avenue"]
:extract {:date #"Invoice Date:\s+([0-9]+/[0-9]+/[0-9]+)"
:customer-identifier #"Sold To:.*?\n\s+(.*?)\s{2,}"
:invoice-number #"Invoice Number:\s+(.*?)\n"
:total #"Invoice Total:\s+([\d\-,]+\.\d{2,2}+)"}
:parser {:date [:clj-time "MM/dd/yyyy"]
:total [:trim-commas-and-negate nil]}}
{:vendor "Mani Imports"
:keywords [#"Mani Imports"]
:extract {:date #"Order Date\s+([0-9]+/[0-9]+/[0-9]+)"
@@ -708,7 +733,7 @@
:invoice-number #"Invoice Number:\s+(.*?)\n"
:total #"Invoice Total:\s+([\d\-,]+\.\d{2,2}+)"}
:parser {:date [:clj-time "MM/dd/yyyy"]
:total [:trim-commas-and-negate nil]} }
:total [:trim-commas-and-negate nil]}}
{:vendor "Reel Produce"
:keywords [#"REEL Produce, Inc" #"Statem"]
:extract {:date #"\s*([0-9]+/[0-9]+/[0-9]+)"
@@ -768,13 +793,33 @@
(->> r first not-empty))))
(map
(fn [[customer-number _ _ _ invoice-number date amount :as row]]
(println "DAT E is" date)
{:customer-identifier customer-number
:text (str/join " " row)
:full-text (str/join " " row)
:date (u/parse-value :clj-time "MM/dd/yyyy" (str/trim date))
:date (try (or (u/parse-value :clj-time "MM/dd/yyyy" (str/trim date))
(try
(atime/as-local-time
(time/plus (time/date-time 1900 1 1)
(time/days (dec (dec (Integer/parseInt "45663"))))))
(catch Exception e
nil)
))
(catch Exception e
(try
(atime/as-local-time
(time/plus (time/date-time 1900 1 1)
(time/days (dec (dec (Integer/parseInt "45663"))))))
(catch Exception e
nil)
)
))
:invoice-number invoice-number
:total (str amount)
:vendor-code vendor})))
conj
[]
sheet)))}])

View File

@@ -278,7 +278,7 @@
"text/csv"
"application/pdf")
:content-length (.length tempfile)})
imports (->> (parse/parse-file (.getPath tempfile) filename)
imports (->> (parse/parse-file (.getPath tempfile) filename :allow-glimpse? true)
(map #(assoc %
:client-override client
:location-override location

View File

@@ -180,10 +180,10 @@
(defn pc [start end]
{"query" {"filter" {"date_time_filter"
{"created_at" {"start_at" (->square-date start)
{"updated_at" {"start_at" (->square-date start)
"end_at" (->square-date end)}}}
"sort" {"sort_field" "CREATED_AT"
"sort" {"sort_field" "UPDATED_AT"
"sort_order" "DESC"}}})
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
@@ -388,7 +388,7 @@
(defn daily-results
([client location]
(daily-results client location (time/plus (time/now) (time/days -7)) (time/now)))
(daily-results client location (time/plus (time/now) (time/days -10)) (time/now)))
([client location start end]
(capture-context->lc
(->
@@ -458,7 +458,7 @@
(:payout_entries result)))))))
(defn payouts
([client location] (payouts client location (time/plus (time/now) (time/days -7)) (time/now)))
([client location] (payouts client location (time/plus (time/now) (time/days -10)) (time/now)))
([client location start end]
(with-context-as {:location (:square-location/client-location location)} lc
(de/chain (manifold-api-call
@@ -599,7 +599,7 @@
(apply de/zip
(for [square-location (:client/square-locations client)
:when (:square-location/client-location square-location)]
(upsert client square-location (time/plus (time/now) (time/days -14)) (time/now)))))
(upsert client square-location (time/plus (time/now) (time/days -10)) (time/now)))))
([client location start end]
(capture-context->lc
(de/chain (daily-results client location start end)
@@ -618,7 +618,7 @@
:when (:square-location/client-location square-location)]
(upsert-payouts client square-location))))
([client location]
(upsert-payouts client location (time/plus (time/now) (time/days -7)) (time/now)))
(upsert-payouts client location (time/plus (time/now) (time/days -10)) (time/now)))
([client location start end]
(with-context-as {:source "Square payout loading"
:client (:client/code client)} lc
@@ -825,7 +825,7 @@
(apply de/zip
(for [square-location (:client/square-locations client)
:when (:square-location/client-location square-location)]
(remove-voided-orders client square-location (time/plus (time/now) (time/days -14)) (time/now)))))
(remove-voided-orders client square-location (time/plus (time/now) (time/days -10)) (time/now)))))
([client location start end]
(let [start (max-date start (coerce/to-date-time #inst "2024-04-15T00:00:00-08:00"))]
(capture-context->lc
@@ -1056,5 +1056,12 @@
@(upsert c))
#_(filter (comp #{"OTHER"} :type) (mapcat :tenders z))
)
(let [[c [l]] (get-square-client-and-location "LFHH")]
(search c l (clj-time.coerce/from-date #inst "2025-02-28") (clj-time.coerce/from-date #inst "2025-03-01"))
(:order (get-order c l "CLjQqkzVfGa82o5hEFUrGtUGO6QZY" ))
)
)

View File

@@ -0,0 +1,319 @@
(ns auto-ap.ssr.company
(:require [amazonica.aws.s3 :as s3]
[auto-ap.datomic :refer [conn pull-attr]]
[auto-ap.datomic.clients :refer [full-read]]
[auto-ap.graphql.utils :refer [assert-can-see-client]]
[auto-ap.permissions :as permissions]
[auto-ap.solr :as solr]
[auto-ap.ssr-routes :as ssr-routes]
[auto-ap.ssr.components :as com]
[auto-ap.ssr.hx :as hx]
[auto-ap.ssr.svg :as svg]
[auto-ap.ssr.ui :refer [base-page]]
[auto-ap.ssr.utils :refer [html-response]]
[bidi.bidi :as bidi]
[cemerick.url :as url]
[clojure.java.io :as io]
[clojure.string :as str]
[config.core :refer [env]]
[datomic.api :as dc]
[ring.middleware.json :refer [wrap-json-response]])
(:import [java.util UUID]
(org.apache.commons.codec.binary Base64)))
(defn please-select-client-screen* []
[:div.grid.grid-cols-3
(com/content-card {}
[:div.col-span-1.p-4 {:class "p-4 sm:p-6"}
[:h3 {:class "mb-4 text-xl font-semibold dark:text-white"}
"Please select a company"]])])
(defn signature [request]
(let [signature-file (pull-attr (dc/db conn) :client/signature-file (:db/id (:client request)))]
(com/content-card {:class " w-[748px]"
:hx-target "this"
:hx-swap "outerHTML"}
[:div.col-span-1.p-4 {:class "p-4 sm:p-6 space-y-4 overflow-visible "
}
[:h3 {:class "mb-4 text-xl font-semibold dark:text-white"}
"Signature"]
[:div#signature-notification.notification.block {:style {:display "none"}}]
[:div {:x-data (hx/json {"signature" nil
"editing" false
"existing" (boolean signature-file)})
:hx-put (bidi/path-for ssr-routes/only-routes
:company-update-signature)
:hx-trigger "accepted"
:hx-vals "js:{signatureData: event.detail.signatureData}"}
[:div.htmx-indicator
[:div.bg-gray-100.flex.items-center.text-green-500.justify-center.rounded.rounded-lg.border.border-gray-400 {:style {:width "696px" :height "261px"}}
(svg/spinner {:class "w-4 h-4 text-primary-300"})
[:div.ml-3 "Loading..."]]]
[:div.htmx-indicator-hidden
(when signature-file
[:img.rounded.rounded-lg.border.border-gray-300.bg-gray-50 {:src signature-file
:width 696
:height 261
:x-show "existing && !editing"}])
[:canvas.rounded.rounded-lg.border.border-gray-300
{:style {:width 696
:height 261}
:x-init "signature= new SignaturePad($el); signature.off()"
":class" "editing ? 'bg-white' : 'bg-gray-50' "
:width 696
:height 261
:x-show "existing ? editing: true"}]]
[:div.flex.gap-2.justify-end
(com/button {:color :primary
:x-show "!editing"
"@click" "signature.clear(); signature.on(); editing=true;"}
"New signature")
(com/button {:color :primary
:x-show "editing"
"@click" "signature.clear();"}
"Clear")
(com/button {:color :primary
"@click" "$data.signatureData=signature.toDataURL('image/png'); signature.off(); editing=false; $dispatch('accepted', {signatureData: $data.signatureData}) "
:x-show "editing"}
"Accept")]]
[:div
[:div.flex.justify-center " - or -"]
[:form {:hx-post (bidi/path-for ssr-routes/only-routes
:company-upload-signature)
:hx-disinherit "hx-vals"
:hx-encoding "multipart/form-data"
#_#_:hx-target "#signature-notification"
:hx-swap "outerHTML"
:id "upload"
:hx-trigger "z"
}
[:div.htmx-indicator
[:div.bg-gray-100.flex.items-center.text-green-500.justify-center.rounded.rounded-lg.border.border-gray-400 {:style {:width "696px" :height "261px"}}
(svg/spinner {:class "w-4 h-4 text-primary-300"})
[:div.ml-3 "Loading..."]]]
[:div.htmx-indicator-hidden
[:div.border-2.border-dashed.rounded-lg.p-4.w-full.text-center.cursor-pointer.h-64.flex.items-center.justify-center.text-lg.relative
{:x-data (hx/json {"files" nil
"hovering" false})
:x-dispatch:z "files"
":class" "{'bg-blue-100': !hovering,
'border-blue-300': !hovering,
'text-blue-700': !hovering,
'bg-green-100': hovering,
'border-green-300': hovering,
'text-green-700': hovering
}"}
[:input {:type "file"
:name "file"
:class "absolute inset-0 m-0 p-0 w-full h-full outline-none opacity-0",
:x-on:change "files = $event.target.files;",
:x-on:dragover "hovering = true",
:x-on:dragleave "hovering = false",
:x-on:drop "hovering = false"}]
[:div.flex.flex-col.space-2
[:div
[:ul {:x-show "files != null"}
[:template {:x-for "f in files"}
[:li (com/pill {:color :primary :x-text "f.name"})]]]]
[:div.htmx-indicator-hidden "Drop a signature file (696x261 pixels jpeg) here."]]]] ]]])))
(defn upload-signature-data [{{:strs [signatureData]} :form-params client :client :as request}]
(let [prefix "data:image/png;base64,"]
(when signatureData
(when-not (str/starts-with? signatureData prefix)
(throw (ex-info "Invalid signature image" {:validation-error (str "Invalid signature image.")})))
(let [signature-id (str (UUID/randomUUID))
raw-bytes (Base64/decodeBase64 (subs signatureData (count prefix)))]
(s3/put-object :bucket-name "integreat-signature-images" #_(:data-bucket env)
:key (str signature-id ".png")
:input-stream (io/make-input-stream raw-bytes {})
:metadata {:content-type "image/png"
:content-length (count raw-bytes)}
:canned-acl "public-read")
@(dc/transact conn [{:db/id (:db/id client)
:client/signature-file (str "https://integreat-signature-images.s3.amazonaws.com/" signature-id ".png")}])
(html-response
(signature request))))))
(defn upload-signature-file [{{:strs [signatureData]} :form-params client :client user :identity :as request}]
(assert-can-see-client user client)
(let [{:strs [file]} (:multipart-params request) ]
(try
(let [signature-id (str (UUID/randomUUID)) ]
(s3/put-object :bucket-name "integreat-signature-images" #_(:data-bucket env)
:key (str signature-id ".jpg")
:input-stream (io/input-stream (:tempfile file))
:metadata {:content-type "image/jpeg"
:content-length (:length (:tempfile file))}
:canned-acl "public-read")
@(dc/transact conn [{:db/id (:db/id client)
:client/signature-file (str "https://integreat-signature-images.s3.amazonaws.com/" signature-id ".jpg")}])
(html-response
(signature request)))
(catch Exception e
(println e)
#_(-> result
(assoc :error? true)
(update :results conj {:filename filename
:response (.getMessage e)
:sample (:sample (ex-data e))
:template (:template (ex-data e))}))))
#_(html-response [:div#page-notification.p-4.rounded-lg
{:class (if (:error? results)
"bg-red-50 text-red-700"
"bg-primary-50 text-primary-700")}
[:table
[:thead
[:tr [:td "File"] [:td "Result"]
[:td "Template"]
(if (:error? results)
[:td "Sample match"])]
#_[:tr "Result"]
#_[:tr "Template"]]
(for [r (:results results)]
[:tr
[:td.p-2.border
{:class (if (:error? results)
"bg-red-50 text-red-700 border-red-300"
"bg-primary-50 text-primary-700 border-green-500")}
(:filename r)]
[:td.p-2.border
{:class (if (:error? results)
"bg-red-50 text-red-700 border-red-300"
"bg-primary-50 text-primary-700 border-green-500")}
(:response r)]
[:td.p-2.border
{:class (if (:error? results)
"bg-red-50 text-red-700 border-red-300"
"bg-primary-50 text-primary-700 border-green-500")}
"Template: " (:template r)]
(if (:error? results)
[:td.p-2.border
{:class "bg-red-50 text-red-700 border-red-300"}
[:ul
(for [[k v] (dissoc (:sample r) :template :source-url :full-text :text)]
[:li (name k) ": " (str v)])]
#_(:template r)])])]]
:headers
{"hx-trigger" "invalidated"})))
(defn main-content* [{:keys [client identity] :as request}]
(if-not client
(please-select-client-screen*)
(let [client (dc/pull (dc/db conn) full-read (:db/id client))]
[:div
[:div.grid.grid-cols-3.gap-4
(com/content-card {}
[:div.col-span-1.p-4 {:class "p-4 sm:p-6"}
[:h3 {:class "mb-4 text-xl font-semibold dark:text-white"}
(:client/name client)]
(when-let [address (-> client :client/address)]
[:div.flex.flex-col.gap-1.text-lg.dark:text-white.text-gray-700
[:p (-> address :address/street1)]
[:p (-> address :address/street2)]
[:p (-> address :address/city) " "
(-> address :address/state) ", "
(-> address :address/zip)]])])
(com/content-card {}
[:div.col-span-1.p-4 {:class "p-4 sm:p-6"}
[:h3 {:class "mb-4 text-xl font-semibold dark:text-white"}
"Downloads"]
[:a {:href (str (assoc (url/url (str (:base-url env) "/api/vendors/company/export"))
:query {"client" (:client/code client)}))}
(com/button {:color :primary}
"Download vendor list"
(com/button-icon {} svg/download))]])
[:div]]
(when (permissions/can? identity {:client client :subject :signature :activity :edit})
(signature request))])))
(defn page [{:keys [identity matched-route] :as request}]
(base-page
request
(com/page {:nav com/company-aside-nav
:client-selection (:client-selection request)
:request request
:client (:client request)
:clients (:clients request)
:identity (:identity request)
:app-params {:hx-get (bidi/path-for ssr-routes/only-routes
:company)
:hx-trigger "clientSelected from:body"
:hx-select "#app-contents"
:hx-swap "outerHTML swap:300ms"}}
(com/breadcrumbs {}
[:a {:href (bidi/path-for ssr-routes/only-routes
:company)}
"My Company"])
(main-content* request))
"My Company"))
(defn search [{:keys [clients query-params]}]
(let [valid-client-ids (set (map :db/id clients))
name-like-ids (when (not-empty (get query-params "q"))
(set (map (comp #(Long/parseLong %) :id)
(solr/query solr/impl "clients"
{"query" (format "_text_:(%s*)" (str/upper-case (solr/escape (get query-params "q"))))
"fields" "id"
"limit" 300}))))
valid-clients (for [n name-like-ids
:when (valid-client-ids n)]
{"value" n "label" (pull-attr (dc/db conn) :client/name n)})]
{:body (take 10 valid-clients)}))
(def search (wrap-json-response search))
(defn bank-account-search [{:keys [route-params query-params clients]}]
(let [valid-client-ids (set (map :db/id clients))
selected-client-id (Long/parseLong (get route-params :db/id))
bank-accounts (when (valid-client-ids selected-client-id)
(->> (dc/pull (dc/db conn) [{:client/bank-accounts [:db/id :bank-account/name]}]
selected-client-id)
:client/bank-accounts
(filter (fn [{:keys [bank-account/name]}]
(str/includes? (or (some-> name str/upper-case) "")
(or (some-> query-params
(get "q")
str/upper-case)
"__"))))
(map (fn [{:keys [db/id bank-account/name]}]
{"value" id "label" name}))))]
{:body (take 10 bank-accounts)}))
(def bank-account-search (wrap-json-response bank-account-search))
(defn bank-account-typeahead* [{:keys [client-id name value]}]
(if client-id
(com/typeahead {:name name
:class "w-96"
:placeholder "Search..."
:url (bidi/path-for ssr-routes/only-routes :bank-account-search
:db/id client-id)
:value value
:value-fn (some-fn :db/id identity)
:content-fn (some-fn :bank-account/name #(pull-attr (dc/db conn) :bank-account/name %))})
[:span.text-xs.text-gray-500 "Please select a client before selecting a bank account."
[:input {:type "hidden"
:name name}]]))
(defn bank-account-typeahead [{:keys [query-params clients]}]
(html-response (bank-account-typeahead* {:client-id ((set (map :db/id clients))
(some->> "client-id"
(get query-params)
not-empty
Long/parseLong))
:name (get query-params "name")})))

View File

@@ -711,7 +711,7 @@
:content-length (.length tempfile)})
imports (->> (if force-chatgpt
(parse/glimpse2 (.getPath tempfile))
(parse/parse-file (.getPath tempfile) filename))
(parse/parse-file (.getPath tempfile) filename :allow-glimpse? true))
(map #(assoc %
:client-override force-client
:location-override force-location

View File

@@ -1166,7 +1166,7 @@
:name (fc/field-name)
:error? (fc/field-errors)
:placeholder "10001"}))))
(when (= :handwrite-check (:method (:snapshot (:multi-form-state request))))
(when (#{:handwrite-check :print-check} (:method (:snapshot (:multi-form-state request))))
(fc/with-field :handwritten-date
(com/validated-field
{:errors (fc/field-errors)
@@ -1187,6 +1187,7 @@
(format "Pay in full ($%,.2f)" total)))}
{:value "advanced"
:content "Customize payments"}]})
[:div.space-y-4
(fc/with-field :invoices
(com/validated-field
@@ -1245,13 +1246,13 @@
bank-account
:payment-type/check
0
invoice-payment-lookup)]
invoice-payment-lookup
(:handwritten-date snapshot))]
(let [result (audit-transact
(into [(assoc base-payment
:payment/type :payment-type/check
:payment/status :payment-status/pending
:payment/check-number (:check-number snapshot)
:payment/date (coerce/to-date (:handwritten-date snapshot)))]
:payment/check-number (:check-number snapshot))]
(invoice-payments invoices invoice-payment-lookup))
(:identity request))]
(try
@@ -1345,24 +1346,33 @@
(= "" (:check-number snapshot)))
(throw (Exception. "Check number is required")))
true))
result (exception->4xx
#(if (= :handwrite-check (:method snapshot))
(add-handwritten-check request this snapshot)
(print-checks-internal (map (fn [i] {:invoice-id (:invoice-id i)
:amount (:amount i)})
(:invoices snapshot))
(:client snapshot)
(:bank-account snapshot)
(cond (= :print-check (:method snapshot))
:payment-type/check
(= :debit (:method snapshot))
:payment-type/debit
(= :cash (:method snapshot))
:payment-type/cash
(= :credit (:method snapshot))
:payment-type/credit
:else :payment-type/debit)
identity)))]
#(do
(when (:handwritten-date snapshot)
(let [invoices (d-invoices/get-multi (map :invoice-id (:invoices snapshot)))]
(assert-not-locked (:db/id (:invoice/client (first invoices))) (:handwritten-date snapshot))))
(if (= :handwrite-check (:method snapshot))
(add-handwritten-check request this snapshot)
(try
(print-checks-internal (map (fn [i] {:invoice-id (:invoice-id i)
:amount (:amount i)})
(:invoices snapshot))
(:client snapshot)
(:bank-account snapshot)
(cond (= :print-check (:method snapshot))
:payment-type/check
(= :debit (:method snapshot))
:payment-type/debit
(= :cash (:method snapshot))
:payment-type/cash
(= :credit (:method snapshot))
:payment-type/credit
:else :payment-type/debit)
identity
(:handwritten-date snapshot))
(catch Exception e
(println e))))))]
(modal-response
(com/modal {}
(com/modal-card-advanced

View File

@@ -8,7 +8,7 @@
[auto-ap.ledger.reports :as l-reports]
[auto-ap.logging :as alog]
[auto-ap.pdf.ledger :refer [table->pdf]]
[auto-ap.permissions :refer [wrap-must]]
[auto-ap.permissions :refer [can? wrap-must]]
[auto-ap.routes.ledger :as route]
[auto-ap.routes.utils
:refer [wrap-client-redirect-unauthenticated]]
@@ -86,7 +86,7 @@
data (into []
(for [client-id client-ids
p periods
[client-id account-id location debits credits balance count] (iol-ion.query/detailed-account-snapshot (dc/db conn) client-id (coerce/to-date (:end p)) (coerce/to-date (:start p)))
[client-id account-id location debits credits balance count sample] (iol-ion.query/detailed-account-snapshot (dc/db conn) client-id (coerce/to-date (:end p)) (coerce/to-date (:start p)))
:let [account ((or (lookup-account client-id) {}) account-id)]]
{:client-id client-id
:account-id account-id
@@ -102,6 +102,7 @@
:account-type (:account_type account)
:numeric-code (:numeric_code account)
:name (:name account)
:sample sample
:period {:start ( coerce/to-date (:start p)) :end (coerce/to-date (time/plus (:end p) (time/days 1)))}}))
args (assoc (:form-params request)
:periods (map (fn [d] {:start ( coerce/to-date (:start d)) :end ( coerce/to-date (:end d))}) periods))
@@ -130,14 +131,26 @@
(list
[:div.text-2xl.font-bold.text-gray-600 (str "Profit and loss - " (str/join ", " (map :client/name client)))]
(table {:widths (into [20] (take (dec (cell-count table-contents))
(mapcat identity
(repeat
(if (-> data :args :include-deltas)
[13 6 13]
[13 6])))))
(mapcat identity
(repeat
(if (-> data :args :include-deltas)
[13 6 13]
[13 6])))))
:investigate-url (bidi.bidi/path-for ssr-routes/only-routes ::route/investigate)
:table table-contents
:warning (not-empty (str/join "\n " (filter not-empty [warning (:warning report)])))}))))])
:warning [:div
(not-empty (str (str/join "\n " (filter not-empty [warning (:warning report)]))))
(when (can? (:identity request)
{:subject :history
:activity :view})
(for [n (:invalid-ids report)]
[:div
(com/link {:href (str (bidi/path-for ssr-routes/only-routes
:admin-history)
"/" n)}
"Sample")]))]
}))))])
@@ -215,7 +228,8 @@
:ttf-name "fonts/calibri-light.ttf"}}
[:heading (str "Balance Sheet - " (str/join ", " (map :client/name (seq (:client (:form-params request))))))]]
(conj [:paragraph {:color [128 0 0] :size 9} (:warning report)])
(conj [:paragraph {:color [128 0 0] :size 9} (:warning report)
])
(conj
(table->pdf table
(into [20] (take (dec (cell-count table))

View File

@@ -715,8 +715,17 @@
(:location %)))
errors))))))
(defn invalid-ids [pnl-data]
(let [errors (->> pnl-data
:data
(filter (fn [{:keys [numeric-code]}]
(nil? numeric-code)))
(map :sample)) ]
errors))
(defn summarize-pnl [pnl-data]
{:warning (warning-message pnl-data)
:invalid-ids (invalid-ids pnl-data)
:summaries
(if (-> pnl-data :args :column-per-location)
[(location-summary-table (mapcat identity (for [[period i] (map vector (-> pnl-data :args :periods ) (range))]

View File

@@ -1,5 +1,5 @@
#!/bin/bash
#sudo docker run --rm -ti -v ~/dev/integreat/data/solr:/var/solr --network=bridge -p 8983:8983 bryce-solr
sudo podman container run --user 1000 --privileged --volume /home/notid/dev/integreat/data/solr:/var/solr -p 8983:8983 bryce-solr
sudo docker run --rm -ti -v ~/dev/integreat/data/solr:/var/solr --network=bridge -p 8983:8983 bryce-solr
#sudo podman container run --user 1000 --privileged --volume /home/notid/dev/integreat/data/solr:/var/solr -p 8983:8983 bryce-solr