merged
This commit is contained in:
@@ -241,7 +241,7 @@
|
|||||||
([db client-id ^java.util.Date end ^java.util.Date start]
|
([db client-id ^java.util.Date end ^java.util.Date start]
|
||||||
|
|
||||||
(for [running-balance-set (account-sets db client-id)
|
(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
|
(->> running-balance-set
|
||||||
(filter (fn until-end [^Line l]
|
(filter (fn until-end [^Line l]
|
||||||
(let [^java.util.Date d (.-date l)]
|
(let [^java.util.Date d (.-date l)]
|
||||||
@@ -259,13 +259,15 @@
|
|||||||
:current-balance (.-running-balance l)
|
:current-balance (.-running-balance l)
|
||||||
:account-id (.-account-id l)
|
:account-id (.-account-id l)
|
||||||
:client-id (.-client-id l)
|
:client-id (.-client-id l)
|
||||||
:location (.-location l)))
|
:location (.-location l)
|
||||||
|
:sample (.-id l)))
|
||||||
(transient {:count 0
|
(transient {:count 0
|
||||||
:debits 0.0
|
:debits 0.0
|
||||||
:credits 0.0
|
:credits 0.0
|
||||||
:current-balance 0.0})))]
|
:current-balance 0.0})))]
|
||||||
:when client-id]
|
: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
|
(comment
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1,29 +1,27 @@
|
|||||||
(ns auto-ap.graphql.checks
|
(ns auto-ap.graphql.checks
|
||||||
(:require
|
(:require
|
||||||
[amazonica.aws.s3 :as s3]
|
[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.accounts :as a]
|
||||||
[auto-ap.datomic.bank-accounts :as d-bank-accounts]
|
[auto-ap.datomic.bank-accounts :as d-bank-accounts]
|
||||||
[auto-ap.datomic.checks :as d-checks]
|
[auto-ap.datomic.checks :as d-checks]
|
||||||
[auto-ap.datomic.clients :as d-clients]
|
[auto-ap.datomic.clients :as d-clients]
|
||||||
[auto-ap.datomic.invoices :as d-invoices]
|
[auto-ap.datomic.invoices :as d-invoices]
|
||||||
[auto-ap.datomic.transactions :as d-transactions]
|
[auto-ap.datomic.transactions :as d-transactions]
|
||||||
[auto-ap.logging :as alog]
|
|
||||||
[auto-ap.datomic.vendors :as d-vendors]
|
[auto-ap.datomic.vendors :as d-vendors]
|
||||||
[auto-ap.graphql.utils
|
[auto-ap.graphql.utils
|
||||||
:refer [->graphql
|
:refer [->graphql <-graphql assert-admin assert-can-see-client
|
||||||
<-graphql
|
assert-failure assert-not-locked attach-tracing-resolvers
|
||||||
attach-tracing-resolvers
|
|
||||||
assert-admin
|
|
||||||
assert-can-see-client
|
|
||||||
assert-failure
|
|
||||||
assert-not-locked
|
|
||||||
enum->keyword]]
|
enum->keyword]]
|
||||||
|
[auto-ap.logging :as alog]
|
||||||
[auto-ap.numeric :refer [num->words]]
|
[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?]]
|
[auto-ap.utils :refer [by dollars-0?]]
|
||||||
[clj-pdf.core :as pdf]
|
[clj-pdf.core :as pdf]
|
||||||
[clj-time.coerce :as c]
|
[clj-time.coerce :as c]
|
||||||
|
[clj-time.coerce :as coerce]
|
||||||
[clj-time.core :as time]
|
[clj-time.core :as time]
|
||||||
[clj-time.format :as f]
|
[clj-time.format :as f]
|
||||||
[clojure.edn :as edn]
|
[clojure.edn :as edn]
|
||||||
@@ -31,12 +29,9 @@
|
|||||||
[clojure.set :as set]
|
[clojure.set :as set]
|
||||||
[clojure.string :as str]
|
[clojure.string :as str]
|
||||||
[com.brunobonacci.mulog :as mu]
|
[com.brunobonacci.mulog :as mu]
|
||||||
[com.walmartlabs.lacinia.util :refer [attach-resolvers]]
|
|
||||||
[config.core :refer [env]]
|
[config.core :refer [env]]
|
||||||
[clj-time.coerce :as coerce]
|
|
||||||
[datomic.api :as dc]
|
[datomic.api :as dc]
|
||||||
[digest]
|
[digest])
|
||||||
[auto-ap.solr :as solr])
|
|
||||||
(:import
|
(:import
|
||||||
(java.io ByteArrayOutputStream)
|
(java.io ByteArrayOutputStream)
|
||||||
(java.text DecimalFormat)
|
(java.text DecimalFormat)
|
||||||
@@ -215,7 +210,7 @@
|
|||||||
(str "https://" (:data-bucket env) "/merged-checks/" uuid ".pdf")))
|
(str "https://" (:data-bucket env) "/merged-checks/" uuid ".pdf")))
|
||||||
|
|
||||||
#_{:clj-kondo/ignore [:unused-binding]}
|
#_{: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))
|
type))
|
||||||
(defn invoice-payments [invoices invoice-amounts]
|
(defn invoice-payments [invoices invoice-amounts]
|
||||||
(->> (for [invoice invoices
|
(->> (for [invoice invoices
|
||||||
@@ -228,16 +223,16 @@
|
|||||||
[:pay (:db/id invoice) invoice-amount]])
|
[:pay (:db/id invoice) invoice-amount]])
|
||||||
(reduce into [])))
|
(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))
|
{:db/id (str (:db/id vendor))
|
||||||
:payment/bank-account (:db/id bank-account)
|
:payment/bank-account (:db/id bank-account)
|
||||||
:payment/amount (reduce + 0 (map (comp invoice-amounts :db/id) invoices))
|
:payment/amount (reduce + 0 (map (comp invoice-amounts :db/id) invoices))
|
||||||
:payment/vendor (:db/id vendor)
|
:payment/vendor (:db/id vendor)
|
||||||
:payment/client (:db/id client)
|
: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)})
|
: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
|
(when (<= (->> invoices
|
||||||
(map (comp invoice-amounts :db/id))
|
(map (comp invoice-amounts :db/id))
|
||||||
(reduce + 0.0))
|
(reduce + 0.0))
|
||||||
@@ -245,15 +240,16 @@
|
|||||||
(throw (ex-info "The selected invoices do not have an outstanding balance."
|
(throw (ex-info "The selected invoices do not have an outstanding balance."
|
||||||
{:validation-error "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))
|
(let [uuid (str (UUID/randomUUID))
|
||||||
|
date (or date (time/now))
|
||||||
memo (str "Invoice #'s: "
|
memo (str "Invoice #'s: "
|
||||||
(str/join ", "
|
(str/join ", "
|
||||||
(map (fn [i]
|
(map (fn [i]
|
||||||
(str (:invoice/invoice-number i) "(" (invoice-amounts (:db/id i)) ")"))
|
(str (:invoice/invoice-number i) "(" (invoice-amounts (:db/id i)) ")"))
|
||||||
invoices)))
|
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
|
payment
|
||||||
(remove-nils
|
(remove-nils
|
||||||
(assoc base-payment
|
(cond-> (assoc base-payment
|
||||||
:payment/s3-uuid (when (> (:payment/amount base-payment) 0) uuid)
|
: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-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/s3-url (when (> (:payment/amount base-payment) 0) (str "https://" (:data-bucket env) "/checks/" uuid ".pdf"))
|
||||||
@@ -266,7 +262,7 @@
|
|||||||
:amount (reduce + 0 (map (comp invoice-amounts :db/id) invoices))
|
:amount (reduce + 0 (map (comp invoice-amounts :db/id) invoices))
|
||||||
:check (str (+ index (:bank-account/check-number bank-account)))
|
:check (str (+ index (:bank-account/check-number bank-account)))
|
||||||
:memo memo
|
:memo memo
|
||||||
:date (date->str (local-now))
|
: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)
|
: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)
|
:bank-account (dissoc bank-account :bank-account/start-date :bank-account/integration-status)
|
||||||
#_#_:client {:name (:name client)
|
#_#_:client {:name (:name client)
|
||||||
@@ -275,21 +271,21 @@
|
|||||||
:bank {:name (:bank-account/bank-name bank-account)
|
:bank {:name (:bank-account/bank-name bank-account)
|
||||||
:acct (:bank-account/bank-code bank-account)
|
:acct (:bank-account/bank-code bank-account)
|
||||||
:routing (:bank-account/routing bank-account)
|
:routing (:bank-account/routing bank-account)
|
||||||
:acct-number (:bank-account/number bank-account)}}})))]
|
:acct-number (:bank-account/number bank-account)}}}))))]
|
||||||
|
|
||||||
(-> []
|
(-> []
|
||||||
(conj payment)
|
(conj payment)
|
||||||
(into (invoice-payments invoices invoice-amounts)))))
|
(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
|
(when (<= (->> invoices
|
||||||
(map (comp invoice-amounts :db/id))
|
(map (comp invoice-amounts :db/id))
|
||||||
(reduce + 0.0))
|
(reduce + 0.0))
|
||||||
0.001)
|
0.001)
|
||||||
(throw (ex-info "The selected invoices do not have an outstanding balance."
|
(throw (ex-info "The selected invoices do not have an outstanding balance."
|
||||||
{:validation-error "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/type :payment-type/debit
|
||||||
:payment/memo (str "Debit Invoice #'s: "
|
:payment/memo (str "Debit Invoice #'s: "
|
||||||
(str/join ", "
|
(str/join ", "
|
||||||
@@ -310,14 +306,14 @@
|
|||||||
(throw (ex-info "The selected invoices do not have an outstanding balance."
|
(throw (ex-info "The selected invoices do not have an outstanding balance."
|
||||||
{:validation-error "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
|
(when (>= (->> invoices
|
||||||
(map (comp invoice-amounts :db/id))
|
(map (comp invoice-amounts :db/id))
|
||||||
(reduce + 0.0))
|
(reduce + 0.0))
|
||||||
0.001)
|
0.001)
|
||||||
(throw (ex-info "The selected invoices do not have an outstanding balance."
|
(throw (ex-info "The selected invoices do not have an outstanding balance."
|
||||||
{:validation-error "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/type :payment-type/credit
|
||||||
:payment/memo (str "Debit Invoice #'s: "
|
:payment/memo (str "Debit Invoice #'s: "
|
||||||
(str/join ", "
|
(str/join ", "
|
||||||
@@ -329,14 +325,14 @@
|
|||||||
(conj payment)
|
(conj payment)
|
||||||
(into (invoice-payments invoices invoice-amounts)))))
|
(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
|
(when (<= (->> invoices
|
||||||
(map (comp invoice-amounts :db/id))
|
(map (comp invoice-amounts :db/id))
|
||||||
(reduce + 0.0))
|
(reduce + 0.0))
|
||||||
0.001)
|
0.001)
|
||||||
(throw (ex-info "The selected invoices do not have an outstanding balance."
|
(throw (ex-info "The selected invoices do not have an outstanding balance."
|
||||||
{:validation-error "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))
|
transaction-id (str (UUID/randomUUID))
|
||||||
memo (str "Cash Invoice #'s: "
|
memo (str "Cash Invoice #'s: "
|
||||||
(str/join ", "
|
(str/join ", "
|
||||||
@@ -381,7 +377,8 @@
|
|||||||
:client-id client-id
|
:client-id client-id
|
||||||
:invoices (map :invoice/invoice-number invoices)}))))
|
:invoices (map :invoice/invoice-number invoices)}))))
|
||||||
|
|
||||||
(defn print-checks-internal [invoice-payments client-id bank-account-id type id]
|
(defn print-checks-internal
|
||||||
|
([invoice-payments client-id bank-account-id type id date]
|
||||||
(let [type (keyword "payment-type" (name type))
|
(let [type (keyword "payment-type" (name type))
|
||||||
invoices (d-invoices/get-multi (map :invoice-id invoice-payments))
|
invoices (d-invoices/get-multi (map :invoice-id invoice-payments))
|
||||||
client (d-clients/get-by-id client-id)
|
client (d-clients/get-by-id client-id)
|
||||||
@@ -398,7 +395,7 @@
|
|||||||
{:validation-error message}))))
|
{:validation-error message}))))
|
||||||
checks (mu/trace ::build-checks []
|
checks (mu/trace ::build-checks []
|
||||||
(->> (for [[[vendor-id invoices] index] (map vector invoices-grouped-by-vendor (range))]
|
(->> (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))
|
(invoices->entities invoices (vendors vendor-id) client bank-account type index invoice-amounts date))
|
||||||
(reduce into [])
|
(reduce into [])
|
||||||
doall))
|
doall))
|
||||||
checks (if (= type :payment-type/check)
|
checks (if (= type :payment-type/check)
|
||||||
@@ -424,7 +421,7 @@
|
|||||||
(mu/trace ::merge-pdfs
|
(mu/trace ::merge-pdfs
|
||||||
[]
|
[]
|
||||||
(merge-pdfs (filter identity (map :payment/s3-key checks))))
|
(merge-pdfs (filter identity (map :payment/s3-key checks))))
|
||||||
nil)}))
|
nil)})))
|
||||||
|
|
||||||
(defn get-payment-page [context args _]
|
(defn get-payment-page [context args _]
|
||||||
(let [[payments checks-count] (d-checks/get-graphql (-> args
|
(let [[payments checks-count] (d-checks/get-graphql (-> args
|
||||||
@@ -474,14 +471,15 @@
|
|||||||
bank-account
|
bank-account
|
||||||
:payment-type/check
|
:payment-type/check
|
||||||
0
|
0
|
||||||
invoice-payment-lookup)]
|
invoice-payment-lookup
|
||||||
|
(parse (:date args) iso-date))]
|
||||||
|
|
||||||
(let [result (audit-transact
|
(let [result (audit-transact
|
||||||
(into [(assoc base-payment
|
(into [(assoc base-payment
|
||||||
:payment/type :payment-type/check
|
:payment/type :payment-type/check
|
||||||
:payment/status :payment-status/pending
|
:payment/status :payment-status/pending
|
||||||
:payment/check-number (:check_number args)
|
: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))
|
(invoice-payments invoices invoice-payment-lookup))
|
||||||
(:id context))]
|
(:id context))]
|
||||||
(doseq [[_ i] (:tempids result)]
|
(doseq [[_ i] (:tempids result)]
|
||||||
@@ -586,7 +584,8 @@
|
|||||||
(:client_id args)
|
(:client_id args)
|
||||||
(:bank_account_id args)
|
(:bank_account_id args)
|
||||||
(:type args)
|
(:type args)
|
||||||
(:id context))))
|
(:id context)
|
||||||
|
(time/now))))
|
||||||
|
|
||||||
(defn pay-invoices-from-balance [context {invoices :invoices
|
(defn pay-invoices-from-balance [context {invoices :invoices
|
||||||
client-id :client_id} _]
|
client-id :client_id} _]
|
||||||
|
|||||||
@@ -212,7 +212,8 @@
|
|||||||
client_id
|
client_id
|
||||||
bank-account-id
|
bank-account-id
|
||||||
type
|
type
|
||||||
(:id context))
|
(:id context)
|
||||||
|
(time/now))
|
||||||
u/->graphql))))
|
u/->graphql))))
|
||||||
|
|
||||||
(defn edit-invoice [context {{:keys [id due invoice_number vendor_id total date expense_accounts scheduled_payment] :as in} :invoice} _]
|
(defn edit-invoice [context {{:keys [id due invoice_number vendor_id total date expense_accounts scheduled_payment] :as in} :invoice} _]
|
||||||
|
|||||||
@@ -75,8 +75,10 @@
|
|||||||
(assoc :per-page Integer/MAX_VALUE)
|
(assoc :per-page Integer/MAX_VALUE)
|
||||||
(d-transactions/raw-graphql-ids )
|
(d-transactions/raw-graphql-ids )
|
||||||
:ids)
|
:ids)
|
||||||
specific-ids (d-transactions/filter-ids (:ids args))]
|
specific-ids (d-transactions/filter-ids (seq (:ids args)))]
|
||||||
(into (set ids) specific-ids)))
|
(if (seq (:ids args))
|
||||||
|
(set specific-ids)
|
||||||
|
(set ids))))
|
||||||
|
|
||||||
(defn all-ids-not-locked [all-ids]
|
(defn all-ids-not-locked [all-ids]
|
||||||
(->> all-ids
|
(->> all-ids
|
||||||
@@ -188,10 +190,12 @@
|
|||||||
(audit-transact-batch
|
(audit-transact-batch
|
||||||
(map (fn [t]
|
(map (fn [t]
|
||||||
(let [locations (client->locations (-> t :transaction/client :db/id))]
|
(let [locations (client->locations (-> t :transaction/client :db/id))]
|
||||||
|
(doto
|
||||||
[:upsert-transaction (cond-> t
|
[:upsert-transaction (cond-> t
|
||||||
(:approval_status args) (assoc :transaction/approval-status (enum->keyword (:approval_status args) "transaction-approval-status"))
|
(:approval_status args) (assoc :transaction/approval-status (enum->keyword (:approval_status args) "transaction-approval-status"))
|
||||||
(:vendor args) (assoc :transaction/vendor (:vendor args))
|
(:vendor args) (assoc :transaction/vendor (:vendor args))
|
||||||
(seq (:accounts args)) (assoc :transaction/accounts (maybe-code-accounts t (:accounts args) locations)))]))
|
(seq (:accounts args)) (assoc :transaction/accounts (maybe-code-accounts t (:accounts args) locations)))]
|
||||||
|
clojure.pprint/pprint)))
|
||||||
transactions)
|
transactions)
|
||||||
(:id context))
|
(:id context))
|
||||||
{:message (str "Successfully coded " (count all-ids) " transactions.")}))
|
{:message (str "Successfully coded " (count all-ids) " transactions.")}))
|
||||||
|
|||||||
@@ -62,7 +62,7 @@
|
|||||||
:input-stream (io/input-stream filename)
|
:input-stream (io/input-stream filename)
|
||||||
:metadata {:content-type "application/pdf"
|
:metadata {:content-type "application/pdf"
|
||||||
:content-length (.length (io/file filename))})
|
: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 %
|
(map #(assoc %
|
||||||
:source-url (str "https://" (:data-bucket env)
|
:source-url (str "https://" (:data-bucket env)
|
||||||
"/"
|
"/"
|
||||||
|
|||||||
@@ -61,14 +61,20 @@
|
|||||||
(extract-template text)))
|
(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]
|
(defn invoke-glimpse2 [f]
|
||||||
(let [result (slurp (:payload (lambda/invoke {:client-config {:request-timeout 120000
|
(let [result (slurp (:payload (lambda/invoke {:client-config {:request-timeout 120000
|
||||||
:socket-timeout 120000}} {:function-name "glimpse2" :payload
|
:socket-timeout 120000}} {:function-name "glimpse2" :payload
|
||||||
(json/write-str
|
(json/write-str
|
||||||
(alog/peek ::x {"url" (str "https://" "data.prod.app.integreatconsult.com" "/" f ) }))
|
(alog/peek ::x {"url" (str "https://" "data.prod.app.integreatconsult.com" "/" f)}))})))]
|
||||||
})))]
|
|
||||||
|
|
||||||
(alog/info ::glimpse2-payload :payload result)
|
(alog/info ::glimpse2-payload :payload result)
|
||||||
(-> result
|
(-> result
|
||||||
@@ -92,35 +98,35 @@
|
|||||||
(get "label"))
|
(get "label"))
|
||||||
:total (get i "total")
|
:total (get i "total")
|
||||||
:invoice-number (get i "invoice_number")
|
:invoice-number (get i "invoice_number")
|
||||||
:template "None found - defaulting to ChatGPT"})
|
:template "None found - defaulting to ChatGPT"}))
|
||||||
)
|
|
||||||
(catch Exception e
|
(catch Exception e
|
||||||
(alog/warn ::glimpse2-not-work :error e)
|
(alog/warn ::glimpse2-not-work :error e)
|
||||||
nil)))
|
nil)))
|
||||||
|
|
||||||
(defmethod parse-file
|
(defmethod parse-file
|
||||||
"pdf"
|
"pdf"
|
||||||
[file _]
|
[file _ & {:keys [allow-glimpse?] :or {allow-glimpse? false}}]
|
||||||
(or
|
(or
|
||||||
(-> (sh/sh "pdftotext" "-layout" file "-")
|
(-> (sh/sh "pdftotext" "-layout" file "-")
|
||||||
:out
|
:out
|
||||||
parse)
|
parse)
|
||||||
(alog/peek ::glimpse2-result (glimpse2 file))))
|
(and allow-glimpse? (alog/peek ::glimpse2-result (glimpse2 file)))))
|
||||||
|
|
||||||
(defmethod parse-file
|
(defmethod parse-file
|
||||||
"csv"
|
"csv"
|
||||||
[file filename]
|
[file filename & _]
|
||||||
(csv/parse-file file filename))
|
(csv/parse-file file filename))
|
||||||
|
|
||||||
(defmethod parse-file
|
(defmethod parse-file
|
||||||
"xls"
|
"xls"
|
||||||
[file filename]
|
[file filename & _]
|
||||||
(excel/parse-file file filename))
|
(excel/parse-file file filename))
|
||||||
|
|
||||||
|
|
||||||
(defmethod parse-file
|
(defmethod parse-file
|
||||||
"xlsx"
|
"xlsx"
|
||||||
[file filename]
|
[file filename & _]
|
||||||
(excel/parse-file file filename))
|
(excel/parse-file file filename))
|
||||||
|
|
||||||
(defn best-match
|
(defn best-match
|
||||||
@@ -203,3 +209,7 @@
|
|||||||
(map
|
(map
|
||||||
(fn [x] (dissoc x :full-text :text))
|
(fn [x] (dissoc x :full-text :text))
|
||||||
(parse v)))
|
(parse v)))
|
||||||
|
|
||||||
|
#_(nth (re-find #"ELECTRONICALLY.*\n\s*(.*?)\s{2,}" @last-text)
|
||||||
|
|
||||||
|
1)
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
(ns auto-ap.parse.templates
|
(ns auto-ap.parse.templates
|
||||||
(:require [auto-ap.parse.util :as u]
|
(:require [auto-ap.parse.util :as u]
|
||||||
[auto-ap.logging :as alog]
|
[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
|
(def pdf-templates
|
||||||
@@ -682,7 +684,8 @@
|
|||||||
:parser {:date [:clj-time "MM/dd/yy"]
|
:parser {:date [:clj-time "MM/dd/yy"]
|
||||||
:total [:trim-commas-and-negate nil]}}
|
:total [:trim-commas-and-negate nil]}}
|
||||||
|
|
||||||
{:vendor "Reel Produce"
|
;; TODO DISABLING TO FOCUS ON STATEMENT
|
||||||
|
#_{:vendor "Reel Produce"
|
||||||
:keywords [#"reelproduce.com"]
|
:keywords [#"reelproduce.com"]
|
||||||
:extract {:date #"([0-9]+/[0-9]+/[0-9]+)"
|
:extract {:date #"([0-9]+/[0-9]+/[0-9]+)"
|
||||||
:customer-identifier #"Bill To(?:.*?)\n\n\s+(.*?)\s{2,}"
|
:customer-identifier #"Bill To(?:.*?)\n\n\s+(.*?)\s{2,}"
|
||||||
@@ -701,6 +704,29 @@
|
|||||||
:total [:trim-commas-and-negate nil]}
|
:total [:trim-commas-and-negate nil]}
|
||||||
:multi #"\n"
|
:multi #"\n"
|
||||||
:multi-match? #"^\s*Invoice\s{2,}"}
|
: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"
|
{:vendor "Mani Imports"
|
||||||
:keywords [#"Mani Imports"]
|
:keywords [#"Mani Imports"]
|
||||||
:extract {:date #"Order Date\s+([0-9]+/[0-9]+/[0-9]+)"
|
:extract {:date #"Order Date\s+([0-9]+/[0-9]+/[0-9]+)"
|
||||||
@@ -710,19 +736,19 @@
|
|||||||
:parser {:date [:clj-time "MM/dd/yyyy"]
|
:parser {:date [:clj-time "MM/dd/yyyy"]
|
||||||
:total [:trim-commas-and-negate nil]}}
|
:total [:trim-commas-and-negate nil]}}
|
||||||
{:vendor "Reel Produce"
|
{:vendor "Reel Produce"
|
||||||
:keywords [#"REEL Produce, Inc" #"Statem"]
|
:keywords [#"reelproduce.com" #"Statem"]
|
||||||
:extract {:date #"\s*([0-9]+/[0-9]+/[0-9]+)"
|
:extract {:date #"\s*([0-9]+/[0-9]+/[0-9]+)"
|
||||||
:customer-identifier #"To:\s*\n\s+(.*?)\s{2,}"
|
:customer-identifier #"ELECTRONICALLY.*\n\s*(.*?)\s{2,}"
|
||||||
:invoice-number #"INV #(\d+)"
|
:invoice-number #"#(\d+)"
|
||||||
:total #"INV #(?:.*?)\s{2,}([\d\-,]+\.\d{2,2}+)"}
|
:total #"([\d\-,]+\.\d{2,2})"}
|
||||||
:parser {:date [:clj-time "MM/dd/yyyy"]
|
:parser {:date [:clj-time "MM/dd/yyyy"]
|
||||||
:total [:trim-commas-and-negate nil]}
|
:total [:trim-commas-and-negate nil]}
|
||||||
:multi #"\n"
|
:multi #"\n"
|
||||||
:multi-match? #"INV #"}
|
:multi-match? #"INV #"}
|
||||||
{:vendor "Paulino's Bakery"
|
{:vendor "Paulino's Bakery"
|
||||||
:keywords [#"Paulino's Bakery" #"Statement"]
|
:keywords [#"paulinosbakery"]
|
||||||
:extract {:date #"\s*([0-9]+/[0-9]+/[0-9]+)"
|
:extract {:date #"\s*([0-9]+/[0-9]+/[0-9]+)"
|
||||||
:customer-identifier #"To:\s*\n\s+(.*?)\s{2,}"
|
:customer-identifier #"831-333-1010\s*\n\s+(.*?)\s{2,}"
|
||||||
:invoice-number #"INV #(\d+)"
|
:invoice-number #"INV #(\d+)"
|
||||||
:total #"INV #(?:.*?)\s{2,}([\d\-,]+\.\d{2,2}+)"}
|
:total #"INV #(?:.*?)\s{2,}([\d\-,]+\.\d{2,2}+)"}
|
||||||
:parser {:date [:clj-time "MM/dd/yyyy"]
|
:parser {:date [:clj-time "MM/dd/yyyy"]
|
||||||
@@ -768,13 +794,33 @@
|
|||||||
(->> r first not-empty))))
|
(->> r first not-empty))))
|
||||||
(map
|
(map
|
||||||
(fn [[customer-number _ _ _ invoice-number date amount :as row]]
|
(fn [[customer-number _ _ _ invoice-number date amount :as row]]
|
||||||
|
(println "DAT E is" date)
|
||||||
{:customer-identifier customer-number
|
{:customer-identifier customer-number
|
||||||
:text (str/join " " row)
|
:text (str/join " " row)
|
||||||
:full-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
|
:invoice-number invoice-number
|
||||||
:total (str amount)
|
:total (str amount)
|
||||||
:vendor-code vendor})))
|
:vendor-code vendor})))
|
||||||
conj
|
conj
|
||||||
[]
|
[]
|
||||||
sheet)))}])
|
sheet)))}])
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
(java.text DecimalFormat)
|
(java.text DecimalFormat)
|
||||||
(java.util UUID)))
|
(java.util UUID)))
|
||||||
|
|
||||||
|
(def ^:dynamic *report-pedantic* false)
|
||||||
|
|
||||||
(defn cell->pdf [cell]
|
(defn cell->pdf [cell]
|
||||||
(let [cell-contents (cond
|
(let [cell-contents (cond
|
||||||
(and (= :dollar (:format cell))
|
(and (= :dollar (:format cell))
|
||||||
@@ -28,7 +30,7 @@
|
|||||||
(.format (DecimalFormat. "$###,##0.00") (:value cell))
|
(.format (DecimalFormat. "$###,##0.00") (:value cell))
|
||||||
|
|
||||||
(= :percent (:format cell))
|
(= :percent (:format cell))
|
||||||
(.format (DecimalFormat. "0%") (:value cell))
|
(.format (DecimalFormat. (if *report-pedantic* "0.00%" "0%")) (:value cell))
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(str (:value cell)))]
|
(str (:value cell)))]
|
||||||
|
|||||||
@@ -278,7 +278,7 @@
|
|||||||
"text/csv"
|
"text/csv"
|
||||||
"application/pdf")
|
"application/pdf")
|
||||||
:content-length (.length tempfile)})
|
:content-length (.length tempfile)})
|
||||||
imports (->> (parse/parse-file (.getPath tempfile) filename)
|
imports (->> (parse/parse-file (.getPath tempfile) filename :allow-glimpse? true)
|
||||||
(map #(assoc %
|
(map #(assoc %
|
||||||
:client-override client
|
:client-override client
|
||||||
:location-override location
|
:location-override location
|
||||||
|
|||||||
@@ -180,10 +180,10 @@
|
|||||||
|
|
||||||
(defn pc [start end]
|
(defn pc [start end]
|
||||||
{"query" {"filter" {"date_time_filter"
|
{"query" {"filter" {"date_time_filter"
|
||||||
{"created_at" {"start_at" (->square-date start)
|
{"updated_at" {"start_at" (->square-date start)
|
||||||
"end_at" (->square-date end)}}}
|
"end_at" (->square-date end)}}}
|
||||||
|
|
||||||
"sort" {"sort_field" "CREATED_AT"
|
"sort" {"sort_field" "UPDATED_AT"
|
||||||
"sort_order" "DESC"}}})
|
"sort_order" "DESC"}}})
|
||||||
|
|
||||||
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
|
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
|
||||||
@@ -388,7 +388,7 @@
|
|||||||
|
|
||||||
(defn daily-results
|
(defn daily-results
|
||||||
([client location]
|
([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]
|
([client location start end]
|
||||||
(capture-context->lc
|
(capture-context->lc
|
||||||
(->
|
(->
|
||||||
@@ -458,7 +458,7 @@
|
|||||||
(:payout_entries result)))))))
|
(:payout_entries result)))))))
|
||||||
|
|
||||||
(defn payouts
|
(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]
|
([client location start end]
|
||||||
(with-context-as {:location (:square-location/client-location location)} lc
|
(with-context-as {:location (:square-location/client-location location)} lc
|
||||||
(de/chain (manifold-api-call
|
(de/chain (manifold-api-call
|
||||||
@@ -599,7 +599,7 @@
|
|||||||
(apply de/zip
|
(apply de/zip
|
||||||
(for [square-location (:client/square-locations client)
|
(for [square-location (:client/square-locations client)
|
||||||
:when (:square-location/client-location square-location)]
|
: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]
|
([client location start end]
|
||||||
(capture-context->lc
|
(capture-context->lc
|
||||||
(de/chain (daily-results client location start end)
|
(de/chain (daily-results client location start end)
|
||||||
@@ -618,7 +618,7 @@
|
|||||||
:when (:square-location/client-location square-location)]
|
:when (:square-location/client-location square-location)]
|
||||||
(upsert-payouts client square-location))))
|
(upsert-payouts client square-location))))
|
||||||
([client 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]
|
([client location start end]
|
||||||
(with-context-as {:source "Square payout loading"
|
(with-context-as {:source "Square payout loading"
|
||||||
:client (:client/code client)} lc
|
:client (:client/code client)} lc
|
||||||
@@ -825,7 +825,7 @@
|
|||||||
(apply de/zip
|
(apply de/zip
|
||||||
(for [square-location (:client/square-locations client)
|
(for [square-location (:client/square-locations client)
|
||||||
:when (:square-location/client-location square-location)]
|
: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]
|
([client location start end]
|
||||||
(let [start (max-date start (coerce/to-date-time #inst "2024-04-15T00:00:00-08:00"))]
|
(let [start (max-date start (coerce/to-date-time #inst "2024-04-15T00:00:00-08:00"))]
|
||||||
(capture-context->lc
|
(capture-context->lc
|
||||||
@@ -1057,4 +1057,11 @@
|
|||||||
|
|
||||||
#_(filter (comp #{"OTHER"} :type) (mapcat :tenders z))
|
#_(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" ))
|
||||||
|
)
|
||||||
|
|
||||||
)
|
)
|
||||||
@@ -520,7 +520,8 @@
|
|||||||
["manually-pay-cintas" "Manually Pay Cintas"]
|
["manually-pay-cintas" "Manually Pay Cintas"]
|
||||||
["include-in-ntg-corp-reports" "Include in NTG Corporate reports"]
|
["include-in-ntg-corp-reports" "Include in NTG Corporate reports"]
|
||||||
["import-custom-amount" "Import Custom Amount Line Items from Square"]
|
["import-custom-amount" "Import Custom Amount Line Items from Square"]
|
||||||
["code-sysco-items" "Code individual sysco line items"]]})))
|
["code-sysco-items" "Code individual sysco line items"]
|
||||||
|
["report-pedantic" "Show two decimals in reports"]]})))
|
||||||
|
|
||||||
(com/data-grid-cell {:class "align-top"}
|
(com/data-grid-cell {:class "align-top"}
|
||||||
(com/a-icon-button {"@click.prevent.stop" "$refs.p.remove()"} svg/x))))
|
(com/a-icon-button {"@click.prevent.stop" "$refs.p.remove()"} svg/x))))
|
||||||
|
|||||||
319
src/clj/auto_ap/ssr/company.clj.bak
Normal file
319
src/clj/auto_ap/ssr/company.clj.bak
Normal 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")})))
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
(ns auto-ap.ssr.invoice.import
|
(ns auto-ap.ssr.invoice.import
|
||||||
(:require [amazonica.aws.s3 :as s3]
|
(:require
|
||||||
|
[amazonica.aws.s3 :as s3]
|
||||||
[auto-ap.datomic
|
[auto-ap.datomic
|
||||||
:refer [add-sorter-fields apply-pagination apply-sort-3
|
:refer [add-sorter-fields apply-pagination apply-sort-3
|
||||||
audit-transact conn merge-query observable-query
|
audit-transact conn merge-query observable-query
|
||||||
@@ -7,9 +8,8 @@
|
|||||||
[auto-ap.datomic.clients :as d-clients]
|
[auto-ap.datomic.clients :as d-clients]
|
||||||
[auto-ap.datomic.invoices :as d-invoices]
|
[auto-ap.datomic.invoices :as d-invoices]
|
||||||
[auto-ap.datomic.vendors :as d-vendors]
|
[auto-ap.datomic.vendors :as d-vendors]
|
||||||
[auto-ap.graphql.utils :refer [assert-can-see-client
|
[auto-ap.graphql.utils :refer [assert-can-see-client assert-not-locked
|
||||||
assert-not-locked
|
can-see-client? exception->notification
|
||||||
exception->notification
|
|
||||||
extract-client-ids]]
|
extract-client-ids]]
|
||||||
[auto-ap.logging :as alog]
|
[auto-ap.logging :as alog]
|
||||||
[auto-ap.parse :as parse]
|
[auto-ap.parse :as parse]
|
||||||
@@ -656,17 +656,28 @@
|
|||||||
:invoice/status :invoice-status/unpaid}))
|
:invoice/status :invoice-status/unpaid}))
|
||||||
|
|
||||||
(defn validate-invoice [invoice user]
|
(defn validate-invoice [invoice user]
|
||||||
(when-not (:invoice/client invoice)
|
(let [missing-keys (for [k [:invoice/invoice-number :invoice/client :invoice/vendor :invoice/total :invoice/outstanding-balance :invoice/date]
|
||||||
(throw (ex-info (str "Searched clients for '" (:invoice/client-identifier invoice) "'. No client found in file. Select a client first.")
|
:when (not (get invoice k))]
|
||||||
{:invoice-number (:invoice/invoice-number invoice)
|
k
|
||||||
:customer-identifier (:invoice/client-identifier invoice)})))
|
)]
|
||||||
(assert-can-see-client user (:invoice/client invoice))
|
(cond
|
||||||
(doseq [k [:invoice/invoice-number :invoice/client :invoice/vendor :invoice/total :invoice/outstanding-balance :invoice/date]]
|
(not (:invoice/client invoice))
|
||||||
(when (not (get invoice k))
|
(do
|
||||||
(throw (ex-info (str (name k) "not found on invoice " invoice)
|
(alog/warn ::no-client :invoice invoice)
|
||||||
invoice))))
|
(assoc invoice :error-message (str "Searched clients for '" (:invoice/client-identifier invoice) "'. No client found in file. Select a client first.")))
|
||||||
|
|
||||||
invoice)
|
(not (can-see-client? user (:invoice/client invoice)))
|
||||||
|
(do
|
||||||
|
(alog/warn ::cant-see-client :invoice invoice )
|
||||||
|
(assoc invoice :error-message "No access for the client in this file.")
|
||||||
|
)
|
||||||
|
|
||||||
|
(seq missing-keys)
|
||||||
|
(do
|
||||||
|
(alog/warn ::mising-keys :keys missing-keys)
|
||||||
|
(assoc invoice :error-message (str "Missing the key " missing-keys)))
|
||||||
|
:else
|
||||||
|
invoice)))
|
||||||
|
|
||||||
(defn admin-only-if-multiple-clients [is]
|
(defn admin-only-if-multiple-clients [is]
|
||||||
(let [client-count (->> is
|
(let [client-count (->> is
|
||||||
@@ -683,19 +694,29 @@
|
|||||||
(map #(import->invoice % user))
|
(map #(import->invoice % user))
|
||||||
(map #(validate-invoice % user))
|
(map #(validate-invoice % user))
|
||||||
admin-only-if-multiple-clients
|
admin-only-if-multiple-clients
|
||||||
|
)
|
||||||
|
errored-invoices (->> potential-invoices
|
||||||
|
(filter #(:error-message %)))
|
||||||
|
successful-invoices (->> potential-invoices
|
||||||
|
(filter #(not (:error-message %))))
|
||||||
|
proposed-invoices (->> potential-invoices
|
||||||
|
(filter #(not (:error-message %)))
|
||||||
(mapv d-invoices/code-invoice)
|
(mapv d-invoices/code-invoice)
|
||||||
(mapv (fn [i] [:propose-invoice i])))]
|
(mapv (fn [i] [:propose-invoice i])))]
|
||||||
|
|
||||||
(alog/info ::creating-invoice :invoices potential-invoices)
|
(alog/info ::creating-invoice :invoices proposed-invoices)
|
||||||
(let [tx (audit-transact potential-invoices user)]
|
(let [tx (audit-transact proposed-invoices user)]
|
||||||
(when-not (seq (dc/q '[:find ?i
|
#_(when-not (seq (dc/q '[:find ?i
|
||||||
:in $ [?i ...]
|
:in $ [?i ...]
|
||||||
:where [?i :invoice/invoice-number]]
|
:where [?i :invoice/invoice-number]]
|
||||||
(:db-after tx)
|
(:db-after tx)
|
||||||
(map :e (:tx-data tx))))
|
(map :e (:tx-data tx))))
|
||||||
(throw (ex-info "No new invoices found."
|
(throw (ex-info "No new invoices found."
|
||||||
{:template (:template (first imports))})))
|
{:template (:template (first imports))})))
|
||||||
tx)))
|
{:tx tx
|
||||||
|
:errored-invoices errored-invoices
|
||||||
|
:successful-invoices successful-invoices
|
||||||
|
:imports imports})))
|
||||||
|
|
||||||
(defn import-internal [tempfile filename force-client force-location force-vendor force-chatgpt identity]
|
(defn import-internal [tempfile filename force-client force-location force-vendor force-chatgpt identity]
|
||||||
(mu/with-context {:parsing-file filename}
|
(mu/with-context {:parsing-file filename}
|
||||||
@@ -711,7 +732,7 @@
|
|||||||
:content-length (.length tempfile)})
|
:content-length (.length tempfile)})
|
||||||
imports (->> (if force-chatgpt
|
imports (->> (if force-chatgpt
|
||||||
(parse/glimpse2 (.getPath tempfile))
|
(parse/glimpse2 (.getPath tempfile))
|
||||||
(parse/parse-file (.getPath tempfile) filename))
|
(parse/parse-file (.getPath tempfile) filename :allow-glimpse? true))
|
||||||
(map #(assoc %
|
(map #(assoc %
|
||||||
:client-override force-client
|
:client-override force-client
|
||||||
:location-override force-location
|
:location-override force-location
|
||||||
@@ -722,6 +743,7 @@
|
|||||||
(try
|
(try
|
||||||
|
|
||||||
(import-uploaded-invoice identity imports)
|
(import-uploaded-invoice identity imports)
|
||||||
|
|
||||||
(catch Exception e
|
(catch Exception e
|
||||||
(alog/warn ::couldnt-import-upload
|
(alog/warn ::couldnt-import-upload
|
||||||
:error e
|
:error e
|
||||||
@@ -729,8 +751,7 @@
|
|||||||
(throw (ex-info (ex-message e)
|
(throw (ex-info (ex-message e)
|
||||||
{:template (:template ( first imports))
|
{:template (:template ( first imports))
|
||||||
:sample (first imports)}
|
:sample (first imports)}
|
||||||
e))))
|
e)))))
|
||||||
imports)
|
|
||||||
(catch Exception e
|
(catch Exception e
|
||||||
(alog/warn ::couldnt-import-upload
|
(alog/warn ::couldnt-import-upload
|
||||||
:error e)
|
:error e)
|
||||||
@@ -746,59 +767,72 @@
|
|||||||
(fn [result {:keys [filename tempfile]}]
|
(fn [result {:keys [filename tempfile]}]
|
||||||
(try
|
(try
|
||||||
(let [i (import-internal tempfile filename force-client force-location force-vendor force-chatgpt (:identity request))]
|
(let [i (import-internal tempfile filename force-client force-location force-vendor force-chatgpt (:identity request))]
|
||||||
(update result :results conj {:filename filename
|
(alog/info ::failure-error-count :count (count (:errored-invoices i)) )
|
||||||
:response "success!"
|
|
||||||
:template (:template (first i))}))
|
(-> result
|
||||||
|
(update :error? #(or %
|
||||||
|
(boolean (seq (:errored-invoices i)))))
|
||||||
|
|
||||||
|
(update :files conj {:filename filename
|
||||||
|
:error? (boolean (seq (:errored-invoices i)))
|
||||||
|
:successful-invoices (count (:successful-invoices i))
|
||||||
|
:errors [:div
|
||||||
|
[:p.text-green-500 [:b (count (:successful-invoices i)) " succeeded in total."]
|
||||||
|
]
|
||||||
|
[:p [:b (count (:errored-invoices i)) " failed in total."]
|
||||||
|
]
|
||||||
|
[:ul
|
||||||
|
(for [e (take 5 (:errored-invoices i))]
|
||||||
|
[:li (:error-message e)]) ]]
|
||||||
|
:template (:template (first (:imports i)))})))
|
||||||
(catch Exception e
|
(catch Exception e
|
||||||
(-> result
|
(-> result
|
||||||
(assoc :error? true)
|
(assoc :error? true)
|
||||||
(update :results conj {:filename filename
|
(update :files conj {:filename filename
|
||||||
|
:errors "Can't process file"
|
||||||
:response (.getMessage e)
|
:response (.getMessage e)
|
||||||
:sample (:sample (ex-data e))
|
:sample (:sample (ex-data e))
|
||||||
:template (:template (ex-data e))})))))
|
:template (:template (ex-data e))})))))
|
||||||
{:error? false :results []}
|
{:error? false
|
||||||
|
:files []
|
||||||
|
}
|
||||||
file)]
|
file)]
|
||||||
|
|
||||||
(html-response [:div#page-notification.p-4.rounded-lg
|
(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
|
[:table
|
||||||
[:thead
|
[:thead
|
||||||
[:tr [:td "File"] [:td "Result"]
|
[:tr [:td "File"] [:td "Result"]
|
||||||
[:td "Template"]
|
[:td "Template"]
|
||||||
(if (:error? results)
|
[:td "Sample"]]
|
||||||
[:td "Sample match"])]
|
|
||||||
#_[:tr "Result"]
|
#_[:tr "Result"]
|
||||||
#_[:tr "Template"]
|
#_[:tr "Template"]]
|
||||||
]
|
(for [r (:files results)]
|
||||||
(for [r (:results results)]
|
|
||||||
[:tr
|
[:tr
|
||||||
[:td.p-2.border
|
[:td.p-2.border.align-top
|
||||||
{:class (if (:error? results)
|
{:class (if (:error? r)
|
||||||
"bg-red-50 text-red-700 border-red-300"
|
"bg-red-50 text-red-700 border-red-300"
|
||||||
"bg-primary-50 text-primary-700 border-green-500")}
|
"bg-primary-50 text-primary-700 border-green-500")}
|
||||||
(:filename r)]
|
(:filename r)]
|
||||||
[:td.p-2.border
|
[:td.p-2.border.align-top
|
||||||
{:class (if (:error? results)
|
{:class (if (:error? r)
|
||||||
"bg-red-50 text-red-700 border-red-300"
|
"bg-red-50 text-red-700 border-red-300"
|
||||||
"bg-primary-50 text-primary-700 border-green-500")}
|
"bg-primary-50 text-primary-700 border-green-500")}
|
||||||
(:response r)]
|
(:errors r)]
|
||||||
[:td.p-2.border
|
[:td.p-2.border.align-top
|
||||||
{:class (if (:error? results)
|
{:class (if (:error? r)
|
||||||
"bg-red-50 text-red-700 border-red-300"
|
"bg-red-50 text-red-700 border-red-300"
|
||||||
"bg-primary-50 text-primary-700 border-green-500")}
|
"bg-primary-50 text-primary-700 border-green-500")}
|
||||||
"Template: " (:template r)]
|
"Template: " (:template r)]
|
||||||
(if (:error? results )
|
[:td.p-2.border.align-top
|
||||||
[:td.p-2.border
|
{:class (if (:error? r)
|
||||||
{:class "bg-red-50 text-red-700 border-red-300"}
|
"bg-red-50 text-red-700 border-red-300"
|
||||||
|
"bg-primary-50 text-primary-700 border-green-500")}
|
||||||
[:ul
|
[:ul
|
||||||
(for [[k v] (dissoc (:sample r) :template :source-url :full-text :text)]
|
(for [[k v] (dissoc (:sample r) :template :source-url :full-text :text)]
|
||||||
[:li (name k) ": " (str v)])]
|
[:li (name k) ": " (str v)])]
|
||||||
#_(:template r)])])]]
|
#_(:template r)]])]]
|
||||||
:headers
|
:headers
|
||||||
{"hx-trigger" "invalidated"})
|
{"hx-trigger" "invalidated"})))
|
||||||
))
|
|
||||||
|
|
||||||
#_(defn wrap-test [handler]
|
#_(defn wrap-test [handler]
|
||||||
(fn [request]
|
(fn [request]
|
||||||
|
|||||||
@@ -1166,7 +1166,7 @@
|
|||||||
:name (fc/field-name)
|
:name (fc/field-name)
|
||||||
:error? (fc/field-errors)
|
:error? (fc/field-errors)
|
||||||
:placeholder "10001"}))))
|
: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
|
(fc/with-field :handwritten-date
|
||||||
(com/validated-field
|
(com/validated-field
|
||||||
{:errors (fc/field-errors)
|
{:errors (fc/field-errors)
|
||||||
@@ -1187,6 +1187,7 @@
|
|||||||
(format "Pay in full ($%,.2f)" total)))}
|
(format "Pay in full ($%,.2f)" total)))}
|
||||||
{:value "advanced"
|
{:value "advanced"
|
||||||
:content "Customize payments"}]})
|
:content "Customize payments"}]})
|
||||||
|
|
||||||
[:div.space-y-4
|
[:div.space-y-4
|
||||||
(fc/with-field :invoices
|
(fc/with-field :invoices
|
||||||
(com/validated-field
|
(com/validated-field
|
||||||
@@ -1245,13 +1246,13 @@
|
|||||||
bank-account
|
bank-account
|
||||||
:payment-type/check
|
:payment-type/check
|
||||||
0
|
0
|
||||||
invoice-payment-lookup)]
|
invoice-payment-lookup
|
||||||
|
(:handwritten-date snapshot))]
|
||||||
(let [result (audit-transact
|
(let [result (audit-transact
|
||||||
(into [(assoc base-payment
|
(into [(assoc base-payment
|
||||||
:payment/type :payment-type/check
|
:payment/type :payment-type/check
|
||||||
:payment/status :payment-status/pending
|
:payment/status :payment-status/pending
|
||||||
:payment/check-number (:check-number snapshot)
|
:payment/check-number (:check-number snapshot))]
|
||||||
:payment/date (coerce/to-date (:handwritten-date snapshot)))]
|
|
||||||
(invoice-payments invoices invoice-payment-lookup))
|
(invoice-payments invoices invoice-payment-lookup))
|
||||||
(:identity request))]
|
(:identity request))]
|
||||||
(try
|
(try
|
||||||
@@ -1345,9 +1346,15 @@
|
|||||||
(= "" (:check-number snapshot)))
|
(= "" (:check-number snapshot)))
|
||||||
(throw (Exception. "Check number is required")))
|
(throw (Exception. "Check number is required")))
|
||||||
true))
|
true))
|
||||||
|
|
||||||
result (exception->4xx
|
result (exception->4xx
|
||||||
#(if (= :handwrite-check (:method snapshot))
|
#(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)
|
(add-handwritten-check request this snapshot)
|
||||||
|
(try
|
||||||
(print-checks-internal (map (fn [i] {:invoice-id (:invoice-id i)
|
(print-checks-internal (map (fn [i] {:invoice-id (:invoice-id i)
|
||||||
:amount (:amount i)})
|
:amount (:amount i)})
|
||||||
(:invoices snapshot))
|
(:invoices snapshot))
|
||||||
@@ -1362,7 +1369,10 @@
|
|||||||
(= :credit (:method snapshot))
|
(= :credit (:method snapshot))
|
||||||
:payment-type/credit
|
:payment-type/credit
|
||||||
:else :payment-type/debit)
|
:else :payment-type/debit)
|
||||||
identity)))]
|
identity
|
||||||
|
(:handwritten-date snapshot))
|
||||||
|
(catch Exception e
|
||||||
|
(println e))))))]
|
||||||
(modal-response
|
(modal-response
|
||||||
(com/modal {}
|
(com/modal {}
|
||||||
(com/modal-card-advanced
|
(com/modal-card-advanced
|
||||||
|
|||||||
@@ -7,8 +7,8 @@
|
|||||||
[auto-ap.ledger :refer [build-account-lookup upsert-running-balance]]
|
[auto-ap.ledger :refer [build-account-lookup upsert-running-balance]]
|
||||||
[auto-ap.ledger.reports :as l-reports]
|
[auto-ap.ledger.reports :as l-reports]
|
||||||
[auto-ap.logging :as alog]
|
[auto-ap.logging :as alog]
|
||||||
[auto-ap.pdf.ledger :refer [table->pdf]]
|
[auto-ap.pdf.ledger :refer [table->pdf *report-pedantic*]]
|
||||||
[auto-ap.permissions :refer [wrap-must]]
|
[auto-ap.permissions :refer [can? wrap-must]]
|
||||||
[auto-ap.routes.ledger :as route]
|
[auto-ap.routes.ledger :as route]
|
||||||
[auto-ap.routes.utils
|
[auto-ap.routes.utils
|
||||||
:refer [wrap-client-redirect-unauthenticated]]
|
:refer [wrap-client-redirect-unauthenticated]]
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
[:or
|
[:or
|
||||||
[:enum :all]
|
[:enum :all]
|
||||||
[:vector {:coerce? true :min 1}
|
[:vector {:coerce? true :min 1}
|
||||||
[:entity-map {:pull [:db/id :client/name]}]]]]
|
[:entity-map {:pull [:db/id :client/name :client/feature-flags]}]]]]
|
||||||
[:column-per-location {:default false}
|
[:column-per-location {:default false}
|
||||||
[:boolean {:decode/string {:enter #(if (= % "on") true
|
[:boolean {:decode/string {:enter #(if (= % "on") true
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@
|
|||||||
data (into []
|
data (into []
|
||||||
(for [client-id client-ids
|
(for [client-id client-ids
|
||||||
p periods
|
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)]]
|
:let [account ((or (lookup-account client-id) {}) account-id)]]
|
||||||
{:client-id client-id
|
{:client-id client-id
|
||||||
:account-id account-id
|
:account-id account-id
|
||||||
@@ -102,12 +102,16 @@
|
|||||||
:account-type (:account_type account)
|
:account-type (:account_type account)
|
||||||
:numeric-code (:numeric_code account)
|
:numeric-code (:numeric_code account)
|
||||||
:name (:name account)
|
:name (:name account)
|
||||||
:period {:start ( coerce/to-date (:start p)) :end (coerce/to-date (time/plus (:end p) (time/days 1)))}}))
|
:sample sample
|
||||||
|
:period {:start ( coerce/to-date (:start p)) :end (coerce/to-date (:end p))}}))
|
||||||
|
|
||||||
args (assoc (:form-params request)
|
args (assoc (:form-params request)
|
||||||
:periods (map (fn [d] {:start ( coerce/to-date (:start d)) :end ( coerce/to-date (:end d))}) periods))
|
:periods (map (fn [d]
|
||||||
clients (pull-many (dc/db conn) [:client/code :client/name :db/id] client-ids)
|
{:start ( coerce/to-date (:start d)) :end ( coerce/to-date (:end d))}) periods))
|
||||||
|
clients (pull-many (dc/db conn) [:client/code :client/name :db/id :client/feature-flags] client-ids)
|
||||||
|
|
||||||
pnl-data (l-reports/->PNLData args data (by :db/id :client/code clients))
|
pnl-data (l-reports/->PNLData args data (by :db/id :client/code clients))
|
||||||
|
#_#__ (clojure.pprint/pprint pnl-data)
|
||||||
report (l-reports/summarize-pnl pnl-data)]
|
report (l-reports/summarize-pnl pnl-data)]
|
||||||
(alog/info ::profit-and-loss :params args)
|
(alog/info ::profit-and-loss :params args)
|
||||||
{:data report
|
{:data report
|
||||||
@@ -137,7 +141,19 @@
|
|||||||
[13 6])))))
|
[13 6])))))
|
||||||
:investigate-url (bidi.bidi/path-for ssr-routes/only-routes ::route/investigate)
|
:investigate-url (bidi.bidi/path-for ssr-routes/only-routes ::route/investigate)
|
||||||
:table table-contents
|
: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")]))]
|
||||||
|
}))))])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -213,10 +229,21 @@
|
|||||||
:size :letter
|
:size :letter
|
||||||
:font {:size 6
|
:font {:size 6
|
||||||
:ttf-name "fonts/calibri-light.ttf"}}
|
:ttf-name "fonts/calibri-light.ttf"}}
|
||||||
[:heading (str "Balance Sheet - " (str/join ", " (map :client/name (seq (:client (:form-params request))))))]]
|
[:heading (str "Profit and Loss - " (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
|
|
||||||
|
(into
|
||||||
|
(for [table (concat (:summaries report)
|
||||||
|
(:details report))]
|
||||||
|
(table->pdf table
|
||||||
|
(into [20] (take (dec (cell-count table))
|
||||||
|
(mapcat identity
|
||||||
|
(repeat
|
||||||
|
(if (-> (:form-params request) :include-deltas)
|
||||||
|
[13 6 13]
|
||||||
|
[13 6]))))))))
|
||||||
|
#_(conj
|
||||||
(table->pdf table
|
(table->pdf table
|
||||||
(into [20] (take (dec (cell-count table))
|
(into [20] (take (dec (cell-count table))
|
||||||
(mapcat identity
|
(mapcat identity
|
||||||
@@ -235,13 +262,14 @@
|
|||||||
(:date (:query-params request))
|
(:date (:query-params request))
|
||||||
atime/iso-date)
|
atime/iso-date)
|
||||||
name (->> request :query-params :client (map :db/id) join-names)]
|
name (->> request :query-params :client (map :db/id) join-names)]
|
||||||
(format "Balance-sheet-%s-for-%s" date name)))
|
(format "Profit-and-loss-%s-for-%s" date name)))
|
||||||
|
|
||||||
(defn print-profit-and-loss [request]
|
(defn print-profit-and-loss [request]
|
||||||
(let [uuid (str (UUID/randomUUID))
|
(let [uuid (str (UUID/randomUUID))
|
||||||
{:keys [client warning]} (maybe-trim-clients request (:client (:form-params request)))
|
{:keys [client warning]} (maybe-trim-clients request (:client (:form-params request)))
|
||||||
request (assoc-in request [:form-params :client] client)
|
request (assoc-in request [:form-params :client] client)
|
||||||
pdf-data (make-profit-and-loss-pdf request (:report (get-report request)))
|
pdf-data (binding [*report-pedantic* (boolean ((set (:client/feature-flags (first client)))
|
||||||
|
"report-pedantic"))] (make-profit-and-loss-pdf request (:report (get-report request))))
|
||||||
name (profit-and-loss-args->name request)
|
name (profit-and-loss-args->name request)
|
||||||
key (str "reports/profit-and-loss/" uuid "/" name ".pdf")
|
key (str "reports/profit-and-loss/" uuid "/" name ".pdf")
|
||||||
url (str "https://" (:data-bucket env) "/" key)]
|
url (str "https://" (:data-bucket env) "/" key)]
|
||||||
|
|||||||
@@ -715,8 +715,17 @@
|
|||||||
(:location %)))
|
(:location %)))
|
||||||
errors))))))
|
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]
|
(defn summarize-pnl [pnl-data]
|
||||||
{:warning (warning-message pnl-data)
|
{:warning (warning-message pnl-data)
|
||||||
|
:invalid-ids (invalid-ids pnl-data)
|
||||||
:summaries
|
:summaries
|
||||||
(if (-> pnl-data :args :column-per-location)
|
(if (-> pnl-data :args :column-per-location)
|
||||||
[(location-summary-table (mapcat identity (for [[period i] (map vector (-> pnl-data :args :periods ) (range))]
|
[(location-summary-table (mapcat identity (for [[period i] (map vector (-> pnl-data :args :periods ) (range))]
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
#sudo docker run --rm -ti -v ~/dev/integreat/data/solr:/var/solr --network=bridge -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
|
#sudo podman container run --user 1000 --privileged --volume /home/notid/dev/integreat/data/solr:/var/solr -p 8983:8983 bryce-solr
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user