diff --git a/e2e/bulk-code-transactions.spec.ts b/e2e/bulk-code-transactions.spec.ts
index 90dec175..4c3bf9e9 100644
--- a/e2e/bulk-code-transactions.spec.ts
+++ b/e2e/bulk-code-transactions.spec.ts
@@ -375,7 +375,6 @@ test.describe('Bulk Code Transactions - Account Distribution', () => {
await addNewAccount(page);
await selectAccountFromTypeahead(page, 0, 'test-account');
- // "Shared" should be valid for accounts without fixed location
await setAccountLocation(page, 0, 'Shared');
await setAccountPercentage(page, 0, '100');
@@ -385,3 +384,60 @@ test.describe('Bulk Code Transactions - Account Distribution', () => {
await page.waitForSelector('table tbody tr');
});
});
+
+test.describe('Bulk Code Transactions - Vendor Pre-population', () => {
+ test('should pre-populate default account when vendor is selected', async ({ page }) => {
+ await navigateToTransactions(page);
+ await selectTransactionByIndex(page, 0);
+ await openBulkCodeModal(page);
+
+ // Select vendor (test vendor has default-account set to test-account)
+ const testInfo = await getTestInfo(page);
+ const vendorId = testInfo.accounts.vendor;
+
+ // The vendor typeahead dispatches change from its parent div
+ // We need to set the hidden input and dispatch change on the container
+ const vendorContainer = page.locator('div[hx-post*="vendor-changed"]').first();
+ const vendorHidden = vendorContainer.locator('input[type="hidden"]').first();
+
+ await vendorHidden.evaluate((el: HTMLInputElement, value: string) => {
+ const newInput = document.createElement('input');
+ newInput.type = 'hidden';
+ newInput.name = el.name;
+ newInput.value = value;
+ el.parentNode.replaceChild(newInput, el);
+ }, vendorId.toString());
+
+ // Dispatch change on the container to trigger HTMX
+ await vendorContainer.evaluate((el: HTMLElement) => {
+ el.dispatchEvent(new Event('change', { bubbles: true }));
+ });
+
+ // Wait for HTMX response
+ await page.waitForResponse(response => response.url().includes('/vendor-changed') && response.status() === 200);
+ await page.waitForTimeout(500);
+
+ // Account should be pre-populated - check for account row
+ const accountRows = page.locator('#account-entries tbody tr');
+ const rowCount = await accountRows.count();
+
+ // Should have at least 1 account row (the default account) plus the new-row button
+ expect(rowCount).toBeGreaterThanOrEqual(2);
+
+ // The account should have a hidden input with the test-account ID
+ const accountHidden = page.locator('input[type="hidden"][name*="[account]"]').first();
+ const accountValue = await accountHidden.inputValue();
+ expect(accountValue).toBe(testInfo.accounts['test-account'].toString());
+
+ // Percentage should be 100
+ const percentageInput = page.locator('input[name*="percentage"]').first();
+ const percentageValue = await percentageInput.inputValue();
+ expect(percentageValue).toBe('100');
+
+ // Submit should succeed
+ await submitBulkCodeForm(page);
+ await closeBulkCodeModal(page);
+
+ await page.waitForSelector('table tbody tr');
+ });
+});
diff --git a/iol_ion/src/iol_ion/query.clj b/iol_ion/src/iol_ion/query.clj
index 8873ff86..82d5b276 100644
--- a/iol_ion/src/iol_ion/query.clj
+++ b/iol_ion/src/iol_ion/query.clj
@@ -14,7 +14,7 @@
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
(defn dollars= [amt1 amt2]
- (dollars-0? (- amt1 amt2) ))
+ (dollars-0? (- amt1 amt2)))
(defn localize [d]
(time/to-time-zone d (time/time-zone-for-id "America/Los_Angeles")))
@@ -22,7 +22,6 @@
(defn local-now []
(localize (time/now)))
-
(defn recent-date
([]
(recent-date 90))
@@ -32,16 +31,16 @@
(def excel-formatter (f/with-zone (f/formatter "MM/dd/yyyy") (time/time-zone-for-id "America/Los_Angeles")))
(defn excel-date [d]
(->> d
- (coerce/to-date-time)
- localize
- (f/unparse excel-formatter )))
+ (coerce/to-date-time)
+ localize
+ (f/unparse excel-formatter)))
(def iso-formatter (f/with-zone (f/formatter "yyyy-MM-dd") (time/time-zone-for-id "America/Los_Angeles")))
(defn iso-date [d]
(->> d
- (coerce/to-date-time)
- localize
- (f/unparse iso-formatter )))
+ (coerce/to-date-time)
+ localize
+ (f/unparse iso-formatter)))
(defn sales-orders-in-range [db client start end]
(let [end (or end #inst "2050-01-01")]
@@ -53,9 +52,6 @@
[client start]
[client end]))))
-
-
-
(defn can-see-client? [identity client]
(when (not client)
(println "WARNING - permission checking for null client"))
@@ -63,11 +59,9 @@
((set (map :db/id (:user/clients identity))) (:db/id client))
((set (map :db/id (:user/clients identity))) client)))
-
(defn ->pattern [x]
(. java.util.regex.Pattern (compile x java.util.regex.Pattern/CASE_INSENSITIVE)))
-
(defn dom [^java.util.Date x]
(-> x
(.toInstant)
@@ -85,8 +79,8 @@
:let [c (entid db c)]
r (seq (dc/index-range db
:sales-order/client+date
- [c (or start #inst "2001-01-01T08:00:00.000-00:00") ]
- [c (or (next-day end) #inst "2030-03-05T08:00:00.000-00:00") ]))]
+ [c (or start #inst "2001-01-01T08:00:00.000-00:00")]
+ [c (or (next-day end) #inst "2030-03-05T08:00:00.000-00:00")]))]
[(:e r) (first (:v r)) (second (:v r))]))
(defn scan-charges [db clients start end]
@@ -94,8 +88,8 @@
:let [c (entid db c)]
r (seq (dc/index-range db
:charge/client+date
- [c (or start #inst "2001-01-01T08:00:00.000-00:00") ]
- [c (or (next-day end) #inst "2030-03-05T08:00:00.000-00:00") ]))]
+ [c (or start #inst "2001-01-01T08:00:00.000-00:00")]
+ [c (or (next-day end) #inst "2030-03-05T08:00:00.000-00:00")]))]
[(:e r) (first (:v r)) (second (:v r))]))
(defn scan-sales-refunds [db clients start end]
@@ -103,8 +97,8 @@
:let [c (entid db c)]
r (seq (dc/index-range db
:sales-refund/client+date
- [c (or start #inst "2001-01-01T08:00:00.000-00:00") ]
- [c (or (next-day end) #inst "2030-03-05T08:00:00.000-00:00") ]))]
+ [c (or start #inst "2001-01-01T08:00:00.000-00:00")]
+ [c (or (next-day end) #inst "2030-03-05T08:00:00.000-00:00")]))]
[(:e r) (first (:v r)) (second (:v r))]))
(defn scan-expected-deposits [db clients start end]
@@ -112,8 +106,8 @@
:let [c (entid db c)]
r (seq (dc/index-range db
:expected-deposit/client+date
- [c (or start #inst "2001-01-01T08:00:00.000-00:00") ]
- [c (or (next-day end) #inst "2030-03-05T08:00:00.000-00:00") ]))]
+ [c (or start #inst "2001-01-01T08:00:00.000-00:00")]
+ [c (or (next-day end) #inst "2030-03-05T08:00:00.000-00:00")]))]
[(:e r) (first (:v r)) (second (:v r))]))
(defn scan-cash-drawer-shifts [db clients start end]
@@ -121,8 +115,8 @@
:let [c (entid db c)]
r (seq (dc/index-range db
:cash-drawer-shift/client+date
- [c (or start #inst "2001-01-01T08:00:00.000-00:00") ]
- [c (or (next-day end) #inst "2030-03-05T08:00:00.000-00:00") ]))]
+ [c (or start #inst "2001-01-01T08:00:00.000-00:00")]
+ [c (or (next-day end) #inst "2030-03-05T08:00:00.000-00:00")]))]
[(:e r) (first (:v r)) (second (:v r))]))
(defn scan-invoices [db clients start end]
@@ -130,17 +124,17 @@
:let [c (entid db c)]
r (seq (dc/index-range db
:invoice/client+date
- [c (or start #inst "2001-01-01T08:00:00.000-00:00") ]
- [c (or (next-day end) #inst "2030-03-05T08:00:00.000-00:00") ]))]
+ [c (or start #inst "2001-01-01T08:00:00.000-00:00")]
+ [c (or (next-day end) #inst "2030-03-05T08:00:00.000-00:00")]))]
[(:e r) (first (:v r)) (second (:v r))]))
(defn scan-transactions [db clients start end]
(for [c clients
:let [c (entid db c)]
r (seq (dc/index-range db
- :transaction/client+date
- [c (or start #inst "2001-01-01T08:00:00.000-00:00") ]
- [c (or (next-day end) #inst "2030-03-05T08:00:00.000-00:00") ]))]
+ :transaction/client+date
+ [c (or start #inst "2001-01-01T08:00:00.000-00:00")]
+ [c (or (next-day end) #inst "2030-03-05T08:00:00.000-00:00")]))]
[(:e r) (first (:v r)) (second (:v r))]))
(defn scan-ledger [db clients start end]
@@ -148,8 +142,8 @@
:let [c (entid db c)]
r (seq (dc/index-range db
:journal-entry/client+date
- [c (or start #inst "2001-01-01T08:00:00.000-00:00") ]
- [c (or (next-day end) #inst "2030-03-05T08:00:00.000-00:00") ]))]
+ [c (or start #inst "2001-01-01T08:00:00.000-00:00")]
+ [c (or (next-day end) #inst "2030-03-05T08:00:00.000-00:00")]))]
[(:e r) (first (:v r)) (second (:v r))]))
(defn scan-payments [db clients start end]
@@ -157,15 +151,14 @@
:let [c (entid db c)]
r (seq (dc/index-range db
:payment/client+date
- [c (or start #inst "2001-01-01T08:00:00.000-00:00") ]
- [c (or (next-day end) #inst "2030-03-05T08:00:00.000-00:00") ]))]
+ [c (or start #inst "2001-01-01T08:00:00.000-00:00")]
+ [c (or (next-day end) #inst "2030-03-05T08:00:00.000-00:00")]))]
[(:e r) (first (:v r)) (second (:v r))]))
(defn ident [x]
(:db/ident x))
-(deftype Line [^Long id ^Long client-id ^Long account-id ^String location ^java.util.Date date ^Double debit ^Double credit ^Double running-balance]
- )
+(deftype Line [^Long id ^Long client-id ^Long account-id ^String location ^java.util.Date date ^Double debit ^Double credit ^Double running-balance])
(defmethod print-method Line [entity writer]
(.write writer (format "Line %d: client:%d account:%d location:%s date:%s"
@@ -175,18 +168,16 @@
(.-location entity)
(iso-date (.-date entity)))))
-
(defn ->line [{[current-client current-account current-location current-date debit credit running-balance]
:v
id :e}]
- (Line. id current-client current-account current-location current-date debit credit running-balance)
- )
+ (Line. id current-client current-account current-location current-date debit credit running-balance))
+
+(defn compare-account [^Line l1 ^Line l2]
-(defn compare-account [^Line l1 ^Line l2]
-
(let [a (compare (.-date l1) (.-date l2))]
(if (not= 0 a)
- a
+ a
(compare (.-id l1) (.-id l2)))))
(defn account-sets [db client-id]
@@ -194,7 +185,7 @@
(seq)
(map ->line)
(partition-by (fn set-partition [^Line l]
- [(.-account-id l) (.-location l)]))) ]
+ [(.-account-id l) (.-location l)])))]
(->> running-balance-set
(sort compare-account))))
@@ -205,35 +196,35 @@
(take-while (fn until-date [^Line l]
(let [^java.util.Date d (.-date l)]
(<= (.compareTo ^java.util.Date d end) 0))))
- last) ]
+ last)]
:when (and z (.-id z))]
[(.-client-id z) (.-account-id z) (.-location z) (.-date z) (.-running-balance z)]))
-#_(doseq [[ n] (dc/q '[:find ?cd :where [?c :client/code ?cd] [?c :client/groups "NTG"]] (dc/db auto-ap.datomic/conn))]
- (println n)
- (dc/q '[:find ?code ?name ?afc ?an ?l ?d2 ?balance
- :in $ ?end ?group
- :where
- [(clj-time.coerce/to-date-time ?end) ?end2]
- [(iol-ion.query/localize ?end2) ?end3]
- [(clj-time.coerce/to-date ?end3) ?end4]
- (or
- [?c :client/groups ?group]
- [?c :client/code ?group])
- [?c :client/name ?name]
- [?c :client/code ?code]
- [?c :client/bank-accounts ?b]
- [(iol-ion.query/account-snapshot $ ?c ?end4) [?x ...]]
- [(untuple ?x) [_ ?a ?l ?date ?balance]]
- [(not= nil ?a)]
- [(iol-ion.query/excel-date ?date) ?d2]
- (or-join [?a ?afc ?an]
- (and [?a :account/name ?an]
- [?a :account/numeric-code ?afc])
- (and [?a :bank-account/name ?an]
- [?a :bank-account/numeric-code ?afc]))]
- (dc/db auto-ap.datomic/conn)
- #inst "2024-10-10" n))
+#_(doseq [[n] (dc/q '[:find ?cd :where [?c :client/code ?cd] [?c :client/groups "NTG"]] (dc/db auto-ap.datomic/conn))]
+ (println n)
+ (dc/q '[:find ?code ?name ?afc ?an ?l ?d2 ?balance
+ :in $ ?end ?group
+ :where
+ [(clj-time.coerce/to-date-time ?end) ?end2]
+ [(iol-ion.query/localize ?end2) ?end3]
+ [(clj-time.coerce/to-date ?end3) ?end4]
+ (or
+ [?c :client/groups ?group]
+ [?c :client/code ?group])
+ [?c :client/name ?name]
+ [?c :client/code ?code]
+ [?c :client/bank-accounts ?b]
+ [(iol-ion.query/account-snapshot $ ?c ?end4) [?x ...]]
+ [(untuple ?x) [_ ?a ?l ?date ?balance]]
+ [(not= nil ?a)]
+ [(iol-ion.query/excel-date ?date) ?d2]
+ (or-join [?a ?afc ?an]
+ (and [?a :account/name ?an]
+ [?a :account/numeric-code ?afc])
+ (and [?a :bank-account/name ?an]
+ [?a :bank-account/numeric-code ?afc]))]
+ (dc/db auto-ap.datomic/conn)
+ #inst "2024-10-10" n))
(defn detailed-account-snapshot
([db client-id ^java.util.Date end]
@@ -266,12 +257,11 @@
:credits 0.0
:current-balance 0.0})))]
:when client-id]
- (do
+ (do
[client-id account-id location debits credits current-balance count sample]))))
-
-(comment
- (->>
+(comment
+ (->>
(detailed-account-snapshot (dc/db auto-ap.datomic/conn)
(auto-ap.datomic/pull-id (dc/db auto-ap.datomic/conn)
[:client/code "NGOP"])
@@ -280,65 +270,65 @@
(into #{})
seq)
-(account-snapshot (dc/db auto-ap.datomic/conn)
- (auto-ap.datomic/pull-id (dc/db auto-ap.datomic/conn)
- [:client/code "NGOP"])
- #inst "2022-01-01")
+ (account-snapshot (dc/db auto-ap.datomic/conn)
+ (auto-ap.datomic/pull-id (dc/db auto-ap.datomic/conn)
+ [:client/code "NGOP"])
+ #inst "2022-01-01")
-(def orig (->> [:client/code "NGOP"]
- (auto-ap.datomic/pull-id (dc/db auto-ap.datomic/conn))
- (account-sets (dc/db auto-ap.datomic/conn))
- (mapcat (fn [ls]
- ls))
- (filter (fn [l] (nil? (.-location l))))
- (into #{})))
+ (def orig (->> [:client/code "NGOP"]
+ (auto-ap.datomic/pull-id (dc/db auto-ap.datomic/conn))
+ (account-sets (dc/db auto-ap.datomic/conn))
+ (mapcat (fn [ls]
+ ls))
+ (filter (fn [l] (nil? (.-location l))))
+ (into #{})))
-(.-location orig)
+ (.-location orig)
-(def orig (into [] (take 5000 (mapcat (fn [ls]
- (map #(.-id %) ls)) (account-sets (dc/db auto-ap.datomic/conn)
- (auto-ap.datomic/pull-id (dc/db auto-ap.datomic/conn)
- [:client/code "NGOP"]))))))
+ (def orig (into [] (take 5000 (mapcat (fn [ls]
+ (map #(.-id %) ls)) (account-sets (dc/db auto-ap.datomic/conn)
+ (auto-ap.datomic/pull-id (dc/db auto-ap.datomic/conn)
+ [:client/code "NGOP"]))))))
+
+ (def n (into [] (take 5000 (mapcat (fn [ls]
+ (map #(.-id %) ls)) (account-sets (dc/db auto-ap.datomic/conn)
+ (auto-ap.datomic/pull-id (dc/db auto-ap.datomic/conn)
+ [:client/code "NGOP"]))))))
- (def n (into [] (take 5000 (mapcat (fn [ls]
- (map #(.-id %) ls)) (account-sets (dc/db auto-ap.datomic/conn)
- (auto-ap.datomic/pull-id (dc/db auto-ap.datomic/conn)
- [:client/code "NGOP"]))))))
-
(= orig n)
-#_(seq (dc/q '[:find ?c ?a ?l ?date ?balance
- :in $
- :where [?c :client/code "NGOP"]
- [(iol-ion.query/account-snapshot $ ?c #inst "2023-01-01") [?x ...]]
- [(untuple ?x) [_ ?a ?l ?date ?balance]]]
- (dc/db auto-ap.datomic/conn)))
+ #_(seq (dc/q '[:find ?c ?a ?l ?date ?balance
+ :in $
+ :where [?c :client/code "NGOP"]
+ [(iol-ion.query/account-snapshot $ ?c #inst "2023-01-01") [?x ...]]
+ [(untuple ?x) [_ ?a ?l ?date ?balance]]]
+ (dc/db auto-ap.datomic/conn)))
-#_(->> (seq (dc/q '[:find ?code ?name ?afc ?an ?l ?d2 ?balance ?end4
- :in $ ?end ?group
- :where
- [(clj-time.coerce/to-date-time ?end) ?end2]
- [(iol-ion.query/localize ?end2) ?end3]
- [(clj-time.coerce/to-date ?end3) ?end4]
- (or
- [?c :client/groups ?group]
- [?c :client/code ?group])
- [?c :client/name ?name]
- [?c :client/code ?code]
- [?c :client/bank-accounts ?b]
- [(iol-ion.query/account-snapshot $ ?c ?end4) [?x ...]]
- [(untuple ?x) [_ ?a ?l ?date ?balance]]
- [(iol-ion.query/excel-date ?date) ?d2]
- [(not= nil ?a)]
- (or-join [?a ?afc ?an]
- (and [?a :account/name ?an]
- [?a :account/numeric-code ?afc])
- (and [?a :bank-account/name ?an]
- [?a :bank-account/numeric-code ?afc]))]
- (dc/db auto-ap.datomic/conn)
- #inst "2024-09-23"
- "NGKG"))
- (filter (fn [[_ _ afc]]
- (= 12990 afc)))
- (map (fn [[_ _ _ _ _ _ a]]
- (Math/round a)))))
\ No newline at end of file
+ #_(->> (seq (dc/q '[:find ?code ?name ?afc ?an ?l ?d2 ?balance ?end4
+ :in $ ?end ?group
+ :where
+ [(clj-time.coerce/to-date-time ?end) ?end2]
+ [(iol-ion.query/localize ?end2) ?end3]
+ [(clj-time.coerce/to-date ?end3) ?end4]
+ (or
+ [?c :client/groups ?group]
+ [?c :client/code ?group])
+ [?c :client/name ?name]
+ [?c :client/code ?code]
+ [?c :client/bank-accounts ?b]
+ [(iol-ion.query/account-snapshot $ ?c ?end4) [?x ...]]
+ [(untuple ?x) [_ ?a ?l ?date ?balance]]
+ [(iol-ion.query/excel-date ?date) ?d2]
+ [(not= nil ?a)]
+ (or-join [?a ?afc ?an]
+ (and [?a :account/name ?an]
+ [?a :account/numeric-code ?afc])
+ (and [?a :bank-account/name ?an]
+ [?a :bank-account/numeric-code ?afc]))]
+ (dc/db auto-ap.datomic/conn)
+ #inst "2024-09-23"
+ "NGKG"))
+ (filter (fn [[_ _ afc]]
+ (= 12990 afc)))
+ (map (fn [[_ _ _ _ _ _ a]]
+ (Math/round a)))))
\ No newline at end of file
diff --git a/iol_ion/src/iol_ion/tx.clj b/iol_ion/src/iol_ion/tx.clj
index 1d90c284..40f4f421 100644
--- a/iol_ion/src/iol_ion/tx.clj
+++ b/iol_ion/src/iol_ion/tx.clj
@@ -11,7 +11,6 @@
(def pull-many iol-ion.utils/pull-many)
(def remove-nils iol-ion.utils/remove-nils)
-
;; TODO expected-deposit ledger entry
#_(defmethod entity-change->ledger :expected-deposit
[db [type id]]
@@ -33,9 +32,6 @@
:location "A"
:account :account/ccp}]}))
-
-
-
(defn regenerate-literals []
(require 'com.github.ivarref.gen-fn)
(spit
diff --git a/iol_ion/src/iol_ion/tx/propose_invoice.clj b/iol_ion/src/iol_ion/tx/propose_invoice.clj
index 53481c58..21d74782 100644
--- a/iol_ion/src/iol_ion/tx/propose_invoice.clj
+++ b/iol_ion/src/iol_ion/tx/propose_invoice.clj
@@ -13,11 +13,11 @@
(:invoice/invoice-number invoice)
(:invoice/client invoice)
(:invoice/vendor invoice))))
- [ locked-until] (first (dc/q '[:find ?locked-until
- :in $ ?c
- :where [?c :client/locked-until ?locked-until]]
- db
- (:invoice/client invoice)))
+ [locked-until] (first (dc/q '[:find ?locked-until
+ :in $ ?c
+ :where [?c :client/locked-until ?locked-until]]
+ db
+ (:invoice/client invoice)))
is-locked? (cond
(not locked-until) false
(not (:invoice/date invoice)) true
diff --git a/iol_ion/src/iol_ion/tx/reset_rels.clj b/iol_ion/src/iol_ion/tx/reset_rels.clj
index 1110fcef..2762562b 100644
--- a/iol_ion/src/iol_ion/tx/reset_rels.clj
+++ b/iol_ion/src/iol_ion/tx/reset_rels.clj
@@ -4,11 +4,11 @@
(defn reset-rels [db e a vs]
(assert (every? :db/id vs) (format "In order to reset attribute %s, every value must have :db/id" a))
(let [ids (when-not (string? e)
- (->> (dc/q '[:find ?z
- :in $ ?e ?a
- :where [?e ?a ?z]]
- db e a)
- (map first)))
+ (->> (dc/q '[:find ?z
+ :in $ ?e ?a
+ :where [?e ?a ?z]]
+ db e a)
+ (map first)))
new-id-set (set (map :db/id vs))
retract-ids (filter (complement new-id-set) ids)
{is-component? :db/isComponent} (dc/pull db [:db/isComponent] a)
@@ -16,6 +16,6 @@
(-> []
(into (map (fn [i] (if is-component?
[:db/retractEntity i]
- [:db/retract e a i ])) retract-ids))
+ [:db/retract e a i])) retract-ids))
(into (map (fn [i] [:db/add e a i]) new-rels))
(into (map (fn [i] [:upsert-entity i]) vs)))))
diff --git a/iol_ion/src/iol_ion/tx/reset_scalars.clj b/iol_ion/src/iol_ion/tx/reset_scalars.clj
index bcd79e5f..c48e4ad8 100644
--- a/iol_ion/src/iol_ion/tx/reset_scalars.clj
+++ b/iol_ion/src/iol_ion/tx/reset_scalars.clj
@@ -2,7 +2,7 @@
(:require [datomic.api :as dc]))
(defn reset-scalars [db e a vs]
-
+
(let [extant (when-not (string? e)
(->> (dc/q '[:find ?z
:in $ ?e ?a
@@ -12,5 +12,5 @@
retracts (filter (complement (set vs)) extant)
new (filter (complement (set extant)) vs)]
(-> []
- (into (map (fn [i] [:db/retract e a i ]) retracts))
+ (into (map (fn [i] [:db/retract e a i]) retracts))
(into (map (fn [i] [:db/add e a i]) new)))))
diff --git a/iol_ion/src/iol_ion/tx/upsert_entity.clj b/iol_ion/src/iol_ion/tx/upsert_entity.clj
index e3b2f8f3..3ef5801d 100644
--- a/iol_ion/src/iol_ion/tx/upsert_entity.clj
+++ b/iol_ion/src/iol_ion/tx/upsert_entity.clj
@@ -5,7 +5,6 @@
)
(:import [java.util UUID]))
-
(defn -random-tempid []
(str (UUID/randomUUID)))
@@ -36,7 +35,6 @@
;; :else
;; v))
-
(defn upsert-entity [db entity]
(when-not (or (:db/id entity)
(:db/ident entity))
@@ -90,7 +88,7 @@
ops
;; reset relationships if it's refs, and not a lookup (i.e., seq of maps, or empty seq)
-
+
(and (sequential? v) (= :db.type/tuple (ident->value-type a)) (not (= :db.cardinality/many (ident->cardinality a))))
(conj ops [:db/add e a v])
diff --git a/iol_ion/src/iol_ion/tx/upsert_invoice.clj b/iol_ion/src/iol_ion/tx/upsert_invoice.clj
index cbbae5b1..4fe1ba68 100644
--- a/iol_ion/src/iol_ion/tx/upsert_invoice.clj
+++ b/iol_ion/src/iol_ion/tx/upsert_invoice.clj
@@ -4,13 +4,12 @@
(defn -remove-nils [m]
(let [result (reduce-kv
- (fn [m k v]
- (if (not (nil? v))
- (assoc m k v)
- m
- ))
- {}
- m)]
+ (fn [m k v]
+ (if (not (nil? v))
+ (assoc m k v)
+ m))
+ {}
+ m)]
(if (seq result)
result
nil)))
@@ -33,41 +32,38 @@
invoice-id)
credit-invoice? (< (:invoice/total entity 0.0) 0.0)]
(when-not (or
- (not (:invoice/total entity))
- (= true (:invoice/exclude-from-ledger entity))
- (= :import-status/pending (:db/ident (:invoice/import-status entity)))
- (= :invoice-status/voided (:db/ident (:invoice/status entity)))
- (< -0.001 (:invoice/total entity) 0.001))
-
- (-remove-nils
- {:journal-entry/source "invoice"
- :journal-entry/client (:db/id (:invoice/client entity))
- :journal-entry/date (:invoice/date entity)
- :journal-entry/original-entity raw-invoice-id
- :journal-entry/vendor (:db/id (:invoice/vendor entity))
- :journal-entry/amount (Math/abs (:invoice/total entity))
+ (not (:invoice/total entity))
+ (= true (:invoice/exclude-from-ledger entity))
+ (= :import-status/pending (:db/ident (:invoice/import-status entity)))
+ (= :invoice-status/voided (:db/ident (:invoice/status entity)))
+ (< -0.001 (:invoice/total entity) 0.001))
- :journal-entry/line-items (into [(cond-> {:db/id (str raw-invoice-id "-" 0)
- :journal-entry-line/account :account/accounts-payable
- :journal-entry-line/location "A"
- }
- credit-invoice? (assoc :journal-entry-line/debit (Math/abs (:invoice/total entity)))
- (not credit-invoice?) (assoc :journal-entry-line/credit (Math/abs (:invoice/total entity))))]
- (map-indexed (fn [i ea]
- (cond->
- {:db/id (str raw-invoice-id "-" (inc i))
- :journal-entry-line/account (:db/id (:invoice-expense-account/account ea))
- :journal-entry-line/location (or (:invoice-expense-account/location ea) "HQ")
- }
- credit-invoice? (assoc :journal-entry-line/credit (Math/abs (:invoice-expense-account/amount ea)))
- (not credit-invoice?) (assoc :journal-entry-line/debit (Math/abs (:invoice-expense-account/amount ea)))))
- (:invoice/expense-accounts entity)))
- :journal-entry/cleared (and (< (:invoice/outstanding-balance entity) 0.01)
- (every? #(= :payment-status/cleared (:payment/status %)) (:invoice/payments entity))
- )}))))
+ (-remove-nils
+ {:journal-entry/source "invoice"
+ :journal-entry/client (:db/id (:invoice/client entity))
+ :journal-entry/date (:invoice/date entity)
+ :journal-entry/original-entity raw-invoice-id
+ :journal-entry/vendor (:db/id (:invoice/vendor entity))
+ :journal-entry/amount (Math/abs (:invoice/total entity))
+
+ :journal-entry/line-items (into [(cond-> {:db/id (str raw-invoice-id "-" 0)
+ :journal-entry-line/account :account/accounts-payable
+ :journal-entry-line/location "A"}
+ credit-invoice? (assoc :journal-entry-line/debit (Math/abs (:invoice/total entity)))
+ (not credit-invoice?) (assoc :journal-entry-line/credit (Math/abs (:invoice/total entity))))]
+ (map-indexed (fn [i ea]
+ (cond->
+ {:db/id (str raw-invoice-id "-" (inc i))
+ :journal-entry-line/account (:db/id (:invoice-expense-account/account ea))
+ :journal-entry-line/location (or (:invoice-expense-account/location ea) "HQ")}
+ credit-invoice? (assoc :journal-entry-line/credit (Math/abs (:invoice-expense-account/amount ea)))
+ (not credit-invoice?) (assoc :journal-entry-line/debit (Math/abs (:invoice-expense-account/amount ea)))))
+ (:invoice/expense-accounts entity)))
+ :journal-entry/cleared (and (< (:invoice/outstanding-balance entity) 0.01)
+ (every? #(= :payment-status/cleared (:payment/status %)) (:invoice/payments entity)))}))))
(defn current-date [db]
- (let [ last-tx (dc/t->tx (dc/basis-t db))
+ (let [last-tx (dc/t->tx (dc/basis-t db))
[[date]] (seq (dc/q '[:find ?ti :in $ ?tx
:where [?tx :db/txInstant ?ti]]
db
@@ -80,15 +76,15 @@
invoice-id (or (-> with-invoice :tempids (get (:db/id invoice)))
(:db/id invoice))
journal-entry (invoice->journal-entry (:db-after with-invoice)
- invoice-id
- (:db/id invoice))
- client-id (-> (dc/pull (:db-after with-invoice)
- [{:invoice/client [:db/id]}]
+ invoice-id
+ (:db/id invoice))
+ client-id (-> (dc/pull (:db-after with-invoice)
+ [{:invoice/client [:db/id]}]
invoice-id)
- :invoice/client
+ :invoice/client
:db/id)]
(into upserted-entity
- (if journal-entry
+ (if journal-entry
[[:upsert-ledger journal-entry]]
[[:db/retractEntity [:journal-entry/original-entity (:db/id invoice)]]
{:db/id client-id
diff --git a/iol_ion/src/iol_ion/tx/upsert_ledger.clj b/iol_ion/src/iol_ion/tx/upsert_ledger.clj
index 9fd95220..0238eaaa 100644
--- a/iol_ion/src/iol_ion/tx/upsert_ledger.clj
+++ b/iol_ion/src/iol_ion/tx/upsert_ledger.clj
@@ -10,7 +10,7 @@
next-jel (->> (dc/index-pull db {:index :avet
:selector [:db/id :journal-entry-line/client+account+location+date]
:start [:journal-entry-line/client+account+location+date
- (:journal-entry-line/client+account+location+date jel)
+ (:journal-entry-line/client+account+location+date jel)
(:db/id jel)]})
(take-while (fn line-must-match-client-account-location [result]
(and
@@ -24,9 +24,8 @@
(def extant-read '[:db/id :journal-entry/date :journal-entry/client {:journal-entry/line-items [:journal-entry-line/account :journal-entry-line/location :db/id :journal-entry-line/client+account+location+date]}])
-
(defn current-date [db]
- (let [ last-tx (dc/t->tx (dc/basis-t db))
+ (let [last-tx (dc/t->tx (dc/basis-t db))
[[date]] (seq (dc/q '[:find ?ti :in $ ?tx
:where [?tx :db/txInstant ?ti]]
db
@@ -51,7 +50,7 @@
(let [extant-entry (or (when-let [original-entity (:journal-entry/original-entity ledger-entry)]
(dc/pull db extant-read [:journal-entry/original-entity original-entity]))
(when-let [external-id (:journal-entry/external-id ledger-entry)]
- (dc/pull db extant-read [:journal-entry/external-id external-id]))) ]
+ (dc/pull db extant-read [:journal-entry/external-id external-id])))]
(cond->
[[:upsert-entity (into (-> ledger-entry
@@ -59,11 +58,11 @@
(:db/id ledger-entry)
(:db/id extant-entry)
(-random-tempid)))
- (update :journal-entry/line-items
+ (update :journal-entry/line-items
(fn [lis]
(mapv #(-> %
(assoc :journal-entry-line/date (:journal-entry/date ledger-entry))
- (assoc :journal-entry-line/client (:journal-entry/client ledger-entry)))
+ (assoc :journal-entry-line/client (:journal-entry/client ledger-entry)))
lis)))))]
{:db/id (:journal-entry/client ledger-entry)
:client/ledger-last-change (current-date db)}])))
diff --git a/iol_ion/src/iol_ion/tx/upsert_sales_summary_ledger.clj b/iol_ion/src/iol_ion/tx/upsert_sales_summary_ledger.clj
index 3db00f80..93b8b60a 100644
--- a/iol_ion/src/iol_ion/tx/upsert_sales_summary_ledger.clj
+++ b/iol_ion/src/iol_ion/tx/upsert_sales_summary_ledger.clj
@@ -27,11 +27,10 @@
(get m :ledger-side/debit) (assoc :journal-entry-line/debit (get m :ledger-side/debit))
(get m :ledger-side/credit) (assoc :journal-entry-line/credit (get m :ledger-side/credit))))
aggregated)
-
+
total-debits (reduce + 0.0 (map #(get % :ledger-side/debit 0.0) aggregated))
total-credits (reduce + 0.0 (map #(get % :ledger-side/credit 0.0) aggregated))
-_ (clojure.pprint/pprint [total-debits total-credits])
- ]
+ _ (clojure.pprint/pprint [total-debits total-credits])]
(when (and (seq line-items)
(= (Math/round (* 1000 total-debits))
(Math/round (* 1000 total-credits))))
@@ -60,11 +59,10 @@ _ (clojure.pprint/pprint [total-debits total-credits])
journal-entry (summary->journal-entry db-after summary-id)]
upserted-summary
#_(into upserted-summary
- (if journal-entry
- [[:upsert-ledger journal-entry]]
- (concat
- [[:db/retractEntity [:journal-entry/original-entity (:db/id summary)]]]
+ (if journal-entry
+ [[:upsert-ledger journal-entry]]
+ (concat
+ [[:db/retractEntity [:journal-entry/original-entity (:db/id summary)]]]
-
- (when client-id [{:db/id client-id
- :client/ledger-last-change (current-date db)}]))))))
+ (when client-id [{:db/id client-id
+ :client/ledger-last-change (current-date db)}]))))))
diff --git a/iol_ion/src/iol_ion/tx/upsert_transaction.clj b/iol_ion/src/iol_ion/tx/upsert_transaction.clj
index 80c3559a..525df1a3 100644
--- a/iol_ion/src/iol_ion/tx/upsert_transaction.clj
+++ b/iol_ion/src/iol_ion/tx/upsert_transaction.clj
@@ -81,73 +81,70 @@
[[:upsert-ledger journal-entry]]
[[:db/retractEntity [:journal-entry/original-entity (:db/id transaction)]]]))))
-
#_(comment
;; If transactions are failing, it is likely that there are multiple bank accounts linked
;; to yodlee or plaid. here is how i debugged
- (upsert-transaction (dc/db auto-ap.datomic/conn) {:transaction/matched-rule 17592233159891,
- :db/id "34411061-4656-4e77-8cc0-2f2769b4324c",
- :transaction/status "POSTED",
- :transaction/description-original "Rotten Robbie #03",
- :transaction/approval-status {:db/id 17592231963877,
- :db/ident :transaction-approval-status/approved},
- :transaction/plaid-merchant {:db/id "223ceae4-d9e7-4e7f-92be-4fb00676088b",
- :plaid-merchant/name "Rotten Robbie"},
- :transaction/bank-account 17592232681223,
- :transaction/vendor 17592232627053,
- :transaction/date #inst "2024-02-24T08:00:00Z",
- :transaction/client 17592232577980,
- :transaction/id "11a4a13e713d63f476009027e9a53e217e13d0192a37df8ab96c0eed4bdbe996",
- :transaction/amount -84.43,
- :transaction/accounts [{:db/id "cad8463f-2dfe-47dc-ab17-831e87a633d5",
- :transaction-account/account 17592231963549,
- :transaction-account/location "CB",
- :transaction-account/amount 84.43}],
- :transaction/raw-id "gQypbv5946F08op74wZmidDg8qD8Q1fM6gEBP"})
+ (upsert-transaction (dc/db auto-ap.datomic/conn) {:transaction/matched-rule 17592233159891,
+ :db/id "34411061-4656-4e77-8cc0-2f2769b4324c",
+ :transaction/status "POSTED",
+ :transaction/description-original "Rotten Robbie #03",
+ :transaction/approval-status {:db/id 17592231963877,
+ :db/ident :transaction-approval-status/approved},
+ :transaction/plaid-merchant {:db/id "223ceae4-d9e7-4e7f-92be-4fb00676088b",
+ :plaid-merchant/name "Rotten Robbie"},
+ :transaction/bank-account 17592232681223,
+ :transaction/vendor 17592232627053,
+ :transaction/date #inst "2024-02-24T08:00:00Z",
+ :transaction/client 17592232577980,
+ :transaction/id "11a4a13e713d63f476009027e9a53e217e13d0192a37df8ab96c0eed4bdbe996",
+ :transaction/amount -84.43,
+ :transaction/accounts [{:db/id "cad8463f-2dfe-47dc-ab17-831e87a633d5",
+ :transaction-account/account 17592231963549,
+ :transaction-account/location "CB",
+ :transaction-account/amount 84.43}],
+ :transaction/raw-id "gQypbv5946F08op74wZmidDg8qD8Q1fM6gEBP"})
- ["upsert-transaction"]
- (user/init-repl)
+ ["upsert-transaction"]
+ (user/init-repl)
- (def my-transaction {:transaction/bank-account 17592232681223,
- :transaction/date #inst "2024-02-24T08:00:00.000-00:00",
- :transaction/matched-rule 17592233159891,
- :transaction/client 17592232577980,
- :transaction/status "POSTED",
- :transaction/plaid-merchant
- {:plaid-merchant/name "Rotten Robbie", :db/id "b2776792-9e2b-46e8-a9c8-bf80abea359e"},
- :db/id "ac2efd80-bb03-48b2-b0d0-6b47a5c119dc",
- :transaction/id "11a4a13e713d63f476009027e9a53e217e13d0192a37df8ab96c0eed4bdbe996",
- :transaction/description-original "Rotten Robbie #03",
- :transaction/approval-status {:db/id 17592231963877, :db/ident :transaction-approval-status/approved}, :transaction/amount -84.43,
- :transaction/accounts [{:db/id "c402c7b3-c11b-484b-b670-bd48f79a3e5f", :transaction-account/account 17592231963549, :transaction-account/amount 84.43, :transaction-account/location "CB"}],
- :transaction/raw-id "gQypbv5946F08op74wZmidDg8qD8Q1fM6gEBP",
- :transaction/vendor 17592232627053})
+ (def my-transaction {:transaction/bank-account 17592232681223,
+ :transaction/date #inst "2024-02-24T08:00:00.000-00:00",
+ :transaction/matched-rule 17592233159891,
+ :transaction/client 17592232577980,
+ :transaction/status "POSTED",
+ :transaction/plaid-merchant
+ {:plaid-merchant/name "Rotten Robbie", :db/id "b2776792-9e2b-46e8-a9c8-bf80abea359e"},
+ :db/id "ac2efd80-bb03-48b2-b0d0-6b47a5c119dc",
+ :transaction/id "11a4a13e713d63f476009027e9a53e217e13d0192a37df8ab96c0eed4bdbe996",
+ :transaction/description-original "Rotten Robbie #03",
+ :transaction/approval-status {:db/id 17592231963877, :db/ident :transaction-approval-status/approved}, :transaction/amount -84.43,
+ :transaction/accounts [{:db/id "c402c7b3-c11b-484b-b670-bd48f79a3e5f", :transaction-account/account 17592231963549, :transaction-account/amount 84.43, :transaction-account/location "CB"}],
+ :transaction/raw-id "gQypbv5946F08op74wZmidDg8qD8Q1fM6gEBP",
+ :transaction/vendor 17592232627053})
- (def my-journal {:journal-entry/alternate-description "Rotten Robbie #03",
- :journal-entry/date #inst "2024-02-24T08:00:00.000-00:00",
- :journal-entry/original-entity "ac2efd80-bb03-48b2-b0d0-6b47a5c119dc",
- :journal-entry/client 17592232577980,
- :journal-entry/line-items [{:journal-entry-line/credit 84.43, :journal-entry-line/account 17592232681223, :db/id "ac2efd80-bb03-48b2-b0d0-6b47a5c119dc-0", :journal-entry-line/location "A"}
- {:journal-entry-line/account 17592231963549, :db/id "ac2efd80-bb03-48b2-b0d0-6b47a5c119dc-1", :journal-entry-line/debit 84.43, :journal-entry-line/location "CB"}
- {:journal-entry-line/account 17592231963549, :db/id "ac2efd80-bb03-48b2-b0d0-6b47a5c119dc-2", :journal-entry-line/debit 84.43, :journal-entry-line/location "CB"}],
- :journal-entry/source "transaction",
- :journal-entry/cleared true,
- :journal-entry/amount 84.43,
- :journal-entry/vendor 17592232627053})
- (dc/pull (dc/db auto-ap.datomic/conn) '[*] [:transaction/id "11a4a13e713d63f476009027e9a53e217e13d0192a37df8ab96c0eed4bdbe996"])
- wl
- (user/init-repl)
+ (def my-journal {:journal-entry/alternate-description "Rotten Robbie #03",
+ :journal-entry/date #inst "2024-02-24T08:00:00.000-00:00",
+ :journal-entry/original-entity "ac2efd80-bb03-48b2-b0d0-6b47a5c119dc",
+ :journal-entry/client 17592232577980,
+ :journal-entry/line-items [{:journal-entry-line/credit 84.43, :journal-entry-line/account 17592232681223, :db/id "ac2efd80-bb03-48b2-b0d0-6b47a5c119dc-0", :journal-entry-line/location "A"}
+ {:journal-entry-line/account 17592231963549, :db/id "ac2efd80-bb03-48b2-b0d0-6b47a5c119dc-1", :journal-entry-line/debit 84.43, :journal-entry-line/location "CB"}
+ {:journal-entry-line/account 17592231963549, :db/id "ac2efd80-bb03-48b2-b0d0-6b47a5c119dc-2", :journal-entry-line/debit 84.43, :journal-entry-line/location "CB"}],
+ :journal-entry/source "transaction",
+ :journal-entry/cleared true,
+ :journal-entry/amount 84.43,
+ :journal-entry/vendor 17592232627053})
+ (dc/pull (dc/db auto-ap.datomic/conn) '[*] [:transaction/id "11a4a13e713d63f476009027e9a53e217e13d0192a37df8ab96c0eed4bdbe996"])
+ wl
+ (user/init-repl)
- (or (when-let [original-entity (:journal-entry/original-entity my-journal)]
- (dc/pull (dc/db auto-ap.datomic/conn) iol-ion.tx.upsert-ledger/extant-read [:journal-entry/original-entity original-entity]))
- (when-let [external-id (:journal-entry/external-id my-journal)]
- (dc/pull (dc/db auto-ap.datomic/conn) iol-ion.tx.upsert-ledger/extant-read [:journal-entry/external-id external-id])))
+ (or (when-let [original-entity (:journal-entry/original-entity my-journal)]
+ (dc/pull (dc/db auto-ap.datomic/conn) iol-ion.tx.upsert-ledger/extant-read [:journal-entry/original-entity original-entity]))
+ (when-let [external-id (:journal-entry/external-id my-journal)]
+ (dc/pull (dc/db auto-ap.datomic/conn) iol-ion.tx.upsert-ledger/extant-read [:journal-entry/external-id external-id])))
- @(dc/transact auto-ap.datomic/conn [[:upsert-entity my-transaction]
- [:upsert-ledger my-journal]])
+ @(dc/transact auto-ap.datomic/conn [[:upsert-entity my-transaction]
+ [:upsert-ledger my-journal]])
- (auto-ap.datomic/pull-attr (dc/db auto-ap.datomic/conn) :bank-account/code 17592232681223)
- (auto-ap.datomic/pull-attr (dc/db auto-ap.datomic/conn) :bank-account/code 17592232681228)
-
- )
\ No newline at end of file
+ (auto-ap.datomic/pull-attr (dc/db auto-ap.datomic/conn) :bank-account/code 17592232681223)
+ (auto-ap.datomic/pull-attr (dc/db auto-ap.datomic/conn) :bank-account/code 17592232681228))
\ No newline at end of file
diff --git a/iol_ion/src/iol_ion/utils.clj b/iol_ion/src/iol_ion/utils.clj
index 7d81eb66..b7e34813 100644
--- a/iol_ion/src/iol_ion/utils.clj
+++ b/iol_ion/src/iol_ion/utils.clj
@@ -10,11 +10,11 @@
(by f identity xs))
([f fv xs]
(reduce
- #(assoc %1 (f %2) (fv %2))
- {}
- xs)))
+ #(assoc %1 (f %2) (fv %2))
+ {}
+ xs)))
-(defn pull-many [db read ids ]
+(defn pull-many [db read ids]
(->> (dc/q '[:find (pull ?e r)
:in $ [?e ...] r]
db
@@ -24,13 +24,12 @@
(defn remove-nils [m]
(let [result (reduce-kv
- (fn [m k v]
- (if (not (nil? v))
- (assoc m k v)
- m
- ))
- {}
- m)]
+ (fn [m k v]
+ (if (not (nil? v))
+ (assoc m k v)
+ m))
+ {}
+ m)]
(if (seq result)
result
nil)))
diff --git a/resources/public/output.css b/resources/public/output.css
index b35ba9f9..fc6d4583 100644
--- a/resources/public/output.css
+++ b/resources/public/output.css
@@ -1 +1 @@
-/*! tailwindcss v3.3.2 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Calibri,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}[multiple],[type=date],[type=datetime-local],[type=email],[type=month],[type=number],[type=password],[type=search],[type=tel],[type=text],[type=time],[type=url],[type=week],select,textarea{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#6b7280;border-width:1px;border-radius:0;padding:.5rem .75rem;font-size:1rem;line-height:1.5rem;--tw-shadow:0 0 #0000}[multiple]:focus,[type=date]:focus,[type=datetime-local]:focus,[type=email]:focus,[type=month]:focus,[type=number]:focus,[type=password]:focus,[type=search]:focus,[type=tel]:focus,[type=text]:focus,[type=time]:focus,[type=url]:focus,[type=week]:focus,select:focus,textarea:focus{outline:2px solid #0000;outline-offset:2px;--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#007dbb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);border-color:#007dbb}input::-moz-placeholder,textarea::-moz-placeholder{color:#6b7280;opacity:1}input::placeholder,textarea::placeholder{color:#6b7280;opacity:1}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-date-and-time-value{min-height:1.5em}select:not([size]){background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%236B7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3E%3C/svg%3E");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;-webkit-print-color-adjust:exact;print-color-adjust:exact}[multiple]{background-image:none;background-position:0 0;background-repeat:unset;background-size:initial;padding-right:.75rem;-webkit-print-color-adjust:unset;print-color-adjust:unset}[type=checkbox],[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;padding:0;-webkit-print-color-adjust:exact;print-color-adjust:exact;display:inline-block;vertical-align:middle;background-origin:border-box;-webkit-user-select:none;-moz-user-select:none;user-select:none;flex-shrink:0;height:1rem;width:1rem;color:#007dbb;background-color:#fff;border-color:#6b7280;border-width:1px;--tw-shadow:0 0 #0000}[type=checkbox]{border-radius:0}[type=radio]{border-radius:100%}[type=checkbox]:focus,[type=radio]:focus{outline:2px solid #0000;outline-offset:2px;--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:2px;--tw-ring-offset-color:#fff;--tw-ring-color:#007dbb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.dark [type=checkbox]:checked,.dark [type=radio]:checked,[type=checkbox]:checked,[type=radio]:checked{border-color:#0000;background-color:currentColor;background-size:100% 100%;background-position:50%;background-repeat:no-repeat}[type=checkbox]:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 16 16'%3E%3Cpath d='M12.207 4.793a1 1 0 0 1 0 1.414l-5 5a1 1 0 0 1-1.414 0l-2-2a1 1 0 0 1 1.414-1.414L6.5 9.086l4.293-4.293a1 1 0 0 1 1.414 0z'/%3E%3C/svg%3E")}[type=radio]:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='3'/%3E%3C/svg%3E")}[type=checkbox]:indeterminate{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3E%3Cpath stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3E%3C/svg%3E");background-size:100% 100%;background-position:50%;background-repeat:no-repeat}[type=checkbox]:indeterminate,[type=checkbox]:indeterminate:focus,[type=checkbox]:indeterminate:hover{border-color:#0000;background-color:currentColor}[type=file]{background:unset;border-color:inherit;border-width:0;border-radius:0;padding:0;font-size:unset;line-height:inherit}[type=file]:focus{outline:1px auto inherit}input[type=file]::file-selector-button{color:#fff;background:#1f2937;border:0;font-weight:500;font-size:.875rem;cursor:pointer;padding:.625rem 1rem .625rem 2rem;-webkit-margin-start:-1rem;margin-inline-start:-1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}input[type=file]::file-selector-button:hover{background:#374151}.dark input[type=file]::file-selector-button{color:#fff;background:#4b5563}.dark input[type=file]::file-selector-button:hover{background:#6b7280}input[type=range]::-webkit-slider-thumb{height:1.25rem;width:1.25rem;background:#007dbb;border-radius:9999px;border:0;appearance:none;-moz-appearance:none;-webkit-appearance:none;cursor:pointer}input[type=range]:disabled::-webkit-slider-thumb{background:#9ca3af}.dark input[type=range]:disabled::-webkit-slider-thumb{background:#6b7280}input[type=range]:focus::-webkit-slider-thumb{outline:2px solid #0000;outline-offset:2px;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-opacity:1px;--tw-ring-color:rgb(164 202 254/var(--tw-ring-opacity))}input[type=range]::-moz-range-thumb{height:1.25rem;width:1.25rem;background:#007dbb;border-radius:9999px;border:0;appearance:none;-moz-appearance:none;-webkit-appearance:none;cursor:pointer}input[type=range]:disabled::-moz-range-thumb{background:#9ca3af}.dark input[type=range]:disabled::-moz-range-thumb{background:#6b7280}input[type=range]::-moz-range-progress{background:#009cea}input[type=range]::-ms-fill-lower{background:#009cea}.toggle-bg:after{content:"";position:absolute;top:.125rem;left:.125rem;background:#fff;border-color:#d1d5db;border-width:1px;border-radius:9999px;height:1.25rem;width:1.25rem;transition-property:background-color,border-color,color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-duration:.15s;box-shadow:var(--tw-ring-inset) 0 0 0 calc(var(--tw-ring-offset-width)) var(--tw-ring-color)}input:checked+.toggle-bg:after{transform:translateX(100%);;border-color:#fff}input:checked+.toggle-bg{background:#007dbb;border-color:#007dbb}.tooltip-arrow,.tooltip-arrow:before{position:absolute;width:8px;height:8px;background:inherit}.tooltip-arrow{visibility:hidden}.tooltip-arrow:before{content:"";visibility:visible;transform:rotate(45deg)}[data-tooltip-style^=light]+.tooltip>.tooltip-arrow:before{border-style:solid;border-color:#e5e7eb}[data-tooltip-style^=light]+.tooltip[data-popper-placement^=top]>.tooltip-arrow:before{border-bottom-width:1px;border-right-width:1px}[data-tooltip-style^=light]+.tooltip[data-popper-placement^=right]>.tooltip-arrow:before{border-bottom-width:1px;border-left-width:1px}[data-tooltip-style^=light]+.tooltip[data-popper-placement^=bottom]>.tooltip-arrow:before{border-top-width:1px;border-left-width:1px}[data-tooltip-style^=light]+.tooltip[data-popper-placement^=left]>.tooltip-arrow:before{border-top-width:1px;border-right-width:1px}.tooltip[data-popper-placement^=top]>.tooltip-arrow{bottom:-4px}.tooltip[data-popper-placement^=bottom]>.tooltip-arrow{top:-4px}.tooltip[data-popper-placement^=left]>.tooltip-arrow{right:-4px}.tooltip[data-popper-placement^=right]>.tooltip-arrow{left:-4px}.tooltip.invisible>.tooltip-arrow:before{visibility:hidden}[data-popper-arrow],[data-popper-arrow]:before{position:absolute;width:8px;height:8px;background:inherit}[data-popper-arrow]{visibility:hidden}[data-popper-arrow]:after,[data-popper-arrow]:before{content:"";visibility:visible;transform:rotate(45deg)}[data-popper-arrow]:after{position:absolute;width:9px;height:9px;background:inherit}[role=tooltip]>[data-popper-arrow]:before{border-style:solid;border-color:#e5e7eb}.dark [role=tooltip]>[data-popper-arrow]:before{border-style:solid;border-color:#4b5563}[role=tooltip]>[data-popper-arrow]:after{border-style:solid;border-color:#e5e7eb}.dark [role=tooltip]>[data-popper-arrow]:after{border-style:solid;border-color:#4b5563}[data-popover][role=tooltip][data-popper-placement^=top]>[data-popper-arrow]:after,[data-popover][role=tooltip][data-popper-placement^=top]>[data-popper-arrow]:before{border-bottom-width:1px;border-right-width:1px}[data-popover][role=tooltip][data-popper-placement^=right]>[data-popper-arrow]:after,[data-popover][role=tooltip][data-popper-placement^=right]>[data-popper-arrow]:before{border-bottom-width:1px;border-left-width:1px}[data-popover][role=tooltip][data-popper-placement^=bottom]>[data-popper-arrow]:after,[data-popover][role=tooltip][data-popper-placement^=bottom]>[data-popper-arrow]:before{border-top-width:1px;border-left-width:1px}[data-popover][role=tooltip][data-popper-placement^=left]>[data-popper-arrow]:after,[data-popover][role=tooltip][data-popper-placement^=left]>[data-popper-arrow]:before{border-top-width:1px;border-right-width:1px}[data-popover][role=tooltip][data-popper-placement^=top]>[data-popper-arrow]{bottom:-5px}[data-popover][role=tooltip][data-popper-placement^=bottom]>[data-popper-arrow]{top:-5px}[data-popover][role=tooltip][data-popper-placement^=left]>[data-popper-arrow]{right:-5px}[data-popover][role=tooltip][data-popper-placement^=right]>[data-popper-arrow]{left:-5px}[role=tooltip].invisible>[data-popper-arrow]:after,[role=tooltip].invisible>[data-popper-arrow]:before{visibility:hidden}*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#009cea80;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.invisible{visibility:hidden}.collapse{visibility:collapse}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.inset-y-0{top:0;bottom:0}.-right-2{right:-.5rem}.-top-2{top:-.5rem}.bottom-0{bottom:0}.bottom-\[60px\]{bottom:60px}.left-0{left:0}.left-1\/2{left:50%}.right-0{right:0}.right-2{right:.5rem}.start-0{inset-inline-start:0}.top-0{top:0}.top-2{top:.5rem}.top-2\/4{top:50%}.top-5{top:1.25rem}.z-0{z-index:0}.z-10{z-index:10}.z-20{z-index:20}.z-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.z-\[99\]{z-index:99}.col-span-1{grid-column:span 1/span 1}.col-span-2{grid-column:span 2/span 2}.col-span-3{grid-column:span 3/span 3}.col-span-6{grid-column:span 6/span 6}.col-start-1{grid-column-start:1}.row-span-2{grid-row:span 2/span 2}.m-0{margin:0}.m-1{margin:.25rem}.m-2{margin:.5rem}.m-4{margin:1rem}.mx-4{margin-left:1rem;margin-right:1rem}.mx-auto{margin-left:auto;margin-right:auto}.my-0{margin-top:0;margin-bottom:0}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-2{margin-top:.5rem;margin-bottom:.5rem}.my-4{margin-top:1rem;margin-bottom:1rem}.-mb-1{margin-bottom:-.25rem}.-mb-px{margin-bottom:-1px}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.me-2{-webkit-margin-end:.5rem;margin-inline-end:.5rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.ml-3{margin-left:.75rem}.ml-4{margin-left:1rem}.ml-auto{margin-left:auto}.mr-1{margin-right:.25rem}.mr-10{margin-right:2.5rem}.mr-2{margin-right:.5rem}.mr-3{margin-right:.75rem}.mr-8{margin-right:2rem}.ms-3{-webkit-margin-start:.75rem;margin-inline-start:.75rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-5{margin-top:1.25rem}.mt-6{margin-top:1.5rem}.mt-0{margin-top:0}.box-content{box-sizing:initial}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.contents{display:contents}.hidden{display:none}.h-10{height:2.5rem}.h-16{height:4rem}.h-2{height:.5rem}.h-24{height:6rem}.h-3{height:.75rem}.h-32{height:8rem}.h-4{height:1rem}.h-48{height:12rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-64{height:16rem}.h-8{height:2rem}.h-96{height:24rem}.h-\[350px\]{height:350px}.h-\[49rem\]{height:49rem}.h-\[600px\]{height:600px}.h-\[70vh\]{height:70vh}.h-full{height:100%}.h-px{height:1px}.h-screen{height:100vh}.max-h-0{max-height:0}.max-h-96{max-height:24rem}.max-h-\[300px\]{max-height:300px}.max-h-\[600px\]{max-height:600px}.max-h-\[700px\]{max-height:700px}.max-h-\[inherit\]{max-height:inherit}.max-h-full{max-height:100%}.max-h-screen{max-height:100vh}.w-1\/2{width:50%}.w-1\/4{width:25%}.w-11{width:2.75rem}.w-16{width:4rem}.w-2{width:.5rem}.w-20{width:5rem}.w-24{width:6rem}.w-3{width:.75rem}.w-3\/4{width:75%}.w-32{width:8rem}.w-36{width:9rem}.w-4{width:1rem}.w-48{width:12rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-64{width:16rem}.w-72{width:18rem}.w-8{width:2rem}.w-96{width:24rem}.w-\[10em\]{width:10em}.w-\[20em\]{width:20em}.w-\[300px\]{width:300px}.w-\[30em\]{width:30em}.w-\[5em\]{width:5em}.w-\[600px\]{width:600px}.w-\[700px\]{width:700px}.w-\[748px\]{width:748px}.w-\[7em\]{width:7em}.w-\[850px\]{width:850px}.w-\[8em\]{width:8em}.w-auto{width:auto}.w-full{width:100%}.w-max{width:-moz-max-content;width:max-content}.w-screen{width:100vw}.w-28{width:7rem}.min-w-full{min-width:100%}.min-w-0{min-width:0}.max-w-2xl{max-width:42rem}.max-w-\[12rem\]{max-width:12rem}.max-w-\[24em\]{max-width:24em}.max-w-lg{max-width:32rem}.max-w-md{max-width:28rem}.max-w-screen-2xl{max-width:1536px}.max-w-screen-lg{max-width:1024px}.flex-1{flex:1 1 0%}.flex-initial{flex:0 1 auto}.flex-shrink{flex-shrink:1}.flex-shrink-0{flex-shrink:0}.shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.flex-grow,.grow{flex-grow:1}.grow-0{flex-grow:0}.basis-1\/2{flex-basis:50%}.\!translate-y-0{--tw-translate-y:0px!important}.\!translate-y-0,.\!translate-y-32{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))!important}.\!translate-y-32{--tw-translate-y:8rem!important}.-translate-x-1\/2{--tw-translate-x:-50%}.-translate-x-1\/2,.-translate-x-full{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-x-full{--tw-translate-x:-100%}.-translate-y-1\/2{--tw-translate-y:-50%}.-translate-y-1\/2,.-translate-y-full{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-full{--tw-translate-y:-100%}.translate-x-0{--tw-translate-x:0px}.translate-x-0,.translate-x-full{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-full{--tw-translate-x:100%}.translate-y-0{--tw-translate-y:0px}.translate-y-0,.translate-y-full{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-full{--tw-translate-y:100%}.rotate-180{--tw-rotate:180deg}.rotate-180,.scale-100{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-100{--tw-scale-x:1;--tw-scale-y:1}.scale-95{--tw-scale-x:.95;--tw-scale-y:.95}.scale-95,.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform-none{transform:none}@keyframes gentleGrow{0%{transform:scale(1);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:scale(1.1);animation-timing-function:cubic-bezier(0,0,.2,1)}to{transform:scale(1);animation-timing-function:cubic-bezier(.8,0,1,1)}}.animate-gg{animation:gentleGrow 1s infinite}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes shake{0%{transform:translateX(0)}12.5%{transform:translateX(-5px)}25%{transform:translateX(0)}37.5%{transform:translateX(5px)}50%{transform:translateX(0)}62.5%{transform:translateX(-5px)}75%{transform:translateX(5px)}87.5%{transform:translateX(5px)}to{transform:translateX(0)}}.animate-shake{animation:shake .5s ease-out 1}@keyframes slideUp{0%{transform:translateY(20px);opacity:0}to{transform:translateY(0);opacity:1}}.animate-slideUp{animation:slideUp .5s ease-out forwards}@keyframes spin{to{transform:rotate(1turn)}}.animate-spin{animation:spin 1s linear infinite}.cursor-default{cursor:default}.cursor-move{cursor:move}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize{resize:both}.list-disc{list-style-type:disc}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.auto-cols-min{grid-auto-columns:min-content}.grid-flow-row{grid-auto-flow:row}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.grid-cols-7{grid-template-columns:repeat(7,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-row-reverse{flex-direction:row-reverse}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.place-items-center{place-items:center}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.items-baseline{align-items:baseline}.items-stretch{align-items:stretch}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.justify-stretch{justify-content:stretch}.justify-items-stretch{justify-items:stretch}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-4{gap:1rem}.gap-8{gap:2rem}.gap-6{gap:1.5rem}.gap-x-2{-moz-column-gap:.5rem;column-gap:.5rem}.gap-x-4{-moz-column-gap:1rem;column-gap:1rem}.gap-y-2{row-gap:.5rem}.-space-x-px>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(-1px*var(--tw-space-x-reverse));margin-left:calc(-1px*(1 - var(--tw-space-x-reverse)))}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.25rem*var(--tw-space-x-reverse));margin-left:calc(.25rem*(1 - var(--tw-space-x-reverse)))}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem*var(--tw-space-x-reverse));margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)))}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.75rem*var(--tw-space-x-reverse));margin-left:calc(.75rem*(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem*var(--tw-space-y-reverse))}.space-y-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.375rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem*var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem*var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.75rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem*var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem*var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem*var(--tw-space-y-reverse))}.space-y-0>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(0px*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(0px*var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px*var(--tw-divide-y-reverse))}.divide-gray-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(243 244 246/var(--tw-divide-opacity))}.divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(229 231 235/var(--tw-divide-opacity))}.place-self-end{place-self:end}.self-center{align-self:center}.self-stretch{align-self:stretch}.justify-self-end{justify-self:end}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-visible{overflow:visible}.overflow-scroll{overflow:scroll}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-y-scroll{overflow-y:scroll}.truncate{overflow:hidden;text-overflow:ellipsis}.truncate,.whitespace-nowrap{white-space:nowrap}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-sm{border-radius:.125rem}.rounded-b-lg{border-bottom-right-radius:.5rem}.rounded-b-lg,.rounded-l-lg{border-bottom-left-radius:.5rem}.rounded-l-lg{border-top-left-radius:.5rem}.rounded-r-lg{border-top-right-radius:.5rem;border-bottom-right-radius:.5rem}.rounded-t{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.rounded-t-lg{border-top-left-radius:.5rem;border-top-right-radius:.5rem}.border{border-width:1px}.border-0{border-width:0}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-t-2{border-top-width:2px}.border-l-2{border-left-width:2px}.border-dashed{border-style:dashed}.border-dotted{border-style:dotted}.border-blue-300{--tw-border-opacity:1;border-color:rgb(102 196 242/var(--tw-border-opacity))}.border-blue-600{--tw-border-opacity:1;border-color:rgb(0 125 187/var(--tw-border-opacity))}.border-blue-700{--tw-border-opacity:1;border-color:rgb(0 94 140/var(--tw-border-opacity))}.border-gray-100{--tw-border-opacity:1;border-color:rgb(243 244 246/var(--tw-border-opacity))}.border-gray-200{--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity))}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity))}.border-gray-400{--tw-border-opacity:1;border-color:rgb(156 163 175/var(--tw-border-opacity))}.border-gray-500{--tw-border-opacity:1;border-color:rgb(107 114 128/var(--tw-border-opacity))}.border-green-300{--tw-border-opacity:1;border-color:rgb(175 211 130/var(--tw-border-opacity))}.border-green-500{--tw-border-opacity:1;border-color:rgb(121 181 46/var(--tw-border-opacity))}.border-primary-300{--tw-border-opacity:1;border-color:rgb(175 211 130/var(--tw-border-opacity))}.border-primary-600{--tw-border-opacity:1;border-color:rgb(97 145 37/var(--tw-border-opacity))}.border-red-300{--tw-border-opacity:1;border-color:rgb(255 104 104/var(--tw-border-opacity))}.border-white{--tw-border-opacity:1;border-color:rgb(255 255 255/var(--tw-border-opacity))}.border-amber-300{--tw-border-opacity:1;border-color:rgb(252 211 77/var(--tw-border-opacity))}.border-emerald-300{--tw-border-opacity:1;border-color:rgb(110 231 183/var(--tw-border-opacity))}.border-slate-300{--tw-border-opacity:1;border-color:rgb(203 213 225/var(--tw-border-opacity))}.border-red-200{--tw-border-opacity:1;border-color:rgb(255 154 154/var(--tw-border-opacity))}.border-indigo-300{--tw-border-opacity:1;border-color:rgb(180 198 252/var(--tw-border-opacity))}.\!bg-primary-200{--tw-bg-opacity:1!important;background-color:rgb(201 225 171/var(--tw-bg-opacity))!important}.bg-blue-100{--tw-bg-opacity:1;background-color:rgb(204 235 251/var(--tw-bg-opacity))}.bg-blue-200{--tw-bg-opacity:1;background-color:rgb(153 215 247/var(--tw-bg-opacity))}.bg-blue-300{--tw-bg-opacity:1;background-color:rgb(102 196 242/var(--tw-bg-opacity))}.bg-blue-400{--tw-bg-opacity:1;background-color:rgb(51 176 238/var(--tw-bg-opacity))}.bg-blue-50{--tw-bg-opacity:1;background-color:rgb(230 245 253/var(--tw-bg-opacity))}.bg-blue-500{--tw-bg-opacity:1;background-color:rgb(0 156 234/var(--tw-bg-opacity))}.bg-blue-600{--tw-bg-opacity:1;background-color:rgb(0 125 187/var(--tw-bg-opacity))}.bg-blue-700{--tw-bg-opacity:1;background-color:rgb(0 94 140/var(--tw-bg-opacity))}.bg-blue-800{--tw-bg-opacity:1;background-color:rgb(0 62 94/var(--tw-bg-opacity))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity))}.bg-gray-200{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.bg-gray-400{--tw-bg-opacity:1;background-color:rgb(156 163 175/var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}.bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity))}.bg-green-100{--tw-bg-opacity:1;background-color:rgb(228 240 213/var(--tw-bg-opacity))}.bg-green-200{--tw-bg-opacity:1;background-color:rgb(201 225 171/var(--tw-bg-opacity))}.bg-green-300{--tw-bg-opacity:1;background-color:rgb(175 211 130/var(--tw-bg-opacity))}.bg-green-400{--tw-bg-opacity:1;background-color:rgb(148 196 88/var(--tw-bg-opacity))}.bg-green-50{--tw-bg-opacity:1;background-color:rgb(242 248 234/var(--tw-bg-opacity))}.bg-green-500{--tw-bg-opacity:1;background-color:rgb(121 181 46/var(--tw-bg-opacity))}.bg-green-600{--tw-bg-opacity:1;background-color:rgb(97 145 37/var(--tw-bg-opacity))}.bg-green-700{--tw-bg-opacity:1;background-color:rgb(73 109 28/var(--tw-bg-opacity))}.bg-green-800{--tw-bg-opacity:1;background-color:rgb(48 72 18/var(--tw-bg-opacity))}.bg-primary-200{--tw-bg-opacity:1;background-color:rgb(201 225 171/var(--tw-bg-opacity))}.bg-primary-50{--tw-bg-opacity:1;background-color:rgb(242 248 234/var(--tw-bg-opacity))}.bg-purple-50{--tw-bg-opacity:1;background-color:rgb(246 245 255/var(--tw-bg-opacity))}.bg-red-100{--tw-bg-opacity:1;background-color:rgb(255 205 205/var(--tw-bg-opacity))}.bg-red-200{--tw-bg-opacity:1;background-color:rgb(255 154 154/var(--tw-bg-opacity))}.bg-red-300{--tw-bg-opacity:1;background-color:rgb(255 104 104/var(--tw-bg-opacity))}.bg-red-50{--tw-bg-opacity:1;background-color:rgb(255 230 230/var(--tw-bg-opacity))}.bg-red-500{--tw-bg-opacity:1;background-color:rgb(255 3 3/var(--tw-bg-opacity))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.bg-white\/50{background-color:#ffffff80}.bg-yellow-100{--tw-bg-opacity:1;background-color:rgb(253 246 178/var(--tw-bg-opacity))}.bg-yellow-200{--tw-bg-opacity:1;background-color:rgb(252 233 106/var(--tw-bg-opacity))}.bg-yellow-50{--tw-bg-opacity:1;background-color:rgb(253 253 234/var(--tw-bg-opacity))}.bg-amber-100{--tw-bg-opacity:1;background-color:rgb(254 243 199/var(--tw-bg-opacity))}.bg-emerald-100{--tw-bg-opacity:1;background-color:rgb(209 250 229/var(--tw-bg-opacity))}.bg-slate-50{--tw-bg-opacity:1;background-color:rgb(248 250 252/var(--tw-bg-opacity))}.bg-indigo-50\/40{background-color:#f0f5ff66}.\!bg-opacity-0{--tw-bg-opacity:0!important}.\!bg-opacity-100{--tw-bg-opacity:1!important}.\!bg-opacity-50{--tw-bg-opacity:0.5!important}.bg-opacity-50{--tw-bg-opacity:0.5}.p-0{padding:0}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-2\.5{padding:.625rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-8{padding:2rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0{padding-top:0;padding-bottom:0}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.py-8{padding-top:2rem;padding-bottom:2rem}.px-1{padding-left:.25rem;padding-right:.25rem}.pb-2{padding-bottom:.5rem}.pb-3{padding-bottom:.75rem}.pl-10{padding-left:2.5rem}.pl-11{padding-left:2.75rem}.pl-2{padding-left:.5rem}.pl-3{padding-left:.75rem}.pl-4{padding-left:1rem}.pl-6{padding-left:1.5rem}.pr-2{padding-right:.5rem}.pr-2\.5{padding-right:.625rem}.pr-6{padding-right:1.5rem}.ps-10{-webkit-padding-start:2.5rem;padding-inline-start:2.5rem}.ps-3{-webkit-padding-start:.75rem;padding-inline-start:.75rem}.pt-16{padding-top:4rem}.pt-2{padding-top:.5rem}.pt-4{padding-top:1rem}.pt-5{padding-top:1.25rem}.pt-8{padding-top:2rem}.pt-1{padding-top:.25rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.align-baseline{vertical-align:initial}.align-top{vertical-align:top}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-\[0\.6rem\]{font-size:.6rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-black{font-weight:900}.font-bold{font-weight:700}.font-extrabold{font-weight:800}.font-light{font-weight:300}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.leading-6{line-height:1.5rem}.leading-9{line-height:2.25rem}.leading-none{line-height:1}.leading-tight{line-height:1.25}.tracking-wider{letter-spacing:.05em}.text-black{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity))}.text-blue-400{--tw-text-opacity:1;color:rgb(51 176 238/var(--tw-text-opacity))}.text-blue-600{--tw-text-opacity:1;color:rgb(0 125 187/var(--tw-text-opacity))}.text-blue-700{--tw-text-opacity:1;color:rgb(0 94 140/var(--tw-text-opacity))}.text-blue-800{--tw-text-opacity:1;color:rgb(0 62 94/var(--tw-text-opacity))}.text-gray-200{--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity))}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity))}.text-gray-800{--tw-text-opacity:1;color:rgb(31 41 55/var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity))}.text-green-300{--tw-text-opacity:1;color:rgb(175 211 130/var(--tw-text-opacity))}.text-green-500{--tw-text-opacity:1;color:rgb(121 181 46/var(--tw-text-opacity))}.text-green-600{--tw-text-opacity:1;color:rgb(97 145 37/var(--tw-text-opacity))}.text-green-700{--tw-text-opacity:1;color:rgb(73 109 28/var(--tw-text-opacity))}.text-green-800{--tw-text-opacity:1;color:rgb(48 72 18/var(--tw-text-opacity))}.text-primary-300{--tw-text-opacity:1;color:rgb(175 211 130/var(--tw-text-opacity))}.text-primary-600{--tw-text-opacity:1;color:rgb(97 145 37/var(--tw-text-opacity))}.text-primary-700{--tw-text-opacity:1;color:rgb(73 109 28/var(--tw-text-opacity))}.text-primary-800{--tw-text-opacity:1;color:rgb(48 72 18/var(--tw-text-opacity))}.text-primary-900{--tw-text-opacity:1;color:rgb(24 36 9/var(--tw-text-opacity))}.text-purple-600{--tw-text-opacity:1;color:rgb(126 58 242/var(--tw-text-opacity))}.text-red-300{--tw-text-opacity:1;color:rgb(255 104 104/var(--tw-text-opacity))}.text-red-500{--tw-text-opacity:1;color:rgb(255 3 3/var(--tw-text-opacity))}.text-red-600{--tw-text-opacity:1;color:rgb(204 2 2/var(--tw-text-opacity))}.text-red-700{--tw-text-opacity:1;color:rgb(153 2 2/var(--tw-text-opacity))}.text-red-800{--tw-text-opacity:1;color:rgb(102 1 1/var(--tw-text-opacity))}.text-red-900{--tw-text-opacity:1;color:rgb(51 1 1/var(--tw-text-opacity))}.text-slate-700{--tw-text-opacity:1;color:rgb(51 65 85/var(--tw-text-opacity))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.text-yellow-600{--tw-text-opacity:1;color:rgb(159 88 10/var(--tw-text-opacity))}.text-yellow-800{--tw-text-opacity:1;color:rgb(114 59 19/var(--tw-text-opacity))}.text-yellow-900{--tw-text-opacity:1;color:rgb(99 49 18/var(--tw-text-opacity))}.text-amber-800{--tw-text-opacity:1;color:rgb(146 64 14/var(--tw-text-opacity))}.text-emerald-800{--tw-text-opacity:1;color:rgb(6 95 70/var(--tw-text-opacity))}.text-slate-600{--tw-text-opacity:1;color:rgb(71 85 105/var(--tw-text-opacity))}.text-slate-900{--tw-text-opacity:1;color:rgb(15 23 42/var(--tw-text-opacity))}.underline{text-decoration-line:underline}.line-through{text-decoration-line:line-through}.\!opacity-0{opacity:0!important}.\!opacity-100{opacity:1!important}.opacity-0{opacity:0}.opacity-100{opacity:1}.opacity-70{opacity:.7}.shadow{--tw-shadow:0 1px 3px 0 #0000001a,0 1px 2px -1px #0000001a;--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.shadow,.shadow-2xl{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-2xl{--tw-shadow:0 25px 50px -12px #00000040;--tw-shadow-colored:0 25px 50px -12px var(--tw-shadow-color)}.shadow-lg{--tw-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-lg,.shadow-md{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a;--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color)}.shadow-sm{--tw-shadow:0 1px 2px 0 #0000000d;--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid #0000;outline-offset:2px}.outline{outline-style:solid}.outline-0{outline-width:0}.ring{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.ring,.ring-1{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-1{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.blur{--tw-blur:blur(8px)}.blur,.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-100{transition-duration:.1s}.duration-150{transition-duration:.15s}.duration-300{transition-duration:.3s}.duration-500{transition-duration:.5s}.duration-75{transition-duration:75ms}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.htmx-added .fade-in,.htmx-added.fade-in{opacity:0!important}.fade-in{opacity:1}.htmx-settling .fade-in-settle,.htmx-settling.fade-in-settle{opacity:0!important}.fade-in-settle{opacity:1}.htmx-added .swipe-left-swap,.htmx-added.swipe-left-swap{opacity:1!important;--tw-scale-x:1!important;--tw-scale-y:1!important;--tw-translate-x:-50%!important;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))!important}.swipe-left-swap{opacity:1;--tw-scale-x:1;--tw-scale-y:1;--tw-translate-x:0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.htmx-settling.htmx-added .swipe-left-swap,.htmx-settling.htmx-added.swipe-left-swap{opacity:0!important;--tw-scale-x:.75!important;--tw-scale-y:.75!important;--tw-translate-x:50%!important;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))!important}.htmx-settling .slide-up-settle,.htmx-settling.slide-up-settle{--tw-translate-y:1.25rem!important;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))!important}.slide-up-settle{--tw-translate-y:0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hidden .slide-up,.htmx-added .slide-up{--tw-translate-y:1.25rem!important;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))!important}.slide-up{--tw-translate-y:0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.live-added{animation:pulse-green .3s 2;animation-direction:alternate;animation-timing-function:ease-in-out}.dark .live-added{animation:pulse-dark-green .3s 2!important;animation-direction:alternate;animation-timing-function:ease-in-out}.live-removed{animation:pulse-red .3s 2;animation-direction:alternate;animation-timing-function:ease-in-out}.dark .live-removed{animation:pulse-dark-red .3s 2!important;animation-direction:alternate;animation-timing-function:ease-in-out}@keyframes pulse-green{0%{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}to{--tw-bg-opacity:1;background-color:rgb(175 211 130/var(--tw-bg-opacity))}:is(.dark to){--tw-bg-opacity:1;background-color:rgb(153 2 2/var(--tw-bg-opacity))}}@keyframes pulse-dark-green{:is(.dark 0%){--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}to{--tw-bg-opacity:1;background-color:rgb(73 109 28/var(--tw-bg-opacity))}}@keyframes pulse-red{0%{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}to{--tw-bg-opacity:1;background-color:rgb(255 104 104/var(--tw-bg-opacity))}:is(.dark to){--tw-bg-opacity:1;background-color:rgb(153 2 2/var(--tw-bg-opacity))}}@keyframes pulse-dark-red{:is(.dark 0%){--tw-bg-opacity:1;background-color:rgb(153 2 2/var(--tw-bg-opacity))}to{--tw-bg-opacity:1;background-color:rgb(153 2 2/var(--tw-bg-opacity))}}.htmx-request .htmx-indicator,.htmx-request.htmx-indicator{display:inherit!important}.htmx-indicator{display:none}.htmx-request .htmx-indicator-hidden{display:none!important}.htmx-indicator-hidden{display:inherit}.htmx-request .htmx-indicator-invisible{visibility:hidden!important}.htmx-indicator-invisible{display:inherit}.htmx-swapping .fade-out{opacity:0!important}.fade-out{opacity:1}.min-h-content{min-height:calc(100vh - 4em)}.arrow,.arrow:before{position:absolute;width:24px;height:24px;background:inherit}.arrow{visibility:hidden}.arrow:before{visibility:visible;content:"";transform:rotate(45deg)}.arrow{bottom:-4px}.ct-series-a .ct-bar{stroke:#79b52e;fill:#79b52e}.ct-series-b .ct-bar{stroke:#ff0303;fill:#ff0303}.ct-series-c .ct-bar{stroke:#009cea;fill:#009cea}.ct-series-d .ct-bar{stroke:#f48017;fill:#f48017}.ct-series-e .ct-bar{stroke:#9c27b0;fill:#9c27b0}[x-cloak]{display:none}.tippy-box[data-theme~=dropdown] .tippy-content{padding:0}.tippy-box[data-theme~=dropdown]{background-color:unset!important}.after\:absolute:after{content:var(--tw-content);position:absolute}.after\:start-\[2px\]:after{content:var(--tw-content);inset-inline-start:2px}.after\:top-\[2px\]:after{content:var(--tw-content);top:2px}.after\:h-5:after{content:var(--tw-content);height:1.25rem}.after\:w-5:after{content:var(--tw-content);width:1.25rem}.after\:rounded-full:after{content:var(--tw-content);border-radius:9999px}.after\:border:after{content:var(--tw-content);border-width:1px}.after\:border-gray-300:after{content:var(--tw-content);--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity))}.after\:bg-white:after{content:var(--tw-content);--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.after\:transition-all:after{content:var(--tw-content);transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.after\:content-\[\'\'\]:after{--tw-content:"";content:var(--tw-content)}.indeterminate\:bg-gray-300:indeterminate{--tw-bg-opacity:1;background-color:rgb(209 213 219/var(--tw-bg-opacity))}.hover\:scale-105:hover{--tw-scale-x:1.05;--tw-scale-y:1.05}.hover\:scale-105:hover,.hover\:scale-110:hover{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:scale-110:hover{--tw-scale-x:1.1;--tw-scale-y:1.1}.hover\:border-gray-300:hover{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity))}.hover\:border-green-300:hover{--tw-border-opacity:1;border-color:rgb(175 211 130/var(--tw-border-opacity))}.hover\:bg-blue-100:hover{--tw-bg-opacity:1;background-color:rgb(204 235 251/var(--tw-bg-opacity))}.hover\:bg-blue-300:hover{--tw-bg-opacity:1;background-color:rgb(102 196 242/var(--tw-bg-opacity))}.hover\:bg-blue-600:hover{--tw-bg-opacity:1;background-color:rgb(0 125 187/var(--tw-bg-opacity))}.hover\:bg-blue-800:hover{--tw-bg-opacity:1;background-color:rgb(0 62 94/var(--tw-bg-opacity))}.hover\:bg-gray-100:hover{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity))}.hover\:bg-gray-200:hover{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.hover\:bg-green-100:hover{--tw-bg-opacity:1;background-color:rgb(228 240 213/var(--tw-bg-opacity))}.hover\:bg-green-200:hover{--tw-bg-opacity:1;background-color:rgb(201 225 171/var(--tw-bg-opacity))}.hover\:bg-green-300:hover{--tw-bg-opacity:1;background-color:rgb(175 211 130/var(--tw-bg-opacity))}.hover\:bg-green-600:hover{--tw-bg-opacity:1;background-color:rgb(97 145 37/var(--tw-bg-opacity))}.hover\:bg-green-700:hover{--tw-bg-opacity:1;background-color:rgb(73 109 28/var(--tw-bg-opacity))}.hover\:bg-neutral-100:hover{--tw-bg-opacity:1;background-color:rgb(245 245 245/var(--tw-bg-opacity))}.hover\:bg-primary-100:hover{--tw-bg-opacity:1;background-color:rgb(228 240 213/var(--tw-bg-opacity))}.hover\:bg-red-300:hover{--tw-bg-opacity:1;background-color:rgb(255 104 104/var(--tw-bg-opacity))}.hover\:bg-white:hover{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.hover\:text-blue-600:hover{--tw-text-opacity:1;color:rgb(0 125 187/var(--tw-text-opacity))}.hover\:text-gray-600:hover{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity))}.hover\:text-gray-700:hover{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity))}.hover\:text-gray-800:hover{--tw-text-opacity:1;color:rgb(31 41 55/var(--tw-text-opacity))}.hover\:text-gray-900:hover{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity))}.hover\:text-primary-700:hover{--tw-text-opacity:1;color:rgb(73 109 28/var(--tw-text-opacity))}.hover\:underline:hover{text-decoration-line:underline}.focus\:z-10:focus{z-index:10}.focus\:border-blue-500:focus{--tw-border-opacity:1;border-color:rgb(0 156 234/var(--tw-border-opacity))}.focus\:border-primary-500:focus{--tw-border-opacity:1;border-color:rgb(121 181 46/var(--tw-border-opacity))}.focus\:bg-neutral-100:focus{--tw-bg-opacity:1;background-color:rgb(245 245 245/var(--tw-bg-opacity))}.focus\:text-green-700:focus{--tw-text-opacity:1;color:rgb(73 109 28/var(--tw-text-opacity))}.focus\:outline-none:focus{outline:2px solid #0000;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-2:focus,.focus\:ring-4:focus{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-4:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-blue-100:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(204 235 251/var(--tw-ring-opacity))}.focus\:ring-blue-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(153 215 247/var(--tw-ring-opacity))}.focus\:ring-blue-300:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(102 196 242/var(--tw-ring-opacity))}.focus\:ring-blue-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(0 156 234/var(--tw-ring-opacity))}.focus\:ring-gray-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(229 231 235/var(--tw-ring-opacity))}.focus\:ring-gray-300:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(209 213 219/var(--tw-ring-opacity))}.focus\:ring-green-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(201 225 171/var(--tw-ring-opacity))}.focus\:ring-green-300:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(175 211 130/var(--tw-ring-opacity))}.focus\:ring-green-400:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(148 196 88/var(--tw-ring-opacity))}.focus\:ring-green-700:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(73 109 28/var(--tw-ring-opacity))}.focus\:ring-primary-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(121 181 46/var(--tw-ring-opacity))}.focus\:ring-red-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(255 154 154/var(--tw-ring-opacity))}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:scale-110{--tw-scale-x:1.1;--tw-scale-y:1.1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:hover .group-hover\:text-blue-500{--tw-text-opacity:1;color:rgb(0 156 234/var(--tw-text-opacity))}.group.raw .group-\[\.raw\]\:sticky{position:sticky}.group.raw .group-\[\.raw\]\:top-0{top:0}.group.raw .group-\[\.raw\]\:z-10{z-index:10}.group.raw .group-\[\.raw\]\:block{display:block}.group.raw .group-\[\.raw\]\:hidden{display:none}.group.has-error .group-\[\.has-error\]\:border-red-500{--tw-border-opacity:1;border-color:rgb(255 3 3/var(--tw-border-opacity))}.group.implied .group-\[\&\.implied\]\:bg-green-200{--tw-bg-opacity:1;background-color:rgb(201 225 171/var(--tw-bg-opacity))}.group.has-error .group-\[\.has-error\]\:bg-red-50{--tw-bg-opacity:1;background-color:rgb(255 230 230/var(--tw-bg-opacity))}.group.has-error .group-\[\.has-error\]\:text-red-900{--tw-text-opacity:1;color:rgb(51 1 1/var(--tw-text-opacity))}.group.has-error .group-\[\.has-error\]\:placeholder-red-700::-moz-placeholder{--tw-placeholder-opacity:1;color:rgb(153 2 2/var(--tw-placeholder-opacity))}.group.has-error .group-\[\.has-error\]\:placeholder-red-700::placeholder{--tw-placeholder-opacity:1;color:rgb(153 2 2/var(--tw-placeholder-opacity))}.group.has-error .group-\[\.has-error\]\:focus\:border-red-500:focus{--tw-border-opacity:1;border-color:rgb(255 3 3/var(--tw-border-opacity))}.group.has-error .group-\[\.has-error\]\:focus\:ring-red-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(255 3 3/var(--tw-ring-opacity))}.peer:checked~.peer-checked\:bg-blue-600{--tw-bg-opacity:1;background-color:rgb(0 125 187/var(--tw-bg-opacity))}.peer:checked~.peer-checked\:after\:translate-x-full:after{content:var(--tw-content);--tw-translate-x:100%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.peer:checked~.peer-checked\:after\:border-white:after{content:var(--tw-content);--tw-border-opacity:1;border-color:rgb(255 255 255/var(--tw-border-opacity))}.peer:hover~.peer-hover\:block{display:block}.peer:focus~.peer-focus\:outline-none{outline:2px solid #0000;outline-offset:2px}.peer:focus~.peer-focus\:ring-4{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.peer:focus~.peer-focus\:ring-blue-300{--tw-ring-opacity:1;--tw-ring-color:rgb(102 196 242/var(--tw-ring-opacity))}.data-\[active\]\:border-blue-600[data-active]{--tw-border-opacity:1;border-color:rgb(0 125 187/var(--tw-border-opacity))}.data-\[active\]\:text-blue-600[data-active]{--tw-text-opacity:1;color:rgb(0 125 187/var(--tw-text-opacity))}.htmx-swapping\:-translate-x-2\/3.htmx-swapping{--tw-translate-x:-66.666667%}.htmx-swapping\:-translate-x-2\/3.htmx-swapping,.htmx-swapping\:translate-x-2\/3.htmx-swapping{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.htmx-swapping\:translate-x-2\/3.htmx-swapping{--tw-translate-x:66.666667%}.htmx-swapping\:scale-0.htmx-swapping{--tw-scale-x:0;--tw-scale-y:0;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.htmx-swapping\:opacity-0.htmx-swapping{opacity:0}.group\/transition.backward .group-\[\.backward\]\/transition\:htmx-swapping\:translate-x-1\/4.htmx-swapping{--tw-translate-x:25%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group\/transition.forward .group-\[\.forward\]\/transition\:htmx-swapping\:-translate-x-1\/4.htmx-swapping{--tw-translate-x:-25%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group\/transition.backward .group-\[\.backward\]\/transition\:htmx-swapping\:scale-75.htmx-swapping,.group\/transition.forward .group-\[\.forward\]\/transition\:htmx-swapping\:scale-75.htmx-swapping{--tw-scale-x:.75;--tw-scale-y:.75;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group\/transition.backward .group-\[\.backward\]\/transition\:htmx-swapping\:opacity-0.htmx-swapping,.group\/transition.forward .group-\[\.forward\]\/transition\:htmx-swapping\:opacity-0.htmx-swapping{opacity:0}.group\/transition.backward .group-\[\.backward\]\/transition\:htmx-swapping\:ease-in.htmx-swapping,.group\/transition.forward .group-\[\.forward\]\/transition\:htmx-swapping\:ease-in.htmx-swapping{transition-timing-function:cubic-bezier(.4,0,1,1)}.htmx-swapping .htmx-swapping\:-translate-x-2\/3{--tw-translate-x:-66.666667%}.htmx-swapping .htmx-swapping\:-translate-x-2\/3,.htmx-swapping .htmx-swapping\:translate-x-2\/3{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.htmx-swapping .htmx-swapping\:translate-x-2\/3{--tw-translate-x:66.666667%}.htmx-swapping .htmx-swapping\:scale-0{--tw-scale-x:0;--tw-scale-y:0;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.htmx-swapping .htmx-swapping\:opacity-0{opacity:0}.group\/transition.backward .htmx-swapping .group-\[\.backward\]\/transition\:htmx-swapping\:translate-x-1\/4{--tw-translate-x:25%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group\/transition.forward .htmx-swapping .group-\[\.forward\]\/transition\:htmx-swapping\:-translate-x-1\/4{--tw-translate-x:-25%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group\/transition.backward .htmx-swapping .group-\[\.backward\]\/transition\:htmx-swapping\:scale-75,.group\/transition.forward .htmx-swapping .group-\[\.forward\]\/transition\:htmx-swapping\:scale-75{--tw-scale-x:.75;--tw-scale-y:.75;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group\/transition.backward .htmx-swapping .group-\[\.backward\]\/transition\:htmx-swapping\:opacity-0,.group\/transition.forward .htmx-swapping .group-\[\.forward\]\/transition\:htmx-swapping\:opacity-0{opacity:0}.group\/transition.backward .htmx-swapping .group-\[\.backward\]\/transition\:htmx-swapping\:ease-in,.group\/transition.forward .htmx-swapping .group-\[\.forward\]\/transition\:htmx-swapping\:ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.htmx-added\:-translate-x-2\/3.htmx-added{--tw-translate-x:-66.666667%}.htmx-added\:-translate-x-2\/3.htmx-added,.htmx-added\:translate-x-2\/3.htmx-added{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.htmx-added\:translate-x-2\/3.htmx-added{--tw-translate-x:66.666667%}.htmx-added\:scale-0.htmx-added{--tw-scale-x:0;--tw-scale-y:0;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.htmx-added\:opacity-0.htmx-added{opacity:0}.group\/transition.backward .group-\[\.backward\]\/transition\:htmx-added\:-translate-x-1\/4.htmx-added{--tw-translate-x:-25%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group\/transition.forward .group-\[\.forward\]\/transition\:htmx-added\:translate-x-1\/4.htmx-added{--tw-translate-x:25%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group\/transition.backward .group-\[\.backward\]\/transition\:htmx-added\:scale-75.htmx-added,.group\/transition.forward .group-\[\.forward\]\/transition\:htmx-added\:scale-75.htmx-added{--tw-scale-x:.75;--tw-scale-y:.75;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group\/transition.backward .group-\[\.backward\]\/transition\:htmx-added\:opacity-0.htmx-added,.group\/transition.forward .group-\[\.forward\]\/transition\:htmx-added\:opacity-0.htmx-added{opacity:0}.group\/transition.backward .group-\[\.backward\]\/transition\:htmx-added\:ease-out.htmx-added,.group\/transition.forward .group-\[\.forward\]\/transition\:htmx-added\:ease-out.htmx-added{transition-timing-function:cubic-bezier(0,0,.2,1)}.htmx-added .htmx-added\:-translate-x-2\/3{--tw-translate-x:-66.666667%}.htmx-added .htmx-added\:-translate-x-2\/3,.htmx-added .htmx-added\:translate-x-2\/3{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.htmx-added .htmx-added\:translate-x-2\/3{--tw-translate-x:66.666667%}.htmx-added .htmx-added\:scale-0{--tw-scale-x:0;--tw-scale-y:0;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.htmx-added .htmx-added\:opacity-0{opacity:0}.group\/transition.backward .htmx-added .group-\[\.backward\]\/transition\:htmx-added\:-translate-x-1\/4{--tw-translate-x:-25%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group\/transition.forward .htmx-added .group-\[\.forward\]\/transition\:htmx-added\:translate-x-1\/4{--tw-translate-x:25%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group\/transition.backward .htmx-added .group-\[\.backward\]\/transition\:htmx-added\:scale-75,.group\/transition.forward .htmx-added .group-\[\.forward\]\/transition\:htmx-added\:scale-75{--tw-scale-x:.75;--tw-scale-y:.75;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group\/transition.backward .htmx-added .group-\[\.backward\]\/transition\:htmx-added\:opacity-0,.group\/transition.forward .htmx-added .group-\[\.forward\]\/transition\:htmx-added\:opacity-0{opacity:0}.group\/transition.backward .htmx-added .group-\[\.backward\]\/transition\:htmx-added\:ease-out,.group\/transition.forward .htmx-added .group-\[\.forward\]\/transition\:htmx-added\:ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}:is([dir=rtl] .peer:checked~.rtl\:peer-checked\:after\:-translate-x-full):after{content:var(--tw-content);--tw-translate-x:-100%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}:is(.dark .dark\:border-blue-500){--tw-border-opacity:1;border-color:rgb(0 156 234/var(--tw-border-opacity))}:is(.dark .dark\:border-gray-400){--tw-border-opacity:1;border-color:rgb(156 163 175/var(--tw-border-opacity))}:is(.dark .dark\:border-gray-500){--tw-border-opacity:1;border-color:rgb(107 114 128/var(--tw-border-opacity))}:is(.dark .dark\:border-gray-600){--tw-border-opacity:1;border-color:rgb(75 85 99/var(--tw-border-opacity))}:is(.dark .dark\:border-gray-700){--tw-border-opacity:1;border-color:rgb(55 65 81/var(--tw-border-opacity))}:is(.dark .dark\:border-gray-900){--tw-border-opacity:1;border-color:rgb(17 24 39/var(--tw-border-opacity))}:is(.dark .dark\:border-green-800){--tw-border-opacity:1;border-color:rgb(48 72 18/var(--tw-border-opacity))}:is(.dark .dark\:border-primary-500){--tw-border-opacity:1;border-color:rgb(121 181 46/var(--tw-border-opacity))}:is(.dark .dark\:border-transparent){border-color:#0000}:is(.dark .dark\:bg-blue-400){--tw-bg-opacity:1;background-color:rgb(51 176 238/var(--tw-bg-opacity))}:is(.dark .dark\:bg-blue-600){--tw-bg-opacity:1;background-color:rgb(0 125 187/var(--tw-bg-opacity))}:is(.dark .dark\:bg-blue-700){--tw-bg-opacity:1;background-color:rgb(0 94 140/var(--tw-bg-opacity))}:is(.dark .dark\:bg-blue-900){--tw-bg-opacity:1;background-color:rgb(0 31 47/var(--tw-bg-opacity))}:is(.dark .dark\:bg-gray-600){--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity))}:is(.dark .dark\:bg-gray-700){--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}:is(.dark .dark\:bg-gray-800){--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}:is(.dark .dark\:bg-gray-800\/50){background-color:#1f293780}:is(.dark .dark\:bg-gray-900){--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity))}:is(.dark .dark\:bg-green-600){--tw-bg-opacity:1;background-color:rgb(97 145 37/var(--tw-bg-opacity))}:is(.dark .dark\:bg-green-700){--tw-bg-opacity:1;background-color:rgb(73 109 28/var(--tw-bg-opacity))}:is(.dark .dark\:bg-green-900){--tw-bg-opacity:1;background-color:rgb(24 36 9/var(--tw-bg-opacity))}:is(.dark .dark\:bg-red-700){--tw-bg-opacity:1;background-color:rgb(153 2 2/var(--tw-bg-opacity))}:is(.dark .dark\:bg-red-900){--tw-bg-opacity:1;background-color:rgb(51 1 1/var(--tw-bg-opacity))}:is(.dark .dark\:bg-yellow-900){--tw-bg-opacity:1;background-color:rgb(99 49 18/var(--tw-bg-opacity))}:is(.dark .dark\:bg-opacity-80){--tw-bg-opacity:0.8}:is(.dark .dark\:text-blue-100){--tw-text-opacity:1;color:rgb(204 235 251/var(--tw-text-opacity))}:is(.dark .dark\:text-blue-200){--tw-text-opacity:1;color:rgb(153 215 247/var(--tw-text-opacity))}:is(.dark .dark\:text-blue-300){--tw-text-opacity:1;color:rgb(102 196 242/var(--tw-text-opacity))}:is(.dark .dark\:text-blue-400){--tw-text-opacity:1;color:rgb(51 176 238/var(--tw-text-opacity))}:is(.dark .dark\:text-blue-500){--tw-text-opacity:1;color:rgb(0 156 234/var(--tw-text-opacity))}:is(.dark .dark\:text-gray-100){--tw-text-opacity:1;color:rgb(243 244 246/var(--tw-text-opacity))}:is(.dark .dark\:text-gray-200){--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity))}:is(.dark .dark\:text-gray-300){--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity))}:is(.dark .dark\:text-gray-400){--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}:is(.dark .dark\:text-gray-50){--tw-text-opacity:1;color:rgb(249 250 251/var(--tw-text-opacity))}:is(.dark .dark\:text-gray-500){--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}:is(.dark .dark\:text-green-300){--tw-text-opacity:1;color:rgb(175 211 130/var(--tw-text-opacity))}:is(.dark .dark\:text-green-400){--tw-text-opacity:1;color:rgb(148 196 88/var(--tw-text-opacity))}:is(.dark .dark\:text-primary-500){--tw-text-opacity:1;color:rgb(121 181 46/var(--tw-text-opacity))}:is(.dark .dark\:text-red-300){--tw-text-opacity:1;color:rgb(255 104 104/var(--tw-text-opacity))}:is(.dark .dark\:text-red-400){--tw-text-opacity:1;color:rgb(255 53 53/var(--tw-text-opacity))}:is(.dark .dark\:text-red-500){--tw-text-opacity:1;color:rgb(255 3 3/var(--tw-text-opacity))}:is(.dark .dark\:text-white){--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}:is(.dark .dark\:text-yellow-300){--tw-text-opacity:1;color:rgb(250 202 21/var(--tw-text-opacity))}:is(.dark .dark\:placeholder-gray-400)::-moz-placeholder{--tw-placeholder-opacity:1;color:rgb(156 163 175/var(--tw-placeholder-opacity))}:is(.dark .dark\:placeholder-gray-400)::placeholder{--tw-placeholder-opacity:1;color:rgb(156 163 175/var(--tw-placeholder-opacity))}:is(.dark .dark\:ring-offset-gray-700){--tw-ring-offset-color:#374151}:is(.dark .dark\:ring-offset-gray-800){--tw-ring-offset-color:#1f2937}:is(.dark .hover\:dark\:border-green-800):hover{--tw-border-opacity:1;border-color:rgb(48 72 18/var(--tw-border-opacity))}:is(.dark .dark\:hover\:bg-blue-600:hover){--tw-bg-opacity:1;background-color:rgb(0 125 187/var(--tw-bg-opacity))}:is(.dark .dark\:hover\:bg-blue-700:hover){--tw-bg-opacity:1;background-color:rgb(0 94 140/var(--tw-bg-opacity))}:is(.dark .dark\:hover\:bg-blue-800:hover){--tw-bg-opacity:1;background-color:rgb(0 62 94/var(--tw-bg-opacity))}:is(.dark .dark\:hover\:bg-gray-600:hover){--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity))}:is(.dark .dark\:hover\:bg-gray-700:hover){--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}:is(.dark .dark\:hover\:bg-gray-800:hover){--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}:is(.dark .dark\:hover\:bg-green-600:hover){--tw-bg-opacity:1;background-color:rgb(97 145 37/var(--tw-bg-opacity))}:is(.dark .dark\:hover\:bg-green-700:hover){--tw-bg-opacity:1;background-color:rgb(73 109 28/var(--tw-bg-opacity))}:is(.dark .dark\:hover\:bg-red-600:hover){--tw-bg-opacity:1;background-color:rgb(204 2 2/var(--tw-bg-opacity))}:is(.dark .hover\:dark\:bg-gray-800):hover{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}:is(.dark .dark\:hover\:text-blue-500:hover){--tw-text-opacity:1;color:rgb(0 156 234/var(--tw-text-opacity))}:is(.dark .dark\:hover\:text-gray-100:hover){--tw-text-opacity:1;color:rgb(243 244 246/var(--tw-text-opacity))}:is(.dark .dark\:hover\:text-gray-300:hover){--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity))}:is(.dark .dark\:hover\:text-white:hover){--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}:is(.dark .hover\:dark\:text-green-400):hover{--tw-text-opacity:1;color:rgb(148 196 88/var(--tw-text-opacity))}:is(.dark .dark\:focus\:border-blue-500:focus){--tw-border-opacity:1;border-color:rgb(0 156 234/var(--tw-border-opacity))}:is(.dark .dark\:focus\:border-primary-500:focus){--tw-border-opacity:1;border-color:rgb(121 181 46/var(--tw-border-opacity))}:is(.dark .dark\:focus\:text-white:focus){--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}:is(.dark .dark\:focus\:ring-blue-500:focus){--tw-ring-opacity:1;--tw-ring-color:rgb(0 156 234/var(--tw-ring-opacity))}:is(.dark .dark\:focus\:ring-blue-600:focus){--tw-ring-opacity:1;--tw-ring-color:rgb(0 125 187/var(--tw-ring-opacity))}:is(.dark .dark\:focus\:ring-blue-800:focus){--tw-ring-opacity:1;--tw-ring-color:rgb(0 62 94/var(--tw-ring-opacity))}:is(.dark .dark\:focus\:ring-gray-600:focus){--tw-ring-opacity:1;--tw-ring-color:rgb(75 85 99/var(--tw-ring-opacity))}:is(.dark .dark\:focus\:ring-green-500:focus){--tw-ring-opacity:1;--tw-ring-color:rgb(121 181 46/var(--tw-ring-opacity))}:is(.dark .dark\:focus\:ring-green-800:focus){--tw-ring-opacity:1;--tw-ring-color:rgb(48 72 18/var(--tw-ring-opacity))}:is(.dark .dark\:focus\:ring-primary-500:focus){--tw-ring-opacity:1;--tw-ring-color:rgb(121 181 46/var(--tw-ring-opacity))}:is(.dark .dark\:focus\:ring-primary-600:focus){--tw-ring-opacity:1;--tw-ring-color:rgb(97 145 37/var(--tw-ring-opacity))}:is(.dark .dark\:focus\:ring-offset-gray-700:focus){--tw-ring-offset-color:#374151}:is(.dark .group:hover .dark\:group-hover\:text-white){--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.group.has-error :is(.dark .group-\[\.has-error\]\:dark\:border-red-500){--tw-border-opacity:1;border-color:rgb(255 3 3/var(--tw-border-opacity))}.group.has-error :is(.dark .group-\[\.has-error\]\:dark\:bg-gray-700){--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}.group.has-error :is(.dark .group-\[\.has-error\]\:dark\:text-red-500){--tw-text-opacity:1;color:rgb(255 3 3/var(--tw-text-opacity))}.group.has-error :is(.dark .group-\[\.has-error\]\:dark\:placeholder-red-500)::-moz-placeholder{--tw-placeholder-opacity:1;color:rgb(255 3 3/var(--tw-placeholder-opacity))}.group.has-error :is(.dark .group-\[\.has-error\]\:dark\:placeholder-red-500)::placeholder{--tw-placeholder-opacity:1;color:rgb(255 3 3/var(--tw-placeholder-opacity))}:is(.dark .peer:focus~.dark\:peer-focus\:ring-blue-800){--tw-ring-opacity:1;--tw-ring-color:rgb(0 62 94/var(--tw-ring-opacity))}@media (min-width:640px){.sm\:ml-4{margin-left:1rem}.sm\:block{display:block}.sm\:inline{display:inline}.sm\:space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}.sm\:space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem*var(--tw-space-y-reverse))}.sm\:rounded-lg{border-radius:.5rem}.sm\:p-6{padding:1.5rem}.sm\:py-5{padding-top:1.25rem;padding-bottom:1.25rem}.sm\:text-base{font-size:1rem;line-height:1.5rem}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}}@media (min-width:768px){.md\:ml-2{margin-left:.5rem}.md\:mr-24{margin-right:6rem}.md\:block{display:block}.md\:table-cell{display:table-cell}.md\:h-\[600px\]{height:600px}.md\:h-\[800px\]{height:800px}.md\:w-\[750px\]{width:750px}.md\:flex-row{flex-direction:row}.md\:items-center{align-items:center}.md\:justify-center{justify-content:center}.md\:space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.75rem*var(--tw-space-x-reverse));margin-left:calc(.75rem*(1 - var(--tw-space-x-reverse)))}.md\:space-y-0>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(0px*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(0px*var(--tw-space-y-reverse))}.md\:p-12{padding:3rem}}@media (min-width:1024px){.lg\:block{display:block}.lg\:table-cell{display:table-cell}.lg\:hidden{display:none}.lg\:h-\[900px\]{height:900px}.lg\:h-\[640px\]{height:640px}.lg\:h-\[600px\]{height:600px}.lg\:w-96{width:24rem}.lg\:w-\[850px\]{width:850px}.lg\:w-\[920px\]{width:920px}.lg\:w-\[900px\]{width:900px}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:flex-row{flex-direction:row}.lg\:items-center{align-items:center}.lg\:items-baseline{align-items:baseline}.lg\:justify-end{justify-content:flex-end}.lg\:justify-between{justify-content:space-between}.lg\:space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}.lg\:space-y-0>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(0px*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(0px*var(--tw-space-y-reverse))}.lg\:px-5{padding-left:1.25rem;padding-right:1.25rem}.lg\:pl-3{padding-left:.75rem}.lg\:pl-64{padding-left:16rem}}@media (min-width:1280px){.xl\:table-cell{display:table-cell}}@media (min-width:1536px){.\32xl\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}.\[\&\.active\]\:bg-primary-300.active{--tw-bg-opacity:1;background-color:rgb(175 211 130/var(--tw-bg-opacity))}.\[\&\.active\]\:bg-primary-500.active{--tw-bg-opacity:1;background-color:rgb(121 181 46/var(--tw-bg-opacity))}:is(.dark .\[\&\.active\]\:dark\:bg-primary-700).active{--tw-bg-opacity:1;background-color:rgb(73 109 28/var(--tw-bg-opacity))}.\[\&\.implied\]\:text-gray-500.implied{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}
\ No newline at end of file
+/*! tailwindcss v3.3.2 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Calibri,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}[multiple],[type=date],[type=datetime-local],[type=email],[type=month],[type=number],[type=password],[type=search],[type=tel],[type=text],[type=time],[type=url],[type=week],select,textarea{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#6b7280;border-width:1px;border-radius:0;padding:.5rem .75rem;font-size:1rem;line-height:1.5rem;--tw-shadow:0 0 #0000}[multiple]:focus,[type=date]:focus,[type=datetime-local]:focus,[type=email]:focus,[type=month]:focus,[type=number]:focus,[type=password]:focus,[type=search]:focus,[type=tel]:focus,[type=text]:focus,[type=time]:focus,[type=url]:focus,[type=week]:focus,select:focus,textarea:focus{outline:2px solid #0000;outline-offset:2px;--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#007dbb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);border-color:#007dbb}input::-moz-placeholder,textarea::-moz-placeholder{color:#6b7280;opacity:1}input::placeholder,textarea::placeholder{color:#6b7280;opacity:1}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-date-and-time-value{min-height:1.5em}select:not([size]){background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%236B7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3E%3C/svg%3E");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;-webkit-print-color-adjust:exact;print-color-adjust:exact}[multiple]{background-image:none;background-position:0 0;background-repeat:unset;background-size:initial;padding-right:.75rem;-webkit-print-color-adjust:unset;print-color-adjust:unset}[type=checkbox],[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;padding:0;-webkit-print-color-adjust:exact;print-color-adjust:exact;display:inline-block;vertical-align:middle;background-origin:border-box;-webkit-user-select:none;-moz-user-select:none;user-select:none;flex-shrink:0;height:1rem;width:1rem;color:#007dbb;background-color:#fff;border-color:#6b7280;border-width:1px;--tw-shadow:0 0 #0000}[type=checkbox]{border-radius:0}[type=radio]{border-radius:100%}[type=checkbox]:focus,[type=radio]:focus{outline:2px solid #0000;outline-offset:2px;--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:2px;--tw-ring-offset-color:#fff;--tw-ring-color:#007dbb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.dark [type=checkbox]:checked,.dark [type=radio]:checked,[type=checkbox]:checked,[type=radio]:checked{border-color:#0000;background-color:currentColor;background-size:100% 100%;background-position:50%;background-repeat:no-repeat}[type=checkbox]:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 16 16'%3E%3Cpath d='M12.207 4.793a1 1 0 0 1 0 1.414l-5 5a1 1 0 0 1-1.414 0l-2-2a1 1 0 0 1 1.414-1.414L6.5 9.086l4.293-4.293a1 1 0 0 1 1.414 0z'/%3E%3C/svg%3E")}[type=radio]:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='3'/%3E%3C/svg%3E")}[type=checkbox]:indeterminate{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3E%3Cpath stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3E%3C/svg%3E");background-size:100% 100%;background-position:50%;background-repeat:no-repeat}[type=checkbox]:indeterminate,[type=checkbox]:indeterminate:focus,[type=checkbox]:indeterminate:hover{border-color:#0000;background-color:currentColor}[type=file]{background:unset;border-color:inherit;border-width:0;border-radius:0;padding:0;font-size:unset;line-height:inherit}[type=file]:focus{outline:1px auto inherit}input[type=file]::file-selector-button{color:#fff;background:#1f2937;border:0;font-weight:500;font-size:.875rem;cursor:pointer;padding:.625rem 1rem .625rem 2rem;-webkit-margin-start:-1rem;margin-inline-start:-1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}input[type=file]::file-selector-button:hover{background:#374151}.dark input[type=file]::file-selector-button{color:#fff;background:#4b5563}.dark input[type=file]::file-selector-button:hover{background:#6b7280}input[type=range]::-webkit-slider-thumb{height:1.25rem;width:1.25rem;background:#007dbb;border-radius:9999px;border:0;appearance:none;-moz-appearance:none;-webkit-appearance:none;cursor:pointer}input[type=range]:disabled::-webkit-slider-thumb{background:#9ca3af}.dark input[type=range]:disabled::-webkit-slider-thumb{background:#6b7280}input[type=range]:focus::-webkit-slider-thumb{outline:2px solid #0000;outline-offset:2px;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-opacity:1px;--tw-ring-color:rgb(164 202 254/var(--tw-ring-opacity))}input[type=range]::-moz-range-thumb{height:1.25rem;width:1.25rem;background:#007dbb;border-radius:9999px;border:0;appearance:none;-moz-appearance:none;-webkit-appearance:none;cursor:pointer}input[type=range]:disabled::-moz-range-thumb{background:#9ca3af}.dark input[type=range]:disabled::-moz-range-thumb{background:#6b7280}input[type=range]::-moz-range-progress{background:#009cea}input[type=range]::-ms-fill-lower{background:#009cea}.toggle-bg:after{content:"";position:absolute;top:.125rem;left:.125rem;background:#fff;border-color:#d1d5db;border-width:1px;border-radius:9999px;height:1.25rem;width:1.25rem;transition-property:background-color,border-color,color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-duration:.15s;box-shadow:var(--tw-ring-inset) 0 0 0 calc(var(--tw-ring-offset-width)) var(--tw-ring-color)}input:checked+.toggle-bg:after{transform:translateX(100%);;border-color:#fff}input:checked+.toggle-bg{background:#007dbb;border-color:#007dbb}.tooltip-arrow,.tooltip-arrow:before{position:absolute;width:8px;height:8px;background:inherit}.tooltip-arrow{visibility:hidden}.tooltip-arrow:before{content:"";visibility:visible;transform:rotate(45deg)}[data-tooltip-style^=light]+.tooltip>.tooltip-arrow:before{border-style:solid;border-color:#e5e7eb}[data-tooltip-style^=light]+.tooltip[data-popper-placement^=top]>.tooltip-arrow:before{border-bottom-width:1px;border-right-width:1px}[data-tooltip-style^=light]+.tooltip[data-popper-placement^=right]>.tooltip-arrow:before{border-bottom-width:1px;border-left-width:1px}[data-tooltip-style^=light]+.tooltip[data-popper-placement^=bottom]>.tooltip-arrow:before{border-top-width:1px;border-left-width:1px}[data-tooltip-style^=light]+.tooltip[data-popper-placement^=left]>.tooltip-arrow:before{border-top-width:1px;border-right-width:1px}.tooltip[data-popper-placement^=top]>.tooltip-arrow{bottom:-4px}.tooltip[data-popper-placement^=bottom]>.tooltip-arrow{top:-4px}.tooltip[data-popper-placement^=left]>.tooltip-arrow{right:-4px}.tooltip[data-popper-placement^=right]>.tooltip-arrow{left:-4px}.tooltip.invisible>.tooltip-arrow:before{visibility:hidden}[data-popper-arrow],[data-popper-arrow]:before{position:absolute;width:8px;height:8px;background:inherit}[data-popper-arrow]{visibility:hidden}[data-popper-arrow]:after,[data-popper-arrow]:before{content:"";visibility:visible;transform:rotate(45deg)}[data-popper-arrow]:after{position:absolute;width:9px;height:9px;background:inherit}[role=tooltip]>[data-popper-arrow]:before{border-style:solid;border-color:#e5e7eb}.dark [role=tooltip]>[data-popper-arrow]:before{border-style:solid;border-color:#4b5563}[role=tooltip]>[data-popper-arrow]:after{border-style:solid;border-color:#e5e7eb}.dark [role=tooltip]>[data-popper-arrow]:after{border-style:solid;border-color:#4b5563}[data-popover][role=tooltip][data-popper-placement^=top]>[data-popper-arrow]:after,[data-popover][role=tooltip][data-popper-placement^=top]>[data-popper-arrow]:before{border-bottom-width:1px;border-right-width:1px}[data-popover][role=tooltip][data-popper-placement^=right]>[data-popper-arrow]:after,[data-popover][role=tooltip][data-popper-placement^=right]>[data-popper-arrow]:before{border-bottom-width:1px;border-left-width:1px}[data-popover][role=tooltip][data-popper-placement^=bottom]>[data-popper-arrow]:after,[data-popover][role=tooltip][data-popper-placement^=bottom]>[data-popper-arrow]:before{border-top-width:1px;border-left-width:1px}[data-popover][role=tooltip][data-popper-placement^=left]>[data-popper-arrow]:after,[data-popover][role=tooltip][data-popper-placement^=left]>[data-popper-arrow]:before{border-top-width:1px;border-right-width:1px}[data-popover][role=tooltip][data-popper-placement^=top]>[data-popper-arrow]{bottom:-5px}[data-popover][role=tooltip][data-popper-placement^=bottom]>[data-popper-arrow]{top:-5px}[data-popover][role=tooltip][data-popper-placement^=left]>[data-popper-arrow]{right:-5px}[data-popover][role=tooltip][data-popper-placement^=right]>[data-popper-arrow]{left:-5px}[role=tooltip].invisible>[data-popper-arrow]:after,[role=tooltip].invisible>[data-popper-arrow]:before{visibility:hidden}*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#009cea80;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.invisible{visibility:hidden}.collapse{visibility:collapse}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.inset-y-0{top:0;bottom:0}.-right-2{right:-.5rem}.-top-2{top:-.5rem}.bottom-0{bottom:0}.bottom-\[60px\]{bottom:60px}.left-0{left:0}.left-1\/2{left:50%}.right-0{right:0}.right-2{right:.5rem}.start-0{inset-inline-start:0}.top-0{top:0}.top-2{top:.5rem}.top-2\/4{top:50%}.top-5{top:1.25rem}.z-0{z-index:0}.z-10{z-index:10}.z-20{z-index:20}.z-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.z-\[99\]{z-index:99}.col-span-1{grid-column:span 1/span 1}.col-span-2{grid-column:span 2/span 2}.col-span-3{grid-column:span 3/span 3}.col-span-6{grid-column:span 6/span 6}.col-start-1{grid-column-start:1}.row-span-2{grid-row:span 2/span 2}.m-0{margin:0}.m-1{margin:.25rem}.m-2{margin:.5rem}.m-4{margin:1rem}.mx-4{margin-left:1rem;margin-right:1rem}.mx-auto{margin-left:auto;margin-right:auto}.my-0{margin-top:0;margin-bottom:0}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-2{margin-top:.5rem;margin-bottom:.5rem}.my-4{margin-top:1rem;margin-bottom:1rem}.-mb-1{margin-bottom:-.25rem}.-mb-px{margin-bottom:-1px}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.me-2{-webkit-margin-end:.5rem;margin-inline-end:.5rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.ml-3{margin-left:.75rem}.ml-4{margin-left:1rem}.ml-auto{margin-left:auto}.mr-1{margin-right:.25rem}.mr-10{margin-right:2.5rem}.mr-2{margin-right:.5rem}.mr-3{margin-right:.75rem}.mr-8{margin-right:2rem}.ms-3{-webkit-margin-start:.75rem;margin-inline-start:.75rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-5{margin-top:1.25rem}.mt-6{margin-top:1.5rem}.mt-0{margin-top:0}.box-content{box-sizing:initial}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.contents{display:contents}.hidden{display:none}.h-10{height:2.5rem}.h-16{height:4rem}.h-2{height:.5rem}.h-24{height:6rem}.h-3{height:.75rem}.h-32{height:8rem}.h-4{height:1rem}.h-48{height:12rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-64{height:16rem}.h-8{height:2rem}.h-96{height:24rem}.h-\[350px\]{height:350px}.h-\[49rem\]{height:49rem}.h-\[600px\]{height:600px}.h-\[70vh\]{height:70vh}.h-full{height:100%}.h-px{height:1px}.h-screen{height:100vh}.h-9{height:2.25rem}.max-h-0{max-height:0}.max-h-96{max-height:24rem}.max-h-\[300px\]{max-height:300px}.max-h-\[600px\]{max-height:600px}.max-h-\[700px\]{max-height:700px}.max-h-\[inherit\]{max-height:inherit}.max-h-full{max-height:100%}.max-h-screen{max-height:100vh}.w-1\/2{width:50%}.w-1\/4{width:25%}.w-11{width:2.75rem}.w-16{width:4rem}.w-2{width:.5rem}.w-20{width:5rem}.w-24{width:6rem}.w-3{width:.75rem}.w-3\/4{width:75%}.w-32{width:8rem}.w-36{width:9rem}.w-4{width:1rem}.w-48{width:12rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-64{width:16rem}.w-72{width:18rem}.w-8{width:2rem}.w-96{width:24rem}.w-\[10em\]{width:10em}.w-\[20em\]{width:20em}.w-\[300px\]{width:300px}.w-\[30em\]{width:30em}.w-\[5em\]{width:5em}.w-\[600px\]{width:600px}.w-\[700px\]{width:700px}.w-\[748px\]{width:748px}.w-\[7em\]{width:7em}.w-\[850px\]{width:850px}.w-\[8em\]{width:8em}.w-auto{width:auto}.w-full{width:100%}.w-max{width:-moz-max-content;width:max-content}.w-screen{width:100vw}.w-28{width:7rem}.min-w-full{min-width:100%}.min-w-0{min-width:0}.max-w-2xl{max-width:42rem}.max-w-\[12rem\]{max-width:12rem}.max-w-\[24em\]{max-width:24em}.max-w-lg{max-width:32rem}.max-w-md{max-width:28rem}.max-w-screen-2xl{max-width:1536px}.max-w-screen-lg{max-width:1024px}.flex-1{flex:1 1 0%}.flex-initial{flex:0 1 auto}.flex-shrink{flex-shrink:1}.flex-shrink-0{flex-shrink:0}.shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.flex-grow,.grow{flex-grow:1}.grow-0{flex-grow:0}.basis-1\/2{flex-basis:50%}.\!translate-y-0{--tw-translate-y:0px!important}.\!translate-y-0,.\!translate-y-32{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))!important}.\!translate-y-32{--tw-translate-y:8rem!important}.-translate-x-1\/2{--tw-translate-x:-50%}.-translate-x-1\/2,.-translate-x-full{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-x-full{--tw-translate-x:-100%}.-translate-y-1\/2{--tw-translate-y:-50%}.-translate-y-1\/2,.-translate-y-full{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-full{--tw-translate-y:-100%}.translate-x-0{--tw-translate-x:0px}.translate-x-0,.translate-x-full{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-full{--tw-translate-x:100%}.translate-y-0{--tw-translate-y:0px}.translate-y-0,.translate-y-full{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-full{--tw-translate-y:100%}.rotate-180{--tw-rotate:180deg}.rotate-180,.scale-100{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-100{--tw-scale-x:1;--tw-scale-y:1}.scale-95{--tw-scale-x:.95;--tw-scale-y:.95}.scale-95,.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform-none{transform:none}@keyframes gentleGrow{0%{transform:scale(1);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:scale(1.1);animation-timing-function:cubic-bezier(0,0,.2,1)}to{transform:scale(1);animation-timing-function:cubic-bezier(.8,0,1,1)}}.animate-gg{animation:gentleGrow 1s infinite}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes shake{0%{transform:translateX(0)}12.5%{transform:translateX(-5px)}25%{transform:translateX(0)}37.5%{transform:translateX(5px)}50%{transform:translateX(0)}62.5%{transform:translateX(-5px)}75%{transform:translateX(5px)}87.5%{transform:translateX(5px)}to{transform:translateX(0)}}.animate-shake{animation:shake .5s ease-out 1}@keyframes slideUp{0%{transform:translateY(20px);opacity:0}to{transform:translateY(0);opacity:1}}.animate-slideUp{animation:slideUp .5s ease-out forwards}@keyframes spin{to{transform:rotate(1turn)}}.animate-spin{animation:spin 1s linear infinite}.cursor-default{cursor:default}.cursor-move{cursor:move}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize{resize:both}.list-disc{list-style-type:disc}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.auto-cols-min{grid-auto-columns:min-content}.grid-flow-row{grid-auto-flow:row}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.grid-cols-7{grid-template-columns:repeat(7,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-row-reverse{flex-direction:row-reverse}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.place-items-center{place-items:center}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.items-baseline{align-items:baseline}.items-stretch{align-items:stretch}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.justify-stretch{justify-content:stretch}.justify-items-stretch{justify-items:stretch}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-4{gap:1rem}.gap-8{gap:2rem}.gap-6{gap:1.5rem}.gap-x-2{-moz-column-gap:.5rem;column-gap:.5rem}.gap-x-4{-moz-column-gap:1rem;column-gap:1rem}.gap-y-2{row-gap:.5rem}.-space-x-px>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(-1px*var(--tw-space-x-reverse));margin-left:calc(-1px*(1 - var(--tw-space-x-reverse)))}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.25rem*var(--tw-space-x-reverse));margin-left:calc(.25rem*(1 - var(--tw-space-x-reverse)))}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem*var(--tw-space-x-reverse));margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)))}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.75rem*var(--tw-space-x-reverse));margin-left:calc(.75rem*(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem*var(--tw-space-y-reverse))}.space-y-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.375rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem*var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem*var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.75rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem*var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem*var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem*var(--tw-space-y-reverse))}.space-y-0>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(0px*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(0px*var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px*var(--tw-divide-y-reverse))}.divide-gray-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(243 244 246/var(--tw-divide-opacity))}.divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(229 231 235/var(--tw-divide-opacity))}.place-self-end{place-self:end}.self-center{align-self:center}.self-stretch{align-self:stretch}.justify-self-end{justify-self:end}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-visible{overflow:visible}.overflow-scroll{overflow:scroll}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-y-scroll{overflow-y:scroll}.truncate{overflow:hidden;text-overflow:ellipsis}.truncate,.whitespace-nowrap{white-space:nowrap}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-sm{border-radius:.125rem}.rounded-b-lg{border-bottom-right-radius:.5rem}.rounded-b-lg,.rounded-l-lg{border-bottom-left-radius:.5rem}.rounded-l-lg{border-top-left-radius:.5rem}.rounded-r-lg{border-top-right-radius:.5rem;border-bottom-right-radius:.5rem}.rounded-t{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.rounded-t-lg{border-top-left-radius:.5rem;border-top-right-radius:.5rem}.border{border-width:1px}.border-0{border-width:0}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-t-2{border-top-width:2px}.border-l-2{border-left-width:2px}.border-dashed{border-style:dashed}.border-dotted{border-style:dotted}.border-blue-300{--tw-border-opacity:1;border-color:rgb(102 196 242/var(--tw-border-opacity))}.border-blue-600{--tw-border-opacity:1;border-color:rgb(0 125 187/var(--tw-border-opacity))}.border-blue-700{--tw-border-opacity:1;border-color:rgb(0 94 140/var(--tw-border-opacity))}.border-gray-100{--tw-border-opacity:1;border-color:rgb(243 244 246/var(--tw-border-opacity))}.border-gray-200{--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity))}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity))}.border-gray-400{--tw-border-opacity:1;border-color:rgb(156 163 175/var(--tw-border-opacity))}.border-gray-500{--tw-border-opacity:1;border-color:rgb(107 114 128/var(--tw-border-opacity))}.border-green-300{--tw-border-opacity:1;border-color:rgb(175 211 130/var(--tw-border-opacity))}.border-green-500{--tw-border-opacity:1;border-color:rgb(121 181 46/var(--tw-border-opacity))}.border-primary-300{--tw-border-opacity:1;border-color:rgb(175 211 130/var(--tw-border-opacity))}.border-primary-600{--tw-border-opacity:1;border-color:rgb(97 145 37/var(--tw-border-opacity))}.border-red-300{--tw-border-opacity:1;border-color:rgb(255 104 104/var(--tw-border-opacity))}.border-white{--tw-border-opacity:1;border-color:rgb(255 255 255/var(--tw-border-opacity))}.border-amber-300{--tw-border-opacity:1;border-color:rgb(252 211 77/var(--tw-border-opacity))}.border-emerald-300{--tw-border-opacity:1;border-color:rgb(110 231 183/var(--tw-border-opacity))}.border-slate-300{--tw-border-opacity:1;border-color:rgb(203 213 225/var(--tw-border-opacity))}.border-red-200{--tw-border-opacity:1;border-color:rgb(255 154 154/var(--tw-border-opacity))}.border-indigo-300{--tw-border-opacity:1;border-color:rgb(180 198 252/var(--tw-border-opacity))}.\!bg-primary-200{--tw-bg-opacity:1!important;background-color:rgb(201 225 171/var(--tw-bg-opacity))!important}.bg-blue-100{--tw-bg-opacity:1;background-color:rgb(204 235 251/var(--tw-bg-opacity))}.bg-blue-200{--tw-bg-opacity:1;background-color:rgb(153 215 247/var(--tw-bg-opacity))}.bg-blue-300{--tw-bg-opacity:1;background-color:rgb(102 196 242/var(--tw-bg-opacity))}.bg-blue-400{--tw-bg-opacity:1;background-color:rgb(51 176 238/var(--tw-bg-opacity))}.bg-blue-50{--tw-bg-opacity:1;background-color:rgb(230 245 253/var(--tw-bg-opacity))}.bg-blue-500{--tw-bg-opacity:1;background-color:rgb(0 156 234/var(--tw-bg-opacity))}.bg-blue-600{--tw-bg-opacity:1;background-color:rgb(0 125 187/var(--tw-bg-opacity))}.bg-blue-700{--tw-bg-opacity:1;background-color:rgb(0 94 140/var(--tw-bg-opacity))}.bg-blue-800{--tw-bg-opacity:1;background-color:rgb(0 62 94/var(--tw-bg-opacity))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity))}.bg-gray-200{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.bg-gray-400{--tw-bg-opacity:1;background-color:rgb(156 163 175/var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}.bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity))}.bg-green-100{--tw-bg-opacity:1;background-color:rgb(228 240 213/var(--tw-bg-opacity))}.bg-green-200{--tw-bg-opacity:1;background-color:rgb(201 225 171/var(--tw-bg-opacity))}.bg-green-300{--tw-bg-opacity:1;background-color:rgb(175 211 130/var(--tw-bg-opacity))}.bg-green-400{--tw-bg-opacity:1;background-color:rgb(148 196 88/var(--tw-bg-opacity))}.bg-green-50{--tw-bg-opacity:1;background-color:rgb(242 248 234/var(--tw-bg-opacity))}.bg-green-500{--tw-bg-opacity:1;background-color:rgb(121 181 46/var(--tw-bg-opacity))}.bg-green-600{--tw-bg-opacity:1;background-color:rgb(97 145 37/var(--tw-bg-opacity))}.bg-green-700{--tw-bg-opacity:1;background-color:rgb(73 109 28/var(--tw-bg-opacity))}.bg-green-800{--tw-bg-opacity:1;background-color:rgb(48 72 18/var(--tw-bg-opacity))}.bg-primary-200{--tw-bg-opacity:1;background-color:rgb(201 225 171/var(--tw-bg-opacity))}.bg-primary-50{--tw-bg-opacity:1;background-color:rgb(242 248 234/var(--tw-bg-opacity))}.bg-purple-50{--tw-bg-opacity:1;background-color:rgb(246 245 255/var(--tw-bg-opacity))}.bg-red-100{--tw-bg-opacity:1;background-color:rgb(255 205 205/var(--tw-bg-opacity))}.bg-red-200{--tw-bg-opacity:1;background-color:rgb(255 154 154/var(--tw-bg-opacity))}.bg-red-300{--tw-bg-opacity:1;background-color:rgb(255 104 104/var(--tw-bg-opacity))}.bg-red-50{--tw-bg-opacity:1;background-color:rgb(255 230 230/var(--tw-bg-opacity))}.bg-red-500{--tw-bg-opacity:1;background-color:rgb(255 3 3/var(--tw-bg-opacity))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.bg-white\/50{background-color:#ffffff80}.bg-yellow-100{--tw-bg-opacity:1;background-color:rgb(253 246 178/var(--tw-bg-opacity))}.bg-yellow-200{--tw-bg-opacity:1;background-color:rgb(252 233 106/var(--tw-bg-opacity))}.bg-yellow-50{--tw-bg-opacity:1;background-color:rgb(253 253 234/var(--tw-bg-opacity))}.bg-amber-100{--tw-bg-opacity:1;background-color:rgb(254 243 199/var(--tw-bg-opacity))}.bg-emerald-100{--tw-bg-opacity:1;background-color:rgb(209 250 229/var(--tw-bg-opacity))}.bg-slate-50{--tw-bg-opacity:1;background-color:rgb(248 250 252/var(--tw-bg-opacity))}.bg-indigo-50\/40{background-color:#f0f5ff66}.\!bg-opacity-0{--tw-bg-opacity:0!important}.\!bg-opacity-100{--tw-bg-opacity:1!important}.\!bg-opacity-50{--tw-bg-opacity:0.5!important}.bg-opacity-50{--tw-bg-opacity:0.5}.p-0{padding:0}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-2\.5{padding:.625rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-8{padding:2rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0{padding-top:0;padding-bottom:0}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.py-8{padding-top:2rem;padding-bottom:2rem}.px-1{padding-left:.25rem;padding-right:.25rem}.pb-2{padding-bottom:.5rem}.pb-3{padding-bottom:.75rem}.pl-10{padding-left:2.5rem}.pl-11{padding-left:2.75rem}.pl-2{padding-left:.5rem}.pl-3{padding-left:.75rem}.pl-4{padding-left:1rem}.pl-6{padding-left:1.5rem}.pr-2{padding-right:.5rem}.pr-2\.5{padding-right:.625rem}.pr-6{padding-right:1.5rem}.ps-10{-webkit-padding-start:2.5rem;padding-inline-start:2.5rem}.ps-3{-webkit-padding-start:.75rem;padding-inline-start:.75rem}.pt-16{padding-top:4rem}.pt-2{padding-top:.5rem}.pt-4{padding-top:1rem}.pt-5{padding-top:1.25rem}.pt-8{padding-top:2rem}.pt-1{padding-top:.25rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.align-baseline{vertical-align:initial}.align-top{vertical-align:top}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-\[0\.6rem\]{font-size:.6rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-black{font-weight:900}.font-bold{font-weight:700}.font-extrabold{font-weight:800}.font-light{font-weight:300}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.leading-6{line-height:1.5rem}.leading-9{line-height:2.25rem}.leading-none{line-height:1}.leading-tight{line-height:1.25}.tracking-wider{letter-spacing:.05em}.text-black{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity))}.text-blue-400{--tw-text-opacity:1;color:rgb(51 176 238/var(--tw-text-opacity))}.text-blue-600{--tw-text-opacity:1;color:rgb(0 125 187/var(--tw-text-opacity))}.text-blue-700{--tw-text-opacity:1;color:rgb(0 94 140/var(--tw-text-opacity))}.text-blue-800{--tw-text-opacity:1;color:rgb(0 62 94/var(--tw-text-opacity))}.text-gray-200{--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity))}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity))}.text-gray-800{--tw-text-opacity:1;color:rgb(31 41 55/var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity))}.text-green-300{--tw-text-opacity:1;color:rgb(175 211 130/var(--tw-text-opacity))}.text-green-500{--tw-text-opacity:1;color:rgb(121 181 46/var(--tw-text-opacity))}.text-green-600{--tw-text-opacity:1;color:rgb(97 145 37/var(--tw-text-opacity))}.text-green-700{--tw-text-opacity:1;color:rgb(73 109 28/var(--tw-text-opacity))}.text-green-800{--tw-text-opacity:1;color:rgb(48 72 18/var(--tw-text-opacity))}.text-primary-300{--tw-text-opacity:1;color:rgb(175 211 130/var(--tw-text-opacity))}.text-primary-600{--tw-text-opacity:1;color:rgb(97 145 37/var(--tw-text-opacity))}.text-primary-700{--tw-text-opacity:1;color:rgb(73 109 28/var(--tw-text-opacity))}.text-primary-800{--tw-text-opacity:1;color:rgb(48 72 18/var(--tw-text-opacity))}.text-primary-900{--tw-text-opacity:1;color:rgb(24 36 9/var(--tw-text-opacity))}.text-purple-600{--tw-text-opacity:1;color:rgb(126 58 242/var(--tw-text-opacity))}.text-red-300{--tw-text-opacity:1;color:rgb(255 104 104/var(--tw-text-opacity))}.text-red-500{--tw-text-opacity:1;color:rgb(255 3 3/var(--tw-text-opacity))}.text-red-600{--tw-text-opacity:1;color:rgb(204 2 2/var(--tw-text-opacity))}.text-red-700{--tw-text-opacity:1;color:rgb(153 2 2/var(--tw-text-opacity))}.text-red-800{--tw-text-opacity:1;color:rgb(102 1 1/var(--tw-text-opacity))}.text-red-900{--tw-text-opacity:1;color:rgb(51 1 1/var(--tw-text-opacity))}.text-slate-700{--tw-text-opacity:1;color:rgb(51 65 85/var(--tw-text-opacity))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.text-yellow-600{--tw-text-opacity:1;color:rgb(159 88 10/var(--tw-text-opacity))}.text-yellow-800{--tw-text-opacity:1;color:rgb(114 59 19/var(--tw-text-opacity))}.text-yellow-900{--tw-text-opacity:1;color:rgb(99 49 18/var(--tw-text-opacity))}.text-amber-800{--tw-text-opacity:1;color:rgb(146 64 14/var(--tw-text-opacity))}.text-emerald-800{--tw-text-opacity:1;color:rgb(6 95 70/var(--tw-text-opacity))}.text-slate-600{--tw-text-opacity:1;color:rgb(71 85 105/var(--tw-text-opacity))}.text-slate-900{--tw-text-opacity:1;color:rgb(15 23 42/var(--tw-text-opacity))}.underline{text-decoration-line:underline}.line-through{text-decoration-line:line-through}.\!opacity-0{opacity:0!important}.\!opacity-100{opacity:1!important}.opacity-0{opacity:0}.opacity-100{opacity:1}.opacity-70{opacity:.7}.shadow{--tw-shadow:0 1px 3px 0 #0000001a,0 1px 2px -1px #0000001a;--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.shadow,.shadow-2xl{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-2xl{--tw-shadow:0 25px 50px -12px #00000040;--tw-shadow-colored:0 25px 50px -12px var(--tw-shadow-color)}.shadow-lg{--tw-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-lg,.shadow-md{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a;--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color)}.shadow-sm{--tw-shadow:0 1px 2px 0 #0000000d;--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid #0000;outline-offset:2px}.outline{outline-style:solid}.outline-0{outline-width:0}.ring{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.ring,.ring-1{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-1{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.blur{--tw-blur:blur(8px)}.blur,.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-100{transition-duration:.1s}.duration-150{transition-duration:.15s}.duration-300{transition-duration:.3s}.duration-500{transition-duration:.5s}.duration-75{transition-duration:75ms}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.htmx-added .fade-in,.htmx-added.fade-in{opacity:0!important}.fade-in{opacity:1}.htmx-settling .fade-in-settle,.htmx-settling.fade-in-settle{opacity:0!important}.fade-in-settle{opacity:1}.htmx-added .swipe-left-swap,.htmx-added.swipe-left-swap{opacity:1!important;--tw-scale-x:1!important;--tw-scale-y:1!important;--tw-translate-x:-50%!important;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))!important}.swipe-left-swap{opacity:1;--tw-scale-x:1;--tw-scale-y:1;--tw-translate-x:0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.htmx-settling.htmx-added .swipe-left-swap,.htmx-settling.htmx-added.swipe-left-swap{opacity:0!important;--tw-scale-x:.75!important;--tw-scale-y:.75!important;--tw-translate-x:50%!important;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))!important}.htmx-settling .slide-up-settle,.htmx-settling.slide-up-settle{--tw-translate-y:1.25rem!important;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))!important}.slide-up-settle{--tw-translate-y:0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hidden .slide-up,.htmx-added .slide-up{--tw-translate-y:1.25rem!important;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))!important}.slide-up{--tw-translate-y:0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.live-added{animation:pulse-green .3s 2;animation-direction:alternate;animation-timing-function:ease-in-out}.dark .live-added{animation:pulse-dark-green .3s 2!important;animation-direction:alternate;animation-timing-function:ease-in-out}.live-removed{animation:pulse-red .3s 2;animation-direction:alternate;animation-timing-function:ease-in-out}.dark .live-removed{animation:pulse-dark-red .3s 2!important;animation-direction:alternate;animation-timing-function:ease-in-out}@keyframes pulse-green{0%{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}to{--tw-bg-opacity:1;background-color:rgb(175 211 130/var(--tw-bg-opacity))}:is(.dark to){--tw-bg-opacity:1;background-color:rgb(153 2 2/var(--tw-bg-opacity))}}@keyframes pulse-dark-green{:is(.dark 0%){--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}to{--tw-bg-opacity:1;background-color:rgb(73 109 28/var(--tw-bg-opacity))}}@keyframes pulse-red{0%{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}to{--tw-bg-opacity:1;background-color:rgb(255 104 104/var(--tw-bg-opacity))}:is(.dark to){--tw-bg-opacity:1;background-color:rgb(153 2 2/var(--tw-bg-opacity))}}@keyframes pulse-dark-red{:is(.dark 0%){--tw-bg-opacity:1;background-color:rgb(153 2 2/var(--tw-bg-opacity))}to{--tw-bg-opacity:1;background-color:rgb(153 2 2/var(--tw-bg-opacity))}}.htmx-request .htmx-indicator,.htmx-request.htmx-indicator{display:inherit!important}.htmx-indicator{display:none}.htmx-request .htmx-indicator-hidden{display:none!important}.htmx-indicator-hidden{display:inherit}.htmx-request .htmx-indicator-invisible{visibility:hidden!important}.htmx-indicator-invisible{display:inherit}.htmx-swapping .fade-out{opacity:0!important}.fade-out{opacity:1}.min-h-content{min-height:calc(100vh - 4em)}.arrow,.arrow:before{position:absolute;width:24px;height:24px;background:inherit}.arrow{visibility:hidden}.arrow:before{visibility:visible;content:"";transform:rotate(45deg)}.arrow{bottom:-4px}.ct-series-a .ct-bar{stroke:#79b52e;fill:#79b52e}.ct-series-b .ct-bar{stroke:#ff0303;fill:#ff0303}.ct-series-c .ct-bar{stroke:#009cea;fill:#009cea}.ct-series-d .ct-bar{stroke:#f48017;fill:#f48017}.ct-series-e .ct-bar{stroke:#9c27b0;fill:#9c27b0}[x-cloak]{display:none}.tippy-box[data-theme~=dropdown] .tippy-content{padding:0}.tippy-box[data-theme~=dropdown]{background-color:unset!important}.after\:absolute:after{content:var(--tw-content);position:absolute}.after\:start-\[2px\]:after{content:var(--tw-content);inset-inline-start:2px}.after\:top-\[2px\]:after{content:var(--tw-content);top:2px}.after\:h-5:after{content:var(--tw-content);height:1.25rem}.after\:w-5:after{content:var(--tw-content);width:1.25rem}.after\:rounded-full:after{content:var(--tw-content);border-radius:9999px}.after\:border:after{content:var(--tw-content);border-width:1px}.after\:border-gray-300:after{content:var(--tw-content);--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity))}.after\:bg-white:after{content:var(--tw-content);--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.after\:transition-all:after{content:var(--tw-content);transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.after\:content-\[\'\'\]:after{--tw-content:"";content:var(--tw-content)}.indeterminate\:bg-gray-300:indeterminate{--tw-bg-opacity:1;background-color:rgb(209 213 219/var(--tw-bg-opacity))}.hover\:scale-105:hover{--tw-scale-x:1.05;--tw-scale-y:1.05}.hover\:scale-105:hover,.hover\:scale-110:hover{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:scale-110:hover{--tw-scale-x:1.1;--tw-scale-y:1.1}.hover\:border-gray-300:hover{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity))}.hover\:border-green-300:hover{--tw-border-opacity:1;border-color:rgb(175 211 130/var(--tw-border-opacity))}.hover\:bg-blue-100:hover{--tw-bg-opacity:1;background-color:rgb(204 235 251/var(--tw-bg-opacity))}.hover\:bg-blue-300:hover{--tw-bg-opacity:1;background-color:rgb(102 196 242/var(--tw-bg-opacity))}.hover\:bg-blue-600:hover{--tw-bg-opacity:1;background-color:rgb(0 125 187/var(--tw-bg-opacity))}.hover\:bg-blue-800:hover{--tw-bg-opacity:1;background-color:rgb(0 62 94/var(--tw-bg-opacity))}.hover\:bg-gray-100:hover{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity))}.hover\:bg-gray-200:hover{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.hover\:bg-green-100:hover{--tw-bg-opacity:1;background-color:rgb(228 240 213/var(--tw-bg-opacity))}.hover\:bg-green-200:hover{--tw-bg-opacity:1;background-color:rgb(201 225 171/var(--tw-bg-opacity))}.hover\:bg-green-300:hover{--tw-bg-opacity:1;background-color:rgb(175 211 130/var(--tw-bg-opacity))}.hover\:bg-green-600:hover{--tw-bg-opacity:1;background-color:rgb(97 145 37/var(--tw-bg-opacity))}.hover\:bg-green-700:hover{--tw-bg-opacity:1;background-color:rgb(73 109 28/var(--tw-bg-opacity))}.hover\:bg-neutral-100:hover{--tw-bg-opacity:1;background-color:rgb(245 245 245/var(--tw-bg-opacity))}.hover\:bg-primary-100:hover{--tw-bg-opacity:1;background-color:rgb(228 240 213/var(--tw-bg-opacity))}.hover\:bg-red-300:hover{--tw-bg-opacity:1;background-color:rgb(255 104 104/var(--tw-bg-opacity))}.hover\:bg-white:hover{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.hover\:text-blue-600:hover{--tw-text-opacity:1;color:rgb(0 125 187/var(--tw-text-opacity))}.hover\:text-gray-600:hover{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity))}.hover\:text-gray-700:hover{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity))}.hover\:text-gray-800:hover{--tw-text-opacity:1;color:rgb(31 41 55/var(--tw-text-opacity))}.hover\:text-gray-900:hover{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity))}.hover\:text-primary-700:hover{--tw-text-opacity:1;color:rgb(73 109 28/var(--tw-text-opacity))}.hover\:underline:hover{text-decoration-line:underline}.focus\:z-10:focus{z-index:10}.focus\:border-blue-500:focus{--tw-border-opacity:1;border-color:rgb(0 156 234/var(--tw-border-opacity))}.focus\:border-primary-500:focus{--tw-border-opacity:1;border-color:rgb(121 181 46/var(--tw-border-opacity))}.focus\:bg-neutral-100:focus{--tw-bg-opacity:1;background-color:rgb(245 245 245/var(--tw-bg-opacity))}.focus\:text-green-700:focus{--tw-text-opacity:1;color:rgb(73 109 28/var(--tw-text-opacity))}.focus\:outline-none:focus{outline:2px solid #0000;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-2:focus,.focus\:ring-4:focus{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-4:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-blue-100:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(204 235 251/var(--tw-ring-opacity))}.focus\:ring-blue-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(153 215 247/var(--tw-ring-opacity))}.focus\:ring-blue-300:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(102 196 242/var(--tw-ring-opacity))}.focus\:ring-blue-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(0 156 234/var(--tw-ring-opacity))}.focus\:ring-gray-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(229 231 235/var(--tw-ring-opacity))}.focus\:ring-gray-300:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(209 213 219/var(--tw-ring-opacity))}.focus\:ring-green-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(201 225 171/var(--tw-ring-opacity))}.focus\:ring-green-300:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(175 211 130/var(--tw-ring-opacity))}.focus\:ring-green-400:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(148 196 88/var(--tw-ring-opacity))}.focus\:ring-green-700:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(73 109 28/var(--tw-ring-opacity))}.focus\:ring-primary-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(121 181 46/var(--tw-ring-opacity))}.focus\:ring-red-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(255 154 154/var(--tw-ring-opacity))}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:scale-110{--tw-scale-x:1.1;--tw-scale-y:1.1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:hover .group-hover\:text-blue-500{--tw-text-opacity:1;color:rgb(0 156 234/var(--tw-text-opacity))}.group.raw .group-\[\.raw\]\:sticky{position:sticky}.group.raw .group-\[\.raw\]\:top-0{top:0}.group.raw .group-\[\.raw\]\:z-10{z-index:10}.group.raw .group-\[\.raw\]\:block{display:block}.group.raw .group-\[\.raw\]\:hidden{display:none}.group.has-error .group-\[\.has-error\]\:border-red-500{--tw-border-opacity:1;border-color:rgb(255 3 3/var(--tw-border-opacity))}.group.implied .group-\[\&\.implied\]\:bg-green-200{--tw-bg-opacity:1;background-color:rgb(201 225 171/var(--tw-bg-opacity))}.group.has-error .group-\[\.has-error\]\:bg-red-50{--tw-bg-opacity:1;background-color:rgb(255 230 230/var(--tw-bg-opacity))}.group.has-error .group-\[\.has-error\]\:text-red-900{--tw-text-opacity:1;color:rgb(51 1 1/var(--tw-text-opacity))}.group.has-error .group-\[\.has-error\]\:placeholder-red-700::-moz-placeholder{--tw-placeholder-opacity:1;color:rgb(153 2 2/var(--tw-placeholder-opacity))}.group.has-error .group-\[\.has-error\]\:placeholder-red-700::placeholder{--tw-placeholder-opacity:1;color:rgb(153 2 2/var(--tw-placeholder-opacity))}.group.has-error .group-\[\.has-error\]\:focus\:border-red-500:focus{--tw-border-opacity:1;border-color:rgb(255 3 3/var(--tw-border-opacity))}.group.has-error .group-\[\.has-error\]\:focus\:ring-red-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(255 3 3/var(--tw-ring-opacity))}.peer:checked~.peer-checked\:bg-blue-600{--tw-bg-opacity:1;background-color:rgb(0 125 187/var(--tw-bg-opacity))}.peer:checked~.peer-checked\:after\:translate-x-full:after{content:var(--tw-content);--tw-translate-x:100%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.peer:checked~.peer-checked\:after\:border-white:after{content:var(--tw-content);--tw-border-opacity:1;border-color:rgb(255 255 255/var(--tw-border-opacity))}.peer:hover~.peer-hover\:block{display:block}.peer:focus~.peer-focus\:outline-none{outline:2px solid #0000;outline-offset:2px}.peer:focus~.peer-focus\:ring-4{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.peer:focus~.peer-focus\:ring-blue-300{--tw-ring-opacity:1;--tw-ring-color:rgb(102 196 242/var(--tw-ring-opacity))}.data-\[active\]\:border-blue-600[data-active]{--tw-border-opacity:1;border-color:rgb(0 125 187/var(--tw-border-opacity))}.data-\[active\]\:text-blue-600[data-active]{--tw-text-opacity:1;color:rgb(0 125 187/var(--tw-text-opacity))}.htmx-swapping\:-translate-x-2\/3.htmx-swapping{--tw-translate-x:-66.666667%}.htmx-swapping\:-translate-x-2\/3.htmx-swapping,.htmx-swapping\:translate-x-2\/3.htmx-swapping{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.htmx-swapping\:translate-x-2\/3.htmx-swapping{--tw-translate-x:66.666667%}.htmx-swapping\:scale-0.htmx-swapping{--tw-scale-x:0;--tw-scale-y:0;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.htmx-swapping\:opacity-0.htmx-swapping{opacity:0}.group\/transition.backward .group-\[\.backward\]\/transition\:htmx-swapping\:translate-x-1\/4.htmx-swapping{--tw-translate-x:25%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group\/transition.forward .group-\[\.forward\]\/transition\:htmx-swapping\:-translate-x-1\/4.htmx-swapping{--tw-translate-x:-25%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group\/transition.backward .group-\[\.backward\]\/transition\:htmx-swapping\:scale-75.htmx-swapping,.group\/transition.forward .group-\[\.forward\]\/transition\:htmx-swapping\:scale-75.htmx-swapping{--tw-scale-x:.75;--tw-scale-y:.75;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group\/transition.backward .group-\[\.backward\]\/transition\:htmx-swapping\:opacity-0.htmx-swapping,.group\/transition.forward .group-\[\.forward\]\/transition\:htmx-swapping\:opacity-0.htmx-swapping{opacity:0}.group\/transition.backward .group-\[\.backward\]\/transition\:htmx-swapping\:ease-in.htmx-swapping,.group\/transition.forward .group-\[\.forward\]\/transition\:htmx-swapping\:ease-in.htmx-swapping{transition-timing-function:cubic-bezier(.4,0,1,1)}.htmx-swapping .htmx-swapping\:-translate-x-2\/3{--tw-translate-x:-66.666667%}.htmx-swapping .htmx-swapping\:-translate-x-2\/3,.htmx-swapping .htmx-swapping\:translate-x-2\/3{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.htmx-swapping .htmx-swapping\:translate-x-2\/3{--tw-translate-x:66.666667%}.htmx-swapping .htmx-swapping\:scale-0{--tw-scale-x:0;--tw-scale-y:0;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.htmx-swapping .htmx-swapping\:opacity-0{opacity:0}.group\/transition.backward .htmx-swapping .group-\[\.backward\]\/transition\:htmx-swapping\:translate-x-1\/4{--tw-translate-x:25%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group\/transition.forward .htmx-swapping .group-\[\.forward\]\/transition\:htmx-swapping\:-translate-x-1\/4{--tw-translate-x:-25%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group\/transition.backward .htmx-swapping .group-\[\.backward\]\/transition\:htmx-swapping\:scale-75,.group\/transition.forward .htmx-swapping .group-\[\.forward\]\/transition\:htmx-swapping\:scale-75{--tw-scale-x:.75;--tw-scale-y:.75;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group\/transition.backward .htmx-swapping .group-\[\.backward\]\/transition\:htmx-swapping\:opacity-0,.group\/transition.forward .htmx-swapping .group-\[\.forward\]\/transition\:htmx-swapping\:opacity-0{opacity:0}.group\/transition.backward .htmx-swapping .group-\[\.backward\]\/transition\:htmx-swapping\:ease-in,.group\/transition.forward .htmx-swapping .group-\[\.forward\]\/transition\:htmx-swapping\:ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.htmx-added\:-translate-x-2\/3.htmx-added{--tw-translate-x:-66.666667%}.htmx-added\:-translate-x-2\/3.htmx-added,.htmx-added\:translate-x-2\/3.htmx-added{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.htmx-added\:translate-x-2\/3.htmx-added{--tw-translate-x:66.666667%}.htmx-added\:scale-0.htmx-added{--tw-scale-x:0;--tw-scale-y:0;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.htmx-added\:opacity-0.htmx-added{opacity:0}.group\/transition.backward .group-\[\.backward\]\/transition\:htmx-added\:-translate-x-1\/4.htmx-added{--tw-translate-x:-25%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group\/transition.forward .group-\[\.forward\]\/transition\:htmx-added\:translate-x-1\/4.htmx-added{--tw-translate-x:25%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group\/transition.backward .group-\[\.backward\]\/transition\:htmx-added\:scale-75.htmx-added,.group\/transition.forward .group-\[\.forward\]\/transition\:htmx-added\:scale-75.htmx-added{--tw-scale-x:.75;--tw-scale-y:.75;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group\/transition.backward .group-\[\.backward\]\/transition\:htmx-added\:opacity-0.htmx-added,.group\/transition.forward .group-\[\.forward\]\/transition\:htmx-added\:opacity-0.htmx-added{opacity:0}.group\/transition.backward .group-\[\.backward\]\/transition\:htmx-added\:ease-out.htmx-added,.group\/transition.forward .group-\[\.forward\]\/transition\:htmx-added\:ease-out.htmx-added{transition-timing-function:cubic-bezier(0,0,.2,1)}.htmx-added .htmx-added\:-translate-x-2\/3{--tw-translate-x:-66.666667%}.htmx-added .htmx-added\:-translate-x-2\/3,.htmx-added .htmx-added\:translate-x-2\/3{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.htmx-added .htmx-added\:translate-x-2\/3{--tw-translate-x:66.666667%}.htmx-added .htmx-added\:scale-0{--tw-scale-x:0;--tw-scale-y:0;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.htmx-added .htmx-added\:opacity-0{opacity:0}.group\/transition.backward .htmx-added .group-\[\.backward\]\/transition\:htmx-added\:-translate-x-1\/4{--tw-translate-x:-25%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group\/transition.forward .htmx-added .group-\[\.forward\]\/transition\:htmx-added\:translate-x-1\/4{--tw-translate-x:25%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group\/transition.backward .htmx-added .group-\[\.backward\]\/transition\:htmx-added\:scale-75,.group\/transition.forward .htmx-added .group-\[\.forward\]\/transition\:htmx-added\:scale-75{--tw-scale-x:.75;--tw-scale-y:.75;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group\/transition.backward .htmx-added .group-\[\.backward\]\/transition\:htmx-added\:opacity-0,.group\/transition.forward .htmx-added .group-\[\.forward\]\/transition\:htmx-added\:opacity-0{opacity:0}.group\/transition.backward .htmx-added .group-\[\.backward\]\/transition\:htmx-added\:ease-out,.group\/transition.forward .htmx-added .group-\[\.forward\]\/transition\:htmx-added\:ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}:is([dir=rtl] .peer:checked~.rtl\:peer-checked\:after\:-translate-x-full):after{content:var(--tw-content);--tw-translate-x:-100%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}:is(.dark .dark\:border-blue-500){--tw-border-opacity:1;border-color:rgb(0 156 234/var(--tw-border-opacity))}:is(.dark .dark\:border-gray-400){--tw-border-opacity:1;border-color:rgb(156 163 175/var(--tw-border-opacity))}:is(.dark .dark\:border-gray-500){--tw-border-opacity:1;border-color:rgb(107 114 128/var(--tw-border-opacity))}:is(.dark .dark\:border-gray-600){--tw-border-opacity:1;border-color:rgb(75 85 99/var(--tw-border-opacity))}:is(.dark .dark\:border-gray-700){--tw-border-opacity:1;border-color:rgb(55 65 81/var(--tw-border-opacity))}:is(.dark .dark\:border-gray-900){--tw-border-opacity:1;border-color:rgb(17 24 39/var(--tw-border-opacity))}:is(.dark .dark\:border-green-800){--tw-border-opacity:1;border-color:rgb(48 72 18/var(--tw-border-opacity))}:is(.dark .dark\:border-primary-500){--tw-border-opacity:1;border-color:rgb(121 181 46/var(--tw-border-opacity))}:is(.dark .dark\:border-transparent){border-color:#0000}:is(.dark .dark\:bg-blue-400){--tw-bg-opacity:1;background-color:rgb(51 176 238/var(--tw-bg-opacity))}:is(.dark .dark\:bg-blue-600){--tw-bg-opacity:1;background-color:rgb(0 125 187/var(--tw-bg-opacity))}:is(.dark .dark\:bg-blue-700){--tw-bg-opacity:1;background-color:rgb(0 94 140/var(--tw-bg-opacity))}:is(.dark .dark\:bg-blue-900){--tw-bg-opacity:1;background-color:rgb(0 31 47/var(--tw-bg-opacity))}:is(.dark .dark\:bg-gray-600){--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity))}:is(.dark .dark\:bg-gray-700){--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}:is(.dark .dark\:bg-gray-800){--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}:is(.dark .dark\:bg-gray-800\/50){background-color:#1f293780}:is(.dark .dark\:bg-gray-900){--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity))}:is(.dark .dark\:bg-green-600){--tw-bg-opacity:1;background-color:rgb(97 145 37/var(--tw-bg-opacity))}:is(.dark .dark\:bg-green-700){--tw-bg-opacity:1;background-color:rgb(73 109 28/var(--tw-bg-opacity))}:is(.dark .dark\:bg-green-900){--tw-bg-opacity:1;background-color:rgb(24 36 9/var(--tw-bg-opacity))}:is(.dark .dark\:bg-red-700){--tw-bg-opacity:1;background-color:rgb(153 2 2/var(--tw-bg-opacity))}:is(.dark .dark\:bg-red-900){--tw-bg-opacity:1;background-color:rgb(51 1 1/var(--tw-bg-opacity))}:is(.dark .dark\:bg-yellow-900){--tw-bg-opacity:1;background-color:rgb(99 49 18/var(--tw-bg-opacity))}:is(.dark .dark\:bg-opacity-80){--tw-bg-opacity:0.8}:is(.dark .dark\:text-blue-100){--tw-text-opacity:1;color:rgb(204 235 251/var(--tw-text-opacity))}:is(.dark .dark\:text-blue-200){--tw-text-opacity:1;color:rgb(153 215 247/var(--tw-text-opacity))}:is(.dark .dark\:text-blue-300){--tw-text-opacity:1;color:rgb(102 196 242/var(--tw-text-opacity))}:is(.dark .dark\:text-blue-400){--tw-text-opacity:1;color:rgb(51 176 238/var(--tw-text-opacity))}:is(.dark .dark\:text-blue-500){--tw-text-opacity:1;color:rgb(0 156 234/var(--tw-text-opacity))}:is(.dark .dark\:text-gray-100){--tw-text-opacity:1;color:rgb(243 244 246/var(--tw-text-opacity))}:is(.dark .dark\:text-gray-200){--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity))}:is(.dark .dark\:text-gray-300){--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity))}:is(.dark .dark\:text-gray-400){--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}:is(.dark .dark\:text-gray-50){--tw-text-opacity:1;color:rgb(249 250 251/var(--tw-text-opacity))}:is(.dark .dark\:text-gray-500){--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}:is(.dark .dark\:text-green-300){--tw-text-opacity:1;color:rgb(175 211 130/var(--tw-text-opacity))}:is(.dark .dark\:text-green-400){--tw-text-opacity:1;color:rgb(148 196 88/var(--tw-text-opacity))}:is(.dark .dark\:text-primary-500){--tw-text-opacity:1;color:rgb(121 181 46/var(--tw-text-opacity))}:is(.dark .dark\:text-red-300){--tw-text-opacity:1;color:rgb(255 104 104/var(--tw-text-opacity))}:is(.dark .dark\:text-red-400){--tw-text-opacity:1;color:rgb(255 53 53/var(--tw-text-opacity))}:is(.dark .dark\:text-red-500){--tw-text-opacity:1;color:rgb(255 3 3/var(--tw-text-opacity))}:is(.dark .dark\:text-white){--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}:is(.dark .dark\:text-yellow-300){--tw-text-opacity:1;color:rgb(250 202 21/var(--tw-text-opacity))}:is(.dark .dark\:placeholder-gray-400)::-moz-placeholder{--tw-placeholder-opacity:1;color:rgb(156 163 175/var(--tw-placeholder-opacity))}:is(.dark .dark\:placeholder-gray-400)::placeholder{--tw-placeholder-opacity:1;color:rgb(156 163 175/var(--tw-placeholder-opacity))}:is(.dark .dark\:ring-offset-gray-700){--tw-ring-offset-color:#374151}:is(.dark .dark\:ring-offset-gray-800){--tw-ring-offset-color:#1f2937}:is(.dark .hover\:dark\:border-green-800):hover{--tw-border-opacity:1;border-color:rgb(48 72 18/var(--tw-border-opacity))}:is(.dark .dark\:hover\:bg-blue-600:hover){--tw-bg-opacity:1;background-color:rgb(0 125 187/var(--tw-bg-opacity))}:is(.dark .dark\:hover\:bg-blue-700:hover){--tw-bg-opacity:1;background-color:rgb(0 94 140/var(--tw-bg-opacity))}:is(.dark .dark\:hover\:bg-blue-800:hover){--tw-bg-opacity:1;background-color:rgb(0 62 94/var(--tw-bg-opacity))}:is(.dark .dark\:hover\:bg-gray-600:hover){--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity))}:is(.dark .dark\:hover\:bg-gray-700:hover){--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}:is(.dark .dark\:hover\:bg-gray-800:hover){--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}:is(.dark .dark\:hover\:bg-green-600:hover){--tw-bg-opacity:1;background-color:rgb(97 145 37/var(--tw-bg-opacity))}:is(.dark .dark\:hover\:bg-green-700:hover){--tw-bg-opacity:1;background-color:rgb(73 109 28/var(--tw-bg-opacity))}:is(.dark .dark\:hover\:bg-red-600:hover){--tw-bg-opacity:1;background-color:rgb(204 2 2/var(--tw-bg-opacity))}:is(.dark .hover\:dark\:bg-gray-800):hover{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}:is(.dark .dark\:hover\:text-blue-500:hover){--tw-text-opacity:1;color:rgb(0 156 234/var(--tw-text-opacity))}:is(.dark .dark\:hover\:text-gray-100:hover){--tw-text-opacity:1;color:rgb(243 244 246/var(--tw-text-opacity))}:is(.dark .dark\:hover\:text-gray-300:hover){--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity))}:is(.dark .dark\:hover\:text-white:hover){--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}:is(.dark .hover\:dark\:text-green-400):hover{--tw-text-opacity:1;color:rgb(148 196 88/var(--tw-text-opacity))}:is(.dark .dark\:focus\:border-blue-500:focus){--tw-border-opacity:1;border-color:rgb(0 156 234/var(--tw-border-opacity))}:is(.dark .dark\:focus\:border-primary-500:focus){--tw-border-opacity:1;border-color:rgb(121 181 46/var(--tw-border-opacity))}:is(.dark .dark\:focus\:text-white:focus){--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}:is(.dark .dark\:focus\:ring-blue-500:focus){--tw-ring-opacity:1;--tw-ring-color:rgb(0 156 234/var(--tw-ring-opacity))}:is(.dark .dark\:focus\:ring-blue-600:focus){--tw-ring-opacity:1;--tw-ring-color:rgb(0 125 187/var(--tw-ring-opacity))}:is(.dark .dark\:focus\:ring-blue-800:focus){--tw-ring-opacity:1;--tw-ring-color:rgb(0 62 94/var(--tw-ring-opacity))}:is(.dark .dark\:focus\:ring-gray-600:focus){--tw-ring-opacity:1;--tw-ring-color:rgb(75 85 99/var(--tw-ring-opacity))}:is(.dark .dark\:focus\:ring-green-500:focus){--tw-ring-opacity:1;--tw-ring-color:rgb(121 181 46/var(--tw-ring-opacity))}:is(.dark .dark\:focus\:ring-green-800:focus){--tw-ring-opacity:1;--tw-ring-color:rgb(48 72 18/var(--tw-ring-opacity))}:is(.dark .dark\:focus\:ring-primary-500:focus){--tw-ring-opacity:1;--tw-ring-color:rgb(121 181 46/var(--tw-ring-opacity))}:is(.dark .dark\:focus\:ring-primary-600:focus){--tw-ring-opacity:1;--tw-ring-color:rgb(97 145 37/var(--tw-ring-opacity))}:is(.dark .dark\:focus\:ring-offset-gray-700:focus){--tw-ring-offset-color:#374151}:is(.dark .group:hover .dark\:group-hover\:text-white){--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.group.has-error :is(.dark .group-\[\.has-error\]\:dark\:border-red-500){--tw-border-opacity:1;border-color:rgb(255 3 3/var(--tw-border-opacity))}.group.has-error :is(.dark .group-\[\.has-error\]\:dark\:bg-gray-700){--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}.group.has-error :is(.dark .group-\[\.has-error\]\:dark\:text-red-500){--tw-text-opacity:1;color:rgb(255 3 3/var(--tw-text-opacity))}.group.has-error :is(.dark .group-\[\.has-error\]\:dark\:placeholder-red-500)::-moz-placeholder{--tw-placeholder-opacity:1;color:rgb(255 3 3/var(--tw-placeholder-opacity))}.group.has-error :is(.dark .group-\[\.has-error\]\:dark\:placeholder-red-500)::placeholder{--tw-placeholder-opacity:1;color:rgb(255 3 3/var(--tw-placeholder-opacity))}:is(.dark .peer:focus~.dark\:peer-focus\:ring-blue-800){--tw-ring-opacity:1;--tw-ring-color:rgb(0 62 94/var(--tw-ring-opacity))}@media (min-width:640px){.sm\:ml-4{margin-left:1rem}.sm\:block{display:block}.sm\:inline{display:inline}.sm\:space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}.sm\:space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem*var(--tw-space-y-reverse))}.sm\:rounded-lg{border-radius:.5rem}.sm\:p-6{padding:1.5rem}.sm\:py-5{padding-top:1.25rem;padding-bottom:1.25rem}.sm\:text-base{font-size:1rem;line-height:1.5rem}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}}@media (min-width:768px){.md\:ml-2{margin-left:.5rem}.md\:mr-24{margin-right:6rem}.md\:block{display:block}.md\:table-cell{display:table-cell}.md\:h-\[600px\]{height:600px}.md\:h-\[800px\]{height:800px}.md\:w-\[750px\]{width:750px}.md\:flex-row{flex-direction:row}.md\:items-center{align-items:center}.md\:justify-center{justify-content:center}.md\:space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.75rem*var(--tw-space-x-reverse));margin-left:calc(.75rem*(1 - var(--tw-space-x-reverse)))}.md\:space-y-0>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(0px*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(0px*var(--tw-space-y-reverse))}.md\:p-12{padding:3rem}}@media (min-width:1024px){.lg\:block{display:block}.lg\:table-cell{display:table-cell}.lg\:hidden{display:none}.lg\:h-\[900px\]{height:900px}.lg\:h-\[640px\]{height:640px}.lg\:h-\[600px\]{height:600px}.lg\:w-96{width:24rem}.lg\:w-\[850px\]{width:850px}.lg\:w-\[920px\]{width:920px}.lg\:w-\[900px\]{width:900px}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:flex-row{flex-direction:row}.lg\:items-center{align-items:center}.lg\:items-baseline{align-items:baseline}.lg\:justify-end{justify-content:flex-end}.lg\:justify-between{justify-content:space-between}.lg\:space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}.lg\:space-y-0>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(0px*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(0px*var(--tw-space-y-reverse))}.lg\:px-5{padding-left:1.25rem;padding-right:1.25rem}.lg\:pl-3{padding-left:.75rem}.lg\:pl-64{padding-left:16rem}}@media (min-width:1280px){.xl\:table-cell{display:table-cell}}@media (min-width:1536px){.\32xl\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}.\[\&\.active\]\:bg-primary-300.active{--tw-bg-opacity:1;background-color:rgb(175 211 130/var(--tw-bg-opacity))}.\[\&\.active\]\:bg-primary-500.active{--tw-bg-opacity:1;background-color:rgb(121 181 46/var(--tw-bg-opacity))}:is(.dark .\[\&\.active\]\:dark\:bg-primary-700).active{--tw-bg-opacity:1;background-color:rgb(73 109 28/var(--tw-bg-opacity))}.\[\&\.implied\]\:text-gray-500.implied{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}
\ No newline at end of file
diff --git a/src/clj/amazonica/aws/textract.clj b/src/clj/amazonica/aws/textract.clj
index 3278a264..a0ed389e 100644
--- a/src/clj/amazonica/aws/textract.clj
+++ b/src/clj/amazonica/aws/textract.clj
@@ -1,12 +1,11 @@
(ns amazonica.aws.textract
(:require [amazonica.core :as amz])
- (:import [com.amazonaws.services.textract AmazonTextractClient ]))
+ (:import [com.amazonaws.services.textract AmazonTextractClient]))
-#_
-(import '[com.amazonaws.services.textract AmazonTextractClient ])
-#_(import '[com.amazonaws.services.textract.model S3Object ])
-#_(import '[com.amazonaws.services.textract.model StartExpenseAnalysisRequest ])
-#_(import '[com.amazonaws.services.textract.model GetExpenseAnalysisRequest ])
+#_(import '[com.amazonaws.services.textract AmazonTextractClient])
+#_(import '[com.amazonaws.services.textract.model S3Object])
+#_(import '[com.amazonaws.services.textract.model StartExpenseAnalysisRequest])
+#_(import '[com.amazonaws.services.textract.model GetExpenseAnalysisRequest])
#_(import '[com.amazonaws.services.textract.model DocumentLocation])
(amz/set-client AmazonTextractClient *ns*)
diff --git a/src/clj/auto_ap/background/metrics.clj b/src/clj/auto_ap/background/metrics.clj
index 760d50d7..608f128d 100644
--- a/src/clj/auto_ap/background/metrics.clj
+++ b/src/clj/auto_ap/background/metrics.clj
@@ -28,7 +28,6 @@
[(str "container:" (:DockerId container-data))
(str "ip:" (-> container-data :Networks first :IPv4Addresses first))])
-
(mount/defstate container-tags
:start (get-container-tags)
:stop nil)
diff --git a/src/clj/auto_ap/cursor.clj b/src/clj/auto_ap/cursor.clj
index 28508791..c6e867a3 100644
--- a/src/clj/auto_ap/cursor.clj
+++ b/src/clj/auto_ap/cursor.clj
@@ -5,14 +5,11 @@
(path [cursor])
(state [cursor]))
-
(defprotocol ITransact
(-transact! [cursor f]))
-
(declare to-cursor cursor?)
-
(deftype ValCursor [value state path]
IDeref
(deref [_]
@@ -23,9 +20,8 @@
ITransact
(-transact! [_ f]
(get-in
- (swap! state (if (empty? path) f #(update-in % path f)))
- path)))
-
+ (swap! state (if (empty? path) f #(update-in % path f)))
+ path)))
(deftype MapCursor [value state path]
Counted
@@ -53,14 +49,13 @@
ITransact
(-transact! [cursor f]
(get-in
- (swap! state (if (empty? path) f #(update-in % path f)))
- path))
+ (swap! state (if (empty? path) f #(update-in % path f)))
+ path))
Seqable
(seq [this]
(for [[k v] @this]
[k (to-cursor v state (conj path k) nil)])))
-
(deftype VecCursor [value state path]
Counted
(count [_]
@@ -91,29 +86,25 @@
ITransact
(-transact! [cursor f]
(get-in
- (swap! state (if (empty? path) f #(update-in % path f)))
- path))
+ (swap! state (if (empty? path) f #(update-in % path f)))
+ path))
Seqable
(seq [this]
(for [[v i] (map vector @this (range))]
(to-cursor v state (conj path i) nil))))
-
(defn- to-cursor
([v state path value]
(cond
(cursor? v) v
(map? v) (MapCursor. value state path)
(vector? v) (VecCursor. value state path)
- :else (ValCursor. value state path)
- )))
-
+ :else (ValCursor. value state path))))
(defn cursor? [c]
"Returns true if c is a cursor."
(satisfies? ICursor c))
-
(defn cursor [v]
"Creates cursor from supplied value v. If v is an ordinary
data structure, it is wrapped into atom. If v is an atom,
@@ -123,7 +114,6 @@
(if (instance? Atom v) v (atom v))
[] nil))
-
(defn synthetic-cursor [v prefix]
(let [internal-cursor (cursor v)]
(reify ICursor
@@ -132,14 +122,12 @@
(state [this]
(state internal-cursor)))))
-
(defn transact! [cursor f]
"Changes value beneath cursor by passing it to a single-argument
function f. Old value will be passed as function argument. Function
result will be the new value."
(-transact! cursor f))
-
(defn update! [cursor v]
"Replaces value supplied by cursor with value v."
(-transact! cursor (constantly v)))
diff --git a/src/clj/auto_ap/datomic.clj b/src/clj/auto_ap/datomic.clj
index 80b9271e..d66cd4c3 100644
--- a/src/clj/auto_ap/datomic.clj
+++ b/src/clj/auto_ap/datomic.clj
@@ -5,11 +5,11 @@
[iol-ion.tx.propose-invoice]
[iol-ion.tx.reset-rels]
[iol-ion.tx.reset-scalars]
- [iol-ion.tx.upsert-entity]
- [iol-ion.tx.upsert-invoice]
- [iol-ion.tx.upsert-ledger]
- [iol-ion.tx.upsert-transaction]
- [iol-ion.tx.upsert-sales-summary-ledger]
+ [iol-ion.tx.upsert-entity]
+ [iol-ion.tx.upsert-invoice]
+ [iol-ion.tx.upsert-ledger]
+ [iol-ion.tx.upsert-transaction]
+ [iol-ion.tx.upsert-sales-summary-ledger]
[com.github.ivarref.gen-fn :refer [gen-fn! datomic-fn]]
[auto-ap.utils :refer [default-pagination-size by]]
[clojure.edn :as edn]
@@ -27,8 +27,8 @@
(def uri (:datomic-url env))
#_(mount/defstate client
- :start (dc/client (:client-config env))
- :stop nil)
+ :start (dc/client (:client-config env))
+ :stop nil)
(mount/defstate conn
:start (dc/connect uri)
@@ -38,21 +38,20 @@
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
#_(defn create-database []
- (d/create-database uri))
+ (d/create-database uri))
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
#_(defn drop-database []
- (d/delete-database uri))
+ (d/delete-database uri))
(defn remove-nils [m]
(let [result (reduce-kv
- (fn [m k v]
- (if (not (nil? v))
- (assoc m k v)
- m
- ))
- {}
- m)]
+ (fn [m k v]
+ (if (not (nil? v))
+ (assoc m k v)
+ m))
+ {}
+ m)]
(if (seq result)
result
nil)))
@@ -80,7 +79,7 @@
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/doc "A vendor's email address"}
-
+
{:db/ident :vendor/phone
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
@@ -102,14 +101,13 @@
:db/valueType :db.type/ref
:db/isComponent true
:db/cardinality :db.cardinality/one
- :db/doc "The vendor's secondary contact"}
+ :db/doc "The vendor's secondary contact"}
{:db/ident :vendor/address
:db/valueType :db.type/ref
:db/cardinality :db.cardinality/one
:db/isComponent true
:db.install/_attribute :db.part/db
- :db/doc "The vendor's address"}
- ])
+ :db/doc "The vendor's address"}])
(def client-schema
[{:db/ident :client/original-id
@@ -151,8 +149,7 @@
:db/doc "Bank accounts for the client"}])
(def address-schema
- [
- {:db/ident :address/street1
+ [{:db/ident :address/street1
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/doc "123 main st"}
@@ -174,8 +171,7 @@
:db/doc "95014"}])
(def contact-schema
- [
- {:db/ident :contact/name
+ [{:db/ident :contact/name
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/doc "John Smith"}
@@ -188,8 +184,6 @@
:db/cardinality :db.cardinality/one
:db/doc "hello@example.com"}])
-
-
(def bank-account-schema
[{:db/ident :bank-account/external-id
:db/valueType :db.type/long
@@ -296,7 +290,6 @@
:db/cardinality :db.cardinality/many
:db/isComponent true
:db/doc "The expense account categories for this invoice"}
-
{:db/ident :invoice-status/paid}
{:db/ident :invoice-status/unpaid}
@@ -312,17 +305,15 @@
:db/valueType :db.type/long
:db/cardinality :db.cardinality/one
:db/doc "The code for the expense account"}
- {:db/ident :invoice-expense-account/location
+ {:db/ident :invoice-expense-account/location
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
- :db/doc "Location for this expense account"}
+ :db/doc "Location for this expense account"}
{:db/ident :invoice-expense-account/amount
:db/valueType :db.type/double
:db/cardinality :db.cardinality/one
:db/doc "The amount that this contributes to"}])
-
-
(def payment-schema
[{:db/ident :payment/original-id
:db/valueType :db.type/long
@@ -373,9 +364,8 @@
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/doc "raw data used to generate check pdf"}
-
- ;; relations
+;; relations
{:db/ident :payment/vendor
:db/valueType :db.type/ref
:db/cardinality :db.cardinality/one
@@ -400,8 +390,7 @@
{:db/ident :payment-type/cash}
{:db/ident :payment-type/check}
- {:db/ident :payment-type/debit}
- ])
+ {:db/ident :payment-type/debit}])
(def invoice-payment-schema
[{:db/ident :invoice-payment/original-id
@@ -414,7 +403,7 @@
:db/valueType :db.type/double
:db/cardinality :db.cardinality/one
:db/doc "The amount that was paid to this invoice"}
-
+
;; relations
{:db/ident :invoice-payment/invoice
:db/valueType :db.type/ref
@@ -481,8 +470,7 @@
:db/cardinality :db.cardinality/one
:db/doc "The check number that was parsed from the description"}
-
- ;; relations
+;; relations
{:db/ident :transaction/vendor
:db/valueType :db.type/ref
:db/cardinality :db.cardinality/one
@@ -498,8 +486,7 @@
{:db/ident :transaction/payment
:db/valueType :db.type/ref
:db/cardinality :db.cardinality/one
- :db/doc "The payment that this transaction matched to"}
- ])
+ :db/doc "The payment that this transaction matched to"}])
(def user-schema
[{:db/ident :user/original-id
@@ -531,12 +518,10 @@
;;enums
{:db/ident :user-role/admin}
{:db/ident :user-role/user}
- {:db/ident :user-role/none}
- ])
+ {:db/ident :user-role/none}])
(def base-schema
- [ address-schema contact-schema vendor-schema client-schema bank-account-schema invoice-schema invoice-expense-account-schema payment-schema invoice-payment-schema transaction-schema user-schema])
-
+ [address-schema contact-schema vendor-schema client-schema bank-account-schema invoice-schema invoice-expense-account-schema payment-schema invoice-payment-schema transaction-schema user-schema])
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
(defn migrate-vendors [_]
@@ -590,19 +575,19 @@
(defn add-sorter-fields-2 [q sort-map args]
(reduce
- (fn [q {:keys [sort-key]}]
- (merge-query q
- {:query {:find [(last (last (sort-map
- sort-key
- (println "Warning, trying to sort by unsupported field" sort-key))))]
- :where (sort-map
- sort-key
- (println "Warning, trying to sort by unsupported field" sort-key))}}))
- q
- (:sort args)))
+ (fn [q {:keys [sort-key]}]
+ (merge-query q
+ {:query {:find [(last (last (sort-map
+ sort-key
+ (println "Warning, trying to sort by unsupported field" sort-key))))]
+ :where (sort-map
+ sort-key
+ (println "Warning, trying to sort by unsupported field" sort-key))}}))
+ q
+ (:sort args)))
(defn apply-sort-3 [args results]
-
+
(let [sort-bys (conj (into [] (:sort args))
{:sort-key "default" :asc (if (contains? args :default-asc?)
(:default-asc? args)
@@ -611,7 +596,7 @@
comparator (fn [xs ys]
(reduce
(fn [_ i]
-
+
(let [comparison (if (:asc (nth sort-bys i))
(compare (nth xs i) (nth ys i))
(compare (nth ys i) (nth xs i)))]
@@ -625,18 +610,18 @@
;; TODO replace COULD JUST BE SORT-3
(defn apply-sort-4 [args results]
-
+
(let [sort-bys (-> []
(into (:sort args))
(conj {:sort-key "default" :asc (if (contains? args :default-asc?)
- (:default-asc? args)
- true)})
+ (:default-asc? args)
+ true)})
(conj {:sort-key "e" :asc true}))
length (count sort-bys)
comparator (fn [xs ys]
(reduce
(fn [_ i]
-
+
(let [comparison (if (:asc (nth sort-bys i))
(compare (nth xs i) (nth ys i))
(compare (nth ys i) (nth xs i)))]
@@ -657,7 +642,7 @@
(defn apply-pagination [args results]
{:ids (->> results
(drop (or (:start args) 0))
- (take (or (:count args )
+ (take (or (:count args)
(:per-page args)
default-pagination-size))
(map last))
@@ -669,8 +654,8 @@
(reduce
(fn [full-tx batch]
(let [batch (conj (vec batch) {:db/id "datomic.tx"
- :audit/user (str (:user/role id) "-" (:user/name id))
- :audit/batch batch-id})
+ :audit/user (str (:user/role id) "-" (:user/name id))
+ :audit/batch batch-id})
_ (mu/log ::transacting-batch
:batch batch-id
:count (count batch))
@@ -687,18 +672,17 @@
(partition-all 200 txes))))
(defn audit-transact [txes id]
- (try
+ (try
@(dc/transact-async conn (conj txes {:db/id "datomic.tx"
- :audit/user (str (:user/role id) "-" (:user/name id))}))
+ :audit/user (str (:user/role id) "-" (:user/name id))}))
(catch Exception e
(mu/log ::transaction-error
:exception e
:level :error
:tx txes)
- (throw e)
- )))
+ (throw e))))
-(defn pull-many [db read ids ]
+(defn pull-many [db read ids]
(->> (dc/q '[:find (pull ?e r)
:in $ [?e ...] r]
db
@@ -706,22 +690,22 @@
read)
(map first)))
-(defn pull-many-by-id [db read ids ]
+(defn pull-many-by-id [db read ids]
(into {}
(map (fn [[e]]
[(:db/id e) e]))
(dc/q '[:find (pull ?e r)
- :in $ [?e ...] r]
- db
- ids
- read)))
+ :in $ [?e ...] r]
+ db
+ ids
+ read)))
(defn random-tempid []
(str (UUID/randomUUID)))
(defn pull-id [db id]
(if (sequential? id)
- (ffirst (dc/q '[:find ?i
+ (ffirst (dc/q '[:find ?i
:in $ [?a ?v]
:where [?i ?a ?v]]
db
@@ -734,170 +718,163 @@
(defn pull-ref [db k id]
(:db/id (pull-attr db k id)))
+#_(comment
+ (dc/pull (dc/db conn) '[*] 175921860633685)
+ (upsert-entity (dc/db conn) {:db/id 175921860633685 :invoice/invoice-number nil :invoice/date #inst "2021-01-01" :invoice/expense-accounts [:reset-rels [{:db/id "new" :invoice-expense-account/amount 1}]]})
+ (upsert-entity (dc/db conn) {:invoice/client #:db{:id 79164837221949},
+ :invoice/status #:db{:id 101155069755470, :ident :invoice-status/paid},
+ :invoice/due #inst "2020-12-23T08:00:00.000-00:00",
+ :invoice/invoice-number "12648",
+ :invoice/import-status
+ :import-status/imported,
+ :invoice/vendor nil,
+ :invoice/date #inst "2020-12-16T08:00:00.000-00:00",
+ :entity/migration-key 17592234924273,
+ :db/id 175921860633685,
+ :invoice/outstanding-balance 0.0,
+ :invoice/expense-accounts
+ [{:entity/migration-key 17592234924274,
+ :invoice-expense-account/location nil
+ :invoice-expense-account/amount 360.0,
+ :invoice-expense-account/account #:db{:id 92358976759248}}]})
-#_(comment
- (dc/pull (dc/db conn) '[*] 175921860633685)
-
- (upsert-entity (dc/db conn) {:db/id 175921860633685 :invoice/invoice-number nil :invoice/date #inst "2021-01-01" :invoice/expense-accounts [:reset-rels [{:db/id "new" :invoice-expense-account/amount 1}]]})
-
- (upsert-entity (dc/db conn) {:invoice/client #:db{:id 79164837221949},
- :invoice/status #:db{:id 101155069755470, :ident :invoice-status/paid},
- :invoice/due #inst "2020-12-23T08:00:00.000-00:00",
- :invoice/invoice-number "12648",
- :invoice/import-status
- :import-status/imported,
- :invoice/vendor nil,
- :invoice/date #inst "2020-12-16T08:00:00.000-00:00",
- :entity/migration-key 17592234924273,
- :db/id 175921860633685,
- :invoice/outstanding-balance 0.0,
- :invoice/expense-accounts
- [{:entity/migration-key 17592234924274,
- :invoice-expense-account/location nil
- :invoice-expense-account/amount 360.0,
- :invoice-expense-account/account #:db{:id 92358976759248}}],})
-
-
-
- #_(dc/pull (dc/db conn) auto-ap.datomic.clients 79164837221904)
- (upsert-entity (dc/db conn) {:client/name "20Twenty - WG Development LLC",
- :client/square-locations
- [{:db/id 83562883711605,
- :entity/migration-key 17592258901782,
- :square-location/square-id "L2579ATQ0X1ET",
- :square-location/name "20Twenty",
- :square-location/client-location "WG"}],
- :client/square-auth-token
- "EAAAEEr749Ea6AdPTdngsmUPwIM3ETbPwcx3QQl_NS0KWuIL-JNzAg4f3W9DGQhb",
- :client/bank-accounts
- [{:bank-account/sort-order 2,
- :bank-account/include-in-reports true,
- :bank-account/number "3467",
- :bank-account/code "20TY-WFCC3467",
- :bank-account/locations ["WG"],
- :entity/migration-key 17592245102834,
- :bank-account/current-balance 11160.289999999979,
- :bank-account/name "Wells Fargo CC - 3467",
- :db/id 83562883732805,
- :bank-account/start-date #inst "2021-12-01T08:00:00.000-00:00",
- :bank-account/visible true,
- :bank-account/type
- #:db{:id 101155069755504, :ident :bank-account-type/credit},
- :bank-account/intuit-bank-account #:db{:id 105553116286744},
- :bank-account/integration-status
- {:db/id 74766790691480,
- :entity/migration-key 17592267080690,
- :integration-status/last-updated #inst "2022-08-23T03:47:44.892-00:00",
- :integration-status/last-attempt #inst "2022-08-23T03:47:44.892-00:00",
+ #_(dc/pull (dc/db conn) auto-ap.datomic.clients 79164837221904)
+ (upsert-entity (dc/db conn) {:client/name "20Twenty - WG Development LLC",
+ :client/square-locations
+ [{:db/id 83562883711605,
+ :entity/migration-key 17592258901782,
+ :square-location/square-id "L2579ATQ0X1ET",
+ :square-location/name "20Twenty",
+ :square-location/client-location "WG"}],
+ :client/square-auth-token
+ "EAAAEEr749Ea6AdPTdngsmUPwIM3ETbPwcx3QQl_NS0KWuIL-JNzAg4f3W9DGQhb",
+ :client/bank-accounts
+ [{:bank-account/sort-order 2,
+ :bank-account/include-in-reports true,
+ :bank-account/number "3467",
+ :bank-account/code "20TY-WFCC3467",
+ :bank-account/locations ["WG"],
+ :entity/migration-key 17592245102834,
+ :bank-account/current-balance 11160.289999999979,
+ :bank-account/name "Wells Fargo CC - 3467",
+ :db/id 83562883732805,
+ :bank-account/start-date #inst "2021-12-01T08:00:00.000-00:00",
+ :bank-account/visible true,
+ :bank-account/type
+ #:db{:id 101155069755504, :ident :bank-account-type/credit},
+ :bank-account/intuit-bank-account #:db{:id 105553116286744},
+ :bank-account/integration-status
+ {:db/id 74766790691480,
+ :entity/migration-key 17592267080690,
+ :integration-status/last-updated #inst "2022-08-23T03:47:44.892-00:00",
+ :integration-status/last-attempt #inst "2022-08-23T03:47:44.892-00:00",
+ :integration-status/state
+ #:db{:id 101155069755529, :ident :integration-state/success}},
+ :bank-account/bank-name "Wells Fargo"}
+ {:bank-account/sort-order 0,
+ :bank-account/include-in-reports true,
+ :bank-account/numeric-code 11301,
+ :bank-account/check-number 301,
+ :bank-account/number "1734742859",
+ :bank-account/code "20TY-WF2882",
+ :bank-account/locations ["WG"],
+ :bank-account/bank-code "11-4288/1210 4285",
+ :entity/migration-key 17592241193004,
+ :bank-account/current-balance -47342.54000000085,
+ :bank-account/name "Wells Fargo Main - 2859",
+ :db/id 83562883732846,
+ :bank-account/start-date #inst "2021-12-01T08:00:00.000-00:00",
+ :bank-account/visible true,
+ :bank-account/type
+ #:db{:id 101155069755468, :ident :bank-account-type/check},
+ :bank-account/intuit-bank-account #:db{:id 105553116286745},
+ :bank-account/routing "121042882",
+ :bank-account/integration-status
+ {:db/id 74766790691458,
+ :entity/migration-key 17592267080255,
+ :integration-status/last-updated #inst "2022-08-23T03:46:45.879-00:00",
+ :integration-status/last-attempt #inst "2022-08-23T03:46:45.879-00:00",
+ :integration-status/state
+ #:db{:id 101155069755529, :ident :integration-state/success}},
+ :bank-account/bank-name "Wells Fargo"}
+ {:bank-account/sort-order 1,
+ :bank-account/include-in-reports true,
+ :bank-account/numeric-code 20101,
+ :bank-account/yodlee-account-id 27345526,
+ :bank-account/number "41006",
+ :bank-account/code "20TY-Amex41006",
+ :bank-account/locations ["WG"],
+ :entity/migration-key 17592241193006,
+ :bank-account/current-balance 9674.069999999963,
+ :bank-account/name "Amex - 41006",
+ :db/id 83562883732847,
+ :bank-account/visible true,
+ :bank-account/type
+ #:db{:id 101155069755504, :ident :bank-account-type/credit},
+ :bank-account/bank-name "American Express"}
+ {:bank-account/sort-order 3,
+ :bank-account/include-in-reports true,
+ :bank-account/numeric-code 11101,
+ :bank-account/code "20TY-0",
+ :bank-account/locations ["WG"],
+ :entity/migration-key 17592241193005,
+ :bank-account/current-balance 0.0,
+ :bank-account/name "CASH",
+ :db/id 83562883732848,
+ :bank-account/visible true,
+ :bank-account/type
+ #:db{:id 101155069755469, :ident :bank-account-type/cash}}],
+ :entity/migration-key 17592241193003,
+ :db/id 79164837221904,
+ :client/address
+ {:db/id 105553116285906,
+ :entity/migration-key 17592250661126,
+ :address/street1 "1389 Lincoln Ave",
+ :address/city "San Jose",
+ :address/state "CA",
+ :address/zip "95125"},
+ :client/code "NY",
+ :client/locations ["WE" "NG"],
+ :client/square-integration-status
+ {:db/id 74766790691447,
+ :entity/migration-key 17592267072653,
+ :integration-status/last-updated #inst "2022-08-23T13:09:16.082-00:00",
+ :integration-status/last-attempt #inst "2022-08-23T13:08:47.018-00:00",
:integration-status/state
- #:db{:id 101155069755529, :ident :integration-state/success}},
- :bank-account/bank-name "Wells Fargo"}
- {:bank-account/sort-order 0,
- :bank-account/include-in-reports true,
- :bank-account/numeric-code 11301,
- :bank-account/check-number 301,
- :bank-account/number "1734742859",
- :bank-account/code "20TY-WF2882",
- :bank-account/locations ["WG"],
- :bank-account/bank-code "11-4288/1210 4285",
- :entity/migration-key 17592241193004,
- :bank-account/current-balance -47342.54000000085,
- :bank-account/name "Wells Fargo Main - 2859",
- :db/id 83562883732846,
- :bank-account/start-date #inst "2021-12-01T08:00:00.000-00:00",
- :bank-account/visible true,
- :bank-account/type
- #:db{:id 101155069755468, :ident :bank-account-type/check},
- :bank-account/intuit-bank-account #:db{:id 105553116286745},
- :bank-account/routing "121042882",
- :bank-account/integration-status
- {:db/id 74766790691458,
- :entity/migration-key 17592267080255,
- :integration-status/last-updated #inst "2022-08-23T03:46:45.879-00:00",
- :integration-status/last-attempt #inst "2022-08-23T03:46:45.879-00:00",
- :integration-status/state
- #:db{:id 101155069755529, :ident :integration-state/success}},
- :bank-account/bank-name "Wells Fargo"}
- {:bank-account/sort-order 1,
- :bank-account/include-in-reports true,
- :bank-account/numeric-code 20101,
- :bank-account/yodlee-account-id 27345526,
- :bank-account/number "41006",
- :bank-account/code "20TY-Amex41006",
- :bank-account/locations ["WG"],
- :entity/migration-key 17592241193006,
- :bank-account/current-balance 9674.069999999963,
- :bank-account/name "Amex - 41006",
- :db/id 83562883732847,
- :bank-account/visible true,
- :bank-account/type
- #:db{:id 101155069755504, :ident :bank-account-type/credit},
- :bank-account/bank-name "American Express"}
- {:bank-account/sort-order 3,
- :bank-account/include-in-reports true,
- :bank-account/numeric-code 11101,
- :bank-account/code "20TY-0",
- :bank-account/locations ["WG"],
- :entity/migration-key 17592241193005,
- :bank-account/current-balance 0.0,
- :bank-account/name "CASH",
- :db/id 83562883732848,
- :bank-account/visible true,
- :bank-account/type
- #:db{:id 101155069755469, :ident :bank-account-type/cash}}],
- :entity/migration-key 17592241193003,
- :db/id 79164837221904,
- :client/address
- {:db/id 105553116285906,
- :entity/migration-key 17592250661126,
- :address/street1 "1389 Lincoln Ave",
- :address/city "San Jose",
- :address/state "CA",
- :address/zip "95125"},
- :client/code "NY",
- :client/locations ["WE" "NG"],
- :client/square-integration-status
- {:db/id 74766790691447,
- :entity/migration-key 17592267072653,
- :integration-status/last-updated #inst "2022-08-23T13:09:16.082-00:00",
- :integration-status/last-attempt #inst "2022-08-23T13:08:47.018-00:00",
- :integration-status/state
- #:db{:id 101155069755529, :ident :integration-state/success}}})
-
- )
+ #:db{:id 101155069755529, :ident :integration-state/success}}}))
(defn install-functions []
@(dc/transact conn
- (edn/read-string {:readers {'db/id id-literal
- 'db/fn construct}} (slurp (io/resource "functions.edn")))))
+ (edn/read-string {:readers {'db/id id-literal
+ 'db/fn construct}} (slurp (io/resource "functions.edn")))))
(defn all-schema []
(edn/read-string (slurp (io/resource "schema.edn"))))
(defn transact-schema [conn]
@(dc/transact conn
- (edn/read-string (slurp (io/resource "schema.edn"))))
+ (edn/read-string (slurp (io/resource "schema.edn"))))
;; this is temporary for any new stuff that needs to be asserted for cloud migration.
@(dc/transact conn
- (edn/read-string (slurp (io/resource "cloud-migration-schema.edn")))))
+ (edn/read-string (slurp (io/resource "cloud-migration-schema.edn")))))
(defn backoff [n]
- (let [base-timeout 500
+ (let [base-timeout 500
max-timeout 300000 ; 5 minutes
max-retries 10
backoff-time (* base-timeout (Math/pow 2 (min n max-retries)))]
(min (+ backoff-time (rand-int base-timeout)) max-timeout)))
(defn transact-with-backoff
- ([tx ] (transact-with-backoff tx 0))
+ ([tx] (transact-with-backoff tx 0))
([tx attempt]
- (try
+ (try
@(dc/transact conn tx)
(catch Exception e
(if (< attempt 10)
- (do
+ (do
(Thread/sleep (backoff attempt))
(mu/log ::transact-failed
:exception e
@@ -923,7 +900,6 @@
(into #{}
(map :db/id (:user/clients id [])))))
-
(defn query2 [query]
(apply dc/q (:query query) (:args query)))
@@ -933,14 +909,14 @@
(defn observable-query [query]
(mu/with-context {:query (pr-str (:query query))
:args (pr-str (:args query))}
- (mu/trace ::query
- []
- (let [query-results (dc/query {:query (:query query)
- :args (:args query)
- :query-stats true
- :io-context ::hello})]
- (alog/info ::query-stats
- :io-stats (pr-str (:io-stats query-results))
- :query-stats (pr-str (:query-stats query-results)))
- (:ret query-results)))))
+ (mu/trace ::query
+ []
+ (let [query-results (dc/query {:query (:query query)
+ :args (:args query)
+ :query-stats true
+ :io-context ::hello})]
+ (alog/info ::query-stats
+ :io-stats (pr-str (:io-stats query-results))
+ :query-stats (pr-str (:query-stats query-results)))
+ (:ret query-results)))))
diff --git a/src/clj/auto_ap/datomic/accounts.clj b/src/clj/auto_ap/datomic/accounts.clj
index 51f9d0b6..28ed5d45 100644
--- a/src/clj/auto_ap/datomic/accounts.clj
+++ b/src/clj/auto_ap/datomic/accounts.clj
@@ -12,18 +12,18 @@
[datomic.api :as dc]))
(defn <-datomic [a]
- (-> a
+ (-> a
(update :account/applicability :db/ident)
(update :account/invoice-allowance :db/ident)
(update :account/vendor-allowance :db/ident)))
(def default-read ['* {:account/type [:db/ident :db/id]
- :account/applicability [:db/ident :db/id]
+ :account/applicability [:db/ident :db/id]
:account/invoice-allowance [:db/ident :db/id]
:account/vendor-allowance [:db/ident :db/id]
- :account/client-overrides [:db/id
- :account-client-override/name
- {:account-client-override/client [:db/id :client/name]}]}])
+ :account/client-overrides [:db/id
+ :account-client-override/name
+ {:account-client-override/client [:db/id :client/name]}]}])
(defn clientize [a client]
(if-let [override-name (->> a
@@ -52,44 +52,44 @@
(map <-datomic)))))
(defn get-for-vendor [vendor-id client-id]
- (if client-id
+ (if client-id
(->>
- (dc/q '[:find (pull ?e r)
- :in $ ?v ?c r
- :where (or-join [?v ?c ?e]
- (and [?v :vendor/account-overrides ?ao]
- [?ao :vendor-account-override/client ?c]
- [?ao :vendor-account-override/account ?e])
- (and [?v :vendor/account-overrides ?ao]
- (not [?ao :vendor-account-override/client ?c])
- [?v :vendor/default-account ?e])
- (and (not [?v :vendor/account-overrides])
- [?v :vendor/default-account ?e]))]
+ (dc/q '[:find (pull ?e r)
+ :in $ ?v ?c r
+ :where (or-join [?v ?c ?e]
+ (and [?v :vendor/account-overrides ?ao]
+ [?ao :vendor-account-override/client ?c]
+ [?ao :vendor-account-override/account ?e])
+ (and [?v :vendor/account-overrides ?ao]
+ (not [?ao :vendor-account-override/client ?c])
+ [?v :vendor/default-account ?e])
+ (and (not [?v :vendor/account-overrides])
+ [?v :vendor/default-account ?e]))]
- (dc/db conn )
+ (dc/db conn)
vendor-id
client-id
default-read)
- (map first)
- (map <-datomic)
- first)
+ (map first)
+ (map <-datomic)
+ first)
(<-datomic (dc/q '[:find (pull ?e r)
:in $ ?v r
:where [?v :vendor/default-account ?e]]
- (dc/db conn )
+ (dc/db conn)
vendor-id
default-read))))
(defn get-account-by-numeric-code-and-sets [numeric-code _]
(->>
- (dc/q {:find ['(pull ?e [* {:account/type [:db/ident :db/id]}])]
- :in ['$ '?numeric-code]
- :where ['[?e :account/numeric-code ?numeric-code]]}
- (dc/db conn) numeric-code)
- (map first)
- (map <-datomic)
- (first)))
+ (dc/q {:find ['(pull ?e [* {:account/type [:db/ident :db/id]}])]
+ :in ['$ '?numeric-code]
+ :where ['[?e :account/numeric-code ?numeric-code]]}
+ (dc/db conn) numeric-code)
+ (map first)
+ (map <-datomic)
+ (first)))
(defn raw-graphql-ids [db args]
(let [query (cond-> {:query {:find []
@@ -111,23 +111,21 @@
:args [(re-pattern (str "(?i)" (:name-like args)))]})
true
- (merge-query {:query {:find ['?sort-default '?e ]
+ (merge-query {:query {:find ['?sort-default '?e]
:where ['[?e :account/name]
'[?e :account/numeric-code ?sort-default]]}}))]
-
(cond->> (query2 query)
true (apply-sort-3 args)
true (apply-pagination args))))
-
(defn graphql-results [ids db _]
(let [results (->> (pull-many db default-read ids)
(group-by :db/id))
accounts (->> ids
- (map results)
- (map first)
- (map <-datomic))]
+ (map results)
+ (map first)
+ (map <-datomic))]
accounts))
(defn get-graphql [args]
diff --git a/src/clj/auto_ap/datomic/bank_accounts.clj b/src/clj/auto_ap/datomic/bank_accounts.clj
index bf23f336..aa9ca405 100644
--- a/src/clj/auto_ap/datomic/bank_accounts.clj
+++ b/src/clj/auto_ap/datomic/bank_accounts.clj
@@ -13,15 +13,12 @@
(def default-read '[* {:client/_bank-accounts [:db/id]}])
(defn <-datomic [x]
- (->> x
- (map #(update % :bank-account/type :db/ident))
- ))
-
-
+ (->> x
+ (map #(update % :bank-account/type :db/ident))))
(defn get-by-id [id]
- (->> [(dc/pull (dc/db conn ) default-read id)]
- (<-datomic)
+ (->> [(dc/pull (dc/db conn) default-read id)]
+ (<-datomic)
(first)))
diff --git a/src/clj/auto_ap/datomic/checks.clj b/src/clj/auto_ap/datomic/checks.clj
index 332c54f3..589ffb54 100644
--- a/src/clj/auto_ap/datomic/checks.clj
+++ b/src/clj/auto_ap/datomic/checks.clj
@@ -16,22 +16,22 @@
(defn <-datomic [result]
(-> result
- (update :payment/date c/from-date)
- (update :payment/status :db/ident)
- (update :payment/type :db/ident)
- (update :transaction/_payment (fn [transactions]
- (mapv (fn [transaction]
- (update transaction :transaction/date c/from-date))
- transactions)))
- (rename-keys {:invoice-payment/_payment :payment/invoices})))
+ (update :payment/date c/from-date)
+ (update :payment/status :db/ident)
+ (update :payment/type :db/ident)
+ (update :transaction/_payment (fn [transactions]
+ (mapv (fn [transaction]
+ (update transaction :transaction/date c/from-date))
+ transactions)))
+ (rename-keys {:invoice-payment/_payment :payment/invoices})))
(def default-read '[*
- {:invoice-payment/_payment [* {:invoice-payment/invoice [*]}]}
- {:payment/client [:client/name :db/id :client/code]}
- {:payment/bank-account [*]}
- {:payment/vendor [:vendor/name {:vendor/default-account
- [:account/name :account/numeric-code :db/id]} :db/id {:vendor/primary-contact [*]} {:vendor/address [*]}]}
- {:payment/status [:db/ident]}
+ {:invoice-payment/_payment [* {:invoice-payment/invoice [*]}]}
+ {:payment/client [:client/name :db/id :client/code]}
+ {:payment/bank-account [*]}
+ {:payment/vendor [:vendor/name {:vendor/default-account
+ [:account/name :account/numeric-code :db/id]} :db/id {:vendor/primary-contact [*]} {:vendor/address [*]}]}
+ {:payment/status [:db/ident]}
{:payment/type [:db/ident]}
{:transaction/_payment [:db/id :transaction/date]}])
@@ -60,7 +60,7 @@
(:sort args) (add-sorter-fields {"client" ['[?e :payment/client ?c]
'[?c :client/name ?sort-client]]
"vendor" ['[?e :payment/vendor ?v]
- '[?v :vendor/name ?sort-vendor]]
+ '[?v :vendor/name ?sort-vendor]]
"bank-account" ['[?e :payment/bank-account ?ba]
'[?ba :bank-account/name ?sort-bank-account]]
"check-number" ['[(get-else $ ?e :payment/check-number 0) ?sort-check-number]]
@@ -73,7 +73,7 @@
:where []}
:args [(:exact-match-id args)]})
- (:vendor-id args)
+ (:vendor-id args)
(merge-query {:query {:in ['?vendor-id]
:where ['[?e :payment/vendor ?vendor-id]]}
:args [(:vendor-id args)]})
@@ -100,13 +100,13 @@
:where ['[?e :payment/bank-account ?bank-account-id]]}
:args [(:bank-account-id args)]})
- (:amount-gte args)
+ (:amount-gte args)
(merge-query {:query {:in ['?amount-gte]
:where ['[?e :payment/amount ?a]
'[(>= ?a ?amount-gte)]]}
:args [(:amount-gte args)]})
- (:amount-lte args)
+ (:amount-lte args)
(merge-query {:query {:in ['?amount-lte]
:where ['[?e :payment/amount ?a]
'[(<= ?a ?amount-lte)]]}
@@ -118,7 +118,6 @@
'[(iol-ion.query/dollars= ?transaction-amount ?amount)]]}
:args [(:amount args)]})
-
(:status args)
(merge-query {:query {:in ['?status]
:where ['[?e :payment/status ?status]]}
@@ -137,7 +136,6 @@
true
(merge-query {:query {:find ['?sort-default '?e]}})))]
-
(cond->> (observable-query query)
true (apply-sort-3 (assoc args :default-asc? false))
true (apply-pagination args)))))
@@ -146,9 +144,9 @@
(let [results (->> (pull-many db default-read ids)
(group-by :db/id))
payments (->> ids
- (map results)
- (map first)
- (mapv <-datomic))]
+ (map results)
+ (map first)
+ (mapv <-datomic))]
payments))
(defn get-graphql [args]
@@ -169,7 +167,7 @@
[]))
(defn get-by-id [id]
- (->>
+ (->>
(dc/pull (dc/db conn) default-read id)
(<-datomic)))
diff --git a/src/clj/auto_ap/datomic/clients.clj b/src/clj/auto_ap/datomic/clients.clj
index 5e55b2f1..64515333 100644
--- a/src/clj/auto_ap/datomic/clients.clj
+++ b/src/clj/auto_ap/datomic/clients.clj
@@ -122,8 +122,6 @@
Long/parseLong
(#(hash-map :db/id %)))))
-
-
(defn exact-match [identifier]
(when (and identifier (not-empty identifier))
(some-> (solr/query solr/impl "clients"
@@ -170,7 +168,6 @@
matching-ids)
(set (map :db/id (:clients args))))
-
query (cond-> {:query {:find []
:in ['$]
:where []}
@@ -179,7 +176,6 @@
(merge-query {:query {:in ['[?e ...]]}
:args [(set valid-ids)]})
-
(:sort args) (add-sorter-fields {"name" ['[?e :client/name ?sort-name]]}
args)
@@ -195,7 +191,6 @@
(map cleanse))]
results))
-
(defn get-graphql-page [args]
(let [db (dc/db conn)
{ids-to-retrieve :ids matching-count :count} (raw-graphql-ids db args)]
diff --git a/src/clj/auto_ap/datomic/expected_deposit.clj b/src/clj/auto_ap/datomic/expected_deposit.clj
index 8e390c97..a06292b6 100644
--- a/src/clj/auto_ap/datomic/expected_deposit.clj
+++ b/src/clj/auto_ap/datomic/expected_deposit.clj
@@ -36,7 +36,7 @@
"date" ['[?e :expected-deposit/date ?sort-date]]
"total" ['[?e :expected-deposit/total ?sort-total]]
"fee" ['[?e :expected-deposit/fee ?sort-fee]]}
- args)
+ args)
true
(merge-query {:query {:in ['[?xx ...]]
@@ -58,13 +58,13 @@
'[?client-id :client/code ?client-code]]}
:args [(:client-code args)]})
- (:total-gte args)
+ (:total-gte args)
(merge-query {:query {:in ['?total-gte]
:where ['[?e :expected-deposit/total ?a]
'[(>= ?a ?total-gte)]]}
:args [(:total-gte args)]})
- (:total-lte args)
+ (:total-lte args)
(merge-query {:query {:in ['?total-lte]
:where ['[?e :expected-deposit/total ?a]
'[(<= ?a ?total-lte)]]}
@@ -76,7 +76,6 @@
'[(iol-ion.query/dollars= ?expected-deposit-total ?total)]]}
:args [(:total args)]})
-
(:start (:date-range args))
(merge-query {:query {:in '[?start-date]
:where ['[?e :expected-deposit/date ?date]
@@ -92,7 +91,7 @@
true
(merge-query {:query {:find ['?sort-default '?e]
:where ['[?e :expected-deposit/date ?sort-default]]}}))]
-
+
(cond->> (query2 query)
true (apply-sort-3 args)
true (apply-pagination args))))
@@ -101,26 +100,26 @@
(let [results (->> (pull-many db default-read ids)
(group-by :db/id))
payments (->> ids
- (map results)
- (map first)
- (mapv <-datomic)
- (map (fn get-totals [ed]
- (assoc ed :totals
- (->> (dc/q '[:find ?d4 (count ?c) (sum ?a)
- :in $ ?ed
- :where [?ed :expected-deposit/charges ?c]
- [?c :charge/total ?a]
- [?o :sales-order/charges ?c]
- [?o :sales-order/date ?d]
- [(clj-time.coerce/from-date ?d) ?d2]
- [(auto-ap.time/localize ?d2) ?d3]
- [(clj-time.coerce/to-local-date ?d3) ?d4]]
- (dc/db conn)
- (:db/id ed))
- (map (fn [[date count amount]]
- {:date (c/to-date-time date)
- :count count
- :amount amount})))))))]
+ (map results)
+ (map first)
+ (mapv <-datomic)
+ (map (fn get-totals [ed]
+ (assoc ed :totals
+ (->> (dc/q '[:find ?d4 (count ?c) (sum ?a)
+ :in $ ?ed
+ :where [?ed :expected-deposit/charges ?c]
+ [?c :charge/total ?a]
+ [?o :sales-order/charges ?c]
+ [?o :sales-order/date ?d]
+ [(clj-time.coerce/from-date ?d) ?d2]
+ [(auto-ap.time/localize ?d2) ?d3]
+ [(clj-time.coerce/to-local-date ?d3) ?d4]]
+ (dc/db conn)
+ (:db/id ed))
+ (map (fn [[date count amount]]
+ {:date (c/to-date-time date)
+ :count count
+ :amount amount})))))))]
payments))
(defn get-graphql [args]
diff --git a/src/clj/auto_ap/datomic/invoices.clj b/src/clj/auto_ap/datomic/invoices.clj
index f585123a..130bb337 100644
--- a/src/clj/auto_ap/datomic/invoices.clj
+++ b/src/clj/auto_ap/datomic/invoices.clj
@@ -34,16 +34,15 @@
(defn <-datomic [x]
(-> x
- (update :invoice/date coerce/from-date)
- (update :invoice/due coerce/from-date)
- (update :invoice/scheduled-payment coerce/from-date)
- (update :invoice/status :db/ident)
- (update :invoice/expense-accounts (fn [eas]
- (map
- #(update % :invoice-expense-account/account d-accounts/clientize (:db/id (:invoice/client x)))
- eas)))
- (rename-keys {:invoice-payment/_invoice :invoice/payments})))
-
+ (update :invoice/date coerce/from-date)
+ (update :invoice/due coerce/from-date)
+ (update :invoice/scheduled-payment coerce/from-date)
+ (update :invoice/status :db/ident)
+ (update :invoice/expense-accounts (fn [eas]
+ (map
+ #(update % :invoice-expense-account/account d-accounts/clientize (:db/id (:invoice/client x)))
+ eas)))
+ (rename-keys {:invoice-payment/_invoice :invoice/payments})))
(defn raw-graphql-ids
([args]
@@ -63,37 +62,29 @@
valid-clients]}
(cond-> {:query {:find []
:in '[$ [?clients ?start ?end]]
- :where '[
- [(iol-ion.query/scan-invoices $ ?clients ?start ?end) [[?e _ ?sort-default] ...]]
- ]}
+ :where '[[(iol-ion.query/scan-invoices $ ?clients ?start ?end) [[?e _ ?sort-default] ...]]]}
:args [db
[valid-clients
(some-> (:start (:date-range args)) coerce/to-date)
(some-> (:end (:date-range args)) coerce/to-date)]]}
-
(:client-id args)
(merge-query {:query {:in ['?client-id]
:where ['[?e :invoice/client ?client-id]]}
- :args [ (:client-id args)]})
+ :args [(:client-id args)]})
(:client-code args)
(merge-query {:query {:in ['?client-code]
:where ['[?e :invoice/client ?client-id]
'[?client-id :client/code ?client-code]]}
- :args [ (:client-code args)]})
+ :args [(:client-code args)]})
(:original-id args)
(merge-query {:query {:in ['?original-id]
- :where [
- '[?e :invoice/client ?c]
+ :where ['[?e :invoice/client ?c]
'[?c :client/original-id ?original-id]]}
- :args [ (cond-> (:original-id args)
- (string? (:original-id args)) Long/parseLong )]})
-
-
-
-
+ :args [(cond-> (:original-id args)
+ (string? (:original-id args)) Long/parseLong)]})
(:start (:due-range args)) (merge-query {:query {:in '[?start-due]
:where ['[?e :invoice/due ?due]
@@ -104,34 +95,33 @@
:where ['[?e :invoice/due ?due]
'[(<= ?due ?end-due)]]}
:args [(coerce/to-date (:end (:due-range args)))]})
-
(:import-status args)
(merge-query {:query {:in ['?import-status]
:where ['[?e :invoice/import-status ?import-status]]}
- :args [ (keyword "import-status" (:import-status args))]})
+ :args [(keyword "import-status" (:import-status args))]})
(:status args)
(merge-query {:query {:in ['?status]
:where ['[?e :invoice/status ?status]]}
- :args [ (:status args)]})
+ :args [(:status args)]})
(:vendor-id args)
(merge-query {:query {:in ['?vendor-id]
:where ['[?e :invoice/vendor ?vendor-id]]}
- :args [ (:vendor-id args)]})
+ :args [(:vendor-id args)]})
(:account-id args)
(merge-query {:query {:in ['?account-id]
:where ['[?e :invoice/expense-accounts ?iea ?]
'[?iea :invoice-expense-account/account ?account-id]]}
- :args [ (:account-id args)]})
+ :args [(:account-id args)]})
- (:amount-gte args)
+ (:amount-gte args)
(merge-query {:query {:in ['?amount-gte]
:where ['[?e :invoice/total ?total-filter]
'[(>= ?total-filter ?amount-gte)]]}
:args [(:amount-gte args)]})
- (:amount-lte args)
+ (:amount-lte args)
(merge-query {:query {:in ['?amount-lte]
:where ['[?e :invoice/total ?total-filter]
'[(<= ?total-filter ?amount-lte)]]}
@@ -151,7 +141,7 @@
(:unresolved args)
(merge-query {:query {:in []
:where ['(or-join [?e]
- (not [?e :invoice/expense-accounts ])
+ (not [?e :invoice/expense-accounts])
(and [?e :invoice/expense-accounts ?ea]
(not [?ea :invoice-expense-account/account])))]}
:args []})
@@ -165,7 +155,7 @@
(:sort args) (add-sorter-fields {"client" ['[?e :invoice/client ?c]
'[?c :client/name ?sort-client]]
"vendor" ['[?e :invoice/vendor ?v]
- '[?v :vendor/name ?sort-vendor]]
+ '[?v :vendor/name ?sort-vendor]]
"description-original" ['[?e :transaction/description-original ?sort-description-original]]
"location" ['[?e :invoice/expense-accounts ?iea]
'[?iea :invoice-expense-account/location ?sort-location]]
@@ -176,16 +166,15 @@
"outstanding-balance" ['[?e :invoice/outstanding-balance ?sort-outstanding-balance]]}
args)
true
- (merge-query {:query {:find ['?sort-default '?e ]}}) ))]
+ (merge-query {:query {:find ['?sort-default '?e]}})))]
(->> (observable-query query)
- (apply-sort-3 args)
- (apply-pagination args)))))
-
+ (apply-sort-3 args)
+ (apply-pagination args)))))
(defn graphql-results [ids db _]
(let [results (->> (pull-many db default-read ids)
(group-by :db/id))
-
+
invoices (->> ids
(map results)
(map first)
@@ -193,40 +182,38 @@
invoices))
(defn sum-outstanding [ids]
-
- (->>
- (dc/q {:find ['?id '?o]
- :in ['$ '[?id ...]]
- :where ['[?id :invoice/outstanding-balance ?o]]}
- (dc/db conn)
- ids)
+
+ (->>
+ (dc/q {:find ['?id '?o]
+ :in ['$ '[?id ...]]
+ :where ['[?id :invoice/outstanding-balance ?o]]}
+ (dc/db conn)
+ ids)
(map last)
(reduce
+
0.0)))
(defn sum-total-amount [ids]
-
- (->>
- (dc/q {:find ['?id '?o]
- :in ['$ '[?id ...]]
- :where ['[?id :invoice/total ?o]]
- }
- (dc/db conn)
- ids)
- (map last)
- (reduce
- +
- 0.0)))
+
+ (->>
+ (dc/q {:find ['?id '?o]
+ :in ['$ '[?id ...]]
+ :where ['[?id :invoice/total ?o]]}
+ (dc/db conn)
+ ids)
+ (map last)
+ (reduce
+ +
+ 0.0)))
(defn get-graphql [args]
-
+
(let [db (dc/db conn)
{ids-to-retrieve :ids matching-count :count} (raw-graphql-ids db args)
outstanding (sum-outstanding ids-to-retrieve)
total-amount (sum-total-amount ids-to-retrieve)]
-
[(->> (graphql-results ids-to-retrieve db args))
matching-count
outstanding
@@ -239,56 +226,51 @@
(defn get-multi [ids]
(map <-datomic
- (pull-many (dc/db conn) default-read ids )))
-
-
+ (pull-many (dc/db conn) default-read ids)))
(defn find-conflicting [{:keys [:invoice/invoice-number :invoice/vendor :invoice/client :db/id]}]
-
+
(->> (dc/q
- {:find [(list 'pull '?e default-read)]
- :in ['$ '?invoice-number '?vendor '?client '?invoice-id]
- :where '[[?e :invoice/invoice-number ?invoice-number]
- [?e :invoice/vendor ?vendor]
- [?e :invoice/client ?client]
- (not [?e :invoice/status :invoice-status/voided])
- [(not= ?e ?invoice-id)]]}
- (dc/db conn) invoice-number vendor client (or id 0))
+ {:find [(list 'pull '?e default-read)]
+ :in ['$ '?invoice-number '?vendor '?client '?invoice-id]
+ :where '[[?e :invoice/invoice-number ?invoice-number]
+ [?e :invoice/vendor ?vendor]
+ [?e :invoice/client ?client]
+ (not [?e :invoice/status :invoice-status/voided])
+ [(not= ?e ?invoice-id)]]}
+ (dc/db conn) invoice-number vendor client (or id 0))
(map first)
(map <-datomic)))
-
(defn get-existing-set []
(let [vendored-results (set (dc/q {:find ['?vendor '?client '?invoice-number]
:in ['$]
:where '[[?e :invoice/invoice-number ?invoice-number]
[?e :invoice/vendor ?vendor]
[?e :invoice/client ?client]
- (not [?e :invoice/status :invoice-status/voided])
- ]}
+ (not [?e :invoice/status :invoice-status/voided])]}
(dc/db conn)))
vendorless-results (->> (dc/q {:find ['?client '?invoice-number]
:in ['$]
:where '[[?e :invoice/invoice-number ?invoice-number]
(not [?e :invoice/vendor])
[?e :invoice/client ?client]
- (not [?e :invoice/status :invoice-status/voided])
- ]}
+ (not [?e :invoice/status :invoice-status/voided])]}
(dc/db conn))
(mapv (fn [[client invoice-number]]
- [nil client invoice-number]) )
+ [nil client invoice-number]))
set)]
(into vendored-results vendorless-results)))
-
+
(defn filter-ids [ids]
- (if ids
- (->>
- (dc/q {:find ['?e]
- :in ['$ '[?e ...]]
- :where ['[?e :invoice/date]]}
- (dc/db conn) ids)
- (map first)
- vec)
+ (if ids
+ (->>
+ (dc/q {:find ['?e]
+ :in ['$ '[?e ...]]
+ :where ['[?e :invoice/date]]}
+ (dc/db conn) ids)
+ (map first)
+ vec)
[]))
(defn code-invoice
@@ -317,7 +299,7 @@
client-id))))
[schedule-payment-dom] (map first (dc/q '[:find ?dom
:in $ ?v ?c
- :where [?v :vendor/schedule-payment-dom ?sp ]
+ :where [?v :vendor/schedule-payment-dom ?sp]
[?sp :vendor-schedule-payment-dom/client ?c]
[?sp :vendor-schedule-payment-dom/dom ?dom]]
db
diff --git a/src/clj/auto_ap/datomic/ledger.clj b/src/clj/auto_ap/datomic/ledger.clj
index cd30e80b..11bfeed5 100644
--- a/src/clj/auto_ap/datomic/ledger.clj
+++ b/src/clj/auto_ap/datomic/ledger.clj
@@ -34,8 +34,8 @@
(some-> (:start (:date-range args)) coerce/to-date)
(some-> (:end (:date-range args)) coerce/to-date)]]}
- (:only-external args)
- (merge-query {:query {:where ['(not [?e :journal-entry/original-entity ])]}})
+ (:only-external args)
+ (merge-query {:query {:where ['(not [?e :journal-entry/original-entity])]}})
(seq (:external-id-like args))
(merge-query {:query {:in ['?external-id-like]
@@ -48,12 +48,11 @@
:where ['[?e :journal-entry/source ?source]]}
:args [(:source args)]})
- (:vendor-id args)
+ (:vendor-id args)
(merge-query {:query {:in ['?vendor-id]
:where ['[?e :journal-entry/vendor ?vendor-id]]}
:args [(:vendor-id args)]})
-
(or (seq (:numeric-code args))
(:bank-account-id args)
(not-empty (:location args)))
@@ -70,36 +69,35 @@
:args [(vec (for [{:keys [from to]} (:numeric-code args)]
[(or from 0) (or to 99999)]))]})
-
- (:amount-gte args)
+ (:amount-gte args)
(merge-query {:query {:in ['?amount-gte]
:where ['[?e :journal-entry/amount ?a]
'[(>= ?a ?amount-gte)]]}
:args [(:amount-gte args)]})
- (:amount-lte args)
+ (:amount-lte args)
(merge-query {:query {:in ['?amount-lte]
:where ['[?e :journal-entry/amount ?a]
'[(<= ?a ?amount-lte)]]}
:args [(:amount-lte args)]})
- (:bank-account-id args)
+ (:bank-account-id args)
(merge-query {:query {:in ['?a]
:where ['[?li :journal-entry-line/account ?a]]}
:args [(:bank-account-id args)]})
- (:account-id args)
+ (:account-id args)
(merge-query {:query {:in ['?a2]
:where ['[?e :journal-entry/line-items ?li2]
'[?li2 :journal-entry-line/account ?a2]]}
:args [(:account-id args)]})
- (not-empty (:location args))
+ (not-empty (:location args))
(merge-query {:query {:in ['?location]
:where ['[?li :journal-entry-line/location ?location]]}
:args [(:location args)]})
- (not-empty (:locations args))
+ (not-empty (:locations args))
(merge-query {:query {:in ['[?location ...]]
:where ['[?li :journal-entry-line/location ?location]]}
:args [(:locations args)]})
@@ -118,7 +116,7 @@
(merge-query {:query {:find ['?sort-default '?e]}})))]
(->> (observable-query query)
(apply-sort-4 (assoc args :default-asc? true))
- (apply-pagination args))))
+ (apply-pagination args))))
(defn graphql-results [ids db _]
(let [results (->> (pull-many db '[* {:journal-entry/client [:client/name :client/code :db/id]
@@ -134,15 +132,15 @@
(update je :journal-entry/line-items
(fn [jels]
(map
- #(update % :journal-entry-line/account d-accounts/clientize (:db/id (:journal-entry/client je)))
- jels)))))
+ #(update % :journal-entry-line/account d-accounts/clientize (:db/id (:journal-entry/client je)))
+ jels)))))
(filter (fn [je]
(every?
- (fn [jel]
- (let [include-in-reports (-> jel :journal-entry-line/account :bank-account/include-in-reports)]
- (or (nil? include-in-reports)
- (true? include-in-reports))))
- (:journal-entry/line-items je))))
+ (fn [jel]
+ (let [include-in-reports (-> jel :journal-entry-line/account :bank-account/include-in-reports)]
+ (or (nil? include-in-reports)
+ (true? include-in-reports))))
+ (:journal-entry/line-items je))))
(group-by :db/id))]
(->> ids
(map results)
@@ -156,7 +154,7 @@
matching-count]))
(defn filter-ids [ids]
- (if ids
+ (if ids
(->> (dc/q {:find ['?e]
:in ['$ '[?e ...]]
:where ['[?e :journal-entry/date]]}
diff --git a/src/clj/auto_ap/datomic/sales_orders.clj b/src/clj/auto_ap/datomic/sales_orders.clj
index 09f9c409..ba3528f6 100644
--- a/src/clj/auto_ap/datomic/sales_orders.clj
+++ b/src/clj/auto_ap/datomic/sales_orders.clj
@@ -23,9 +23,9 @@
(update :sales-order/charges (fn [cs]
(map (fn [c]
(-> c
- (update :charge/processor :db/ident)
- (set/rename-keys {:expected-deposit/_charges :expected-deposit})
- (update :expected-deposit first)))
+ (update :charge/processor :db/ident)
+ (set/rename-keys {:expected-deposit/_charges :expected-deposit})
+ (update :expected-deposit first)))
cs)))))
(def default-read '[:db/id
@@ -43,8 +43,7 @@
:sales-order/source,
:sales-order/reference-link,
{:sales-order/client [:client/name :db/id :client/code]
- :sales-order/charges [
- :charge/type-name,
+ :sales-order/charges [:charge/type-name,
:charge/total,
:charge/tax,
:charge/tip,
@@ -63,7 +62,6 @@
(set/intersection #{(:client-id args)}
visible-clients)
-
(:client-code args)
(set/intersection #{(pull-id db [:client/code (:client-code args)])}
visible-clients)
@@ -79,7 +77,7 @@
:where '[[(iol-ion.query/scan-sales-orders $ ?clients ?start-date ?end-date) [[?e _ ?sort-default] ...]]]}
:args [db [selected-clients
(some-> (:start (:date-range args)) c/to-date)
- (some-> (:end (:date-range args)) c/to-date )]]}
+ (some-> (:end (:date-range args)) c/to-date)]]}
(:sort args) (add-sorter-fields-2 {"client" ['[?e :sales-order/client ?c]
'[?c :client/name ?sort-client]]
@@ -108,13 +106,13 @@
'[?chg :charge/type-name ?type-name]]}
:args [(:type-name args)]})
- (:total-gte args)
+ (:total-gte args)
(merge-query {:query {:in ['?total-gte]
:where ['[?e :sales-order/total ?a]
'[(>= ?a ?total-gte)]]}
:args [(:total-gte args)]})
- (:total-lte args)
+ (:total-lte args)
(merge-query {:query {:in ['?total-lte]
:where ['[?e :sales-order/total ?a]
'[(<= ?a ?total-lte)]]}
@@ -136,7 +134,7 @@
(defn graphql-results [ids db _]
(let [results (->> (pull-many db default-read ids)
- (group-by :db/id))
+ (group-by :db/id))
payments (->> ids
(map results)
(map first)
@@ -146,14 +144,14 @@
(defn summarize-orders [ids]
(let [[total tax] (->>
- (dc/q {:find ['(sum ?t) '(sum ?tax)]
- :with ['?id]
- :in ['$ '[?id ...]]
- :where ['[?id :sales-order/total ?t]
- '[?id :sales-order/tax ?tax]]}
- (dc/db conn)
- ids)
- first)]
+ (dc/q {:find ['(sum ?t) '(sum ?tax)]
+ :with ['?id]
+ :in ['$ '[?id ...]]
+ :where ['[?id :sales-order/total ?t]
+ '[?id :sales-order/tax ?tax]]}
+ (dc/db conn)
+ ids)
+ first)]
{:total total
:tax tax}))
diff --git a/src/clj/auto_ap/datomic/transaction_rules.clj b/src/clj/auto_ap/datomic/transaction_rules.clj
index 959ee04a..f341a6f8 100644
--- a/src/clj/auto_ap/datomic/transaction_rules.clj
+++ b/src/clj/auto_ap/datomic/transaction_rules.clj
@@ -49,7 +49,7 @@
"note" ['[?e :transaction-rule/note ?sort-note]]
"amount-lte" ['[?e :transaction-rule/amount-lte ?sort-amount-lte]]
"amount-gte" ['[?e :transaction-rule/amount-gte ?sort-amount-gte]]}
- args)
+ args)
(seq (:clients args))
(merge-query {:query {:in ['[?xx ...]]
@@ -78,7 +78,6 @@
(merge-query {:query {:find ['?e]
:where ['[?e :transaction-rule/transaction-approval-status]]}}))]
-
(cond->> (query2 query)
true (apply-sort-3 args)
true (apply-pagination args))))
@@ -99,13 +98,13 @@
matching-count]))
(defn get-by-id [id]
- (->>
+ (->>
(dc/pull (dc/db conn) default-read id)
(<-datomic)))
(defn get-all []
(mapv first
- (dc/q {:find [(list 'pull '?e default-read )]
+ (dc/q {:find [(list 'pull '?e default-read)]
:in ['$]
:where ['[?e :transaction-rule/transaction-approval-status]]}
(dc/db conn))))
diff --git a/src/clj/auto_ap/datomic/transactions.clj b/src/clj/auto_ap/datomic/transactions.clj
index 90207718..4a472631 100644
--- a/src/clj/auto_ap/datomic/transactions.clj
+++ b/src/clj/auto_ap/datomic/transactions.clj
@@ -37,7 +37,6 @@
(map first)
set)))
-
(defn raw-graphql-ids
([args] (raw-graphql-ids (dc/db conn) args))
([db args]
@@ -87,7 +86,6 @@
:where ['[?e :transaction/vendor ?vendor-id]]}
:args [(:vendor-id args)]})
-
(:amount-gte args)
(merge-query {:query {:in ['?amount-gte]
:where ['[?e :transaction/amount ?a]
diff --git a/src/clj/auto_ap/datomic/users.clj b/src/clj/auto_ap/datomic/users.clj
index bac3ef54..a2f2f70e 100644
--- a/src/clj/auto_ap/datomic/users.clj
+++ b/src/clj/auto_ap/datomic/users.clj
@@ -4,15 +4,15 @@
[datomic.api :as dc]
[datomic.api :as d]))
-(defn find-or-insert! [{:keys [:user/provider :user/provider-id ] :as new-user}]
+(defn find-or-insert! [{:keys [:user/provider :user/provider-id] :as new-user}]
(let [is-first-user? (not (seq (dc/q [:find '?e
:in '$
:where '[?e :user/provider]]
(dc/db conn))))
user-id (ffirst (dc/q '[:find ?e
- :in $ ?provider ?provider-id
- :where [?e :user/provider ?provider]
- [?e :user/provider-id ?provider-id]]
+ :in $ ?provider ?provider-id
+ :where [?e :user/provider ?provider]
+ [?e :user/provider-id ?provider-id]]
(dc/db conn) provider provider-id))
result @(dc/transact conn [[:upsert-entity (cond-> (assoc new-user :db/id (or user-id "user")
:user/last-login (java.util.Date.))
diff --git a/src/clj/auto_ap/datomic/vendors.clj b/src/clj/auto_ap/datomic/vendors.clj
index c583351c..b02b5da9 100644
--- a/src/clj/auto_ap/datomic/vendors.clj
+++ b/src/clj/auto_ap/datomic/vendors.clj
@@ -18,29 +18,29 @@
(:vendor/legal-entity-tin-type a) (update :vendor/legal-entity-tin-type :db/ident)
(:vendor/legal-entity-1099-type a) (update :vendor/legal-entity-1099-type :db/ident)
true (assoc :usage (:vendor-usage/_vendor a))
- true (dissoc :vendor-usage/_vendor )))
+ true (dissoc :vendor-usage/_vendor)))
(defn cleanse [id vendor]
(let [clients (if-let [clients (limited-clients id)]
(set (map :db/id clients))
nil)]
(if clients
- (-> vendor
+ (-> vendor
(update :vendor/account-overrides (fn [aos]
(->> aos
(filter #(clients (:db/id (:vendor-account-override/client %))))
(map #(update % :vendor-account-override/account d-accounts/clientize (:db/id (:vendor-account-override/client %)))))))
(update :vendor/terms-overrides (fn [to] (filter #(clients (:db/id (:vendor-terms-override/client %))) to)))
(update :vendor/schedule-payment-dom (fn [to] (filter #(clients (:db/id (:vendor-schedule-payment-dom/client %))) to))))
- (-> vendor
+ (-> vendor
(update :vendor/account-overrides (fn [aos]
(->> aos
(map #(update % :vendor-account-override/account d-accounts/clientize (:db/id (:vendor-account-override/client %)))))))))))
(def default-read
'[* {:vendor/account-overrides [* {:vendor-account-override/client [:client/name :db/id]
- :vendor-account-override/account [:account/name :account/numeric-code :db/id
- {:account/client-overrides [:account-client-override/client :account-client-override/name]}]}]
+ :vendor-account-override/account [:account/name :account/numeric-code :db/id
+ {:account/client-overrides [:account-client-override/client :account-client-override/name]}]}]
:vendor/terms-overrides [* {:vendor-terms-override/client [:client/name :client/code :db/id]}]
:vendor/schedule-payment-dom [* {:vendor-schedule-payment-dom/client [:client/name :client/code :db/id]}]
:vendor/automatically-paid-when-due [:db/id :client/name]
@@ -50,14 +50,13 @@
:vendor/plaid-merchant [:db/id :plaid-merchant/name]
:vendor-usage/_vendor [:vendor-usage/client :vendor-usage/count]}])
-
(defn raw-graphql-ids [db args]
(let [query (cond-> {:query {:find []
:in ['$]
:where []}
:args [db]}
(:sort args) (add-sorter-fields {"name" ['[?e :vendor/name ?sort-name]]}
- args)
+ args)
(not (str/blank? (:name-like args)))
(merge-query {:query {:in ['?name-like]
@@ -70,25 +69,22 @@
(merge-query {:query {:find ['?e]
:where ['[?e :vendor/name]]}}))]
-
(cond->> (query2 query)
true (apply-sort-3 args)
true (apply-pagination args))))
(defn trim-usage [v limited-clients]
(->> (if limited-clients
- (update v :usage (fn [usages]
- (->> usages
- (filter (comp (set (map :db/id limited-clients)) :db/id :vendor-usage/client))
- (map (fn [u] {:client-id (:db/id (:vendor-usage/client u))
- :count (:vendor-usage/count u)})))))
+ (update v :usage (fn [usages]
+ (->> usages
+ (filter (comp (set (map :db/id limited-clients)) :db/id :vendor-usage/client))
+ (map (fn [u] {:client-id (:db/id (:vendor-usage/client u))
+ :count (:vendor-usage/count u)})))))
- (update v :usage (fn [usages]
- (->> usages
- (map (fn [u] {:client-id (:db/id (:vendor-usage/client u))
- :count (:vendor-usage/count u)}))))))
-
- ))
+ (update v :usage (fn [usages]
+ (->> usages
+ (map (fn [u] {:client-id (:db/id (:vendor-usage/client u))
+ :count (:vendor-usage/count u)}))))))))
(defn graphql-results [ids db args]
(let [results (->> (pull-many db default-read ids)
@@ -104,9 +100,7 @@
(let [db (dc/db conn)
{ids-to-retrieve :ids matching-count :count} (raw-graphql-ids db args)]
[(->> (graphql-results ids-to-retrieve db args))
- matching-count])
-
- )
+ matching-count]))
(defn get-graphql-by-id [args id]
(->> (dc/q {:find [(list 'pull '?e default-read)]
@@ -120,29 +114,28 @@
first))
(defn get-by-id [id]
-
+
(->> (dc/q '[:find (pull ?e [*
- {:vendor/default-account [:account/name :db/id :account/location]
- :vendor/legal-entity-tin-type [:db/ident :db/id]
- :vendor/legal-entity-1099-type [:db/ident :db/id]
- :vendor/plaid-merchant [:db/id :plaid-merchant/name]
- :vendor/account-overrides [* {:vendor-account-override/client [:client/name :db/id]
- :vendor-account-override/account [:account/name :account/numeric-code :db/id]}]
- :vendor/terms-overrides [* {:vendor-terms-override/client [:client/name :db/id]}]
- :vendor/schedule-payment-dom [* {:vendor-schedule-payment-dom/client [:client/name :db/id]}]
- :vendor/automatically-paid-when-due [:db/id :client/name]}])
- :in $ ?e
- :where [?e]]
- (dc/db conn)
- id)
+ {:vendor/default-account [:account/name :db/id :account/location]
+ :vendor/legal-entity-tin-type [:db/ident :db/id]
+ :vendor/legal-entity-1099-type [:db/ident :db/id]
+ :vendor/plaid-merchant [:db/id :plaid-merchant/name]
+ :vendor/account-overrides [* {:vendor-account-override/client [:client/name :db/id]
+ :vendor-account-override/account [:account/name :account/numeric-code :db/id]}]
+ :vendor/terms-overrides [* {:vendor-terms-override/client [:client/name :db/id]}]
+ :vendor/schedule-payment-dom [* {:vendor-schedule-payment-dom/client [:client/name :db/id]}]
+ :vendor/automatically-paid-when-due [:db/id :client/name]}])
+ :in $ ?e
+ :where [?e]]
+ (dc/db conn)
+ id)
(map first)
(map <-datomic)
(first)))
-
(defn terms-for-client-id [vendor client-id]
(or
- (->>
+ (->>
(filter
(fn [to]
(= (:db/id (:vendor-terms-override/client to))
@@ -153,8 +146,8 @@
(:vendor/terms vendor)))
(defn account-for-client-id [vendor client-id]
- (or
- (->>
+ (or
+ (->>
(filter
(fn [to]
(= (:db/id (:vendor-account-override/client to))
@@ -165,7 +158,7 @@
(:vendor/default-account vendor)))
(defn automatically-paid-for-client-id? [vendor client-id]
- (->>
+ (->>
(:vendor/automatically-paid-when-due vendor)
(filter
(fn [client]
diff --git a/src/clj/auto_ap/datomic/yodlee_merchants.clj b/src/clj/auto_ap/datomic/yodlee_merchants.clj
index da234046..7705dc04 100644
--- a/src/clj/auto_ap/datomic/yodlee_merchants.clj
+++ b/src/clj/auto_ap/datomic/yodlee_merchants.clj
@@ -6,8 +6,8 @@
(defn get-merchants [_]
;; TODO admin?
(->>
- (dc/q {:find ['(pull ?e [:yodlee-merchant/name :yodlee-merchant/yodlee-id :db/id])]
- :in ['$]
- :where [['?e :yodlee-merchant/name]]}
- (dc/db conn))
- (mapv first)))
+ (dc/q {:find ['(pull ?e [:yodlee-merchant/name :yodlee-merchant/yodlee-id :db/id])]
+ :in ['$]
+ :where [['?e :yodlee-merchant/name]]}
+ (dc/db conn))
+ (mapv first)))
diff --git a/src/clj/auto_ap/ezcater/core.clj b/src/clj/auto_ap/ezcater/core.clj
index 900a214a..30f603e0 100644
--- a/src/clj/auto_ap/ezcater/core.clj
+++ b/src/clj/auto_ap/ezcater/core.clj
@@ -1,6 +1,6 @@
(ns auto-ap.ezcater.core
(:require
- [auto-ap.datomic :refer [conn random-tempid]]
+ [auto-ap.datomic :refer [conn random-tempid]]
[datomic.api :as dc]
[clj-http.client :as client]
[venia.core :as v]
@@ -20,42 +20,41 @@
:body (json/write-str {"query" (v/graphql-query q)})
:as :json})
:body
- :data
- ))
+ :data))
(defn get-caterers [integration]
(:caterers (query integration {:venia/queries [{:query/data
- [:caterers [:name :uuid [:address [:name :street]]]]}]} )))
+ [:caterers [:name :uuid [:address [:name :street]]]]}]})))
(defn get-subscriptions [integration]
(->> (query integration {:venia/queries [{:query/data
- [:subscribers [:id [:subscriptions [:parentId :parentEntity :eventEntity :eventKey]] ]]}]} )
+ [:subscribers [:id [:subscriptions [:parentId :parentEntity :eventEntity :eventKey]]]]}]})
:subscribers
first
:subscriptions))
(defn get-integrations []
(map first (dc/q '[:find (pull ?i [:ezcater-integration/api-key
- :ezcater-integration/subscriber-uuid
- :db/id
- :ezcater-integration/integration-status [:db/id]])
- :in $
- :where [?i :ezcater-integration/api-key]]
- (dc/db conn))))
+ :ezcater-integration/subscriber-uuid
+ :db/id
+ :ezcater-integration/integration-status [:db/id]])
+ :in $
+ :where [?i :ezcater-integration/api-key]]
+ (dc/db conn))))
(defn mark-integration-status [integration integration-status]
@(dc/transact conn
- [{:db/id (:db/id integration)
- :ezcater-integration/integration-status (assoc integration-status
- :db/id (or (-> integration :ezcater-integration/integration-status :db/id)
- (random-tempid)))}]))
+ [{:db/id (:db/id integration)
+ :ezcater-integration/integration-status (assoc integration-status
+ :db/id (or (-> integration :ezcater-integration/integration-status :db/id)
+ (random-tempid)))}]))
(defn upsert-caterers
([integration]
@(dc/transact
conn
(for [caterer (get-caterers integration)]
- {:db/id (:db/id integration)
+ {:db/id (:db/id integration)
:ezcater-integration/caterers [{:ezcater-caterer/name (str (:name caterer) " (" (:street (:address caterer)) ")")
:ezcater-caterer/search-terms (str (:name caterer) " " (:street (:address caterer)))
:ezcater-caterer/uuid (:uuid caterer)}]}))))
@@ -64,14 +63,14 @@
([integration]
(let [extant (get-subscriptions integration)
to-ensure (set (map first (dc/q '[:find ?cu
- :in $
- :where [_ :client/ezcater-locations ?el]
- [?el :ezcater-location/caterer ?c]
- [?c :ezcater-caterer/uuid ?cu]]
- (dc/db conn))))
+ :in $
+ :where [_ :client/ezcater-locations ?el]
+ [?el :ezcater-location/caterer ?c]
+ [?c :ezcater-caterer/uuid ?cu]]
+ (dc/db conn))))
to-create (set/difference
- to-ensure
- (set (map :parentId extant)))]
+ to-ensure
+ (set (map :parentId extant)))]
(doseq [parentId to-create]
(query integration
{:venia/operation {:operation/type :mutation
@@ -94,7 +93,6 @@
:eventKey 'cancelled}}
[[:subscription [:parentId :parentEntity :eventEntity :eventKey]]]]]})))))
-
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
(defn upsert-ezcater
([] (upsert-ezcater (get-integrations)))
@@ -115,12 +113,11 @@
(defn get-caterer [caterer-uuid]
(dc/pull (dc/db conn)
- '[:ezcater-caterer/name
- {:ezcater-integration/_caterers [:ezcater-integration/api-key]}
- {:ezcater-location/_caterer [:ezcater-location/location
- {:client/_ezcater-locations [:client/code]}]}]
- [:ezcater-caterer/uuid caterer-uuid]))
-
+ '[:ezcater-caterer/name
+ {:ezcater-integration/_caterers [:ezcater-integration/api-key]}
+ {:ezcater-location/_caterer [:ezcater-location/location
+ {:client/_ezcater-locations [:client/code]}]}]
+ [:ezcater-caterer/uuid caterer-uuid]))
(defn round-carry-cents [f]
(with-precision 2 (double (.setScale (bigdec f) 2 java.math.RoundingMode/HALF_UP))))
@@ -135,126 +132,118 @@
0.15M
:else
0.07M)]
- (round-carry-cents
- (* commision%
- 0.01M
- (+
- (-> order :totals :subTotal :subunits )
- (reduce +
- 0
- (map (comp :subunits :cost) (:feesAndDiscounts (:catererCart order)))))))))
-
-(defn ccp-fee [order]
- (round-carry-cents
- (* 0.000299M
- (+
- (-> order :totals :subTotal :subunits )
- (-> order :totals :salesTax :subunits )
+ (round-carry-cents
+ (* commision%
+ 0.01M
+ (+
+ (-> order :totals :subTotal :subunits)
(reduce +
0
- (map (comp :subunits :cost) (:feesAndDiscounts (:catererCart order))))))))
+ (map (comp :subunits :cost) (:feesAndDiscounts (:catererCart order)))))))))
+
+(defn ccp-fee [order]
+ (round-carry-cents
+ (* 0.000299M
+ (+
+ (-> order :totals :subTotal :subunits)
+ (-> order :totals :salesTax :subunits)
+ (reduce +
+ 0
+ (map (comp :subunits :cost) (:feesAndDiscounts (:catererCart order))))))))
(defn order->sales-order [{{:keys [timestamp]} :event {:keys [orderItems]} :catererCart :keys [client-code client-location uuid] :as order}]
(let [adjustment (round-carry-cents (- (+ (-> order :totals :subTotal :subunits (* 0.01))
(-> order :totals :salesTax :subunits (* 0.01)))
- (-> order :catererCart :totals :catererTotalDue )
+ (-> order :catererCart :totals :catererTotalDue)
(commision order)
(ccp-fee order)))
service-charge (+ (commision order) (ccp-fee order))
tax (-> order :totals :salesTax :subunits (* 0.01))
tip (-> order :totals :tip :subunits (* 0.01))]
#:sales-order
- {:date (atime/localize (coerce/to-date-time timestamp))
- :external-id (str "ezcater/order/" client-code "-" client-location "-" uuid)
- :client [:client/code client-code]
- :location client-location
- :reference-link (str (url/url "https://ezmanage.ezcater.com/orders/" uuid ))
- :line-items [#:order-line-item
- {:external-id (str "ezcater/order/" client-code "-" client-location "-" uuid "-" 0)
- :item-name "EZCater Catering"
- :category "EZCater Catering"
- :discount adjustment
- :tax tax
- :total (+ (-> order :totals :subTotal :subunits (* 0.01))
- tax
- tip)}]
- :charges [#:charge
- {:type-name "CARD"
- :date (atime/localize (coerce/to-date-time timestamp))
- :client [:client/code client-code]
- :location client-location
- :external-id (str "ezcater/charge/" uuid)
- :processor :ccp-processor/ezcater
- :total (+ (-> order :totals :subTotal :subunits (* 0.01))
- tax
- tip)
- :tip tip}]
-
- :total (+ (-> order :totals :subTotal :subunits (* 0.01))
- tax
- tip)
- :discount adjustment
- :service-charge service-charge
- :tax tax
- :tip tip
- :returns 0.0
- :vendor :vendor/ccp-ezcater}))
+ {:date (atime/localize (coerce/to-date-time timestamp))
+ :external-id (str "ezcater/order/" client-code "-" client-location "-" uuid)
+ :client [:client/code client-code]
+ :location client-location
+ :reference-link (str (url/url "https://ezmanage.ezcater.com/orders/" uuid))
+ :line-items [#:order-line-item
+ {:external-id (str "ezcater/order/" client-code "-" client-location "-" uuid "-" 0)
+ :item-name "EZCater Catering"
+ :category "EZCater Catering"
+ :discount adjustment
+ :tax tax
+ :total (+ (-> order :totals :subTotal :subunits (* 0.01))
+ tax
+ tip)}]
+ :charges [#:charge
+ {:type-name "CARD"
+ :date (atime/localize (coerce/to-date-time timestamp))
+ :client [:client/code client-code]
+ :location client-location
+ :external-id (str "ezcater/charge/" uuid)
+ :processor :ccp-processor/ezcater
+ :total (+ (-> order :totals :subTotal :subunits (* 0.01))
+ tax
+ tip)
+ :tip tip}]
+ :total (+ (-> order :totals :subTotal :subunits (* 0.01))
+ tax
+ tip)
+ :discount adjustment
+ :service-charge service-charge
+ :tax tax
+ :tip tip
+ :returns 0.0
+ :vendor :vendor/ccp-ezcater}))
(defn get-by-id [integration id]
- (query
- integration
- {:venia/queries [[:order {:id id}
- [:uuid
- :orderNumber
- :orderSourceType
- [:caterer
- [:name
- :uuid
- [:address [:street]]]]
- [:event
- [:timestamp
- :catererHandoffFoodTime
- :orderType]]
- [:catererCart [[:orderItems
- [:name
- :quantity
- :posItemId
- [:totalInSubunits
- [:currency
- :subunits]]]]
- [:totals
- [:catererTotalDue]]
- [:feesAndDiscounts
- {:type 'DELIVERY_FEE}
- [[:cost
- [:currency
- :subunits]]]]]]
- [:totals [[:customerTotalDue
- [
- :currency
- :subunits
- ]]
- [:pointOfSaleIntegrationFee
- [
- :currency
- :subunits
- ]]
- [:tip
- [:currency
- :subunits]]
- [:salesTax
- [
- :currency
- :subunits
- ]]
- [:salesTaxRemittance
- [:currency
- :subunits
- ]]
- [:subTotal
- [:currency
- :subunits]]]]]]]}))
+ (query
+ integration
+ {:venia/queries [[:order {:id id}
+ [:uuid
+ :orderNumber
+ :orderSourceType
+ [:caterer
+ [:name
+ :uuid
+ [:address [:street]]]]
+ [:event
+ [:timestamp
+ :catererHandoffFoodTime
+ :orderType]]
+ [:catererCart [[:orderItems
+ [:name
+ :quantity
+ :posItemId
+ [:totalInSubunits
+ [:currency
+ :subunits]]]]
+ [:totals
+ [:catererTotalDue]]
+ [:feesAndDiscounts
+ {:type 'DELIVERY_FEE}
+ [[:cost
+ [:currency
+ :subunits]]]]]]
+ [:totals [[:customerTotalDue
+ [:currency
+ :subunits]]
+ [:pointOfSaleIntegrationFee
+ [:currency
+ :subunits]]
+ [:tip
+ [:currency
+ :subunits]]
+ [:salesTax
+ [:currency
+ :subunits]]
+ [:salesTaxRemittance
+ [:currency
+ :subunits]]
+ [:subTotal
+ [:currency
+ :subunits]]]]]]]}))
(defn lookup-order [json]
(let [caterer (get-caterer (get json "parent_id"))
@@ -262,25 +251,25 @@
client (-> caterer :ezcater-location/_caterer first :client/_ezcater-locations :client/code)
location (-> caterer :ezcater-location/_caterer first :ezcater-location/location)]
(if (and client location)
- (doto
- (-> (get-by-id integration (get json "entity_id"))
- (:order)
- (assoc :client-code client
- :client-location location))
+ (doto
+ (-> (get-by-id integration (get json "entity_id"))
+ (:order)
+ (assoc :client-code client
+ :client-location location))
(#(alog/info ::order-details :detail %)))
(alog/warn ::caterer-no-longer-has-location :json json))))
(defn import-order [json]
;; {"id" "bf3dcf5c-a68f-42d9-9084-049133e03d3d", "parent_type" "Caterer", "parent_id" "91541331-d7ae-4634-9e8b-ccbbcfb2ce70", "entity_type" "Order", "entity_id" "9ab05fee-a9c5-483b-a7f2-14debde4b7a8", "key" "accepted", "occurred_at" "2022-07-21T19:21:07.549Z"}
(alog/info
- ::try-import-order
- :json json)
+ ::try-import-order
+ :json json)
@(dc/transact conn (filter identity
- [(some-> json
- (lookup-order)
- (order->sales-order)
- (update :sales-order/date coerce/to-date)
- (update-in [:sales-order/charges 0 :charge/date] coerce/to-date))])))
+ [(some-> json
+ (lookup-order)
+ (order->sales-order)
+ (update :sales-order/date coerce/to-date)
+ (update-in [:sales-order/charges 0 :charge/date] coerce/to-date))])))
(defn upsert-recent []
(upsert-ezcater)
@@ -289,17 +278,17 @@
(filter #(= 7 (time/day-of-week %)))))
(time/days 1)))
orders-to-update (doall (for [[order uuid] (dc/q '[:find ?eid ?uuid
- :in $ ?start
- :where [?e :sales-order/vendor :vendor/ccp-ezcater]
- [?e :sales-order/date ?d]
- [(>= ?d ?start)]
- [?e :sales-order/external-id ?eid]
- [?e :sales-order/client ?c]
- [?c :client/ezcater-locations ?l]
- [?l :ezcater-location/caterer ?c2]
- [?c2 :ezcater-caterer/uuid ?uuid]]
- (dc/db conn)
- last-sunday)
+ :in $ ?start
+ :where [?e :sales-order/vendor :vendor/ccp-ezcater]
+ [?e :sales-order/date ?d]
+ [(>= ?d ?start)]
+ [?e :sales-order/external-id ?eid]
+ [?e :sales-order/client ?c]
+ [?c :client/ezcater-locations ?l]
+ [?l :ezcater-location/caterer ?c2]
+ [?c2 :ezcater-caterer/uuid ?uuid]]
+ (dc/db conn)
+ last-sunday)
:let [_ (alog/info ::considering
:order order)
id (last (str/split order #"/"))
@@ -313,29 +302,29 @@
"occurred_at" "2022-07-21T19:21:07.549Z"}
ezcater-order (lookup-order lookup-map)
extant-order (dc/pull (dc/db conn) '[:sales-order/total
- :sales-order/tax
- :sales-order/tip
- :sales-order/discount
- :sales-order/external-id
- {:sales-order/charges [:charge/tax
- :charge/tip
- :charge/total
- :charge/external-id]
- :sales-order/line-items [:order-line-item/external-id
- :order-line-item/total
- :order-line-item/tax
- :order-line-item/discount]}]
- [:sales-order/external-id order])
+ :sales-order/tax
+ :sales-order/tip
+ :sales-order/discount
+ :sales-order/external-id
+ {:sales-order/charges [:charge/tax
+ :charge/tip
+ :charge/total
+ :charge/external-id]
+ :sales-order/line-items [:order-line-item/external-id
+ :order-line-item/total
+ :order-line-item/tax
+ :order-line-item/discount]}]
+ [:sales-order/external-id order])
updated-order (-> (order->sales-order ezcater-order)
(select-keys
- #{:sales-order/total
- :sales-order/tax
- :sales-order/tip
- :sales-order/discount
- :sales-order/charges
- :sales-order/external-id
- :sales-order/line-items})
+ #{:sales-order/total
+ :sales-order/tax
+ :sales-order/tip
+ :sales-order/discount
+ :sales-order/charges
+ :sales-order/external-id
+ :sales-order/line-items})
(update :sales-order/line-items
(fn [c]
(map #(select-keys % #{:order-line-item/external-id
diff --git a/src/clj/auto_ap/graphql.clj b/src/clj/auto_ap/graphql.clj
index 3174155f..abbfcb22 100644
--- a/src/clj/auto_ap/graphql.clj
+++ b/src/clj/auto_ap/graphql.clj
@@ -34,15 +34,14 @@
(clojure.lang IPersistentMap)))
(def integreat-schema
- {
- :scalars {:id {:parse #(cond (number? %)
+ {:scalars {:id {:parse #(cond (number? %)
%
%
(Long/parseLong %))
-
+
:serialize #(.toString %)}
- :ident {:parse (fn [x] {:db/ident x})
+ :ident {:parse (fn [x] {:db/ident x})
:serialize #(or (:ident %) (:db/ident %) %)}
:iso_date {:parse #(time/parse % time/iso-date)
:serialize #(time/unparse % time/iso-date)}
@@ -65,29 +64,28 @@
:else
%)
:serialize #(cond (double? %)
- (str %)
-
- (int? %)
- (str %)
-
- :else
- %)}
-
- :percentage {:parse #(cond (and (string? %)
- (not (str/blank? %)))
- (Double/parseDouble %)
+ (str %)
(int? %)
- (double %)
+ (str %)
:else
- %)
+ %)}
+
+ :percentage {:parse #(cond (and (string? %)
+ (not (str/blank? %)))
+ (Double/parseDouble %)
+
+ (int? %)
+ (double %)
+
+ :else
+ %)
:serialize #(if (double? %)
(str %)
%)}}
:objects
- {
- :message
+ {:message
{:fields {:message {:type 'String}}}
:search_result
@@ -128,8 +126,7 @@
:email {:type 'String}
:phone {:type 'String}}}
-
- :address
+ :address
{:fields {:id {:type :id}
:street1 {:type 'String}
:street2 {:type 'String}
@@ -184,7 +181,6 @@
:legal_entity_tin_type {:type :tin_type}
:legal_entity_1099_type {:type :type_1099}}}
-
:reminder
{:fields {:id {:type 'Int}
:email {:type 'String}
@@ -193,13 +189,13 @@
:scheduled {:type 'String}
:sent {:type 'String}
:vendor {:type :vendor}}}
-
+
:yodlee_merchant {:fields {:id {:type :id}
:yodlee_id {:type 'String}
:name {:type 'String}}}
:plaid_merchant {:fields {:id {:type :id}
- :name {:type 'String}}}
+ :name {:type 'String}}}
:intuit_bank_account {:fields {:id {:type :id}
:external_id {:type 'String}
@@ -222,8 +218,6 @@
:accounts {:type '(list :percentage_account)}
:transaction_approval_status {:type :transaction_approval_status}}}
-
-
:user
{:fields {:id {:type :id}
:name {:type 'String}
@@ -264,18 +258,12 @@
:start {:type 'Int}
:end {:type 'Int}}}
-
-
- :transaction_rule_page {:fields {:transaction_rules {:type '(list :transaction_rule)}
+ :transaction_rule_page {:fields {:transaction_rules {:type '(list :transaction_rule)}
:count {:type 'Int}
:total {:type 'Int}
:start {:type 'Int}
:end {:type 'Int}}}
-
-
-
-
:vendor_page {:fields {:vendors {:type '(list :vendor)}
:count {:type 'Int}
:total {:type 'Int}
@@ -283,10 +271,10 @@
:end {:type 'Int}}}
:account_page {:fields {:accounts {:type '(list :account)}
- :count {:type 'Int}
- :total {:type 'Int}
- :start {:type 'Int}
- :end {:type 'Int}}}
+ :count {:type 'Int}
+ :total {:type 'Int}
+ :start {:type 'Int}
+ :end {:type 'Int}}}
:reminder_page {:fields {:reminders {:type '(list :reminder)}
:count {:type 'Int}
@@ -303,7 +291,7 @@
:paid {:type 'String}
:unpaid {:type 'String}}}
- :upcoming_transaction {:fields {:amount {:type :money}
+ :upcoming_transaction {:fields {:amount {:type :money}
:identifier {:type 'String}
:date {:type :iso_date}}}
@@ -320,14 +308,13 @@
:potential_transaction_rule_matches {:type '(list :transaction_rule)
:args {:transaction_id {:type :id}}
:resolve :get-transaction-rule-matches}
-
:test_transaction_rule {:type '(list :transaction)
- :args {:transaction_rule {:type :edit_transaction_rule}}
- :resolve :test-transaction-rule}
+ :args {:transaction_rule {:type :edit_transaction_rule}}
+ :resolve :test-transaction-rule}
:run_transaction_rule {:type '(list :transaction)
- :args {:transaction_rule_id {:type :id}}
+ :args {:transaction_rule_id {:type :id}}
:resolve :run-transaction-rule}
:invoice_stats {:type '(list :invoice_stat)
@@ -337,7 +324,6 @@
:cash_flow {:type :cash_flow_result
:args {:client_id {:type :id}}
:resolve :get-cash-flow}
-
:all_accounts {:type '(list :account)
:args {}
@@ -351,11 +337,7 @@
:allowance {:type :account_allowance}
:client_id {:type :id}
:vendor_id {:type :id}}
- :resolve :search-account}
-
-
-
-
+ :resolve :search-account}
:yodlee_merchants {:type '(list :yodlee_merchant)
:args {}
@@ -376,16 +358,13 @@
:description {:type 'String}}
:resolve :get-transaction-rule-page}
-
-
-
:vendor {:type :vendor_page
:args {:name_like {:type 'String}
:start {:type 'Int}
:per_page {:type 'Int}
:sort {:type '(list :sort_item)}}
:resolve :get-vendor}
-
+
:vendor_by_id {:type :vendor
:args {:id {:type :id}}
:resolve :vendor-by-id}
@@ -395,8 +374,7 @@
:resolve :account-for-vendor}}
:input-objects
- {
- :sort_item
+ {:sort_item
{:fields {:sort_key {:type 'String}
:sort_name {:type 'String}
:asc {:type 'Boolean}}}
@@ -495,8 +473,7 @@
:name {:type 'String}
:client_overrides {:type '(list :edit_account_client_override)}}}}
- :enums {
- :processor {:values [{:enum-value :na}
+ :enums {:processor {:values [{:enum-value :na}
{:enum-value :doordash}
{:enum-value :koala}
{:enum-value :ezcater}
@@ -533,9 +510,7 @@
{:enum-value :equity}
{:enum-value :revenue}]}}
:mutations
- {
-
- :delete_transaction_rule
+ {:delete_transaction_rule
{:type :id
:args {:transaction_rule_id {:type :id}}
:resolve :mutation/delete-transaction-rule}
@@ -553,9 +528,7 @@
:upsert_transaction_rule
{:type :transaction_rule
:args {:transaction_rule {:type :edit_transaction_rule}}
- :resolve :mutation/upsert-transaction-rule}
- }})
-
+ :resolve :mutation/upsert-transaction-rule}}})
(defn snake->kebab [s]
(str/replace s #"_" "-"))
@@ -571,65 +544,64 @@
(defn ->graphql [m]
(walk/postwalk
- (fn [node]
- (cond
+ (fn [node]
+ (cond
- (keyword? node)
- (snake node)
+ (keyword? node)
+ (snake node)
- :else
- node))
- m))
+ :else
+ node))
+ m))
-
-(defn get-expense-account-stats [_ {:keys [client_id] } _]
+(defn get-expense-account-stats [_ {:keys [client_id]} _]
(let [query (cond-> {:query {:find ['?account '?account-name '(sum ?amount)]
- :in ['$]
- :where []}
- :args [(dc/db conn) client_id]}
- client_id (merge-query {:query {:in ['?c]}
-
- :args [client_id]})
- (not client_id) (merge-query {:query {:where ['[?c :client/name]]}})
+ :in ['$]
+ :where []}
+ :args [(dc/db conn) client_id]}
+ client_id (merge-query {:query {:in ['?c]}
- true (merge-query {:query {:where ['[?i :invoice/client ?c]
- '[?i :invoice/expense-accounts ?expense-account]
- '[?expense-account :invoice-expense-account/account ?account]
- '[?account :account/name ?account-name]
- '[?expense-account :invoice-expense-account/amount ?amount]]}}))
+ :args [client_id]})
+ (not client_id) (merge-query {:query {:where ['[?c :client/name]]}})
+
+ true (merge-query {:query {:where ['[?i :invoice/client ?c]
+ '[?i :invoice/expense-accounts ?expense-account]
+ '[?expense-account :invoice-expense-account/account ?account]
+ '[?account :account/name ?account-name]
+ '[?expense-account :invoice-expense-account/amount ?amount]]}}))
result (query2 query)]
(for [[account-id account-name total] result]
{:account {:id account-id :name account-name} :total total})))
-(defn get-invoice-stats [_ {:keys [client_id] } _]
+(defn get-invoice-stats [_ {:keys [client_id]} _]
(let [query (cond-> {:query {:find ['?name '(sum ?outstanding-balance) '(sum ?total)]
- :in ['$]
- :where []}
- :args [(dc/db conn) client_id]}
- client_id (merge-query {:query {:in ['?c]}
- :args [client_id]})
- (not client_id) (merge-query {:query {:where ['[?c :client/name]]}})
+ :in ['$]
+ :where []}
+ :args [(dc/db conn) client_id]}
+ client_id (merge-query {:query {:in ['?c]}
+ :args [client_id]})
+ (not client_id) (merge-query {:query {:where ['[?c :client/name]]}})
- true (merge-query {:query {:where ['[?i :invoice/client ?c]
- '[?i :invoice/outstanding-balance ?outstanding-balance]
- '[?i :invoice/total ?total]
- '[?i :invoice/due ?date]
- '[(.toInstant ^java.util.Date ?date) ?d2]
- '[(.between java.time.temporal.ChronoUnit/DAYS (java.time.Instant/now) ?d2 ) ?d3]
- '(or-join [?d3 ?name]
- (and [(<= ?d3 0)]
- [(ground :due) ?name])
- (and [(<= ?d3 30)]
- [(ground :due-30) ?name])
- (and [(<= ?d3 60)]
- [(ground :due-30) ?name])
- (and [(> ?d3 60)]
- [(ground :due-later) ?name]))]}}))
+ true (merge-query {:query {:where ['[?i :invoice/client ?c]
+ '[?i :invoice/outstanding-balance ?outstanding-balance]
+ '[?i :invoice/total ?total]
+ '[?i :invoice/due ?date]
+ '[(.toInstant ^java.util.Date ?date) ?d2]
+ '[(.between java.time.temporal.ChronoUnit/DAYS (java.time.Instant/now) ?d2) ?d3]
+ '(or-join [?d3 ?name]
+ (and [(<= ?d3 0)]
+ [(ground :due) ?name])
+ (and [(<= ?d3 30)]
+ [(ground :due-30) ?name])
+ (and [(<= ?d3 60)]
+ [(ground :due-30) ?name])
+ (and [(> ?d3 60)]
+ [(ground :due-later) ?name]))]}}))
result (->> (query2 query)
(group-by first))]
-
+
(for [[id name] [[:due "Due"] [:due-30 "0-30 days"] [:due-60 "31-60 days"] [:due-later ">60 days"]]
- :let [[[_ outstanding-balance total] ] (id result nil)
+ :let [[[_ outstanding-balance total]] (id result nil)
outstanding-balance (or outstanding-balance 0)
total (or total 0)]]
{:name name :unpaid outstanding-balance :paid (if (= :due id)
@@ -637,7 +609,7 @@
(- total outstanding-balance))})))
(defn has-fulfilled? [id date recent-fulfillments]
-
+
(seq (transduce
(filter (fn [[potential-id potential-date]]
(let [date (coerce/to-date-time date)
@@ -652,7 +624,7 @@
(defn get-cash-flow [_ {:keys [client_id]} _]
(when client_id
- (let [{:client/keys [week-a-credits week-a-debits week-b-credits week-b-debits forecasted-transactions ]} (dc/pull (dc/db conn) '[*] client_id)
+ (let [{:client/keys [week-a-credits week-a-debits week-b-credits week-b-debits forecasted-transactions]} (dc/pull (dc/db conn) '[*] client_id)
total-cash (reduce
(fn [total [credit debit]]
(- (+ total credit)
@@ -685,9 +657,9 @@
:where ['[?p :payment/client ?client]
'[?p :payment/status :payment-status/pending]
'[?p :payment/amount ?amount]
- '(or
- [?p :payment/type :payment-type/debit]
- [?p :payment/type :payment-type/check])]}
+ '(or
+ [?p :payment/type :payment-type/debit]
+ [?p :payment/type :payment-type/check])]}
(dc/db conn) client_id (coerce/to-date (t/plus (time/local-now) (t/days 180))))))
recent-fulfillments (dc/q {:find '[?f ?d]
:in '[$ ?client ?min-date]
@@ -710,7 +682,7 @@
:date (coerce/to-date-time next)})
is-week-a? (fn [d]
(= 0 (mod (t/in-weeks (t/interval first-week-a d)) 2)))]
-
+
{:beginning_balance total-cash
:outstanding_payments outstanding-checks
:invoices_due_soon (mapv (fn [[due outstanding invoice-number vendor-id vendor-name]]
@@ -735,31 +707,29 @@
:date (coerce/to-date-time date)})
(take (* 7 4) (time/day-of-week-seq 1)))
(filter #(< (:amount %) 0) forecasted-transactions))})))
-
(def schema
(-> integreat-schema
(attach-tracing-resolvers
- {
- :get-all-accounts gq-accounts/get-all-graphql
- :get-transaction-rule-page gq-transaction-rules/get-transaction-rule-page
- :get-transaction-rule-matches gq-transaction-rules/get-transaction-rule-matches
- :get-expense-account-stats get-expense-account-stats
- :get-invoice-stats get-invoice-stats
- :get-cash-flow get-cash-flow
- :get-yodlee-merchants ym/get-yodlee-merchants
- :get-intuit-bank-accounts gq-intuit-bank-accounts/get-intuit-bank-accounts
- :vendor-by-id gq-vendors/get-by-id
- :account-for-vendor gq-accounts/default-for-vendor
- :mutation/delete-transaction-rule gq-transaction-rules/delete-transaction-rule
- :mutation/upsert-transaction-rule gq-transaction-rules/upsert-transaction-rule
- :test-transaction-rule gq-transaction-rules/test-transaction-rule
- :run-transaction-rule gq-transaction-rules/run-transaction-rule
- :mutation/upsert-vendor gq-vendors/upsert-vendor
- :mutation/merge-vendors gq-vendors/merge-vendors
- :get-vendor gq-vendors/get-graphql
- :search-vendor gq-vendors/search
- :search-account gq-accounts/search})
+ {:get-all-accounts gq-accounts/get-all-graphql
+ :get-transaction-rule-page gq-transaction-rules/get-transaction-rule-page
+ :get-transaction-rule-matches gq-transaction-rules/get-transaction-rule-matches
+ :get-expense-account-stats get-expense-account-stats
+ :get-invoice-stats get-invoice-stats
+ :get-cash-flow get-cash-flow
+ :get-yodlee-merchants ym/get-yodlee-merchants
+ :get-intuit-bank-accounts gq-intuit-bank-accounts/get-intuit-bank-accounts
+ :vendor-by-id gq-vendors/get-by-id
+ :account-for-vendor gq-accounts/default-for-vendor
+ :mutation/delete-transaction-rule gq-transaction-rules/delete-transaction-rule
+ :mutation/upsert-transaction-rule gq-transaction-rules/upsert-transaction-rule
+ :test-transaction-rule gq-transaction-rules/test-transaction-rule
+ :run-transaction-rule gq-transaction-rules/run-transaction-rule
+ :mutation/upsert-vendor gq-vendors/upsert-vendor
+ :mutation/merge-vendors gq-vendors/merge-vendors
+ :get-vendor gq-vendors/get-graphql
+ :search-vendor gq-vendors/search
+ :search-account gq-accounts/search})
gq-checks/attach
gq-ledger/attach
gq-plaid/attach
@@ -772,30 +742,28 @@
gq-sales-orders/attach
schema/compile))
-
-
(defn simplify
"Converts all ordered maps nested within the map into standard hash maps, and
sequences into vectors, which makes for easier constants in the tests, and eliminates ordering problems."
[m]
(walk/postwalk
- (fn [node]
- (cond
- (instance? IPersistentMap node)
- (into {} node)
+ (fn [node]
+ (cond
+ (instance? IPersistentMap node)
+ (into {} node)
- (seq? node)
- (vec node)
+ (seq? node)
+ (vec node)
- (keyword? node)
- (kebab node)
+ (keyword? node)
+ (kebab node)
- :else
- node))
- m))
+ :else
+ node))
+ m))
(defn query-name [q]
- (try
+ (try
(str/join "__" (map name (:operations (p/operations (p/parse-query schema q)))))
(catch Exception _
"unknown query")))
@@ -805,32 +773,32 @@
(query id q nil))
([id q v]
(statsd/increment "query.graphql.count" {:tags #{(str "query:" (query-name q))}})
- (statsd/time! [(str "query.graphql.time" ) {:tags #{(str "query:" (query-name q))}}]
- (mu/with-context {:query-name (query-name q) :user id :query q}
- (mu/trace ::executing-query
- []
- (try
- (let [[result time] (time-it (simplify (execute schema q (dissoc v
- :clients) {:id id
- :clients (:clients v)
- :log-context (or (mu/local-context) {})})))]
-
- (when (seq (:errors result))
- (throw (ex-info "GraphQL error" {:result result})))
- result)
+ (statsd/time! [(str "query.graphql.time") {:tags #{(str "query:" (query-name q))}}]
+ (mu/with-context {:query-name (query-name q) :user id :query q}
+ (mu/trace ::executing-query
+ []
+ (try
+ (let [[result time] (time-it (simplify (execute schema q (dissoc v
+ :clients) {:id id
+ :clients (:clients v)
+ :log-context (or (mu/local-context) {})})))]
- (catch Exception e
- (if-let [v (or (:validation-error (ex-data e))
- (:validation-error (ex-data (.getCause e))))]
-
- (do
- (alog/warn ::query-validation
- :exception e)
- (throw e)
- #_{:errors [{:message v}]})
- (do
- (alog/error ::query-error
- :exception e)
+ (when (seq (:errors result))
+ (throw (ex-info "GraphQL error" {:result result})))
+ result)
- (throw e))))))))))
+ (catch Exception e
+ (if-let [v (or (:validation-error (ex-data e))
+ (:validation-error (ex-data (.getCause e))))]
+
+ (do
+ (alog/warn ::query-validation
+ :exception e)
+ (throw e)
+ #_{:errors [{:message v}]})
+ (do
+ (alog/error ::query-error
+ :exception e)
+
+ (throw e))))))))))
diff --git a/src/clj/auto_ap/graphql/accounts.clj b/src/clj/auto_ap/graphql/accounts.clj
index cec20fee..048ecc55 100644
--- a/src/clj/auto_ap/graphql/accounts.clj
+++ b/src/clj/auto_ap/graphql/accounts.clj
@@ -18,7 +18,6 @@
[iol-ion.tx :refer [random-tempid]]
[com.brunobonacci.mulog :as mu]))
-
(defn get-all-graphql [context args _]
(assert-admin (:id context))
(let [args (assoc args :id (:id context))
diff --git a/src/clj/auto_ap/graphql/checks.clj b/src/clj/auto_ap/graphql/checks.clj
index abe0e26b..4ed55ba3 100644
--- a/src/clj/auto_ap/graphql/checks.clj
+++ b/src/clj/auto_ap/graphql/checks.clj
@@ -97,7 +97,6 @@
[:line {:line-width 0.15 :color [50 50 50]}]]
[:cell {:colspan 3}]]
-
[[:cell {:size 9 :leading 11.5} "\n\n\n\n\nMEMO"]
[:cell {:colspan 5 :leading 11.5} (split-memo memo)
[:line {:line-width 0.15 :color [50 50 50]}]]
@@ -186,8 +185,6 @@
:payment/pdf-data
(edn/read-string)
-
-
make-check-pdf)]
(s3/put-object :bucket-name (:data-bucket env)
:key (:payment/s3-key check)
@@ -277,7 +274,6 @@
(conj payment)
(into (invoice-payments invoices 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))
@@ -297,7 +293,6 @@
(conj payment)
(into (invoice-payments invoices invoice-amounts)))))
-
(defmethod invoices->entities :payment-type/balance-credit [invoices invoice-amounts]
(when (<= (->> invoices
(map (comp invoice-amounts :db/id))
@@ -488,7 +483,6 @@
{:s3-url nil
:invoices (d-invoices/get-multi (map :invoice_id (:invoice_payments args)))})))
-
(defn void-payment [context {id :payment_id} _]
(let [check (d-checks/get-by-id id)]
(assert (or (= :payment-status/pending (:payment/status check))
@@ -549,7 +543,6 @@
:invoice-status/unpaid)}]]))))))))
id))
-
(defn void-payments [context args _]
(assert-admin (:id context))
(let [args (assoc args :clients (:clients context))
@@ -607,7 +600,6 @@
0.001))
invoices)
-
total-to-pay (reduce + 0 (map :invoice/outstanding-balance invoices-to-be-paid))
_ (when (<= total-to-pay 0.001)
(assert-failure "You must select invoices that need to be paid."))
@@ -637,8 +629,6 @@
[total-to-pay []])))
(into {}))
-
-
vendor-id (:db/id (:invoice/vendor (first invoices)))
payment {:db/id (str vendor-id)
:payment/amount total-to-pay
@@ -751,7 +741,6 @@
{:enum-value :pending}
{:enum-value :cleared}]}})
-
(def resolvers
{:get-potential-payments get-potential-payments
:get-payment-page get-payment-page
diff --git a/src/clj/auto_ap/graphql/clients.clj b/src/clj/auto_ap/graphql/clients.clj
index d7e22aef..ac6ef937 100644
--- a/src/clj/auto_ap/graphql/clients.clj
+++ b/src/clj/auto_ap/graphql/clients.clj
@@ -19,9 +19,9 @@
(defn get-admin-client [context {:keys [id]} _]
(assert-admin (:id context))
(->graphql
- (-> (d-clients/get-by-id id)
- (update :client/bank-accounts (fn [bas]
- (map #(set/rename-keys % {:bank-account/use-date-instead-of-post-date? :use-date-instead-of-post-date}) bas))))))
+ (-> (d-clients/get-by-id id)
+ (update :client/bank-accounts (fn [bas]
+ (map #(set/rename-keys % {:bank-account/use-date-instead-of-post-date? :use-date-instead-of-post-date}) bas))))))
(defn get-client-page [context args _]
(assert-admin (:id context))
@@ -29,7 +29,7 @@
[clients clients-count] (d-clients/get-graphql-page (assoc (<-graphql (:filters args))
:clients (:clients context)))
clients (->> clients
-
+
(map (fn [c]
(update c :client/bank-accounts (fn [bas]
(map #(set/rename-keys % {:bank-account/use-date-instead-of-post-date? :use-date-instead-of-post-date}) bas)))))
@@ -47,13 +47,6 @@
bank-accounts))))))]
(result->page clients clients-count :clients (:filters args))))
-
-
-
-
-
-
-
(def objects
{:location_match
{:fields {:location {:type 'String}
@@ -102,15 +95,13 @@
:yodlee_provider_accounts {:type '(list :yodlee_provider_account)}
:plaid_items {:type '(list :plaid_item)}}}
- :client_page
+ :client_page
{:fields {:clients {:type '(list :client)}
:count {:type 'Int}
:total {:type 'Int}
:start {:type 'Int}
:end {:type 'Int}}}
-
-
:bank_account
{:fields {:id {:type :id}
:integration_status {:type :integration_status}
@@ -139,9 +130,7 @@
:forecasted_transaction {:fields {:identifier {:type 'String}
:id {:type :id}
:day_of_month {:type 'Int}
- :amount {:type :money}}}
-
-})
+ :amount {:type :money}}}})
(def queries
{:client {:type '(list :client)
@@ -158,12 +147,12 @@
{})
(def input-objects
- { :client_filters
+ {:client_filters
{:fields {:code {:type 'String}
:name_like {:type 'String}
:start {:type 'Int}
:per_page {:type 'Int}
- :sort {:type '(list :sort_item)}}} })
+ :sort {:type '(list :sort_item)}}}})
(def enums
{:bank_account_type {:values [{:enum-value :check}
@@ -173,11 +162,10 @@
(def resolvers
{:get-client get-client
:get-admin-client get-admin-client
- :get-client-page get-client-page })
-
+ :get-client-page get-client-page})
(defn attach [schema]
- (->
+ (->
(merge-with merge schema
{:objects objects
:queries queries
diff --git a/src/clj/auto_ap/graphql/expected_deposit.clj b/src/clj/auto_ap/graphql/expected_deposit.clj
index 50ac678a..1e2477ec 100644
--- a/src/clj/auto_ap/graphql/expected_deposit.clj
+++ b/src/clj/auto_ap/graphql/expected_deposit.clj
@@ -11,7 +11,7 @@
(defn get-all-expected-deposits [context args _]
(assert-admin (:id context))
(map
- (comp ->graphql status->graphql)
+ (comp ->graphql status->graphql)
(first (d-expected-deposit/get-graphql (assoc (<-graphql args) :count Integer/MAX_VALUE)))))
(defn get-expected-deposit-page [context args _]
diff --git a/src/clj/auto_ap/graphql/ezcater.clj b/src/clj/auto_ap/graphql/ezcater.clj
index 373e2bd6..50ce20f1 100644
--- a/src/clj/auto_ap/graphql/ezcater.clj
+++ b/src/clj/auto_ap/graphql/ezcater.clj
@@ -17,15 +17,14 @@
{:name name
:id id}))
-
(def objects
{:ezcater_caterer {:fields {:name {:type 'String}
:id {:type :id}}}})
(def queries
{:search_ezcater_caterer {:type '(list :search_result)
- :args {:query {:type 'String}}
- :resolve :search-ezcater-caterer}})
+ :args {:query {:type 'String}}
+ :resolve :search-ezcater-caterer}})
(def enums
{})
diff --git a/src/clj/auto_ap/graphql/import_batch.clj b/src/clj/auto_ap/graphql/import_batch.clj
index 147343cf..57cf67ba 100644
--- a/src/clj/auto_ap/graphql/import_batch.clj
+++ b/src/clj/auto_ap/graphql/import_batch.clj
@@ -40,7 +40,6 @@
(merge-query {:query {:find ['?e]
:where ['[?e :import-batch/date]]}}))]
-
(cond->> (query2 query)
true (apply-sort-3 args)
true (apply-pagination args))))
@@ -66,9 +65,8 @@
(map #(update % :import-batch/date coerce/to-date-time)))
matching-count :data args)))
-
(defn attach [schema]
- (->
+ (->
(merge-with merge schema
{:objects {:import_batch {:fields {:user_name {:type 'String}
:id {:type :id}
@@ -83,12 +81,10 @@
:count {:type 'Int}
:total {:type 'Int}
:start {:type 'Int}
- :end {:type 'Int}}}
-
-}
+ :end {:type 'Int}}}}
:queries {:import_batch_page {:type :import_batch_page
:args {:filters {:type :import_batch_filters}}
-
+
:resolve :get-import-batch-page}}
:mutations {}
:input-objects {:import_batch_filters {:fields {:start {:type 'Int}
diff --git a/src/clj/auto_ap/graphql/intuit_bank_accounts.clj b/src/clj/auto_ap/graphql/intuit_bank_accounts.clj
index 9fc1ab50..f47c2eb9 100644
--- a/src/clj/auto_ap/graphql/intuit_bank_accounts.clj
+++ b/src/clj/auto_ap/graphql/intuit_bank_accounts.clj
@@ -7,6 +7,6 @@
(defn get-intuit-bank-accounts [context _ _]
(assert-admin (:id context))
(->graphql (map first (dc/q '[:find (pull ?e [*])
- :in $
- :where [?e :intuit-bank-account/external-id]]
- (dc/db conn)))))
+ :in $
+ :where [?e :intuit-bank-account/external-id]]
+ (dc/db conn)))))
diff --git a/src/clj/auto_ap/graphql/invoices.clj b/src/clj/auto_ap/graphql/invoices.clj
index d6ab1e3a..0035df06 100644
--- a/src/clj/auto_ap/graphql/invoices.clj
+++ b/src/clj/auto_ap/graphql/invoices.clj
@@ -174,8 +174,6 @@
(let [error (str "Expense account total (" expense-account-total ") does not equal invoice total (" total ")")]
(throw (ex-info error {:validation-error error}))))))
-
-
(defn add-invoice [context {{:keys [expense_accounts client_id vendor_id] :as in} :invoice} _]
(assert-no-conflicting in)
(assert-can-see-client (:id context) client_id)
@@ -193,8 +191,6 @@
(when-not ((set (map :db/id (:client/bank-accounts (d-clients/get-by-id client-id)))) bank-account-id)
(throw (ex-info (str "Bank account does not belong to client") {:validation-error "Bank account does not belong to client."}))))
-
-
(defn add-and-print-invoice [context {{:keys [total client_id vendor_id] :as in} :invoice bank-account-id :bank_account_id type :type} _]
(mu/trace ::validating-invoice [:invoice in]
(do
@@ -261,7 +257,6 @@
(-> (d-invoices/get-by-id id) (->graphql (:id context)))))
-
(defn get-ids-matching-filters [args]
(let [ids (some-> args
:filters
@@ -448,8 +443,6 @@
[])]
accounts)))
-
-
(defn bulk-change-invoices [context args _]
(assert-admin (:id context))
(when-not (:client_id args)
diff --git a/src/clj/auto_ap/graphql/ledger.clj b/src/clj/auto_ap/graphql/ledger.clj
index 367151e7..4bac4e66 100644
--- a/src/clj/auto_ap/graphql/ledger.clj
+++ b/src/clj/auto_ap/graphql/ledger.clj
@@ -38,9 +38,9 @@
_ (when (:client_id (:filters args))
(assert-can-see-client (:id context) (:client_id (:filters args))))
clients (or (and (:client_id (:filters args))
- [{:db/id (:client_id (:filters args))}])
- (:clients context))
-
+ [{:db/id (:client_id (:filters args))}])
+ (:clients context))
+
[journal-entries journal-entries-count] (l/get-graphql (assoc (<-graphql (:filters args))
:clients clients))
@@ -55,12 +55,10 @@
(let [args (assoc args :id (:id context))
[journal-entries journal-entries-count] (l/get-graphql (assoc (<-graphql (:filters args))
:per-page Integer/MAX_VALUE
- :clients (:clients context)))
+ :clients (:clients context)))]
-
- ]
{:csv_content_b64 (Base64/encodeBase64String
- (.getBytes
+ (.getBytes
(with-open [w (java.io.StringWriter.)]
(csv/write-csv w
(into [["Client" "Vendor" "Date" "Journal Entry" "Journal Entry Line" "Account Code" "Account Name" "Account Type" "Debit" "Credit" "Net"]]
@@ -83,22 +81,19 @@
(-> li :journal-entry-line/account :bank-account/numeric-code))
(or (-> li :journal-entry-line/account :account/name)
(-> li :journal-entry-line/account :bank-account/name))
- (some-> account-type name )
+ (some-> account-type name)
(-> li :journal-entry-line/debit)
(-> li :journal-entry-line/credit)
(if (#{:account-type/asset
:account-type/dividend
:account-type/expense} account-type)
(- (or (-> li :journal-entry-line/debit) 0.0) (or (-> li :journal-entry-line/credit) 0.0))
- (- (or (-> li :journal-entry-line/credit) 0.0) (or (-> li :journal-entry-line/debit) 0.0)))
-
- ]))
- (:journal-entry/line-items j))
- ))))
+ (- (or (-> li :journal-entry-line/credit) 0.0) (or (-> li :journal-entry-line/debit) 0.0)))]))
+
+ (:journal-entry/line-items j))))))
:quote? (constantly true))
(.toString w))))}))
-
(defn roll-up-until
([lookup-account all-ledger-entries end-date]
(roll-up-until lookup-account all-ledger-entries end-date nil))
@@ -107,57 +102,56 @@
(filter (fn [[d]]
(if start-date
(and
- (>= (compare d start-date) 0)
- (<= (compare d end-date) 0))
+ (>= (compare d start-date) 0)
+ (<= (compare d end-date) 0))
(<= (compare d end-date) 0))))
(reduce
- (fn [acc [_ _ account location debit credit]]
- (-> acc
- (update-in [[location account] :debit] (fnil + 0.0) debit)
- (update-in [[location account] :credit] (fnil + 0.0) credit)
- (update-in [[location account] :count] (fnil + 0) 1))
- )
- {})
+ (fn [acc [_ _ account location debit credit]]
+ (-> acc
+ (update-in [[location account] :debit] (fnil + 0.0) debit)
+ (update-in [[location account] :credit] (fnil + 0.0) credit)
+ (update-in [[location account] :count] (fnil + 0) 1)))
+ {})
(reduce-kv
- (fn [acc [location account-id] {:keys [debit credit count]}]
- (let [account (lookup-account account-id)
- account-type (:account_type account)]
-
- (conj acc (merge {:id (str account-id "-" location)
- :location (or location "")
- :count count
- :debits debit
- :credits credit
- :amount (if account-type (if (#{:account-type/asset
- :account-type/dividend
- :account-type/expense} account-type)
- (- debit credit)
- (- credit debit))
- 0.0)}
- account))))
- []))))
+ (fn [acc [location account-id] {:keys [debit credit count]}]
+ (let [account (lookup-account account-id)
+ account-type (:account_type account)]
+
+ (conj acc (merge {:id (str account-id "-" location)
+ :location (or location "")
+ :count count
+ :debits debit
+ :credits credit
+ :amount (if account-type (if (#{:account-type/asset
+ :account-type/dividend
+ :account-type/expense} account-type)
+ (- debit credit)
+ (- credit debit))
+ 0.0)}
+ account))))
+ []))))
(defn full-ledger-for-client [client-id]
- (->> (dc/q
- {:find ['?d '?jel '?account '?location '?debit '?credit]
- :in ['$ '?client-id]
- :where '[[?e :journal-entry/client ?client-id]
- [?e :journal-entry/date ?d]
- [?e :journal-entry/line-items ?jel]
- (or-join [?e]
- (and [?e :journal-entry/original-entity ?i]
- (or-join [?e ?i]
- (and
- [?i :transaction/bank-account ?b]
- (or [?b :bank-account/include-in-reports true]
- (not [?b :bank-account/include-in-reports])))
- (not [?i :transaction/bank-account])))
- (not [?e :journal-entry/original-entity ]))
- [(get-else $ ?jel :journal-entry-line/account :account/unknown) ?account]
- [(get-else $ ?jel :journal-entry-line/debit 0.0) ?debit ]
- [(get-else $ ?jel :journal-entry-line/credit 0.0) ?credit]
- [(get-else $ ?jel :journal-entry-line/location "") ?location]]}
- (dc/db conn) client-id)
+ (->> (dc/q
+ {:find ['?d '?jel '?account '?location '?debit '?credit]
+ :in ['$ '?client-id]
+ :where '[[?e :journal-entry/client ?client-id]
+ [?e :journal-entry/date ?d]
+ [?e :journal-entry/line-items ?jel]
+ (or-join [?e]
+ (and [?e :journal-entry/original-entity ?i]
+ (or-join [?e ?i]
+ (and
+ [?i :transaction/bank-account ?b]
+ (or [?b :bank-account/include-in-reports true]
+ (not [?b :bank-account/include-in-reports])))
+ (not [?i :transaction/bank-account])))
+ (not [?e :journal-entry/original-entity]))
+ [(get-else $ ?jel :journal-entry-line/account :account/unknown) ?account]
+ [(get-else $ ?jel :journal-entry-line/debit 0.0) ?debit]
+ [(get-else $ ?jel :journal-entry-line/credit 0.0) ?credit]
+ [(get-else $ ?jel :journal-entry-line/location "") ?location]]}
+ (dc/db conn) client-id)
(sort-by first)))
(defn get-balance-sheet [context args _]
@@ -180,30 +174,29 @@
[client-id (build-account-lookup client-id)]))
(into {}))]
(alog/info ::balance-sheet :params args)
-
+
(cond-> {:balance-sheet-accounts (mapcat
- #(roll-up-until (lookup-account %) (all-ledger-entries %) end-date )
- client-ids)
- }
+ #(roll-up-until (lookup-account %) (all-ledger-entries %) end-date)
+ client-ids)}
(:include_comparison args) (assoc :comparable-balance-sheet-accounts (mapcat
- #(roll-up-until (lookup-account %) (all-ledger-entries %) comparable-date )
- client-ids))
+ #(roll-up-until (lookup-account %) (all-ledger-entries %) comparable-date)
+ client-ids))
true ->graphql)))
(defn get-profit-and-loss-raw [client-ids periods]
- (let [ all-ledger-entries (->> client-ids
- (map (fn [client-id]
- [client-id (full-ledger-for-client client-id)]))
- (into {}))
+ (let [all-ledger-entries (->> client-ids
+ (map (fn [client-id]
+ [client-id (full-ledger-for-client client-id)]))
+ (into {}))
lookup-account (->> client-ids
(map (fn [client-id]
[client-id (build-account-lookup client-id)]))
(into {}))]
- (->graphql {:periods
+ (->graphql {:periods
(->> periods
(mapv (fn [{:keys [start end]}]
{:accounts (mapcat
- #(roll-up-until (lookup-account %) (all-ledger-entries %) (coerce/to-date end) (coerce/to-date start) )
+ #(roll-up-until (lookup-account %) (all-ledger-entries %) (coerce/to-date end) (coerce/to-date start))
client-ids)})))})))
(defn get-profit-and-loss [context args _]
@@ -216,12 +209,9 @@
(assert-can-see-client (:id context) client-id))
_ (when (and (:include_deltas args)
(:column_per_location args))
- (throw (ex-info "Please select one of 'Include deltas' or 'Column per location'" {:validation-error "Please select one of 'Include deltas' or 'Column per location'"}))) ]
+ (throw (ex-info "Please select one of 'Include deltas' or 'Column per location'" {:validation-error "Please select one of 'Include deltas' or 'Column per location'"})))]
(get-profit-and-loss-raw client-ids (:periods args))))
-
-
-
;; profit and loss based off of index
#_(defn get-profit-and-loss [context args _]
(let [client-id (:client_id args)
@@ -239,17 +229,17 @@
:in $ [?c ...]
:where
(or-join [?c ?a ?l]
- (and
- [?a :account/numeric-code]
- (not [?a :account/location])
- [?c :client/locations ?l])
(and
- [?a :account/numeric-code]
- [?a :account/location ?l]
- [?c :client/locations ?l])
+ [?a :account/numeric-code]
+ (not [?a :account/location])
+ [?c :client/locations ?l])
(and
- [?c :client/bank-accounts ?a]
- [(ground "A") ?l]))]
+ [?a :account/numeric-code]
+ [?a :account/location ?l]
+ [?c :client/locations ?l])
+ (and
+ [?c :client/bank-accounts ?a]
+ [(ground "A") ?l]))]
(dc/db conn)
client-ids)
lookup-account (->> client-ids
@@ -257,49 +247,48 @@
[client-id (build-account-lookup client-id)]))
(into {}))]
(->graphql
- {:periods
- (->> (:periods args)
- (mapv (fn [{:keys [start end]}]
- (let [start (coerce/to-date start)
- end (coerce/to-date end)]
- {:accounts (mapcat
- (fn [[c a l]]
- (let [start-point (->> (dc/index-pull db
- {:index :avet
- :selector [:db/id :journal-entry-line/running-balance :journal-entry-line/client+account+location+date]
- :start [:journal-entry-line/client+account+location+date [c a l start]]
- :reverse true
- :limit 1})
- (take-while (fn [result]
- (= [c a l]
- (take 3 (:journal-entry-line/client+account+location+date result)))))
- (drop-while (fn [{[_ _ _ date] :journal-entry-line/client+account+location+date}]
- (>= (compare date start) 0)))
- first)
- end-point (->> (dc/index-pull db
- {:index :avet
- :selector [:db/id :journal-entry-line/running-balance :journal-entry-line/client+account+location+date]
- :start [:journal-entry-line/client+account+location+date [c a l end]]
- :reverse true
- :limit 1})
- (take-while (fn [result]
- (= [c a l]
- (take 3 (:journal-entry-line/client+account+location+date result)))))
- (take 1)
- (drop-while (fn [{[_ _ _ date] :journal-entry-line/client+account+location+date}]
- (>= (compare date end) 0)))
- first)]
- (when end-point
- [(merge {:id (str a "-" l)
- :location (or l "")
- :count 0
- :debits 0
- :credits 0
- :amount (- (or (:journal-entry-line/running-balance end-point) 0.0)
- (or (:journal-entry-line/running-balance start-point) 0.0))
- }
- ((lookup-account c) a))])))
- all-used-account-locations)}))))})))
+ {:periods
+ (->> (:periods args)
+ (mapv (fn [{:keys [start end]}]
+ (let [start (coerce/to-date start)
+ end (coerce/to-date end)]
+ {:accounts (mapcat
+ (fn [[c a l]]
+ (let [start-point (->> (dc/index-pull db
+ {:index :avet
+ :selector [:db/id :journal-entry-line/running-balance :journal-entry-line/client+account+location+date]
+ :start [:journal-entry-line/client+account+location+date [c a l start]]
+ :reverse true
+ :limit 1})
+ (take-while (fn [result]
+ (= [c a l]
+ (take 3 (:journal-entry-line/client+account+location+date result)))))
+ (drop-while (fn [{[_ _ _ date] :journal-entry-line/client+account+location+date}]
+ (>= (compare date start) 0)))
+ first)
+ end-point (->> (dc/index-pull db
+ {:index :avet
+ :selector [:db/id :journal-entry-line/running-balance :journal-entry-line/client+account+location+date]
+ :start [:journal-entry-line/client+account+location+date [c a l end]]
+ :reverse true
+ :limit 1})
+ (take-while (fn [result]
+ (= [c a l]
+ (take 3 (:journal-entry-line/client+account+location+date result)))))
+ (take 1)
+ (drop-while (fn [{[_ _ _ date] :journal-entry-line/client+account+location+date}]
+ (>= (compare date end) 0)))
+ first)]
+ (when end-point
+ [(merge {:id (str a "-" l)
+ :location (or l "")
+ :count 0
+ :debits 0
+ :credits 0
+ :amount (- (or (:journal-entry-line/running-balance end-point) 0.0)
+ (or (:journal-entry-line/running-balance start-point) 0.0))}
+ ((lookup-account c) a))])))
+ all-used-account-locations)}))))})))
(defn profit-and-loss-pdf [context args value]
(let [data (get-profit-and-loss context args value)
@@ -320,10 +309,9 @@
(->graphql result)))
-
(defn assoc-error [f]
(fn [entry]
- (try
+ (try
(f entry)
(catch Exception e
(assoc entry :error (.getMessage e)
@@ -333,13 +321,13 @@
(defn all-ids-not-locked [all-ids]
(->> all-ids
(dc/q '[:find ?t
- :in $ [?t ...]
- :where
- [?t :journal-entry/client ?c]
- [(get-else $ ?c :client/locked-until #inst "2000-01-01") ?lu]
- [?t :journal-entry/date ?d]
- [(>= ?d ?lu)]]
- (dc/db conn))
+ :in $ [?t ...]
+ :where
+ [?t :journal-entry/client ?c]
+ [(get-else $ ?c :client/locked-until #inst "2000-01-01") ?lu]
+ [?t :journal-entry/date ?d]
+ [(>= ?d ?lu)]]
+ (dc/db conn))
(map first)))
(defn delete-external-ledger [context args _]
@@ -353,8 +341,8 @@
(#(l/raw-graphql-ids (dc/db conn) %))
:ids)
_ (alog/info ::trying-to-delete
- :count (count ids)
- :sample (take 3 ids))
+ :count (count ids)
+ :sample (take 3 ids))
specific-ids (l/filter-ids (:ids args))
all-ids (all-ids-not-locked (into (set ids) specific-ids))]
(if (> (count all-ids) 1000)
@@ -364,7 +352,7 @@
(audit-transact-batch
(map (fn [i]
[:db/retractEntity i])
- all-ids)
+ all-ids)
(:id context))
{:message (str "Succesfully deleted " (count all-ids) " ledger entries.")}))))
@@ -372,15 +360,15 @@
(assert-admin (:id context))
(let [used-vendor-names (set (map :vendor_name (:entries args)))
all-vendors (mu/trace ::get-all-vendors
- []
- (->> (dc/q '[:find ?e
- :in $ [?name ...]
- :where [?e :vendor/name ?name]]
- (dc/db conn)
- used-vendor-names)
- (map first)
- (pull-many (dc/db conn) [:db/id :vendor/name])
- (by :vendor/name)))
+ []
+ (->> (dc/q '[:find ?e
+ :in $ [?name ...]
+ :where [?e :vendor/name ?name]]
+ (dc/db conn)
+ used-vendor-names)
+ (map first)
+ (pull-many (dc/db conn) [:db/id :vendor/name])
+ (by :vendor/name)))
client-locked-lookup (mu/trace ::get-all-clients []
(->> (dc/q '[:find ?code ?locked-until
:in $
@@ -389,18 +377,18 @@
(dc/db conn))
(into {})))
all-client-bank-accounts (mu/trace ::get-all-client-bank-accounts
- []
- (->> (dc/q '[:find ?code ?ba-code
- :in $
- :where [?c :client/code ?code]
- [?c :client/bank-accounts ?ba]
- [?ba :bank-account/code ?ba-code]]
- (dc/db conn))
- (reduce
- (fn [acc [code ba-code]]
- (update acc code (fnil conj #{}) ba-code))
- {})))
-
+ []
+ (->> (dc/q '[:find ?code ?ba-code
+ :in $
+ :where [?c :client/code ?code]
+ [?c :client/bank-accounts ?ba]
+ [?ba :bank-account/code ?ba-code]]
+ (dc/db conn))
+ (reduce
+ (fn [acc [code ba-code]]
+ (update acc code (fnil conj #{}) ba-code))
+ {})))
+
all-client-locations (mu/trace ::get-all-client-locations
[]
(->> (dc/q '[:find ?code ?location
@@ -409,160 +397,158 @@
[?c :client/locations ?location]]
(dc/db conn))
(reduce
- (fn [acc [code ba-code]]
- (update acc code (fnil conj #{"HQ" "A"}) ba-code))
- {})))
-
+ (fn [acc [code ba-code]]
+ (update acc code (fnil conj #{"HQ" "A"}) ba-code))
+ {})))
+
new-hidden-vendors (reduce
- (fn [new-vendors {:keys [vendor_name]}]
- (if (or (all-vendors vendor_name)
- (new-vendors vendor_name))
- new-vendors
- (assoc new-vendors vendor_name
- {:vendor/name vendor_name
- :vendor/hidden true
- :db/id vendor_name})))
- {}
- (:entries args))
+ (fn [new-vendors {:keys [vendor_name]}]
+ (if (or (all-vendors vendor_name)
+ (new-vendors vendor_name))
+ new-vendors
+ (assoc new-vendors vendor_name
+ {:vendor/name vendor_name
+ :vendor/hidden true
+ :db/id vendor_name})))
+ {}
+ (:entries args))
_ (mu/trace ::upsert-new-vendors
- []
- (audit-transact-batch (vec (vals new-hidden-vendors)) (:id context)))
+ []
+ (audit-transact-batch (vec (vals new-hidden-vendors)) (:id context)))
all-vendors (->> (dc/q '[:find ?e
- :in $ [?name ...]
- :where [?e :vendor/name ?name]]
- (dc/db conn)
- used-vendor-names)
+ :in $ [?name ...]
+ :where [?e :vendor/name ?name]]
+ (dc/db conn)
+ used-vendor-names)
(map first)
(pull-many (dc/db conn) [:db/id :vendor/name])
(by :vendor/name))
all-accounts (mu/trace ::get-all-accounts []
(transduce (map (comp str :account/numeric-code)) conj #{} (a/get-accounts)))
transaction (mu/trace ::build-transaction
- [:count (count (:entries args))]
- (doall (map
- (assoc-error (fn [entry]
- (let [vendor (all-vendors (:vendor_name entry))]
- (when-not (client-locked-lookup (:client_code entry))
- (throw (ex-info (str "Client '" (:client_code entry )"' not found.") {:status :error}) ))
- (when-not vendor
- (throw (ex-info (str "Vendor '" (:vendor_name entry) "' not found.") {:status :error})))
- (when-not (re-find #"\d{1,2}/\d{1,2}/\d{4}" (:date entry))
- (throw (ex-info (str "Date must be MM/dd/yyyy") {:status :error})))
- (when-let [locked-until (client-locked-lookup (:client_code entry))]
- (when (and (not (t/after? (coerce/to-date-time (coerce/to-date (parse/parse-value :clj-time "MM/dd/yyyy" (:date entry))))
- (coerce/to-date-time locked-until)))
- (not (t/equal? (coerce/to-date-time (coerce/to-date (parse/parse-value :clj-time "MM/dd/yyyy" (:date entry))))
- (coerce/to-date-time locked-until))))
- (throw (ex-info (str "Client's data is locked until " locked-until) {:status :error}))))
-
- (when-not (dollars= (reduce (fnil + 0.0 0.0) 0.0 (map :debit (:line_items entry)))
- (reduce (fnil + 0.0 0.0) 0.0 (map :credit (:line_items entry))))
- (throw (ex-info (str "Debits '"
- (reduce (fnil + 0.0 0.0) 0 (map :debit (:line_items entry)))
- "' and credits '"
- (reduce (fnil + 0.0 0.0) 0 (map :credit (:line_items entry)))
- "' do not add up.")
- {:status :error})))
- (when (dollars= (reduce (fnil + 0.0 0.0) 0.0 (map :debit (:line_items entry)))
- 0.0)
- (throw (ex-info (str "Cannot have ledger entries that total $0.00")
- {:status :ignored})))
- (assoc entry
- :status :success
- :tx
- [:upsert-ledger
- (remove-nils
- {:journal-entry/source (:source entry)
- :journal-entry/client [:client/code (:client_code entry)]
- :journal-entry/date (coerce/to-date (parse/parse-value :clj-time "MM/dd/yyyy" (:date entry)))
- :journal-entry/external-id (:external_id entry)
- :journal-entry/vendor (:db/id (all-vendors (:vendor_name entry)))
- :journal-entry/amount (:amount entry)
- :journal-entry/note (:note entry)
- :journal-entry/cleared-against (:cleared_against entry)
+ [:count (count (:entries args))]
+ (doall (map
+ (assoc-error (fn [entry]
+ (let [vendor (all-vendors (:vendor_name entry))]
+ (when-not (client-locked-lookup (:client_code entry))
+ (throw (ex-info (str "Client '" (:client_code entry) "' not found.") {:status :error})))
+ (when-not vendor
+ (throw (ex-info (str "Vendor '" (:vendor_name entry) "' not found.") {:status :error})))
+ (when-not (re-find #"\d{1,2}/\d{1,2}/\d{4}" (:date entry))
+ (throw (ex-info (str "Date must be MM/dd/yyyy") {:status :error})))
+ (when-let [locked-until (client-locked-lookup (:client_code entry))]
+ (when (and (not (t/after? (coerce/to-date-time (coerce/to-date (parse/parse-value :clj-time "MM/dd/yyyy" (:date entry))))
+ (coerce/to-date-time locked-until)))
+ (not (t/equal? (coerce/to-date-time (coerce/to-date (parse/parse-value :clj-time "MM/dd/yyyy" (:date entry))))
+ (coerce/to-date-time locked-until))))
+ (throw (ex-info (str "Client's data is locked until " locked-until) {:status :error}))))
- :journal-entry/line-items
- (mapv (fn [ea]
- (let [debit (or (:debit ea) 0.0)
- credit (or (:credit ea) 0.0)]
- (when (and (not (get
- (get all-client-locations (:client_code entry))
- (:location ea)))
- (not= "A" (:location ea)))
- (throw (ex-info (str "Location '" (:location ea) "' not found.")
- {:status :error})))
- (when (and (<= debit 0.0)
- (<= credit 0.0))
- (throw (ex-info (str "Line item amount " (or debit credit) " must be greater than 0.")
- {:status :error})))
- (when (and (not (all-accounts (:account_identifier ea)))
- (not (get
- (get all-client-bank-accounts (:client_code entry))
- (:account_identifier ea))))
- (throw (ex-info (str "Account '" (:account_identifier ea) "' not found.")
- {:status :error})))
- (let [matching-account (when (re-matches #"^[0-9]+$" (:account_identifier ea))
- (a/get-account-by-numeric-code-and-sets (Integer/parseInt (:account_identifier ea)) ["default"]))]
- (when (and matching-account
- (:account/location matching-account)
- (not= (:account/location matching-account)
- (:location ea)))
- (throw (ex-info (str "Account '"
- (:account/numeric-code matching-account)
- "' requires location '"
- (:account/location matching-account)
- "' but got '"
- (:location ea)
- "'")
- {:status :error})))
- (when (and matching-account
- (not (:account/location matching-account))
- (= "A" (:location ea)))
- (throw (ex-info (str "Account '"
- (:account/numeric-code matching-account)
- "' cannot use location '"
- (:location ea)
- "'")
- {:status :error})))
- (remove-nils (cond-> {:db/id (random-tempid)
- :journal-entry-line/location (:location ea)
- :journal-entry-line/debit (when (> debit 0)
- debit)
- :journal-entry-line/credit (when (> credit 0)
- credit)}
- matching-account (assoc :journal-entry-line/account (:db/id matching-account))
- (not matching-account) (assoc :journal-entry-line/account [:bank-account/code (:account_identifier ea)]))))))
- (:line_items entry))
-
- :journal-entry/cleared true})]))))
- (:entries args))))
+ (when-not (dollars= (reduce (fnil + 0.0 0.0) 0.0 (map :debit (:line_items entry)))
+ (reduce (fnil + 0.0 0.0) 0.0 (map :credit (:line_items entry))))
+ (throw (ex-info (str "Debits '"
+ (reduce (fnil + 0.0 0.0) 0 (map :debit (:line_items entry)))
+ "' and credits '"
+ (reduce (fnil + 0.0 0.0) 0 (map :credit (:line_items entry)))
+ "' do not add up.")
+ {:status :error})))
+ (when (dollars= (reduce (fnil + 0.0 0.0) 0.0 (map :debit (:line_items entry)))
+ 0.0)
+ (throw (ex-info (str "Cannot have ledger entries that total $0.00")
+ {:status :ignored})))
+ (assoc entry
+ :status :success
+ :tx
+ [:upsert-ledger
+ (remove-nils
+ {:journal-entry/source (:source entry)
+ :journal-entry/client [:client/code (:client_code entry)]
+ :journal-entry/date (coerce/to-date (parse/parse-value :clj-time "MM/dd/yyyy" (:date entry)))
+ :journal-entry/external-id (:external_id entry)
+ :journal-entry/vendor (:db/id (all-vendors (:vendor_name entry)))
+ :journal-entry/amount (:amount entry)
+ :journal-entry/note (:note entry)
+ :journal-entry/cleared-against (:cleared_against entry)
+
+ :journal-entry/line-items
+ (mapv (fn [ea]
+ (let [debit (or (:debit ea) 0.0)
+ credit (or (:credit ea) 0.0)]
+ (when (and (not (get
+ (get all-client-locations (:client_code entry))
+ (:location ea)))
+ (not= "A" (:location ea)))
+ (throw (ex-info (str "Location '" (:location ea) "' not found.")
+ {:status :error})))
+ (when (and (<= debit 0.0)
+ (<= credit 0.0))
+ (throw (ex-info (str "Line item amount " (or debit credit) " must be greater than 0.")
+ {:status :error})))
+ (when (and (not (all-accounts (:account_identifier ea)))
+ (not (get
+ (get all-client-bank-accounts (:client_code entry))
+ (:account_identifier ea))))
+ (throw (ex-info (str "Account '" (:account_identifier ea) "' not found.")
+ {:status :error})))
+ (let [matching-account (when (re-matches #"^[0-9]+$" (:account_identifier ea))
+ (a/get-account-by-numeric-code-and-sets (Integer/parseInt (:account_identifier ea)) ["default"]))]
+ (when (and matching-account
+ (:account/location matching-account)
+ (not= (:account/location matching-account)
+ (:location ea)))
+ (throw (ex-info (str "Account '"
+ (:account/numeric-code matching-account)
+ "' requires location '"
+ (:account/location matching-account)
+ "' but got '"
+ (:location ea)
+ "'")
+ {:status :error})))
+ (when (and matching-account
+ (not (:account/location matching-account))
+ (= "A" (:location ea)))
+ (throw (ex-info (str "Account '"
+ (:account/numeric-code matching-account)
+ "' cannot use location '"
+ (:location ea)
+ "'")
+ {:status :error})))
+ (remove-nils (cond-> {:db/id (random-tempid)
+ :journal-entry-line/location (:location ea)
+ :journal-entry-line/debit (when (> debit 0)
+ debit)
+ :journal-entry-line/credit (when (> credit 0)
+ credit)}
+ matching-account (assoc :journal-entry-line/account (:db/id matching-account))
+ (not matching-account) (assoc :journal-entry-line/account [:bank-account/code (:account_identifier ea)]))))))
+ (:line_items entry))
+
+ :journal-entry/cleared true})]))))
+ (:entries args))))
errors (filter #(= (:status %) :error) transaction)
ignored (filter #(= (:status %) :ignored) transaction)
success (filter #(= (:status %) :success) transaction)
retraction (mapv (fn [x] [:db/retractEntity [:journal-entry/external-id (:external_id x)]])
success)
ignore-retraction (->> ignored
- (map :external_id )
+ (map :external_id)
(dc/q '[:find ?je
- :in $ [?ei ...]
- :where [?je :journal-entry/external-id ?ei]]
- (dc/db conn)
- )
+ :in $ [?ei ...]
+ :where [?je :journal-entry/external-id ?ei]]
+ (dc/db conn))
(map first)
(map (fn [je] [:db/retractEntity je])))]
(alog/info ::manual-import
:errors (count errors)
:sample (take 3 errors))
-
(mu/trace ::retraction-tx
- [:count (count retraction)]
- (audit-transact-batch retraction (:id context)))
+ [:count (count retraction)]
+ (audit-transact-batch retraction (:id context)))
(mu/trace ::ignore-retraction-tx
- [:count (count ignore-retraction)]
- (when (seq ignore-retraction)
- (audit-transact-batch ignore-retraction (:id context))))
- (let [invalidated
+ [:count (count ignore-retraction)]
+ (when (seq ignore-retraction)
+ (audit-transact-batch ignore-retraction (:id context))))
+ (let [invalidated
(mu/trace ::success-tx
[:count (count success)]
(for [[_ n] (:tempids (audit-transact-batch (map :tx success) (:id context)))]
@@ -573,7 +559,7 @@
[:count (count invalidated)]
(doseq [n invalidated]
(solr/touch n)))))
-
+
{:successful (map (fn [x] {:external_id (:external_id x)}) success)
:ignored (map (fn [x]
{:external_id (:external_id x)})
@@ -582,7 +568,6 @@
:errors (map (fn [x] {:external_id (:external_id x)
:error (:error x)}) errors)}))
-
(defn get-journal-detail-report [context input _]
(let [category-totals (atom {})
base-categories (into []
@@ -597,20 +582,19 @@
:clients [{:db/id client-id}])
{:filters {:location location
:date_range (:date_range input)
- :from_numeric_code (l-reports/min-numeric-code category )
- :to_numeric_code (l-reports/max-numeric-code category )
+ :from_numeric_code (l-reports/min-numeric-code category)
+ :to_numeric_code (l-reports/max-numeric-code category)
:per_page Integer/MAX_VALUE}}
nil)
:journal_entries
(mapcat (fn [je]
(->> (je :line_items)
(filter (fn [jel]
- (when-let [account (account-lookup (:id (:account jel)))]
- (and
- (l-reports/account-belongs-in-category? (:numeric_code account) category)
- (= location (:location jel)))))
- )
- (map (fn [jel ]
+ (when-let [account (account-lookup (:id (:account jel)))]
+ (and
+ (l-reports/account-belongs-in-category? (:numeric_code account) category)
+ (= location (:location jel))))))
+ (map (fn [jel]
{:date (:date je)
:debit (:debit jel)
:credit (:credit jel)
@@ -621,18 +605,18 @@
(into []))
_ (swap! category-totals assoc-in [client-id location category]
(- (or (reduce + 0.0 (map #(or (:credit %) 0.0) all-journal-entries)) 0.0)
- (or (reduce + 0.0 (map #(or (:debit %) 0.0) all-journal-entries)) 0.0)) )
+ (or (reduce + 0.0 (map #(or (:debit %) 0.0) all-journal-entries)) 0.0)))
journal-entries-by-account (group-by #(account-lookup (get-in % [:account :id])) all-journal-entries)]
[account journal-entries] (conj (vec journal-entries-by-account) [nil all-journal-entries])
:let [journal-entries (first (reduce
- (fn [[acc last-je] je]
- (let [next-je (assoc je :running_balance
- (- (+ (or (:running_balance last-je 0.0) 0.0)
- (or (:credit je 0.0) 0.0))
- (or (:debit je 0.0) 0.0)))]
- [(conj acc next-je) next-je]))
- []
- (sort-by :date journal-entries)))]]
+ (fn [[acc last-je] je]
+ (let [next-je (assoc je :running_balance
+ (- (+ (or (:running_balance last-je 0.0) 0.0)
+ (or (:credit je 0.0) 0.0))
+ (or (:debit je 0.0) 0.0)))]
+ [(conj acc next-je) next-je]))
+ []
+ (sort-by :date journal-entries)))]]
{:category (->graphql category)
:client_id client-id
:location location
@@ -641,7 +625,7 @@
:journal_entries (when account journal-entries)
:total (- (or (reduce + 0.0 (map #(or (:credit %) 0.0) journal-entries)) 0.0)
(or (reduce + 0.0 (map #(or (:debit %) 0.0) journal-entries)) 0.0))}))
- result {:categories
+ result {:categories
(into base-categories
(for [client-id (:client_ids input)
:let [_ (assert-can-see-client (:id context) client-id)
@@ -675,15 +659,12 @@
line))}]
result))
-
-
(defn journal-detail-report-pdf [context args value]
(let [data (get-journal-detail-report context args value)
result (print-journal-detail-report (:id context) args data)]
(->graphql result)))
-
(def objects
{:balance_sheet_account
{:fields {:id {:type 'String}
@@ -847,7 +828,7 @@
(def input-objects
{:numeric_code_range
{:fields {:from {:type 'Int}
- :to {:type 'Int}}}
+ :to {:type 'Int}}}
:ledger_filters
{:fields {:client_id {:type :id}
:vendor_id {:type :id}
@@ -874,25 +855,23 @@
:credit {:type :money}}}
:import_ledger_entry
{:fields {:source {:type 'String}
- :external_id {:type 'String}
+ :external_id {:type 'String}
:client_code {:type 'String}
:date {:type 'String}
:vendor_name {:type 'String}
:amount {:type :money}
:note {:type 'String}
:cleared_against {:type 'String}
- :line_items {:type '(list :import_ledger_line_item)}}}
- })
+ :line_items {:type '(list :import_ledger_line_item)}}}})
(def enums
{:ledger_category {:values [{:enum-value :sales}
- {:enum-value :cogs}
- {:enum-value :payroll}
+ {:enum-value :cogs}
+ {:enum-value :payroll}
{:enum-value :controllable}
{:enum-value :fixed_overhead}
{:enum-value :ownership_controllable}]}})
-
(def resolvers
{:get-ledger-page get-ledger-page
:get-balance-sheet get-balance-sheet
diff --git a/src/clj/auto_ap/graphql/plaid.clj b/src/clj/auto_ap/graphql/plaid.clj
index a678215d..9f8d174e 100644
--- a/src/clj/auto_ap/graphql/plaid.clj
+++ b/src/clj/auto_ap/graphql/plaid.clj
@@ -21,13 +21,11 @@
:name (first name)}))
[]))
-
-
(defn attach [schema]
- (->
+ (->
(merge-with merge schema
{:objects {:plaid_link_result
- {:fields {:token {:type 'String}} }
+ {:fields {:token {:type 'String}}}
:plaid_item
{:fields {:external_id {:type 'String}
@@ -50,7 +48,7 @@
:name {:type 'String}
:number {:type 'String}}}}
:queries {:search_plaid_merchants {:type '(list :plaid_merchant)
- :args {:query {:type 'String}}
- :resolve :search-plaid-merchants}}})
+ :args {:query {:type 'String}}
+ :resolve :search-plaid-merchants}}})
(attach-tracing-resolvers {:search-plaid-merchants search-merchants})))
diff --git a/src/clj/auto_ap/graphql/sales_orders.clj b/src/clj/auto_ap/graphql/sales_orders.clj
index 660c30c8..2193254b 100644
--- a/src/clj/auto_ap/graphql/sales_orders.clj
+++ b/src/clj/auto_ap/graphql/sales_orders.clj
@@ -1,6 +1,6 @@
(ns auto-ap.graphql.sales-orders
(:require [auto-ap.datomic.sales-orders :as d-sales-orders2]
- [auto-ap.graphql.utils :refer [->graphql <-graphql result->page assert-admin] ]
+ [auto-ap.graphql.utils :refer [->graphql <-graphql result->page assert-admin]]
[com.walmartlabs.lacinia.util :refer [attach-resolvers]]
[auto-ap.graphql.utils :refer [attach-tracing-resolvers]]))
@@ -14,19 +14,18 @@
(defn get-all-sales-orders [context args _]
(assert-admin (:id context))
(map
- ->graphql
- (first (d-sales-orders2/get-graphql (assoc (<-graphql args) :count Integer/MAX_VALUE)))))
-
+ ->graphql
+ (first (d-sales-orders2/get-graphql (assoc (<-graphql args) :count Integer/MAX_VALUE)))))
(def objects
{:sales_order_page
{:fields {:sales_orders {:type '(list :sales_order)}
- :count {:type 'Int}
- :total {:type 'Int}
- :start {:type 'Int}
- :end {:type 'Int}
- :sales_order_total {:type :money}
- :sales_order_tax {:type :money}}}
+ :count {:type 'Int}
+ :total {:type 'Int}
+ :start {:type 'Int}
+ :end {:type 'Int}
+ :sales_order_total {:type :money}
+ :sales_order_tax {:type :money}}}
:sales_order
{:fields {:id {:type :id}
@@ -93,8 +92,7 @@
(def resolvers
{:get-all-sales-orders get-all-sales-orders
- :get-sales-order-page get-sales-orders-page
- })
+ :get-sales-order-page get-sales-orders-page})
(defn attach [schema]
(->
diff --git a/src/clj/auto_ap/graphql/transaction_rules.clj b/src/clj/auto_ap/graphql/transaction_rules.clj
index ed2a1c5e..4544973a 100644
--- a/src/clj/auto_ap/graphql/transaction_rules.clj
+++ b/src/clj/auto_ap/graphql/transaction_rules.clj
@@ -29,9 +29,8 @@
(defn get-transaction-rule-matches [context args _]
(if (= "admin" (:user/role (:id context)))
(let [transaction (update (d-transactions/get-by-id (:transaction_id args)) :transaction/date c/to-date)
- all-rules (tr/get-all-for-client (:db/id (:transaction/client transaction)))
+ all-rules (tr/get-all-for-client (:db/id (:transaction/client transaction)))]
- ]
(mu/log ::counted
:count (count all-rules))
(doto (map ->graphql (rm/get-matching-rules transaction all-rules)) (#(println (count %)))))
@@ -43,7 +42,7 @@
:account account_id
:location location})
-(defn delete-transaction-rule [context {:keys [transaction_rule_id ]} _]
+(defn delete-transaction-rule [context {:keys [transaction_rule_id]} _]
(assert-admin (:id context))
(let [existing-transaction-rule (tr/get-by-id transaction_rule_id)]
(when-not (:transaction-rule/description existing-transaction-rule)
@@ -59,62 +58,59 @@
(. java.util.regex.Pattern (compile description java.util.regex.Pattern/CASE_INSENSITIVE))
(catch Exception e
(throw (ex-info (ex-message e) {:validation-error (ex-message e)}))))
- _ (when-not (dollars= 1.0 account-total)
+ _ (when-not (dollars= 1.0 account-total)
(let [error (str "Account total (" account-total ") does not reach 100%")]
(throw (ex-info error {:validation-error error}))))
_ (when (and (str/blank? description)
- (nil? yodlee_merchant_id))
+ (nil? yodlee_merchant_id))
(let [error (str "You must provide a description or a yodlee merchant")]
(throw (ex-info error {:validation-error error}))))
_ (doseq [a accounts
:let [{:keys [:account/location :account/name]} (dc/pull (dc/db conn) [:account/location :account/name] (:account_id a))
- client (dc/pull (dc/db conn) [:client/locations] client_id)
- ]]
+ client (dc/pull (dc/db conn) [:client/locations] client_id)]]
(when (and location (not= location (:location a)))
(let [err (str "Account " name " uses location " (:location a) ", but is supposed to be " location)]
- (throw (ex-info err {:validation-error err}) )))
-
+ (throw (ex-info err {:validation-error err}))))
+
(when (and (not location)
(not (get (into #{"Shared"} (:client/locations client))
(:location a))))
(let [err (str "Account " name " uses location " (:location a) ", but doesn't belong to the client.")]
- (throw (ex-info err {:validation-error err}) ))))
+ (throw (ex-info err {:validation-error err})))))
rule-id (if id
id
"transaction-rule")
transaction [[:upsert-entity #:transaction-rule {:db/id (or rule-id (random-tempid))
- :description description
- :note note
- :client client_id
- :bank-account bank_account_id
- :yodlee-merchant yodlee_merchant_id
- :dom-lte dom_lte
- :dom-gte dom_gte
- :amount-lte amount_lte
- :amount-gte amount_gte
- :vendor vendor_id
- :transaction-approval-status
- (some->> transaction_approval_status
- name
- snake->kebab
- (keyword "transaction-approval-status"))
- :transaction-rule/accounts (map transaction-rule-account->entity accounts)}]]
-
+ :description description
+ :note note
+ :client client_id
+ :bank-account bank_account_id
+ :yodlee-merchant yodlee_merchant_id
+ :dom-lte dom_lte
+ :dom-gte dom_gte
+ :amount-lte amount_lte
+ :amount-gte amount_gte
+ :vendor vendor_id
+ :transaction-approval-status
+ (some->> transaction_approval_status
+ name
+ snake->kebab
+ (keyword "transaction-approval-status"))
+ :transaction-rule/accounts (map transaction-rule-account->entity accounts)}]]
transaction-result (audit-transact transaction (:id context))]
(-> (tr/get-by-id (or (-> transaction-result :tempids (get "transaction-rule"))
- id))
+ id))
((ident->enum-f :transaction-rule/transaction-approval-status))
(->graphql))))
(defn -test-transaction-rule [id {:keys [:transaction-rule/description :transaction-rule/client :transaction-rule/bank-account :transaction-rule/amount-lte :transaction-rule/amount-gte :transaction-rule/dom-lte :transaction-rule/dom-gte :transaction-rule/yodlee-merchant]} include-coded? count]
(let [query (cond-> {:query {:find ['(pull ?e [* {:transaction/client [:client/name]
:transaction/bank-account [:bank-account/name]
- :transaction/payment [:db/id]}
- ])]
- :in ['$ ]
+ :transaction/payment [:db/id]}])]
+ :in ['$]
:where []}
- :args [(dc/db conn)]}
+ :args [(dc/db conn)]}
description
(merge-query {:query {:in ['?descr]
:where ['[(iol-ion.query/->pattern ?descr) ?description-regex]]}
@@ -170,23 +166,22 @@
:where ['[?e :transaction/client ?client-id]]}
:args [(:db/id client)]})
-
(not include-coded?)
(merge-query {:query {:where ['[or [?e :transaction/approval-status :transaction-approval-status/unapproved]
[(missing? $ ?e :transaction/approval-status)]]]}})
true
(merge-query {:query {:where ['[?e :transaction/id]]}}))]
- (->>
- (query2 query)
- (transduce (comp
- (take (or count 15))
- (map first)
- (map #(dissoc % :transaction/id))
- (map (fn [x]
- (update x :transaction/date c/from-date)))
- (map ->graphql))
- conj []))))
+ (->>
+ (query2 query)
+ (transduce (comp
+ (take (or count 15))
+ (map first)
+ (map #(dissoc % :transaction/id))
+ (map (fn [x]
+ (update x :transaction/date c/from-date)))
+ (map ->graphql))
+ conj []))))
(defn test-transaction-rule [{:keys [id]} {{:keys [description client_id bank_account_id amount_lte amount_gte dom_lte dom_gte yodlee_merchant_id]} :transaction_rule} _]
(assert-admin id)
@@ -200,7 +195,6 @@
:yodlee-merchant (when yodlee_merchant_id {:db/id yodlee_merchant_id})}
true 15))
-
(defn run-transaction-rule [{:keys [id]} {:keys [transaction_rule_id count]} _]
(assert-admin id)
(-test-transaction-rule id (tr/get-by-id transaction_rule_id) false count))
diff --git a/src/clj/auto_ap/graphql/transactions.clj b/src/clj/auto_ap/graphql/transactions.clj
index a248ea73..a968a0e5 100644
--- a/src/clj/auto_ap/graphql/transactions.clj
+++ b/src/clj/auto_ap/graphql/transactions.clj
@@ -66,14 +66,14 @@
(defn get-ids-matching-filters [args]
(alog/info ::getting-ids-matching-filters
- :args args)
+ :args args)
(let [ids (some-> (:filters args)
(assoc :clients (:clients args))
(assoc :id (:id args))
(<-graphql)
(update :approval-status enum->keyword "transaction-approval-status")
(assoc :per-page Integer/MAX_VALUE)
- (d-transactions/raw-graphql-ids )
+ (d-transactions/raw-graphql-ids)
:ids)
specific-ids (d-transactions/filter-ids (seq (:ids args)))]
(if (seq (:ids args))
@@ -83,13 +83,13 @@
(defn all-ids-not-locked [all-ids]
(->> all-ids
(dc/q '[:find ?t
- :in $ [?t ...]
- :where
- [?t :transaction/client ?c]
- [(get-else $ ?c :client/locked-until #inst "2000-01-01") ?lu]
- [?t :transaction/date ?d]
- [(>= ?d ?lu)]]
- (dc/db conn))
+ :in $ [?t ...]
+ :where
+ [?t :transaction/client ?c]
+ [(get-else $ ?c :client/locked-until #inst "2000-01-01") ?lu]
+ [?t :transaction/date ?d]
+ [(>= ?d ?lu)]]
+ (dc/db conn))
(map first)))
(defn bulk-change-status [context args _]
(let [_ (assert-admin (:id context))
@@ -98,47 +98,46 @@
all-ids-not-locked)]
(alog/info ::bulk-change-status
- :count (count all-ids)
- :sample (take 3 all-ids)
- :status (:status args)
- )
+ :count (count all-ids)
+ :sample (take 3 all-ids)
+ :status (:status args))
(audit-transact-batch
(->> all-ids
(mapv (fn [t]
[:upsert-transaction {:db/id t
:transaction/approval-status (enum->keyword (:status args) "transaction-approval-status")}])))
-
+
(:id context))
- {:message (str "Succesfully changed " (count all-ids) " transactions to be " (name (:status args) ) ".")}))
+ {:message (str "Succesfully changed " (count all-ids) " transactions to be " (name (:status args)) ".")}))
;; TODO very similar to rule-matching
(defn maybe-code-accounts [transaction account-rules valid-locations]
(with-precision 2
(let [accounts (vec (mapcat
- (fn [ar]
- (let [cents-to-distribute (int (Math/round (Math/abs (* (:percentage ar)
- (:transaction/amount transaction)
- 100))))]
- (if (= "Shared" (:location ar))
- (->> valid-locations
- (map
- (fn [cents location]
- {:db/id (random-tempid)
- :transaction-account/account (:account_id ar)
- :transaction-account/amount (* 0.01 cents)
- :transaction-account/location location})
- (rm/spread-cents cents-to-distribute (count valid-locations))))
- [(cond-> {:db/id (random-tempid)
- :transaction-account/account (:account_id ar)
- :transaction-account/amount (* 0.01 cents-to-distribute)}
- (:location ar) (assoc :transaction-account/location (:location ar)))])))
- account-rules))
+ (fn [ar]
+ (let [cents-to-distribute (int (Math/round (Math/abs (* (:percentage ar)
+ (:transaction/amount transaction)
+ 100))))]
+ (if (= "Shared" (:location ar))
+ (->> valid-locations
+ (map
+ (fn [cents location]
+ {:db/id (random-tempid)
+ :transaction-account/account (:account_id ar)
+ :transaction-account/amount (* 0.01 cents)
+ :transaction-account/location location})
+ (rm/spread-cents cents-to-distribute (count valid-locations))))
+ [(cond-> {:db/id (random-tempid)
+ :transaction-account/account (:account_id ar)
+ :transaction-account/amount (* 0.01 cents-to-distribute)}
+ (:location ar) (assoc :transaction-account/location (:location ar)))])))
+ account-rules))
accounts (mapv
- (fn [a]
- (update a :transaction-account/amount
- #(with-precision 2
- (double (.setScale (bigdec %) 2 java.math.RoundingMode/HALF_UP)))))
- accounts)
+ (fn [a]
+ (update a :transaction-account/amount
+ #(with-precision 2
+ (double (.setScale (bigdec %) 2 java.math.RoundingMode/HALF_UP)))))
+ accounts)
leftover (with-precision 2 (.round (bigdec (- (Math/abs (:transaction/amount transaction))
(Math/abs (reduce + 0.0 (map #(:transaction-account/amount %) accounts)))))
*math-context*))
@@ -152,13 +151,13 @@
(when-not (seq (:clients context))
(throw (ex-info "Client is required"
{:validation-error "Client is required"})))
- (let [args (assoc args :clients (:clients context) :id (:id context))
+ (let [args (assoc args :clients (:clients context) :id (:id context))
client->locations (->> (:clients context)
- (map :db/id )
+ (map :db/id)
(dc/q
- '[:find (pull ?e [:db/id :client/locations])
- :in $ [?e ...]]
- (dc/db conn))
+ '[:find (pull ?e [:db/id :client/locations])
+ :in $ [?e ...]]
+ (dc/db conn))
(map (fn [[client]]
[(:db/id client) (:client/locations client)]))
(into {}))
@@ -166,41 +165,40 @@
transactions (pull-many (dc/db conn) [:db/id :transaction/amount {:transaction/client [:db/id]}] (vec all-ids))
account-total (reduce + 0 (map (fn [x] (:percentage x)) (:accounts args)))]
(alog/info ::bulk-coding-transactions
- :count (count transactions)
- :sample (take 3 transactions))
+ :count (count transactions)
+ :sample (take 3 transactions))
(when
- (and
- (seq (:accounts args))
- (not (dollars= 1.0 account-total)))
- (let [error (str "Account total (" account-total ") does not reach 100%")]
- (throw (ex-info error {:validation-error error}))))
+ (and
+ (seq (:accounts args))
+ (not (dollars= 1.0 account-total)))
+ (let [error (str "Account total (" account-total ") does not reach 100%")]
+ (throw (ex-info error {:validation-error error}))))
(doseq [a (:accounts args)
:let [{:keys [:account/location :account/name]} (dc/pull (dc/db conn)
[:account/location :account/name]
(:account_id a))]]
(when (and location (not= location (:location a)))
(let [err (str "Account " name " uses location " (:location a) ", but is supposed to be " location)]
- (throw (ex-info err {:validation-error err}) )))
+ (throw (ex-info err {:validation-error err}))))
(doseq [[_ locations] client->locations]
(when (and (not location)
(not (get (into #{"Shared"} locations)
(:location a))))
(let [err (str "Account " name " uses location " (:location a) ", but doesn't belong to the client.")]
- (throw (ex-info err {:validation-error err}) )))))
+ (throw (ex-info err {:validation-error err}))))))
(audit-transact-batch
- (map (fn [t]
- (let [locations (client->locations (-> t :transaction/client :db/id))]
- (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))
+ (map (fn [t]
+ (let [locations (client->locations (-> t :transaction/client :db/id))]
+ (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.")}))
-
(defn delete-transactions [context args _]
(let [_ (assert-admin (:id context))
args (assoc args :clients (:clients context))
@@ -208,24 +206,24 @@
db (dc/db conn)]
(alog/info ::bulk-delete-transactions
- :count (count all-ids)
- :sample (take 3 all-ids))
+ :count (count all-ids)
+ :sample (take 3 all-ids))
(audit-transact-batch
- (mapcat (fn [i]
- (let [transaction (dc/pull db [:transaction/payment
- :transaction/expected-deposit
- :db/id] i)
- payment-id (-> transaction :transaction/payment :db/id)
- expected-deposit-id (-> transaction :transaction/expected-deposit :db/id)]
- (cond->> [[:db/retractEntity [:journal-entry/original-entity i]]]
- payment-id (into [{:db/id payment-id
- :payment/status :payment-status/pending}
- [:db/retract (:db/id transaction) :transaction/payment payment-id]])
- expected-deposit-id (into [{:db/id expected-deposit-id
- :expected-deposit/status :expected-deposit-status/pending}
- [:db/retract (:db/id transaction) :transaction/expected-deposit expected-deposit-id]]))))
- all-ids)
- (:id context))
+ (mapcat (fn [i]
+ (let [transaction (dc/pull db [:transaction/payment
+ :transaction/expected-deposit
+ :db/id] i)
+ payment-id (-> transaction :transaction/payment :db/id)
+ expected-deposit-id (-> transaction :transaction/expected-deposit :db/id)]
+ (cond->> [[:db/retractEntity [:journal-entry/original-entity i]]]
+ payment-id (into [{:db/id payment-id
+ :payment/status :payment-status/pending}
+ [:db/retract (:db/id transaction) :transaction/payment payment-id]])
+ expected-deposit-id (into [{:db/id expected-deposit-id
+ :expected-deposit/status :expected-deposit-status/pending}
+ [:db/retract (:db/id transaction) :transaction/expected-deposit expected-deposit-id]]))))
+ all-ids)
+ (:id context))
(audit-transact-batch
(mapcat (fn [i]
(let [transaction-tx (if (:suppress args)
@@ -242,21 +240,21 @@
(assert-power-user (:id context))
(let [transaction (d-transactions/get-by-id (:transaction_id args))
- _ (assert-can-see-client (:id context) (:transaction/client transaction) )
+ _ (assert-can-see-client (:id context) (:transaction/client transaction))
matches-set (i-transactions/match-transaction-to-unfulfilled-autopayments (:transaction/amount transaction)
(:db/id (:transaction/client transaction)))]
(->graphql (for [matches matches-set]
- (for [[_ invoice-id ] matches]
+ (for [[_ invoice-id] matches]
(d-invoices/get-by-id invoice-id))))))
(defn get-potential-unpaid-invoices-matches [context args _]
(assert-power-user (:id context))
(let [transaction (d-transactions/get-by-id (:transaction_id args))
- _ (assert-can-see-client (:id context) (:transaction/client transaction) )
+ _ (assert-can-see-client (:id context) (:transaction/client transaction))
matches-set (i-transactions/match-transaction-to-unpaid-invoices (:transaction/amount transaction)
(:db/id (:transaction/client transaction)))]
(->graphql (for [matches matches-set]
- (for [[_ invoice-id ] matches]
+ (for [[_ invoice-id] matches]
(d-invoices/get-by-id invoice-id))))))
(defn unlink-transaction [context args _]
@@ -264,20 +262,20 @@
args (assoc args :id (:id context))
transaction-id (:transaction_id args)
transaction (dc/pull (dc/db conn)
- [:transaction/approval-status
- :transaction/status
- :transaction/date
- :transaction/location
- :transaction/vendor
- :transaction/accounts
- :transaction/client [:db/id]
- {:transaction/payment [:payment/date {:payment/status [:db/ident]} :db/id]} ]
- transaction-id)
- _ (assert-can-see-client (:id context) (:transaction/client transaction) )
+ [:transaction/approval-status
+ :transaction/status
+ :transaction/date
+ :transaction/location
+ :transaction/vendor
+ :transaction/accounts
+ :transaction/client [:db/id]
+ {:transaction/payment [:payment/date {:payment/status [:db/ident]} :db/id]}]
+ transaction-id)
+ _ (assert-can-see-client (:id context) (:transaction/client transaction))
_ (assert-not-locked (:db/id (:transaction/client transaction)) (:transaction/date transaction))
_ (when (:transaction/payment transaction)
(assert-not-locked (:db/id (:transaction/client transaction)) (-> transaction :transaction/payment :payment/date)))
- payment (-> transaction :transaction/payment )
+ payment (-> transaction :transaction/payment)
is-autopay-payment? (some->> (dc/q {:find ['?sp]
:in ['$ '?payment]
:where ['[?ip :invoice-payment/payment ?payment]
@@ -286,8 +284,7 @@
(dc/db conn) (:db/id payment))
seq
(map first)
- (every? #(instance? java.util.Date %)))
- ]
+ (every? #(instance? java.util.Date %)))]
(alog/info ::unlinking :transaction (pr-str transaction) :autopay is-autopay-payment? :payment (pr-str payment))
@@ -295,49 +292,47 @@
(throw (ex-info "Payment can't be undone because it isn't cleared." {:validation-error "Payment can't be undone because it isn't cleared."})))
(if is-autopay-payment?
(audit-transact
- (-> [{:db/id (:db/id payment)
- :payment/status :payment-status/pending}
- [:upsert-transaction
- {:db/id transaction-id
- :transaction/approval-status :transaction-approval-status/unapproved
- :transaction/payment nil
- :transaction/vendor nil
- :transaction/location nil
- :transaction/accounts nil}]
+ (-> [{:db/id (:db/id payment)
+ :payment/status :payment-status/pending}
+ [:upsert-transaction
+ {:db/id transaction-id
+ :transaction/approval-status :transaction-approval-status/unapproved
+ :transaction/payment nil
+ :transaction/vendor nil
+ :transaction/location nil
+ :transaction/accounts nil}]
- [:db/retractEntity (:db/id payment) ]]
+ [:db/retractEntity (:db/id payment)]]
- (into (map (fn [[invoice-payment]]
- [:db/retractEntity invoice-payment])
- (dc/q {:find ['?ip]
- :in ['$ '?p]
- :where ['[?ip :invoice-payment/payment ?p]]}
- (dc/db conn)
- (:db/id payment) ))))
+ (into (map (fn [[invoice-payment]]
+ [:db/retractEntity invoice-payment])
+ (dc/q {:find ['?ip]
+ :in ['$ '?p]
+ :where ['[?ip :invoice-payment/payment ?p]]}
+ (dc/db conn)
+ (:db/id payment)))))
(:id context))
(audit-transact
- [{:db/id (:db/id payment)
- :payment/status :payment-status/pending}
- [:upsert-transaction
- {:db/id transaction-id
- :transaction/approval-status :transaction-approval-status/unapproved
- :transaction/payment nil
- :transaction/vendor nil
- :transaction/location nil
- :transaction/accounts nil}]]
+ [{:db/id (:db/id payment)
+ :payment/status :payment-status/pending}
+ [:upsert-transaction
+ {:db/id transaction-id
+ :transaction/approval-status :transaction-approval-status/unapproved
+ :transaction/payment nil
+ :transaction/vendor nil
+ :transaction/location nil
+ :transaction/accounts nil}]]
(:id context)))
(-> (d-transactions/get-by-id transaction-id)
approval-status->graphql
->graphql)))
-
(defn transaction-account->entity [{:keys [id account_id amount location]}]
#:transaction-account {:amount amount
:db/id (or id (random-tempid))
:account account_id
:location location})
-
(defn assert-valid-expense-accounts [accounts]
(doseq [trans-account accounts
:let [account (dc/pull (dc/db conn)
@@ -351,7 +346,7 @@
(:account/location account)))
(let [err (str "Account uses location '" (:location trans-account) "' but expects '" (:account/location account) "'")]
(throw (ex-info err
- {:validation-error err}))))
+ {:validation-error err}))))
(when (and (empty? (:account/location account))
(= "A" (:location trans-account)))
@@ -359,13 +354,12 @@
(throw (ex-info err
{:validation-error err}))))
-
(when (nil? (:account_id trans-account))
(throw (ex-info "Account is missing account" {:validation-error "Account is missing account"})))))
(defn edit-transaction [context {{:keys [id accounts vendor_id approval_status memo forecast_match]} :transaction} _]
(let [existing-transaction (d-transactions/get-by-id id)
- _ (assert-can-see-client (:id context) (:transaction/client existing-transaction) )
+ _ (assert-can-see-client (:id context) (:transaction/client existing-transaction))
_ (assert-valid-expense-accounts accounts)
_ (assert-not-locked (:db/id (:transaction/client existing-transaction)) (:transaction/date existing-transaction))
account-total (reduce + 0 (map (fn [x] (:amount x)) accounts))
@@ -378,17 +372,17 @@
set
(conj "A")
(conj "HQ"))))]
-
+
(when (and (not (dollars= (Math/abs (:transaction/amount existing-transaction)) account-total))
(or
(and (= approval_status :unapproved)
(> (count accounts) 0))
- (not= approval_status :unapproved)))
+ (not= approval_status :unapproved)))
(let [error (str "Expense account total (" account-total ") does not equal transaction total (" (Math/abs (:transaction/amount existing-transaction)) ")")]
- (throw (ex-info error {:validation-error error}))))
+ (throw (ex-info error {:validation-error error}))))
(when missing-locations
- (throw (ex-info (str "Location '" (str/join ", " missing-locations) "' not found on client.") {})) )
-
+ (throw (ex-info (str "Location '" (str/join ", " missing-locations) "' not found on client.") {})))
+
(audit-transact (cond-> [[:upsert-transaction {:db/id id
:transaction/vendor vendor_id
:transaction/memo memo
@@ -413,8 +407,8 @@
(defn match-transaction [context {:keys [transaction_id payment_id]} _]
(let [transaction (d-transactions/get-by-id transaction_id)
payment (d-checks/get-by-id payment_id)
- _ (assert-can-see-client (:id context) (:transaction/client transaction) )
- _ (assert-can-see-client (:id context) (:payment/client payment) )
+ _ (assert-can-see-client (:id context) (:transaction/client transaction))
+ _ (assert-can-see-client (:id context) (:payment/client payment))
_ (assert-not-locked (:db/id (:transaction/client transaction)) (:transaction/date transaction))]
(when (not= (:db/id (:transaction/client transaction))
(:db/id (:payment/client payment)))
@@ -423,7 +417,7 @@
(when-not (dollars= (- (:transaction/amount transaction))
(:payment/amount payment))
(throw (ex-info "Amounts don't match" {:validation-error "Amounts don't match"})))
- (audit-transact (into
+ (audit-transact (into
[{:db/id (:db/id payment)
:payment/status :payment-status/cleared
:payment/date (coerce/to-date (first (sort [(:payment/date payment)
@@ -431,14 +425,14 @@
[:upsert-transaction
{:db/id (:db/id transaction)
- :transaction/payment (:db/id payment)
- :transaction/vendor (:db/id (:payment/vendor payment))
- :transaction/location "A"
- :transaction/approval-status :transaction-approval-status/approved
- :transaction/accounts [{:db/id (random-tempid)
- :transaction-account/account (:db/id (a/get-account-by-numeric-code-and-sets 21000 ["default"]))
- :transaction-account/location "A"
- :transaction-account/amount (Math/abs (:transaction/amount transaction))}]}]])
+ :transaction/payment (:db/id payment)
+ :transaction/vendor (:db/id (:payment/vendor payment))
+ :transaction/location "A"
+ :transaction/approval-status :transaction-approval-status/approved
+ :transaction/accounts [{:db/id (random-tempid)
+ :transaction-account/account (:db/id (a/get-account-by-numeric-code-and-sets 21000 ["default"]))
+ :transaction-account/location "A"
+ :transaction-account/amount (Math/abs (:transaction/amount transaction))}]}]])
(:id context)))
(solr/touch-with-ledger transaction_id)
(-> (d-transactions/get-by-id transaction_id)
@@ -448,7 +442,7 @@
(defn match-transaction-autopay-invoices [context {:keys [transaction_id autopay_invoice_ids]} _]
(let [_ (assert-power-user (:id context))
transaction (d-transactions/get-by-id transaction_id)
- _ (assert-can-see-client (:id context) (:transaction/client transaction) )
+ _ (assert-can-see-client (:id context) (:transaction/client transaction))
db (dc/db conn)
invoice-clients (set (map #(pull-ref db :invoice/client %) autopay_invoice_ids))
invoice-amount (reduce + 0.0 (map #(pull-attr db :invoice/total %) autopay_invoice_ids))
@@ -474,9 +468,9 @@
(:db/id (:transaction/bank-account transaction))
(:db/id (:transaction/client transaction)))]
(alog/info ::adding-payment-from-autopay-invoice
- :payment (pr-str payment-tx))
+ :payment (pr-str payment-tx))
(audit-transact payment-tx (:id context)))
- (solr/touch-with-ledger transaction_id)
+ (solr/touch-with-ledger transaction_id)
(-> (d-transactions/get-by-id transaction_id)
approval-status->graphql
->graphql)))
@@ -485,8 +479,8 @@
(defn match-transaction-unpaid-invoices [context {:keys [transaction_id unpaid_invoice_ids]} _]
(let [_ (assert-power-user (:id context))
transaction (d-transactions/get-by-id transaction_id)
-
- _ (assert-can-see-client (:id context) (:transaction/client transaction) )
+
+ _ (assert-can-see-client (:id context) (:transaction/client transaction))
_ (assert-not-locked (:db/id (:transaction/client transaction)) (:transaction/date transaction))
db (dc/db conn)
invoice-clients (set (map #(pull-ref db :invoice/client %) unpaid_invoice_ids))
@@ -502,17 +496,17 @@
(throw (ex-info "Amounts don't match" {:validation-error "Amounts don't match"})))
(when (:transaction/payment transaction)
(throw (ex-info "Transaction already linked" {:validation-error "Transaction already linked"})))
-
+
(let [payment-tx (i-transactions/add-new-payment (dc/pull db [:transaction/amount :transaction/date :db/id] transaction_id)
- (map (fn [id]
- (let [entity (dc/pull db [:invoice/vendor :db/id :invoice/total] id)]
- [(or (-> entity :invoice/vendor :db/id)
- (-> entity :invoice/vendor))
- (-> entity :db/id)
- (-> entity :invoice/total)]))
- unpaid_invoice_ids)
- (:db/id (:transaction/bank-account transaction))
- (:db/id (:transaction/client transaction)))]
+ (map (fn [id]
+ (let [entity (dc/pull db [:invoice/vendor :db/id :invoice/total] id)]
+ [(or (-> entity :invoice/vendor :db/id)
+ (-> entity :invoice/vendor))
+ (-> entity :db/id)
+ (-> entity :invoice/total)]))
+ unpaid_invoice_ids)
+ (:db/id (:transaction/bank-account transaction))
+ (:db/id (:transaction/client transaction)))]
(audit-transact payment-tx (:id context)))
(solr/touch-with-ledger transaction_id)
@@ -527,9 +521,8 @@
:count Integer/MAX_VALUE} nil)
(filter #(not (:payment %)))
- (map :id ))
+ (map :id))
-
transaction_ids)
_ (mu/log ::here :txids transaction_ids)
transaction_ids (all-ids-not-locked transaction_ids)
@@ -553,17 +546,16 @@
(audit-transact (mapv (fn [t]
[:upsert-transaction
(remove-nils (rm/apply-rule {:db/id (:db/id t)
- :transaction/amount (:transaction/amount t)}
- transaction-rule
+ :transaction/amount (:transaction/amount t)}
+ transaction-rule
- (or (-> t :transaction/bank-account :bank-account/locations)
- (-> t :transaction/client :client/locations))))])
+ (or (-> t :transaction/bank-account :bank-account/locations)
+ (-> t :transaction/client :client/locations))))])
transactions)
(:id context))
(doseq [n transactions]
- (solr/touch-with-ledger (:db/id n)))
- )
+ (solr/touch-with-ledger (:db/id n))))
(transduce
(comp
(map d-transactions/get-by-id)
@@ -571,12 +563,12 @@
(map ->graphql))
conj
[]
- transaction_ids ))
+ transaction_ids))
(def objects
{:transaction {:fields {:id {:type :id}
:amount {:type 'String}
- :memo {:type 'String}
+ :memo {:type 'String}
:is_locked {:type 'Boolean}
:description_original {:type 'String}
:description_simple {:type 'String}
@@ -628,8 +620,8 @@
:resolve :mutation/bulk-code-transactions}
:delete_transactions {:type :message
:args {:filters {:type :transaction_filters}
- :ids {:type '(list :id)}
- :suppress {:type 'Boolean}}
+ :ids {:type '(list :id)}
+ :suppress {:type 'Boolean}}
:resolve :mutation/delete-transactions}
:edit_transaction {:type :transaction
:args {:transaction {:type :edit_transaction}}
@@ -711,9 +703,8 @@
:mutation/match-transaction-unpaid-invoices match-transaction-unpaid-invoices
:mutation/match-transaction-rules match-transaction-rules})
-
(defn attach [schema]
- (->
+ (->
(merge-with merge schema
{:objects objects
:queries queries
diff --git a/src/clj/auto_ap/graphql/utils.clj b/src/clj/auto_ap/graphql/utils.clj
index 056f6ac7..8211626f 100644
--- a/src/clj/auto_ap/graphql/utils.clj
+++ b/src/clj/auto_ap/graphql/utils.clj
@@ -13,7 +13,6 @@
[iol-ion.query :refer [entid]]
[slingshot.slingshot :refer [throw+]]))
-
(defn snake->kebab [s]
(str/replace s #"_" "-"))
@@ -107,8 +106,6 @@
(#{"manager" "user" "power-user" "read-only"} (:user/role id))
(:user/clients id [])))
-
-
(defn result->page [results result-count key args]
{key (map ->graphql results)
:total result-count
@@ -197,7 +194,6 @@
(= :client/code (first x)))
[(entid (dc/db conn) x)]
-
(sequential? x)
(mapcat coerce-client-ids x)
@@ -218,14 +214,14 @@
e)))))
(defn exception->4xx [f]
- (try
+ (try
(f)
(catch Throwable e
-(throw+ (ex-info (.getMessage e) {:type :form-validation
- :form-validation-errors [(.getMessage e)]}))
+ (throw+ (ex-info (.getMessage e) {:type :form-validation
+ :form-validation-errors [(.getMessage e)]}))
#_(throw (ex-info (.getMessage e)
- {:type :notification}
- e)))))
+ {:type :notification}
+ e)))))
(defn notify-if-locked [client-id date]
(try
diff --git a/src/clj/auto_ap/graphql/vendors.clj b/src/clj/auto_ap/graphql/vendors.clj
index 7cface3c..454f1bc1 100644
--- a/src/clj/auto_ap/graphql/vendors.clj
+++ b/src/clj/auto_ap/graphql/vendors.clj
@@ -130,7 +130,6 @@
:vendor/schedule-payment-dom schedule-payment-dom
:vendor/automatically-paid-when-due (:automatically_paid_when_due in)))]
-
transaction-result (audit-transact [transaction] (:id context))
new-vendor (d-vendors/get-by-id (or (-> transaction-result :tempids (get "vendor"))
id))]
@@ -160,7 +159,6 @@
(audit-transact transaction (:id context))
to))
-
(defn get-graphql [context args _]
(assert-admin (:id context))
(let [args (assoc args :id (:id context))
@@ -187,7 +185,6 @@
(if-let [query (not-empty (cleanse-query (:query args)))]
(let [search-query (str "name:(" query ")")]
-
(for [{:keys [id name]} (solr/query solr/impl "vendors" {"query" (cond-> search-query
(not (is-admin? (:id context))) (str " hidden:false"))
"fields" "id, name"})]
diff --git a/src/clj/auto_ap/handler.clj b/src/clj/auto_ap/handler.clj
index 8f02a10b..d5afdfa8 100644
--- a/src/clj/auto_ap/handler.clj
+++ b/src/clj/auto_ap/handler.clj
@@ -70,7 +70,6 @@
:headers {}
:body ""})
-
(defn home-handler [{:keys [identity]}]
(if identity
{:status 302
@@ -78,7 +77,6 @@
{:status 302
:headers {"Location" "/login"}}))
-
(def match->handler-lookup
(-> {:not-found not-found
:home home-handler}
@@ -90,15 +88,13 @@
(merge yodlee2/match->handler)
(merge auth/match->handler)
(merge invoices/match->handler)
- (merge exports/match->handler)
- ))
+ (merge exports/match->handler)))
(def match->handler
(fn [route]
(or (get match->handler-lookup route)
route)))
-
(def route-handler
(make-handler all-routes
match->handler))
@@ -125,7 +121,6 @@
uri
:request-method method))
-
(def auth-backend (jws-backend {:secret (:jwt-secret env) :options {:alg :hs512}}))
(defn wrap-logging [handler]
@@ -159,8 +154,6 @@
:exception e)
(throw e)))))))
-
-
(defn wrap-idle-session-timeout
[handler]
(fn [request]
diff --git a/src/clj/auto_ap/import/common.clj b/src/clj/auto_ap/import/common.clj
index a296294b..472dc02b 100644
--- a/src/clj/auto_ap/import/common.clj
+++ b/src/clj/auto_ap/import/common.clj
@@ -9,21 +9,21 @@
(random-tempid)))
(defn wrap-integration [f bank-account]
- (try
+ (try
(let [result (f)]
@(dc/transact-async conn [{:db/id bank-account
- :bank-account/integration-status
- {:db/id (bank-account->integration-id bank-account)
- :integration-status/state :integration-state/success
- :integration-status/last-attempt (java.util.Date.)
- :integration-status/last-updated (java.util.Date.)}}])
+ :bank-account/integration-status
+ {:db/id (bank-account->integration-id bank-account)
+ :integration-status/state :integration-state/success
+ :integration-status/last-attempt (java.util.Date.)
+ :integration-status/last-updated (java.util.Date.)}}])
result)
(catch Exception e
@(dc/transact-async conn [{:db/id bank-account
- :bank-account/integration-status
- {:db/id (bank-account->integration-id bank-account)
- :integration-status/state :integration-state/failed
- :integration-status/last-attempt (java.util.Date.)
- :integration-status/message (.getMessage e)}}])
+ :bank-account/integration-status
+ {:db/id (bank-account->integration-id bank-account)
+ :integration-status/state :integration-state/failed
+ :integration-status/last-attempt (java.util.Date.)
+ :integration-status/message (.getMessage e)}}])
(alog/warn ::integration-failed :error e)
nil)))
diff --git a/src/clj/auto_ap/import/intuit.clj b/src/clj/auto_ap/import/intuit.clj
index ff4dd3e0..3b67f203 100644
--- a/src/clj/auto_ap/import/intuit.clj
+++ b/src/clj/auto_ap/import/intuit.clj
@@ -12,15 +12,15 @@
[datomic.api :as dc]
[iol-ion.utils :refer [remove-nils]]))
-(defn get-intuit-bank-accounts
- ( [db]
- (dc/q '[:find ?external-id ?ba ?c
- :in $
- :where
- [?c :client/bank-accounts ?ba]
- [?ba :bank-account/intuit-bank-account ?iab]
- [?iab :intuit-bank-account/external-id ?external-id]]
- db))
+(defn get-intuit-bank-accounts
+ ([db]
+ (dc/q '[:find ?external-id ?ba ?c
+ :in $
+ :where
+ [?c :client/bank-accounts ?ba]
+ [?ba :bank-account/intuit-bank-account ?iab]
+ [?iab :intuit-bank-account/external-id ?external-id]]
+ db))
([db & client-codes]
(dc/q '[:find ?external-id ?ba ?c
:in $ [?cc ...]
@@ -32,7 +32,6 @@
db
client-codes)))
-
(defn intuit->transaction [transaction]
(let [check-number (when (not (str/blank? (:Num transaction)))
(try
@@ -46,7 +45,6 @@
:transaction/status "POSTED"}
check-number (assoc :transaction/check-number check-number))))
-
(defn intuits->transactions [transactions bank-account-id client-id]
(->> transactions
(map intuit->transaction)
diff --git a/src/clj/auto_ap/import/intuit_test.clj b/src/clj/auto_ap/import/intuit_test.clj
index 4fe46121..fefbf1fc 100644
--- a/src/clj/auto_ap/import/intuit_test.clj
+++ b/src/clj/auto_ap/import/intuit_test.clj
@@ -11,10 +11,9 @@
(t/is (= #inst "2021-01-01T00:00:00-08:00" (:transaction/date (sut/intuit->transaction (assoc base-transaction :Date "2021-01-01")))))
(t/is (= #inst "2021-06-01T00:00:00-07:00" (:transaction/date (sut/intuit->transaction (assoc base-transaction :Date "2021-06-01")))))))
-
(t/deftest intuits->transactions
(t/testing "should give unique ids to duplicates"
(t/is (= ["2021-10-11T00:00:00.000-07:00-123-this is a description-45.23-0-345"
"2021-10-11T00:00:00.000-07:00-123-this is a description-45.23-1-345"] (map :transaction/raw-id (sut/intuits->transactions [base-transaction base-transaction]
- 123
- 345))))))
+ 123
+ 345))))))
diff --git a/src/clj/auto_ap/import/manual.clj b/src/clj/auto_ap/import/manual.clj
index 8f54b0b7..41885b4b 100644
--- a/src/clj/auto_ap/import/manual.clj
+++ b/src/clj/auto_ap/import/manual.clj
@@ -7,8 +7,6 @@
[clojure.data.csv :as csv]
[datomic.api :as dc]))
-
-
(def columns [:status :raw-date :description-original :high-level-category nil nil :amount nil nil nil nil nil :bank-account-code :client-code])
(defn tabulate-data [data]
@@ -33,12 +31,12 @@
(defn import-batch [transactions user]
(let [bank-account-code->client (into {}
- (dc/q '[:find ?bac ?c
- :in $
- :where
- [?c :client/bank-accounts ?ba]
- [?ba :bank-account/code ?bac]]
- (dc/db conn)))
+ (dc/q '[:find ?bac ?c
+ :in $
+ :where
+ [?c :client/bank-accounts ?ba]
+ [?ba :bank-account/code ?bac]]
+ (dc/db conn)))
bank-account-code->bank-account (into {}
(dc/q '[:find ?bac ?ba
:in $
@@ -46,9 +44,9 @@
(dc/db conn)))
import-batch (t/start-import-batch :import-source/manual user)
transactions (->> transactions
- (map (fn [t]
- (manual->transaction t bank-account-code->bank-account bank-account-code->client)))
- (t/apply-synthetic-ids ))]
+ (map (fn [t]
+ (manual->transaction t bank-account-code->bank-account bank-account-code->client)))
+ (t/apply-synthetic-ids))]
(try
(doseq [transaction transactions]
(when-not (seq (:errors transaction))
diff --git a/src/clj/auto_ap/import/manual/common.clj b/src/clj/auto_ap/import/manual/common.clj
index 41464ab5..d12a793d 100644
--- a/src/clj/auto_ap/import/manual/common.clj
+++ b/src/clj/auto_ap/import/manual/common.clj
@@ -22,9 +22,9 @@
(defn parse-date [{:keys [raw-date]}]
(when-not
- (re-find #"\d{1,2}/\d{1,2}/\d{4}" raw-date)
+ (re-find #"\d{1,2}/\d{1,2}/\d{4}" raw-date)
(throw (Exception. (str "Date " raw-date " must match MM/dd/yyyy"))))
- (try
+ (try
(parse-u/parse-value :clj-time "MM/dd/yyyy" raw-date)
(catch Exception e
diff --git a/src/clj/auto_ap/import/plaid.clj b/src/clj/auto_ap/import/plaid.clj
index d77223fc..6041c638 100644
--- a/src/clj/auto_ap/import/plaid.clj
+++ b/src/clj/auto_ap/import/plaid.clj
@@ -16,7 +16,7 @@
[manifold.deferred :as de]
[manifold.executor :as ex]))
-(defn get-plaid-accounts
+(defn get-plaid-accounts
([db]
(-> (dc/q '[:find ?ba ?c ?external-id ?t
:in $
@@ -40,7 +40,6 @@
db
client-codes))))
-
(defn plaid->transaction [t plaid-merchant->vendor-id]
(alog/info ::trying-transaction :transaction t)
(cond-> #:transaction {:description-original (:name t)
@@ -57,7 +56,7 @@
:db/id (random-tempid)})
(not (str/blank? (:check_number t))) (assoc :transaction/check-number (Long/parseLong (:check_number t)))
#_#_(plaid-merchant->vendor-id (:merchant_name t)) (assoc :transaction/default-vendor
- (plaid-merchant->vendor-id (:merchant_name t)))))
+ (plaid-merchant->vendor-id (:merchant_name t)))))
(defn build-plaid-merchant->vendor-id []
(into {}
@@ -66,23 +65,22 @@
:where
[?v :vendor/plaid-merchant ?pm]
[?pm :plaid-merchant/name ?pmn]]
- (dc/db conn ))))
-
+ (dc/db conn))))
(def single-thread (ex/fixed-thread-executor 1))
(defn rebuild-search-index []
(de/future-with
- single-thread
- (auto-ap.solr/index-documents-raw
- auto-ap.solr/impl
- "plaid_merchants"
- (for [[result] (dc/qseq {:query '[:find (pull ?v [:plaid-merchant/name :db/id])
- :in $
- :where [?v :plaid-merchant/name]]
- :args [(dc/db conn)]})]
- {"id" (:db/id result)
- "name" (:plaid-merchant/name result)}))))
+ single-thread
+ (auto-ap.solr/index-documents-raw
+ auto-ap.solr/impl
+ "plaid_merchants"
+ (for [[result] (dc/qseq {:query '[:find (pull ?v [:plaid-merchant/name :db/id])
+ :in $
+ :where [?v :plaid-merchant/name]]
+ :args [(dc/db conn)]})]
+ {"id" (:db/id result)
+ "name" (:plaid-merchant/name result)}))))
(defn upsert-accounts []
(try
@@ -96,20 +94,16 @@
(remove-nils
{:plaid-account/external-id (:account_id a)
:plaid-account/last-synced (coerce/to-date (coerce/to-date-time (-> item :status :transactions :last_successful_update))) :plaid-account/balance (or (some-> a
- :balances
- :current
- double)
- 0.0)}))))
+ :balances
+ :current
+ double)
+ 0.0)}))))
(catch Exception e
(alog/warn ::couldnt-upsert-account :error e))))
-
(catch Exception e
(alog/warn ::couldnt-upsert-accounts :error e))))
-
-
-
(defn import-plaid-int []
(let [_ (upsert-accounts)
import-batch (t/start-import-batch :import-source/plaid "Automated plaid user")
@@ -119,8 +113,8 @@
(try
(doseq [[bank-account-id client-id external-id access-token] (get-plaid-accounts (dc/db conn))
:let [transaction-result (wrap-integration #(p/get-transactions access-token external-id start end)
- bank-account-id)
- accounts-by-id (by :account_id (:accounts transaction-result))]
+ bank-account-id)
+ accounts-by-id (by :account_id (:accounts transaction-result))]
transaction (:transactions transaction-result)]
(when (not (:pending transaction))
(t/import-transaction! import-batch (assoc (plaid->transaction (assoc transaction
diff --git a/src/clj/auto_ap/import/transactions.clj b/src/clj/auto_ap/import/transactions.clj
index ae08abe1..c9d435c5 100644
--- a/src/clj/auto_ap/import/transactions.clj
+++ b/src/clj/auto_ap/import/transactions.clj
@@ -21,7 +21,7 @@
(if (and client-id bank-account-id amount)
(let [[matching-checks] (d-checks/get-graphql {:client-id client-id
:clients [client-id]
- :bank-account-id bank-account-id
+ :bank-account-id bank-account-id
:amount (- amount)
:status :payment-status/pending})]
(if (= 1 (count matching-checks))
@@ -29,7 +29,6 @@
nil))
nil))
-
(defn transaction->existing-payment [_ check-number client-id bank-account-id amount id]
(alog/info ::searching
:client-id client-id
@@ -46,7 +45,7 @@
check-number
(-> (d-checks/get-graphql {:client-id client-id
:clients [client-id]
- :bank-account-id bank-account-id
+ :bank-account-id bank-account-id
:check-number check-number
:amount (- amount)
:status :payment-status/pending})
@@ -70,12 +69,12 @@
(group-by first) ;; group by vendors
vals)
considerations (for [candidate-invoices candidate-invoices-vendor-groups
- invoice-count (range 1 32)
- consideration (partition invoice-count 1 candidate-invoices)
- :when (dollars= (reduce (fn [acc [_ _ amount]]
- (+ acc amount)) 0.0 consideration)
- (- amount))]
- consideration)]
+ invoice-count (range 1 32)
+ consideration (partition invoice-count 1 candidate-invoices)
+ :when (dollars= (reduce (fn [acc [_ _ amount]]
+ (+ acc amount)) 0.0 consideration)
+ (- amount))]
+ consideration)]
(alog/info ::unfulfilled-autoapayment-considerations
:count (count considerations)
:amount amount)
@@ -85,13 +84,13 @@
(alog/info ::searching-unpaid-invoice
:client-id client-id
:amount amount)
- (try
+ (try
(let [candidate-invoices-vendor-groups (->> (dc/q {:find ['?vendor-id '?e '?outstanding-balance '?d]
:in ['$ '?client-id]
:where ['[?e :invoice/client ?client-id]
'[?e :invoice/status :invoice-status/unpaid]
'(not [_ :invoice-payment/invoice ?e])
- '[?e :invoice/vendor ?vendor-id]
+ '[?e :invoice/vendor ?vendor-id]
'[?e :invoice/outstanding-balance ?outstanding-balance]
'[?e :invoice/date ?d]]}
(dc/db conn) client-id)
@@ -110,10 +109,10 @@
:amount amount
:count (count considerations))
considerations)
- (catch Exception e
- (alog/error ::cant-get-considerations
- :error e)
- [])))
+ (catch Exception e
+ (alog/error ::cant-get-considerations
+ :error e)
+ [])))
(defn match-transaction-to-single-unfulfilled-autopayments [amount client-id]
(let [considerations (match-transaction-to-unfulfilled-autopayments amount client-id)]
@@ -134,10 +133,10 @@
:transaction/location "A"
:transaction/accounts
[#:transaction-account
- {:db/id (random-tempid)
- :account (:db/id (a/get-account-by-numeric-code-and-sets 21000 ["default"]))
- :location "A"
- :amount (Math/abs (:transaction/amount transaction))}])]]
+ {:db/id (random-tempid)
+ :account (:db/id (a/get-account-by-numeric-code-and-sets 21000 ["default"]))
+ :location "A"
+ :amount (Math/abs (:transaction/amount transaction))}])]]
(conj {:payment/bank-account bank-account-id
:payment/client client-id
@@ -169,30 +168,26 @@
(= 1234 (extract-check-number {:transaction/description-original "Check abc 1234"}))
-(= 1234 (extract-check-number {:transaction/description-original "Check abc 4/10 1234"}))
-(= 1234 (extract-check-number {:transaction/description-original "Check abc 4/10 1234 12/3"}))
+ (= 1234 (extract-check-number {:transaction/description-original "Check abc 4/10 1234"}))
+ (= 1234 (extract-check-number {:transaction/description-original "Check abc 4/10 1234 12/3"}))
-(not= 1234 (extract-check-number {:transaction/description-original "Checkcard 4/10 1234"}))
-
- )
+ (not= 1234 (extract-check-number {:transaction/description-original "Checkcard 4/10 1234"})))
(defn find-expected-deposit [client-id amount date]
- (when date
+ (when date
(-> (dc/q
- '[:find (pull ?ed [:db/id {:expected-deposit/vendor [:db/id]}])
- :in $ ?c ?a ?d-start
+ '[:find (pull ?ed [:db/id {:expected-deposit/vendor [:db/id]}])
+ :in $ ?c ?a ?d-start
:where
[?ed :expected-deposit/client ?c]
(not [?ed :expected-deposit/status :expected-deposit-status/cleared])
[?ed :expected-deposit/date ?d]
[(>= ?d ?d-start)]
[?ed :expected-deposit/total ?a2]
- [(auto-ap.utils/dollars= ?a2 ?a)]
- ]
+ [(auto-ap.utils/dollars= ?a2 ?a)]]
(dc/db conn) client-id amount (coerce/to-date (t/plus date (t/days -10))))
ffirst)))
-
(defn categorize-transaction [transaction bank-account existing]
(cond (= :transaction-approval-status/suppressed (existing (:transaction/id transaction)))
:suppressed
@@ -235,7 +230,6 @@
(assoc transaction :transaction/check-number check-number)
transaction))
-
(defn maybe-clear-payment [{:transaction/keys [check-number client bank-account amount id] :as transaction}]
(when-let [existing-payment (transaction->existing-payment transaction check-number client bank-account amount id)]
(assoc transaction
@@ -245,10 +239,10 @@
:transaction/vendor (:db/id (:payment/vendor existing-payment))
:transaction/location "A"
:transaction/accounts [#:transaction-account
- {:db/id (random-tempid)
- :account (:db/id (a/get-account-by-numeric-code-and-sets 21000 ["default"]))
- :location "A"
- :amount (Math/abs (double amount))}])))
+ {:db/id (random-tempid)
+ :account (:db/id (a/get-account-by-numeric-code-and-sets 21000 ["default"]))
+ :location "A"
+ :amount (Math/abs (double amount))}])))
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
(defn maybe-autopay-invoices [{:transaction/keys [amount client bank-account] :as transaction}]
@@ -266,8 +260,7 @@
:transaction-account/amount amount
:transaction-account/location "A"}]
:transaction/approval-status :transaction-approval-status/approved
- :transaction/vendor (:db/id (:expected-deposit/vendor expected-deposit))
- ))))
+ :transaction/vendor (:db/id (:expected-deposit/vendor expected-deposit))))))
(defn maybe-code [{:transaction/keys [client amount] :as transaction} apply-rules valid-locations]
(mu/trace
@@ -304,7 +297,6 @@
(maybe-apply-default-vendor)
remove-nils)))
-
(defn get-existing [bank-account]
(into {}
(dc/q '[:find ?tid ?as2
@@ -317,7 +309,7 @@
(defprotocol ImportBatch
(import-transaction! [this transaction])
- (get-stats [this ])
+ (get-stats [this])
(get-pending-balance [this bank-account])
(finish! [this])
(fail! [this error]))
@@ -326,21 +318,21 @@
:db/id
:bank-account/locations
:bank-account/start-date
- {:client/_bank-accounts [:client/code :client/locked-until :client/locations :db/id]} ])
+ {:client/_bank-accounts [:client/code :client/locked-until :client/locations :db/id]}])
(defn start-import-batch [source user]
(let [stats (atom {:import-batch/imported 0
- :import-batch/suppressed 0
- :import-batch/error 0
- :import-batch/not-ready 0
- :import-batch/extant 0})
+ :import-batch/suppressed 0
+ :import-batch/error 0
+ :import-batch/not-ready 0
+ :import-batch/extant 0})
pending-balance (atom {})
- extant-cache (atom (cache/ttl-cache-factory {} :ttl 60000 ))
+ extant-cache (atom (cache/ttl-cache-factory {} :ttl 60000))
import-id (get (:tempids @(dc/transact-async conn [{:db/id "import-batch"
- :import-batch/date (coerce/to-date (t/now))
- :import-batch/source source
- :import-batch/status :import-status/started
- :import-batch/user-name user}])) "import-batch")
+ :import-batch/date (coerce/to-date (t/now))
+ :import-batch/source source
+ :import-batch/status :import-status/started
+ :import-batch/user-name user}])) "import-batch")
rule-applying-function (rm/rule-applying-fn (tr/get-all))]
(alog/info ::starting-transaction-import
:source source)
@@ -349,17 +341,17 @@
(import-transaction! [_ transaction]
(let [bank-account (dc/pull (dc/db conn)
bank-account-pull
- (:transaction/bank-account transaction))
+ (:transaction/bank-account transaction))
extant (get (swap! extant-cache cache/through-cache (:transaction/bank-account transaction) get-existing)
(:transaction/bank-account transaction))
action (categorize-transaction transaction bank-account extant)]
- (try
+ (try
(when (not= "POSTED" (:transaction/status transaction))
(swap! pending-balance (fn [pb]
- (update pb
+ (update pb
(:transaction/bank-account transaction)
- (fnil + 0.0)
+ (fnil + 0.0)
(:transaction/amount transaction)))))
(catch Exception e
(alog/warn ::cant-capture-pending
@@ -372,7 +364,7 @@
:error :import-batch/error
:not-ready :import-batch/not-ready) inc))
(when (= :import action)
- (try
+ (try
(let [result (audit-transact [[:upsert-transaction (transaction->txs transaction bank-account rule-applying-function)]
{:db/id import-id
:import-batch/entry (:db/id transaction)}]
@@ -390,14 +382,14 @@
(get-stats [_]
@stats)
(get-pending-balance [_ bank-account]
- (or (get @pending-balance bank-account)
- 0.0))
+ (or (get @pending-balance bank-account)
+ 0.0))
(fail! [_ error]
(alog/error ::cant-complete-import
:import-id import-id
:error error)
-
+
@(dc/transact-async conn [(merge {:db/id import-id
:import-batch/status :import-status/completed
:import-batch/error-message (str error)}
@@ -407,12 +399,10 @@
(alog/info ::finished :import-id import-id :source source :stats (pr-str @stats))
@(dc/transact conn [(merge {:db/id import-id
- :import-batch/status :import-status/completed}
- @stats)])))))
+ :import-batch/status :import-status/completed}
+ @stats)])))))
-
-
-(defn synthetic-key [{:transaction/keys [date bank-account description-original amount client] } index]
+(defn synthetic-key [{:transaction/keys [date bank-account description-original amount client]} index]
(str (str (some-> date coerce/to-date-time atime/localize)) "-" bank-account "-" description-original "-" amount "-" index "-" client))
(defn apply-synthetic-ids [transactions]
@@ -424,7 +414,7 @@
(let [raw-id (synthetic-key transaction index)]
(assoc transaction
:transaction/id #_{:clj-kondo/ignore [:unresolved-var]}
- (di/sha-256 raw-id)
+ (di/sha-256 raw-id)
:transaction/raw-id raw-id
:db/id (random-tempid))))
(range)
diff --git a/src/clj/auto_ap/import/yodlee2.clj b/src/clj/auto_ap/import/yodlee2.clj
index 0bf9df65..947fd9df 100644
--- a/src/clj/auto_ap/import/yodlee2.clj
+++ b/src/clj/auto_ap/import/yodlee2.clj
@@ -73,10 +73,10 @@
(alog/info ::finished-import)
(t/finish! import-batch)
(doseq [[_ bank-account _ _ ya] account-lookup]
- (try
+ (try
@(dc/transact auto-ap.datomic/conn
- [{:db/id ya
- :yodlee-account/pending-balance (t/get-pending-balance import-batch bank-account)}])
+ [{:db/id ya
+ :yodlee-account/pending-balance (t/get-pending-balance import-batch bank-account)}])
(catch Exception e
(alog/error ::cant-persist-yodlee-account-pending-balance
:error e)))))
@@ -95,5 +95,4 @@
nil)
(Thread/sleep 10000)))))
-
(def import-yodlee2 (allow-once import-yodlee2-int))
diff --git a/src/clj/auto_ap/intuit/core.clj b/src/clj/auto_ap/intuit/core.clj
index ed69df79..3aa995e0 100644
--- a/src/clj/auto_ap/intuit/core.clj
+++ b/src/clj/auto_ap/intuit/core.clj
@@ -15,7 +15,6 @@
;; (def base-url "https://sandbox-quickbooks.api.intuit.com/v3")
-
(def prod-client-id "ABFRwAiOqQiLN66HKplXfyRE3ipD390DHsrUquflRCiOa81mxa")
(def prod-client-secret "xDUj04GeQXpLvrhxep1jjDYwjJWbzzOPrirUQTKF")
@@ -27,21 +26,18 @@
;; "accessToken":,
;;
-
-
(def prod-company-id "123146163906404")
-
(def prod-base-url "https://quickbooks.api.intuit.com/v3")
(defn set-access-token [t]
- (s3/put-object :bucket-name (:data-bucket env)
+ (s3/put-object :bucket-name (:data-bucket env)
:key (str "intuit/access-token")
:input-stream (io/make-input-stream (.getBytes t) {})
:metadata {:content-type "application/text"
:content-length (count (.getBytes t))}))
(defn set-refresh-token [t]
- (s3/put-object :bucket-name (:data-bucket env)
+ (s3/put-object :bucket-name (:data-bucket env)
:key (str "intuit/refresh-token")
:input-stream (io/make-input-stream (.getBytes t) {})
:metadata {:content-type "application/text"
@@ -53,7 +49,6 @@
:bucket-name "data.prod.app.integreatconsult.com"
:key "intuit/refresh-token")))))
-
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
(defn init-tokens [access refresh]
(set-access-token access)
@@ -74,17 +69,16 @@
(defn get-basic-auth []
(Base64/encodeBase64String (.getBytes (str prod-client-id ":" prod-client-secret))))
-
(defn get-fresh-access-token []
(let [refresh-token (lookup-refresh-token)
- response (:body (client/post (str "https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer" )
+ response (:body (client/post (str "https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer")
- {:headers {"Accept" "application/json"
- "Content-Type" "application/x-www-form-urlencoded"
- "Authorization" (str "Basic " (get-basic-auth))}
- :form-params {"grant_type" "refresh_token"
- "refresh_token" refresh-token}
- :as :json}))]
+ {:headers {"Accept" "application/json"
+ "Content-Type" "application/x-www-form-urlencoded"
+ "Authorization" (str "Basic " (get-basic-auth))}
+ :form-params {"grant_type" "refresh_token"
+ "refresh_token" refresh-token}
+ :as :json}))]
(set-access-token (:access_token response))
(set-refresh-token (:refresh_token response))
(:access_token response)))
@@ -94,21 +88,20 @@
(defn with-auth [t token]
(assoc t "Authorization" (str "Bearer " token)))
-#_(client/get (str base-url "/company/4620816365202617680")
- {:headers base-headers
- :as :json})
+#_(client/get (str base-url "/company/4620816365202617680")
+ {:headers base-headers
+ :as :json})
(defn get-bank-accounts-raw [token]
- (->> (:body (client/get (str prod-base-url "/company/" prod-company-id "/query" )
+ (->> (:body (client/get (str prod-base-url "/company/" prod-company-id "/query")
{:headers
(with-auth prod-base-headers token)
:as :json
:query-params {"query" "SELECT * From Account maxresults 1000"}}))
:QueryResponse))
-
(defn get-bank-accounts [token]
- (->> (:body (client/get (str prod-base-url "/company/" prod-company-id "/query" )
+ (->> (:body (client/get (str prod-base-url "/company/" prod-company-id "/query")
{:headers
(with-auth prod-base-headers token)
:as :json
@@ -116,7 +109,7 @@
:QueryResponse
:Account
#_(filter
- #(#{"Bank" "Credit Card"} (:AccountType %)))
+ #(#{"Bank" "Credit Card"} (:AccountType %)))
(map (juxt :Id :Name :CurrentBalance :MetaData))
(map (fn [[id name current-balance metadata]]
{:id id
@@ -124,10 +117,9 @@
:last-updated (c/to-date-time (-> metadata :LastUpdatedTime))
:current-balance (try (double current-balance) (catch Exception _ nil))}))))
-
(defn get-all-transactions [start end]
(let [token (get-fresh-access-token)]
- (:body (client/get (str prod-base-url "/company/" prod-company-id "/reports/TransactionList" "?minorversion=63&start_date=" start "&end_date=" end)
+ (:body (client/get (str prod-base-url "/company/" prod-company-id "/reports/TransactionList" "?minorversion=63&start_date=" start "&end_date=" end)
{:headers (with-auth prod-base-headers token)
:as :json}))))
diff --git a/src/clj/auto_ap/jobs/bulk_journal_import.clj b/src/clj/auto_ap/jobs/bulk_journal_import.clj
index bc633edc..4f787660 100644
--- a/src/clj/auto_ap/jobs/bulk_journal_import.clj
+++ b/src/clj/auto_ap/jobs/bulk_journal_import.clj
@@ -12,12 +12,11 @@
(defn line->id [{:keys [source id client-code]}]
(str client-code "-" source "-" id))
-
(defn csv->graphql-rows [lines]
(for [lines (partition-by line->id (drop 1 lines))
- :let [{:keys [source client-code date vendor-name note cleared-against] :as line} (first lines)]]
+ :let [{:keys [source client-code date vendor-name note cleared-against] :as line} (first lines)]]
{:source source
- :external_id (line->id line)
+ :external_id (line->id line)
:client_code client-code
:date date
:note note
@@ -33,8 +32,8 @@
{:account_identifier account-identifier
:location (some-> location str/trim)
:debit (if (str/blank? debit)
- 0.0
- (Double/parseDouble debit))
+ 0.0
+ (Double/parseDouble debit))
:credit (if (str/blank? credit)
0.0
(Double/parseDouble credit))})
diff --git a/src/clj/auto_ap/jobs/close_auto_invoices.clj b/src/clj/auto_ap/jobs/close_auto_invoices.clj
index fb1891fd..f531feb7 100644
--- a/src/clj/auto_ap/jobs/close_auto_invoices.clj
+++ b/src/clj/auto_ap/jobs/close_auto_invoices.clj
@@ -20,8 +20,7 @@
(mapv (fn [[i]] {:db/id i
:invoice/outstanding-balance 0.0
- :invoice/status :invoice-status/paid}))
- ))
+ :invoice/status :invoice-status/paid}))))
(alog/info ::closed :count (count invoices-to-close))))
diff --git a/src/clj/auto_ap/jobs/core.clj b/src/clj/auto_ap/jobs/core.clj
index b906f838..7c30c0c3 100644
--- a/src/clj/auto_ap/jobs/core.clj
+++ b/src/clj/auto_ap/jobs/core.clj
@@ -1,7 +1,7 @@
(ns auto-ap.jobs.core
(:require [auto-ap.utils :refer [heartbeat]]
[mount.core :as mount]
- [auto-ap.datomic :refer [conn ]]
+ [auto-ap.datomic :refer [conn]]
[auto-ap.logging :as alog]
[nrepl.server :refer [start-server]]
[auto-ap.background.metrics :refer [metrics-setup container-tags container-data logging-context]]
@@ -13,8 +13,8 @@
:service name}
(mu/trace ::execute-background-job
[]
- (try
- (mount/start (mount/only #{#'conn #'metrics-setup #'container-tags #'logging-context #'container-data }))
+ (try
+ (mount/start (mount/only #{#'conn #'metrics-setup #'container-tags #'logging-context #'container-data}))
(start-server :port 9000 :bind "0.0.0.0" #_#_:handler (cider-nrepl-handler))
((heartbeat f name))
(alog/info ::stopping :job name)
diff --git a/src/clj/auto_ap/jobs/import_uploaded_invoices.clj b/src/clj/auto_ap/jobs/import_uploaded_invoices.clj
index 9ba7b834..cf1150fd 100644
--- a/src/clj/auto_ap/jobs/import_uploaded_invoices.clj
+++ b/src/clj/auto_ap/jobs/import_uploaded_invoices.clj
@@ -18,7 +18,7 @@
(javax.mail Session)
(javax.mail.internet MimeMessage)))
-(defn send-email-about-failed-message [mail-bucket mail-key message]
+(defn send-email-about-failed-message [mail-bucket mail-key message]
(let [target-key (str "failed-emails/" mail-key ".eml")
target-url (str "https://" (:data-bucket env) "/" target-key)]
(alog/info ::sending-failure-email :who (:import-failure-destination-emails env))
@@ -29,7 +29,6 @@
:body {:html (str "
You can download the original email
here.
" message "
")
:text (str "You can download the original email here: " target-url)}}})))
-
(defn process-sqs []
(alog/info ::fetching-sqs)
(doseq [message (:messages (sqs/receive-message {:queue-url (:invoice-import-queue-url env)
@@ -79,27 +78,20 @@
(defn -main [& _]
(execute "import-uploaded-invoices" process-sqs))
-
-(comment
+(comment
(with-open [i (io/output-stream "/tmp/bryce.pdf")]
- (clojure.java.io/copy
+ (clojure.java.io/copy
(-> (s3/get-object :bucket-name (:data-bucket env)
- :key "invoice-files/f0e73dcb-e5e5-4c81-b82b-319b7caedacf.pdf"
-
- )
+ :key "invoice-files/f0e73dcb-e5e5-4c81-b82b-319b7caedacf.pdf")
+
:input-stream)
i))
(parse/parse-file "/tmp/bryce.pdf" "/tmp/bryce.pdf")
-
- (-> (clojure.java.shell/sh "pdftotext" "-layout" "/tmp/bryce.pdf" "-")
- :out
- )
+ (-> (clojure.java.shell/sh "pdftotext" "-layout" "/tmp/bryce.pdf" "-")
+ :out)
-
1
- (user/init-repl)
-
- )
\ No newline at end of file
+ (user/init-repl))
\ No newline at end of file
diff --git a/src/clj/auto_ap/jobs/insight_outcome_recommendation.clj b/src/clj/auto_ap/jobs/insight_outcome_recommendation.clj
index 6e13aab1..d9217c29 100644
--- a/src/clj/auto_ap/jobs/insight_outcome_recommendation.clj
+++ b/src/clj/auto_ap/jobs/insight_outcome_recommendation.clj
@@ -23,39 +23,39 @@
[?t :transaction/client ?c]])))
(defn get-pinecone [transaction-id]
- (->
- (http2/get (-> "https://transactions-a8257ba.svc.us-west4-gcp-free.pinecone.io/vectors/fetch"
+ (->
+ (http2/get (-> "https://transactions-a8257ba.svc.us-west4-gcp-free.pinecone.io/vectors/fetch"
url/url
(assoc :query {:ids transaction-id})
str)
{:headers {"Api-Key" "f2d3a78e-bcea-4fcd-88b6-2527b8423607"}
:as :json
:keywordize? false})
- :body
- :vectors
- ((keyword (str transaction-id)))
- :values))
+ :body
+ :vectors
+ ((keyword (str transaction-id)))
+ :values))
(defn get-pinecone-similarities [transaction-id]
(if-let [vector (get-pinecone transaction-id)]
- (filter
- (fn [{:keys [score]}]
- (> score 0.95))
- (->
- (http2/post (-> "https://transactions-a8257ba.svc.us-west4-gcp-free.pinecone.io/query"
- url/url
- str)
- {:headers {"Api-Key" "f2d3a78e-bcea-4fcd-88b6-2527b8423607"}
- :form-params {"vector" vector
- "topK" 200,
- "includeMetadata" true
- "namespace" ""}
- :content-type :json
- :as :json})
- :body
- :matches
- (doto (#(alog/info ::similarities-found :transaction transaction-id :count (count %) :sample (take 3 %))))))
-
+ (filter
+ (fn [{:keys [score]}]
+ (> score 0.95))
+ (->
+ (http2/post (-> "https://transactions-a8257ba.svc.us-west4-gcp-free.pinecone.io/query"
+ url/url
+ str)
+ {:headers {"Api-Key" "f2d3a78e-bcea-4fcd-88b6-2527b8423607"}
+ :form-params {"vector" vector
+ "topK" 200,
+ "includeMetadata" true
+ "namespace" ""}
+ :content-type :json
+ :as :json})
+ :body
+ :matches
+ (doto (#(alog/info ::similarities-found :transaction transaction-id :count (count %) :sample (take 3 %))))))
+
(do (alog/info ::no-matches-for :transaction transaction-id)
[])))
diff --git a/src/clj/auto_ap/jobs/load_historical_sales.clj b/src/clj/auto_ap/jobs/load_historical_sales.clj
index fa5d92da..8558c6ca 100644
--- a/src/clj/auto_ap/jobs/load_historical_sales.clj
+++ b/src/clj/auto_ap/jobs/load_historical_sales.clj
@@ -10,16 +10,15 @@
[config.core :refer [env]]
[datomic.api :as dc]))
-
(defn historical-load-sales [client days]
(alog/info ::new-sales-loading :client (:client/code client) :days days)
(let [client (dc/pull (dc/db auto-ap.datomic/conn)
- square3/square-read
- client)
- days (cond-> days (string? days) ( #(Long/parseLong %)))]
+ square3/square-read
+ client)
+ days (cond-> days (string? days) (#(Long/parseLong %)))]
(doseq [square-location (:client/square-locations client)
:when (:square-location/client-location square-location)]
-
+
(println "orders")
(doseq [d (per/periodic-seq (time/plus (time/today) (time/days (- days)))
(time/plus (time/today) (time/days 2))
@@ -28,14 +27,13 @@
@(square3/upsert client square-location (coerce/to-date-time d) (coerce/to-date-time (time/plus d (time/days 1)))))
(println "refunds")
- @(square3/upsert-refunds client square-location)
- @(square3/upsert-payouts client square-location (time/plus (time/now) (time/days (- days))) (time/now)))))
-
+ @(square3/upsert-refunds client square-location)
+ @(square3/upsert-payouts client square-location (time/plus (time/now) (time/days (- days))) (time/now)))))
(defn load-historical-sales [args]
(let [{:keys [days client]} args
- client (cond-> client
- ( string? client) ( #( Long/parseLong %)))]
+ client (cond-> client
+ (string? client) (#(Long/parseLong %)))]
(historical-load-sales client days)))
(defn -main [& _]
diff --git a/src/clj/auto_ap/jobs/ntg.clj b/src/clj/auto_ap/jobs/ntg.clj
index 9b2cabee..b417955e 100644
--- a/src/clj/auto_ap/jobs/ntg.clj
+++ b/src/clj/auto_ap/jobs/ntg.clj
@@ -28,19 +28,17 @@
(defn read-xml [stream]
(-> (slurp stream)
(.getBytes)
- (java.io.ByteArrayInputStream. )
+ (java.io.ByteArrayInputStream.)
xml/parse
zip/xml-zip))
-
-
(defn mark-key [k]
(s3/copy-object {:source-bucket-name bucket-name
:destination-bucket-name bucket-name
:destination-key (str/replace-first k "pending" "imported")
:source-key k})
#_(s3/delete-object {:bucket-name bucket-name
- :key k}))
+ :key k}))
(defn is-csv-file? [x]
(= "dat" (last (str/split x #"[\\.]"))))
@@ -54,7 +52,7 @@
(and (str/includes? k "GeneralProduce")
(str/includes? k "FRANCHISEE")
(is-csv-file? k))
- :general-produce
+ :general-produce
:else
:unknown))
@@ -66,15 +64,15 @@
[k input-stream clients]
(log/info ::parsing-general-produce :key k)
(let [missing-client-hints (atom #{})]
- (try
+ (try
(->> (read-csv input-stream)
(drop 1)
#_(filter (fn [[_ _ _ _ _ _ _ _ _ _ _ break-flag]]
(= "Y" break-flag)))
- (map (fn [[_ location-hint invoice-number ship-date invoice-total ]]
+ (map (fn [[_ location-hint invoice-number ship-date invoice-total]]
(let [matching-client (and location-hint
(parse/exact-match clients location-hint))
- location (parse/best-location-match matching-client location-hint location-hint )
+ location (parse/best-location-match matching-client location-hint location-hint)
vendor (d/pull (d/db conn) '[:vendor/default-account] :vendor/general-produce)]
(when-not (and matching-client
(not (@missing-client-hints location-hint))
@@ -99,8 +97,7 @@
(-> vendor :vendor/default-account :db/id)
:invoice-expense-account/location location
:invoice-expense-account/amount (Math/abs (Double/parseDouble invoice-total))
- :db/id (random-tempid)
- }]})))
+ :db/id (random-tempid)}]})))
(filter :invoice/client)
(reduce (fn [[seen-so-far list] i]
(let [k [(:invoice/invoice-number i) (:invoice/client i)]]
@@ -108,8 +105,7 @@
[seen-so-far list]
[(conj seen-so-far k) (conj list i)])))
[#{} []])
- (second)
- )
+ (second))
(catch Exception e
(log/error ::cant-import-general-produce
:error e)
@@ -123,8 +119,8 @@
(defn zip-seq [zipper]
(->> (zip/xml-zip (zip/node zipper))
- (iterate zip/next )
- (take-while (complement zip/end?))))
+ (iterate zip/next)
+ (take-while (complement zip/end?))))
(defmethod extract-invoice-details :cintas
[k input-stream clients]
@@ -160,10 +156,10 @@
atime/localize
(atime/unparse atime/iso-date)
(atime/parse atime/iso-date))))
- location (parse/best-location-match matching-client location-hint location-hint )
+ location (parse/best-location-match matching-client location-hint location-hint)
due (-> invoice-date
- (time/plus (time/days 30))
- (coerce/to-date))
+ (time/plus (time/days 30))
+ (coerce/to-date))
total (->> node-seq
(filter (fn [zipper]
(= (:tag (zip/node zipper))
@@ -178,7 +174,7 @@
:content
first
Double/parseDouble)
- invoice {:db/id (random-tempid )
+ invoice {:db/id (random-tempid)
:invoice/vendor :vendor/cintas
:invoice/import-status :import-status/imported
:invoice/status :invoice-status/unpaid
@@ -188,37 +184,36 @@
:invoice/total total
:invoice/outstanding-balance total
:invoice/invoice-number (->> node-seq
- (map zip/node)
- (filter (fn [node]
- (= (:tag node)
- :InvoiceDetailRequestHeader)))
- first
- (#(-> % :attrs :invoiceID)))
+ (map zip/node)
+ (filter (fn [node]
+ (= (:tag node)
+ :InvoiceDetailRequestHeader)))
+ first
+ (#(-> % :attrs :invoiceID)))
:invoice/due due
:invoice/scheduled-payment (when-not ((into #{} (->> matching-client
:client/feature-flags))
"manually-pay-cintas")
- due)
+ due)
:invoice/date (coerce/to-date invoice-date)
:invoice/expense-accounts [{:invoice-expense-account/account
(-> vendor :vendor/default-account :db/id)
:invoice-expense-account/location location
:invoice-expense-account/amount (Math/abs total)
- :db/id (random-tempid)
- }]}]
+ :db/id (random-tempid)}]}]
(log/info ::cintas-invoice-importing
:invoice invoice)
[invoice])
- (do
+ (do
;; disabling logging for cintas
#_(log/warn ::missing-client
- :client-hint location-hint)
+ :client-hint location-hint)
[]))))
(defn mark-error [k]
- (s3/copy-object {:source-bucket-name bucket-name
+ (s3/copy-object {:source-bucket-name bucket-name
:destination-bucket-name bucket-name
:source-key k
:destination-key (str "ntg-invoices/error/"
@@ -232,17 +227,17 @@
(s3/copy-object {:source-bucket-name bucket-name
:destination-bucket-name bucket-name
:source-key k
- :destination-key invoice-key })
+ :destination-key invoice-key})
invoice-key))
(defn get-all-keys
([]
- (let [first-page-result (s3/list-objects-v2 {:bucket-name bucket-name
+ (let [first-page-result (s3/list-objects-v2 {:bucket-name bucket-name
:prefix "ntg-invoices/pending"})]
(lazy-seq (concat (:object-summaries first-page-result) (get-all-keys (:next-continuation-token first-page-result))))))
- ([next-token ]
- (when next-token
- (let [page-result (s3/list-objects-v2 {:bucket-name bucket-name
+ ([next-token]
+ (when next-token
+ (let [page-result (s3/list-objects-v2 {:bucket-name bucket-name
:prefix "ntg-invoices/pending"
:continuation-token next-token})]
(println "getting next page " next-token)
@@ -250,60 +245,58 @@
(lazy-seq (concat (:object-summaries page-result) (get-all-keys (:next-continuation-token page-result)))))))))
(defn recent? [k]
- (time/after? (:last-modified k) (time/plus (time/now) (time/days -15)))
- )
+ (time/after? (:last-modified k) (time/plus (time/now) (time/days -15))))
(defn import-ntg-invoices
([] (import-ntg-invoices (->> (get-all-keys)
(filter recent?)
(map :key))))
([keys]
- (let [clients (map first (d/q '[:find (pull ?c [:client/code
- :db/id
- :client/feature-flags
- {:client/location-matches [:location-match/matches :location-match/location]}
- :client/name
- :client/matches
- :client/locations])
- :where [?c :client/code]]
- (d/db conn)))]
- (log/info ::found-invoice-keys
- :keys keys )
- (let [transaction (->> keys
- (mapcat (fn [k]
- (try
- (let [invoice-key (copy-readable-version k)
- invoice-url (str "https://" bucket-name "/" invoice-key)]
- (with-open [is (-> (s3/get-object {:bucket-name bucket-name
- :key k})
- :input-stream)]
- (->> (extract-invoice-details k
- is
- clients)
- (set)
- (map (fn [i]
- (log/info ::importing-invoice
- :invoice i)
- i))
- (mapv (fn [i]
- (if (= :vendor/cintas (:invoice/vendor i))
- [:propose-invoice (assoc i :invoice/source-url invoice-url)]
- [:propose-invoice i]))))))
- (catch Exception e
- (log/error ::cant-load-file
- :key k
- :exception e)
- (mark-error k)
- []))))
- (into []))]
- (doseq [t transaction]
- (audit-transact [t] {:user/name "sysco importer" :user/role "admin"}))
- (log/info ::success
- :count (count transaction)
- :sample (take 3 transaction)))
- (doseq [k keys]
- (mark-key k)))))
-
+ (let [clients (map first (d/q '[:find (pull ?c [:client/code
+ :db/id
+ :client/feature-flags
+ {:client/location-matches [:location-match/matches :location-match/location]}
+ :client/name
+ :client/matches
+ :client/locations])
+ :where [?c :client/code]]
+ (d/db conn)))]
+ (log/info ::found-invoice-keys
+ :keys keys)
+ (let [transaction (->> keys
+ (mapcat (fn [k]
+ (try
+ (let [invoice-key (copy-readable-version k)
+ invoice-url (str "https://" bucket-name "/" invoice-key)]
+ (with-open [is (-> (s3/get-object {:bucket-name bucket-name
+ :key k})
+ :input-stream)]
+ (->> (extract-invoice-details k
+ is
+ clients)
+ (set)
+ (map (fn [i]
+ (log/info ::importing-invoice
+ :invoice i)
+ i))
+ (mapv (fn [i]
+ (if (= :vendor/cintas (:invoice/vendor i))
+ [:propose-invoice (assoc i :invoice/source-url invoice-url)]
+ [:propose-invoice i]))))))
+ (catch Exception e
+ (log/error ::cant-load-file
+ :key k
+ :exception e)
+ (mark-error k)
+ []))))
+ (into []))]
+ (doseq [t transaction]
+ (audit-transact [t] {:user/name "sysco importer" :user/role "admin"}))
+ (log/info ::success
+ :count (count transaction)
+ :sample (take 3 transaction)))
+ (doseq [k keys]
+ (mark-key k)))))
(defn -main [& _]
(execute "ntg" import-ntg-invoices))
diff --git a/src/clj/auto_ap/jobs/register_invoice_import.clj b/src/clj/auto_ap/jobs/register_invoice_import.clj
index e5821988..3b269ec6 100644
--- a/src/clj/auto_ap/jobs/register_invoice_import.clj
+++ b/src/clj/auto_ap/jobs/register_invoice_import.clj
@@ -18,7 +18,7 @@
(def bucket (:data-bucket env))
(defn s3->csv [url]
- (try
+ (try
(->> (-> (s3/get-object {:bucket-name bucket
:key (str "bulk-import/" url)})
:input-stream
@@ -26,9 +26,9 @@
csv/read-csv))
(catch Exception e
(alog/error
- :file-not-found
- :error e
- :url url)
+ :file-not-found
+ :error e
+ :url url)
(throw e))))
(defn register-invoice-import* [data]
@@ -45,106 +45,100 @@
(reduce + 0.0
(->> values
(map (fn [[_ _ _ _ amount]]
- (- (Double/parseDouble amount))))))
- ]))
+ (- (Double/parseDouble amount))))))]))
(into {}))]
- (->>
- (for [[i
- invoice-expense-account-id
- target-account
- target-date
- amount
- _
- location] (drop 1 data)
- :let [invoice-id (i->invoice-id i)
+ (->>
+ (for [[i
+ invoice-expense-account-id
+ target-account
+ target-date
+ amount
+ _
+ location] (drop 1 data)
+ :let [invoice-id (i->invoice-id i)
- invoice (dc/pull db '[*] invoice-id)
- current-total (:invoice/total invoice)
- target-total (invoice-totals invoice-id) ;; TODO should include expense accounts not visible
- new-account? (not (boolean (or (some-> invoice-expense-account-id not-empty Long/parseLong)
- (:db/id (first (:invoice/expense-accounts invoice))))))
+ invoice (dc/pull db '[*] invoice-id)
+ current-total (:invoice/total invoice)
+ target-total (invoice-totals invoice-id) ;; TODO should include expense accounts not visible
+ new-account? (not (boolean (or (some-> invoice-expense-account-id not-empty Long/parseLong)
+ (:db/id (first (:invoice/expense-accounts invoice))))))
- invoice-expense-account-id (or (some-> invoice-expense-account-id not-empty Long/parseLong)
- (:db/id (first (:invoice/expense-accounts invoice)))
- (str (UUID/randomUUID)))
- invoice-expense-account (when-not new-account?
- (or (dc/pull db '[*] invoice-expense-account-id)
- (dc/pull db '[*] [:invoice-expense-account/original-id invoice-expense-account-id])))
- current-account-id (:db/id (:invoice-expense-account/account invoice-expense-account))
- target-account-id (Long/parseLong (str/trim target-account))
+ invoice-expense-account-id (or (some-> invoice-expense-account-id not-empty Long/parseLong)
+ (:db/id (first (:invoice/expense-accounts invoice)))
+ (str (UUID/randomUUID)))
+ invoice-expense-account (when-not new-account?
+ (or (dc/pull db '[*] invoice-expense-account-id)
+ (dc/pull db '[*] [:invoice-expense-account/original-id invoice-expense-account-id])))
+ current-account-id (:db/id (:invoice-expense-account/account invoice-expense-account))
+ target-account-id (Long/parseLong (str/trim target-account))
- target-date (coerce/to-date (atime/parse target-date atime/normal-date))
- current-date (:invoice/date invoice)
-
+ target-date (coerce/to-date (atime/parse target-date atime/normal-date))
+ current-date (:invoice/date invoice)
- current-expense-account-amount (:invoice-expense-account/amount invoice-expense-account 0.0)
- target-expense-account-amount (- (Double/parseDouble amount))
+ current-expense-account-amount (:invoice-expense-account/amount invoice-expense-account 0.0)
+ target-expense-account-amount (- (Double/parseDouble amount))
+ current-expense-account-location (:invoice-expense-account/location invoice-expense-account)
+ target-expense-account-location location
- current-expense-account-location (:invoice-expense-account/location invoice-expense-account)
- target-expense-account-location location
+ [[_ _ invoice-payment]] (vec (dc/q
+ '[:find ?p ?a ?ip
+ :in $ ?i
+ :where [?ip :invoice-payment/invoice ?i]
+ [?ip :invoice-payment/amount ?a]
+ [?ip :invoice-payment/payment ?p]]
+ db invoice-id))]
+ :when current-total]
+ [(when (not (dollars= current-total target-total))
+ {:db/id invoice-id
+ :invoice/total target-total})
- [[_ _ invoice-payment]] (vec (dc/q
- '[:find ?p ?a ?ip
- :in $ ?i
- :where [?ip :invoice-payment/invoice ?i]
- [?ip :invoice-payment/amount ?a]
- [?ip :invoice-payment/payment ?p]
- ]
- db invoice-id))]
- :when current-total]
+ (when (and (not (dollars= 0.0 target-total))
+ (= :invoice-status/voided (:db/ident (:invoice/status invoice))))
+ {:db/id invoice-id
+ :invoice/total target-total
+ :invoice/status :invoice-status/paid})
- [
- (when (not (dollars= current-total target-total))
- {:db/id invoice-id
- :invoice/total target-total})
+ (when new-account?
+ {:db/id invoice-id
+ :invoice/expense-accounts invoice-expense-account-id})
- (when (and (not (dollars= 0.0 target-total))
- (= :invoice-status/voided (:db/ident (:invoice/status invoice))))
- {:db/id invoice-id
- :invoice/total target-total
- :invoice/status :invoice-status/paid})
+ (when (and target-date (not= current-date target-date))
+ {:db/id invoice-id
+ :invoice/date target-date})
- (when new-account?
- {:db/id invoice-id
- :invoice/expense-accounts invoice-expense-account-id})
+ (when (and
+ (not (dollars= current-total target-total))
+ invoice-payment)
+ [:db/retractEntity invoice-payment])
- (when (and target-date (not= current-date target-date))
- {:db/id invoice-id
- :invoice/date target-date})
+ (when (or new-account?
+ (not (dollars= current-expense-account-amount target-expense-account-amount)))
+ {:db/id invoice-expense-account-id
+ :invoice-expense-account/amount target-expense-account-amount})
- (when (and
- (not (dollars= current-total target-total))
- invoice-payment)
- [:db/retractEntity invoice-payment])
+ (when (not= current-expense-account-location
+ target-expense-account-location)
+ {:db/id invoice-expense-account-id
+ :invoice-expense-account/location target-expense-account-location})
- (when (or new-account?
- (not (dollars= current-expense-account-amount target-expense-account-amount)))
- {:db/id invoice-expense-account-id
- :invoice-expense-account/amount target-expense-account-amount})
-
- (when (not= current-expense-account-location
- target-expense-account-location)
- {:db/id invoice-expense-account-id
- :invoice-expense-account/location target-expense-account-location})
-
- (when (not= current-account-id target-account-id )
- {:db/id invoice-expense-account-id
- :invoice-expense-account/account target-account-id})])
- (mapcat identity)
- (filter identity)
- vec)))
+ (when (not= current-account-id target-account-id)
+ {:db/id invoice-expense-account-id
+ :invoice-expense-account/account target-account-id})])
+ (mapcat identity)
+ (filter identity)
+ vec)))
(defn register-invoice-import [args]
(let [{:keys [invoice-url]} args
data (s3->csv invoice-url)]
(alog/info ::rows
- :count (count data))
+ :count (count data))
(doseq [n (partition-all 50 (register-invoice-import* data))]
(alog/info ::transacting
- :count (count n)
- :sample (take 2 n))
+ :count (count n)
+ :sample (take 2 n))
(audit-transact n {:user/name "register-invoice-import"
:user/role "admin"}))))
diff --git a/src/clj/auto_ap/jobs/restore_from_backup.clj b/src/clj/auto_ap/jobs/restore_from_backup.clj
index 50fbc645..305f113b 100644
--- a/src/clj/auto_ap/jobs/restore_from_backup.clj
+++ b/src/clj/auto_ap/jobs/restore_from_backup.clj
@@ -20,32 +20,29 @@
(def buffered (ex/fixed-thread-executor 100))
(defn order-of-insert [entity-dependencies]
- (loop [entity-dependencies entity-dependencies
+ (loop [entity-dependencies entity-dependencies
order []]
(let [next-order (for [[entity deps] entity-dependencies
:when (not (seq deps))]
entity)
next-deps (reduce
- (fn [entity-dependencies next-entity]
- (into {}
- (map
- (fn [[k v]]
- [k (disj v next-entity)])
- entity-dependencies)))
- (apply dissoc entity-dependencies next-order)
- next-order)]
+ (fn [entity-dependencies next-entity]
+ (into {}
+ (map
+ (fn [[k v]]
+ [k (disj v next-entity)])
+ entity-dependencies)))
+ (apply dissoc entity-dependencies next-order)
+ next-order)]
(if (seq next-deps)
(recur next-deps (into order next-order))
(into order next-order)))))
-
-
-
(def loaded (atom #{}))
(defn upsert-batch
[batch context]
- (de/future-with request-pool
+ (de/future-with request-pool
(mu/with-context context
(transact-with-backoff batch))
batch))
@@ -68,39 +65,39 @@
(mu/with-context {:entity entity}
(mu/log ::starting)
@(s/consume (fn [batch]
- (mu/with-context {:entity entity}
- (try
- (swap! so-far #(+ % (count batch)))
- (mu/log ::loaded :count (count batch)
- :so-far @so-far)
- (catch Exception e
- (mu/log ::error
- :exception e)
- (throw e)))))
- (->> (partition-all 1000 entities)
- (s/->source)
- (s/onto buffered)
- (s/map (fn [entities]
- (when @die?
- (reset! die? false)
- (throw (Exception. "dead")))
- (upsert-batch entities {:entity entity
- :service "restore-from-backup"
- :background-job "restore-from-backup"})))
- (s/buffer 20)
- (s/realize-each)))
+ (mu/with-context {:entity entity}
+ (try
+ (swap! so-far #(+ % (count batch)))
+ (mu/log ::loaded :count (count batch)
+ :so-far @so-far)
+ (catch Exception e
+ (mu/log ::error
+ :exception e)
+ (throw e)))))
+ (->> (partition-all 1000 entities)
+ (s/->source)
+ (s/onto buffered)
+ (s/map (fn [entities]
+ (when @die?
+ (reset! die? false)
+ (throw (Exception. "dead")))
+ (upsert-batch entities {:entity entity
+ :service "restore-from-backup"
+ :background-job "restore-from-backup"})))
+ (s/buffer 20)
+ (s/realize-each)))
(swap! loaded conj entity))))
-(defn load-from-backup
+(defn load-from-backup
([backup-id connection] (load-from-backup backup-id connection nil))
([backup-id connection starting-at]
(let [schema (edn/read-string (slurp (pull-file backup-id "schema.edn")))
full-dependencies (edn/read-string (slurp (pull-file backup-id "full-dependencies.edn")))
entity-dependencies (edn/read-string (slurp (pull-file backup-id "entity-dependencies.edn")))]
@(dc/transact connection [{:db/ident :entity/migration-key
- :db/unique :db.unique/identity
- :db/cardinality :db.cardinality/one
- :db/valueType :db.type/long}])
+ :db/unique :db.unique/identity
+ :db/cardinality :db.cardinality/one
+ :db/valueType :db.type/long}])
@(dc/transact connection (map
(fn [s]
(set/rename-keys s {:db/id :entity/migration-key}))
@@ -108,14 +105,13 @@
;; TEMP - this has been fixed in current export (ezcater-olaciotn)
@(dc/transact connection [{:entity/migration-key 17592257603901 :vendor/name "unknown"}
- {:entity/migration-key 17592232621701}
- {:entity/migration-key 17592263907739}
- {:entity/migration-key 17592271516922}])
-
+ {:entity/migration-key 17592232621701}
+ {:entity/migration-key 17592263907739}
+ {:entity/migration-key 17592271516922}])
(doseq [entity (cond->> (order-of-insert entity-dependencies)
true (filter #(not= "audit" %))
- starting-at (drop-while #(not= starting-at %)))
+ starting-at (drop-while #(not= starting-at %)))
:let [_ (reset! so-far 0)
_ (mu/log ::querying :entity entity)
entities (mu/trace ::file-pulled
@@ -136,9 +132,8 @@
(mu/log ::refresh-running-balance-cache-complete)
(mu/log ::done))
-
(defn -main [& _]
- (try
+ (try
(println "restore")
(execute "restore-from-backup" #(restore-fresh-from-backup (:args env)))
(catch Exception e
@@ -151,13 +146,11 @@
(throw e))))
;; cloud load
-#_(comment
+#_(comment
;; /datomic-backup/079df203-eae0-4acf-94d5-8608ba8b8a9a
- (load-from-backup "079df203-eae0-4acf-94d5-8608ba8b8a9a" auto-ap.datomic/conn ["charge"])
+ (load-from-backup "079df203-eae0-4acf-94d5-8608ba8b8a9a" auto-ap.datomic/conn ["charge"])
- (load-entity "charge" (ednl/slurp "/tmp/tmp-edn"))
+ (load-entity "charge" (ednl/slurp "/tmp/tmp-edn")))
-
- )
;; => nil
diff --git a/src/clj/auto_ap/jobs/sales_summaries.clj b/src/clj/auto_ap/jobs/sales_summaries.clj
index e759078b..def61fdc 100644
--- a/src/clj/auto_ap/jobs/sales_summaries.clj
+++ b/src/clj/auto_ap/jobs/sales_summaries.clj
@@ -39,17 +39,14 @@
(dc/db conn)
number)))
-
(defn delete-all []
@(dc/transact-async conn
- (->>
- (dc/q '[:find ?ss
- :where [?ss :sales-summary/date]]
- (dc/db conn))
- (map (fn [[ ss]]
- [:db/retractEntity ss])))))
-
-
+ (->>
+ (dc/q '[:find ?ss
+ :where [?ss :sales-summary/date]]
+ (dc/db conn))
+ (map (fn [[ss]]
+ [:db/retractEntity ss])))))
(defn dirty-sales-summaries [c]
(let [client-id (dc/entid (dc/db conn) c)]
@@ -99,53 +96,53 @@
"food app refunds" 41400})
(defn get-payment-items [c date]
- (->>
- (dc/q '[:find ?processor ?type-name (sum ?total)
- :with ?c
- :in $ [?clients ?start-date ?end-date]
- :where [(iol-ion.query/scan-sales-orders $ ?clients ?start-date ?end-date) [[?e _ ?sort-default] ...]]
- [?e :sales-order/charges ?c]
- [?c :charge/type-name ?type-name]
- (or-join [?c ?processor]
- (and [?c :charge/processor ?p]
- [?p :db/ident ?processor])
- (and
- (not [?c :charge/processor])
- [(ground :ccp-processor/na) ?processor]))
- [?c :charge/total ?total]]
- (dc/db conn)
- [[c] date date])
- (reduce
- (fn [acc [processor type-name total]]
- (update
- acc
- (cond (= type-name "CARD")
- "Card Payments"
- (= type-name "CASH")
- "Cash Payments"
- (#{"SQUARE_GIFT_CARD" "WALLET" "GIFT_CARD"} type-name)
- "Gift Card Payments"
- (#{:ccp-processor/toast
- #_:ccp-processor/ezcater
- #_:ccp-processor/koala
- :ccp-processor/doordash
- :ccp-processor/grubhub
- :ccp-processor/uber-eats} processor)
- "Food App Payments"
- :else
- "Unknown")
- (fnil + 0.0)
- total))
- {})
- (map (fn [[k v]]
- {:db/id (str (java.util.UUID/randomUUID))
- :sales-summary-item/sort-order 0
- :sales-summary-item/category k
-
- :ledger-mapped/amount (if (= "Card Payments" k)
- (- v (get-fee c date))
- v)
- :ledger-mapped/ledger-side :ledger-side/debit}))))
+ (->>
+ (dc/q '[:find ?processor ?type-name (sum ?total)
+ :with ?c
+ :in $ [?clients ?start-date ?end-date]
+ :where [(iol-ion.query/scan-sales-orders $ ?clients ?start-date ?end-date) [[?e _ ?sort-default] ...]]
+ [?e :sales-order/charges ?c]
+ [?c :charge/type-name ?type-name]
+ (or-join [?c ?processor]
+ (and [?c :charge/processor ?p]
+ [?p :db/ident ?processor])
+ (and
+ (not [?c :charge/processor])
+ [(ground :ccp-processor/na) ?processor]))
+ [?c :charge/total ?total]]
+ (dc/db conn)
+ [[c] date date])
+ (reduce
+ (fn [acc [processor type-name total]]
+ (update
+ acc
+ (cond (= type-name "CARD")
+ "Card Payments"
+ (= type-name "CASH")
+ "Cash Payments"
+ (#{"SQUARE_GIFT_CARD" "WALLET" "GIFT_CARD"} type-name)
+ "Gift Card Payments"
+ (#{:ccp-processor/toast
+ #_:ccp-processor/ezcater
+ #_:ccp-processor/koala
+ :ccp-processor/doordash
+ :ccp-processor/grubhub
+ :ccp-processor/uber-eats} processor)
+ "Food App Payments"
+ :else
+ "Unknown")
+ (fnil + 0.0)
+ total))
+ {})
+ (map (fn [[k v]]
+ {:db/id (str (java.util.UUID/randomUUID))
+ :sales-summary-item/sort-order 0
+ :sales-summary-item/category k
+
+ :ledger-mapped/amount (if (= "Card Payments" k)
+ (- v (get-fee c date))
+ v)
+ :ledger-mapped/ledger-side :ledger-side/debit}))))
(defn get-discounts [c date]
(when-let [discount (ffirst (dc/q '[:find (sum ?discount)
@@ -162,7 +159,7 @@
:ledger-mapped/ledger-side :ledger-side/debit}))
(defn get-refund-items [c date]
- (->>
+ (->>
(dc/q '[:find ?type-name (sum ?t)
:with ?e
:in $ [?clients ?start-date ?end-date]
@@ -173,26 +170,24 @@
(dc/db conn)
[[c] date date])
(reduce
- (fn [acc [type-name total]]
- (update
- acc
- (cond (= type-name "CARD")
- "Card Refunds"
- (= type-name "CASH")
- "Cash Refunds"
- :else
- "Food App Refunds")
- (fnil + 0.0)
- total))
- {})
+ (fn [acc [type-name total]]
+ (update
+ acc
+ (cond (= type-name "CARD")
+ "Card Refunds"
+ (= type-name "CASH")
+ "Cash Refunds"
+ :else
+ "Food App Refunds")
+ (fnil + 0.0)
+ total))
+ {})
(map (fn [[k v]]
- {:db/id (str (java.util.UUID/randomUUID))
- :sales-summary-item/sort-order 3
- :sales-summary-item/category k
- :ledger-mapped/amount v
- :ledger-mapped/ledger-side :ledger-side/credit}))))
-
-
+ {:db/id (str (java.util.UUID/randomUUID))
+ :sales-summary-item/sort-order 3
+ :sales-summary-item/category k
+ :ledger-mapped/amount v
+ :ledger-mapped/ledger-side :ledger-side/credit}))))
(defn get-fees [c date]
(when-let [fee (get-fee c date)]
@@ -278,17 +273,17 @@
(defn sales-summaries-v2 []
(doseq [[c client-code] (dc/q '[:find ?c ?client-code
- :in $
- :where [?c :client/code ?client-code]]
- (dc/db conn))
- {:sales-summary/keys [date] :db/keys [id] :as existing-summary} (dirty-sales-summaries c)]
+ :in $
+ :where [?c :client/code ?client-code]]
+ (dc/db conn))
+ {:sales-summary/keys [date] :db/keys [id] :as existing-summary} (dirty-sales-summaries c)]
(mu/with-context {:client-code client-code
- :date date}
- (alog/info ::updating)
- (let [manual-items (->> existing-summary
- :sales-summary/items
- (filter :sales-summary-item/manual?))
- calculated-items (->>
+ :date date}
+ (alog/info ::updating)
+ (let [manual-items (->> existing-summary
+ :sales-summary/items
+ (filter :sales-summary-item/manual?))
+ calculated-items (->>
(get-sales c date)
(concat (get-payment-items c date))
(concat (get-refund-items c date))
@@ -301,20 +296,19 @@
(map (fn [z]
(assoc z :ledger-mapped/account (some-> z :sales-summary-item/category str/lower-case name->number lookup-account)
:sales-summary-item/manual? false))))
- all-items (concat calculated-items manual-items)
- result {:db/id id
- :sales-summary/client c
- :sales-summary/date date
- :sales-summary/dirty false
- :sales-summary/client+date [c date]
- :sales-summary/items all-items}]
- (if (seq (:sales-summary/items result))
- (do
- (alog/info ::upserting-summaries
- :category-count (count (:sales-summary/items result)))
- @(dc/transact conn [[:upsert-sales-summary result]]))
- @(dc/transact conn [{:db/id id :sales-summary/dirty false}]))))))
-
+ all-items (concat calculated-items manual-items)
+ result {:db/id id
+ :sales-summary/client c
+ :sales-summary/date date
+ :sales-summary/dirty false
+ :sales-summary/client+date [c date]
+ :sales-summary/items all-items}]
+ (if (seq (:sales-summary/items result))
+ (do
+ (alog/info ::upserting-summaries
+ :category-count (count (:sales-summary/items result)))
+ @(dc/transact conn [[:upsert-sales-summary result]]))
+ @(dc/transact conn [{:db/id id :sales-summary/dirty false}]))))))
(defn reset-summaries []
@(dc/transact conn (->> (dc/q '[:find ?sos
@@ -324,9 +318,6 @@
(map (fn [[sos]]
[:db/retractEntity sos])))))
-
-
-
(comment
(auto-ap.datomic/transact-schema conn)
@@ -336,26 +327,19 @@
(dirty-sales-summaries [:client/code "NGWH"])
-
(apply mark-dirty [:client/code "NGWH"] (last-n-days 5))
(iol-ion.tx.upsert-sales-summary-ledger/summary->journal-entry (dc/db conn) 17592314245819)
-
(iol-ion.tx.upsert-sales-summary-ledger/upsert-sales-summary (dc/db conn) {:db/id 17592314241429})
-
-
(mark-all-dirty 5)
(delete-all)
-
(sales-summaries-v2)
-
1
-
(dc/q '[:find (pull ?sos [* {:sales-summary/sales-items [*]}])
:in $
:where [?sos :sales-summary/client [:client/code "NGHW"]]
@@ -386,15 +370,7 @@
@(dc/transact conn [{:db/id :sales-summary/total-tax :db/ident :sales-summary/total-tax-legacy}
{:db/id :sales-summary/total-tip :db/ident :sales-summary/total-tip-legacy}])
- (auto-ap.datomic/transact-schema conn)
-
-
-
-
- )
-
-
+ (auto-ap.datomic/transact-schema conn))
(defn -main [& _]
(execute "sales-summaries" sales-summaries-v2))
-
\ No newline at end of file
diff --git a/src/clj/auto_ap/jobs/sysco.clj b/src/clj/auto_ap/jobs/sysco.clj
index bf6a9e61..c8465ce0 100644
--- a/src/clj/auto_ap/jobs/sysco.clj
+++ b/src/clj/auto_ap/jobs/sysco.clj
@@ -44,7 +44,6 @@
(dc/db conn)
50000))))
-
(def ^:dynamic bucket-name (:data-bucket env))
(def header-keys ["TransCode" "GroupID" "Company" "CustomerNumber" "InvoiceNumber" "RecordType" "Item" "InvoiceDocument" "AccountName" "AccountDunsNo" "InvoiceDate" "AccountDate" "CustomerPONo" "PaymentTerms" "TermsDescription" "StoreNumber" "CustomerName" "AddressLine1" "AddressLine2" "City1" "State1" "Zip1" "Phone1" "Duns1" "Hin1" "Dea1" "TIDCustomer" "ChainNumber" "BidNumber" "ContractNumber" "CompanyNumber" "BriefName" "Address" "Address2" "City2" "State2" "Zip2" "Phone2" "Duns2" "Hin2" "Dea2" "Tid_OPCO" "ObligationIndicator" "Manifest" "Route" "Stop" "TermsDiscountPercent" "TermsDiscountDueDate" "TermsNetDueDate" "TermsDiscountAmount" "TermsDiscountCode" "OrderDate" "DepartmentCode"])
@@ -56,14 +55,13 @@
(defn get-sysco-vendor []
(let [db (dc/db conn)]
(->
- (dc/q '[:find (pull ?v r)
- :in $ r
- :where [?v :vendor/name "Sysco"]]
- db
- d-vendors/default-read)
- first
- first)))
-
+ (dc/q '[:find (pull ?v r)
+ :in $ r
+ :where [?v :vendor/name "Sysco"]]
+ db
+ d-vendors/default-read)
+ first
+ first)))
(defn read-sysco-csv [k]
(-> (s3/get-object {:bucket-name bucket-name
@@ -73,34 +71,32 @@
csv/read-csv))
(defn check-okay-amount? [i]
- (dollars=
+ (dollars=
(:invoice/total i)
(reduce + 0.0 (map :invoice-expense-account/amount (:invoice/expense-accounts i)))))
(defn code-individual-items [invoice csv-rows tax]
(let [items (->> csv-rows
butlast
- (reduce
- (fn [acc row]
- (update acc (get-line-account (nth row item-name-index))
- (fnil + 0.0)
- (Double/parseDouble (nth row item-price-index))
- )
- )
- {})
- )
+ (reduce
+ (fn [acc row]
+ (update acc (get-line-account (nth row item-name-index))
+ (fnil + 0.0)
+ (Double/parseDouble (nth row item-price-index))))
+
+ {}))
items-with-tax (update items (get-line-account "TAX")
- (fnil + 0.0)
+ (fnil + 0.0)
tax)
- updated-invoice (assoc invoice :invoice/expense-accounts
- (for [[account amount] items-with-tax]
- #:invoice-expense-account {:db/id (random-tempid)
- :account account
- :location (:invoice/location invoice)
- :amount amount}))]
+ updated-invoice (assoc invoice :invoice/expense-accounts
+ (for [[account amount] items-with-tax]
+ #:invoice-expense-account {:db/id (random-tempid)
+ :account account
+ :location (:invoice/location invoice)
+ :amount amount}))]
(if (check-okay-amount? updated-invoice)
updated-invoice
- (do (alog/warn ::itemized-expenses-not-adding-up
+ (do (alog/warn ::itemized-expenses-not-adding-up
:invoice updated-invoice)
invoice))))
@@ -122,11 +118,11 @@
(header-row "AddressLine2")
(header-row "City1")
(header-row "City2")])
-
+
account-number (some-> account-number Long/parseLong str)
matching-client (and account-number
(d-clients/exact-match account-number))
-
+
_ (when-not matching-client
(throw (ex-info "cannot find matching client"
{:account-number account-number
@@ -153,9 +149,9 @@
:client/locations]
(:db/id matching-client))
location-hint
- location-hint )
+ location-hint)
:date (coerce/to-date date)
- :vendor (:db/id sysco-vendor )
+ :vendor (:db/id sysco-vendor)
:client (:db/id matching-client)
:import-status :import-status/imported
:status :invoice-status/unpaid
@@ -180,64 +176,54 @@
(s3/delete-object {:bucket-name bucket-name
:key k}))
-(defn get-test-invoice-file
+(defn get-test-invoice-file
([] (get-test-invoice-file 999))
- ( [i]
+ ([i]
(nth (->> (s3/list-objects-v2 {:bucket-name "data.prod.app.integreatconsult.com"
:prefix "sysco/imported"})
:object-summaries
- (map :key)
- )
+ (map :key))
i)))
-
-
-
-
-(comment
- (with-bindings { #'bucket-name "data.prod.app.integreatconsult.com"}
- (doall
- (for [n (range 930 940 )
- :let [result (-> (get-test-invoice-file n)
- read-sysco-csv
- (extract-invoice-details (get-sysco-vendor))
- )]
- #_#_:when (not (check-okay-amount? result))]
+(comment
+ (with-bindings {#'bucket-name "data.prod.app.integreatconsult.com"}
+ (doall
+ (for [n (range 930 940)
+ :let [result (-> (get-test-invoice-file n)
+ read-sysco-csv
+ (extract-invoice-details (get-sysco-vendor)))]
+ #_#_:when (not (check-okay-amount? result))]
result)))
- (with-bindings { #'bucket-name "data.prod.app.integreatconsult.com"}
- (let [result (-> "sysco/error/SYSCO050_00175962_20241010122639019.csv"
+ (with-bindings {#'bucket-name "data.prod.app.integreatconsult.com"}
+ (let [result (-> "sysco/error/SYSCO050_00175962_20241010122639019.csv"
read-sysco-csv
- (extract-invoice-details (get-sysco-vendor))
- )]
+ (extract-invoice-details (get-sysco-vendor)))]
- result))
-
- )
+ result)))
(defn import-sysco []
(let [sysco-vendor (get-sysco-vendor)
keys (->> (s3/list-objects-v2 {:bucket-name bucket-name
:prefix "sysco/pending"})
- :object-summaries
- (map :key))]
-
+ :object-summaries
+ (map :key))]
(alog/info ::importing-sysco
:count (count keys)
:keys (pr-str keys))
-
+
(let [transaction (->> keys
(mapcat (fn [k]
- (try
+ (try
(let [invoice-key (str "invoice-files/" (UUID/randomUUID) ".csv") ;
invoice-url (str "https://" (:data-bucket env) "/" invoice-key)]
(s3/copy-object {:source-bucket-name (:data-bucket env)
:destination-bucket-name (:data-bucket env)
:source-key k
:destination-key invoice-key})
- [[:propose-invoice
+ [[:propose-invoice
(-> k
read-sysco-csv
(extract-invoice-details sysco-vendor)
@@ -246,7 +232,7 @@
(alog/error ::cant-load-file
:file k
:error e e)
- (s3/copy-object {:source-bucket-name (:data-bucket env)
+ (s3/copy-object {:source-bucket-name (:data-bucket env)
:destination-bucket-name (:data-bucket env)
:source-key k
:destination-key (str "sysco/error/"
@@ -256,6 +242,5 @@
(doseq [k keys]
(mark-key k))))
-
(defn -main [& _]
(execute "sysco" import-sysco))
diff --git a/src/clj/auto_ap/jobs/vendor_usages.clj b/src/clj/auto_ap/jobs/vendor_usages.clj
index 3a674697..4c8bf35a 100644
--- a/src/clj/auto_ap/jobs/vendor_usages.clj
+++ b/src/clj/auto_ap/jobs/vendor_usages.clj
@@ -12,7 +12,7 @@
:where
[?v :vendor/name]
(or-join [?v ?c ?e]
- (and
+ (and
[?e :invoice/vendor ?v]
[?e :invoice/client ?c])
(and
diff --git a/src/clj/auto_ap/ledger.clj b/src/clj/auto_ap/ledger.clj
index 2f6a1f4a..7737c100 100644
--- a/src/clj/auto_ap/ledger.clj
+++ b/src/clj/auto_ap/ledger.clj
@@ -18,146 +18,137 @@
(t/plus (t/months -6))
(c/to-date))))
([start-date]
- (let [txes-missing-ledger-entries (->> (dc/q {:find ['?t ]
+ (let [txes-missing-ledger-entries (->> (dc/q {:find ['?t]
:in ['$ '?sd]
- :where [
- '[?t :transaction/date ?d]
+ :where ['[?t :transaction/date ?d]
'[(>= ?d ?sd)]
'(not [_ :journal-entry/original-entity ?t])
'(not [?t :transaction/amount 0.0])
'(not [?t :transaction/approval-status :transaction-approval-status/excluded])
- '(not [?t :transaction/approval-status :transaction-approval-status/suppressed])
- ]}
+ '(not [?t :transaction/approval-status :transaction-approval-status/suppressed])]}
(dc/db conn) start-date)
(map first)
(mapv (fn [t]
[:upsert-transaction {:db/id t}])))
-
- invoices-missing-ledger-entries (->> (dc/q {:find ['?t ]
- :in ['$ '?sd]
- :where ['[?t :invoice/date ?d]
- '[(>= ?d ?sd)]
- '(not [_ :journal-entry/original-entity ?t])
- '[?t :invoice/total ?amt]
- '[(not= 0.0 ?amt)]
- '(not [?t :invoice/status :invoice-status/voided])
- '(not [?t :invoice/import-status :import-status/pending])
- '(not [?t :invoice/exclude-from-ledger true])
- ]}
- (dc/db conn) start-date)
+ invoices-missing-ledger-entries (->> (dc/q {:find ['?t]
+ :in ['$ '?sd]
+ :where ['[?t :invoice/date ?d]
+ '[(>= ?d ?sd)]
+ '(not [_ :journal-entry/original-entity ?t])
+ '[?t :invoice/total ?amt]
+ '[(not= 0.0 ?amt)]
+ '(not [?t :invoice/status :invoice-status/voided])
+ '(not [?t :invoice/import-status :import-status/pending])
+ '(not [?t :invoice/exclude-from-ledger true])]}
+ (dc/db conn) start-date)
(map first)
(mapv (fn [i]
[:upsert-invoice {:db/id i}])))
- sales-summaries-missing-ledger-entries (->> (dc/q {:find ['?ss ]
- :in ['$ '?sd]
- :where ['[?ss :sales-summary/date ?d]
- '[(>= ?d ?sd)]
- '(not [_ :journal-entry/original-entity ?ss])
- '[?ss :sales-summary/items ?item]
- '[?item :ledger-mapped/account]
- ]}
- (dc/db conn) start-date)
- (map first)
- (mapv (fn [ss]
- [:upsert-sales-summary {:db/id ss}])))
+ sales-summaries-missing-ledger-entries (->> (dc/q {:find ['?ss]
+ :in ['$ '?sd]
+ :where ['[?ss :sales-summary/date ?d]
+ '[(>= ?d ?sd)]
+ '(not [_ :journal-entry/original-entity ?ss])
+ '[?ss :sales-summary/items ?item]
+ '[?item :ledger-mapped/account]]}
+ (dc/db conn) start-date)
+ (map first)
+ (mapv (fn [ss]
+ [:upsert-sales-summary {:db/id ss}])))
- repairs (vec (concat txes-missing-ledger-entries invoices-missing-ledger-entries sales-summaries-missing-ledger-entries))]
+ repairs (vec (concat txes-missing-ledger-entries invoices-missing-ledger-entries sales-summaries-missing-ledger-entries))]
(when (seq repairs)
- (mu/log ::ledger-repairs-needed
- :sample (take 3 repairs)
- :transaction-count (count txes-missing-ledger-entries)
- :invoice-count (count invoices-missing-ledger-entries)
- :sales-summary-count (count sales-summaries-missing-ledger-entries))
- @(dc/transact conn repairs)))))
-
+ (mu/log ::ledger-repairs-needed
+ :sample (take 3 repairs)
+ :transaction-count (count txes-missing-ledger-entries)
+ :invoice-count (count invoices-missing-ledger-entries)
+ :sales-summary-count (count sales-summaries-missing-ledger-entries))
+ @(dc/transact conn repairs)))))
(defn touch-transaction [e]
@(dc/transact conn [{:db/id "datomic.tx"
- :db/doc "touching transaction to update ledger"}
- [:upsert-transaction {:db/id e}]]))
+ :db/doc "touching transaction to update ledger"}
+ [:upsert-transaction {:db/id e}]]))
(defn touch-invoice [e]
@(dc/transact conn [{:db/id "datomic.tx"
- :db/doc "touching invoice to update ledger"}
- [:upsert-invoice {:db/id e}]]))
-
-
+ :db/doc "touching invoice to update ledger"}
+ [:upsert-invoice {:db/id e}]]))
(defn recently-changed-entities [start end]
(into #{}
- (map first)
- (dc/q '[:find ?e
- :in $
- :where (or [?e :transaction/date]
- [?e :invoice/date])]
- (dc/since (dc/db conn) start))))
+ (map first)
+ (dc/q '[:find ?e
+ :in $
+ :where (or [?e :transaction/date]
+ [?e :invoice/date])]
+ (dc/since (dc/db conn) start))))
(defn mismatched-transactions
([]
(mismatched-transactions (c/to-date (t/minus (t/now) (t/days 7)))
- (c/to-date (t/minus (t/now) (t/hours 1)))) )
+ (c/to-date (t/minus (t/now) (t/hours 1)))))
([changed-between-start changed-between-end]
(mu/trace ::calculating-mismatched-transactions
- [:range {:start changed-between-start
- :end changed-between-end}]
- (let [entities-to-consider (recently-changed-entities
- changed-between-start
- changed-between-end)
- _ (mu/log ::checking-mismatched-transactions
- :count (count entities-to-consider))
- jel-accounts (reduce
- (fn [acc [e lia]]
- (update acc e (fnil conj #{} ) lia))
- {}
- (dc/q '[:find ?e ?lia
- :in $ [?e ...]
- :where
- [?je :journal-entry/original-entity ?e]
- [?e :transaction/date]
- [?je :journal-entry/line-items ?li]
- [?li :journal-entry-line/account ?lia]
- [?lia :account/name]]
- (dc/db conn)
- entities-to-consider))
- transaction-accounts (reduce
- (fn [acc [e lia]]
- (update acc e (fnil conj #{} ) lia))
- {}
- (dc/q '[:find ?e ?lia
- :in $ [?e ...]
- :where
- [?e :transaction/date ?d]
- [?e :transaction/accounts ?li]
- (not [?e :transaction/approval-status :transaction-approval-status/excluded])
- (not [?e :transaction/approval-status :transaction-approval-status/suppressed])
- [?li :transaction-account/account ?lia]
- [?lia :account/name]
- [?e :transaction/amount ?amt]
- [(not= ?amt 0.0)]]
- (dc/db conn)
- entities-to-consider))]
- (->> transaction-accounts
- (filter
- (fn [[e accounts]] (not= accounts (get jel-accounts e))))
- (doall))))))
+ [:range {:start changed-between-start
+ :end changed-between-end}]
+ (let [entities-to-consider (recently-changed-entities
+ changed-between-start
+ changed-between-end)
+ _ (mu/log ::checking-mismatched-transactions
+ :count (count entities-to-consider))
+ jel-accounts (reduce
+ (fn [acc [e lia]]
+ (update acc e (fnil conj #{}) lia))
+ {}
+ (dc/q '[:find ?e ?lia
+ :in $ [?e ...]
+ :where
+ [?je :journal-entry/original-entity ?e]
+ [?e :transaction/date]
+ [?je :journal-entry/line-items ?li]
+ [?li :journal-entry-line/account ?lia]
+ [?lia :account/name]]
+ (dc/db conn)
+ entities-to-consider))
+ transaction-accounts (reduce
+ (fn [acc [e lia]]
+ (update acc e (fnil conj #{}) lia))
+ {}
+ (dc/q '[:find ?e ?lia
+ :in $ [?e ...]
+ :where
+ [?e :transaction/date ?d]
+ [?e :transaction/accounts ?li]
+ (not [?e :transaction/approval-status :transaction-approval-status/excluded])
+ (not [?e :transaction/approval-status :transaction-approval-status/suppressed])
+ [?li :transaction-account/account ?lia]
+ [?lia :account/name]
+ [?e :transaction/amount ?amt]
+ [(not= ?amt 0.0)]]
+ (dc/db conn)
+ entities-to-consider))]
+ (->> transaction-accounts
+ (filter
+ (fn [[e accounts]] (not= accounts (get jel-accounts e))))
+ (doall))))))
-(defn unbalanced-transactions
+(defn unbalanced-transactions
([] (unbalanced-transactions (c/to-date (t/minus (t/now) (t/days 7)))
(c/to-date (t/minus (t/now) (t/hours 1)))))
([changed-between-start changed-between-end]
(let [entities-to-consider (recently-changed-entities changed-between-start changed-between-end)]
(->> (dc/q '[:find ?je ?a (sum ?debit) (sum ?credit)
- :with ?jel
- :in $ [?je ...]
- :where [?je :journal-entry/amount ?a]
- [?je :journal-entry/line-items ?jel]
- [(get-else $ ?jel :journal-entry-line/debit 0.0) ?debit]
- [(get-else $ ?jel :journal-entry-line/credit 0.0) ?credit]
- ]
- (dc/db conn)
- entities-to-consider)
+ :with ?jel
+ :in $ [?je ...]
+ :where [?je :journal-entry/amount ?a]
+ [?je :journal-entry/line-items ?jel]
+ [(get-else $ ?jel :journal-entry-line/debit 0.0) ?debit]
+ [(get-else $ ?jel :journal-entry-line/credit 0.0) ?credit]]
+ (dc/db conn)
+ entities-to-consider)
(filter (fn [[_ a d c]]
(or (not (dollars= a d))
(not (dollars= a c)))))
@@ -165,15 +156,13 @@
(map (fn [je]
(pull-ref (dc/db conn) :journal-entry/original-entity je)))))))
-
-
(defn unbalanced-invoices
([] (unbalanced-invoices (c/to-date (t/minus (t/now) (t/days 7)))
(c/to-date (t/minus (t/now) (t/hours 1)))))
([changed-between-start changed-between-end]
(let [entities-to-consider (recently-changed-entities
- changed-between-start
- changed-between-end)]
+ changed-between-start
+ changed-between-end)]
(->> (dc/q '[:find ?je ?a (sum ?debit) (sum ?credit)
:with ?jel
:in $ [?je ...]
@@ -195,19 +184,19 @@
(defn mismatched-invoices
([]
(mismatched-invoices (c/to-date (t/minus (t/now) (t/days 7)))
- (c/to-date (t/minus (t/now) (t/hours 1)))) )
+ (c/to-date (t/minus (t/now) (t/hours 1)))))
([changed-between-start changed-between-end]
(let [entities-to-consider (recently-changed-entities changed-between-start changed-between-end)
jel-accounts (reduce
- (fn [acc [e lia]]
- (update acc e (fnil conj #{} ) lia))
- {}
- (dc/q '[:find ?e ?lia
+ (fn [acc [e lia]]
+ (update acc e (fnil conj #{}) lia))
+ {}
+ (dc/q '[:find ?e ?lia
:in $ [?e ...]
- :where
+ :where
[?je :journal-entry/original-entity ?e]
- [?e :invoice/date]
+ [?e :invoice/date]
[?je :journal-entry/line-items ?li]
[?li :journal-entry-line/account ?lia]
(not [?lia :account/numeric-code 21000])
@@ -215,10 +204,10 @@
(dc/db conn)
entities-to-consider))
invoice-accounts (reduce
- (fn [acc [e lia]]
- (update acc e (fnil conj #{} ) lia))
- {}
- (dc/q '[:find ?e ?lia
+ (fn [acc [e lia]]
+ (update acc e (fnil conj #{}) lia))
+ {}
+ (dc/q '[:find ?e ?lia
:in $ [?e ...]
:where
[?e :invoice/expense-accounts ?li]
@@ -230,12 +219,11 @@
(not [?e :invoice/exclude-from-ledger true])
[?e :invoice/import-status :import-status/imported]]
(dc/db conn)
- entities-to-consider))
- ]
- (filter
- (fn [[e accounts]]
- (not= accounts (get jel-accounts e)))
- invoice-accounts))))
+ entities-to-consider))]
+ (filter
+ (fn [[e accounts]]
+ (not= accounts (get jel-accounts e)))
+ invoice-accounts))))
(defn touch-broken-ledger []
(statsd/event {:title "Reconciling Ledger"
@@ -243,95 +231,91 @@
:priority :low}
nil)
(mu/trace ::fixing-mismatched-transactions
- []
- (mu/log ::started-fixing-mismatched-transactions)
- (let [mismatched-ts (mismatched-transactions)]
- (if (seq mismatched-ts)
- (do
- (mu/log ::found-mismatched-transactions
- :status "WARN"
- :count (count mismatched-ts)
- :sample (take 10 mismatched-ts))
- (doseq [[m] mismatched-ts]
- (touch-transaction m))
- (statsd/gauge "data.mismatched_transactions" (count (mismatched-transactions))))
- (statsd/gauge "data.mismatched_transactions" 0.0))))
+ []
+ (mu/log ::started-fixing-mismatched-transactions)
+ (let [mismatched-ts (mismatched-transactions)]
+ (if (seq mismatched-ts)
+ (do
+ (mu/log ::found-mismatched-transactions
+ :status "WARN"
+ :count (count mismatched-ts)
+ :sample (take 10 mismatched-ts))
+ (doseq [[m] mismatched-ts]
+ (touch-transaction m))
+ (statsd/gauge "data.mismatched_transactions" (count (mismatched-transactions))))
+ (statsd/gauge "data.mismatched_transactions" 0.0))))
(mu/trace ::fixing-unbalanced-transactions
- []
- (mu/log ::started-fixing-unbalanced-transactions)
- (let [unbalanced-ts (unbalanced-transactions)]
- (if (seq unbalanced-ts)
- (do
- (mu/log ::found-unbalanced-transactions
- :status "WARN"
- :count (count unbalanced-ts)
- :sample (take 10 unbalanced-ts))
- (doseq [m unbalanced-ts]
- (touch-transaction m))
- (statsd/gauge "data.unbalanced_transactions" (count (unbalanced-transactions))))
- (statsd/gauge "data.unbalanced_transactions" 0.0))))
+ []
+ (mu/log ::started-fixing-unbalanced-transactions)
+ (let [unbalanced-ts (unbalanced-transactions)]
+ (if (seq unbalanced-ts)
+ (do
+ (mu/log ::found-unbalanced-transactions
+ :status "WARN"
+ :count (count unbalanced-ts)
+ :sample (take 10 unbalanced-ts))
+ (doseq [m unbalanced-ts]
+ (touch-transaction m))
+ (statsd/gauge "data.unbalanced_transactions" (count (unbalanced-transactions))))
+ (statsd/gauge "data.unbalanced_transactions" 0.0))))
-
(mu/trace ::fixing-mismatched-invoices
- []
- (mu/log ::started-fixing-mismatched-invoices)
- (let [mismatched-is (mismatched-invoices)]
- (if (seq mismatched-is)
- (do
- (mu/log ::found-mismatched-invoices
- :status "WARN"
- :count (count mismatched-is)
- :sample (take 10 mismatched-is))
- (doseq [[m] mismatched-is]
- (touch-invoice m))
- (statsd/gauge "data.mismatched_invoices" (count (mismatched-invoices))))
- (statsd/gauge "data.mismatched_invoices" 0.0))))
-
+ []
+ (mu/log ::started-fixing-mismatched-invoices)
+ (let [mismatched-is (mismatched-invoices)]
+ (if (seq mismatched-is)
+ (do
+ (mu/log ::found-mismatched-invoices
+ :status "WARN"
+ :count (count mismatched-is)
+ :sample (take 10 mismatched-is))
+ (doseq [[m] mismatched-is]
+ (touch-invoice m))
+ (statsd/gauge "data.mismatched_invoices" (count (mismatched-invoices))))
+ (statsd/gauge "data.mismatched_invoices" 0.0))))
+
(mu/trace ::fixing-unbalanced-invoices
- []
- (mu/log ::started-fixing-unbalance-invoices)
- (let [unbalanced-is (unbalanced-invoices)]
- (if (seq unbalanced-is)
- (do
- (mu/log ::found-mismatched-invoices
- :status "WARN"
- :count (count unbalanced-is)
- :sample (take 10 unbalanced-is))
- (doseq [m unbalanced-is]
- (touch-invoice m))
- (statsd/gauge "data.unbalanced_invoices" (count (unbalanced-invoices))))
- (statsd/gauge "data.unbalanced_invoices" 0.0))))
+ []
+ (mu/log ::started-fixing-unbalance-invoices)
+ (let [unbalanced-is (unbalanced-invoices)]
+ (if (seq unbalanced-is)
+ (do
+ (mu/log ::found-mismatched-invoices
+ :status "WARN"
+ :count (count unbalanced-is)
+ :sample (take 10 unbalanced-is))
+ (doseq [m unbalanced-is]
+ (touch-invoice m))
+ (statsd/gauge "data.unbalanced_invoices" (count (unbalanced-invoices))))
+ (statsd/gauge "data.unbalanced_invoices" 0.0))))
(statsd/event {:title "Finished Reconciling Ledger"
:text "This process looks for unbalance ledger entries, or missing ledger entries"
:priority :low}
nil))
-
(defn build-account-lookup [client-id]
(let [accounts (by :db/id (map first (dc/q {:find ['(pull ?e [:db/id :account/name
- :account/numeric-code
+ :account/numeric-code
{:account/type [:db/ident]
- :account/client-overrides [:account-client-override/client :account-client-override/name]}
- ])]
+ :account/client-overrides [:account-client-override/client :account-client-override/name]}])]
:in ['$]
:where ['[?e :account/name]]}
- (dc/db conn ))))
+ (dc/db conn))))
bank-accounts (by :db/id (map first (dc/q {:find ['(pull ?e [:db/id :bank-account/name :bank-account/numeric-code {:bank-account/type [:db/ident]}])]
:in ['$]
:where ['[?e :bank-account/name]]}
- (dc/db conn))))
+ (dc/db conn))))
overrides-by-client (->> accounts
vals
(mapcat (fn [a]
(map (fn [o]
[[(:db/id a) (:db/id (:account-client-override/client o))]
(:account-client-override/name o)])
- (:account/client-overrides a))
- ) )
- (into {} ))]
+ (:account/client-overrides a))))
+ (into {}))]
(fn [a]
{:name (or (:bank-account/name (bank-accounts a))
(overrides-by-client [a client-id])
@@ -348,7 +332,7 @@
:client_id client-id})))
(defn find-mismatch-index []
- (reduce + 0
+ (reduce + 0
(for [c (map first (dc/q '[:find ?c :where [?c :client/code]] (dc/db conn)))
:let [_ (println "searching for" c)
a (->> (dc/index-pull (dc/db conn)
@@ -356,16 +340,15 @@
:selector [:db/id :journal-entry-line/location :journal-entry-line/account :journal-entry-line/client+account+location+date {:journal-entry/_line-items [:journal-entry/date :journal-entry/client]}]
:start [:journal-entry-line/client+account+location+date [c]]})
(take-while (fn [result]
- (= c (first (:journal-entry-line/client+account+location+date result)))
- ))
+ (= c (first (:journal-entry-line/client+account+location+date result)))))
(filter (fn [{index :journal-entry-line/client+account+location+date :as result}]
(not= index
[(-> result :journal-entry/_line-items :journal-entry/client :db/id)
(-> result :journal-entry-line/account :db/id)
(-> result :journal-entry-line/location)
(-> result :journal-entry/_line-items :journal-entry/date)]))))]]
- (do (println (count a))
- (count a)))))
+ (do (println (count a))
+ (count a)))))
(defn clients-needing-refresh [db available]
(let [clients (->>
@@ -383,18 +366,18 @@
(filter (comp available :db/id) clients)
clients)))
-#_(clients-needing-refresh (dc/db conn) #{ 17592273679867})
+#_(clients-needing-refresh (dc/db conn) #{17592273679867})
#_(comment [17592334354011 #inst "0024-08-03T07:52:58.000-00:00"]
- [17592302554688 #inst "0023-07-20T07:52:58.000-00:00"]
- [17592302554682 #inst "0023-07-16T07:52:58.000-00:00"]
- [17592302554691 #inst "0023-07-22T07:52:58.000-00:00"]
- [17592334353995 #inst "0024-08-10T07:52:58.000-00:00"]
- [17592302554694 #inst "0023-07-27T07:52:58.000-00:00"]
- [17592241669405 #inst "0218-08-04T07:52:58.000-00:00"]
- [17592334353207 #inst "0024-07-27T07:52:58.000-00:00"]
- [17592302554685 #inst "0023-07-16T07:52:58.000-00:00"]
- [17592334353244 #inst "0024-07-14T07:52:58.000-00:00"])
+ [17592302554688 #inst "0023-07-20T07:52:58.000-00:00"]
+ [17592302554682 #inst "0023-07-16T07:52:58.000-00:00"]
+ [17592302554691 #inst "0023-07-22T07:52:58.000-00:00"]
+ [17592334353995 #inst "0024-08-10T07:52:58.000-00:00"]
+ [17592302554694 #inst "0023-07-27T07:52:58.000-00:00"]
+ [17592241669405 #inst "0218-08-04T07:52:58.000-00:00"]
+ [17592334353207 #inst "0024-07-27T07:52:58.000-00:00"]
+ [17592302554685 #inst "0023-07-16T07:52:58.000-00:00"]
+ [17592334353244 #inst "0024-07-14T07:52:58.000-00:00"])
(defn mark-all-clients-dirty []
(auto-ap.datomic/audit-transact-batch
@@ -429,17 +412,17 @@
(for [{client :db/id code :client/code bank-accounts :client/bank-accounts} clients
{bank-account :db/id bac :bank-account/code} bank-accounts]
(let [{[_ _ _ _ _ _ running-balance] :v} (->> (dc/index-range db :journal-entry-line/running-balance-tuple [client bank-account "A"] [client bank-account "A" #inst "2050-01-01"])
- seq
- (sort-by (fn [{id :e [_ _ _ current-date] :v}]
- [current-date id]))
- (last))
+ seq
+ (sort-by (fn [{id :e [_ _ _ current-date] :v}]
+ [current-date id]))
+ (last))
running-balance (or running-balance 0.0)]
(alog/info ::updating-bank-account-balance
:bank-account bac
:balance running-balance)
@(dc/transact conn [{:db/id bank-account
- :bank-account/current-balance-synced (c/to-date (t/now))
- :bank-account/current-balance running-balance}])))))))
+ :bank-account/current-balance-synced (c/to-date (t/now))
+ :bank-account/current-balance running-balance}])))))))
;; TODO using iol-ion query as the base, building running balance sets
(defn upsert-running-balance
@@ -499,98 +482,94 @@
(comment
(pull-id (dc/db conn) [:client/code "SCCB"])
-
- #_(do
- (mu/with-context {:service "upsert-running-balance"
- :source "upsert-running-balance" }
- (mu/trace ::updating-balance
- [:service "upsert-running-balance"
- :source "upsert-running-balance" ]
- (let [db (dc/db conn)
- starting-at (c/to-date (t/now))
- _ (mark-client-dirty "NGA1")
- clients (clients-needing-refresh db)
- _ (alog/info ::clients-needing-update :clients clients :count (count clients))
- client-change-stats (atom {})
- changes (for [c clients
- :let [client-id (:db/id c)
- account-lookup (build-account-lookup client-id)]
- running-balance-set (account-sets db client-id)
- running-balance-change (->> running-balance-set
- (reduce
- (fn [{:keys [changes last-running-balance]}
- line-item]
- (if (= 0 (rand-int 1000))
- (println (.-account-id line-item) (.-debit line-item) (.-credit line-item)))
- (let [delta (if (#{:account-type/asset
- :account-type/dividend
- :account-type/expense} (:account_type (account-lookup (.-account-id line-item))))
- (- (or (.-debit line-item) 0.0) (or (.-credit line-item) 0.0))
- (- (or (.-credit line-item) 0.0) (or (.-debit line-item) 0.0)))
- correct-running-balance (+ last-running-balance delta)
- running-balance-changed? (not (dollars= correct-running-balance (or (.-running-balance line-item) 0.0)))]
- (when running-balance-changed?
- (swap! client-change-stats update (:client/code c) (fnil inc 0)))
- (cond-> {:last-account-lookup account-lookup
- :last-running-balance correct-running-balance
- :changes changes}
-
- running-balance-changed?
- (update :changes conj {:db/id (.-id line-item)
- :journal-entry-line/running-balance correct-running-balance}))))
- {:last-running-balance 0.0})
- :changes)]
- running-balance-change)]
-
- (mu/trace ::update-running-balance []
- (auto-ap.datomic/audit-transact-batch changes
- {:user/name "running balance updater"}))
- (auto-ap.datomic/audit-transact (mapv (fn [c]
- {:db/id (:db/id c)
- :client/last-running-balance starting-at})
- clients)
- {:user/name "running balance updater"})
- (alog/info ::change-stats :stats @client-change-stats)
- (refresh-bank-account-balances (map :db/id clients))
- (count changes)))))
+
+ #_(do
+ (mu/with-context {:service "upsert-running-balance"
+ :source "upsert-running-balance"}
+ (mu/trace ::updating-balance
+ [:service "upsert-running-balance"
+ :source "upsert-running-balance"]
+ (let [db (dc/db conn)
+ starting-at (c/to-date (t/now))
+ _ (mark-client-dirty "NGA1")
+ clients (clients-needing-refresh db)
+ _ (alog/info ::clients-needing-update :clients clients :count (count clients))
+ client-change-stats (atom {})
+ changes (for [c clients
+ :let [client-id (:db/id c)
+ account-lookup (build-account-lookup client-id)]
+ running-balance-set (account-sets db client-id)
+ running-balance-change (->> running-balance-set
+ (reduce
+ (fn [{:keys [changes last-running-balance]}
+ line-item]
+ (if (= 0 (rand-int 1000))
+ (println (.-account-id line-item) (.-debit line-item) (.-credit line-item)))
+ (let [delta (if (#{:account-type/asset
+ :account-type/dividend
+ :account-type/expense} (:account_type (account-lookup (.-account-id line-item))))
+ (- (or (.-debit line-item) 0.0) (or (.-credit line-item) 0.0))
+ (- (or (.-credit line-item) 0.0) (or (.-debit line-item) 0.0)))
+ correct-running-balance (+ last-running-balance delta)
+ running-balance-changed? (not (dollars= correct-running-balance (or (.-running-balance line-item) 0.0)))]
+ (when running-balance-changed?
+ (swap! client-change-stats update (:client/code c) (fnil inc 0)))
+ (cond-> {:last-account-lookup account-lookup
+ :last-running-balance correct-running-balance
+ :changes changes}
+
+ running-balance-changed?
+ (update :changes conj {:db/id (.-id line-item)
+ :journal-entry-line/running-balance correct-running-balance}))))
+ {:last-running-balance 0.0})
+ :changes)]
+ running-balance-change)]
+
+ (mu/trace ::update-running-balance []
+ (auto-ap.datomic/audit-transact-batch changes
+ {:user/name "running balance updater"}))
+ (auto-ap.datomic/audit-transact (mapv (fn [c]
+ {:db/id (:db/id c)
+ :client/last-running-balance starting-at})
+ clients)
+ {:user/name "running balance updater"})
+ (alog/info ::change-stats :stats @client-change-stats)
+ (refresh-bank-account-balances (map :db/id clients))
+ (count changes)))))
(mark-client-dirty "NGA1")
-(mark-all-clients-dirty)
+ (mark-all-clients-dirty)
-(count (clients-needing-refresh (dc/db conn)))
+ (count (clients-needing-refresh (dc/db conn)))
- (upsert-running-balance)
+ (upsert-running-balance)
;; SETUP running-balance-tuple
- (doseq [[c] (dc/q '[:find ?c
- :in $
- :where [?c :client/code]]
- (dc/db conn))]
- (println "CLIENT " c)
- (auto-ap.datomic/audit-transact-batch
- (for [[date client line] (->> (dc/q '[:find ?jed ?jec (pull ?jel [:journal-entry-line/debit :journal-entry-line/credit :journal-entry-line/running-balance :db/id])
- :in $ ?jec
- :where [?je :journal-entry/client ?jec]
- [?je :journal-entry/date ?jed]
- [?je :journal-entry/line-items ?jel]]
- (dc/db conn)
- c))]
- {:db/id (:db/id line)
- :journal-entry-line/date date
- :journal-entry-line/client client})
+ (doseq [[c] (dc/q '[:find ?c
+ :in $
+ :where [?c :client/code]]
+ (dc/db conn))]
+ (println "CLIENT " c)
+ (auto-ap.datomic/audit-transact-batch
+ (for [[date client line] (->> (dc/q '[:find ?jed ?jec (pull ?jel [:journal-entry-line/debit :journal-entry-line/credit :journal-entry-line/running-balance :db/id])
+ :in $ ?jec
+ :where [?je :journal-entry/client ?jec]
+ [?je :journal-entry/date ?jed]
+ [?je :journal-entry/line-items ?jel]]
+ (dc/db conn)
+ c))]
+ {:db/id (:db/id line)
+ :journal-entry-line/date date
+ :journal-entry-line/client client})
+ {:user/name "backfill-client and dates"})
+ (println "done."))
- {:user/name "backfill-client and dates"})
- (println "done."))
-
-
-
- #_(dc/q '[:find (pull ?je [*]) (pull ?jel [*])
- :where [?je :journal-entry/line-items ?jel]
- (not [?jel :journal-entry-line/running-balance-tuple])]
- (dc/db conn)))
-
+ #_(dc/q '[:find (pull ?je [*]) (pull ?jel [*])
+ :where [?je :journal-entry/line-items ?jel]
+ (not [?jel :journal-entry-line/running-balance-tuple])]
+ (dc/db conn)))
;; TODO only enable once IOL is set up in clod
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
diff --git a/src/clj/auto_ap/logging.clj b/src/clj/auto_ap/logging.clj
index fd99f7ee..05bda05b 100644
--- a/src/clj/auto_ap/logging.clj
+++ b/src/clj/auto_ap/logging.clj
@@ -1,7 +1,6 @@
(ns auto-ap.logging
(:require [com.brunobonacci.mulog :as mu]))
-
(defmacro with-context-as [ctx s & body]
`(mu/with-context ~ctx
(let [~s (mu/local-context)]
@@ -12,13 +11,13 @@
~@body))
(defmacro info [x & kvs]
- `(mu/log ~x :status "INFO" ~@kvs ))
+ `(mu/log ~x :status "INFO" ~@kvs))
(defmacro warn [x & kvs]
- `(mu/log ~x :status "WARN" ~@kvs ))
+ `(mu/log ~x :status "WARN" ~@kvs))
(defmacro error [x & kvs]
- `(mu/log ~x :status "ERROR" ~@kvs ))
+ `(mu/log ~x :status "ERROR" ~@kvs))
(defn peek
([x]
diff --git a/src/clj/auto_ap/parse.clj b/src/clj/auto_ap/parse.clj
index a79db964..5a6957b5 100644
--- a/src/clj/auto_ap/parse.clj
+++ b/src/clj/auto_ap/parse.clj
@@ -16,7 +16,6 @@
(defonce last-text (atom nil))
-
(defn template-applies? [text {:keys [keywords]}]
(every? #(re-find % text) keywords))
@@ -24,7 +23,7 @@
([text template]
(alog/info ::template-determined
:template (str template))
-
+
(if (:multi template)
(mapcat
#(extract-template % text (dissoc template :multi))
@@ -60,8 +59,6 @@
first
(extract-template text)))
-
-
(defmulti parse-file
"Parses a file based on its extension. Accepts options as additional arguments.
Options:
@@ -74,17 +71,16 @@
:socket-timeout 120000}} {:function-name "glimpse2" :payload
(json/write-str
(alog/peek ::x {"url" (str "https://" "data.prod.app.integreatconsult.com" "/" f)}))})))]
-
(alog/info ::glimpse2-payload :payload result)
(-> result
- json/read-str)))
+ json/read-str)))
(defn glimpse2 [file]
- (try
+ (try
(let [tmp-key (str "glimpse2/import/" (java.util.UUID/randomUUID) ".pdf")
_ (with-open [f (io/input-stream file)]
- (s3/put-object {:bucket-name "data.prod.app.integreatconsult.com"
+ (s3/put-object {:bucket-name "data.prod.app.integreatconsult.com"
:key tmp-key
:input-stream f}))
is (invoke-glimpse2 tmp-key)]
@@ -99,7 +95,7 @@
:total (get i "total")
:invoice-number (get i "invoice_number")
:template "None found - defaulting to ChatGPT"}))
-
+
(catch Exception e
(alog/warn ::glimpse2-not-work :error e)
nil)))
@@ -107,7 +103,7 @@
(defmethod parse-file
"pdf"
[file _ & {:keys [allow-glimpse?] :or {allow-glimpse? false}}]
- (or
+ (or
(-> (sh/sh "pdftotext" "-layout" file "-")
:out
parse)
@@ -123,7 +119,6 @@
[file filename & _]
(excel/parse-file file filename))
-
(defmethod parse-file
"xlsx"
[file filename & _]
@@ -157,8 +152,8 @@
client-word-match (->> clients
(map
(fn [{:keys [:client/matches :client/name] :as client :or {matches []}}]
- (let [client-words (-> #{}
- (into
+ (let [client-words (-> #{}
+ (into
(mapcat
(fn [match] (str/split (.toLowerCase match) #"\s"))
matches))
@@ -175,13 +170,13 @@
([clients invoice-client-name]
(->> clients
(filter (fn [{:keys [:client/matches :client/location-matches :client/locations :client/name] :as client :or {matches []}}]
- (seq
- (filter (fn [m]
- (and
- m
- invoice-client-name
- (= (.toLowerCase invoice-client-name) (.toLowerCase m))))
- (conj matches name)))))
+ (seq
+ (filter (fn [m]
+ (and
+ m
+ invoice-client-name
+ (= (.toLowerCase invoice-client-name) (.toLowerCase m))))
+ (conj matches name)))))
first)))
(defn best-location-match [client text full-text]
@@ -207,9 +202,9 @@
(defn dbg-parse [v]
(println v)
(map
- (fn [x] (dissoc x :full-text :text))
- (parse v)))
-
+ (fn [x] (dissoc x :full-text :text))
+ (parse v)))
+
#_(nth (re-find #"ELECTRONICALLY.*\n\s*(.*?)\s{2,}" @last-text)
- 1)
+ 1)
diff --git a/src/clj/auto_ap/parse/csv.clj b/src/clj/auto_ap/parse/csv.clj
index eb3997fd..63aca950 100644
--- a/src/clj/auto_ap/parse/csv.clj
+++ b/src/clj/auto_ap/parse/csv.clj
@@ -23,7 +23,6 @@
(str/includes? (str header) "Document Number")
:philz
-
(str/includes? (str header) "DISCOUNT_MESSAGE")
:wismettac
@@ -35,7 +34,7 @@
(str/includes? (str header) "PARENT CUSTOMER NAME")
:worldwide
-
+
:else
nil)]
(alog/info ::csv-type-determined :type csv-type)
@@ -44,18 +43,17 @@
(defmulti parse-csv
determine
:default #_{:clj-kondo/ignore [:unused-binding]}
- (fn default [rows]
- nil))
+ (fn default [rows]
+ nil))
(defn parse-date-fallover [d fmts]
(when-let [valid-fmt (->> fmts
- (filter (fn [f]
- (try
- (u/parse-value :clj-time f d)
- (catch Exception _
- nil))
- ))
- (first))]
+ (filter (fn [f]
+ (try
+ (u/parse-value :clj-time f d)
+ (catch Exception _
+ nil))))
+ (first))]
(u/parse-value :clj-time valid-fmt d)))
(defmethod parse-csv :sysco-style-1
@@ -83,7 +81,7 @@
(defmethod parse-csv :sysco-style-2
[rows]
-
+
(let [header (first rows)]
(transduce
(comp (drop 1)
@@ -109,7 +107,7 @@
(map (fn [[_ po-number _ invoice-number invoice-date customer value :as row]]
{:vendor-code "Mama Lu's Foods"
:customer-identifier customer
- :invoice-number (str po-number "-" invoice-number )
+ :invoice-number (str po-number "-" invoice-number)
:date (parse-date-fallover invoice-date ["M/d/yyyy HH:ss" "M/d/yyyy HH:mm:ss aa" "M/d/yyyy"])
:total (str/replace value #"," "")
:text (str/join " " row)
@@ -122,10 +120,10 @@
[rows]
(transduce
(comp (drop 1)
- (map (fn [[ po-number _ invoice-number invoice-date customer value :as row]]
+ (map (fn [[po-number _ invoice-number invoice-date customer value :as row]]
{:vendor-code "Mama Lu's Foods"
:customer-identifier customer
- :invoice-number (str po-number "-" invoice-number )
+ :invoice-number (str po-number "-" invoice-number)
:date (parse-date-fallover invoice-date ["M/d/yyyy HH:ss" "M/d/yyyy HH:mm:ss aa" "M/d/yyyy"])
:total (str/replace value #"," "")
:text (str/join " " row)
@@ -137,8 +135,8 @@
(defmethod parse-csv :philz
[rows]
(transduce
- (comp
- (filter (fn [[_ _ _ _ _ status _ _ _ ]]
+ (comp
+ (filter (fn [[_ _ _ _ _ status _ _ _]]
(= status "Billed")))
(map (fn [[dt _ doc-number name _ _ _ _ amount :as row]]
{:vendor-code "PHILZ COFFEE, INC"
@@ -147,10 +145,8 @@
:date (some-> dt not-empty (parse-date-fallover ["MM/dd/yyyy"]))
:total (str/replace amount #"," "")
:text (str/join " " row)
- :full-text (str/join " " row)}))
+ :full-text (str/join " " row)})))
-
- )
conj
[]
(drop 1 rows)))
@@ -158,15 +154,14 @@
(defmethod parse-csv :wismettac
[rows]
(transduce
- (comp
+ (comp
(map (fn [[inv_number inv_dt total :as row]]
{:vendor-code "Wismettac"
:invoice-number inv_number
:date (some-> inv_dt not-empty (parse-date-fallover ["MM/dd/yyyy"]))
:total (str/replace total #"," "")
:text (str/join " " row)
- :full-text (str/join " " row)}))
- )
+ :full-text (str/join " " row)})))
conj
[]
(drop 1 rows)))
@@ -174,36 +169,34 @@
(defmethod parse-csv :ledyard
[rows]
(transduce
- (comp
- (map (fn [[invoice-number date due amount standard :as row]]
- {:vendor-code "Performance Food Group - LEDYARD"
- :invoice-number invoice-number
- :date (some-> date not-empty (parse-date-fallover ["MM/dd/yy"]))
- :due (some-> due not-empty (parse-date-fallover ["MM/dd/yy"]))
- :total (str/replace amount #"[\$,]" "")
- :text (str/join " " row)
- :full-text (str/join " " row)}))
- )
- conj
- []
- (drop 1 rows)))
+ (comp
+ (map (fn [[invoice-number date due amount standard :as row]]
+ {:vendor-code "Performance Food Group - LEDYARD"
+ :invoice-number invoice-number
+ :date (some-> date not-empty (parse-date-fallover ["MM/dd/yy"]))
+ :due (some-> due not-empty (parse-date-fallover ["MM/dd/yy"]))
+ :total (str/replace amount #"[\$,]" "")
+ :text (str/join " " row)
+ :full-text (str/join " " row)})))
+ conj
+ []
+ (drop 1 rows)))
(defmethod parse-csv :worldwide
[rows]
(transduce
- (comp
- (map (fn [[_ customer-name _ inv date amount :as row]]
- {:vendor-code "Worldwide Produce"
- :customer-identifier customer-name
- :invoice-number (str/replace inv #"[=\"]" "")
- :date (some-> date not-empty (parse-date-fallover ["MM/dd/yy"]))
- :total (str/replace amount #"[\$,]" "")
- :text (str/join " " row)
- :full-text (str/join " " row)}))
- )
- conj
- []
- (drop 1 rows)))
+ (comp
+ (map (fn [[_ customer-name _ inv date amount :as row]]
+ {:vendor-code "Worldwide Produce"
+ :customer-identifier customer-name
+ :invoice-number (str/replace inv #"[=\"]" "")
+ :date (some-> date not-empty (parse-date-fallover ["MM/dd/yy"]))
+ :total (str/replace amount #"[\$,]" "")
+ :text (str/join " " row)
+ :full-text (str/join " " row)})))
+ conj
+ []
+ (drop 1 rows)))
#_{:clj-kondo/ignore [:unused-binding]}
(defmethod parse-csv nil
diff --git a/src/clj/auto_ap/parse/excel.clj b/src/clj/auto_ap/parse/excel.clj
index c296ccc2..f8305d3f 100644
--- a/src/clj/auto_ap/parse/excel.clj
+++ b/src/clj/auto_ap/parse/excel.clj
@@ -6,42 +6,38 @@
[clojure.data.json :as json]
[config.core :refer [env]]
[clojure.java.io :as io]
- [amazonica.aws.s3 :as s3])
- )
-
-
+ [amazonica.aws.s3 :as s3]))
(defn template-applies? [text {:keys [keywords]}]
-
+
(every? #(re-find % text) keywords))
(defn extract [wb {:keys [extract vendor parser]}]
(if (fn? extract)
(extract wb vendor)
#_[(reduce-kv
- (fn [invoice k [regex offset-row offset-column extract-regex]]
- (assoc invoice k
- (->> wb
- (d/sheet-seq)
- first
- (d/cell-seq)
- (filter (fn [cell]
- (re-find regex (str (d/read-cell cell)))))
- (map (fn [cell]
- (let [address (.getAddress cell)
- cell-value (str (d/read-cell (d/select-cell (.toString (CellAddress. (+ offset-row (.getRow address)) (+ offset-column (.getColumn address)) ))
- (first (d/sheet-seq wb)))))
- raw-result (if extract-regex
- (second (re-find extract-regex cell-value))
-
- cell-value)]
- (if (get parser k)
- (u/parse-value (first (get parser k) ) (second (get parser k) ) raw-result)
- raw-result
- ))))
- first)))
- {:vendor-code vendor}
- extract)]))
+ (fn [invoice k [regex offset-row offset-column extract-regex]]
+ (assoc invoice k
+ (->> wb
+ (d/sheet-seq)
+ first
+ (d/cell-seq)
+ (filter (fn [cell]
+ (re-find regex (str (d/read-cell cell)))))
+ (map (fn [cell]
+ (let [address (.getAddress cell)
+ cell-value (str (d/read-cell (d/select-cell (.toString (CellAddress. (+ offset-row (.getRow address)) (+ offset-column (.getColumn address))))
+ (first (d/sheet-seq wb)))))
+ raw-result (if extract-regex
+ (second (re-find extract-regex cell-value))
+
+ cell-value)]
+ (if (get parser k)
+ (u/parse-value (first (get parser k)) (second (get parser k)) raw-result)
+ raw-result))))
+ first)))
+ {:vendor-code vendor}
+ extract)]))
(defn extract-sheet-details [bucket object]
(doto
@@ -57,7 +53,7 @@
[file _]
(let [tmp-key (str "xls-invoice/import/" (java.util.UUID/randomUUID))
_ (with-open [f (io/input-stream file)]
- (s3/put-object {:bucket-name (:data-bucket env)
+ (s3/put-object {:bucket-name (:data-bucket env)
:key tmp-key
:input-stream f}))
sheet (extract-sheet-details (:data-bucket env) tmp-key)
@@ -67,9 +63,6 @@
first
(extract sheet))))
-
-
-
(defn xls-date->date [f]
(when (not-empty f)
(let [f (Double/parseDouble f)
diff --git a/src/clj/auto_ap/parse/util.clj b/src/clj/auto_ap/parse/util.clj
index ac22ae62..df4b7057 100644
--- a/src/clj/auto_ap/parse/util.clj
+++ b/src/clj/auto_ap/parse/util.clj
@@ -8,11 +8,9 @@
(defmulti parse-value (fn [method _ _]
method))
-
(defmethod parse-value :trim-commas
[_ _ value]
- (str/replace value #"," "")
- )
+ (str/replace value #"," ""))
(defmethod parse-value :trim-commas-and-remove-dollars
[_ _ value]
(str/replace (str/replace value #"," "") #"\$" ""))
@@ -20,7 +18,7 @@
(defmethod parse-value :trim-commas-and-remove-dollars-and-invert-parentheses
[_ _ value]
(let [v (str/replace (str/replace value #"," "") #"\$" "")]
- (if-let [[_ a ] (re-find #"\((.*)\)" v)]
+ (if-let [[_ a] (re-find #"\((.*)\)" v)]
(str "-" a)
v)))
@@ -39,13 +37,12 @@
[_ _ value]
(let [format "yyyy-MM-dd"
- [month day year] (str/split (-> value
- (str/replace #"\s+" " ")
- )
+ [month day year] (str/split (-> value
+ (str/replace #"\s+" " "))
#"\s")
- value (str "20" year "-" month "-" day) ]
- (try
+ value (str "20" year "-" month "-" day)]
+ (try
(time/from-time-zone (f/parse (f/formatter format) value)
(time/time-zone-for-id "America/Los_Angeles"))
(catch Exception e
@@ -59,15 +56,14 @@
[format])]
(reduce
(fn [_ format]
- (try
+ (try
(reduced (time/from-time-zone (f/parse (f/formatter format) value)
(time/time-zone-for-id "America/Los_Angeles")))
(catch Exception e
(alog/warn ::cant-parse-date :error e :raw-value (str value))
nil)))
nil
- format)
- ))
+ format)))
(defmethod parse-value nil
[_ _ value]
diff --git a/src/clj/auto_ap/pdf/ledger.clj b/src/clj/auto_ap/pdf/ledger.clj
index 567063e6..8a0d6a2c 100644
--- a/src/clj/auto_ap/pdf/ledger.clj
+++ b/src/clj/auto_ap/pdf/ledger.clj
@@ -23,7 +23,7 @@
(let [cell-contents (cond
(and (= :dollar (:format cell))
(or (nil? (:value cell))
- (dollars-0? (:value cell))))
+ (dollars-0? (:value cell))))
"-"
(= :dollar (:format cell))
@@ -38,7 +38,7 @@
(cond-> {}
(:border cell) (assoc :border true
- :set-border (:border cell))
+ :set-border (:border cell))
(:colspan cell) (assoc :colspan (:colspan cell))
(:align cell) (assoc :align (:align cell))
(= :dollar (:format cell)) (assoc :align :right)
@@ -47,8 +47,7 @@
(:color cell) (assoc :color (:color cell))
(:bg-color cell) (assoc :background-color (:bg-color cell)))
- cell-contents
- ]))
+ cell-contents]))
(defn cell-count [table]
(let [counts (map count (:rows table))]
@@ -59,9 +58,9 @@
(defn table->pdf [table widths]
(let [cell-count (cell-count table)]
(-> [:pdf-table {:header (mapv
- (fn [header]
- (map cell->pdf header))
- (:header table))
+ (fn [header]
+ (map cell->pdf header))
+ (:header table))
:cell-border false
:width-percent (cond (<= cell-count 5)
50
@@ -77,15 +76,13 @@
:else
100)}
- widths
- ]
+ widths]
- (into
- (for [row (:rows table)]
- (into []
- (for [cell (take cell-count (concat row (repeat nil)))]
- (cell->pdf cell)
- ))))
+ (into
+ (for [row (:rows table)]
+ (into []
+ (for [cell (take cell-count (concat row (repeat nil)))]
+ (cell->pdf cell)))))
(conj (take cell-count (repeat (cell->pdf {:value " "})))))))
(defn split-table [table n]
@@ -95,47 +92,47 @@
(let [new-table (-> table
(update :rows (fn [rows]
(map
- (fn [[header & rest]]
- (into [header]
- (take (dec n) rest)))
- rows)))
+ (fn [[header & rest]]
+ (into [header]
+ (take (dec n) rest)))
+ rows)))
(update :header (fn [headers]
(map
- (fn [[title & header]]
- (first
- (reduce
- (fn [[so-far a] next]
- (let [new-a (+ a (or (:colspan next)
- 1))]
- (if (<= new-a n)
- [(conj so-far next) new-a]
- [so-far new-a])))
+ (fn [[title & header]]
+ (first
+ (reduce
+ (fn [[so-far a] next]
+ (let [new-a (+ a (or (:colspan next)
+ 1))]
+ (if (<= new-a n)
+ [(conj so-far next) new-a]
+ [so-far new-a])))
- [[title] 1]
- header)))
- headers))))
+ [[title] 1]
+ header)))
+ headers))))
remaining (-> table
(update :rows (fn [rows]
(map
- (fn [[header & rest]]
- (into [header]
- (drop (dec n) rest)))
- rows)))
+ (fn [[header & rest]]
+ (into [header]
+ (drop (dec n) rest)))
+ rows)))
(update :header (fn [headers]
(map
- (fn [[title & header]]
- (first
- (reduce
- (fn [[so-far a] next]
- (let [new-a (+ a (or (:colspan next)
- 1))]
- (if (> new-a n)
- [(conj so-far next) new-a]
- [so-far new-a])))
+ (fn [[title & header]]
+ (first
+ (reduce
+ (fn [[so-far a] next]
+ (let [new-a (+ a (or (:colspan next)
+ 1))]
+ (if (> new-a n)
+ [(conj so-far next) new-a]
+ [so-far new-a])))
- [[title] 1]
- header)))
- headers))))]
+ [[title] 1]
+ header)))
+ headers))))]
(into [new-table]
(split-table remaining n))))))
@@ -148,12 +145,12 @@
table))
(defn make-balance-sheet [args data]
-
+
(let [data (<-graphql data)
args (<-graphql args)
args (assoc args
- :periods (filter identity (cond-> [(:date args)]
- (:include-comparison args) (conj (:comparison-date args)))))
+ :periods (filter identity (cond-> [(:date args)]
+ (:include-comparison args) (conj (:comparison-date args)))))
clients (pull-many (dc/db conn) [:client/code :client/name :db/id] (:client-ids args))
data (concat (->> (:balance-sheet-accounts data)
(map (fn [b]
@@ -165,22 +162,22 @@
:period (:comparison-date args))))))
pnl-data (l-reports/->PNLData args data (by :db/id :client/code clients))
client-count (count (set (map :client-id (:data pnl-data))))
-
+
report (l-reports/summarize-balance-sheet pnl-data)
output-stream (ByteArrayOutputStream.)]
(pdf/pdf
- (-> [{:left-margin 10 :right-margin 10 :top-margin 15 :bottom-margin 15
- :size :letter
- :font {:size 6
- :ttf-name "fonts/calibri-light.ttf"}}
- [:heading (str "Balance Sheet - " (str/join ", " (map :client/name clients)))]]
- (conj [:paragraph {:color [128 0 0] :size 9} (:warning report)])
- (conj
- (table->pdf report
- (cond-> (into [30 ] (repeat client-count 13))
- (:include-comparison args) (into (repeat (* 2 client-count) 13))
- (and (> client-count 1) (not (:include-comparison args))) (conj 13)))))
- output-stream)
+ (-> [{:left-margin 10 :right-margin 10 :top-margin 15 :bottom-margin 15
+ :size :letter
+ :font {:size 6
+ :ttf-name "fonts/calibri-light.ttf"}}
+ [:heading (str "Balance Sheet - " (str/join ", " (map :client/name clients)))]]
+ (conj [:paragraph {:color [128 0 0] :size 9} (:warning report)])
+ (conj
+ (table->pdf report
+ (cond-> (into [30] (repeat client-count 13))
+ (:include-comparison args) (into (repeat (* 2 client-count) 13))
+ (and (> client-count 1) (not (:include-comparison args))) (conj 13)))))
+ output-stream)
(.toByteArray output-stream)))
(defn make-pnl [args data]
@@ -190,42 +187,40 @@
data (->> data
:periods
(mapcat (fn [p1 p2]
- (map
- (fn [a]
- (assoc a :period p1)
- )
- (:accounts p2))
- )
+ (map
+ (fn [a]
+ (assoc a :period p1))
+ (:accounts p2)))
(:periods args)))
pnl-data (l-reports/->PNLData args data (by :db/id :client/code clients))
report (l-reports/summarize-pnl pnl-data)
output-stream (ByteArrayOutputStream.)]
(pdf/pdf
- (-> [{:left-margin 10 :right-margin 10 :top-margin 5 :bottom-margin 15
- :size (cond
- (and (>= (count (-> pnl-data :args :periods)) 8 )
- (-> pnl-data :args :include-deltas))
- :a2
+ (-> [{:left-margin 10 :right-margin 10 :top-margin 5 :bottom-margin 15
+ :size (cond
+ (and (>= (count (-> pnl-data :args :periods)) 8)
+ (-> pnl-data :args :include-deltas))
+ :a2
- (>= (count (-> pnl-data :args :periods)) 4 )
- :tabloid
- :else
- :letter)
- :orientation :landscape
- :font {:size 6
- :ttf-name "fonts/calibri-light.ttf"}}
- [:heading (str "Profit and Loss - " (str/join ", " (map :client/name clients)))]]
- (conj [:paragraph {:color [128 0 0] :size 9} (:warning report)])
- (into
- (for [table (concat (:summaries report)
- (:details report))]
- (table->pdf table
- (into [20] (take (dec (cell-count table))
- (mapcat identity
- (repeat
- (if (-> pnl-data :args :include-deltas)
- [13 6 13]
- [13 6])))))))))
+ (>= (count (-> pnl-data :args :periods)) 4)
+ :tabloid
+ :else
+ :letter)
+ :orientation :landscape
+ :font {:size 6
+ :ttf-name "fonts/calibri-light.ttf"}}
+ [:heading (str "Profit and Loss - " (str/join ", " (map :client/name clients)))]]
+ (conj [:paragraph {:color [128 0 0] :size 9} (:warning report)])
+ (into
+ (for [table (concat (:summaries report)
+ (:details report))]
+ (table->pdf table
+ (into [20] (take (dec (cell-count table))
+ (mapcat identity
+ (repeat
+ (if (-> pnl-data :args :include-deltas)
+ [13 6 13]
+ [13 6])))))))))
output-stream)
(.toByteArray output-stream)))
@@ -236,42 +231,40 @@
data (->> data
:periods
(mapcat (fn [p1 p2]
- (map
- (fn [a]
- (assoc a :period p1)
- )
- (:accounts p2))
- )
+ (map
+ (fn [a]
+ (assoc a :period p1))
+ (:accounts p2)))
(:periods args)))
pnl-data (l-reports/->PNLData args data (by :db/id :client/code clients))
report (l-reports/summarize-cash-flows pnl-data)
output-stream (ByteArrayOutputStream.)]
(pdf/pdf
- (-> [{:left-margin 10 :right-margin 10 :top-margin 5 :bottom-margin 15
- :size (cond
- (and (>= (count (-> pnl-data :args :periods)) 8 )
- (-> pnl-data :args :include-deltas))
- :a2
+ (-> [{:left-margin 10 :right-margin 10 :top-margin 5 :bottom-margin 15
+ :size (cond
+ (and (>= (count (-> pnl-data :args :periods)) 8)
+ (-> pnl-data :args :include-deltas))
+ :a2
- (>= (count (-> pnl-data :args :periods)) 4 )
- :tabloid
- :else
- :letter)
- :orientation :landscape
- :font {:size 6
- :ttf-name "fonts/calibri-light.ttf"}}
- [:heading (str "Statement of Cash Flows - " (str/join ", " (map :client/name clients)))]]
- (conj [:paragraph {:color [128 0 0] :size 9} (:warning report)])
- (into
- (for [table (concat (:summaries report)
- (:details report))]
- (table->pdf table
- (into [20] (take (dec (cell-count table))
- (mapcat identity
- (repeat
- (if (-> pnl-data :args :include-deltas)
- [13 6 13]
- [13 6])))))))))
+ (>= (count (-> pnl-data :args :periods)) 4)
+ :tabloid
+ :else
+ :letter)
+ :orientation :landscape
+ :font {:size 6
+ :ttf-name "fonts/calibri-light.ttf"}}
+ [:heading (str "Statement of Cash Flows - " (str/join ", " (map :client/name clients)))]]
+ (conj [:paragraph {:color [128 0 0] :size 9} (:warning report)])
+ (into
+ (for [table (concat (:summaries report)
+ (:details report))]
+ (table->pdf table
+ (into [20] (take (dec (cell-count table))
+ (mapcat identity
+ (repeat
+ (if (-> pnl-data :args :include-deltas)
+ [13 6 13]
+ [13 6])))))))))
output-stream)
(.toByteArray output-stream)))
@@ -283,55 +276,55 @@
output-stream (ByteArrayOutputStream.)]
(alog/info ::make-detail-report)
(pdf/pdf
- (-> [{:left-margin 10 :right-margin 10 :top-margin 15 :bottom-margin 15
- :size :letter
- :font {:size 6
- :ttf-name "fonts/calibri-light.ttf"}}
- [:heading (str "Journal Detail Report - " (str/join ", " (map :client/name clients)))]]
- (conj [:paragraph {:color [128 0 0] :size 9} (:warning report)])
- (conj
- (table->pdf report
- [80 25 80 25 25 25])))
- output-stream)
+ (-> [{:left-margin 10 :right-margin 10 :top-margin 15 :bottom-margin 15
+ :size :letter
+ :font {:size 6
+ :ttf-name "fonts/calibri-light.ttf"}}
+ [:heading (str "Journal Detail Report - " (str/join ", " (map :client/name clients)))]]
+ (conj [:paragraph {:color [128 0 0] :size 9} (:warning report)])
+ (conj
+ (table->pdf report
+ [80 25 80 25 25 25])))
+ output-stream)
(.toByteArray output-stream)))
(defn join-names [client-ids]
- (str/replace (->> client-ids (pull-many (dc/db conn) [:client/name]) (map :client/name) (str/join "-")) #"[^\w]" "_" ))
+ (str/replace (->> client-ids (pull-many (dc/db conn) [:client/name]) (map :client/name) (str/join "-")) #"[^\w]" "_"))
(defn pnl-args->name [args]
(let [min-date (atime/unparse-local
- (->> args :periods (map :start) first)
- atime/iso-date)
+ (->> args :periods (map :start) first)
+ atime/iso-date)
max-date (atime/unparse-local
- (->> args :periods (map :end) last)
- atime/iso-date)
+ (->> args :periods (map :end) last)
+ atime/iso-date)
names (->> args :client_ids join-names)]
(format "Profit-and-loss-%s-to-%s-for-%s" min-date max-date names)))
(defn cash-flows-args->name [args]
(let [min-date (atime/unparse-local
- (->> args :periods (map :start) first)
- atime/iso-date)
+ (->> args :periods (map :start) first)
+ atime/iso-date)
max-date (atime/unparse-local
- (->> args :periods (map :end) last)
- atime/iso-date)
+ (->> args :periods (map :end) last)
+ atime/iso-date)
names (->> args :client_ids join-names)]
(format "Cash-flows-%s-to-%s-for-%s" min-date max-date names)))
(defn journal-detail-args->name [args]
(let [min-date (atime/unparse-local
- (->> args :date_range :start)
- atime/iso-date)
+ (->> args :date_range :start)
+ atime/iso-date)
max-date (atime/unparse-local
- (->> args :date_range :end)
- atime/iso-date)
+ (->> args :date_range :end)
+ atime/iso-date)
names (->> args :client_ids join-names)]
(format "Profit-and-loss-%s-to-%s-for-%s" min-date max-date names)))
(defn balance-sheet-args->name [args]
(let [date (atime/unparse-local
- (:date args)
- atime/iso-date)
+ (:date args)
+ atime/iso-date)
name (->> args :client_ids join-names)]
(format "Balance-sheet-%s-for-%s" date name)))
@@ -347,14 +340,14 @@
:metadata {:content-length (count pdf-data)
:content-type "application/pdf"})
@(dc/transact conn
- [{:report/name name
- :report/client (:client_ids args)
- :report/key key
- :report/url url
- :report/creator (:user user)
- :report/created (java.util.Date.)}])
+ [{:report/name name
+ :report/client (:client_ids args)
+ :report/key key
+ :report/url url
+ :report/creator (:user user)
+ :report/created (java.util.Date.)}])
{:report/name name
- :report/url url }))
+ :report/url url}))
(defn print-cash-flows [user args data]
(let [uuid (str (UUID/randomUUID))
@@ -368,14 +361,14 @@
:metadata {:content-length (count pdf-data)
:content-type "application/pdf"})
@(dc/transact conn
- [{:report/name name
- :report/client (:client_ids args)
- :report/key key
- :report/url url
- :report/creator (:user user)
- :report/created (java.util.Date.)}])
+ [{:report/name name
+ :report/client (:client_ids args)
+ :report/key key
+ :report/url url
+ :report/creator (:user user)
+ :report/created (java.util.Date.)}])
{:report/name name
- :report/url url }))
+ :report/url url}))
(defn print-balance-sheet [user args data]
(let [uuid (str (UUID/randomUUID))
@@ -389,14 +382,14 @@
:metadata {:content-length (count pdf-data)
:content-type "application/pdf"})
@(dc/transact conn
- [{:report/name name
- :report/client (:client_ids args)
- :report/key key
- :report/url url
- :report/creator (:user user)
- :report/created (java.util.Date.)}])
+ [{:report/name name
+ :report/client (:client_ids args)
+ :report/key key
+ :report/url url
+ :report/creator (:user user)
+ :report/created (java.util.Date.)}])
{:report/name name
- :report/url url }))
+ :report/url url}))
(defn print-journal-detail-report [user args data]
(let [uuid (str (UUID/randomUUID))
@@ -410,11 +403,11 @@
:metadata {:content-length (count pdf-data)
:content-type "application/pdf"})
@(dc/transact conn
- [{:report/name name
- :report/client (:client_ids args)
- :report/key key
- :report/url url
- :report/creator (:user user)
- :report/created (java.util.Date.)}])
+ [{:report/name name
+ :report/client (:client_ids args)
+ :report/key key
+ :report/url url
+ :report/creator (:user user)
+ :report/created (java.util.Date.)}])
{:report/name name
- :report/url url }))
+ :report/url url}))
diff --git a/src/clj/auto_ap/plaid/core.clj b/src/clj/auto_ap/plaid/core.clj
index 0439e34d..555baef1 100644
--- a/src/clj/auto_ap/plaid/core.clj
+++ b/src/clj/auto_ap/plaid/core.clj
@@ -13,7 +13,7 @@
(def secret-key (-> env :plaid :secret-key))
(defn get-link-token [client-code]
- (-> (client/post (str base-url "/link/token/create")
+ (-> (client/post (str base-url "/link/token/create")
{:as :json
:headers {"Content-Type" "application/json"}
:body (json/write-str {"client_id" client-id
@@ -40,10 +40,8 @@
:body
:link_token))
-
-
(defn exchange-public-token [public-token _]
- (-> (client/post (str base-url "/item/public_token/exchange")
+ (-> (client/post (str base-url "/item/public_token/exchange")
{:as :json
:headers {"Content-Type" "application/json"}
:body (json/write-str {"client_id" client-id
@@ -87,10 +85,8 @@
(.getMessage (:throwable &throw-context)))
json))))))
-
-
-(defn get-balance [access-token ]
- (-> (client/post (str base-url "/accounts/balance/get")
+(defn get-balance [access-token]
+ (-> (client/post (str base-url "/accounts/balance/get")
{:as :json
:headers {"Content-Type" "application/json"}
:body (json/write-str {"access_token" access-token
@@ -104,7 +100,6 @@
:end (str end)
:acct (str account-id))
-
(try+
(-> (client/post (str base-url "/transactions/get")
{:as :json
@@ -140,6 +135,4 @@
(clojure.pprint/pprint
(get-transactions "access-production-c0e322fa-f33d-4806-bc42-5fc883fb1ba4" "VZ8Y1azZMdhoYo9MQABrfpgz4jm4kPtakyxN5" #clj-time/date-time "2024-03-15" #clj-time/date-time "2024-03-30"))
- (clojure.pprint/pprint (get-accounts "access-production-c0e322fa-f33d-4806-bc42-5fc883fb1ba4"))
-
- )
\ No newline at end of file
+ (clojure.pprint/pprint (get-accounts "access-production-c0e322fa-f33d-4806-bc42-5fc883fb1ba4")))
\ No newline at end of file
diff --git a/src/clj/auto_ap/routes/auth.clj b/src/clj/auto_ap/routes/auth.clj
index 45fd4d14..a999cdf1 100644
--- a/src/clj/auto_ap/routes/auth.clj
+++ b/src/clj/auto_ap/routes/auth.clj
@@ -36,15 +36,15 @@
(.encodeToString (java.util.Base64/getEncoder) (.toByteArray raw))))
(defn gunzip [b64]
-
+
(let [raw-bytes (.decode (java.util.Base64/getDecoder) b64)
raw (java.io.ByteArrayInputStream. raw-bytes)
out (java.io.ByteArrayOutputStream.)]
(with-open [compressed (-> raw
- (io/input-stream)
- (java.util.zip.GZIPInputStream.))]
+ (io/input-stream)
+ (java.util.zip.GZIPInputStream.))]
(io/copy compressed out))
-
+
(edn/read-string (.toString out))))
(defn user->jwt [user oauth-token]
@@ -94,8 +94,8 @@
(if-let [jwt (user->jwt user token)]
{:status 301
- :headers {"Location" (str (or (not-empty state)
- (bidi/path-for ssr-routes/only-routes
+ :headers {"Location" (str (or (not-empty state)
+ (bidi/path-for ssr-routes/only-routes
::dashboard/page)) "?jwt="
(jwt/sign jwt
(:jwt-secret env)
diff --git a/src/clj/auto_ap/routes/exports.clj b/src/clj/auto_ap/routes/exports.clj
index 031d2a8c..b9731d85 100644
--- a/src/clj/auto_ap/routes/exports.clj
+++ b/src/clj/auto_ap/routes/exports.clj
@@ -76,143 +76,137 @@
(double? v)
(str v)
-
:else
- v)
- ]))
+ v)]))
m))
(defn export-invoices [{:keys [query-params identity]}]
(assert-admin identity)
(statsd/time! [(str "export.time") {:tags #{(client-tag query-params)
"export:invoice"}}]
- {:body
- (list (into (list)
- (map datomic-map->graphql-map)
- (map first (dc/q '[:find (pull ?i [:db/id :invoice/total :invoice/outstanding-balance :invoice/invoice-number :invoice/date :invoice/original-id
- { :invoice/status [:db/ident]
- :invoice/payments
- [:invoice-payment/amount
- {:invoice-payment/payment [:payment/check-number
- :payment/memo
- {:payment/bank_account [:bank-account/id :bank-account/name :bank-account/number :bank-account/bank-name :bank-account/bank-code :bank-account/code]}]}]
- :invoice/vendor [:vendor/name
- :db/id
- {:vendor/primary-contact [:contact/name]
- :vendor/address [:address/street1 :address/city :address/state :address/zip]}]
- :invoice/expense-accounts [:db/id
- :invoice-expense-account/amount
- :invoice-expense-account/id
- :invoice-expense-account/location
- {:invoice-expense-account/account
- [:db/id :account/numeric-code :account/name]}]
- :invoice/client [:client/name :db/id :client/code :client/locations]}])
- :in $ ?c
- :where [?i :invoice/client ?c]]
+ {:body
+ (list (into (list)
+ (map datomic-map->graphql-map)
+ (map first (dc/q '[:find (pull ?i [:db/id :invoice/total :invoice/outstanding-balance :invoice/invoice-number :invoice/date :invoice/original-id
+ {:invoice/status [:db/ident]
+ :invoice/payments
+ [:invoice-payment/amount
+ {:invoice-payment/payment [:payment/check-number
+ :payment/memo
+ {:payment/bank_account [:bank-account/id :bank-account/name :bank-account/number :bank-account/bank-name :bank-account/bank-code :bank-account/code]}]}]
+ :invoice/vendor [:vendor/name
+ :db/id
+ {:vendor/primary-contact [:contact/name]
+ :vendor/address [:address/street1 :address/city :address/state :address/zip]}]
+ :invoice/expense-accounts [:db/id
+ :invoice-expense-account/amount
+ :invoice-expense-account/id
+ :invoice-expense-account/location
+ {:invoice-expense-account/account
+ [:db/id :account/numeric-code :account/name]}]
+ :invoice/client [:client/name :db/id :client/code :client/locations]}])
+ :in $ ?c
+ :where [?i :invoice/client ?c]]
- (dc/db conn)
- [:client/code (query-params "client-code")]))))}))
+ (dc/db conn)
+ [:client/code (query-params "client-code")]))))}))
(defn export-payments [{:keys [query-params identity]}]
(assert-admin identity)
(statsd/time! [(str "export.time") {:tags #{(client-tag query-params)
"export:payment"}}]
- (let [query [[:all_payments
- {:client-code (query-params "client-code")
- :original-id (query-params "original")}
- [:id :check-number :amount :memo :date :status :type :original-id
- [:invoices [[:invoice [:id :original-id]] :amount]]
- [:bank-account [:number :code :bank-name :bank-code :id]]
- [:vendor [:name :id [:primary-contact [:name :email :phone]] [:default-account [:name :numeric-code :id]] [:address [:street1 :city :state :zip]]]]
- [:client [:id :name :code]]
- ]]]
- payments (graphql/query identity (venia/graphql-query {:venia/queries (->graphql query)}) {:clients [ [:client/code (query-params "client-code")]]})]
- {:body
- (list (:all-payments (:data payments)))})))
-
+ (let [query [[:all_payments
+ {:client-code (query-params "client-code")
+ :original-id (query-params "original")}
+ [:id :check-number :amount :memo :date :status :type :original-id
+ [:invoices [[:invoice [:id :original-id]] :amount]]
+ [:bank-account [:number :code :bank-name :bank-code :id]]
+ [:vendor [:name :id [:primary-contact [:name :email :phone]] [:default-account [:name :numeric-code :id]] [:address [:street1 :city :state :zip]]]]
+ [:client [:id :name :code]]]]]
+ payments (graphql/query identity (venia/graphql-query {:venia/queries (->graphql query)}) {:clients [[:client/code (query-params "client-code")]]})]
+ {:body
+ (list (:all-payments (:data payments)))})))
(defn export-sales [{:keys [query-params identity]}]
(assert-admin identity)
(statsd/time! [(str "export.time") {:tags #{(client-tag query-params)
"export:sales"}}]
- (let [query [[:all_sales_orders
- (cond-> {:client-code (query-params "client-code")}
- (query-params "after") (assoc :date-range {:start (query-params "after")
- :end nil}))
- [:id
- :location
- :external_id
- :total
- :tip
- :tax
- :discount
- :returns
- :service_charge
- :date
- [:charges [:type_name :total :tip]]
- [:line_items [:item_name :total :tax :discount :category]]
- [:client [:id :name :code]]]]]
- payments (graphql/query identity (venia/graphql-query {:venia/queries (->graphql query)}))
- parsedouble #(some-> % Double/parseDouble) ]
- {:body
- (seq (map
- (fn [s]
- (-> s
- (assoc :utc_date (:date s))
- (update :date (fn [d]
- (coerce/to-string (coerce/to-local-date-time (time/to-time-zone (coerce/to-date-time d) (time/time-zone-for-id "America/Los_Angeles"))))))
- (update :total parsedouble)
- (update :tax parsedouble)
- (update :discount parsedouble)
- (update :tip parsedouble)
- (update :line-items (fn [lis]
- (map
- (fn [li]
- (-> li
- (update :tax parsedouble)
- (update :discount parsedouble)
- (update :total parsedouble)))
- lis)))
- (update :charges (fn [charges]
- (map
- (fn [charge]
- (-> charge
- (update :tip parsedouble)
- (update :total parsedouble)))
- charges)))))
- (:all-sales-orders (:data payments))))})))
+ (let [query [[:all_sales_orders
+ (cond-> {:client-code (query-params "client-code")}
+ (query-params "after") (assoc :date-range {:start (query-params "after")
+ :end nil}))
+ [:id
+ :location
+ :external_id
+ :total
+ :tip
+ :tax
+ :discount
+ :returns
+ :service_charge
+ :date
+ [:charges [:type_name :total :tip]]
+ [:line_items [:item_name :total :tax :discount :category]]
+ [:client [:id :name :code]]]]]
+ payments (graphql/query identity (venia/graphql-query {:venia/queries (->graphql query)}))
+ parsedouble #(some-> % Double/parseDouble)]
+ {:body
+ (seq (map
+ (fn [s]
+ (-> s
+ (assoc :utc_date (:date s))
+ (update :date (fn [d]
+ (coerce/to-string (coerce/to-local-date-time (time/to-time-zone (coerce/to-date-time d) (time/time-zone-for-id "America/Los_Angeles"))))))
+ (update :total parsedouble)
+ (update :tax parsedouble)
+ (update :discount parsedouble)
+ (update :tip parsedouble)
+ (update :line-items (fn [lis]
+ (map
+ (fn [li]
+ (-> li
+ (update :tax parsedouble)
+ (update :discount parsedouble)
+ (update :total parsedouble)))
+ lis)))
+ (update :charges (fn [charges]
+ (map
+ (fn [charge]
+ (-> charge
+ (update :tip parsedouble)
+ (update :total parsedouble)))
+ charges)))))
+ (:all-sales-orders (:data payments))))})))
(defn export-expected-deposits [{:keys [query-params identity]}]
(assert-admin identity)
(statsd/time! [(str "export.time") {:tags #{(client-tag query-params)
"export:deposit"}}]
- (let [query [[:all_expected_deposits
- (cond-> {:client-code (query-params "client-code")}
- (query-params "after") (assoc :date-range {:start (query-params "after")
- :end nil}))
- [:id
- [:client [:id :name :code]]
- :location
- :external_id
- :total
- :fee
- :date]]]
- payments (graphql/query identity (venia/graphql-query {:venia/queries (->graphql query)}))]
- {:body
- (seq (map
- (fn [d]
- (-> d
- (update :fee #(some-> % Double/parseDouble))
- (update :total #(some-> % Double/parseDouble))))
- (:all-expected-deposits (:data payments))))})))
-
+ (let [query [[:all_expected_deposits
+ (cond-> {:client-code (query-params "client-code")}
+ (query-params "after") (assoc :date-range {:start (query-params "after")
+ :end nil}))
+ [:id
+ [:client [:id :name :code]]
+ :location
+ :external_id
+ :total
+ :fee
+ :date]]]
+ payments (graphql/query identity (venia/graphql-query {:venia/queries (->graphql query)}))]
+ {:body
+ (seq (map
+ (fn [d]
+ (-> d
+ (update :fee #(some-> % Double/parseDouble))
+ (update :total #(some-> % Double/parseDouble))))
+ (:all-expected-deposits (:data payments))))})))
(generate/add-encoder org.joda.time.DateTime
- (fn [c jsonGenerator]
- (.writeString jsonGenerator (str c))))
+ (fn [c jsonGenerator]
+ (.writeString jsonGenerator (str c))))
-
-(defn export-clients[{:keys [identity]}]
+(defn export-clients [{:keys [identity]}]
(assert-admin identity)
{:body (into []
(map <-graphql)
@@ -221,57 +215,56 @@
(defn export-vendors [{:keys [identity]}]
(assert-admin identity)
(statsd/time! [(str "export.time") {:tags #{"export:vendors"}}]
- {:body
- (map <-graphql (->> (dc/q '[:find ?e
- :in $
- :where [?e :vendor/name]]
- (dc/db conn))
- (map first)
- (pull-many (dc/db conn) vendor/default-read)))}))
+ {:body
+ (map <-graphql (->> (dc/q '[:find ?e
+ :in $
+ :where [?e :vendor/name]]
+ (dc/db conn))
+ (map first)
+ (pull-many (dc/db conn) vendor/default-read)))}))
(defn export-company-vendors [{:keys [identity query-params]}]
(statsd/time! [(str "export.time") {:tags #{"export:company-vendors"}}]
- (let [client (:db/id (dc/pull (dc/db conn) [:db/id] [:client/code (get query-params "client")]))
+ (let [client (:db/id (dc/pull (dc/db conn) [:db/id] [:client/code (get query-params "client")]))
- _ (assert-can-see-client identity client)
- data (->> (dc/q '[:find (pull ?v [:vendor/name
- :vendor/terms
- {:vendor/default-account [:account/name :account/numeric-code
- {:account/client-overrides
- [:account-client-override/client
- :account-client-override/name]}]
- :vendor/terms-overrides [:vendor-terms-override/client
- :vendor-terms-override/terms]
- :vendor/account-overrides [:vendor-account-override/client
- {:vendor-account-override/account [:account/numeric-code :account/name
- {:account/client-overrides
- [:account-client-override/client
- :account-client-override/name]}]}]
- :vendor/address [:address/street1 :address/city :address/state :address/zip]}])
- :in $ ?c
- :where [?vu :vendor-usage/client ?c]
- [?vu :vendor-usage/count ?count]
- [(>= ?vu 0)]
- [?vu :vendor-usage/vendor ?v]
- (not [?v :vendor/hidden true])]
- (dc/db conn)
- client)
- (map (fn [[v]]
- [(-> v :vendor/name)
- (-> v :vendor/address :address/street1)
- (-> v :vendor/address :address/city)
- (-> v :vendor/address :address/state)
- (-> v :vendor/address :address/zip)
- (-> v (vendor/terms-for-client-id client) )
- (-> v (vendor/account-for-client-id client) (accounts/clientize client) :account/name)
- (-> v (vendor/account-for-client-id client) :account/numeric-code)
- ]
- ))
- (into [["Vendor Name" "Address" "City" "State" "Zip" "Terms" "Account" "Account Code"]]))]
- {:body
- (into []
- data)
- :headers {"content-disposition" "attachment; filename=\"vendors.csv\""}})))
+ _ (assert-can-see-client identity client)
+ data (->> (dc/q '[:find (pull ?v [:vendor/name
+ :vendor/terms
+ {:vendor/default-account [:account/name :account/numeric-code
+ {:account/client-overrides
+ [:account-client-override/client
+ :account-client-override/name]}]
+ :vendor/terms-overrides [:vendor-terms-override/client
+ :vendor-terms-override/terms]
+ :vendor/account-overrides [:vendor-account-override/client
+ {:vendor-account-override/account [:account/numeric-code :account/name
+ {:account/client-overrides
+ [:account-client-override/client
+ :account-client-override/name]}]}]
+ :vendor/address [:address/street1 :address/city :address/state :address/zip]}])
+ :in $ ?c
+ :where [?vu :vendor-usage/client ?c]
+ [?vu :vendor-usage/count ?count]
+ [(>= ?vu 0)]
+ [?vu :vendor-usage/vendor ?v]
+ (not [?v :vendor/hidden true])]
+ (dc/db conn)
+ client)
+ (map (fn [[v]]
+ [(-> v :vendor/name)
+ (-> v :vendor/address :address/street1)
+ (-> v :vendor/address :address/city)
+ (-> v :vendor/address :address/state)
+ (-> v :vendor/address :address/zip)
+ (-> v (vendor/terms-for-client-id client))
+ (-> v (vendor/account-for-client-id client) (accounts/clientize client) :account/name)
+ (-> v (vendor/account-for-client-id client) :account/numeric-code)]))
+
+ (into [["Vendor Name" "Address" "City" "State" "Zip" "Terms" "Account" "Account Code"]]))]
+ {:body
+ (into []
+ data)
+ :headers {"content-disposition" "attachment; filename=\"vendors.csv\""}})))
(defn export-ledger [{:keys [identity query-params]}]
(let [start-date (or (some-> (query-params "start-date")
@@ -282,139 +275,138 @@
(assert-admin identity)
(statsd/time! [(str "export.time") {:tags #{(client-tag query-params)
"export:ledger2"}}]
- (let [results (->> (dc/q '[:find (pull ?e [:db/id
- :journal-entry/external-id
- :journal-entry/cleared
- :journal-entry/alternate-description
- :journal-entry/date
- :journal-entry/note
- :journal-entry/amount
- :journal-entry/source
- :journal-entry/cleared-against
- :journal-entry/original-entity
- {:journal-entry/client [:client/name :client/code :db/id]
- :journal-entry/vendor [:vendor/name :db/id]
- :journal-entry/line-items
- [:db/id
- :journal-entry-line/location
- :journal-entry-line/debit
- :journal-entry-line/credit
- {:journal-entry-line/account
- [:bank-account/include-in-reports
- :bank-account/bank-name
- :bank-account/numeric-code
- :bank-account/code
- :bank-account/visible
- :bank-account/name
- :bank-account/number
- :account/code
- :account/name
- :account/numeric-code
- :account/location
- {:account/type [:db/ident :db/id]}
- {:bank-account/type [:db/ident :db/id]}]}]}])
- :in $ ?c ?start-date
- :where [?e :journal-entry/client ?c]
- [?e :journal-entry/date ?date]
- [(>= ?date ?start-date)]]
- (dc/db conn)
- [:client/code (query-params "client-code")]
- (coerce/to-date start-date)))
- tf-result (transduce (comp
- (map first)
- (filter (fn [je]
- (every?
- (fn [jel]
- (let [include-in-reports (-> jel :journal-entry-line/account :bank-account/include-in-reports)]
- (or (nil? include-in-reports)
- (true? include-in-reports))))
- (:journal-entry/line-items je))))
- (map <-graphql))
- conj
- (list)
- results)]
- {:body
- tf-result}))))
+ (let [results (->> (dc/q '[:find (pull ?e [:db/id
+ :journal-entry/external-id
+ :journal-entry/cleared
+ :journal-entry/alternate-description
+ :journal-entry/date
+ :journal-entry/note
+ :journal-entry/amount
+ :journal-entry/source
+ :journal-entry/cleared-against
+ :journal-entry/original-entity
+ {:journal-entry/client [:client/name :client/code :db/id]
+ :journal-entry/vendor [:vendor/name :db/id]
+ :journal-entry/line-items
+ [:db/id
+ :journal-entry-line/location
+ :journal-entry-line/debit
+ :journal-entry-line/credit
+ {:journal-entry-line/account
+ [:bank-account/include-in-reports
+ :bank-account/bank-name
+ :bank-account/numeric-code
+ :bank-account/code
+ :bank-account/visible
+ :bank-account/name
+ :bank-account/number
+ :account/code
+ :account/name
+ :account/numeric-code
+ :account/location
+ {:account/type [:db/ident :db/id]}
+ {:bank-account/type [:db/ident :db/id]}]}]}])
+ :in $ ?c ?start-date
+ :where [?e :journal-entry/client ?c]
+ [?e :journal-entry/date ?date]
+ [(>= ?date ?start-date)]]
+ (dc/db conn)
+ [:client/code (query-params "client-code")]
+ (coerce/to-date start-date)))
+ tf-result (transduce (comp
+ (map first)
+ (filter (fn [je]
+ (every?
+ (fn [jel]
+ (let [include-in-reports (-> jel :journal-entry-line/account :bank-account/include-in-reports)]
+ (or (nil? include-in-reports)
+ (true? include-in-reports))))
+ (:journal-entry/line-items je))))
+ (map <-graphql))
+ conj
+ (list)
+ results)]
+ {:body
+ tf-result}))))
(defn export-accounts [{:keys [query-params identity]}]
(assert-admin identity)
(statsd/time! [(str "export.time") {:tags #{(client-tag query-params)
"export:accounts"}}]
- (let [client-id (d-clients/code->id (query-params "client-code"))
- query [[:all-accounts
- [:id :numeric_code :type :applicability :location :name [:client_overrides [:name [:client [:id :code :name]]]]]]]
- all-accounts (graphql/query identity (venia/graphql-query {:venia/queries (->graphql query)}))]
+ (let [client-id (d-clients/code->id (query-params "client-code"))
+ query [[:all-accounts
+ [:id :numeric_code :type :applicability :location :name [:client_overrides [:name [:client [:id :code :name]]]]]]]
+ all-accounts (graphql/query identity (venia/graphql-query {:venia/queries (->graphql query)}))]
- {:body
- (list (transduce
- (comp
- (filter (fn [a]
- (let [overriden-clients (set (map (comp :id :client) (:client-overrides a)))]
- (or (nil? (:applicability a))
- (= :global (:applicability a))
- (overriden-clients (str client-id))))))
- (map (fn [a]
- (let [client->name (reduce
- (fn [override co]
- (assoc override (str (:id (:client co))) (:name co)))
- {}
- (:client-overrides a))]
- (-> a
- (assoc :global-name (:name a))
- (assoc :client-name (client->name (str client-id) (:name a)))
- (dissoc :client-overrides))))))
- conj
- (list)
- (:all-accounts (:data all-accounts))))})))
+ {:body
+ (list (transduce
+ (comp
+ (filter (fn [a]
+ (let [overriden-clients (set (map (comp :id :client) (:client-overrides a)))]
+ (or (nil? (:applicability a))
+ (= :global (:applicability a))
+ (overriden-clients (str client-id))))))
+ (map (fn [a]
+ (let [client->name (reduce
+ (fn [override co]
+ (assoc override (str (:id (:client co))) (:name co)))
+ {}
+ (:client-overrides a))]
+ (-> a
+ (assoc :global-name (:name a))
+ (assoc :client-name (client->name (str client-id) (:name a)))
+ (dissoc :client-overrides))))))
+ conj
+ (list)
+ (:all-accounts (:data all-accounts))))})))
(defn export-transactions2 [{:keys [query-params identity]}]
(assert-admin identity)
(statsd/time! [(str "export.time") {:tags #{(client-tag query-params)
"export:transactions2"}}]
- {:body (let [db (dc/db conn)]
- (->>
- (dc/q {:find ['?e]
- :in ['$ '?client-code]
- :where ['[?e :transaction/client ?client-code]]}
- db [:client/code (query-params "client-code")])
- (map first)
+ {:body (let [db (dc/db conn)]
+ (->>
+ (dc/q {:find ['?e]
+ :in ['$ '?client-code]
+ :where ['[?e :transaction/client ?client-code]]}
+ db [:client/code (query-params "client-code")])
+ (map first)
;; TODO
- #_(map (fn [e]
- (let [e (dc/entity db e)
- client (:transaction/client e)
- bank-account (:transaction/bank-account e)]
- {:id (:db/id e)
- :date (:transaction/date e)
- :post_date (:transaction/post-date e)
- :client { :code (:client/code client)
- :id (:db/id client)
- :name (:client/name client)}
- :amount (:transaction/amount e)
- :description_original (:transaction/description-original e)
- :approval_status (:transaction/approval-status e)
- :bank_account {:name (:bank-account/name bank-account)
- :code (:bank-account/code bank-account)
- :id (:db/id bank-account)}})))))}))
+ #_(map (fn [e]
+ (let [e (dc/entity db e)
+ client (:transaction/client e)
+ bank-account (:transaction/bank-account e)]
+ {:id (:db/id e)
+ :date (:transaction/date e)
+ :post_date (:transaction/post-date e)
+ :client {:code (:client/code client)
+ :id (:db/id client)
+ :name (:client/name client)}
+ :amount (:transaction/amount e)
+ :description_original (:transaction/description-original e)
+ :approval_status (:transaction/approval-status e)
+ :bank_account {:name (:bank-account/name bank-account)
+ :code (:bank-account/code bank-account)
+ :id (:db/id bank-account)}})))))}))
(defn export-transactions [{:keys [query-params identity clients]}]
(assert-admin identity)
(statsd/time! [(str "export.time") {:tags #{(client-tag query-params)
"export:transactions"}}]
- (let [[transactions] (d-transactions/get-graphql {:client-code (query-params "client-code")
- :clients clients
- #_#_:original-id (Integer/parseInt (query-params "original"))
- :count Integer/MAX_VALUE})]
-
-
- {:body (map
- (comp ->graphql
- (fn [i]
- (cond-> i
- true (update :transaction/date to-date)
- true (update :transaction/post-date to-date)
- (:transaction/payment i) (update-in [:transaction/payment :payment/date] to-date)
- (:transaction/expected-deposit i) (update-in [:transaction/expected-deposit :expected-deposit/date] to-date))))
- transactions)})))
+ (let [[transactions] (d-transactions/get-graphql {:client-code (query-params "client-code")
+ :clients clients
+ #_#_:original-id (Integer/parseInt (query-params "original"))
+ :count Integer/MAX_VALUE})]
+
+ {:body (map
+ (comp ->graphql
+ (fn [i]
+ (cond-> i
+ true (update :transaction/date to-date)
+ true (update :transaction/post-date to-date)
+ (:transaction/payment i) (update-in [:transaction/payment :payment/date] to-date)
+ (:transaction/expected-deposit i) (update-in [:transaction/expected-deposit :expected-deposit/date] to-date))))
+ transactions)})))
(defn export-trial-balance [{{:strs [client-code as-of]} :query-params}]
(let [db (dc/db conn)
@@ -425,44 +417,44 @@
as-of (coerce/to-date (atime/parse as-of atime/iso-date))]
{:body
- (->>
- (dc/index-pull db {:index :avet
- :selector [:db/id :journal-entry-line/debit :journal-entry-line/location :journal-entry-line/credit {:journal-entry-line/account [:db/id {:account/type [:db/ident]}]} :journal-entry-line/client+account+location+date]
- :start [:journal-entry-line/client+account+location+date
- [client]]})
- (take-while (fn [jel]
- (let [[c _ _ _] (:journal-entry-line/client+account+location+date jel)]
- (= c client))))
- (filter (fn [jel]
- (let [[_ _ _ d] (:journal-entry-line/client+account+location+date jel)]
- (<= (compare (or d #inst "2000-01-01") as-of) 0))))
- (reduce
- (fn [acc jel]
- (update acc [(:db/id (:journal-entry-line/account jel)) (:journal-entry-line/location jel)]
- (fn [v]
- (if (#{:account-type/asset
- :account-type/dividend
- :account-type/expense} (:db/ident (:account/type (:journal-entry-line/account jel))))
- (update (or v {}) :debit (fnil + 0.0) (or (:journal-entry-line/debit jel) 0.0))
- (update (or v {}) :credit (fnil + 0.0) (or (:journal-entry-line/credit jel) 0.0))))))
- {})
- (map (fn [[[a l] {:keys [debit credit]}]]
- [(or (pull-attr db :account/name a)
- (pull-attr db :bank-account/name a))
- (or (pull-attr db :account/numeric-code a)
- (pull-attr db :bank-account/numeric-code a))
- l
- (or debit 0.0)
- (or credit 0.0)]))
- (sort-by second))
+ (->>
+ (dc/index-pull db {:index :avet
+ :selector [:db/id :journal-entry-line/debit :journal-entry-line/location :journal-entry-line/credit {:journal-entry-line/account [:db/id {:account/type [:db/ident]}]} :journal-entry-line/client+account+location+date]
+ :start [:journal-entry-line/client+account+location+date
+ [client]]})
+ (take-while (fn [jel]
+ (let [[c _ _ _] (:journal-entry-line/client+account+location+date jel)]
+ (= c client))))
+ (filter (fn [jel]
+ (let [[_ _ _ d] (:journal-entry-line/client+account+location+date jel)]
+ (<= (compare (or d #inst "2000-01-01") as-of) 0))))
+ (reduce
+ (fn [acc jel]
+ (update acc [(:db/id (:journal-entry-line/account jel)) (:journal-entry-line/location jel)]
+ (fn [v]
+ (if (#{:account-type/asset
+ :account-type/dividend
+ :account-type/expense} (:db/ident (:account/type (:journal-entry-line/account jel))))
+ (update (or v {}) :debit (fnil + 0.0) (or (:journal-entry-line/debit jel) 0.0))
+ (update (or v {}) :credit (fnil + 0.0) (or (:journal-entry-line/credit jel) 0.0))))))
+ {})
+ (map (fn [[[a l] {:keys [debit credit]}]]
+ [(or (pull-attr db :account/name a)
+ (pull-attr db :bank-account/name a))
+ (or (pull-attr db :account/numeric-code a)
+ (pull-attr db :bank-account/numeric-code a))
+ l
+ (or debit 0.0)
+ (or credit 0.0)]))
+ (sort-by second))
:status 200}))
(defn export-raw [{:keys [query-params identity]}]
(assert-admin identity)
- (alog/info ::executing-query :q (get query-params "query" ))
+ (alog/info ::executing-query :q (get query-params "query"))
(statsd/time! [(str "export.time") {:tags #{"export:raw"}}]
- {:body
- (into (list) (apply dc/q (read-string (get query-params "query" )) (into [(dc/db conn)] (read-string (get query-params "args" "[]")))))}))
+ {:body
+ (into (list) (apply dc/q (read-string (get query-params "query")) (into [(dc/db conn)] (read-string (get query-params "args" "[]")))))}))
(defn export-ntg-account-snapshot [_]
(let [clients (->> (dc/q '[:find (pull ?e [:db/id :client/code :client/locations])
@@ -559,65 +551,58 @@
:sales-order/service-charge
{:sales-order/charges [:charge/total :charge/tax :charge/tip
- :charge/type-name
- :charge/reference-link
+ :charge/type-name
+ :charge/reference-link
{[:charge/processor :xform iol-ion.query/ident] [:db/ident]}]
:sales-order/line-items [:order-line-item/item-name
:order-line-item/category
- :order-line-item/total]}
- ]
+ :order-line-item/total]}]
:start [:sales-order/client+date [(:db/id client) (coerce/to-date date)]]
:end [:sales-order/client+date [(:db/id client) (coerce/to-date end)]]
:reverse false
:limit 100})
(take-while (fn matches-client [curr]
- (and
- (= (first (:sales-order/client+date curr))
- (:db/id client))
- (< (compare (:sales-order/date curr)
- (coerce/to-date end))
- 0))
- )))
- ]
+ (and
+ (= (first (:sales-order/client+date curr))
+ (:db/id client))
+ (< (compare (:sales-order/date curr)
+ (coerce/to-date end))
+ 0)))))]
+
entry all-entries
:let [sales-columns [(-> entry :sales-order/client :client/name)
- (atime/unparse-local (coerce/from-date (-> entry :sales-order/date)) atime/standard-time)
- (-> entry :sales-order/total)
- (-> entry :sales-order/tip)
- (-> entry :sales-order/service-charge)
- (-> entry :sales-order/reference-link)
- ]
- sales-column-count (count sales-columns)
- tender-column-count 6]
+ (atime/unparse-local (coerce/from-date (-> entry :sales-order/date)) atime/standard-time)
+ (-> entry :sales-order/total)
+ (-> entry :sales-order/tip)
+ (-> entry :sales-order/service-charge)
+ (-> entry :sales-order/reference-link)]
+ sales-column-count (count sales-columns)
+ tender-column-count 6]
row (concat [sales-columns]
- (map (fn [tender]
- (concat
- (take sales-column-count (repeat nil))
- [
- (-> tender :charge/total)
- (-> tender :charge/tax)
- (-> tender :charge/tip)
- (-> tender :charge/type-name)
- (some-> tender :charge/processor name)
- (-> tender :charge/reference-link)
- ]))
- (:sales-order/charges entry))
- (map (fn [tender]
- (concat
- (take (+ sales-column-count tender-column-count) (repeat nil))
- [
- (-> tender :order-line-item/item-name)
- (-> tender :order-line-item/category)
- (-> tender :order-line-item/total)
- ]))
- (:sales-order/line-items entry)))]
+ (map (fn [tender]
+ (concat
+ (take sales-column-count (repeat nil))
+ [(-> tender :charge/total)
+ (-> tender :charge/tax)
+ (-> tender :charge/tip)
+ (-> tender :charge/type-name)
+ (some-> tender :charge/processor name)
+ (-> tender :charge/reference-link)]))
+ (:sales-order/charges entry))
+ (map (fn [tender]
+ (concat
+ (take (+ sales-column-count tender-column-count) (repeat nil))
+ [(-> tender :order-line-item/item-name)
+ (-> tender :order-line-item/category)
+ (-> tender :order-line-item/total)]))
+ (:sales-order/line-items entry)))]
row
-
+
#_(let [balance (account-lookup (format "%d-%d-%s-%s" (:db/id client) numeric-code l date-str))
_ (when balance
(reset! last-used-value balance))
balance (or balance @last-used-value)]
- [ l numeric-code (account->name a) date-str
+ [l numeric-code (account->name a) date-str
(format "%.2f" balance)])))}))
#_(export-ntg-payment-snapshot nil)
@@ -628,7 +613,6 @@
(handler request)
{:status 401})))
-
(def routes2 {"api/" {"sales/" {"aggregated/" {#"export/?" {:get :aggregated-sales-export}}
#"export/?" {:get :export-sales}
"ntg-export" {:get :export-ntg-sales-snapshot}}
@@ -658,12 +642,11 @@
:export-ledger (-> export-ledger wrap-json-response wrap-secure)
:export-ntg-account-snapshot (-> export-ntg-account-snapshot wrap-csv-response (wrap-predetermined-api-key "fd07755a-ed4c-4c9a-ad85-fbdd8af37206"))
:export-ntg-sales-snapshot (-> export-ntg-sales-snapshot wrap-csv-response
- (wrap-schema-enforce :query-schema (mc/schema [:map
- [:date {:required true
- :decode/string #(try (atime/parse % atime/iso-date) (catch Exception _ nil))} :some]]) )
- (wrap-form-4xx-2 (fn [_] {:body "Invalid Date"}))
- (wrap-predetermined-api-key "fd07755a-ed4c-4c9a-ad85-fbdd8af37206")
- )
+ (wrap-schema-enforce :query-schema (mc/schema [:map
+ [:date {:required true
+ :decode/string #(try (atime/parse % atime/iso-date) (catch Exception _ nil))} :some]]))
+ (wrap-form-4xx-2 (fn [_] {:body "Invalid Date"}))
+ (wrap-predetermined-api-key "fd07755a-ed4c-4c9a-ad85-fbdd8af37206"))
:export-trial-balance (-> export-trial-balance wrap-csv-response wrap-secure)
:export-accounts (-> export-accounts wrap-json-response wrap-secure)
:export-transactions (-> export-transactions wrap-json-response wrap-secure)
diff --git a/src/clj/auto_ap/routes/ezcater.clj b/src/clj/auto_ap/routes/ezcater.clj
index 4d565d84..f6152c8b 100644
--- a/src/clj/auto_ap/routes/ezcater.clj
+++ b/src/clj/auto_ap/routes/ezcater.clj
@@ -7,12 +7,12 @@
(defn handle-ezcater [{:keys [request-method json-params] :as r}]
(cond
(= :get request-method)
- {:status 200
+ {:status 200
:headers {"Content-Type" "application/json"}
:body "{}"}
(= :post request-method)
- (do
+ (do
(alog/info ::ezcater-request
:json-params json-params)
(e/import-order json-params)
@@ -23,8 +23,6 @@
:else
{:status 404}))
-
-
(def routes {"api/" {"ezcater/" {#"event/?" :ezcater-event}}})
(def match->handler {:ezcater-event (-> handle-ezcater
- wrap-json-params)})
+ wrap-json-params)})
diff --git a/src/clj/auto_ap/routes/ezcater_xls.clj b/src/clj/auto_ap/routes/ezcater_xls.clj
index 1a99c263..3184ec72 100644
--- a/src/clj/auto_ap/routes/ezcater_xls.clj
+++ b/src/clj/auto_ap/routes/ezcater_xls.clj
@@ -53,54 +53,54 @@
event-date (some-> (excel/xls-date->date event-date)
coerce/to-date-time
atime/as-local-time
- coerce/to-date )]
- (cond (and event-date client-id location )
+ coerce/to-date)]
+ (cond (and event-date client-id location)
[:order #:sales-order
- {:date event-date
- :external-id (str "ezcater/order/" client-id "-" location "-" order-number)
- :client client-id
- :location location
- :reference-link (str order-number)
- :line-items [#:order-line-item
- {:external-id (str "ezcater/order/" client-id "-" location "-" order-number "-" 0)
- :item-name "EZCater Catering"
- :category "EZCater Catering"
- :discount (fmt-amount (or adjustments 0.0))
- :tax (fmt-amount tax)
- :total (fmt-amount (+ food-total
- tax))}]
+ {:date event-date
+ :external-id (str "ezcater/order/" client-id "-" location "-" order-number)
+ :client client-id
+ :location location
+ :reference-link (str order-number)
+ :line-items [#:order-line-item
+ {:external-id (str "ezcater/order/" client-id "-" location "-" order-number "-" 0)
+ :item-name "EZCater Catering"
+ :category "EZCater Catering"
+ :discount (fmt-amount (or adjustments 0.0))
+ :tax (fmt-amount tax)
+ :total (fmt-amount (+ food-total
+ tax))}]
- :charges [#:charge
- {:type-name "CARD"
- :date event-date
- :client client-id
- :location location
- :external-id (str "ezcater/charge/" client-id "-" location "-" order-number "-" 0)
- :processor :ccp-processor/ezcater
- :total (fmt-amount (+ food-total
- tax
- tip))
- :tip (fmt-amount tip)}]
- :total (fmt-amount (+ food-total
- tax
- (or adjustments 0.0)))
- :discount (fmt-amount (or adjustments 0.0))
- :service-charge (fmt-amount (+ fee commission))
- :tax (fmt-amount tax)
- :tip (fmt-amount tip)
- :returns 0.0
- :vendor :vendor/ccp-ezcater}]
+ :charges [#:charge
+ {:type-name "CARD"
+ :date event-date
+ :client client-id
+ :location location
+ :external-id (str "ezcater/charge/" client-id "-" location "-" order-number "-" 0)
+ :processor :ccp-processor/ezcater
+ :total (fmt-amount (+ food-total
+ tax
+ tip))
+ :tip (fmt-amount tip)}]
+ :total (fmt-amount (+ food-total
+ tax
+ (or adjustments 0.0)))
+ :discount (fmt-amount (or adjustments 0.0))
+ :service-charge (fmt-amount (+ fee commission))
+ :tax (fmt-amount tax)
+ :tip (fmt-amount tip)
+ :returns 0.0
+ :vendor :vendor/ccp-ezcater}]
- caterer-name
- (do
- (alog/warn ::missing-client
- :order order-number
- :store-name store-name
- :caterer-name caterer-name)
- [:missing caterer-name])
+ caterer-name
+ (do
+ (alog/warn ::missing-client
+ :order order-number
+ :store-name store-name
+ :caterer-name caterer-name)
+ [:missing caterer-name])
- :else
- nil)))
+ :else
+ nil)))
(defn stream->sales-orders [s]
(let [clients (map first (dc/q '[:find (pull ?c [:client/code
@@ -115,7 +115,7 @@
object (str "/ezcater-xls/" (str (java.util.UUID/randomUUID)))]
(mu/log ::writing-temp-xls
:location object)
- (s3/put-object {:bucket-name (:data-bucket env)
+ (s3/put-object {:bucket-name (:data-bucket env)
:key object
:input-stream s})
(into []
@@ -157,13 +157,13 @@
});")]])])
(defn upload-xls [{:keys [identity] :as request}]
-
+
(let [file (or (get (:params request) :file)
(get (:params request) "file"))]
(mu/log ::uploading-file
:file file)
(with-open [s (io/input-stream (:tempfile file))]
- (try
+ (try
(let [parse-results (stream->sales-orders s)
new-orders (->> parse-results
(filter (comp #{:order} first))
@@ -181,7 +181,7 @@
[:li ml])]])]))
(catch Exception e
(alog/error ::import-error
- :error e)
+ :error e)
(html-response [:div (.getMessage e)]))))))
(defn page [{:keys [matched-route request-method] :as request}]
diff --git a/src/clj/auto_ap/routes/graphql.clj b/src/clj/auto_ap/routes/graphql.clj
index 6168c884..41779a2a 100644
--- a/src/clj/auto_ap/routes/graphql.clj
+++ b/src/clj/auto_ap/routes/graphql.clj
@@ -11,9 +11,6 @@
[clojure.set :as set]
[datomic.api :as dc]))
-
-
-
(defn handle-graphql [{:keys [request-method query-params clients] :as r}]
(when (= "none" (:user/role (:identity r)))
(throw-unauthorized))
@@ -22,21 +19,21 @@
(let [variables (some-> (query-params "variables")
edn/read-string)
body (some-> r :body slurp)]
-
+
{:status 200
:body (pr-str (ql/query (:identity r) (if (= request-method :get) (query-params "query") body) (assoc variables
:clients
- clients) ))
+ clients)))
:headers {"Content-Type" "application/edn"}})
(catch Throwable e
-
+
(if-let [result (:result (ex-data e))]
(do (alog/warn ::result-error :error e)
- {:status 400
- :body (pr-str result)
- :headers {"Content-Type" "application/edn"}})
- (if-let [message (:validation-error (ex-data (.getCause e)) )]
- (do
+ {:status 400
+ :body (pr-str result)
+ :headers {"Content-Type" "application/edn"}})
+ (if-let [message (:validation-error (ex-data (.getCause e)))]
+ (do
(alog/warn ::graphql-validation-error
:message message
:error e)
@@ -48,6 +45,5 @@
:body (pr-str {:errors [{:message (str "Unhandled error:" (str e))}]})
:headers {"Content-Type" "application/edn"}}))))))
-
(def routes {"api/" {#"graphql/?" :graphql}})
(def match->handler {:graphql (wrap-secure handle-graphql)})
diff --git a/src/clj/auto_ap/routes/invoices.clj b/src/clj/auto_ap/routes/invoices.clj
index 8b1e0ebb..9accd9d3 100644
--- a/src/clj/auto_ap/routes/invoices.clj
+++ b/src/clj/auto_ap/routes/invoices.clj
@@ -29,10 +29,10 @@
{:vendor-code vendor-code})))
(let [vendor-id (or forced-vendor
(->> (dc/q
- {:find ['?vendor]
- :in ['$ '?vendor-name]
- :where ['[?vendor :vendor/name ?vendor-name]]}
- (dc/db conn) vendor-code)
+ {:find ['?vendor]
+ :in ['$ '?vendor-name]
+ :where ['[?vendor :vendor/name ?vendor-name]]}
+ (dc/db conn) vendor-code)
first
first))]
(when-not vendor-id
@@ -40,9 +40,9 @@
{:vendor-code vendor-code})))
(if-let [matching-vendor (->> (dc/q
- {:find [(list 'pull '?vendor-id d-vendors/default-read)]
- :in ['$ '?vendor-id]}
- (dc/db conn) vendor-id)
+ {:find [(list 'pull '?vendor-id d-vendors/default-read)]
+ :in ['$ '?vendor-id]}
+ (dc/db conn) vendor-id)
first
first)]
matching-vendor
@@ -54,16 +54,16 @@
account-number (:db/id (d-clients/exact-match account-number))
customer-identifier (:db/id (d-clients/best-match customer-identifier))
client-override (Long/parseLong client-override))
- _ (alog/info ::client-matched
- :account-number account-number
+ _ (alog/info ::client-matched
+ :account-number account-number
:customer-identifier customer-identifier
- :client-override client-override
- :matching (when matching-client
- (dc/pull (dc/db conn) [:client/name :client/code] matching-client)))
-
+ :client-override client-override
+ :matching (when matching-client
+ (dc/pull (dc/db conn) [:client/name :client/code] matching-client)))
+
matching-vendor (match-vendor vendor-code vendor-override)
matching-location (or (when-not (str/blank? location-override)
- location-override)
+ location-override)
(parse/best-location-match (dc/pull (dc/db conn)
[{:client/location-matches [:location-match/location :location-match/matches]}
:client/default-location
@@ -97,7 +97,6 @@
invoice)
-
(defn admin-only-if-multiple-clients [is]
(let [client-count (->> is
(map :invoice/client)
@@ -112,8 +111,8 @@
(map #(validate-invoice % user))
admin-only-if-multiple-clients
(mapv d-invoices/code-invoice)
- (mapv (fn [i] [:propose-invoice i])))]
-
+ (mapv (fn [i] [:propose-invoice i])))]
+
(alog/info ::creating-invoice :invoices potential-invoices)
(let [tx (audit-transact potential-invoices user)]
(when-not (seq (dc/q '[:find ?i
@@ -155,7 +154,7 @@
customer)))
code->existing-account (by :account/numeric-code (map first (dc/q {:find ['(pull ?e [:account/numeric-code
{:account/applicability [:db/ident]}
- :db/id])]
+ :db/id])]
:in ['$]
:where ['[?e :account/name]]}
(dc/db conn))))
@@ -225,15 +224,15 @@
(defn import-transactions-cleared-against [file]
(let [[_ & rows] (-> file (io/reader) csv/read-csv)
txes (transduce
- (comp
- (filter (fn [[transaction-id _]]
- (dc/pull (dc/db conn) '[:transaction/amount] (Long/parseLong transaction-id))))
- (map (fn [[transaction-id cleared-against]]
- {:db/id (Long/parseLong transaction-id)
- :transaction/cleared-against cleared-against})))
- conj
- []
- rows)]
+ (comp
+ (filter (fn [[transaction-id _]]
+ (dc/pull (dc/db conn) '[:transaction/amount] (Long/parseLong transaction-id))))
+ (map (fn [[transaction-id cleared-against]]
+ {:db/id (Long/parseLong transaction-id)
+ :transaction/cleared-against cleared-against})))
+ conj
+ []
+ rows)]
(audit-transact txes nil)))
(defn batch-upload-transactions [{{:keys [data]} :edn-params user :identity}]
@@ -318,8 +317,6 @@
:data (ex-data e)})
:headers {"Content-Type" "application/edn"}}))))
-
-
(defn bulk-account-overrides [{{files :file
files-2 "file"
client :client
diff --git a/src/clj/auto_ap/routes/queries.clj b/src/clj/auto_ap/routes/queries.clj
index ad73892f..c7b8837d 100644
--- a/src/clj/auto_ap/routes/queries.clj
+++ b/src/clj/auto_ap/routes/queries.clj
@@ -25,8 +25,6 @@
(csv/write-csv w %)
(.toString w))))))
-
-
(defn execute-query [query-params params]
(let [{:keys [query-id]} params]
(mu/with-context {:query-id query-id}
@@ -37,7 +35,6 @@
(into (list) (apply dc/q (edn/read-string query-string)
(into [(dc/db conn)] (edn/read-string (get query-params "args" "[]")))))))))
-
(defn put-query [guid body note & [lookup-key client]]
(let [id (pull-id (dc/db conn) [:saved-query/lookup-key lookup-key])
guid (if lookup-key
@@ -45,11 +42,11 @@
guid)
guid)]
@(dc/transact conn [[:upsert-entity {:db/id (or id (random-tempid))
- :saved-query/guid guid
- :saved-query/description note
- :saved-query/key (str "queries/" guid)
- :saved-query/client client
- :saved-query/lookup-key lookup-key}]])
+ :saved-query/guid guid
+ :saved-query/description note
+ :saved-query/key (str "queries/" guid)
+ :saved-query/client client
+ :saved-query/lookup-key lookup-key}]])
(s3/put-object :bucket-name (:data-bucket env)
:key (str "queries/" guid)
:input-stream (io/make-input-stream (.getBytes body) {})
@@ -62,8 +59,6 @@
:csv-results-url (str "/api/queries/" guid "/results/csv")
:json-results-url (str "/api/queries/" guid "/results/json")}}))
-
-
(defn get-queries [{:keys [identity]}]
(assert-admin identity)
(let [obj (s3/list-objects :bucket-name (:data-bucket env)
@@ -77,7 +72,7 @@
(assert-admin identity)
(put-query (str (UUID/randomUUID)) (body-string request) (query-params "note")))
-(defn get-query [{:keys [identity params]} ]
+(defn get-query [{:keys [identity params]}]
(assert-admin identity)
(let [{:keys [query-id]} params
obj (s3/get-object :bucket-name (:data-bucket env)
@@ -89,13 +84,13 @@
:csv-results-url (str "/api/queries/" query-id "/results/csv")
:json-results-url (str "/api/queries/" query-id "/results/json")}}))
-(defn update-query [{:keys [query-params identity params] :as request} ]
+(defn update-query [{:keys [query-params identity params] :as request}]
(assert-admin identity)
(put-query (:query-id params) (body-string request) (query-params "note")))
-(defn results-json-query [{:keys [query-params params]}]
+(defn results-json-query [{:keys [query-params params]}]
(statsd/time! [(str "export.query.time") {:tags #{(str "query:" (:query-id params))}}]
- {:body (execute-query query-params params)}))
+ {:body (execute-query query-params params)}))
(defn raw-query [{:keys [identity params]}]
(assert-admin identity)
@@ -124,9 +119,9 @@
:create-query create-query
:raw-query raw-query
:get-query (-> get-query
- wrap-json-response)
+ wrap-json-response)
:update-query (-> update-query
- wrap-json-response)
+ wrap-json-response)
:results-json-query (-> results-json-query
wrap-json-response)
diff --git a/src/clj/auto_ap/routes/yodlee2.clj b/src/clj/auto_ap/routes/yodlee2.clj
index 428610d4..221b8c29 100644
--- a/src/clj/auto_ap/routes/yodlee2.clj
+++ b/src/clj/auto_ap/routes/yodlee2.clj
@@ -11,7 +11,7 @@
(defn fastlink [{:keys [query-params identity]}]
(assert-can-see-client identity (pull-attr (dc/db conn) :db/id [:client/code (get query-params "client")]))
-
+
(let [token (if-let [client-id (get query-params "client-id")]
(-> client-id
Long/parseLong
@@ -19,22 +19,22 @@
:client/code
(yodlee/get-access-token))
(yodlee/get-access-token (get query-params "client")))]
- {:status 200
+ {:status 200
:headers {"Content-Type" "application/edn"}
:body (pr-str {:token token
- :url (:yodlee2-fastlink env)}) }))
+ :url (:yodlee2-fastlink env)})}))
(defn refresh-provider-accounts [{:keys [identity edn-params]}]
(assert-admin identity)
(alog/info ::refreshing :params edn-params)
- (try
+ (try
(yodlee/refresh-provider-account (-> (:client-id edn-params)
Long/parseLong
d-clients/get-by-id
:client/code)
(:provider-account-id edn-params))
- {:status 200
+ {:status 200
:headers {"Content-Type" "application/edn"}
- :body "{}" }
+ :body "{}"}
(catch Exception e
(alog/error ::error :error e)
{:status 400
@@ -48,9 +48,9 @@
(alog/info ::looking-up
:client client
:id id)
- (try
-
- {:status 200
+ (try
+
+ {:status 200
:headers {"Content-Type" "application/edn"}
:body (pr-str (yodlee/get-provider-account-detail (-> client
Long/parseLong
@@ -70,12 +70,12 @@
{:status 200
:headers {"Content-Type" "application/edn"}
:body (pr-str (yodlee/reauthenticate-and-recache
- (-> (:client-id data)
- Long/parseLong
- d-clients/get-by-id
- :client/code)
- (Long/parseLong id)
- (dissoc data :client-id )))}
+ (-> (:client-id data)
+ Long/parseLong
+ d-clients/get-by-id
+ :client/code)
+ (Long/parseLong id)
+ (dissoc data :client-id)))}
(catch Exception e
(alog/error ::error :error e)
{:status 500
@@ -85,15 +85,15 @@
(defn delete-provider-account [{:keys [edn-params identity]}]
(assert-admin identity)
- (try
+ (try
(yodlee/delete-provider-account (-> (:client-id edn-params)
Long/parseLong
d-clients/get-by-id
:client/code)
(:provider-account-id edn-params))
- {:status 200
+ {:status 200
:headers {"Content-Type" "application/edn"}
- :body (pr-str {}) }
+ :body (pr-str {})}
(catch Exception e
(alog/error ::error :error e)
{:status 400
@@ -110,11 +110,11 @@
(def routes {"api" {"/yodlee2" {"/fastlink" :fastlink
"/provider-accounts/refresh/" :refresh-provider-accounts
- ["/provider-accounts/" :client "/" :id ] :get-provider-account-detail
- ["/reauthenticate/" :id ] :reauthenticate
+ ["/provider-accounts/" :client "/" :id] :get-provider-account-detail
+ ["/reauthenticate/" :id] :reauthenticate
"/provider-accounts/delete/" :delete-provider-account}}})
(def match->handler {:fastlink (-> fastlink wrap-secure (valid-for :get))
:refresh-provider-accounts (-> refresh-provider-accounts wrap-secure (valid-for :post))
:get-provider-account-detail (-> get-provider-account-detail wrap-secure (valid-for :get))
:reauthenticate (-> reauthenticate wrap-secure (valid-for :post))
- :delete-provider-account (-> delete-provider-account wrap-secure (valid-for :post))} )
+ :delete-provider-account (-> delete-provider-account wrap-secure (valid-for :post))})
diff --git a/src/clj/auto_ap/rule_matching.clj b/src/clj/auto_ap/rule_matching.clj
index 74b9756e..da9cd683 100644
--- a/src/clj/auto_ap/rule_matching.clj
+++ b/src/clj/auto_ap/rule_matching.clj
@@ -8,12 +8,12 @@
:transaction-rule/dom-gte :transaction-rule/dom-lte
:transaction-rule/amount-gte :transaction-rule/amount-lte
:transaction-rule/client :transaction-rule/bank-account
- :transaction-rule/yodlee-merchant]} ]
+ :transaction-rule/yodlee-merchant]}]
(let [transaction-dom (some-> transaction
- :transaction/date
- .toInstant
- (.atZone (java.time.ZoneId/of "US/Pacific"))
- (.get java.time.temporal.ChronoField/DAY_OF_MONTH))]
+ :transaction/date
+ .toInstant
+ (.atZone (java.time.ZoneId/of "US/Pacific"))
+ (.get java.time.temporal.ChronoField/DAY_OF_MONTH))]
(and
(if description
(re-find description (or (:transaction/description-original transaction) ""))
@@ -55,14 +55,14 @@
true))))
(defn rule-priority [rule]
- (or
+ (or
(->> [[:transaction-rule/bank-account 0]
[:transaction-rule/client 1]
[:transaction-rule/client-group 2]
[:transaction-rule/dom-lte 3]
- [:transaction-rule/dom-gte 4]
+ [:transaction-rule/dom-gte 4]
[:transaction-rule/amount-lte 4]
- [:transaction-rule/amount-gte 4]
+ [:transaction-rule/amount-gte 4]
[:transaction-rule/description 5]
[:transaction-rule/yodlee-merchant 6]]
(filter (fn [[key]]
@@ -73,15 +73,13 @@
(defn get-matching-rules-by-priority [rules-by-priority transaction]
(loop [[rule-set & rules] rules-by-priority]
- (if rule-set
+ (if rule-set
(let [matching-rules (into [] (filter #(rule-applies? transaction %) rule-set))]
(if (seq matching-rules)
matching-rules
(recur rules)))
[])))
-
-
(defn group-rules-by-priority [rules]
(->> rules
(map (fn [r] (update r :transaction-rule/description #(some-> % ->pattern))))
@@ -150,7 +148,7 @@
(fn [transaction valid-locations]
(if (:transaction/payment transaction)
transaction
- (let [matching-rules (get-matching-rules-by-priority rules-by-priority transaction )]
+ (let [matching-rules (get-matching-rules-by-priority rules-by-priority transaction)]
(if-let [top-match (and (= (count matching-rules) 1) (first matching-rules))]
(apply-rule transaction top-match valid-locations)
transaction))))))
diff --git a/src/clj/auto_ap/server.clj b/src/clj/auto_ap/server.clj
index c3143575..9b71483c 100644
--- a/src/clj/auto_ap/server.clj
+++ b/src/clj/auto_ap/server.clj
@@ -33,23 +33,22 @@
(.addShutdownHook (Runtime/getRuntime)
(Thread. f)))
-
(defn gzip-handler []
(let [gz (GzipHandler.)]
(doto gz
(.setIncludedMethods (into-array ["GET" "POST" "PUT" "DELETE" "PATCH"]))
- (.setIncludedMimeTypes (into-array ["text/css"
- "text/*"
- "text/plain"
- "text/javascript"
- "text/csv"
- "text/html"
- "text/html;charset=utf-8"
- "application/javascript"
- "application/csv"
- "application/edn"
- "application/json"
- "image/svg+xml"]))
+ (.setIncludedMimeTypes (into-array ["text/css"
+ "text/*"
+ "text/plain"
+ "text/javascript"
+ "text/csv"
+ "text/html"
+ "text/html;charset=utf-8"
+ "application/javascript"
+ "application/csv"
+ "application/edn"
+ "application/json"
+ "image/svg+xml"]))
(.setMinGzipSize 1024))
gz))
diff --git a/src/clj/auto_ap/solr.clj b/src/clj/auto_ap/solr.clj
index 18b535f2..0feae06a 100644
--- a/src/clj/auto_ap/solr.clj
+++ b/src/clj/auto_ap/solr.clj
@@ -64,7 +64,7 @@
nil)
(defmethod datomic->solr "journal-entry" [d]
- (let [i (dc/pull (dc/db conn) '[:db/id
+ (let [i (dc/pull (dc/db conn) '[:db/id
:journal-entry/amount
:journal-entry/source
{:journal-entry/client [:client/code :db/id]
@@ -78,13 +78,13 @@
"date" (some-> i :journal-entry/date c/to-date-time (atime/unparse atime/iso-date) (str "T00:00:00Z"))
"amount" (-> i :journal-entry/amount fmt-amount)
"description" (str
- (when (:journal-entry/source i)
- (str (:journal-entry/source i) ": "))
- (str/join ", " (set (map
- (fn [li]
- (format "%s (%s)" (:account/name (:journal-entry-line/account li))
- (:account/numeric-code (:journal-entry-line/account li))))
- (:journal-entry/line-items i)))))
+ (when (:journal-entry/source i)
+ (str (:journal-entry/source i) ": "))
+ (str/join ", " (set (map
+ (fn [li]
+ (format "%s (%s)" (:account/name (:journal-entry-line/account li))
+ (:account/numeric-code (:journal-entry-line/account li))))
+ (:journal-entry/line-items i)))))
"vendor_name" (-> i :journal-entry/vendor :vendor/name)
"vendor_id" (-> i :journal-entry/vendor :db/id)
"type" "journal-entry"}))
@@ -123,7 +123,6 @@
"vendor_id" (-> i :payment/vendor :db/id)
"type" "payment"}))
-
(defprotocol SolrClient
(index-documents-raw [this index xs])
(index-documents [this index xs])
@@ -135,46 +134,45 @@
SolrClient
(index-documents-raw [this index xs]
(client/post
- (str (assoc (url/url solr-uri "solr" index "update")
- :query {"commitWithin" 5000
- "commit" true}))
-
- {:headers {"Content-Type" "application/json"}
- :socket-timeout 30000
- :connection-timeout 30000
- :method "POST"
- :body (json/write-str xs)}))
+ (str (assoc (url/url solr-uri "solr" index "update")
+ :query {"commitWithin" 5000
+ "commit" true}))
+
+ {:headers {"Content-Type" "application/json"}
+ :socket-timeout 30000
+ :connection-timeout 30000
+ :method "POST"
+ :body (json/write-str xs)}))
(index-documents [this index xs]
(client/post
- (str (assoc (url/url solr-uri "solr" index "update")
- :query {"commitWithin" 5000
- "commit" true}))
- {:headers {"Content-Type" "application/json"}
- :socket-timeout 30000
- :connection-timeout 30000
- :method "POST"
- :body (json/write-str (filter identity (map datomic->solr xs)))}))
+ (str (assoc (url/url solr-uri "solr" index "update")
+ :query {"commitWithin" 5000
+ "commit" true}))
+ {:headers {"Content-Type" "application/json"}
+ :socket-timeout 30000
+ :connection-timeout 30000
+ :method "POST"
+ :body (json/write-str (filter identity (map datomic->solr xs)))}))
(query [this index q]
(-> (client/post (str (url/url solr-uri "solr" index "query"))
- {:body (json/write-str q )
+ {:body (json/write-str q)
:socket-timeout 30000
:connection-timeout 30000
:headers {"Content-Type" "application/json"}
- :as :json}
- )
+ :as :json})
:body
:response
:docs))
(delete [this index]
(client/post
- (str (assoc (url/url solr-uri "solr" index "update")
- :query {"commitWithin" 15000
- "commit" true}))
- {:headers {"Content-Type" "application/json"}
- :method "POST"
- :body (json/write-str {"delete" {"query" "*:*"}})})))
+ (str (assoc (url/url solr-uri "solr" index "update")
+ :query {"commitWithin" 15000
+ "commit" true}))
+ {:headers {"Content-Type" "application/json"}
+ :method "POST"
+ :body (json/write-str {"delete" {"query" "*:*"}})})))
(defrecord MockSolrClient []
SolrClient
@@ -191,11 +189,7 @@
(def impl (if (= :solr (:solr-impl env))
(->RealSolrClient (:solr-uri env))
- (->MockSolrClient )))
-
-
-
-
+ (->MockSolrClient)))
(defn touch-with-ledger [i]
(index-documents impl "invoices" [i [:journal-entry/original-entity i]]))
@@ -205,7 +199,6 @@
([i index]
(index-documents impl index [i])))
-
(defrecord InMemSolrClient [data-set-atom]
SolrClient
(index-documents [this index xs]
diff --git a/src/clj/auto_ap/square/core3.clj b/src/clj/auto_ap/square/core3.clj
index 2f192cd2..ca91c7c5 100644
--- a/src/clj/auto_ap/square/core3.clj
+++ b/src/clj/auto_ap/square/core3.clj
@@ -27,11 +27,9 @@
"Authorization" (str "Bearer " (:client/square-auth-token client))
"Content-Type" "application/json"}))
-
(defn ->square-date [d]
(f/unparse (f/formatter "YYYY-MM-dd'T'HH:mm:ssZZ") d))
-
(def manifold-api-stream
(let [stream (s/stream 100)]
(->> stream
@@ -42,10 +40,10 @@
(de/loop [attempt 0]
(-> (de/chain (de/future-with (ex/execute-pool)
#_(log/info ::request-started
- :url (:url request)
- :attempt attempt
- :source "Square 3"
- :background-job "Square 3")
+ :url (:url request)
+ :attempt attempt
+ :source "Square 3"
+ :background-job "Square 3")
(try
(client/request (assoc request
:socket-timeout 10000
@@ -104,7 +102,6 @@
:exception error))
[]))))
-
(def item-cache (atom {}))
(defn fetch-catalog [client i v]
@@ -124,13 +121,11 @@
#(do (swap! item-cache assoc i %)
%))))
-
(defn fetch-catalog-cache [client i version]
(if (get @item-cache i)
(de/success-deferred (get @item-cache i))
(fetch-catalog client i version)))
-
(defn item->category-name-impl [client item version]
(capture-context->lc
(cond (:item_id (:item_variation_data item))
@@ -161,7 +156,6 @@
:item item)
"Uncategorized"))))
-
(defn item-id->category-name [client i version]
(capture-context->lc
(-> [client i]
@@ -226,7 +220,6 @@
(concat (:orders result) continued-results))))
(:orders result)))))))
-
(defn search
([client location start end]
(capture-context->lc
@@ -250,11 +243,9 @@
(concat (:orders result) continued-results))))
(:orders result))))))))
-
(defn amount->money [amt]
(* 0.01 (or (:amount amt) 0.0)))
-
;; to get totals:
(comment
(reduce
@@ -280,7 +271,7 @@
:reference-link (str (url/url "https://squareup.com/receipt/preview" (:id t)))
:external-id (when (:id t)
(str "square/charge/" (:id t)))
- :processor (cond
+ :processor (cond
(#{"OTHER" "THIRD_PARTY_CARD"} (:type t))
(condp = (some-> (:note t) str/lower-case)
"doordash" :ccp-processor/doordash
@@ -353,7 +344,7 @@
#:sales-order
{:date (if (= "Invoices" (:name (:source order)))
(when (:closed_at order)
- (coerce/to-date (time/to-time-zone (coerce/to-date-time (:closed_at order)) (time/time-zone-for-id "America/Los_Angeles"))))
+ (coerce/to-date (time/to-time-zone (coerce/to-date-time (:closed_at order)) (time/time-zone-for-id "America/Los_Angeles"))))
(coerce/to-date (time/to-time-zone (coerce/to-date-time (:created_at order)) (time/time-zone-for-id "America/Los_Angeles"))))
:client (:db/id client)
:location (:square-location/client-location location)
@@ -415,7 +406,6 @@
:client client
:location location)))))))
-
(defn get-payment [client p]
(de/chain (manifold-api-call
{:url (str "https://connect.squareup.com/v2/payments/" p)
@@ -424,7 +414,6 @@
:body
:payment))
-
(defn continue-payout-entry-list [c l poi cursor]
(capture-context->lc lc
(de/chain
@@ -618,7 +607,6 @@
:count (count x))
@(dc/transact-async conn x))))))))
-
(defn upsert-payouts
([client]
(apply de/zip
@@ -667,7 +655,6 @@
(log/info ::done-loading-refunds)))))))
-
(defn get-cash-shift [client id]
(de/chain (manifold-api-call {:url (str (url/url "https://connect.squareup.com/v2/cash-drawers/shifts" id))
:method :get
@@ -826,8 +813,6 @@
d1
d2))
-
-
(defn remove-voided-orders
([client]
(apply de/zip
@@ -854,7 +839,7 @@
(:sales-order/external-id o))))))
(s/map (fn [[o]]
[[:db/retractEntity [:sales-order/external-id (:sales-order/external-id o)]]]))
-
+
(s/reduce into [])))
(fn [results]
@@ -863,31 +848,26 @@
(log/info ::removing-orders
:count (count x))
@(dc/transact-async conn x)))))
- (de/catch (fn [e]
- (log/warn ::couldnt-remove :error e)
- nil) ))))))
+ (de/catch (fn [e]
+ (log/warn ::couldnt-remove :error e)
+ nil)))))))
-#_(comment
- (require 'auto-ap.time-reader)
+#_(comment
+ (require 'auto-ap.time-reader)
- @(let [[c [l]] (get-square-client-and-location "DBFS") ]
- (log/peek :x [ c l])
- (search c l #clj-time/date-time "2026-03-28" #clj-time/date-time "2026-03-29")
+ @(let [[c [l]] (get-square-client-and-location "DBFS")]
+ (log/peek :x [c l])
+ (search c l #clj-time/date-time "2026-03-28" #clj-time/date-time "2026-03-29"))
- )
+ @(let [[c [l]] (get-square-client-and-location "NGAK")]
+ (log/peek :x [c l])
- @(let [[c [l]] (get-square-client-and-location "NGAK") ]
- (log/peek :x [ c l])
-
- (remove-voided-orders c l #clj-time/date-time "2024-04-11" #clj-time/date-time "2024-04-15"))
- (doseq [c (get-square-clients)]
- (try
- @(remove-voided-orders c)
- (catch Exception e
- nil)))
-
-
- )
+ (remove-voided-orders c l #clj-time/date-time "2024-04-11" #clj-time/date-time "2024-04-15"))
+ (doseq [c (get-square-clients)]
+ (try
+ @(remove-voided-orders c)
+ (catch Exception e
+ nil))))
(defn upsert-all [& clients]
(capture-context->lc
@@ -956,8 +936,6 @@
[:clients clients]
@(apply upsert-all clients)))
-
-
(comment
(defn refunds-raw-cont
([client l cursor so-far]
@@ -987,9 +965,8 @@
(->>
@(let [[c [l]] (get-square-client-and-location "NGGG")]
-
(search c l (time/now) (time/plus (time/now) (time/days -1))))
-
+
(filter (fn [r]
(str/starts-with? (:created_at r) "2024-03-14"))))
@@ -997,7 +974,6 @@
(->>
@(let [[c [l]] (get-square-client-and-location "NGGG")]
-
(refunds-raw-cont c l nil []))
(filter (fn [r]
(str/starts-with? (:created_at r) "2024-03-14")))))
@@ -1031,13 +1007,8 @@
[]))]
[(:client/code c) (atime/unparse-local (clj-time.coerce/to-date-time (:sales-order/date bad-row)) atime/normal-date) (:sales-order/total bad-row) (:sales-order/tax bad-row) (:sales-order/tip bad-row) (:db/id bad-row)])
:separator \tab)
-
-
-
-
- ;; =>
-
+;; =>
(require 'auto-ap.time-reader)
@@ -1046,27 +1017,16 @@
(clojure.pprint/pprint (let [[c [l]] (get-square-client-and-location "NGVT")]
l
-
(def z @(search c l #clj-time/date-time "2025-02-23T00:00:00-08:00"
#clj-time/date-time "2025-02-28T00:00:00-08:00"))
- (take 10 (map #(first (deref (order->sales-order c l %))) z)))
+ (take 10 (map #(first (deref (order->sales-order c l %))) z))))
-
- )
-
-
-
-
- (->> z
+ (->> z
(filter (fn [o]
(seq (filter (comp #{"OTHER"} :type) (:tenders o)))))
(filter #(not (:name (:source %))))
- (count)
-
- )
-
-
-
+ (count))
+
(doseq [[code] (seq (dc/q '[:find ?code
:in $
:where [?o :sales-order/date ?d]
@@ -1075,32 +1035,22 @@
[?o :sales-order/client ?c]
[?c :client/code ?code]]
(dc/db conn)))
- :let [[c [l]] (get-square-client-and-location code)
- ]
+ :let [[c [l]] (get-square-client-and-location code)]
order @(search c l #clj-time/date-time "2026-01-01T00:00:00-08:00" (time/now))
- :when (= "Invoices" (:name (:source order) ))
+ :when (= "Invoices" (:name (:source order)))
:let [[sales-order] @(order->sales-order c l order)]]
-
+
(when (should-import-order? order)
(println "DATE IS" (:sales-order/date sales-order))
(when (some-> (:sales-order/date sales-order) coerce/to-date-time (time/after? #clj-time/date-time "2026-2-16T00:00:00-08:00"))
(println "WOULD UPDATE" sales-order)
- @(dc/transact auto-ap.datomic/conn [sales-order])
- )
- #_@(dc/transact )
- (println "DONE"))
-
-
- )
+ @(dc/transact auto-ap.datomic/conn [sales-order]))
+ #_@(dc/transact)
+ (println "DONE")))
#_(filter (comp #{"OTHER"} :type) (mapcat :tenders z))
-
@(let [[c [l]] (get-square-client-and-location "NGRY")]
#_(search c l (clj-time.coerce/from-date #inst "2025-02-28") (clj-time.coerce/from-date #inst "2025-03-01"))
- (order->sales-order c l (:order (get-order c l "KdvwntmfMNTKBu8NOocbxatOs18YY" )))
-
- )
-
- )
+ (order->sales-order c l (:order (get-order c l "KdvwntmfMNTKBu8NOocbxatOs18YY")))))
diff --git a/src/clj/auto_ap/ssr/account.clj b/src/clj/auto_ap/ssr/account.clj
index d298631e..2be031c4 100644
--- a/src/clj/auto_ap/ssr/account.clj
+++ b/src/clj/auto_ap/ssr/account.clj
@@ -24,7 +24,7 @@
:account/invoice-allowance [:db/ident]
:account/client-overrides [:db/id
:account-client-override/name
- {:account-client-override/client [:db/id :client/name]}]} ])
+ {:account-client-override/client [:db/id :client/name]}]}])
(defn search- [id query client]
(let [client-part (if (some->> client (can-see-client? id))
@@ -71,9 +71,9 @@
(valid-allowances (-> a allowance :db/ident))
(= (:db/id a) vendor-account))))
(map (fn [[n a]]
- {:label (str (:account/numeric-code a) " - " (if client-id
+ {:label (str (:account/numeric-code a) " - " (if client-id
(:account/name (d-accounts/clientize a client-id))
- n))
+ n))
:value (:db/id a)
:location (:account/location a)
:warning (when (= :allowance/warn (-> a allowance :db/ident))
diff --git a/src/clj/auto_ap/ssr/admin.clj b/src/clj/auto_ap/ssr/admin.clj
index b412afcc..687010ad 100644
--- a/src/clj/auto_ap/ssr/admin.clj
+++ b/src/clj/auto_ap/ssr/admin.clj
@@ -15,84 +15,77 @@
(defn hourly-changes []
(let [tx-instant-attr (:db/id (dc/pull (dc/db conn) '[:db/id] :db/txInstant))
- tx-lookup (->>
- (dc/tx-range
- (dc/log conn)
- (coerce/to-date (time/plus (time/now) (time/hours -24)))
- (coerce/to-date (time/now)))
- (map (fn extract-tx-instant [tx]
- (let [tx-id (->> (:data tx)
- (map (fn [d]
- (:tx d)))
- first)
- tx-instant (->> tx
- :data
- (filter (fn [d]
- (and (= (:e d) tx-id)
- (= tx-instant-attr (:a d)))))
- (map :v)
- first)]
-
- tx-instant)))
- (group-by (fn hours-ago [d]
- (time/in-hours (time/interval (coerce/to-date-time d) (time/now)))
- ))
- )]
+ tx-lookup (->>
+ (dc/tx-range
+ (dc/log conn)
+ (coerce/to-date (time/plus (time/now) (time/hours -24)))
+ (coerce/to-date (time/now)))
+ (map (fn extract-tx-instant [tx]
+ (let [tx-id (->> (:data tx)
+ (map (fn [d]
+ (:tx d)))
+ first)
+ tx-instant (->> tx
+ :data
+ (filter (fn [d]
+ (and (= (:e d) tx-id)
+ (= tx-instant-attr (:a d)))))
+ (map :v)
+ first)]
+
+ tx-instant)))
+ (group-by (fn hours-ago [d]
+ (time/in-hours (time/interval (coerce/to-date-time d) (time/now))))))]
+
(for [h (range 24)]
(count (tx-lookup h [])))))
(defn page [request]
- (base-page
- request
- (com/page {:nav com/admin-aside-nav
- :client-selection (:client-selection request)
- :clients (:clients request)
- :client (:client request)
- :identity (:identity request)}
- (com/breadcrumbs {} [:a {:href (bidi/path-for ssr-routes/only-routes :auto-ap.routes.admin/page)}
- "Admin"])
- [:div.flex.space-x-4
- (com/content-card {:class "w-1/4"}
- [:div {:class "flex flex-col px-4 py-3 space-y-3"}
- [:div
- [:h1.text-2xl.mb-3.font-bold "Growth in clients"]
- [:div
- [:div {:class "w-full h-64"
- :id "client-chart"
- :data-chart (hx/json {
- :labels ["2 years ago" "1 year ago" "today"],
- :series [(for [n [2 1 0]
- :let [start (time/plus (time/now) (time/years (- n)))]]
- (->> (dc/q '[:find (count ?c)
- :in $
- :where [?c :client/code]]
- (dc/as-of (dc/db conn) (coerce/to-date start)))
- first
- first))]})}]
- [:script {:lang "javascript"}
- (hiccup/raw
- "new Chartist.Bar('#client-chart', JSON.parse(document.getElementById('client-chart').getAttribute('data-chart')))")]]]])
+ (base-page
+ request
+ (com/page {:nav com/admin-aside-nav
+ :client-selection (:client-selection request)
+ :clients (:clients request)
+ :client (:client request)
+ :identity (:identity request)}
+ (com/breadcrumbs {} [:a {:href (bidi/path-for ssr-routes/only-routes :auto-ap.routes.admin/page)}
+ "Admin"])
+ [:div.flex.space-x-4
+ (com/content-card {:class "w-1/4"}
+ [:div {:class "flex flex-col px-4 py-3 space-y-3"}
+ [:div
+ [:h1.text-2xl.mb-3.font-bold "Growth in clients"]
+ [:div
+ [:div {:class "w-full h-64"
+ :id "client-chart"
+ :data-chart (hx/json {:labels ["2 years ago" "1 year ago" "today"],
+ :series [(for [n [2 1 0]
+ :let [start (time/plus (time/now) (time/years (- n)))]]
+ (->> (dc/q '[:find (count ?c)
+ :in $
+ :where [?c :client/code]]
+ (dc/as-of (dc/db conn) (coerce/to-date start)))
+ first
+ first))]})}]
+ [:script {:lang "javascript"}
+ (hiccup/raw
+ "new Chartist.Bar('#client-chart', JSON.parse(document.getElementById('client-chart').getAttribute('data-chart')))")]]]])
- (com/content-card {:class "w-1/2"}
- [:div {:class "flex flex-col px-4 py-3 space-y-3"}
- [:div
- [:h1.text-2xl.mb-3.font-bold "Changes by hour"]
- [:div
- [:div {:class "w-full h-64"
- :id "changes"
- :data-chart (hx/json {
- :labels (for [n (range -24 0)]
- (format "%d" n)),
- :series [(hourly-changes)]})}]
- [:script {:lang "javascript"}
- (hiccup/raw
- "new Chartist.Line('#changes', JSON.parse(document.getElementById('changes').getAttribute('data-chart')))")]]]])]
- )
- "Admin")
- )
+ (com/content-card {:class "w-1/2"}
+ [:div {:class "flex flex-col px-4 py-3 space-y-3"}
+ [:div
+ [:h1.text-2xl.mb-3.font-bold "Changes by hour"]
+ [:div
+ [:div {:class "w-full h-64"
+ :id "changes"
+ :data-chart (hx/json {:labels (for [n (range -24 0)]
+ (format "%d" n)),
+ :series [(hourly-changes)]})}]
+ [:script {:lang "javascript"}
+ (hiccup/raw
+ "new Chartist.Line('#changes', JSON.parse(document.getElementById('changes').getAttribute('data-chart')))")]]]])])
+ "Admin"))
(def key->handler
- {
- :auto-ap.routes.admin/page (wrap-client-redirect-unauthenticated (wrap-admin page))
- })
+ {:auto-ap.routes.admin/page (wrap-client-redirect-unauthenticated (wrap-admin page))})
diff --git a/src/clj/auto_ap/ssr/admin/accounts.clj b/src/clj/auto_ap/ssr/admin/accounts.clj
index 3b5ac0a0..71437609 100644
--- a/src/clj/auto_ap/ssr/admin/accounts.clj
+++ b/src/clj/auto_ap/ssr/admin/accounts.clj
@@ -37,11 +37,11 @@
(defn filters [request]
[:form {"hx-trigger" "change delay:500ms, keyup changed from:.hot-filter delay:1000ms"
"hx-get" (bidi/path-for ssr-routes/only-routes
- :admin-account-table)
+ :admin-account-table)
"hx-target" "#entity-table"
"hx-indicator" "#entity-table"}
- [:fieldset.space-y-6
+ [:fieldset.space-y-6
(com/field {:label "Name"}
(com/text-input {:name "name"
:id "name"
@@ -111,19 +111,19 @@
'[(clojure.string/upper-case ?an) ?upper-an]
'[(clojure.string/includes? ?upper-an ?ns)]]}
:args [(str/upper-case (:name query-params))]})
-
+
(some->> query-params :code)
(merge-query {:query {:find []
:in ['?nc]
:where ['[?e :account/numeric-code ?nc]]}
:args [(:code query-params)]})
-
+
(some->> query-params :type)
(merge-query {:query {:find []
:in ['?r]
- :where ['[?e :account/type ?r] ]}
+ :where ['[?e :account/type ?r]]}
:args [(some->> query-params :type)]})
-
+
true
(merge-query {:query {:find ['?sort-default '?e]
:where ['[?e :account/numeric-code ?un]
@@ -202,21 +202,20 @@
[:account/numeric-code]
:form-params form-params)))
_ (some->> form-params
- :account/client-overrides
- (group-by :account-client-override/client)
- (filter (fn [[_ overrides]]
- (> (count overrides) 1)))
- (map first)
- seq
- (#(form-validation-error (format "Client(s) %s have more than one override."
- (str/join ", "
- (map (fn [client]
- (format "'%s'" (pull-attr (dc/db conn)
- :client/name
- (-> client)))
- ) %)))
- :form-params form-params)) ;; TODO shouldnt need to bubble this through. See if we can eliminate the passing of form and last-form.
- )
+ :account/client-overrides
+ (group-by :account-client-override/client)
+ (filter (fn [[_ overrides]]
+ (> (count overrides) 1)))
+ (map first)
+ seq
+ (#(form-validation-error (format "Client(s) %s have more than one override."
+ (str/join ", "
+ (map (fn [client]
+ (format "'%s'" (pull-attr (dc/db conn)
+ :client/name
+ (-> client)))) %)))
+ :form-params form-params)) ;; TODO shouldnt need to bubble this through. See if we can eliminate the passing of form and last-form.
+ )
{:keys [tempids]} (audit-transact [[:upsert-entity (cond-> entity
(:account/numeric-code entity) (assoc :account/code (str (:account/numeric-code entity))))]]
(:identity request))
@@ -241,11 +240,11 @@
"client_id" (:db/id (:account-client-override/client o))
"account_client_override_id" (:db/id o)})))
(html-response
- (row* identity updated-account {:flash? true})
- :headers (cond-> {"hx-trigger" "modalclose"}
- (= :post request-method) (assoc "hx-retarget" "#entity-table tbody"
- "hx-reswap" "afterbegin")
- (= :put request-method) (assoc "hx-retarget" (format "#entity-table tr[data-id=\"%d\"]" (:db/id updated-account)))))))
+ (row* identity updated-account {:flash? true})
+ :headers (cond-> {"hx-trigger" "modalclose"}
+ (= :post request-method) (assoc "hx-retarget" "#entity-table tbody"
+ "hx-reswap" "afterbegin")
+ (= :put request-method) (assoc "hx-retarget" (format "#entity-table tr[data-id=\"%d\"]" (:db/id updated-account)))))))
(defn client-override* [override]
(com/data-grid-row (-> {:x-ref "p"
@@ -259,165 +258,161 @@
(com/data-grid-cell {}
(com/validated-field {:errors (fc/field-errors)}
(com/typeahead {:name (fc/field-name)
- :placeholder "Search..."
- :class "w-96"
- :url (bidi/path-for ssr-routes/only-routes
- :company-search)
- :value (fc/field-value)
- :content-fn #(pull-attr (dc/db conn) :client/name %)}))))
+ :placeholder "Search..."
+ :class "w-96"
+ :url (bidi/path-for ssr-routes/only-routes
+ :company-search)
+ :value (fc/field-value)
+ :content-fn #(pull-attr (dc/db conn) :client/name %)}))))
(fc/with-field :account-client-override/name
(com/data-grid-cell
- {}
- (com/validated-field {:errors (fc/field-errors)}
- (com/text-input {:name (fc/field-name)
- :class "w-96"
- :value (fc/field-value)}))))
+ {}
+ (com/validated-field {:errors (fc/field-errors)}
+ (com/text-input {:name (fc/field-name)
+ :class "w-96"
+ :value (fc/field-value)}))))
(com/data-grid-cell {:class "align-top"}
(com/a-icon-button {"@click.prevent.stop" "show=false; setTimeout(() => $refs.p.remove(), 500)"} svg/x))))
(defn dialog* [{:keys [entity form-params form-errors]}]
(fc/start-form form-params form-errors
- [:div {:x-data (hx/json {"accountName" (or (:account/name form-params) (:account/numeric-code entity))
- "accountCode" (or (:account/numeric-code form-params) (:account/numeric-code entity) )})
- :hx-target "this"
- }
- (com/modal
- {}
- [:form (-> {:hx-ext "response-targets"
- :hx-swap "outerHTML swap:300ms"
- :hx-target-400 "#form-errors .error-content" }
- (assoc (if (:db/id entity)
- :hx-put
- :hx-post)
- (str (bidi/path-for ssr-routes/only-routes
- :admin-transaction-rule-edit-save))))
- [:fieldset {:class "hx-disable"}
- (com/modal-card
- {:class "md:h-[600px]"}
- [:div.flex [:div.p-2 "Account"] [:p.ml-2.rounded.bg-gray-200.p-2.dark:bg-gray-600
- [:span {:x-text "accountCode"}]
- " - "
- [:span {:x-text "accountName"}]]]
- [:div.space-y-1
- (when-let [id (:db/id entity)]
- (com/hidden {:name "db/id"
- :value id}))
+ [:div {:x-data (hx/json {"accountName" (or (:account/name form-params) (:account/numeric-code entity))
+ "accountCode" (or (:account/numeric-code form-params) (:account/numeric-code entity))})
+ :hx-target "this"}
+ (com/modal
+ {}
+ [:form (-> {:hx-ext "response-targets"
+ :hx-swap "outerHTML swap:300ms"
+ :hx-target-400 "#form-errors .error-content"}
+ (assoc (if (:db/id entity)
+ :hx-put
+ :hx-post)
+ (str (bidi/path-for ssr-routes/only-routes
+ :admin-transaction-rule-edit-save))))
+ [:fieldset {:class "hx-disable"}
+ (com/modal-card
+ {:class "md:h-[600px]"}
+ [:div.flex [:div.p-2 "Account"] [:p.ml-2.rounded.bg-gray-200.p-2.dark:bg-gray-600
+ [:span {:x-text "accountCode"}]
+ " - "
+ [:span {:x-text "accountName"}]]]
+ [:div.space-y-1
+ (when-let [id (:db/id entity)]
+ (com/hidden {:name "db/id"
+ :value id}))
- (fc/with-field :account/numeric-code
- (if (nil? (:db/id entity))
- (com/validated-field {:label "Numeric code"
- :errors (fc/field-errors)}
- (com/text-input {:name (fc/field-name)
- :value (fc/field-value)
- :x-model "accountCode"
- :autofocus true
- :class "w-32"}))
- (com/hidden {:name (fc/field-name)
- :value (fc/field-value)})))
- (fc/with-field :account/name
- (com/validated-field {:label "Name"
- :errors (fc/field-errors)}
- (com/text-input {:name (fc/field-name)
- :x-model "accountName"
+ (fc/with-field :account/numeric-code
+ (if (nil? (:db/id entity))
+ (com/validated-field {:label "Numeric code"
+ :errors (fc/field-errors)}
+ (com/text-input {:name (fc/field-name)
+ :value (fc/field-value)
+ :x-model "accountCode"
+ :autofocus true
+ :class "w-32"}))
+ (com/hidden {:name (fc/field-name)
+ :value (fc/field-value)})))
+ (fc/with-field :account/name
+ (com/validated-field {:label "Name"
+ :errors (fc/field-errors)}
+ (com/text-input {:name (fc/field-name)
+ :x-model "accountName"
- :class "w-64"
- :value (fc/field-value)})))
- (fc/with-field :account/type
- (com/validated-field {:label "Account Type"
- :errors (fc/field-errors)}
- (com/select {:name (fc/field-name)
- :class "w-36"
- :id "type"
- :value (some-> (fc/field-value) name)
- :options (ref->select-options "account-type")})))
- (fc/with-field :account/location
- (com/validated-field {:label "Location"
- :errors (fc/field-errors)}
- (com/text-input {:name (fc/field-name)
- :class "w-16"
- :value (fc/field-value)})))
+ :class "w-64"
+ :value (fc/field-value)})))
+ (fc/with-field :account/type
+ (com/validated-field {:label "Account Type"
+ :errors (fc/field-errors)}
+ (com/select {:name (fc/field-name)
+ :class "w-36"
+ :id "type"
+ :value (some-> (fc/field-value) name)
+ :options (ref->select-options "account-type")})))
+ (fc/with-field :account/location
+ (com/validated-field {:label "Location"
+ :errors (fc/field-errors)}
+ (com/text-input {:name (fc/field-name)
+ :class "w-16"
+ :value (fc/field-value)})))
- [:div.flex.flex-wrap.gap-4
- (fc/with-field :account/invoice-allowance
- (com/validated-field {:label "Invoice Allowance"
- :errors (fc/field-errors)}
- (com/select {:name (fc/field-name)
- :value (some-> (fc/field-value) name)
- :class "w-36"
- :options (ref->select-options "allowance")})))
- (fc/with-field :account/vendor-allowance
- (com/validated-field {:label "Vendor Allowance"
- :errors (fc/field-errors)}
- (com/select {:name (fc/field-name)
- :class "w-36"
- :value (some-> (fc/field-value) name)
- :options (ref->select-options "allowance")})))]
- (fc/with-field :account/applicability
- (com/validated-field {:label "Applicability"
- :errors (fc/field-errors)}
- (com/select {:name (fc/field-name)
- :class "w-36"
- :value (some-> (fc/field-value) name)
- :options (ref->select-options "account-applicability")})))
+ [:div.flex.flex-wrap.gap-4
+ (fc/with-field :account/invoice-allowance
+ (com/validated-field {:label "Invoice Allowance"
+ :errors (fc/field-errors)}
+ (com/select {:name (fc/field-name)
+ :value (some-> (fc/field-value) name)
+ :class "w-36"
+ :options (ref->select-options "allowance")})))
+ (fc/with-field :account/vendor-allowance
+ (com/validated-field {:label "Vendor Allowance"
+ :errors (fc/field-errors)}
+ (com/select {:name (fc/field-name)
+ :class "w-36"
+ :value (some-> (fc/field-value) name)
+ :options (ref->select-options "allowance")})))]
+ (fc/with-field :account/applicability
+ (com/validated-field {:label "Applicability"
+ :errors (fc/field-errors)}
+ (com/select {:name (fc/field-name)
+ :class "w-36"
+ :value (some-> (fc/field-value) name)
+ :options (ref->select-options "account-applicability")})))
- (fc/with-field :account/client-overrides
-
- (com/field {:label "Client Overrides" :id "client-overrides"}
+ (fc/with-field :account/client-overrides
- (com/data-grid {:headers [(com/data-grid-header {} "Client")
- (com/data-grid-header {} "Account name")
- (com/data-grid-header {})]
- :id "client-override-table"}
- (fc/cursor-map
- #(client-override* %))
+ (com/field {:label "Client Overrides" :id "client-overrides"}
- (com/data-grid-new-row {:colspan 3
- :index (count (fc/field-value))
- :hx-get (bidi/path-for ssr-routes/only-routes
- :admin-account-client-override-new)}
- "New override"))))]
- [:div
- (com/form-errors {:errors (:errors fc/*form-errors*)})
- (com/validated-save-button {:errors (seq form-errors)}
- "Save account")])]])]))
+ (com/data-grid {:headers [(com/data-grid-header {} "Client")
+ (com/data-grid-header {} "Account name")
+ (com/data-grid-header {})]
+ :id "client-override-table"}
+ (fc/cursor-map
+ #(client-override* %))
-(defn new-client-override [{ {:keys [index]} :query-params}]
- (html-response
- (fc/start-form-with-prefix
- [:account/client-overrides (or index 0)]
- {:db/id (str (java.util.UUID/randomUUID))
- :new? true}
- []
- (client-override* fc/*current*))))
+ (com/data-grid-new-row {:colspan 3
+ :index (count (fc/field-value))
+ :hx-get (bidi/path-for ssr-routes/only-routes
+ :admin-account-client-override-new)}
+ "New override"))))]
+ [:div
+ (com/form-errors {:errors (:errors fc/*form-errors*)})
+ (com/validated-save-button {:errors (seq form-errors)}
+ "Save account")])]])]))
+
+(defn new-client-override [{{:keys [index]} :query-params}]
+ (html-response
+ (fc/start-form-with-prefix
+ [:account/client-overrides (or index 0)]
+ {:db/id (str (java.util.UUID/randomUUID))
+ :new? true}
+ []
+ (client-override* fc/*current*))))
(def form-schema (mc/schema
- [:map
- [:db/id {:optional true} [:maybe entity-id]]
- [:account/numeric-code {:optional true} [:maybe :int]]
- [:account/name [:string {:min 1 :decode/string strip}]]
- [:account/location {:optional true} [:maybe [:string {:decode/string strip}]]]
- [:account/type (ref->enum-schema "account-type")]
- [:account/applicability (ref->enum-schema "account-applicability")] ;
- [:account/invoice-allowance (ref->enum-schema "allowance")]
- [:account/vendor-allowance (ref->enum-schema "allowance")]
- [:account/client-overrides {:optional true}
- [:maybe
- (many-entity {}
- [:db/id [:or entity-id temp-id]]
- [:account-client-override/client entity-id]
- [:account-client-override/name [:string {:min 2 :decode/string strip}]])]]]))
+ [:map
+ [:db/id {:optional true} [:maybe entity-id]]
+ [:account/numeric-code {:optional true} [:maybe :int]]
+ [:account/name [:string {:min 1 :decode/string strip}]]
+ [:account/location {:optional true} [:maybe [:string {:decode/string strip}]]]
+ [:account/type (ref->enum-schema "account-type")]
+ [:account/applicability (ref->enum-schema "account-applicability")] ;
+ [:account/invoice-allowance (ref->enum-schema "allowance")]
+ [:account/vendor-allowance (ref->enum-schema "allowance")]
+ [:account/client-overrides {:optional true}
+ [:maybe
+ (many-entity {}
+ [:db/id [:or entity-id temp-id]]
+ [:account-client-override/client entity-id]
+ [:account-client-override/name [:string {:min 2 :decode/string strip}]])]]]))
(defn account-dialog [{:keys [entity form-params form-errors]}]
(modal-response (dialog* {:entity entity
- :form-params (or (when (seq form-params)
- form-params)
- (when entity
- (mc/decode form-schema entity main-transformer))
- {})
- :form-errors form-errors})))
-
-
-
+ :form-params (or (when (seq form-params)
+ form-params)
+ (when entity
+ (mc/decode form-schema entity main-transformer))
+ {})
+ :form-errors form-errors})))
(def key->handler
(apply-middleware-to-all-handlers
diff --git a/src/clj/auto_ap/ssr/admin/background_jobs.clj b/src/clj/auto_ap/ssr/admin/background_jobs.clj
index b5576aa1..34977e2a 100644
--- a/src/clj/auto_ap/ssr/admin/background_jobs.clj
+++ b/src/clj/auto_ap/ssr/admin/background_jobs.clj
@@ -28,14 +28,13 @@
(com.amazonaws.services.ecs.model AssignPublicIp)))
(defn get-ecs-tasks []
- (->>
- (concat (:task-arns (ecs/list-tasks :max-results 50)) (:task-arns (ecs/list-tasks :desired-status "STOPPED" :max-results 50)))
- (ecs/describe-tasks :include [] :tasks)
- :tasks
- (map #(assoc % :task-definition (:task-definition (ecs/describe-task-definition :task-definition (:task-definition-arn %)))))
- (sort-by :created-at)
- reverse))
-
+ (->>
+ (concat (:task-arns (ecs/list-tasks :max-results 50)) (:task-arns (ecs/list-tasks :desired-status "STOPPED" :max-results 50)))
+ (ecs/describe-tasks :include [] :tasks)
+ :tasks
+ (map #(assoc % :task-definition (:task-definition (ecs/describe-task-definition :task-definition (:task-definition-arn %)))))
+ (sort-by :created-at)
+ reverse))
(defn is-background-job?
"This function checks whether a given task is a background job.
@@ -60,7 +59,7 @@
(defn job-exited-successfully? [task]
(if (= 0 (->> task
:containers
- (filter (comp #{"integreat-app" } :name))
+ (filter (comp #{"integreat-app"} :name))
(first)
:exit-code))
true
@@ -77,7 +76,7 @@
:succeeded
:failed))
:name (task-definition->job-name (:task-definition task))
- :end-date (some-> (:stopped-at task) coerce/to-date-time (time/to-time-zone (time/time-zone-for-offset 0)))
+ :end-date (some-> (:stopped-at task) coerce/to-date-time (time/to-time-zone (time/time-zone-for-offset 0)))
:start-date (some-> (:created-at task) coerce/to-date-time (time/to-time-zone (time/time-zone-for-offset 0)))})
(defn fetch-page [request]
@@ -85,7 +84,7 @@
(filter is-background-job?)
(map ecs-task->job))]
[jobs (count jobs)]))
-(def query-schema (mc/schema [:map ]))
+(def query-schema (mc/schema [:map]))
(def grid-page
(helper/build {:id "job-table"
@@ -107,8 +106,7 @@
:entity-name "Job"
:query-schema query-schema
:route :admin-job-table
- :headers [
- {:key "start"
+ :headers [{:key "start"
:name "Start"
:render #(some-> % :start-date (atime/unparse-local atime/standard-time))}
{:key "end"
@@ -119,7 +117,7 @@
:render (fn [e]
(when (and (:start-date e)
(:end-date e))
- (str (time/in-minutes (time/interval
+ (str (time/in-minutes (time/interval
(:start-date e)
(:end-date e))) " minutes")))}
{:key "name"
@@ -150,16 +148,16 @@
:network-configuration {:aws-vpc-configuration {:subnets ["subnet-5e675761" "subnet-8519fde2" "subnet-89bab8d4"]
:security-groups ["sg-004e5855310c453a3" "sg-02d167406b1082698"]
:assign-public-ip AssignPublicIp/ENABLED}}}
- args (assoc-in [:overrides :container-overrides ] [{:name "integreat-app" :environment [{:name "args" :value (pr-str args)}]}]))))
+ args (assoc-in [:overrides :container-overrides] [{:name "integreat-app" :environment [{:name "args" :value (pr-str args)}]}]))))
(defn job-start [{:keys [form-params]}]
(if (not (get (currently-running-jobs) (:name form-params)))
(let [new-job (run-task
- (-> (:name form-params)
- (str/replace #"-" "_")
- (str/replace #":" "")
- (str "_" (:dd-env env)))
- (dissoc form-params :name))]
+ (-> (:name form-params)
+ (str/replace #"-" "_")
+ (str/replace #":" "")
+ (str "_" (:dd-env env)))
+ (dissoc form-params :name))]
{:message (str "task " (str new-job) " started.")})
(form-validation-error "This job is already running"
:form-params form-params)))
@@ -170,107 +168,101 @@
[(fc/with-field :ledger-url
(com/validated-field {:label "Url"
:errors (fc/field-errors)}
- [:div.flex.place-items-center.gap-2
- [:pre.text-xs.mr-1 "s3://data.prod.app.integreatconsult.com/bulk-import/"]
- (com/text-input {:placeholder "ledger-data.csv"
- :name (fc/field-name)
- :value (fc/field-value)} )]))]
+ [:div.flex.place-items-center.gap-2
+ [:pre.text-xs.mr-1 "s3://data.prod.app.integreatconsult.com/bulk-import/"]
+ (com/text-input {:placeholder "ledger-data.csv"
+ :name (fc/field-name)
+ :value (fc/field-value)})]))]
(= "register-invoice-import" name)
- [
- (fc/with-field :invoice-url
- (com/validated-field {:label "Url"
- :errors (fc/field-errors)}
- [:div.flex.place-items-center.gap-2
- [:pre.text-xs.mr-1 "s3://data.prod.app.integreatconsult.com/bulk-import/"]
- (com/text-input {:placeholder "invoice-data.csv"
- :name (fc/field-name)
- :value (fc/field-value)} )]))]
+ [(fc/with-field :invoice-url
+ (com/validated-field {:label "Url"
+ :errors (fc/field-errors)}
+ [:div.flex.place-items-center.gap-2
+ [:pre.text-xs.mr-1 "s3://data.prod.app.integreatconsult.com/bulk-import/"]
+ (com/text-input {:placeholder "invoice-data.csv"
+ :name (fc/field-name)
+ :value (fc/field-value)})]))]
(= "load-historical-sales" name)
- [
- (fc/with-field :client
- (com/validated-field {:label "Client"
- :errors (fc/field-errors)}
- (com/typeahead {:name (fc/field-name)
- :value (fc/field-value)
- :placeholder "Search..."
- :url (bidi/path-for ssr-routes/only-routes
- :company-search)})))
- (fc/with-field :days
+ [(fc/with-field :client
+ (com/validated-field {:label "Client"
+ :errors (fc/field-errors)}
+ (com/typeahead {:name (fc/field-name)
+ :value (fc/field-value)
+ :placeholder "Search..."
+ :url (bidi/path-for ssr-routes/only-routes
+ :company-search)})))
+ (fc/with-field :days
(com/validated-field {:label "Days to load"
:errors (fc/field-errors)}
(com/text-input {:placeholder "60"
:name (fc/field-name)
- :value (fc/field-value)} )))]
- :else nil))
+ :value (fc/field-value)})))]
+ :else nil)))
-
- )
-
-(defn subform [{{:keys [name]} :query-params }]
+(defn subform [{{:keys [name]} :query-params}]
(html-response
- (fc/start-form {} nil
- (subform* {:name name}))))
+ (fc/start-form {} nil
+ (subform* {:name name}))))
(defn job-start-dialog [{:keys [form-errors form-params] :as request}]
(fc/start-form (or form-params {}) form-errors
- (modal-response
- (com/modal ;; TODO we need a cleaner way to have forms that wrap the whole. In this cas
- {}
- [:form {:hx-post (bidi/path-for ssr-routes/only-routes :admin-job-start)
- :class "h-full w-full"}
- [:fieldset {:class "hx-disable h-full w-full"}
- (com/modal-card {}
- [:div.m-2 "New job"]
- [:div.space-y-6
+ (modal-response
+ (com/modal ;; TODO we need a cleaner way to have forms that wrap the whole. In this cas
+ {}
+ [:form {:hx-post (bidi/path-for ssr-routes/only-routes :admin-job-start)
+ :class "h-full w-full"}
+ [:fieldset {:class "hx-disable h-full w-full"}
+ (com/modal-card {}
+ [:div.m-2 "New job"]
+ [:div.space-y-6
- (fc/with-field :name
- (com/validated-field {:label "Job"
- :errors (fc/field-errors)}
- (com/select {:name (fc/field-name)
- :value (fc/field-value)
- :class "w-64"
- :options [["" ""]
- ["yodlee2" "Yodlee Import"]
- ["yodlee2-accounts" "Yodlee Account Import"]
- ["intuit" "Intuit import"]
- ["plaid" "Plaid import"]
- ["bulk-journal-import" "Bulk Journal Import"]
- ["square-import-job" "Square Import"]
- ["register-invoice-import" "Register Invoice Import "]
- ["ezcater-upsert" "Upsert recent ezcater orders"]
- ["load-historical-sales" "Load Historical Square Sales"]
- ["export-backup" "Export Backup"]]
- :hx-get (bidi/path-for ssr-routes/only-routes
- :admin-job-subform)
- :hx-target "#sub-form"
- :hx-swap "innerHTML"})))
+ (fc/with-field :name
+ (com/validated-field {:label "Job"
+ :errors (fc/field-errors)}
+ (com/select {:name (fc/field-name)
+ :value (fc/field-value)
+ :class "w-64"
+ :options [["" ""]
+ ["yodlee2" "Yodlee Import"]
+ ["yodlee2-accounts" "Yodlee Account Import"]
+ ["intuit" "Intuit import"]
+ ["plaid" "Plaid import"]
+ ["bulk-journal-import" "Bulk Journal Import"]
+ ["square-import-job" "Square Import"]
+ ["register-invoice-import" "Register Invoice Import "]
+ ["ezcater-upsert" "Upsert recent ezcater orders"]
+ ["load-historical-sales" "Load Historical Square Sales"]
+ ["export-backup" "Export Backup"]]
+ :hx-get (bidi/path-for ssr-routes/only-routes
+ :admin-job-subform)
+ :hx-target "#sub-form"
+ :hx-swap "innerHTML"})))
- [:div#sub-form (subform* {:name (fc/with-field :name (fc/field-value))}) ]]
- [:div
-
- (com/form-errors {:errors (:errors fc/*form-errors*)})
- (com/validated-save-button {:errors form-errors} "Run job")])]]))))
+ [:div#sub-form (subform* {:name (fc/with-field :name (fc/field-value))})]]
+ [:div
+
+ (com/form-errors {:errors (:errors fc/*form-errors*)})
+ (com/validated-save-button {:errors form-errors} "Run job")])]]))))
(def form-schema (mc/schema [:map
[:name [:string {:min 1}]]
[:ledger-url {:optional true} [:string {:min 1}]]
[:invoice-url {:optional true} [:string {:min 1}]]
[:client {:optional true} entity-id]
- [:days {:optional true} [:int {:min 1 :max 120}]]
- ]))
+ [:days {:optional true} [:int {:min 1 :max 120}]]]))
(def key->handler
- (apply-middleware-to-all-handlers
- (->>
- {:admin-jobs (helper/page-route grid-page)
- :admin-job-table (helper/table-route grid-page)
- :admin-job-subform (-> subform (wrap-schema-enforce :query-schema [:map [:name {:optional true} [:maybe :string]]]))
- :admin-job-start (-> job-start
- (wrap-schema-enforce :form-schema form-schema)
- (wrap-nested-form-params)
- (wrap-form-4xx-2 job-start-dialog))
- :admin-job-start-dialog job-start-dialog})
- (fn [h]
- (-> h
- (wrap-admin)
- (wrap-client-redirect-unauthenticated)))))
+ (apply-middleware-to-all-handlers
+ (->>
+ {:admin-jobs (helper/page-route grid-page)
+ :admin-job-table (helper/table-route grid-page)
+ :admin-job-subform (-> subform (wrap-schema-enforce :query-schema [:map [:name {:optional true} [:maybe :string]]]))
+ :admin-job-start (-> job-start
+ (wrap-schema-enforce :form-schema form-schema)
+ (wrap-nested-form-params)
+ (wrap-form-4xx-2 job-start-dialog))
+ :admin-job-start-dialog job-start-dialog})
+ (fn [h]
+ (-> h
+ (wrap-admin)
+ (wrap-client-redirect-unauthenticated)))))
diff --git a/src/clj/auto_ap/ssr/admin/clients.clj b/src/clj/auto_ap/ssr/admin/clients.clj
index f95a10a6..93847148 100644
--- a/src/clj/auto_ap/ssr/admin/clients.clj
+++ b/src/clj/auto_ap/ssr/admin/clients.clj
@@ -1,9 +1,9 @@
(ns auto-ap.ssr.admin.clients
(:require
[auto-ap.datomic
- :refer [add-sorter-fields apply-pagination apply-sort-3
- audit-transact conn merge-query pull-attr pull-id
- pull-many query2]]
+ :refer [add-sorter-fields apply-pagination apply-sort-3
+ audit-transact conn merge-query pull-attr pull-id
+ pull-many query2]]
[auto-ap.graphql.utils :refer [extract-client-ids]]
[auto-ap.logging :as alog]
[auto-ap.query-params :refer [wrap-copy-qp-pqp]]
@@ -11,7 +11,7 @@
[auto-ap.routes.indicators :as indicators]
[auto-ap.routes.queries :as q]
[auto-ap.routes.utils
- :refer [wrap-admin wrap-client-redirect-unauthenticated]]
+ :refer [wrap-admin wrap-client-redirect-unauthenticated]]
[auto-ap.solr :as solr]
[auto-ap.square.core3 :as square]
[auto-ap.ssr-routes :as ssr-routes]
@@ -26,11 +26,11 @@
[auto-ap.ssr.indicators :as i]
[auto-ap.ssr.svg :as svg]
[auto-ap.ssr.utils
- :refer [apply-middleware-to-all-handlers entity-id
- form-validation-error html-response many-entity
- many-entity-custom modal-response ref->enum-schema strip
- temp-id wrap-entity wrap-merge-prior-hx
- wrap-schema-enforce]]
+ :refer [apply-middleware-to-all-handlers entity-id
+ form-validation-error html-response many-entity
+ many-entity-custom modal-response ref->enum-schema strip
+ temp-id wrap-entity wrap-merge-prior-hx
+ wrap-schema-enforce]]
[auto-ap.time :as atime]
[bidi.bidi :as bidi]
[cheshire.core :as cheshire]
@@ -47,7 +47,6 @@
(:import
[java.util UUID]))
-
;; TODO make more reusable malli schemas, use unions if it would be helpful
;; TODO copy save logic from graphql version
;; TODO cash drawer shift
@@ -67,8 +66,6 @@
[:enum
"" "all" "only-mine"]]]]]))
-
-
(defn filters [request]
[:form {"hx-trigger" "change delay:500ms, keyup changed from:.hot-filter delay:1000ms"
"hx-get" (bidi/path-for ssr-routes/only-routes
@@ -178,7 +175,6 @@
:where ['[?e :client/groups ?g]]}
:args [(clojure.string/upper-case (:group query-params))]})
-
(not (str/blank? (some-> query-params :code)))
(merge-query {:query {:in ['?code]
:where ['[?e :client/code ?code]]}
@@ -293,8 +289,6 @@
(def row* (partial helper/row* grid-page))
-
-
(def bank-account-schema [:and [:map
[:db/id [:or entity-id temp-id]]
[:bank-account/name :string]
@@ -383,10 +377,10 @@
[:maybe (many-entity-custom {}
[:and
[:map
-
+
[:db/id [:or entity-id temp-id]]
[:bank-account/name :string]
-
+
[:bank-account/code :string]
[:bank-account/type [:maybe (ref->enum-schema "bank-account-type")]]
[:bank-account/numeric-code {:optional true} [:maybe :int]]
@@ -401,15 +395,15 @@
[:bank-account/intuit-bank-account {:optional true} [:maybe entity-id]]
[:bank-account/include-in-reports {:default false}
[:boolean {:decode/string {:enter #(if (= % "on") true
-
+
(boolean %))}}]]
[:bank-account/visible {:default false}
[:boolean {:decode/string {:enter #(if (= % "on") true
-
+
(boolean %))}}]]
[:bank-account/use-date-instead-of-post-date? {:default false}
[:boolean {:decode/string {:enter #(if (= % "on") true
-
+
(boolean %))}}]]
[:bank-account/start-date {:optional true} [:maybe {:decode/arbitrary (fn [m]
(if (string? m)
@@ -443,10 +437,6 @@
[:client/week-b-credits {:optional true} [:maybe :double]]
[:client/week-b-debits {:optional true} [:maybe :double]]]))
-
-
-
-
(defn email-contact-row [email-contact-cursor]
(com/data-grid-row
(-> {:x-data (hx/json {:show (boolean (not (fc/field-value (:new? email-contact-cursor))))})
@@ -526,12 +516,10 @@
(com/data-grid-cell {:class "align-top"}
(com/a-icon-button {"@click.prevent.stop" "$refs.p.remove()"} svg/x))))
-
(defn- dialog-header [step]
[:div.flex [:div.p-2 (mm/step-name step)] [:p.ml-2.rounded.bg-gray-50.p-2.dark:bg-gray-600
[:span {:x-text "clientName"}]]])
-
(defrecord InfoModal [linear-wizard]
mm/ModalWizardStep
(step-name [_]
@@ -598,7 +586,6 @@
(mm/default-step-footer linear-wizard this :validation-route ::route/navigate)
:validation-route ::route/navigate)))
-
(defn match-row [_]
(com/data-grid-row
{:x-ref "p"
@@ -644,8 +631,6 @@
(com/data-grid-cell {:class "align-top"}
(com/a-icon-button {"@click.prevent.stop" "show=false; setTimeout(() => $refs.p.remove(), 500)"} svg/x))))
-
-
(defrecord MatchesModal [linear-wizard]
mm/ModalWizardStep
(step-name [_]
@@ -699,7 +684,6 @@
(step-key [_]
:contact)
-
(edit-path [_ _]
[])
@@ -798,7 +782,6 @@
:to (mm/encode-step-key [:bank-account (fc/field-value (:db/id bank-account))])})}
svg/pencil)]])])
-
(defmulti bank-account-card (comp deref :bank-account/type))
(defmethod bank-account-card :bank-account-type/cash [bank-account]
(bank-account-card-base {:bg-color "bg-green-50"
@@ -821,7 +804,6 @@
:icon svg/check
:bank-account bank-account}))
-
(defmulti bank-account-form (comp deref :bank-account/type))
(defmethod bank-account-form :bank-account-type/cash [bank-account]
[:div
@@ -904,8 +886,6 @@
:checked (fc/field-value)}
"Visible for payment"))]])
-
-
(defn- plaid-account-select [client-id]
(fc/with-field :bank-account/plaid-account
(com/validated-field {:errors (fc/field-errors)
@@ -1048,7 +1028,6 @@
[:div#days-indicator
(i/days-ago* (some-> (fc/field-value)))]])
-
(fc/with-field :bank-account/include-in-reports
(com/checkbox {:name (fc/field-name)
:value (boolean (fc/field-value))
@@ -1224,8 +1203,6 @@
(yodlee-account-select (:db/id (:snapshot fc/*form-data*)))
(intuit-account-select (:db/id (:snapshot fc/*form-data*)))])
-
-
(defn new-bank-account-card []
[:div {:class "w-[30em]"}
(com/card {:class "w-full border-dotted bg-gray-50"}
@@ -1255,7 +1232,6 @@
(edit-path [_ _] [])
-
(step-schema [_]
(mut/select-keys (mm/form-schema linear-wizard) #{}))
@@ -1284,7 +1260,6 @@
:validation-route ::route/navigate)]
:validation-route ::route/navigate)))
-
(defn square-location-table []
[:div#square-locations
[:div.htmx-indicator
@@ -1367,7 +1342,7 @@
:hx-include "#square-token"
:hx-trigger "click"
:hx-indicator "#square-locations"
- :hx-target "#square-locations" }
+ :hx-target "#square-locations"}
"Refresh")]
(fc/with-field :client/square-locations
@@ -1447,8 +1422,6 @@
(filterv #(not= (get-in multi-form-state [:step-params :db/id]) (:db/id %)) bank-accounts)))
(mm/select-state [] nil))))
-
-
(defrecord CashFlowModal [linear-wizard]
mm/ModalWizardStep
(step-name [_]
@@ -1663,7 +1636,6 @@
#(mm/select-state % [] {})
#(assoc-in % [:snapshot :client/bank-accounts] new-bank-accounts)))))))
-
(def sales-summary-query
"[:find ?d4 (sum ?total) (sum ?tax) (sum ?tip) (sum ?service-charge) (sum ?discount) (sum ?returns)
:with ?s
@@ -1792,9 +1764,6 @@
[?cds :cash-drawer-shift/opened-cash ?opened-cash]
[(iol-ion.query/excel-date ?date) ?d4]]")
-
-
-
(defn setup-sales-queries-impl [client-id]
(let [{client-code :client/code feature-flags :client/feature-flags} (dc/pull (dc/db conn) '[:client/code :client/feature-flags] client-id)
is-new-square? ((set feature-flags) "new-square")]
@@ -1840,7 +1809,6 @@
(cheshire/generate-string (format (slurp (io/resource which)) url)))}
children))
-
(defn biweekly-sales-powerquery [request]
(setup-sales-queries-impl (:db/id (:route-params request)))
(modal-response
@@ -1872,7 +1840,6 @@
(com/modal-footer {} [:div])))))
-
(def key->handler
(apply-middleware-to-all-handlers
{::route/page (helper/page-route grid-page)
diff --git a/src/clj/auto_ap/ssr/admin/excel_invoice.clj b/src/clj/auto_ap/ssr/admin/excel_invoice.clj
index 2b0435c0..b0d7e2f3 100644
--- a/src/clj/auto_ap/ssr/admin/excel_invoice.clj
+++ b/src/clj/auto_ap/ssr/admin/excel_invoice.clj
@@ -6,7 +6,7 @@
[auto-ap.logging :as alog]
[auto-ap.routes.admin.excel-invoices :as route]
[auto-ap.routes.utils
- :refer [wrap-admin wrap-client-redirect-unauthenticated]]
+ :refer [wrap-admin wrap-client-redirect-unauthenticated]]
[auto-ap.ssr-routes :as ssr-routes]
[auto-ap.ssr.components :as com]
[auto-ap.ssr.components.inputs :as inputs]
@@ -16,8 +16,8 @@
[auto-ap.ssr.nested-form-params :refer [wrap-nested-form-params]]
[auto-ap.ssr.ui :refer [base-page]]
[auto-ap.ssr.utils
- :refer [apply-middleware-to-all-handlers html-response wrap-form-4xx-2
- wrap-schema-enforce]]
+ :refer [apply-middleware-to-all-handlers html-response wrap-form-4xx-2
+ wrap-schema-enforce]]
[auto-ap.utils :refer [by]]
[bidi.bidi :as bidi]
[clj-time.coerce :as coerce]
@@ -38,7 +38,6 @@
invoice)
-
(defn reset-id [i]
(update i :invoice-number
(fn [n] (if (re-matches #"#+" n)
@@ -85,7 +84,6 @@
(get (by (comp :db/id :vendor-schedule-payment-dom/client) :vendor-schedule-payment-dom/dom (:vendor/schedule-payment-dom vendor))
client-id))
-
(defn invoice-rows->transaction [rows user]
(->> rows
(mapcat (fn [{:keys [vendor-id total client-id date invoice-number default-location check automatically-paid-when-due account-id]}]
@@ -121,8 +119,7 @@
(let [[[bank-account]] (seq (dc/q '[:find ?ba
:in $ ?c
:where [?c :client/bank-accounts ?ba]
- [?ba :bank-account/type :bank-account-type/cash]
- ]
+ [?ba :bank-account/type :bank-account-type/cash]]
(dc/db conn)
client-id))]
[:upsert-transaction #:transaction {:amount (- (:invoice/total invoice))
@@ -130,18 +127,17 @@
:client (:invoice/client invoice)
:status "POSTED"
:bank-account bank-account
- :db/id #_ {:clj-kondo/ignore [:unresolved-var]} (digest/sha-256 transaction-id)
- :id #_ {:clj-kondo/ignore [:unresolved-var]} (digest/sha-256 transaction-id)
+ :db/id #_{:clj-kondo/ignore [:unresolved-var]} (digest/sha-256 transaction-id)
+ :id #_{:clj-kondo/ignore [:unresolved-var]} (digest/sha-256 transaction-id)
:raw-id transaction-id
:vendor (:invoice/vendor invoice)
:description-original "Cash payment"
:date (coerce/to-date date)
:approval-status :transaction-approval-status/approved
- :accounts [{:db/id (str #_ {:clj-kondo/ignore [:unresolved-var]} (digest/sha-256 transaction-id) "-account")
+ :accounts [{:db/id (str #_{:clj-kondo/ignore [:unresolved-var]} (digest/sha-256 transaction-id) "-account")
:transaction-account/account (:db/id (a/get-account-by-numeric-code-and-sets 21000 ["default"]))
:transaction-account/location "A"
- :transaction-account/amount (Math/abs (:invoice/total invoice))}]}]))
- ]
+ :transaction-account/amount (Math/abs (:invoice/total invoice))}]}]))]
[[:propose-invoice (d-invoices/code-invoice (validate-invoice (remove-nils invoice))
account-id)]
(some-> payment remove-nils)
@@ -154,17 +150,16 @@
(map #(str/split % #"\t"))
(map #(into {} (map (fn [c k] [k c]) % columns))))
vendor-name->vendor (->>
- (set (map :vendor-name tabulated))
- (dc/q '[:find ?n ?v
- :in $ [?n ...]
- :where [?v :vendor/name ?n]]
- (dc/db conn)
- )
- (into {}))
- all-clients (merge (into {}(dc/q '[:find ?n (pull ?v [:db/id :client/locations])
- :in $
- :where [?v :client/name ?n]]
- (dc/db conn)))
+ (set (map :vendor-name tabulated))
+ (dc/q '[:find ?n ?v
+ :in $ [?n ...]
+ :where [?v :vendor/name ?n]]
+ (dc/db conn))
+ (into {}))
+ all-clients (merge (into {} (dc/q '[:find ?n (pull ?v [:db/id :client/locations])
+ :in $
+ :where [?v :client/name ?n]]
+ (dc/db conn)))
(into {}
(dc/q '[:find ?n (pull ?v [:db/id :client/locations])
:in $
@@ -190,16 +185,16 @@
(let [parsed-invoice-rows (parse-invoice-rows excel-rows)
existing-rows (set (d-invoices/get-existing-set))
grouped-rows (group-by
- (fn [i]
- (cond (seq (:errors i))
- :error
+ (fn [i]
+ (cond (seq (:errors i))
+ :error
- (existing-rows [(:vendor-id i) (:client-id i) (:invoice-number i)])
- :exists
+ (existing-rows [(:vendor-id i) (:client-id i) (:invoice-number i)])
+ :exists
- :else
- :new))
- parsed-invoice-rows)
+ :else
+ :new))
+ parsed-invoice-rows)
vendors-not-found (->> parsed-invoice-rows
(filter #(and (nil? (:vendor-id %))
(not= "Cash" (:check %))))
@@ -208,9 +203,9 @@
(audit-transact (invoice-rows->transaction (:new grouped-rows) user)
user)
{:imported (count (:new grouped-rows))
- :already-imported (count (:exists grouped-rows))
- :vendors-not-found vendors-not-found
- :errors (map #(dissoc % :date) (:error grouped-rows))}))
+ :already-imported (count (:exists grouped-rows))
+ :vendors-not-found vendors-not-found
+ :errors (map #(dissoc % :date) (:error grouped-rows))}))
(def sample "6/16/17 Acme Bread NMKT-CB 3/26/56 12:00 AM $54.00 Naschmarkt X 7/31/17 8:26 AM 8/1/17 3:57 PM 31000
6/20/17 Acme Bread NMKT-CB 3/19/58 12:00 AM $54.00 Naschmarkt X 7/31/17 8:26 AM 8/1/17 3:57 PM
@@ -218,100 +213,98 @@
(defn form* [{:keys [form-params form-errors]} & children]
(fc/start-form form-params form-errors
- [:form {:hx-post (bidi/path-for ssr-routes/only-routes ::route/import) :hx-swap "outerHTML"}
- [:div {:class "flex flex-col px-4 py-3 space-y-3 w-full"}
- [:h1.text-2xl.mb-3.font-bold "Import invoices from excel"]
+ [:form {:hx-post (bidi/path-for ssr-routes/only-routes ::route/import) :hx-swap "outerHTML"}
+ [:div {:class "flex flex-col px-4 py-3 space-y-3 w-full"}
+ [:h1.text-2xl.mb-3.font-bold "Import invoices from excel"]
- (fc/with-field :tsv
- (com/validated-field {:label "Tab-separated invoices"
- :errors (fc/field-errors)}
- [:textarea {:class (hh/add-class "w-full h-96" inputs/default-input-classes) :placeholder (hiccup/raw sample)
- :name (fc/field-name)
- }
- (fc/field-value)]))
- (com/form-errors {:errors (:errors fc/*form-errors*)})
- (com/validated-save-button {:color :primary
- :class "place-self-end w-32"
- :errors (seq form-errors)}
- "Import")
- children]]))
+ (fc/with-field :tsv
+ (com/validated-field {:label "Tab-separated invoices"
+ :errors (fc/field-errors)}
+ [:textarea {:class (hh/add-class "w-full h-96" inputs/default-input-classes) :placeholder (hiccup/raw sample)
+ :name (fc/field-name)}
+ (fc/field-value)]))
+ (com/form-errors {:errors (:errors fc/*form-errors*)})
+ (com/validated-save-button {:color :primary
+ :class "place-self-end w-32"
+ :errors (seq form-errors)}
+ "Import")
+ children]]))
(defn page [{:keys [form-params form-errors] :as request}]
- (base-page
- request
- (com/page {:nav com/admin-aside-nav
- :client-selection (:client-selection request)
- :clients (:clients request)
- :client (:client request)
- :identity (:identity request)
- :request request}
- (com/breadcrumbs {} [:a {:href (bidi/path-for ssr-routes/only-routes ::route/page)}
- "Admin"])
- [:div.flex.space-x-4
- (com/content-card
- {:class "w-3/4"}
- (form* {:form-params {}
- :form-errors []}))])
- "Admin"))
+ (base-page
+ request
+ (com/page {:nav com/admin-aside-nav
+ :client-selection (:client-selection request)
+ :clients (:clients request)
+ :client (:client request)
+ :identity (:identity request)
+ :request request}
+ (com/breadcrumbs {} [:a {:href (bidi/path-for ssr-routes/only-routes ::route/page)}
+ "Admin"])
+ [:div.flex.space-x-4
+ (com/content-card
+ {:class "w-3/4"}
+ (form* {:form-params {}
+ :form-errors []}))])
+ "Admin"))
(defn form [{:keys [form-params form-errors] :as request}]
- (html-response
- (form* {:form-params (or form-params {})
- :form-errors (or form-errors [])})))
+ (html-response
+ (form* {:form-params (or form-params {})
+ :form-errors (or form-errors [])})))
(defn import [{:keys [form-params form-errors] :as request}]
- (html-response
- (let [result (bulk-upload-invoices (:tsv form-params) (:identity request))]
- (form* {:form-params form-params
- :form-errors form-errors}
- [:div.flex.space-x-4
- (com/pill {:color :primary}
- (format "%d imported" (:imported result)))
- (com/pill {:color :secondary}
- (format "%d extant" (:already-imported result)))
- (when (seq (:vendors-not-found result))
- (list
- (com/pill {:color :yellow
- "@mouseover" "show=true"
- "@mouseout" "show=false"
- "x-tooltip" "{content: ()=>$refs.tooltip.innerHTML ,
- allowHTML: true}" }
+ (html-response
+ (let [result (bulk-upload-invoices (:tsv form-params) (:identity request))]
+ (form* {:form-params form-params
+ :form-errors form-errors}
+ [:div.flex.space-x-4
+ (com/pill {:color :primary}
+ (format "%d imported" (:imported result)))
+ (com/pill {:color :secondary}
+ (format "%d extant" (:already-imported result)))
+ (when (seq (:vendors-not-found result))
+ (list
+ (com/pill {:color :yellow
+ "@mouseover" "show=true"
+ "@mouseout" "show=false"
+ "x-tooltip" "{content: ()=>$refs.tooltip.innerHTML ,
+ allowHTML: true}"}
- (format "%d vendors not found" (count (:vendors-not-found result))))
- [:template {:x-ref "tooltip"}
- [:ul
- (for [n (take 5 (:vendors-not-found result))]
- [:li n])]]))]
+ (format "%d vendors not found" (count (:vendors-not-found result))))
+ [:template {:x-ref "tooltip"}
+ [:ul
+ (for [n (take 5 (:vendors-not-found result))]
+ [:li n])]]))]
- (when (seq (:errors result))
- (com/field {:label "Errors"}
- (com/data-grid
- {:headers [(com/data-grid-header {} "Date")
- (com/data-grid-header {} "Invoice #")
- (com/data-grid-header {} "Client")
- (com/data-grid-header {} "Vendor")
- (com/data-grid-header {} "Amount")
- (com/data-grid-header {} "Errors")]}
- (for [r (:errors result)]
- (com/data-grid-row
- {}
- (com/data-grid-cell {} (:raw-date r))
- (com/data-grid-cell {} (:invoice-number r))
- (com/data-grid-cell {} (:client-name r))
- (com/data-grid-cell {} (:vendor-name r))
- (com/data-grid-cell {} (:amount r))
- (com/data-grid-cell {} (str/join ", " (map :info (:errors r)))))))))))))
+ (when (seq (:errors result))
+ (com/field {:label "Errors"}
+ (com/data-grid
+ {:headers [(com/data-grid-header {} "Date")
+ (com/data-grid-header {} "Invoice #")
+ (com/data-grid-header {} "Client")
+ (com/data-grid-header {} "Vendor")
+ (com/data-grid-header {} "Amount")
+ (com/data-grid-header {} "Errors")]}
+ (for [r (:errors result)]
+ (com/data-grid-row
+ {}
+ (com/data-grid-cell {} (:raw-date r))
+ (com/data-grid-cell {} (:invoice-number r))
+ (com/data-grid-cell {} (:client-name r))
+ (com/data-grid-cell {} (:vendor-name r))
+ (com/data-grid-cell {} (:amount r))
+ (com/data-grid-cell {} (str/join ", " (map :info (:errors r)))))))))))))
(def key->handler
- (apply-middleware-to-all-handlers
- (->>
- {::route/page page
- ::route/import (-> import
- (wrap-schema-enforce :form-schema [:map [:tsv :string]])
- (wrap-nested-form-params)
- (wrap-form-4xx-2 form))
- })
- (fn [h]
- (-> h
- (wrap-admin)
- (wrap-client-redirect-unauthenticated)))))
+ (apply-middleware-to-all-handlers
+ (->>
+ {::route/page page
+ ::route/import (-> import
+ (wrap-schema-enforce :form-schema [:map [:tsv :string]])
+ (wrap-nested-form-params)
+ (wrap-form-4xx-2 form))})
+ (fn [h]
+ (-> h
+ (wrap-admin)
+ (wrap-client-redirect-unauthenticated)))))
diff --git a/src/clj/auto_ap/ssr/admin/history.clj b/src/clj/auto_ap/ssr/admin/history.clj
index 9c36b962..c5ec1f2e 100644
--- a/src/clj/auto_ap/ssr/admin/history.clj
+++ b/src/clj/auto_ap/ssr/admin/history.clj
@@ -13,7 +13,7 @@
[bidi.bidi :as bidi]))
(defn tx-rows->changes [history]
- (->> history
+ (->> history
(group-by (fn [[a _ t]]
[a t]))
(map (fn [[[a t] changes]]
@@ -59,7 +59,6 @@
:else
(pr-str v)))
-
(defn inspect [{{:keys [entity-id]} :params :as request}]
(alog/info ::inspect
:request request)
@@ -151,7 +150,7 @@
[:div.mt-4
[:form.flex.gap-2 {"hx-target" "#history-table"
"hx-get" (bidi/path-for ssr-routes/only-routes
- :admin-history)
+ :admin-history)
"hx-select" "#history-table"
"hx-swap" "innerHTML"
"hx-push-url" "true"}
@@ -187,6 +186,5 @@
(if entity-id
(result-table {:entity-id entity-id})
[:div#history-table])
- [:div#inspector]
- ])
+ [:div#inspector]])
"History")))
diff --git a/src/clj/auto_ap/ssr/admin/import_batch.clj b/src/clj/auto_ap/ssr/admin/import_batch.clj
index 7df6a478..46ab6f51 100644
--- a/src/clj/auto_ap/ssr/admin/import_batch.clj
+++ b/src/clj/auto_ap/ssr/admin/import_batch.clj
@@ -37,11 +37,11 @@
(defn filters [request]
[:form {"hx-trigger" "change delay:500ms, keyup changed from:.hot-filter delay:1000ms"
"hx-get" (bidi/path-for ssr-routes/only-routes
- ::route/table)
+ ::route/table)
"hx-target" "#entity-table"
"hx-indicator" "#entity-table"}
- [:fieldset.space-y-6
+ [:fieldset.space-y-6
(date-range-field {:value {:start (:start-date (:query-params request))
:end (:end-date (:query-params request))}
:id "date-range"})
@@ -53,12 +53,12 @@
:options (ref->select-options "import-source" :allow-nil? true)}))
#_(com/field {:label "Code"}
- (com/text-input {:name "code"
- :id "code"
- :class "hot-filter"
- :value (:code (:query-params request))
- :placeholder "11101"
- :size :small}))]])
+ (com/text-input {:name "code"
+ :id "code"
+ :class "hot-filter"
+ :value (:code (:query-params request))
+ :placeholder "11101"
+ :size :small}))]])
(def default-read '[:db/id
[:import-batch/date :xform clj-time.coerce/from-date]
@@ -72,9 +72,9 @@
(defn fetch-ids [db request]
(let [query-params (:query-params request)
query (cond-> {:query {:find []
- :in '[$ ]
+ :in '[$]
:where '[]}
- :args [db ]}
+ :args [db]}
(:sort query-params) (add-sorter-fields {"source" ['[?e :import-batch/source ?s]
'[?s :db/ident ?s2]
'[(name ?s2) ?sort-source]]
@@ -84,8 +84,8 @@
"user" ['[?e :import-batch/user-name ?sort-user]]
"date" ['[?e :import-batch/date ?sort-date]]
"type" ['[?e :account/type ?t]
- '[?t :db/ident ?ti]
- '[(name ?ti) ?sort-type]]}
+ '[?t :db/ident ?ti]
+ '[(name ?ti) ?sort-type]]}
query-params)
(or (:start-date query-params)
@@ -96,7 +96,7 @@
(merge-query {:query '{:in [?start-date]
:where [[(>= ?d ?start-date)]]}
:args [(-> query-params :start-date c/to-date)]})
-
+
(:end-date query-params)
(merge-query {:query '{:in [?end-date]
:where [[(< ?d ?end-date)]]}
@@ -184,17 +184,17 @@
(def row* (partial helper/row* grid-page))
(def table* (partial helper/table* grid-page))
(def key->handler
- (apply-middleware-to-all-handlers
- (->>
- {::route/page (helper/page-route grid-page)
- ::route/table (helper/table-route grid-page)})
- (fn [h]
- (-> h
-
- (wrap-copy-qp-pqp)
- (wrap-apply-sort grid-page)
- (wrap-merge-prior-hx)
- (wrap-schema-enforce :query-schema query-schema)
- (wrap-schema-enforce :hx-schema query-schema)
- (wrap-admin)
- (wrap-client-redirect-unauthenticated)))))
+ (apply-middleware-to-all-handlers
+ (->>
+ {::route/page (helper/page-route grid-page)
+ ::route/table (helper/table-route grid-page)})
+ (fn [h]
+ (-> h
+
+ (wrap-copy-qp-pqp)
+ (wrap-apply-sort grid-page)
+ (wrap-merge-prior-hx)
+ (wrap-schema-enforce :query-schema query-schema)
+ (wrap-schema-enforce :hx-schema query-schema)
+ (wrap-admin)
+ (wrap-client-redirect-unauthenticated)))))
diff --git a/src/clj/auto_ap/ssr/admin/transaction_rules.clj b/src/clj/auto_ap/ssr/admin/transaction_rules.clj
index 5aae28cc..d163b9d4 100644
--- a/src/clj/auto_ap/ssr/admin/transaction_rules.clj
+++ b/src/clj/auto_ap/ssr/admin/transaction_rules.clj
@@ -1,16 +1,16 @@
(ns auto-ap.ssr.admin.transaction-rules
(:require
[auto-ap.datomic
- :refer [add-sorter-fields apply-pagination apply-sort-3
- audit-transact conn merge-query pull-attr pull-many
- query2 remove-nils]]
+ :refer [add-sorter-fields apply-pagination apply-sort-3
+ audit-transact conn merge-query pull-attr pull-many
+ query2 remove-nils]]
[auto-ap.datomic.accounts :as d-accounts]
[auto-ap.datomic.transactions :as d-transactions]
[auto-ap.graphql.utils :refer [extract-client-ids]]
[auto-ap.query-params :as query-params :refer [wrap-copy-qp-pqp]]
[auto-ap.routes.admin.transaction-rules :as route]
[auto-ap.routes.utils
- :refer [wrap-admin wrap-client-redirect-unauthenticated]]
+ :refer [wrap-admin wrap-client-redirect-unauthenticated]]
[auto-ap.rule-matching :as rm]
[auto-ap.solr :as solr]
[auto-ap.ssr-routes :as ssr-routes]
@@ -23,12 +23,12 @@
[auto-ap.ssr.hx :as hx]
[auto-ap.ssr.svg :as svg]
[auto-ap.ssr.utils
- :refer [apply-middleware-to-all-handlers
- default-grid-fields-schema entity-id
- field-validation-error form-validation-error
- html-response many-entity modal-response money percentage
- ref->enum-schema ref->radio-options regex temp-id
- wrap-entity wrap-merge-prior-hx wrap-schema-enforce]]
+ :refer [apply-middleware-to-all-handlers
+ default-grid-fields-schema entity-id
+ field-validation-error form-validation-error
+ html-response many-entity modal-response money percentage
+ ref->enum-schema ref->radio-options regex temp-id
+ wrap-entity wrap-merge-prior-hx wrap-schema-enforce]]
[auto-ap.time :as atime]
[auto-ap.utils :refer [dollars=]]
[bidi.bidi :as bidi]
@@ -40,10 +40,10 @@
[malli.util :as mut]))
(def query-schema (mc/schema
- [:maybe
- (into [:map {}
- [:vendor {:optional true :default nil} [:maybe [:entity-map {:pull [:db/id :vendor/name]}]]] ]
- default-grid-fields-schema)]))
+ [:maybe
+ (into [:map {}
+ [:vendor {:optional true :default nil} [:maybe [:entity-map {:pull [:db/id :vendor/name]}]]]]
+ default-grid-fields-schema)]))
(defn filters [request]
[:form {"hx-trigger" "change delay:500ms, keyup changed from:.hot-filter delay:1000ms"
"hx-get" (bidi/path-for ssr-routes/only-routes
@@ -154,7 +154,7 @@
(not (str/blank? (:client-group query-params)))
(merge-query {:query {:in ['?client-group]
- :where ['[?e :transaction-rule/client-group ?client-group] ]}
+ :where ['[?e :transaction-rule/client-group ?client-group]]}
:args [(clojure.string/upper-case (:client-group query-params))]})
true
@@ -288,10 +288,6 @@
[:transaction-rule/bank-account]
:form-params form-params)))
-
-
-
-
(def transaction-read '[{:transaction/client [:client/name]
:transaction/bank-account [:bank-account/name]}
:transaction/description-original
@@ -369,8 +365,6 @@
'[(>= ?dom ?dom-gte)]]}
:args [dom-gte]})
-
-
true
(merge-query {:query {:where ['[?e :transaction/id]]}}))
results (->>
@@ -436,7 +430,7 @@
:content-fn (fn [value]
(let [a (dc/pull (dc/db conn) d-accounts/default-read value)]
(when value
- (str
+ (str
(:account/numeric-code a)
" - "
(:account/name (d-accounts/clientize a
@@ -505,7 +499,6 @@
(com/data-grid-cell {:class "align-top"}
(com/a-icon-button {"@click.prevent.stop" "show=false; setTimeout(() => $refs.p.remove(), 500)"} svg/x))))
-
(defn all-ids-not-locked [all-ids]
(->> all-ids
(dc/q '[:find ?t
@@ -621,18 +614,18 @@
{}
(com/modal-header {} [:div.p-2.flex.space-x-4 [:div "Transaction Rule"] [:div ">"] [:div "Results"]])
(com/modal-body {} [:form#my-form
- {:hx-post (bidi/path-for ssr-routes/only-routes ::route/execute
- :db/id (:db/id entity))
- :hx-indicator "#code"}
- [:div
- {:hx-get (bidi/path-for ssr-routes/only-routes ::route/check-badges)
- :hx-trigger "change"
- :hx-target "#transaction-test-results .gutter"
- :hx-include "this"}
- (transaction-rule-test-table* {:entity entity
- :clients clients
- :checkboxes? true
- :only-uncoded? true})]])
+ {:hx-post (bidi/path-for ssr-routes/only-routes ::route/execute
+ :db/id (:db/id entity))
+ :hx-indicator "#code"}
+ [:div
+ {:hx-get (bidi/path-for ssr-routes/only-routes ::route/check-badges)
+ :hx-trigger "change"
+ :hx-target "#transaction-test-results .gutter"
+ :hx-include "this"}
+ (transaction-rule-test-table* {:entity entity
+ :clients clients
+ :checkboxes? true
+ :only-uncoded? true})]])
(com/modal-footer {} [:div.flex.justify-end (com/validated-save-button {:form "my-form" :id "code"} "Code transactions")])))
:headers (-> {}
(assoc "hx-trigger-after-settle" "modalnext")
@@ -656,7 +649,7 @@
(edit-path [_ _] [])
(step-schema [_]
- (mm/form-schema linear-wizard))
+ (mm/form-schema linear-wizard))
(render-step [this request]
(mm/default-render-step
@@ -825,11 +818,11 @@
(com/validated-field {:label "Approval status"
:errors (fc/field-errors)}
(com/radio-card {:options (ref->radio-options "transaction-approval-status")
- :value (fc/field-value)
- :name (fc/field-name)
- :size :small
- :orientation :horizontal})))]]])
- :footer
+ :value (fc/field-value)
+ :name (fc/field-name)
+ :size :small
+ :orientation :horizontal})))]]])
+ :footer
(mm/default-step-footer linear-wizard this :validation-route ::route/navigate)
:validation-route ::route/navigate)))
@@ -893,25 +886,25 @@
nil)))
(form-schema [_] form-schema)
(submit [_ {:keys [multi-form-state request-method identity] :as request}]
-
- (let [transaction-rule (:snapshot multi-form-state)
- _ (validate-transaction-rule transaction-rule)
- entity (cond-> transaction-rule
- (:transaction-rule/client-group transaction-rule) (update :transaction-rule/client-group str/upper-case)
- (= :post request-method) (assoc :db/id "new")
- true (assoc :transaction-rule/note (entity->note transaction-rule)))
- {:keys [tempids]} (audit-transact [[:upsert-entity entity]]
- (:identity request))
- updated-rule (dc/pull (dc/db conn)
- default-read
- (or (get tempids (:db/id entity)) (:db/id entity)))]
- (html-response
- (row* identity updated-rule {:flash? true})
- :headers (cond-> {"hx-trigger" "modalclose"}
- (= :post request-method) (assoc "hx-retarget" "#entity-table tbody"
- "hx-reswap" "afterbegin")
- (= :put request-method) (assoc "hx-retarget" (format "#entity-table tr[data-id=\"%d\"]" (:db/id updated-rule))
- "hx-reswap" "outerHTML"))))))
+
+ (let [transaction-rule (:snapshot multi-form-state)
+ _ (validate-transaction-rule transaction-rule)
+ entity (cond-> transaction-rule
+ (:transaction-rule/client-group transaction-rule) (update :transaction-rule/client-group str/upper-case)
+ (= :post request-method) (assoc :db/id "new")
+ true (assoc :transaction-rule/note (entity->note transaction-rule)))
+ {:keys [tempids]} (audit-transact [[:upsert-entity entity]]
+ (:identity request))
+ updated-rule (dc/pull (dc/db conn)
+ default-read
+ (or (get tempids (:db/id entity)) (:db/id entity)))]
+ (html-response
+ (row* identity updated-rule {:flash? true})
+ :headers (cond-> {"hx-trigger" "modalclose"}
+ (= :post request-method) (assoc "hx-retarget" "#entity-table tbody"
+ "hx-reswap" "afterbegin")
+ (= :put request-method) (assoc "hx-retarget" (format "#entity-table tr[data-id=\"%d\"]" (:db/id updated-rule))
+ "hx-reswap" "outerHTML"))))))
(def rule-wizard (->TransactionRuleWizard nil nil nil))
(def key->handler
@@ -1003,10 +996,10 @@
{}))))})
(fn [h]
(-> h
-(wrap-copy-qp-pqp)
- (wrap-apply-sort grid-page)
- (wrap-merge-prior-hx)
- (wrap-schema-enforce :query-schema query-schema)
- (wrap-schema-enforce :hx-schema query-schema)
+ (wrap-copy-qp-pqp)
+ (wrap-apply-sort grid-page)
+ (wrap-merge-prior-hx)
+ (wrap-schema-enforce :query-schema query-schema)
+ (wrap-schema-enforce :hx-schema query-schema)
(wrap-admin)
(wrap-client-redirect-unauthenticated)))))
diff --git a/src/clj/auto_ap/ssr/admin/vendors.clj b/src/clj/auto_ap/ssr/admin/vendors.clj
index 36a6404d..36c0b2ea 100644
--- a/src/clj/auto_ap/ssr/admin/vendors.clj
+++ b/src/clj/auto_ap/ssr/admin/vendors.clj
@@ -2,15 +2,15 @@
(:require
[auto-ap.cursor :as cursor]
[auto-ap.datomic
- :refer [add-sorter-fields apply-pagination apply-sort-3
- audit-transact audit-transact-batch audit-transact-batch
- conn merge-query pull-attr pull-many query2]]
+ :refer [add-sorter-fields apply-pagination apply-sort-3
+ audit-transact audit-transact-batch audit-transact-batch
+ conn merge-query pull-attr pull-many query2]]
[auto-ap.datomic.accounts :as d-accounts]
[auto-ap.logging :as alog]
[auto-ap.query-params :refer [wrap-copy-qp-pqp]]
[auto-ap.routes.admin.vendors :as route]
[auto-ap.routes.utils
- :refer [wrap-admin wrap-client-redirect-unauthenticated]]
+ :refer [wrap-admin wrap-client-redirect-unauthenticated]]
[auto-ap.solr :as solr]
[auto-ap.ssr-routes :as ssr-routes]
[auto-ap.ssr.common-handlers :refer [add-new-entity-handler
@@ -23,12 +23,12 @@
[auto-ap.ssr.hx :as hx]
[auto-ap.ssr.svg :as svg]
[auto-ap.ssr.utils
- :refer [apply-middleware-to-all-handlers
- default-grid-fields-schema entity-id
- form-validation-error html-response many-entity
- modal-response ref->enum-schema ref->select-options strip
- temp-id wrap-entity wrap-form-4xx-2 wrap-merge-prior-hx
- wrap-schema-enforce]]
+ :refer [apply-middleware-to-all-handlers
+ default-grid-fields-schema entity-id
+ form-validation-error html-response many-entity
+ modal-response ref->enum-schema ref->select-options strip
+ temp-id wrap-entity wrap-form-4xx-2 wrap-merge-prior-hx
+ wrap-schema-enforce]]
[bidi.bidi :as bidi]
[clojure.string :as str]
[datomic.api :as dc]
@@ -41,7 +41,7 @@
(into [:map {}
[:name {:optional true :default nil} [:maybe [:string {:string/decode strip}]]]
#_[:role {:optional true} [:maybe (ref->enum-schema "user-role")]]
- #_[:client {:optional true :default nil} [:maybe [:entity-map {:pull [:db/id :client/name]}]]] ]
+ #_[:client {:optional true :default nil} [:maybe [:entity-map {:pull [:db/id :client/name]}]]]]
default-grid-fields-schema)]))
(defn filters [request]
[:form {"hx-trigger" "change delay:500ms, keyup changed from:.hot-filter delay:1000ms"
@@ -60,16 +60,16 @@
:size :small}))
(com/field {:label "Type"}
(com/radio-card {:size :small
- :name "type"
- :value (:type (:query-params request))
- :options [{:value ""
- :content "All"}
- {:value "only-hidden"
- :content "Only hidden"}
- {:value "only-global"
- :content "Only global"}
- #_{:value "potential-duplicates"
- :content "Potential duplicates"}]}))]])
+ :name "type"
+ :value (:type (:query-params request))
+ :options [{:value ""
+ :content "All"}
+ {:value "only-hidden"
+ :content "Only hidden"}
+ {:value "only-global"
+ :content "Only global"}
+ #_{:value "potential-duplicates"
+ :content "Potential duplicates"}]}))]])
(def default-read '[:db/id
:vendor/name
@@ -203,8 +203,6 @@
(def row* (partial helper/row* grid-page))
(def table* (partial helper/table* grid-page))
-
-
(defn merge-submit [{:keys [form-params request-method identity] :as request}]
(if (= (:source-vendor form-params)
(:target-vendor form-params))
@@ -245,7 +243,6 @@
(= i (dec (count steps))) (assoc :last? true))
n)))))
-
;; TODO add plaid merchant
;; TODO each client only used once
@@ -285,7 +282,6 @@
(com/data-grid-cell {:class "align-top"}
(com/a-icon-button {"@click.prevent.stop" "show=false; setTimeout(() => $refs.p.remove(), 500)"} svg/x)))))
-
(defn automatically-paid-when-due-row [terms-override-cursor]
(com/data-grid-row
(-> {:x-data (hx/json {:show (boolean (not (fc/field-value (:new? terms-override-cursor))))})
@@ -303,15 +299,12 @@
:value (fc/field-value)
:value-fn :db/id
-
:content-fn #(pull-attr (dc/db conn) :client/name (:db/id %))
:size :small})))
-
(com/data-grid-cell {:class "align-top"}
(com/a-icon-button {"@click.prevent.stop" "show=false; setTimeout(() => $refs.p.remove(), 500)"} svg/x)))))
-
(defn- account-typeahead*
[{:keys [name value client-id x-model]}]
[:div.flex.flex-col
@@ -370,12 +363,6 @@
(com/data-grid-cell {:class "align-top"}
(com/a-icon-button {"@click.prevent.stop" "show=false; setTimeout(() => $refs.p.remove(), 500)"} svg/x))))))
-
-
-
-
-
-
(defn dialog* [{:keys [entity form-params form-errors] :as params}]
(alog/peek ::dialog-entity form-params)
(fc/start-form form-params form-errors
@@ -868,7 +855,6 @@
(def vendor-wizard (->VendorWizard :info))
-
(def key->handler
(apply-middleware-to-all-handlers
(->>
@@ -921,11 +907,11 @@
(fn [cursor _] (account-override-row cursor)))})
(fn [h]
(-> h
-(wrap-copy-qp-pqp)
- (wrap-apply-sort grid-page)
- (wrap-merge-prior-hx)
- (wrap-schema-enforce :query-schema query-schema)
- (wrap-schema-enforce :hx-schema query-schema)
+ (wrap-copy-qp-pqp)
+ (wrap-apply-sort grid-page)
+ (wrap-merge-prior-hx)
+ (wrap-schema-enforce :query-schema query-schema)
+ (wrap-schema-enforce :hx-schema query-schema)
(wrap-admin)
(wrap-client-redirect-unauthenticated)))))
diff --git a/src/clj/auto_ap/ssr/auth.clj b/src/clj/auto_ap/ssr/auth.clj
index aea7c985..44f9f653 100644
--- a/src/clj/auto_ap/ssr/auth.clj
+++ b/src/clj/auto_ap/ssr/auth.clj
@@ -14,7 +14,6 @@
:headers {"Location" "/login"}
:session {}})
-
(defn impersonate [request]
{:status 200
:session {:identity (dissoc (jwt/unsign (get-in request [:query-params "jwt"])
@@ -39,23 +38,22 @@
next (assoc "state" (hu/url-encode next))))))))
(defn- page-contents [request]
- [:div#app { "@notification.document" "notificationDetails=event.detail.value; showNotification=true"
+ [:div#app {"@notification.document" "notificationDetails=event.detail.value; showNotification=true"
:x-data (hx/json {:showError false
:errorDetails ""
:showNotification false
:notificationDetails ""})
- "@htmx:response-error.camel" "errorDetails = $event.detail.xhr.response; showError=true;"
- }
- [:div#app-contents.flex.overflow-hidden
- [:div#main-content {:class "relative w-full h-full overflow-y-auto px-4 bg-gray-100 dark:bg-gray-900 min-h-content " }
+ "@htmx:response-error.camel" "errorDetails = $event.detail.xhr.response; showError=true;"}
+ [:div#app-contents.flex.overflow-hidden
+ [:div#main-content {:class "relative w-full h-full overflow-y-auto px-4 bg-gray-100 dark:bg-gray-900 min-h-content "}
[:div#notification-holder
- [:div.fixed.top-0.right-0.left-0.z-30.mx-auto.max-w-screen-lg.w-screen-lg.my-0.pt-8.rounded-lg {:x-show "showNotification" }
+ [:div.fixed.top-0.right-0.left-0.z-30.mx-auto.max-w-screen-lg.w-screen-lg.my-0.pt-8.rounded-lg {:x-show "showNotification"}
[:div.relative
[:button.absolute.right-2.top-2.w-6.h-6.z-50.text-blue-400
- { "@click" "showNotification=false"}
+ {"@click" "showNotification=false"}
svg/filled-x]]
-
+
[:div.m-4.overflow-auto.z-30.flex.center-items.justify-center.text-blue-800.bg-blue-50.dark:bg-gray-800.dark:text-blue-400.border-blue-300.rounded-lg.border.max-h-96
{:x-show "showNotification"
"x-transition:enter" "transition duration-300 transform ease-in-out"
@@ -64,16 +62,16 @@
"x-transition:leave" "transition duration-300 transform ease-in-out"
"x-transition:leave-start" "opacity-100 translate-y-0"
"x-transition:leave-end" "opacity-0 translate-y-full"}
-
+
[:div {:class "p-4 text-lg w-full" :role "alert"}
[:div.text-sm
[:pre#notification-details.text-xs {:x-html "notificationDetails"}]]]]]]
- [:div {:x-show "showError"
+ [:div {:x-show "showError"
:x-init ""}
[:div.fixed.top-0.right-0.left-0.z-30.mx-auto.max-w-screen-lg.w-screen-lg.my-0.pt-8.rounded-lg
[:div.relative
[:button.absolute.right-2.top-2.w-6.h-6.z-50.text-red-600
- { "@click" "showError=false"}
+ {"@click" "showError=false"}
svg/filled-x]]
[:div.m-4.overflow-auto.z-30.flex.center-items.justify-center.text-red-800.bg-red-50.dark:bg-gray-800.dark:text-red-400.border-red-300.rounded-lg.border.max-h-96
@@ -81,7 +79,7 @@
"x-transition:enter" "transition duration-300"
"x-transition:enter-start" "opacity-0"
"x-transition:enter-end" "opacity-100"}
-
+
[:div {:class "p-4 mb-4 text-lg w-full" :role "alert"}
[:div.inline-block.w-8.h-8.mr-2 svg/alert]
[:span.font-medium "Oh, drat! An unexpected error has occurred."]
@@ -94,14 +92,13 @@
[:div.p-4
[:img {:src "/img/logo-big.png"}]
-[:div
- [:a.button.is-large.is-primary {:href (login-url (get (:query-params request) "redirect-to"))} "Login with Google"]]
- "HELLO"])
- ]]] ])
+ [:div
+ [:a.button.is-large.is-primary {:href (login-url (get (:query-params request) "redirect-to"))} "Login with Google"]]
+ "HELLO"])]]]])
(defn login [request]
(base-page
request
(page-contents request)
-
+
"Dashboard"))
\ No newline at end of file
diff --git a/src/clj/auto_ap/ssr/common_handlers.clj b/src/clj/auto_ap/ssr/common_handlers.clj
index 2c1774e5..82b0b759 100644
--- a/src/clj/auto_ap/ssr/common_handlers.clj
+++ b/src/clj/auto_ap/ssr/common_handlers.clj
@@ -2,18 +2,17 @@
(:require [auto-ap.ssr.form-cursor :as fc]
[auto-ap.ssr.utils :refer [html-response wrap-schema-enforce]]))
-
(defn add-new-entity-handler
([path render-fn] (add-new-entity-handler path
render-fn
- (fn default-data [base _]
+ (fn default-data [base _]
base)))
([path render-fn build-data]
(-> (fn new-entity [{{:keys [index]} :query-params :as request}]
(html-response
(fc/start-form-with-prefix (conj path (or index 0))
(build-data {:db/id (str (java.util.UUID/randomUUID))
- :new? true} request)
+ :new? true} request)
[]
(render-fn fc/*current* request))))
(wrap-schema-enforce :query-schema [:map
diff --git a/src/clj/auto_ap/ssr/company.clj b/src/clj/auto_ap/ssr/company.clj
index 232cf7fa..c4b1f0b9 100644
--- a/src/clj/auto_ap/ssr/company.clj
+++ b/src/clj/auto_ap/ssr/company.clj
@@ -33,18 +33,17 @@
(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 "
- }
+ [: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}"}
+ "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"})
@@ -58,7 +57,6 @@
: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()"
@@ -67,7 +65,6 @@
:height 261
:x-show "existing ? editing: true"}]]
-
[:div.flex.gap-2.justify-end
(com/button {:color :primary
:x-show "!editing"
@@ -83,7 +80,7 @@
:x-show "editing"}
"Accept")]]
- [:div
+ [:div
[:div.flex.justify-center " - or -"]
[:form {:hx-post (bidi/path-for ssr-routes/only-routes
:company-upload-signature)
@@ -92,18 +89,17 @@
#_#_: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,
+ :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,
@@ -111,23 +107,20 @@
'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"})]]]]
-
- [: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."]]]] ]]])))
+ [: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,"]
@@ -149,66 +142,66 @@
(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))}))))
+ (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)
+ {: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 "bg-red-50 text-red-700 border-red-300"}
+ {: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"})))
+ [: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
@@ -276,7 +269,6 @@
(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))
diff --git a/src/clj/auto_ap/ssr/company/company_1099.clj b/src/clj/auto_ap/ssr/company/company_1099.clj
index 7a6b65d9..257c5534 100644
--- a/src/clj/auto_ap/ssr/company/company_1099.clj
+++ b/src/clj/auto_ap/ssr/company/company_1099.clj
@@ -24,32 +24,32 @@
(def query-schema (mc/schema
[:maybe
- (into [:map {} ]
+ (into [:map {}]
default-grid-fields-schema)]))
(def vendor-read '[:db/id
- :vendor/name
- {:vendor/legal-entity-1099-type [:db/ident]}
- {:vendor/legal-entity-tin-type [:db/ident]}
- {:vendor/address [:address/street1
- :address/city
- :address/state
- :address/zip]}
+ :vendor/name
+ {:vendor/legal-entity-1099-type [:db/ident]}
+ {:vendor/legal-entity-tin-type [:db/ident]}
+ {:vendor/address [:address/street1
+ :address/city
+ :address/state
+ :address/zip]}
{:vendor/default-account [:account/name]}
- :vendor/legal-entity-tin
- :vendor/legal-entity-name
- :vendor/legal-entity-first-name
- :vendor/legal-entity-middle-name
- :vendor/legal-entity-last-name])
+ :vendor/legal-entity-tin
+ :vendor/legal-entity-name
+ :vendor/legal-entity-first-name
+ :vendor/legal-entity-middle-name
+ :vendor/legal-entity-last-name])
(defn sum-for-client-vendor [client-id vendor-id]
(ffirst (dc/q '[:find
(sum ?a)
- :with ?d
+ :with ?d
:in $ ?c ?v
:where
[?p :payment/client ?c]
- [?p :payment/date ?d ]
+ [?p :payment/date ?d]
[(>= ?d #inst "2025-01-01T08:00")]
[(< ?d #inst "2026-01-01T08:00")]
[?p :payment/type :payment-type/check]
@@ -64,11 +64,11 @@
(pull ?c [:client/code :db/id])
(pull ?v vendor-read)
(sum ?a)
- :with ?d
+ :with ?d
:in $ [?c ...] vendor-read
:where
[?p :payment/client ?c]
- [?p :payment/date ?d ]
+ [?p :payment/date ?d]
[(>= ?d #inst "2025-01-01T08:00")]
[(< ?d #inst "2026-01-01T08:00")]
[?p :payment/type :payment-type/check]
@@ -78,105 +78,101 @@
trimmed-clients
vendor-read)
all (->> results
- (filter (fn [[_ _ a]]
- (>= (or a 0.0) 600.0)))
- (sort-by (fn [[client _ amount]]
- [(:client/code client ) amount]))
- (into []))
+ (filter (fn [[_ _ a]]
+ (>= (or a 0.0) 600.0)))
+ (sort-by (fn [[client _ amount]]
+ [(:client/code client) amount]))
+ (into []))
paginated (apply-pagination-raw {:start (:start query-params)
:per-page (:per-page query-params)} all)]
[(:entries paginated) (:count paginated)]))
(def grid-page
- (helper/build
- {:id "entity-table"
- :nav com/company-aside-nav
- :id-fn (comp :db/id second)
- :fetch-page fetch-page
- :breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes
- :company)}
- "My Company"]
+ (helper/build
+ {:id "entity-table"
+ :nav com/company-aside-nav
+ :id-fn (comp :db/id second)
+ :fetch-page fetch-page
+ :breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes
+ :company)}
+ "My Company"]
- [:a {:href (bidi/path-for ssr-routes/only-routes
- :company-1099)}
- "1099 Vendor Info"]]
- :title "1099 Vendors"
- :entity-name "Vendors"
- :query-schema query-schema
- :route :company-1099-vendor-table
- :row-buttons (fn [request e]
- [(com/icon-button {:hx-get (url (bidi/path-for ssr-routes/only-routes
- :company-1099-vendor-dialog
- :vendor-id (:db/id (second e)))
- {:client-id (:db/id (first e))})}
- svg/pencil)])
- :headers [{:key "Client"
- :name "Client"
- :sort-key "client"
- :render (comp :client/code first)}
- {:key "vendor-name"
- :name "Vendor Name"
- :sort-key "vendor"
- :render (fn [[_ vendor]]
- [:div.flex.whitespace-nowrap.items-center.gap-4
- [:div [:div (:vendor/name vendor)]
- [:div.text-sm.text-gray-400
- (or (-> vendor :vendor/legal-entity-name not-empty)
- (str (-> vendor :vendor/legal-entity-first-name) " "
- (-> vendor :vendor/legal-entity-middle-name) " "
- (-> vendor :vendor/legal-entity-last-name)))]]
- (when-let [t99-type (some-> vendor :vendor/legal-entity-1099-type :db/ident name)]
- (com/pill
- {:class "text-xs font-medium"
- :color :primary}
- (str/capitalize t99-type))
- )])}
- {:key "tin"
- :name "TIN"
- :sort-key "tin"
- :show-starting "md"
- :render (fn [[_ vendor]]
- [:div.flex.gap-4
- (when-let [tin (-> vendor :vendor/legal-entity-tin)]
- [:span {:class "text-xs font-medium py-0.5 "}
- tin])
- (when-let [tin-type (some-> vendor :vendor/legal-entity-tin-type :db/ident name)]
- (com/pill {:class "text-xs font-medium"
- :color :yellow}
- (name tin-type)))]
- )}
- {:key "expense-account"
- :name "Expense Account"
- :show-starting "md"
- :render (fn [[_ vendor]]
- [:div.flex.gap-4
- (when-let [tin (-> vendor :vendor/default-account :account/name)]
- [:span {:class "text-xs font-medium py-0.5 "}
- tin]) ])}
- {:key "address"
- :name "Address"
- :sort-key "address"
- :show-starting "lg"
- :render (fn [[_ vendor]]
- (if (-> vendor :vendor/address :address/street1)
+ [:a {:href (bidi/path-for ssr-routes/only-routes
+ :company-1099)}
+ "1099 Vendor Info"]]
+ :title "1099 Vendors"
+ :entity-name "Vendors"
+ :query-schema query-schema
+ :route :company-1099-vendor-table
+ :row-buttons (fn [request e]
+ [(com/icon-button {:hx-get (url (bidi/path-for ssr-routes/only-routes
+ :company-1099-vendor-dialog
+ :vendor-id (:db/id (second e)))
+ {:client-id (:db/id (first e))})}
+ svg/pencil)])
+ :headers [{:key "Client"
+ :name "Client"
+ :sort-key "client"
+ :render (comp :client/code first)}
+ {:key "vendor-name"
+ :name "Vendor Name"
+ :sort-key "vendor"
+ :render (fn [[_ vendor]]
+ [:div.flex.whitespace-nowrap.items-center.gap-4
+ [:div [:div (:vendor/name vendor)]
+ [:div.text-sm.text-gray-400
+ (or (-> vendor :vendor/legal-entity-name not-empty)
+ (str (-> vendor :vendor/legal-entity-first-name) " "
+ (-> vendor :vendor/legal-entity-middle-name) " "
+ (-> vendor :vendor/legal-entity-last-name)))]]
+ (when-let [t99-type (some-> vendor :vendor/legal-entity-1099-type :db/ident name)]
+ (com/pill
+ {:class "text-xs font-medium"
+ :color :primary}
+ (str/capitalize t99-type)))])}
+ {:key "tin"
+ :name "TIN"
+ :sort-key "tin"
+ :show-starting "md"
+ :render (fn [[_ vendor]]
+ [:div.flex.gap-4
+ (when-let [tin (-> vendor :vendor/legal-entity-tin)]
+ [:span {:class "text-xs font-medium py-0.5 "}
+ tin])
+ (when-let [tin-type (some-> vendor :vendor/legal-entity-tin-type :db/ident name)]
+ (com/pill {:class "text-xs font-medium"
+ :color :yellow}
+ (name tin-type)))])}
+ {:key "expense-account"
+ :name "Expense Account"
+ :show-starting "md"
+ :render (fn [[_ vendor]]
+ [:div.flex.gap-4
+ (when-let [tin (-> vendor :vendor/default-account :account/name)]
+ [:span {:class "text-xs font-medium py-0.5 "}
+ tin])])}
+ {:key "address"
+ :name "Address"
+ :sort-key "address"
+ :show-starting "lg"
+ :render (fn [[_ vendor]]
+ (if (-> vendor :vendor/address :address/street1)
+ [:div
+ [:div (-> vendor :vendor/address :address/street1)] " "
[:div
- [:div (-> vendor :vendor/address :address/street1)] " "
- [:div
- (-> vendor :vendor/address :address/street2)] " "
- [:div
- (-> vendor :vendor/address :address/city) " "
- (-> vendor :vendor/address :address/state) ","
- (-> vendor :vendor/address :address/zip)]]
- [:p.text-sm.italic.text-gray-400 "No address"]))}
- {:key "paid"
- :name "Paid"
- :sort-key "paid"
- :render (fn [[_ _ paid]]
- (com/pill {:class "text-xs font-medium"
- :color :primary}
- "Paid $" (Math/round paid)))}]}))
-
-
+ (-> vendor :vendor/address :address/street2)] " "
+ [:div
+ (-> vendor :vendor/address :address/city) " "
+ (-> vendor :vendor/address :address/state) ","
+ (-> vendor :vendor/address :address/zip)]]
+ [:p.text-sm.italic.text-gray-400 "No address"]))}
+ {:key "paid"
+ :name "Paid"
+ :sort-key "paid"
+ :render (fn [[_ _ paid]]
+ (com/pill {:class "text-xs font-medium"
+ :color :primary}
+ "Paid $" (Math/round paid)))}]}))
(def table* (partial helper/table* grid-page))
(def row* (partial helper/row* grid-page))
@@ -185,7 +181,6 @@
{:keys [vendor-id]} :route-params
{:keys [client-id]} :query-params}]
-
(assert-can-see-client identity client-id)
@(dc/transact conn [[:upsert-entity (-> form-params
@@ -198,30 +193,28 @@
(:address/zip a)
(:db/id a))
a
- nil)) ))]])
- (html-response
+ nil))))]])
+ (html-response
- (row* identity [(dc/pull (dc/db conn) [:db/id :client/code] client-id)
- (dc/pull (dc/db conn) vendor-read vendor-id)
- (sum-for-client-vendor client-id vendor-id)
- ] {:flash? true})
- :headers {"hx-trigger" "modalclose"
- "hx-retarget" (format "#entity-table tr[data-id=\"%d\"]" vendor-id)}))
+ (row* identity [(dc/pull (dc/db conn) [:db/id :client/code] client-id)
+ (dc/pull (dc/db conn) vendor-read vendor-id)
+ (sum-for-client-vendor client-id vendor-id)] {:flash? true})
+ :headers {"hx-trigger" "modalclose"
+ "hx-retarget" (format "#entity-table tr[data-id=\"%d\"]" vendor-id)}))
(def default-vendor-read '[* {[:vendor/legal-entity-1099-type :xform iol-ion.query/ident] [:db/ident]
[:vendor/legal-entity-tin-type :xform iol-ion.query/ident] [:db/ident]}])
-
(def form-schema (mc/schema [:map
[:vendor/address {:default {}}
[:maybe
- [:map
+ [:map
[:db/id {:optional true} [:maybe entity-id]]
[:address/street1 {:optional true} [:maybe [:string {:decode/string strip}]]]
[:address/street2 {:optional true} [:maybe [:string {:decode/string strip}]]]
[:address/city {:optional true} [:maybe [:string {:decode/string strip}]]]
[:address/state {:optional true} [:maybe [:string {:decode/string strip}]]]
- [:address/zip {:optional true} [:maybe [:re { :error/message "invalid zip"
+ [:address/zip {:optional true} [:maybe [:re {:error/message "invalid zip"
:decode/string strip} #"^(\d{5}|)$"]]]]]]
[:vendor/legal-entity-name {:optional true} [:maybe [:string {:decode/string strip}]]]
[:vendor/legal-entity-first-name {:optional true} [:maybe [:string {:decode/string strip}]]]
@@ -237,131 +230,131 @@
(when entity
(mc/decode form-schema entity main-transformer))
{})
- form-errors
- (modal-response
- (com/modal
- {}
- [:form {:hx-post (url (bidi/path-for ssr-routes/only-routes
- :company-1099-vendor-save
- :request-method :post
- :vendor-id vendor-id)
- {:client-id client-id})
- :class "w-full h-full max-w-2xl"
- :hx-swap "outerHTML swap:300ms"}
+ form-errors
+ (modal-response
+ (com/modal
+ {}
+ [:form {:hx-post (url (bidi/path-for ssr-routes/only-routes
+ :company-1099-vendor-save
+ :request-method :post
+ :vendor-id vendor-id)
+ {:client-id client-id})
+ :class "w-full h-full max-w-2xl"
+ :hx-swap "outerHTML swap:300ms"}
- [:fieldset {:class "hx-disable w-full h-full"}
- (com/modal-card
- {}
- [:div.flex [:div.p-2 "Vendor 1099 Info"] [:p.ml-2.rounded.bg-gray-200.p-2.dark:bg-gray-600 (:vendor/name entity)]]
- [:div.grid.grid-cols-6.gap-x-4.gap-y-2
-
- (fc/with-field-default :vendor/address {}
- (println "ADDRESS" fc/*current*)
- (list [:h4.text-xl.border-b.col-span-6 "Address"]
- [:div.col-span-6
- (fc/with-field :db/id
- (com/hidden {:name (fc/field-name)
- :value (fc/field-value)}))
+ [:fieldset {:class "hx-disable w-full h-full"}
+ (com/modal-card
+ {}
+ [:div.flex [:div.p-2 "Vendor 1099 Info"] [:p.ml-2.rounded.bg-gray-200.p-2.dark:bg-gray-600 (:vendor/name entity)]]
+ [:div.grid.grid-cols-6.gap-x-4.gap-y-2
- (fc/with-field :address/street1
- (com/validated-field {:label "Street 1"
- :errors (fc/field-errors)}
- (com/text-input {:name (fc/field-name)
- :class "w-full"
- :value (fc/field-value)
- :placeholder "1700 Pennsylvania Ave"
- :autofocus true})))]
- [:div.col-span-6
- (fc/with-field :address/street2
- (com/validated-field {:label "Street 2"
- :errors (fc/field-errors)}
- (com/text-input {:name (fc/field-name)
- :class "w-full"
- :value (fc/field-value)
- :placeholder "Suite 200"})))]
- [:div.col-span-3
- (fc/with-field :address/city
- (com/validated-field {:label "City"
- :errors (fc/field-errors)}
- (com/text-input {:name (fc/field-name)
- :class "w-full"
- :value (fc/field-value)
- :placeholder "Cupertino"})))]
- [:div.col-span-1
- (fc/with-field :address/state
- (com/validated-field {:label "State"
- :errors (fc/field-errors)}
- (com/text-input {:name (fc/field-name)
- :class "w-full"
- :value (fc/field-value)
- :placeholder "CA"})))]
- [:div.col-span-2
- (fc/with-field :address/zip
- (com/validated-field {:label "Zip"
- :errors (fc/field-errors)}
- (com/text-input {:name (fc/field-name)
- :class "w-full"
- :value (fc/field-value)
- :placeholder "98102"})))]))
-
- [:h4.text-xl.border-b.col-span-6 "Legal Entity"]
- [:div.col-span-6
- (fc/with-field :vendor/legal-entity-name
- (com/validated-field {:label "Legal Entity Name"
- :errors (fc/field-errors)}
- (com/text-input {:name (fc/field-name)
- :class "w-full"
- :value (fc/field-value)
- :placeholder "Good Restaurant LLC"})))]
- [:div.col-span-6.text-center " - OR -"]
- [:div.col-span-2
- (fc/with-field :vendor/legal-entity-first-name
- (com/validated-field {:label "First Name"
- :errors (fc/field-errors)}
- (com/text-input {:name (fc/field-name)
- :value (fc/field-value)
- :placeholder "John"})))]
- [:div.col-span-2
- (fc/with-field :vendor/legal-entity-middle-name
- (com/validated-field {:label "Middle Name"
- :errors (fc/field-errors)}
- (com/text-input {:name (fc/field-name)
- :value (fc/field-value)
- :placeholder "C."})))]
- [:div.col-span-2
- (fc/with-field :vendor/legal-entity-last-name
- (com/validated-field {:label "Last Name"
- :errors (fc/field-errors)}
- (com/text-input {:name (fc/field-name)
- :value (fc/field-value)
- :placeholder "Riley"})))]
- [:div.col-span-2
- (fc/with-field :vendor/legal-entity-tin
- (com/validated-field {:label "TIN"
- :errors (fc/field-errors)}
- (com/text-input {:name (fc/field-name)
- :value (fc/field-value)
- :placeholder "John"})))]
- [:div.col-span-2
- (fc/with-field :vendor/legal-entity-tin-type
- (com/validated-field {:label "TIN Type"
- :errors (fc/field-errors)}
- (com/select {:name (fc/field-name)
- :allow-blank? true
- :value (some-> (fc/field-value) name)
- :options [["ein" "EIN"]
- ["ssn" "SSN"]]})))]
- [:div.col-span-2
- (fc/with-field :vendor/legal-entity-1099-type
- (com/validated-field {:label "1099 Type"
- :errors (fc/field-errors)}
- (com/select {:name (fc/field-name)
- :allow-blank? true
- :value (some-> (fc/field-value) name)
- :options (ref->select-options "legal-entity-1099-type")})))]]
- [:div
- (com/form-errors {:errors (:errors fc/*form-errors*)})
- (com/validated-save-button {:errors form-errors} "Save vendor")])]]))))
+ (fc/with-field-default :vendor/address {}
+ (println "ADDRESS" fc/*current*)
+ (list [:h4.text-xl.border-b.col-span-6 "Address"]
+ [:div.col-span-6
+ (fc/with-field :db/id
+ (com/hidden {:name (fc/field-name)
+ :value (fc/field-value)}))
+
+ (fc/with-field :address/street1
+ (com/validated-field {:label "Street 1"
+ :errors (fc/field-errors)}
+ (com/text-input {:name (fc/field-name)
+ :class "w-full"
+ :value (fc/field-value)
+ :placeholder "1700 Pennsylvania Ave"
+ :autofocus true})))]
+ [:div.col-span-6
+ (fc/with-field :address/street2
+ (com/validated-field {:label "Street 2"
+ :errors (fc/field-errors)}
+ (com/text-input {:name (fc/field-name)
+ :class "w-full"
+ :value (fc/field-value)
+ :placeholder "Suite 200"})))]
+ [:div.col-span-3
+ (fc/with-field :address/city
+ (com/validated-field {:label "City"
+ :errors (fc/field-errors)}
+ (com/text-input {:name (fc/field-name)
+ :class "w-full"
+ :value (fc/field-value)
+ :placeholder "Cupertino"})))]
+ [:div.col-span-1
+ (fc/with-field :address/state
+ (com/validated-field {:label "State"
+ :errors (fc/field-errors)}
+ (com/text-input {:name (fc/field-name)
+ :class "w-full"
+ :value (fc/field-value)
+ :placeholder "CA"})))]
+ [:div.col-span-2
+ (fc/with-field :address/zip
+ (com/validated-field {:label "Zip"
+ :errors (fc/field-errors)}
+ (com/text-input {:name (fc/field-name)
+ :class "w-full"
+ :value (fc/field-value)
+ :placeholder "98102"})))]))
+
+ [:h4.text-xl.border-b.col-span-6 "Legal Entity"]
+ [:div.col-span-6
+ (fc/with-field :vendor/legal-entity-name
+ (com/validated-field {:label "Legal Entity Name"
+ :errors (fc/field-errors)}
+ (com/text-input {:name (fc/field-name)
+ :class "w-full"
+ :value (fc/field-value)
+ :placeholder "Good Restaurant LLC"})))]
+ [:div.col-span-6.text-center " - OR -"]
+ [:div.col-span-2
+ (fc/with-field :vendor/legal-entity-first-name
+ (com/validated-field {:label "First Name"
+ :errors (fc/field-errors)}
+ (com/text-input {:name (fc/field-name)
+ :value (fc/field-value)
+ :placeholder "John"})))]
+ [:div.col-span-2
+ (fc/with-field :vendor/legal-entity-middle-name
+ (com/validated-field {:label "Middle Name"
+ :errors (fc/field-errors)}
+ (com/text-input {:name (fc/field-name)
+ :value (fc/field-value)
+ :placeholder "C."})))]
+ [:div.col-span-2
+ (fc/with-field :vendor/legal-entity-last-name
+ (com/validated-field {:label "Last Name"
+ :errors (fc/field-errors)}
+ (com/text-input {:name (fc/field-name)
+ :value (fc/field-value)
+ :placeholder "Riley"})))]
+ [:div.col-span-2
+ (fc/with-field :vendor/legal-entity-tin
+ (com/validated-field {:label "TIN"
+ :errors (fc/field-errors)}
+ (com/text-input {:name (fc/field-name)
+ :value (fc/field-value)
+ :placeholder "John"})))]
+ [:div.col-span-2
+ (fc/with-field :vendor/legal-entity-tin-type
+ (com/validated-field {:label "TIN Type"
+ :errors (fc/field-errors)}
+ (com/select {:name (fc/field-name)
+ :allow-blank? true
+ :value (some-> (fc/field-value) name)
+ :options [["ein" "EIN"]
+ ["ssn" "SSN"]]})))]
+ [:div.col-span-2
+ (fc/with-field :vendor/legal-entity-1099-type
+ (com/validated-field {:label "1099 Type"
+ :errors (fc/field-errors)}
+ (com/select {:name (fc/field-name)
+ :allow-blank? true
+ :value (some-> (fc/field-value) name)
+ :options (ref->select-options "legal-entity-1099-type")})))]]
+ [:div
+ (com/form-errors {:errors (:errors fc/*form-errors*)})
+ (com/validated-save-button {:errors form-errors} "Save vendor")])]]))))
(def vendor-table (helper/table-route grid-page))
(def page (helper/page-route grid-page))
diff --git a/src/clj/auto_ap/ssr/company/plaid.clj b/src/clj/auto_ap/ssr/company/plaid.clj
index 984d172f..c6557a58 100644
--- a/src/clj/auto_ap/ssr/company/plaid.clj
+++ b/src/clj/auto_ap/ssr/company/plaid.clj
@@ -25,9 +25,9 @@
[hiccup2.core :as hiccup]
[malli.core :as mc]))
(def query-schema (mc/schema
- [:maybe
- (into [:map {} ]
- default-grid-fields-schema)]))
+ [:maybe
+ (into [:map {}]
+ default-grid-fields-schema)]))
(def default-read '[:db/id
:plaid-item/external-id
@@ -37,7 +37,7 @@
{:plaid-item/accounts [:db/id
{:bank-account/_plaid-account [{:bank-account/integration-status
- [{ [ :integration-status/state :xform iol-ion.query/ident] [:db/ident]}
+ [{[:integration-status/state :xform iol-ion.query/ident] [:db/ident]}
:integration-status/message
:integration-status/last-attempt
:integration-status/last-updated]}]}
@@ -66,7 +66,6 @@
true (apply-sort-3 query-params)
true (apply-pagination query-params))))
-
(defn hydrate-results [ids db _]
(let [results (pull-many-by-id db default-read ids)]
(->> ids
@@ -78,15 +77,12 @@
[(hydrate-results ids-to-retrieve db request)
matching-count]))
-
-
(defn plaid-link-script [token]
(format "window.plaid = Plaid.create(
{ token: \"%s\",
onSuccess: function (x) { htmx.trigger(\"#link-account\", \"linked\", {\"public_token\": x})}
})", token))
-
(defn link [{{client-code "client_code" public-token "public_token"} :form-params
:keys [identity]
:as request}]
@@ -99,24 +95,24 @@
(alog/info ::linking-plaid :id identity :client-code client-code)
(assert-can-see-client identity (pull-attr (dc/db conn) :db/id [:client/code client-code]))
(let [access-token (:access_token (p/exchange-public-token public-token client-code))
- account-result (p/get-accounts access-token )
+ account-result (p/get-accounts access-token)
item {:plaid-item/client [:client/code client-code]
- :plaid-item/external-id (-> account-result :item :item_id )
+ :plaid-item/external-id (-> account-result :item :item_id)
:plaid-item/access-token access-token
:plaid-item/status (or (some-> account-result :item :error)
- "SUCCESS")
+ "SUCCESS")
:plaid-item/last-updated (coerce/to-date (time/now))
:db/id "plaid-item"}]
@(dc/transact conn (->> (:accounts account-result)
- (map (fn [a]
- (let [balance (some-> a :balances :current (* 0.01))]
- (cond-> {:plaid-account/external-id (:account_id a)
- :plaid-account/number (:mask a)
- :plaid-account/name (str (:name a) " " (:mask a))
- :plaid-item/_accounts "plaid-item"}
- balance (assoc :plaid-account/balance balance)))))
- (into [item])))
+ (map (fn [a]
+ (let [balance (some-> a :balances :current (* 0.01))]
+ (cond-> {:plaid-account/external-id (:account_id a)
+ :plaid-account/number (:mask a)
+ :plaid-account/name (str (:name a) " " (:mask a))
+ :plaid-item/_accounts "plaid-item"}
+ balance (assoc :plaid-account/balance balance)))))
+ (into [item])))
(alog/info ::access-token-was :token access-token)
{:headers {"Hx-redirect" (bidi/path-for ssr-routes/only-routes
:company-plaid)}}))
@@ -141,115 +137,110 @@
(com/button-icon {} svg/refresh)
"Start relink")])))
+(def grid-page
+ (helper/build
+ {:id "plaid-table"
+ :nav com/company-aside-nav
+ :fetch-page fetch-page
+ :breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes
+ :company)}
+ "My Company"]
-(def grid-page
- (helper/build
- {:id "plaid-table"
- :nav com/company-aside-nav
- :fetch-page fetch-page
- :breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes
- :company)}
- "My Company"]
+ [:a {:href (bidi/path-for ssr-routes/only-routes
+ :company-plaid)}
+ "Plaid"]]
+ :title "Plaid Accounts"
+ :entity-name "Plaid accounts"
+ :query-schema query-schema
+ :route :company-plaid-table
+ :action-buttons (fn [request]
+ (when-let [client-code (:client/code (:client request))]
+ [[:div {:hx-post (str (bidi/path-for ssr-routes/only-routes
+ :company-plaid-link
+ :request-method :post))
+ :hx-vals (hiccup/raw (format "js:{client_code: \"%s\", public_token: event.detail.public_token}", client-code))
+ :hx-trigger "linked"}
+ [:script (hiccup/raw (plaid-link-script (p/get-link-token client-code)))]
+ (com/button {:color :primary
+ :id "link-account"
+ :onClick "window.plaid.open()"}
+ (com/button-icon {} svg/refresh)
+ (format "Link %s account" client-code))]]))
+ :row-buttons (fn [request e]
+ [[:div (com/button {:hx-put (str (bidi/path-for ssr-routes/only-routes
+ :company-plaid-relink)
+ "?plaid-item-id=" (:db/id e))
+ :color :primary
+ :hx-target "closest div"}
+ "Reauthenticate")]])
+ :headers [{:key "plaid-item"
+ :name "Plaid Item"
+ :sort-key "external-id"
+ :render :plaid-item/external-id}
+ {:key "integreat-plaid-status"
+ :name "Integreat ↔ Plaid status"
+ :render (fn [e]
- [:a {:href (bidi/path-for ssr-routes/only-routes
- :company-plaid)}
- "Plaid"]]
- :title "Plaid Accounts"
- :entity-name "Plaid accounts"
- :query-schema query-schema
- :route :company-plaid-table
- :action-buttons (fn [request]
- (when-let [client-code (:client/code (:client request))]
- [[:div {:hx-post (str (bidi/path-for ssr-routes/only-routes
- :company-plaid-link
- :request-method :post))
- :hx-vals (hiccup/raw (format "js:{client_code: \"%s\", public_token: event.detail.public_token}", client-code))
- :hx-trigger "linked"}
- [:script (hiccup/raw (plaid-link-script (p/get-link-token client-code)))]
- (com/button {:color :primary
- :id "link-account"
- :onClick "window.plaid.open()"}
- (com/button-icon {} svg/refresh)
- (format "Link %s account" client-code))]]))
- :row-buttons (fn [request e]
- [[:div (com/button {:hx-put (str (bidi/path-for ssr-routes/only-routes
- :company-plaid-relink)
- "?plaid-item-id=" (:db/id e))
- :color :primary
- :hx-target "closest div"}
- "Reauthenticate")]])
- :headers [{:key "plaid-item"
- :name "Plaid Item"
- :sort-key "external-id"
- :render :plaid-item/external-id}
- {:key "integreat-plaid-status"
- :name "Integreat ↔ Plaid status"
- :render (fn [e]
-
- (let [bad-integration (->> (:plaid-item/accounts e)
- (map (comp
- first
- :bank-account/_plaid-account))
- (filter (comp #{:integration-state/failed :integration-state/unauthorized}
- :integration-status/state
- :bank-account/integration-status))
- first
- :bank-account/integration-status)]
- [:div
-
- [:div.cursor-pointer (com/pill (cond-> {:color :primary}
- bad-integration (assoc :color :red
- :x-tooltip "{content: ()=>$refs.tooltip.innerHTML, theme: 'light', allowHTML: true}"))
+ (let [bad-integration (->> (:plaid-item/accounts e)
+ (map (comp
+ first
+ :bank-account/_plaid-account))
+ (filter (comp #{:integration-state/failed :integration-state/unauthorized}
+ :integration-status/state
+ :bank-account/integration-status))
+ first
+ :bank-account/integration-status)]
+ [:div
- [:div.inline-flex.gap-2
- (or
- (some-> bad-integration
- :integration-status/state
- name
- str/capitalize)
- "Success")
- (when bad-integration
- " (detail)")
-
-
- (when bad-integration
- [:template {:x-ref "tooltip"}
- [:div.text-red-700
- (:integration-status/message bad-integration)]])])]
- [:div.grid.grid-cols-2.gap-1.auto-cols-min.grid-flow-row.shrink
- [:div "Attempted: "] [:div (atime/unparse-local (coerce/to-date-time (:integration-status/last-attempt e)) atime/normal-date)]
- [:div "Last Updated: "] [:div (atime/unparse-local (coerce/to-date-time (:integration-status/last-updated e)) atime/normal-date)]]]))}
- {:key "plaid-bank-status"
- :name "Plaid ↔ Bank Status"
- :sort-key "plaid-bank-status"
- :render (fn [e]
- (when-let [status (:plaid-item/status e)]
- [:div [:div (com/pill {:color :primary}
- status)]
- [:div (atime/unparse-local (coerce/to-date-time (:plaid-item/last-updated e)) atime/normal-date)]]))}
-
- {:key "accounts"
- :name "Accounts"
- :show-starting "md"
- :render (fn [e]
- [:ul
- (for [a (:plaid-item/accounts e)]
- [:li [:svg.inline {:data-jdenticon-value (:db/id a) :width "24" :height "24"}] (:plaid-account/name a) " - " (:plaid-account/number a) " - updated "
- (atime/unparse-local (:plaid-account/last-synced a) atime/normal-date)])])}]}))
+ [:div.cursor-pointer (com/pill (cond-> {:color :primary}
+ bad-integration (assoc :color :red
+ :x-tooltip "{content: ()=>$refs.tooltip.innerHTML, theme: 'light', allowHTML: true}"))
+ [:div.inline-flex.gap-2
+ (or
+ (some-> bad-integration
+ :integration-status/state
+ name
+ str/capitalize)
+ "Success")
+ (when bad-integration
+ " (detail)")
+
+ (when bad-integration
+ [:template {:x-ref "tooltip"}
+ [:div.text-red-700
+ (:integration-status/message bad-integration)]])])]
+ [:div.grid.grid-cols-2.gap-1.auto-cols-min.grid-flow-row.shrink
+ [:div "Attempted: "] [:div (atime/unparse-local (coerce/to-date-time (:integration-status/last-attempt e)) atime/normal-date)]
+ [:div "Last Updated: "] [:div (atime/unparse-local (coerce/to-date-time (:integration-status/last-updated e)) atime/normal-date)]]]))}
+ {:key "plaid-bank-status"
+ :name "Plaid ↔ Bank Status"
+ :sort-key "plaid-bank-status"
+ :render (fn [e]
+ (when-let [status (:plaid-item/status e)]
+ [:div [:div (com/pill {:color :primary}
+ status)]
+ [:div (atime/unparse-local (coerce/to-date-time (:plaid-item/last-updated e)) atime/normal-date)]]))}
+
+ {:key "accounts"
+ :name "Accounts"
+ :show-starting "md"
+ :render (fn [e]
+ [:ul
+ (for [a (:plaid-item/accounts e)]
+ [:li [:svg.inline {:data-jdenticon-value (:db/id a) :width "24" :height "24"}] (:plaid-account/name a) " - " (:plaid-account/number a) " - updated "
+ (atime/unparse-local (:plaid-account/last-synced a) atime/normal-date)])])}]}))
(def page (helper/page-route grid-page))
(def table (helper/table-route grid-page))
-(def key->handler
- (apply-middleware-to-all-handlers
- {
- :company-plaid page
+(def key->handler
+ (apply-middleware-to-all-handlers
+ {:company-plaid page
:company-plaid-table table
:company-plaid-link link
- :company-plaid-relink relink
-
- }
+ :company-plaid-relink relink}
+
(fn [h]
(-> h
(wrap-copy-qp-pqp)
diff --git a/src/clj/auto_ap/ssr/company/reports.clj b/src/clj/auto_ap/ssr/company/reports.clj
index 416a1d15..4652b295 100644
--- a/src/clj/auto_ap/ssr/company/reports.clj
+++ b/src/clj/auto_ap/ssr/company/reports.clj
@@ -27,13 +27,12 @@
(def query-schema (mc/schema
[:maybe
(into [:map {:date-range [:date-range :start-date :end-date]}
-
+
[:start-date {:optional true}
[:maybe clj-date-schema]]
[:end-date {:optional true}
[:maybe clj-date-schema]]
- [:client {:optional true :default nil} [:maybe [:entity-map {:pull [:db/id :client/name]}]]]
- ]
+ [:client {:optional true :default nil} [:maybe [:entity-map {:pull [:db/id :client/name]}]]]]
default-grid-fields-schema)]))
(def default-read '[:db/id :report/client [:report/created :xform clj-time.coerce/from-date] :report/url :report/name :report/creator])
@@ -43,22 +42,20 @@
query (cond-> {:query {:find []
:in '[$ [?c ...]]
:where '[[?e :report/client ?c]]}
- :args [db (:trimmed-clients request)]}
+ :args [db (:trimmed-clients request)]}
-
(:sort query-params) (add-sorter-fields {"client" ['[?e :report/client ?c]
- '[?c :client/name ?sort-client]]
- "created" ['[?e :report/created ?sort-created]]
- "creator" ['[?e :report/creator ?sort-creator]]
- "name" ['[?e :report/name ?sort-name]
- ]}
- query-params)
+ '[?c :client/name ?sort-client]]
+ "created" ['[?e :report/created ?sort-created]]
+ "creator" ['[?e :report/creator ?sort-creator]]
+ "name" ['[?e :report/name ?sort-name]]}
+ query-params)
true
(merge-query {:query {:find ['?sort-default '?e] :where ['[?e :report/created ?sort-default]]}}))]
(->> (query2 query)
- (apply-sort-3 (update query-params :sort conj {:sort-key "default-2" :asc true}))
- (apply-pagination query-params))))
+ (apply-sort-3 (update query-params :sort conj {:sort-key "default-2" :asc true}))
+ (apply-pagination query-params))))
(defn hydrate-results [ids db request]
(let [results (->> (pull-many db default-read ids)
@@ -67,7 +64,7 @@
(->> ids
(map results)
(filter identity)
-
+
(map first)
(filter (fn [r]
(let [used-clients (set (map :db/id (:report/client r)))]
@@ -78,7 +75,7 @@
(defn fetch-page [args]
(let [db (dc/db conn)
{ids-to-retrieve :ids matching-count :count} (fetch-ids db args)]
-
+
[(->> (hydrate-results ids-to-retrieve db args))
matching-count]))
@@ -115,7 +112,7 @@
:sort-key "creator"
:render (fn [report]
(when (:report/creator report)
- (com/pill {:color :primary }
+ (com/pill {:color :primary}
(:report/creator report))))}
{:key "created"
:name "Created"
@@ -129,7 +126,7 @@
(def page (helper/page-route grid-page))
(defn delete-report [{:keys [form-params identity]}]
-
+
(let [[id-to-delete key] (first (dc/q '[:find ?i ?k
:in $ ?i
:where [?i :report/key ?k]]
@@ -137,29 +134,28 @@
(some-> (get form-params "id") not-empty Long/parseLong)))
report (dc/pull (dc/db conn) default-read id-to-delete)]
(assert-can-see-client identity (:report/client report))
- (when id-to-delete
+ (when id-to-delete
(s3/delete-object :bucket-name (:data-bucket env)
:key key)
@(dc/transact conn [[:db/retractEntity id-to-delete]]))
- (html-response
- (row* identity
- report
- {:flash? true
- :delete-after-settle? true}))))
-
+ (html-response
+ (row* identity
+ report
+ {:flash? true
+ :delete-after-settle? true}))))
(def key->handler
(apply-middleware-to-all-handlers
(->>
- (into
- {:company-reports page
- :company-reports-table table
- :company-reports-delete delete-report}
- company-expense-report/key->handler)
+ (into
+ {:company-reports page
+ :company-reports-table table
+ :company-reports-delete delete-report}
+ company-expense-report/key->handler)
(into company-reconciliation-report/key->handler))
(fn [h]
(-> h
-(wrap-copy-qp-pqp)
+ (wrap-copy-qp-pqp)
(wrap-apply-sort grid-page)
(wrap-merge-prior-hx)
(wrap-schema-enforce :query-schema query-schema)
diff --git a/src/clj/auto_ap/ssr/company/reports/expense.clj b/src/clj/auto_ap/ssr/company/reports/expense.clj
index 0dad1a22..f98c410a 100644
--- a/src/clj/auto_ap/ssr/company/reports/expense.clj
+++ b/src/clj/auto_ap/ssr/company/reports/expense.clj
@@ -1,4 +1,4 @@
-(ns auto-ap.ssr.company.reports.expense
+(ns auto-ap.ssr.company.reports.expense
(:require [auto-ap.datomic :refer [conn merge-query]]
[auto-ap.graphql.utils :refer [extract-client-ids]]
[auto-ap.logging :as alog]
@@ -20,73 +20,71 @@
[hiccup2.core :as hiccup]))
(defn lookup-breakdown-data [request]
- (let [query (cond-> {:query '{:find [?cn ?user-date (sum ?amt)]
- :with [?e]
- :in [$ [?clients ?start ?end]]
- :where
- [[(iol-ion.query/scan-invoices $ ?clients ?start ?end) [[?e _ ?sort-default] ...]]
- (not [?e :invoice/status :invoice-status/voided])
- [?e :invoice/date ?d]
- [?e :invoice/client ?c]
- [?e :invoice/expense-accounts ?iea]
- [?iea :invoice-expense-account/amount ?amt]
- [?c :client/name ?cn]
- [(clj-time.coerce/to-date-time ?d) ?user-date]]}
- :args
- [(dc/db conn)
- [(extract-client-ids (:clients request)
- (:client-id request)
- (when (:client-code request)
- [:client/code (:client-code request)]))
- (some-> (time/plus (time/now) (time/days -65)) coerce/to-date)
- (some-> (time/now) coerce/to-date)]]}
+ (let [query (cond-> {:query '{:find [?cn ?user-date (sum ?amt)]
+ :with [?e]
+ :in [$ [?clients ?start ?end]]
+ :where
+ [[(iol-ion.query/scan-invoices $ ?clients ?start ?end) [[?e _ ?sort-default] ...]]
+ (not [?e :invoice/status :invoice-status/voided])
+ [?e :invoice/date ?d]
+ [?e :invoice/client ?c]
+ [?e :invoice/expense-accounts ?iea]
+ [?iea :invoice-expense-account/amount ?amt]
+ [?c :client/name ?cn]
+ [(clj-time.coerce/to-date-time ?d) ?user-date]]}
+ :args
+ [(dc/db conn)
+ [(extract-client-ids (:clients request)
+ (:client-id request)
+ (when (:client-code request)
+ [:client/code (:client-code request)]))
+ (some-> (time/plus (time/now) (time/days -65)) coerce/to-date)
+ (some-> (time/now) coerce/to-date)]]}
- (:vendor-id (:query-params request))
- (merge-query {:query '{:in [?v]
- :where [ [?e :invoice/vendor ?v]]}
- :args [ (:db/id (:vendor-id (:query-params request)))]})
-
- (:account-id (:query-params request))
- (merge-query {:query '{:in [?a]
- :where [ [?iea :invoice-expense-account/account ?a]]}
- :args [ (:db/id (:account-id (:query-params request)))]}))]
-
- (dc/query query)))
+ (:vendor-id (:query-params request))
+ (merge-query {:query '{:in [?v]
+ :where [[?e :invoice/vendor ?v]]}
+ :args [(:db/id (:vendor-id (:query-params request)))]})
+
+ (:account-id (:query-params request))
+ (merge-query {:query '{:in [?a]
+ :where [[?iea :invoice-expense-account/account ?a]]}
+ :args [(:db/id (:account-id (:query-params request)))]}))]
+
+ (dc/query query)))
(defn lookup-invoice-total-data [request]
- (let [start (:start-date (:query-params request) (time/plus (time/now) (time/days -30)))
- end (:end-date (:query-params request) (time/now))
- query (cond-> {:query '{:find [?cn ?vn (sum ?t)]
- :with [ ?e]
- :in [$ [?clients ?start ?end]]
- :where
- [[(iol-ion.query/scan-invoices $ ?clients ?start ?end) [[?e _ ?sort-default] ...]]
- (not [?e :invoice/status :invoice-status/voided])
- [?e :invoice/client ?c]
- [?e :invoice/total ?t]
- [?e :invoice/vendor ?v]
- [?v :vendor/name ?vn]
- [?c :client/name ?cn]
- ]}
- :args
- [(dc/db conn)
- [(extract-client-ids (:clients request)
- (:client-id request)
- (when (:client-code request)
- [:client/code (:client-code request)]))
- (some-> start coerce/to-date)
- (some-> end coerce/to-date)]]})]
-
- (dc/query query)))
+ (let [start (:start-date (:query-params request) (time/plus (time/now) (time/days -30)))
+ end (:end-date (:query-params request) (time/now))
+ query (cond-> {:query '{:find [?cn ?vn (sum ?t)]
+ :with [?e]
+ :in [$ [?clients ?start ?end]]
+ :where
+ [[(iol-ion.query/scan-invoices $ ?clients ?start ?end) [[?e _ ?sort-default] ...]]
+ (not [?e :invoice/status :invoice-status/voided])
+ [?e :invoice/client ?c]
+ [?e :invoice/total ?t]
+ [?e :invoice/vendor ?v]
+ [?v :vendor/name ?vn]
+ [?c :client/name ?cn]]}
+ :args
+ [(dc/db conn)
+ [(extract-client-ids (:clients request)
+ (:client-id request)
+ (when (:client-code request)
+ [:client/code (:client-code request)]))
+ (some-> start coerce/to-date)
+ (some-> end coerce/to-date)]]})]
-(defn week-seq
+ (dc/query query)))
+
+(defn week-seq
([c] (week-seq c (atime/last-monday)))
([c starting] (reverse (for [n (range c)
:let [start (time/minus starting (time/weeks n))
end (time/minus starting (time/weeks (dec n)))]]
[(atime/as-local-time (coerce/to-date-time start)) (atime/as-local-time (coerce/to-date-time end))]))))
-
(defn- best-week [d weeks]
(reduce
(fn [acc [start end]]
@@ -97,11 +95,10 @@
nil
weeks))
-
(defn expense-breakdown-card* [request]
(com/card {:class "w-full h-full" :id "expense-breakdown-report"}
[:div {:class "flex flex-col px-8 py-8 space-y-3 w-full h-full"}
-
+
[:form {:hx-get (bidi.bidi/path-for ssr-routes/only-routes :company-expense-report-breakdown-card)
:hx-trigger "change"
:hx-target "#expense-breakdown-report"
@@ -157,14 +154,14 @@
(for [d weeks]
(get-in lookup [ea d] 0)))]
[:canvas {:x-data (hx/json {:chart nil
- :labels x-axis
- :datasets (map (fn [s a] {:label a
- :data s
- :borderWidth 1})
- series
- distinct-accounts)})
- :x-init
- "new Chart($el, {
+ :labels x-axis
+ :datasets (map (fn [s a] {:label a
+ :data s
+ :borderWidth 1})
+ series
+ distinct-accounts)})
+ :x-init
+ "new Chart($el, {
type: 'bar',
data: {
labels: labels,
@@ -186,7 +183,7 @@
[:div {:class "flex flex-col px-8 py-8 space-y-3"}
[:div
[:h1.text-2xl.mb-3.font-bold "Invoice totals by vendor"]
- [:form {:hx-get (bidi.bidi/path-for ssr-routes/only-routes :company-expense-report-invoice-total-card )
+ [:form {:hx-get (bidi.bidi/path-for ssr-routes/only-routes :company-expense-report-invoice-total-card)
:hx-trigger "change"
:hx-target "#invoice-totals-report"
:hx-swap "outerHTML"}
@@ -201,7 +198,7 @@
(com/date-input {:name (fc/field-name)
:class "w-64"
:value (some-> (fc/field-value)
- (atime/unparse-local atime/normal-date)) })]))
+ (atime/unparse-local atime/normal-date))})]))
(fc/with-field :end-date
(com/validated-field {:label "End"
:errors (fc/field-errors)}
@@ -209,13 +206,12 @@
(com/date-input {:name (fc/field-name)
:class "w-64"
:value (some-> (fc/field-value)
- (atime/unparse-local atime/normal-date)) })]))])]
+ (atime/unparse-local atime/normal-date))})]))])]
[:div {:class "overflow-scroll min-w-full max-h-[700px]"}
(let [data (lookup-invoice-total-data request)
companies (sort (set (map first data)))
vendors (sort (set (map second data)))
- result (by (juxt first second) last data)
- ]
+ result (by (juxt first second) last data)]
(com/data-grid
{:headers (into
[(com/data-grid-header {:class "sticky left-0 z-60 bg-gray-100"} "Vendor")]
@@ -231,7 +227,7 @@
(com/data-grid-cell
{}
(or (some->> (get result [company vendor])
- (format "$%,.2f" ))
+ (format "$%,.2f"))
[:span.text-gray-200 "-"])))))))]]]))
(defn page [request]
diff --git a/src/clj/auto_ap/ssr/company/reports/reconciliation.clj b/src/clj/auto_ap/ssr/company/reports/reconciliation.clj
index b1e5d059..fce29a9a 100644
--- a/src/clj/auto_ap/ssr/company/reports/reconciliation.clj
+++ b/src/clj/auto_ap/ssr/company/reports/reconciliation.clj
@@ -53,7 +53,7 @@
(com/data-grid-cell {:class class}
(when (> (count (:missing-transactions row)) 0)
[:div
- (com/button { :x-tooltip.on.click "{content: ()=>$refs.tooltip.innerHTML, theme: 'light', allowHTML: true}" }
+ (com/button {:x-tooltip.on.click "{content: ()=>$refs.tooltip.innerHTML, theme: 'light', allowHTML: true}"}
[:div.flex.gap-2.items-center
(count (:missing-transactions row))
[:div.w-4.h-4 svg/question]])
@@ -67,13 +67,12 @@
(com/data-grid-cell {}
(format "$%,.2f" (:transaction/amount r))))))]]))))))])
-
(defn reconciliation-card* [{:keys [request report]}]
(com/content-card {:class "w-full" :id "reconciliation-report"}
[:div {:class "flex flex-col px-8 py-8 space-y-3"}
[:div
[:h1.text-2xl.mb-3.font-bold "Bank Reconciliation Report"]
-
+
[:form {:hx-get (bidi.bidi/path-for ssr-routes/only-routes :company-reconciliation-report-card)
:hx-target "#reconciliation-report"
:hx-swap "outerHTML"}
@@ -88,7 +87,7 @@
(com/date-input {:name (fc/field-name)
:class "w-64"
:value (some-> (fc/field-value)
- (atime/unparse-local atime/normal-date)) })]))
+ (atime/unparse-local atime/normal-date))})]))
(fc/with-field :end-date
(com/validated-field {:label "End"
:errors (fc/field-errors)}
@@ -96,12 +95,11 @@
(com/date-input {:name (fc/field-name)
:class "w-64"
:value (some-> (fc/field-value)
- (atime/unparse-local atime/normal-date)) })]))
+ (atime/unparse-local atime/normal-date))})]))
(com/button {:color :primary :class "self-center w-24"} "Run")])]
-(if report
+ (if report
(report* {:request request :report report})
- [:div "Please choose a time range to run the report"])
- ]]))
+ [:div "Please choose a time range to run the report"])]]))
(defn page [request]
(base-page
@@ -134,7 +132,7 @@
url/map->query))
(defn get-report-data [start-date end-date client-ids]
- (let [client-codes (map first (dc/q '[:find ?cc :in $ [?c ...] :where [?c :client/code ?cc]] (dc/db conn ) client-ids))]
+ (let [client-codes (map first (dc/q '[:find ?cc :in $ [?c ...] :where [?c :client/code ?cc]] (dc/db conn) client-ids))]
(for [[ib ba c] (seq (apply get-intuit-bank-accounts (dc/db conn) client-codes))
:let [raw-transactions (get-transactions (atime/unparse-local start-date atime/iso-date)
(atime/unparse-local end-date atime/iso-date)
@@ -169,11 +167,11 @@
:requires-feedback-count (:transaction-approval-status/requires-feedback found-transactions 0)
:missing-transactions missing-transactions})))
-(defn card [{ {:keys [start-date end-date]} :query-params :as request}]
+(defn card [{{:keys [start-date end-date]} :query-params :as request}]
(let [client-ids (extract-client-ids (:clients request)
- (:client-id request)
- (when (:client-code request)
- [:client/code (:client-code request)]))
+ (:client-id request)
+ (when (:client-code request)
+ [:client/code (:client-code request)]))
report (get-report-data start-date end-date client-ids)]
(html-response
(reconciliation-card* {:request request
@@ -182,7 +180,7 @@
(def key->handler
(apply-middleware-to-all-handlers
- {:company-reconciliation-report page
+ {:company-reconciliation-report page
:company-reconciliation-report-card card}
(fn [h]
(-> h
@@ -191,4 +189,4 @@
[:start-date {:optional true}
[:maybe clj-date-schema]]
[:end-date {:optional true}
- [:maybe clj-date-schema]] ])))))
\ No newline at end of file
+ [:maybe clj-date-schema]]])))))
\ No newline at end of file
diff --git a/src/clj/auto_ap/ssr/company/yodlee.clj b/src/clj/auto_ap/ssr/company/yodlee.clj
index f2e65d0d..163893af 100644
--- a/src/clj/auto_ap/ssr/company/yodlee.clj
+++ b/src/clj/auto_ap/ssr/company/yodlee.clj
@@ -33,36 +33,34 @@
:yodlee-provider-account/client [:client/code]}])
(def query-schema (mc/schema
- [:maybe
- (into [:map {}
- [:client-id {:optional true} [:maybe entity-id]] ]
- default-grid-fields-schema)]))
+ [:maybe
+ (into [:map {}
+ [:client-id {:optional true} [:maybe entity-id]]]
+ default-grid-fields-schema)]))
(defn fetch-ids [db request]
(let [query-params (:query-params request)
query (cond-> {:query {:find []
- :in ['$ '[?xx ...]]
- :where ['[?e :yodlee-provider-account/id]
- '[?e :yodlee-provider-account/client ?xx]]}
- :args [db (:trimmed-clients request)]}
+ :in ['$ '[?xx ...]]
+ :where ['[?e :yodlee-provider-account/id]
+ '[?e :yodlee-provider-account/client ?xx]]}
+ :args [db (:trimmed-clients request)]}
-
- (:sort query-params) (add-sorter-fields {"status" ['[?e :yodlee-provider-account/status ?sort-status]]
- "client" ['[?e :yodlee-provider-account/client ?c]
- '[?c :client/code ?sort-client]]
- "provider-account" ['[?e :yodlee-provider-account/id ?sort-provider-account]]
- "last-updated" ['[?e :yodlee-provider-account/last-updated ?sort-last-updated]]}
- query-params)
- true
- (merge-query {:query {:find ['?e ]
- :where ['[?e :yodlee-provider-account/id]]}}))]
+ (:sort query-params) (add-sorter-fields {"status" ['[?e :yodlee-provider-account/status ?sort-status]]
+ "client" ['[?e :yodlee-provider-account/client ?c]
+ '[?c :client/code ?sort-client]]
+ "provider-account" ['[?e :yodlee-provider-account/id ?sort-provider-account]]
+ "last-updated" ['[?e :yodlee-provider-account/last-updated ?sort-last-updated]]}
+ query-params)
+ true
+ (merge-query {:query {:find ['?e]
+ :where ['[?e :yodlee-provider-account/id]]}}))]
(->> query
(query2)
(apply-sort-3 query-params)
(apply-pagination query-params))))
-
(defn hydrate-results [ids db _]
(let [results (->> (pull-many db default-read ids)
(group-by :db/id))]
@@ -70,26 +68,24 @@
(map results)
(map first))))
-
(defn fetch-page [request]
(let [db (dc/db conn)
{ids-to-retrieve :ids matching-count :count} (fetch-ids db request)]
[(->> (hydrate-results ids-to-retrieve db request))
matching-count]))
-
(defn fastlink-dialog [{:keys [client]}]
(modal-response
- (com/modal
- {}
- (com/modal-card
- {}
- [:div.flex [:div.p-2 "Yodlee Fastlink"] ]
- [:div
- [:div#fa-spot]
- [:script {:lang "text/javascript"}
- (hiccup/raw
- (format "
+ (com/modal
+ {}
+ (com/modal-card
+ {}
+ [:div.flex [:div.p-2 "Yodlee Fastlink"]]
+ [:div
+ [:div#fa-spot]
+ [:script {:lang "text/javascript"}
+ (hiccup/raw
+ (format "
fastlink.open({fastLinkURL: '%s',
accessToken: '%s',
params: {'configName': 'Aggregation'},
@@ -100,25 +96,24 @@ fastlink.open({fastLinkURL: '%s',
}},
'fa-spot');
-" (:yodlee2-fastlink env) (yodlee/get-access-token (:client/code client))))]
- ]
- [:div]))))
+" (:yodlee2-fastlink env) (yodlee/get-access-token (:client/code client))))]]
+ [:div]))))
(defn reauthenticate [{:keys [form-params identity]}]
(assert-can-see-client identity (-> (dc/pull (dc/db conn) '[{:yodlee-provider-account/client [:db/id]}] (Long/parseLong (get form-params "id")))
:yodlee-provider-account/client
:db/id))
(html-response
- (com/modal
- {}
- (com/modal-card
- {}
- [:div.flex [:div.p-2 "Yodlee Fastlink"] ]
- [:div
- [:div#fa-spot]
- [:script {:lang "text/javascript"}
- (hiccup/raw
- (format "
+ (com/modal
+ {}
+ (com/modal-card
+ {}
+ [:div.flex [:div.p-2 "Yodlee Fastlink"]]
+ [:div
+ [:div#fa-spot]
+ [:script {:lang "text/javascript"}
+ (hiccup/raw
+ (format "
fastlink.open({fastLinkURL: '%s',
accessToken: '%s',
params: {'configName': 'Aggregation',
@@ -127,94 +122,93 @@ fastlink.open({fastLinkURL: '%s',
'fa-spot');
"
- (:yodlee2-fastlink env)
- (yodlee/get-access-token (-> (dc/pull (dc/db conn)
- [{:yodlee-provider-account/client [:client/code]}]
- (Long/parseLong (get form-params "id")))
- :yodlee-provider-account/client
- :client/code))
- (pull-attr (dc/db conn) :yodlee-provider-account/id (Long/parseLong (get form-params "id")))))]]
- [:div]))))
+ (:yodlee2-fastlink env)
+ (yodlee/get-access-token (-> (dc/pull (dc/db conn)
+ [{:yodlee-provider-account/client [:client/code]}]
+ (Long/parseLong (get form-params "id")))
+ :yodlee-provider-account/client
+ :client/code))
+ (pull-attr (dc/db conn) :yodlee-provider-account/id (Long/parseLong (get form-params "id")))))]]
+ [:div]))))
(def grid-page
- (helper/build
- {:id "yodlee-table"
- :nav com/company-aside-nav
- :id-fn :db/id
- :fetch-page fetch-page
- :breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes
- :company)}
- "My Company"]
- [:a {:href (bidi/path-for ssr-routes/only-routes
- :company-yodlee)}
- "Yodlee"]]
- :title "Yodlee Accounts"
- :entity-name "Yodlee accounts"
- :query-schema query-schema
- :route :company-yodlee-table
- :action-buttons (fn [request]
- [[:div.flex.flex-col.flex-shrink
- [:div.flex-shrink
- (com/button {:color :primary
- :on-click "openFastlink()"
- :disabled (if (:client request)
- false
- true)
- :hx-get (bidi/path-for ssr-routes/only-routes
- :company-yodlee-fastlink-dialog)
- :hx-target "#modal-holder"}
- (com/button-icon {} svg/refresh)
- "Link new account")]
- (when-not (:client request)
- [:div.text-xs "Note: please select a specific customer to link a new account."])]])
- :row-buttons (fn [request _]
- [
- (com/button {:hx-put (bidi/path-for ssr-routes/only-routes
- :company-yodlee-provider-account-reauthenticate)
- :color :primary
- :hx-target "#modal-holder"}
- "Reauthenticate")
- (when (is-admin? (:identity request))
- (com/icon-button {:hx-put (bidi/path-for ssr-routes/only-routes
- :company-yodlee-provider-account-refresh)
- :hx-target "closest tr"}
- svg/refresh))])
- :headers [{:key "client"
- :name "Client"
- :sort-key "client"
- :hide? (fn [args]
- (= (count (:clients args)) 1))
- :render #(-> % :yodlee-provider-account/client :client/code)}
- {:key "provider-account"
- :name "Provider Account"
- :sort-key "provider-account"
- :render :yodlee-provider-account/id}
- {:key "status"
- :name "Status"
- :sort-key "status"
- :render #(when-let [status (:yodlee-provider-account/status %)]
- (com/pill {:color (if (not= status "SUCCESS")
- :yellow
- :primary) }
- status))}
- {:key "detailed-status"
- :name "Detailed Status"
- :sort-key "detailed-status"
- :render #(when-let [status (:yodlee-provider-account/detailed-status %)]
- status)}
+ (helper/build
+ {:id "yodlee-table"
+ :nav com/company-aside-nav
+ :id-fn :db/id
+ :fetch-page fetch-page
+ :breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes
+ :company)}
+ "My Company"]
+ [:a {:href (bidi/path-for ssr-routes/only-routes
+ :company-yodlee)}
+ "Yodlee"]]
+ :title "Yodlee Accounts"
+ :entity-name "Yodlee accounts"
+ :query-schema query-schema
+ :route :company-yodlee-table
+ :action-buttons (fn [request]
+ [[:div.flex.flex-col.flex-shrink
+ [:div.flex-shrink
+ (com/button {:color :primary
+ :on-click "openFastlink()"
+ :disabled (if (:client request)
+ false
+ true)
+ :hx-get (bidi/path-for ssr-routes/only-routes
+ :company-yodlee-fastlink-dialog)
+ :hx-target "#modal-holder"}
+ (com/button-icon {} svg/refresh)
+ "Link new account")]
+ (when-not (:client request)
+ [:div.text-xs "Note: please select a specific customer to link a new account."])]])
+ :row-buttons (fn [request _]
+ [(com/button {:hx-put (bidi/path-for ssr-routes/only-routes
+ :company-yodlee-provider-account-reauthenticate)
+ :color :primary
+ :hx-target "#modal-holder"}
+ "Reauthenticate")
+ (when (is-admin? (:identity request))
+ (com/icon-button {:hx-put (bidi/path-for ssr-routes/only-routes
+ :company-yodlee-provider-account-refresh)
+ :hx-target "closest tr"}
+ svg/refresh))])
+ :headers [{:key "client"
+ :name "Client"
+ :sort-key "client"
+ :hide? (fn [args]
+ (= (count (:clients args)) 1))
+ :render #(-> % :yodlee-provider-account/client :client/code)}
+ {:key "provider-account"
+ :name "Provider Account"
+ :sort-key "provider-account"
+ :render :yodlee-provider-account/id}
+ {:key "status"
+ :name "Status"
+ :sort-key "status"
+ :render #(when-let [status (:yodlee-provider-account/status %)]
+ (com/pill {:color (if (not= status "SUCCESS")
+ :yellow
+ :primary)}
+ status))}
+ {:key "detailed-status"
+ :name "Detailed Status"
+ :sort-key "detailed-status"
+ :render #(when-let [status (:yodlee-provider-account/detailed-status %)]
+ status)}
- {:key "last-updated"
- :name "Last Updated"
- :sort-key "last-updated"
- :render #(atime/unparse-local (:yodlee-provider-account/last-updated %)
- atime/normal-date)}
- {:key "accounts"
- :name "Accounts"
- :show-starting "md"
- :render (fn [e]
- [:ul
- (for [a (:yodlee-provider-account/accounts e)]
- [:li (:yodlee-account/name a) " - " (:yodlee-account/number a)])])}]}))
+ {:key "last-updated"
+ :name "Last Updated"
+ :sort-key "last-updated"
+ :render #(atime/unparse-local (:yodlee-provider-account/last-updated %)
+ atime/normal-date)}
+ {:key "accounts"
+ :name "Accounts"
+ :show-starting "md"
+ :render (fn [e]
+ [:ul
+ (for [a (:yodlee-provider-account/accounts e)]
+ [:li (:yodlee-account/name a) " - " (:yodlee-account/number a)])])}]}))
(def page (helper/page-route grid-page))
(def table (helper/table-route grid-page))
@@ -224,26 +218,23 @@ fastlink.open({fastLinkURL: '%s',
(yodlee/refresh-provider-account (:client/code (:yodlee-provider-account/client provider-account))
(:yodlee-provider-account/id provider-account))
(html-response
- (helper/row*
- grid-page
- identity
- provider-account
- {:flash? true}))))
+ (helper/row*
+ grid-page
+ identity
+ provider-account
+ {:flash? true}))))
-
-(def key->handler
- (apply-middleware-to-all-handlers
- {
- :company-yodlee page
+(def key->handler
+ (apply-middleware-to-all-handlers
+ {:company-yodlee page
:company-yodlee-table table
- :company-yodlee-fastlink-dialog fastlink-dialog
- }
+ :company-yodlee-fastlink-dialog fastlink-dialog}
(fn [h]
- (-> h
- (wrap-copy-qp-pqp)
- (wrap-apply-sort grid-page)
- (wrap-merge-prior-hx)
- (wrap-schema-enforce :query-schema query-schema)
- (wrap-schema-enforce :hx-schema query-schema)
- (wrap-client-redirect-unauthenticated)
- (wrap-secure)))))
\ No newline at end of file
+ (-> h
+ (wrap-copy-qp-pqp)
+ (wrap-apply-sort grid-page)
+ (wrap-merge-prior-hx)
+ (wrap-schema-enforce :query-schema query-schema)
+ (wrap-schema-enforce :hx-schema query-schema)
+ (wrap-client-redirect-unauthenticated)
+ (wrap-secure)))))
\ No newline at end of file
diff --git a/src/clj/auto_ap/ssr/company_dropdown.clj b/src/clj/auto_ap/ssr/company_dropdown.clj
index 7b9bd2b2..e4f0ba4e 100644
--- a/src/clj/auto_ap/ssr/company_dropdown.clj
+++ b/src/clj/auto_ap/ssr/company_dropdown.clj
@@ -15,7 +15,6 @@
[hiccup2.core :as hiccup]
[iol-ion.query :refer [can-see-client?]]))
-
(defn dropdown-search-results* [{:keys [options]}]
[:ul
(for [{:keys [id name group]} options]
@@ -44,9 +43,6 @@
:hx-trigger "click"}
name])]])])
-
-
-
(defn get-clients [identity query]
(let [raw-query (not-empty (strip-special query))
cleansed-query (not-empty (cleanse-query query))
@@ -89,11 +85,11 @@
"localStorage.setItem(\"last-client-id\", \"" (:db/id client) "\")" "\n"
"localStorage.setItem(\"last-selected-clients\", " (json/write-str (json/write-str client-selection))
#_(cond (:group client-selection)
- (:group client-selection)
- (:selected client-selection)
- (:selected client-selection)
- :else
- client-selection) ")")]
+ (:group client-selection)
+ (:selected client-selection)
+ (:selected client-selection)
+ :else
+ client-selection) ")")]
[:div
[:button#company-dropdown-button {:class "text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-4 py-2.5 text-center inline-flex items-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
"x-tooltip.on.click" "{content: ()=>$refs.tooltip.innerHTML, theme: 'light', onMount(i) { htmx.process(i.popper); }, allowHTML: true, interactive:true}"
@@ -103,11 +99,11 @@
"My Companies"
(= :all client-selection)
"All Companies"
-
+
(and client
(= 1 (count clients)))
(:client/name client)
-
+
:else
(str (count clients) " Companies"))
[:div.w-4.h-4.ml-2
diff --git a/src/clj/auto_ap/ssr/components/aside.clj b/src/clj/auto_ap/ssr/components/aside.clj
index 22c15de3..b3c2f2fe 100644
--- a/src/clj/auto_ap/ssr/components/aside.clj
+++ b/src/clj/auto_ap/ssr/components/aside.clj
@@ -55,8 +55,8 @@
":style" (format "selected == '%s' ? 'max-height: ' + $el.scrollHeight + 'px' : ''" (:selector params))))
(for [c children]
[:li
- (update-in c [1 1 :class ] (fn [c]
- (hh/add-class (or c "") " flex items-center p-2 pl-11 w-full text-base font-normal rounded-lg transition duration-75 group hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700")))])])
+ (update-in c [1 1 :class] (fn [c]
+ (hh/add-class (or c "") " flex items-center p-2 pl-11 w-full text-base font-normal rounded-lg transition duration-75 group hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700")))])])
(defn left-aside- [{:keys [nav page-specific]} & _]
[:aside {:id "left-nav",
@@ -83,7 +83,6 @@
[:div {:class "overflow-y-auto py-5 px-3 h-full bg-gray-50 border-r border-gray-200 dark:bg-gray-800 dark:border-gray-700"}
nav
-
(when page-specific
[:div {:class " pt-5 mt-5 space-y-2 border-t border-gray-200 dark:border-gray-700"}
page-specific])]])
@@ -94,7 +93,7 @@
"invoices"
(#{:pos-sales :pos-expected-deposits :pos-tenders :pos-refunds :pos-cash-drawer-shifts ::ss-routes/page} (:matched-route request))
- "sales"
+ "sales"
(#{::payment-routes/all-page ::payment-routes/pending-page ::payment-routes/cleared-page ::payment-routes/voided-page} (:matched-route request))
"payments"
(#{::transaction-routes/page ::transaction-routes/approved-page ::transaction-routes/unapproved-page ::transaction-routes/requires-feedback-page :transaction-insights} (:matched-route request))
@@ -108,7 +107,7 @@
[:li
(menu-button- {:icon svg/pie
- :href (bidi/path-for ssr-routes/only-routes
+ :href (bidi/path-for ssr-routes/only-routes
::dashboard/page)}
"Dashboard")]
@@ -147,7 +146,6 @@
:hx-boost "true"}
"Voided")
-
(when (can? (:identity request)
{:subject :invoice
:activity :import})
@@ -156,7 +154,6 @@
:active? (= ::invoice-route/import-page (:matched-route request))
:hx-boost "true"} "Import"))
-
#_(when (can? (:identity request)
{:subject :invoice
:activity :import})
@@ -168,7 +165,6 @@
"Glimpse"
(tags/pill- {:color :secondary} "Beta")]))
-
(when (can? (:identity request)
{:subject :ar-invoice
:activity :read})
@@ -213,12 +209,12 @@
:hx-boost "true"}
"Refunds")
- (menu-button- {:href (str (bidi/path-for ssr-routes/only-routes
- :pos-cash-drawer-shifts)
- "?date-range=week")
- :active? (= :pos-cash-drawer-shifts (:matched-route request))
- :hx-boost "true"}
- "Cash drawer shifts")
+ (menu-button- {:href (str (bidi/path-for ssr-routes/only-routes
+ :pos-cash-drawer-shifts)
+ "?date-range=week")
+ :active? (= :pos-cash-drawer-shifts (:matched-route request))
+ :hx-boost "true"}
+ "Cash drawer shifts")
(menu-button- {:href (str (bidi/path-for ssr-routes/only-routes
::ss-routes/page)
"?date-range=week")
@@ -288,7 +284,6 @@
(menu-button- {:href (bidi/path-for ssr-routes/only-routes
:transaction-insights)} "Insights")))]
-
(when (can? (:identity request)
{:subject :ledger-page})
(list
@@ -314,7 +309,7 @@
[:div.flex.gap-2
"External Register"
(tags/pill- {:color :secondary} "WIP")]))
-
+
(menu-button- {:href (hu/url (bidi/path-for ssr-routes/only-routes
::ledger-routes/profit-and-loss))
:active? (= ::ledger-routes/profit-and-loss (:matched-route request))
@@ -322,7 +317,7 @@
[:div.flex.gap-2
"Profit and loss"
(tags/pill- {:color :secondary} "WIP")])
-
+
(menu-button- {:href (hu/url (bidi/path-for ssr-routes/only-routes
::ledger-routes/cash-flows))
:active? (= ::ledger-routes/cash-flows (:matched-route request))
@@ -330,7 +325,7 @@
[:div.flex.gap-2
"Cash flows"
(tags/pill- {:color :secondary} "WIP")])
-
+
(menu-button- {:href (hu/url (bidi/path-for ssr-routes/only-routes
::ledger-routes/balance-sheet))
:active? (= ::ledger-routes/balance-sheet (:matched-route request))
@@ -338,8 +333,7 @@
[:div.flex.gap-2
"Balance Sheet"
(tags/pill- {:color :secondary} "WIP")])
-
-
+
(menu-button- {:href (hu/url (bidi/path-for ssr-routes/only-routes
::ledger-routes/external-import-page)
{:date-range "month"})
@@ -349,7 +343,6 @@
"External Import"
(tags/pill- {:color :secondary} "WIP")]))))]))
-
(defn company-aside-nav- [request]
[:ul {:class "space-y-2" :hx-boost "true"}
[:li
@@ -465,7 +458,6 @@
:hx-boost true}
"Background Jobs")]
-
(menu-button- {:icon svg/arrow-in
"@click.prevent" "if (selected == 'import') {selected = null } else { selected = 'import'} "}
"Import")
diff --git a/src/clj/auto_ap/ssr/components/bank_account_icon.clj b/src/clj/auto_ap/ssr/components/bank_account_icon.clj
index 90cf3906..84e31626 100644
--- a/src/clj/auto_ap/ssr/components/bank_account_icon.clj
+++ b/src/clj/auto_ap/ssr/components/bank_account_icon.clj
@@ -1,4 +1,4 @@
-(ns auto-ap.ssr.components.bank-account-icon
+(ns auto-ap.ssr.components.bank-account-icon
(:require [auto-ap.ssr.hiccup-helper :as hh]
[auto-ap.ssr.svg :as svg]))
diff --git a/src/clj/auto_ap/ssr/components/breadcrumbs.clj b/src/clj/auto_ap/ssr/components/breadcrumbs.clj
index 3cbaf6aa..286c7f85 100644
--- a/src/clj/auto_ap/ssr/components/breadcrumbs.clj
+++ b/src/clj/auto_ap/ssr/components/breadcrumbs.clj
@@ -8,15 +8,15 @@
[:a {:href "#", :class "inline-flex w-4 h-4 mr-2 items-center text-sm font-medium text-gray-700 hover:text-blue-600 dark:text-gray-400 dark:hover:text-white"}
[:div.w-4.h-4 svg/home]]]
(for [p steps]
- [:li
+ [:li
[:div {:class "flex items-center"}
-
- [:div {:class "w-6 h-6 text-gray-400",}
+
+ [:div {:class "w-6 h-6 text-gray-400"}
svg/breadcrumb-component]
-
+
(update-in p [1 :class] str " ml-1 text-sm font-medium text-gray-700 hover:text-blue-600 md:ml-2 dark:text-gray-400 dark:hover:text-white")]])
#_[:li {:aria-current "page"}
- [:div {:class "flex items-center"}
- [:svg {:aria-hidden "true", :class "w-6 h-6 text-gray-400", :fill "currentColor", :viewbox "0 0 20 20", :xmlns "http://www.w3.org/2000/svg"}
- [:path {:fill-rule "evenodd", :d "M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z", :clip-rule "evenodd"}]]
- [:span {:class "ml-1 text-sm font-medium text-gray-500 md:ml-2 dark:text-gray-400"} "Flowbite"]]]]])
+ [:div {:class "flex items-center"}
+ [:svg {:aria-hidden "true", :class "w-6 h-6 text-gray-400", :fill "currentColor", :viewbox "0 0 20 20", :xmlns "http://www.w3.org/2000/svg"}
+ [:path {:fill-rule "evenodd", :d "M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z", :clip-rule "evenodd"}]]
+ [:span {:class "ml-1 text-sm font-medium text-gray-500 md:ml-2 dark:text-gray-400"} "Flowbite"]]]]])
diff --git a/src/clj/auto_ap/ssr/components/buttons.clj b/src/clj/auto_ap/ssr/components/buttons.clj
index b5a3814f..1412043f 100644
--- a/src/clj/auto_ap/ssr/components/buttons.clj
+++ b/src/clj/auto_ap/ssr/components/buttons.clj
@@ -113,9 +113,9 @@
(= :secondary (:color params)) (str " text-white bg-blue-500 hover:bg-blue-600 focus:ring-blue-300 dark:bg-blue-600 dark:hover:bg-blue-700")
(= :primary (:color params)) (str " text-white bg-green-500 hover:bg-green-600 focus:ring-green-300 dark:bg-green-600 dark:hover:bg-green-700 ")
(= :secondary-light (:color params)) (str " text-blue-800 bg-white-200 border-gray-100 border hover:bg-blue-100 focus:ring-blue-100 dark:bg-blue-400 dark:hover:bg-blue-800 ")
-
+
(not (nil? (:color params)))
-(str " text-white " (bg-colors (:color params) (:disabled params)))
+ (str " text-white " (bg-colors (:color params) (:disabled params)))
(nil? (:color params))
(str " bg-white dark:bg-gray-600 border-gray-300 dark:border-gray-700 text-gray-500 hover:text-gray-800 dark:text-gray-400 dark:hover:text-gray-100 font-medium border border-gray-300 dark:border-gray-700")))
@@ -126,7 +126,7 @@
(svg/spinner {:class "inline w-4 h-4 text-white"})
[:div.ml-3 "Loading..."]])
(into [:div.inline-flex.gap-2.items-center.justify-center {:class (when (:indicator? params true)
- "htmx-indicator-hidden")}]
+ "htmx-indicator-hidden")}]
children)])
(defn icon-button- [params & children]
@@ -162,8 +162,6 @@
[:div.ml-3 "Loading..."]]
(into [:div.htmx-indicator-hidden] children)])
-
-
(defn group-button- [{:keys [size] :or {size :normal} :as params} & children]
(into [:button (cond-> params
true (assoc :type (or (:type params) "button"))
@@ -191,7 +189,7 @@
(defn navigation-button- [{:keys [class next-arrow?] :or {next-arrow? true} :as params} & children]
[:button
(-> params
- (update :class (fnil hh/add-class "")
+ (update :class (fnil hh/add-class "")
"p-4 text-green-700 border border-gray-300 rounded-lg bg-gray-50
dark:bg-gray-800 dark:border-green-800 dark:text-green-400
focus:ring-green-400 focus:ring-2
@@ -211,7 +209,7 @@
{:class "space-y-4 w-72"}
(for [n children]
[:li n])
-
+
#_[:li
[:div
{:class
@@ -231,7 +229,6 @@
[:span {:class "sr-only"} "Confirmation"]
[:h3 {:class "font-medium"} "5. Confirmation"]]]]])
-
(defn validated-save-button- [{:keys [errors class] :as params} & children]
(button- (-> {:color (or (:color params) :primary)
:type "submit" :class (cond-> (or class "")
diff --git a/src/clj/auto_ap/ssr/components/card.clj b/src/clj/auto_ap/ssr/components/card.clj
index 2c8f7a28..f13242ad 100644
--- a/src/clj/auto_ap/ssr/components/card.clj
+++ b/src/clj/auto_ap/ssr/components/card.clj
@@ -4,7 +4,7 @@
[clojure.string :as str]))
(defn card- [params & children]
- (into [:div (update params :class
+ (into [:div (update params :class
#(cond-> (or % "")
(not (str/includes? (or % "") "bg-")) (hh/add-class "dark:bg-gray-800 bg-white ")
true (hh/add-class "shadow-md sm:rounded-lg border-2 border-gray-200 dark:border-gray-900 overflow-hidden")))]
@@ -13,6 +13,6 @@
(defn content-card- [params & children]
[:section (merge params {:class (hh/add-class " py-3 sm:py-5" (:class params))})
[:div {:class (:max-w params "max-w-screen-2xl")}
- (into
- [:div {:class "relative overflow-scroll shadow-md dark:bg-gray-800 sm:rounded-lg border-2 border-gray-200 dark:border-gray-900 bg-white"}]
- children)]])
+ (into
+ [:div {:class "relative overflow-scroll shadow-md dark:bg-gray-800 sm:rounded-lg border-2 border-gray-200 dark:border-gray-900 bg-white"}]
+ children)]])
diff --git a/src/clj/auto_ap/ssr/components/data_grid.clj b/src/clj/auto_ap/ssr/components/data_grid.clj
index 63625824..e9bd9f3b 100644
--- a/src/clj/auto_ap/ssr/components/data_grid.clj
+++ b/src/clj/auto_ap/ssr/components/data_grid.clj
@@ -16,13 +16,13 @@
"@click" (format "$dispatch('sorted', {key: '%s'})" (:sort-key params))
:style (:style params)}]
(if (:sort-key params)
- [(into [:a {:href "#"} ] rest)]
+ [(into [:a {:href "#"}] rest)]
rest)))
(defn sort-header- [params & rest]
[:th.px-4.py-3 {:scope "col" :class (:class params)
- "@click" (format "$dispatch('sorted', {key: '%s'})" (:sort-key params)) }
- (into [:a {:href "#"} ] rest)])
+ "@click" (format "$dispatch('sorted', {key: '%s'})" (:sort-key params))}
+ (into [:a {:href "#"}] rest)])
(defn row- [params & rest]
(into [:tr (update params
@@ -31,11 +31,11 @@
(defn cell- [params & rest]
(into [:td.px-4.py-2 (update params
:class #(str (-> ""
- (hh/add-class (or % ""))))) ]
+ (hh/add-class (or % "")))))]
rest))
(defn right-stack-cell- [params & rest]
- (cell- params (into [:div.flex.flex-row-reverse.items-center.justify-between
+ (cell- params (into [:div.flex.flex-row-reverse.items-center.justify-between
rest])))
(defn checkbox-header- [params & rest]
@@ -46,17 +46,17 @@
(defn data-grid-
[{:keys [headers thead-params id] :as params} & rest]
- [:div.shrink.overflow-y-scroll
+ [:div.shrink.overflow-y-scroll
[:table (merge {:class "w-full text-sm text-left text-gray-500 dark:text-gray-400 shrink"}
(dissoc params :headers :thead-params))
[:thead (update thead-params :class #(-> "text-xs text-gray-800 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400 group-[.raw]:sticky group-[.raw]:z-10 group-[.raw]:top-0"
(hh/add-class (or % ""))))
- (into
- [:tr]
- headers)]
- (into
- [:tbody {}]
- rest)]])
+ (into
+ [:tr]
+ headers)]
+ (into
+ [:tbody {}]
+ rest)]])
;; needed for tailwind
;; lg:table-cell md:table-cell
@@ -81,35 +81,35 @@
rows] :as params} & children]
(let [card (if raw? raw-table-card content-card-)]
(card
- (cond-> { :id id :class (cond-> "group" raw? (hh/add-class "raw h-full flex flex-col overflow-hidden")) }
- root-params (merge root-params)
- route (assoc
- :hx-get (bidi/path-for ssr-routes/only-routes
- route
- :request-method :get)
- :hx-trigger "clientSelected from:body, invalidated from:body"
- :hx-swap "outerHTML swap:300ms"))
- [:div {:class " flex flex-col px-4 py-3 space-y-3 lg:flex-row lg:items-baseline lg:justify-between lg:space-y-0 lg:space-x-4 text-gray-800 dark:text-gray-100"}
- [:h1.text-2xl.mb-3.font-bold title]
- [:div {:class "flex items-center flex-1 space-x-4"}
- [:h5
- (when subtitle
- [:span subtitle])]]
- (into [:div {:class "group-[.raw]:hidden flex flex-col flex-shrink-0 space-y-3 md:flex-row md:items-center lg:justify-end md:space-y-0 md:space-x-3"}]
- action-buttons)]
- [:div {:class "overflow-x-auto contents"}
- (data-grid- {:headers headers
- :thead-params thead-params}
- rows)]
+ (cond-> {:id id :class (cond-> "group" raw? (hh/add-class "raw h-full flex flex-col overflow-hidden"))}
+ root-params (merge root-params)
+ route (assoc
+ :hx-get (bidi/path-for ssr-routes/only-routes
+ route
+ :request-method :get)
+ :hx-trigger "clientSelected from:body, invalidated from:body"
+ :hx-swap "outerHTML swap:300ms"))
+ [:div {:class " flex flex-col px-4 py-3 space-y-3 lg:flex-row lg:items-baseline lg:justify-between lg:space-y-0 lg:space-x-4 text-gray-800 dark:text-gray-100"}
+ [:h1.text-2xl.mb-3.font-bold title]
+ [:div {:class "flex items-center flex-1 space-x-4"}
+ [:h5
+ (when subtitle
+ [:span subtitle])]]
+ (into [:div {:class "group-[.raw]:hidden flex flex-col flex-shrink-0 space-y-3 md:flex-row md:items-center lg:justify-end md:space-y-0 md:space-x-3"}]
+ action-buttons)]
+ [:div {:class "overflow-x-auto contents"}
+ (data-grid- {:headers headers
+ :thead-params thead-params}
+ rows)]
- (when (or paginate?
- (nil? paginate?))
- [:div {:class "contents group-[.raw]:block"}
- (paginator- {:start start
- :end (Math/min (+ start per-page) total)
- :per-page per-page
- :total total
- :a-params (fn [page]
+ (when (or paginate?
+ (nil? paginate?))
+ [:div {:class "contents group-[.raw]:block"}
+ (paginator- {:start start
+ :end (Math/min (+ start per-page) total)
+ :per-page per-page
+ :total total
+ :a-params (fn [page]
;; TODO it might be good to have a more global form defined in the specific page
;; with elements that are part of item
;; that way this is not deeply coupled
@@ -117,44 +117,43 @@
;; TODO the other way to think about this is that we want the request to include
;; all of the correct parameters, not parameters to merge with the current ones.
;; think sorting, filters, pagination
- {:hx-get (hu/url (bidi/path-for ssr-routes/only-routes
- route
- :request-method :get)
- {:start (* page per-page)})
- :hx-target (str "#" id)
- :hx-swap "outerHTML show:#app:top"
- :hx-indicator (str "#" id)})
- :per-page-params {:hx-get (hu/url (bidi/path-for ssr-routes/only-routes
+ {:hx-get (hu/url (bidi/path-for ssr-routes/only-routes
route
- :request-method :get))
- :hx-trigger "change"
- :hx-include "this"
- :hx-target (str "#" id) ;
+ :request-method :get)
+ {:start (* page per-page)})
+ :hx-target (str "#" id)
:hx-swap "outerHTML show:#app:top"
- :hx-indicator (str "#" id)}})])
- children
- [:div {:class "htmx-indicator absolute -translate-x-1/2 -translate-y-1/2 top-2/4 left-1/2 overflow-hidden w-full h-full"}
- [:div {:class "flex items-center justify-center w-full h-full border border-gray-200 rounded-lg bg-gray-50 dark:bg-gray-800 dark:border-gray-700 bg-opacity-50" }
- [:div {:class "px-3 py-1 text-xs font-medium leading-none text-center text-blue-800 bg-blue-200 rounded-full animate-pulse dark:bg-blue-900 dark:text-blue-200"} "loading..."]]])))
+ :hx-indicator (str "#" id)})
+ :per-page-params {:hx-get (hu/url (bidi/path-for ssr-routes/only-routes
+ route
+ :request-method :get))
+ :hx-trigger "change"
+ :hx-include "this"
+ :hx-target (str "#" id) ;
+ :hx-swap "outerHTML show:#app:top"
+ :hx-indicator (str "#" id)}})])
+ children
+ [:div {:class "htmx-indicator absolute -translate-x-1/2 -translate-y-1/2 top-2/4 left-1/2 overflow-hidden w-full h-full"}
+ [:div {:class "flex items-center justify-center w-full h-full border border-gray-200 rounded-lg bg-gray-50 dark:bg-gray-800 dark:border-gray-700 bg-opacity-50"}
+ [:div {:class "px-3 py-1 text-xs font-medium leading-none text-center text-blue-800 bg-blue-200 rounded-full animate-pulse dark:bg-blue-900 dark:text-blue-200"} "loading..."]]])))
(defn new-row- [{:keys [index colspan tr-params row-offset] :as params} & content]
(row-
- (merge {:class "new-row"
-"x-on:htmx:after-settle.camel" "let options=$el.parentNode.querySelectorAll('tr'); let target=options[options.length-2]; $nextTick(() => $focus.within(target).first())"
+ (merge {:class "new-row"
+ "x-on:htmx:after-settle.camel" "let options=$el.parentNode.querySelectorAll('tr'); let target=options[options.length-2]; $nextTick(() => $focus.within(target).first())"
- :x-data (hx/json {:newRowIndex index
- :offset (or row-offset 0)}) }
- tr-params)
- (cell- {:colspan colspan
- :class "bg-gray-100"}
- [:div.flex.justify-center
- (a-button- (merge
- (dissoc params :index :colspan)
- {"@click.prevent" "$dispatch('newRow', {index: (newRowIndex++)})"
- :color :secondary
- :hx-trigger "newRow"
- :hx-vals (hiccup/raw "js:{index: event.detail.index }")
- :hx-target "closest .new-row"
- :hx-swap "beforebegin"
- })
- content)])))
+ :x-data (hx/json {:newRowIndex index
+ :offset (or row-offset 0)})}
+ tr-params)
+ (cell- {:colspan colspan
+ :class "bg-gray-100"}
+ [:div.flex.justify-center
+ (a-button- (merge
+ (dissoc params :index :colspan)
+ {"@click.prevent" "$dispatch('newRow', {index: (newRowIndex++)})"
+ :color :secondary
+ :hx-trigger "newRow"
+ :hx-vals (hiccup/raw "js:{index: event.detail.index }")
+ :hx-target "closest .new-row"
+ :hx-swap "beforebegin"})
+ content)])))
diff --git a/src/clj/auto_ap/ssr/components/dialog.clj b/src/clj/auto_ap/ssr/components/dialog.clj
index a27ea8fc..e951ff40 100644
--- a/src/clj/auto_ap/ssr/components/dialog.clj
+++ b/src/clj/auto_ap/ssr/components/dialog.clj
@@ -5,7 +5,6 @@
[auto-ap.ssr.hx :as hx]
[auto-ap.ssr.svg :as svg]))
-
(defn modal-
"This modal function is used to create a modal window with a stack that allows for transitioning between modals.
@@ -26,16 +25,16 @@
:class (fn [c] (-> c
(or "")
(hh/add-class "w-full p-4 modal-card flex max-h-[inherit]"))))
- [:div {:class "bg-white rounded-lg shadow dark:bg-gray-700 dark:text-white modal-content w-full flex flex-col max-h-full overflow-hidden" }
+ [:div {:class "bg-white rounded-lg shadow dark:bg-gray-700 dark:text-white modal-content w-full flex flex-col max-h-full overflow-hidden"}
[:div {:class "flex items-start justify-between p-4 border-b rounded-t dark:border-gray-600 shrink-0"} header]
[:div {:class "px-6 py-2 space-y-6 overflow-y-scroll w-full shrink"}
-
+
content]
(when footer [:div {:class "p-4"}
[:span.items-center.bg-red-100.text-red-800.text-xs.font-medium.mb-2.p-1.rounded-full.inline-flex (hx/alpine-appear {:x-show "unexpectedError" :class "dark:bg-red-900 dark:text-red-300"})
[:span {:class "w-2 h-2 mr-1 bg-red-500 rounded-full"}] [:span.px-2.py-0.5 "An unexpected error has occured. Integreat staff have been notified."]]
- (when (:error params )
- [:span.items-center.bg-red-100.text-red-800.text-xs.font-medium.mb-2.p-1.rounded-full.inline-flex { :class "dark:bg-red-900 dark:text-red-300"}
+ (when (:error params)
+ [:span.items-center.bg-red-100.text-red-800.text-xs.font-medium.mb-2.p-1.rounded-full.inline-flex {:class "dark:bg-red-900 dark:text-red-300"}
[:span {:class "w-2 h-2 mr-1 bg-red-500 rounded-full"}]
[:span.px-2.py-0.5 (:error params)]])
[:div {:class "shrink-0"}
@@ -45,7 +44,6 @@
[:div {:class "flex items-start justify-between p-4 border-b rounded-t dark:border-gray-600 shrink-0"}
children])
-
(defn modal-header-attachment- [params & children]
[:div {:class "flex items-start justify-between p-4 border-b shrink-0"}
children])
@@ -56,7 +54,7 @@
(defn modal-footer- [params & children]
[:div {:class "p-4 border-t"}
- [:span.items-center.bg-red-100.text-red-800.text-xs.font-medium.mb-2.p-1.rounded-full.inline-flex
+ [:span.items-center.bg-red-100.text-red-800.text-xs.font-medium.mb-2.p-1.rounded-full.inline-flex
(hx/alpine-appear {:x-show "unexpectedError" :class "dark:bg-red-900 dark:text-red-300"})
(hx/alpine-appear {:x-show "unexpectedError" :class "dark:bg-red-900 dark:text-red-300"})
[:span {:class "w-2 h-2 bg-red-500 rounded-full"}]
@@ -66,7 +64,7 @@
(defn modal-card-advanced- [params & children]
[:div (merge params
- {:class (hh/add-class "modal-card bg-white rounded-lg shadow dark:bg-gray-700 dark:text-white modal-content flex flex-col max-h-screen max-w-screen" (:class params "")) })
+ {:class (hh/add-class "modal-card bg-white rounded-lg shadow dark:bg-gray-700 dark:text-white modal-content flex flex-col max-h-screen max-w-screen" (:class params ""))})
children])
(defn success-modal- [{:keys [title]} & children]
diff --git a/src/clj/auto_ap/ssr/components/inputs.clj b/src/clj/auto_ap/ssr/components/inputs.clj
index a2cd3ac6..d7e9dddc 100644
--- a/src/clj/auto_ap/ssr/components/inputs.clj
+++ b/src/clj/auto_ap/ssr/components/inputs.clj
@@ -8,7 +8,6 @@
[clojure.string :as str]
[hiccup2.core :as hiccup]))
-
(def default-input-classes
["bg-gray-50" "border" "text-sm" "rounded-lg" "" "block"
"p-2.5" "border-gray-300" "text-gray-900" "focus:ring-blue-500" "focus:border-blue-500"
@@ -149,7 +148,6 @@
[:li {":style" "index == 0 && 'border: 0 !important;'"}
[:label {:class "p-3 group rounded flex gap-2 items-center outline-0 focus:bg-neutral-100 hover:bg-neutral-100 whitespace-nowrap [&.active]:bg-primary-300 [&.active]:dark:bg-primary-700 [&.implied]:text-gray-500 text-gray-800 dark:text-gray-100 cursor-pointer"
-
:href "#"
":class" (hx/json {"active" (hx/js-fn "active==index")
"implied" (hx/js-fn "all_selected && index != 0")})
@@ -178,7 +176,6 @@
:x-show "value.size > 0"}
svg/x]])
-
(defn multi-typeahead- [params]
[:div.relative {:x-data (hx/json {:baseUrl (str (:url params))
:reset_elements (js-fn "function(e) {
@@ -268,21 +265,17 @@
:x-effect "if (value.warning) { $nextTick(()=> warning_badge.update()) }"}
(tags/badge- {:class "peer"} "!")
-
[:div {:x-show "value.warning"
:x-ref "warning_pop"
:class "hidden peer-hover:block bg-red-50 dark:bg-gray-600 rounded-lg shadow-2xl w-max z-50 p-4"
:x-text "value.warning"}]]]
(multi-typeahead-dropdown- params)])])
-
(defn use-size [size]
(if (= :small size)
(str " " "text-xs p-2")
(str " " "text-sm p-2.25")))
-
-
(defn text-input- [{:keys [size error?] :as params}]
[:input
(-> params
@@ -415,8 +408,6 @@
(update :class #(str % (use-size size) " w-full"))
(dissoc :size :name :x-model :x-modelable))]]))
-
-
(defn field-errors- [{:keys [source key]} & rest]
(let [errors (:errors (cond-> (meta source)
key (get key)))]
@@ -469,8 +460,6 @@
(defn hidden- [{:keys [name value] :as params}]
[:input (merge {:type "hidden" :value value :name name} params)])
-
-
(defn toggle- [params & children]
[:label {:class "inline-flex items-center cursor-pointer"}
[:input (merge {:type "checkbox", :class "sr-only peer"} params)]
diff --git a/src/clj/auto_ap/ssr/components/link_dropdown.clj b/src/clj/auto_ap/ssr/components/link_dropdown.clj
index 395ca085..d2d88dd8 100644
--- a/src/clj/auto_ap/ssr/components/link_dropdown.clj
+++ b/src/clj/auto_ap/ssr/components/link_dropdown.clj
@@ -1,4 +1,4 @@
-(ns auto-ap.ssr.components.link-dropdown
+(ns auto-ap.ssr.components.link-dropdown
(:require [auto-ap.ssr.components :as com]
[auto-ap.ssr.hx :as hx]
[auto-ap.ssr.svg :as svg]))
@@ -8,8 +8,7 @@
[:div {:x-data (hx/json {})}
(com/a-icon-button {:class "relative"
- "@click.prevent" "$tooltip($refs.tooltip, {content: ()=>$refs.tooltip.innerHTML, theme: 'light', allowHTML: true, interactive:true, popperOptions: {strategy: 'fixed', modifiers: [{name: 'flip', options: {fallbackPlacements: ['top']}}]}})"
- }
+ "@click.prevent" "$tooltip($refs.tooltip, {content: ()=>$refs.tooltip.innerHTML, theme: 'light', allowHTML: true, interactive:true, popperOptions: {strategy: 'fixed', modifiers: [{name: 'flip', options: {fallbackPlacements: ['top']}}]}})"}
svg/paperclip
(com/badge {:color "blue"} (count links)))
[:template {:x-ref "tooltip"}
diff --git a/src/clj/auto_ap/ssr/components/multi_modal.clj b/src/clj/auto_ap/ssr/components/multi_modal.clj
index b6295ad4..a42d6ac7 100644
--- a/src/clj/auto_ap/ssr/components/multi_modal.clj
+++ b/src/clj/auto_ap/ssr/components/multi_modal.clj
@@ -15,12 +15,11 @@
[malli.core :as mc]
[malli.core :as m]))
-
(def default-form-props {:hx-ext "response-targets"
:hx-swap "outerHTML"
:hx-target-400 "#form-errors .error-content"
:hx-trigger "submit"
- :hx-target "this" })
+ :hx-target "this"})
(defprotocol ModalWizardStep
(step-key [this])
@@ -57,7 +56,6 @@
(or (get-in (:snapshot multi-form-state) edit-path)
default)))
-
(defn merge-multi-form-state [{:keys [snapshot edit-path step-params] :as multi-form-state}]
(let [cursor (cursor/cursor (or snapshot {}))
;; this hack makes sure that, in the event of a missing vector entry, will make sure to add it first
@@ -87,8 +85,6 @@
(fn encode-step-key [sk]
(mc/encode step-key-schema sk main-transformer))))
-
-
(defn render-timeline [linear-wizard current-step validation-route]
(let [step-names (map #(step-name (get-step linear-wizard %)) (steps linear-wizard))
active-index (.indexOf step-names (step-name current-step))]
@@ -148,9 +144,9 @@
next-button-content]}]
[:div.flex.justify-end
[:div.flex.items-baseline.gap-x-4
- (let [step-errors (:step-params fc/*form-errors*)]
- (com/form-errors {:errors (or (:errors step-errors)
- (when (sequential? step-errors) step-errors))}))
+ (let [step-errors (:step-params fc/*form-errors*)]
+ (com/form-errors {:errors (or (:errors step-errors)
+ (when (sequential? step-errors) step-errors))}))
(when (not= (first (steps linear-wizard))
(step-key step))
(when validation-route
@@ -172,7 +168,7 @@
(com/modal-card-advanced
{"@keydown.enter.prevent.stop" "if ($refs.next ) {$refs.next.click()}"
:class (str
- (or width-height-class " md:w-[750px] md:h-[600px] ")
+ (or width-height-class " md:w-[750px] md:h-[600px] ")
" w-full h-full
group-[.forward]/transition:htmx-swapping:opacity-0
group-[.forward]/transition:htmx-swapping:-translate-x-1/4
@@ -261,7 +257,7 @@
:oob (or oob []))))
(def next-handler
-
+
(-> (fn [{:keys [wizard] :as request}]
(let [current-step (get-current-step wizard)]
(if (satisfies? CustomNext current-step)
@@ -361,8 +357,6 @@
(render-wizard wizard request)])
(get query-params :replace-modal) (assoc-in [:headers "hx-trigger"] "modalswap")))
-
-
(defn wrap-init-multi-form-state [handler get-multi-form-state]
(->
(fn init-multi-form [request]
diff --git a/src/clj/auto_ap/ssr/components/page.clj b/src/clj/auto_ap/ssr/components/page.clj
index ba2efea1..b7057b66 100644
--- a/src/clj/auto_ap/ssr/components/page.clj
+++ b/src/clj/auto_ap/ssr/components/page.clj
@@ -6,16 +6,15 @@
[config.core :refer [env]]
[hiccup2.core :as hiccup]))
-(defn page- [{:keys [nav page-specific client clients client-selection identity app-params request] :or {app-params {}} } & children]
- [:div#app { "@notification.document" "notificationDetails=event.detail.value; showNotification=true"
+(defn page- [{:keys [nav page-specific client clients client-selection identity app-params request] :or {app-params {}}} & children]
+ [:div#app {"@notification.document" "notificationDetails=event.detail.value; showNotification=true"
:x-data (hx/json {:leftNavShow true
:showError false
:errorDetails ""
:showNotification false
:notificationDetails ""})
- "@htmx:response-error.camel" "errorDetails = $event.detail.xhr.response; showError=true;"
- }
+ "@htmx:response-error.camel" "errorDetails = $event.detail.xhr.response; showError=true;"}
(navbar- {:client-selection client-selection
:clients clients
:client client
@@ -29,33 +28,32 @@
:page-specific page-specific})
[:div#main-content {:class "relative w-full h-full overflow-y-auto px-4 bg-gray-100 dark:bg-gray-900 min-h-content lg:pl-64"
":class" "leftNavShow ? 'lg:pl-64' : ''"
- :x-effect "leftNavShow ? $el.classList.add('lg:pl-64') : $el.classList.remove('lg:pl-64')"
- }
+ :x-effect "leftNavShow ? $el.classList.add('lg:pl-64') : $el.classList.remove('lg:pl-64')"}
[:div#notification-holder
- [:div.fixed.top-0.right-0.left-0.z-30.mx-auto.max-w-screen-lg.w-screen-lg.my-0.pt-8.rounded-lg {:x-show "showNotification" }
+ [:div.fixed.top-0.right-0.left-0.z-30.mx-auto.max-w-screen-lg.w-screen-lg.my-0.pt-8.rounded-lg {:x-show "showNotification"}
[:div.relative
[:button.absolute.right-2.top-2.w-6.h-6.z-50.text-blue-400
- { "@click" "showNotification=false"}
+ {"@click" "showNotification=false"}
svg/filled-x]]
-
+
[:div.m-4.overflow-auto.z-30.flex.center-items.justify-center.text-blue-800.bg-blue-50.dark:bg-gray-800.dark:text-blue-400.border-blue-300.rounded-lg.border.max-h-96
{:x-show "showNotification"
- "x-transition:enter" "transition duration-300 transform ease-in-out"
- "x-transition:enter-start" "opacity-0 translate-y-full"
- "x-transition:enter-end" "opacity-100 translate-y-0"
- "x-transition:leave" "transition duration-300 transform ease-in-out"
- "x-transition:leave-start" "opacity-100 translate-y-0"
- "x-transition:leave-end" "opacity-0 translate-y-full"}
-
+ "x-transition:enter" "transition duration-300 transform ease-in-out"
+ "x-transition:enter-start" "opacity-0 translate-y-full"
+ "x-transition:enter-end" "opacity-100 translate-y-0"
+ "x-transition:leave" "transition duration-300 transform ease-in-out"
+ "x-transition:leave-start" "opacity-100 translate-y-0"
+ "x-transition:leave-end" "opacity-0 translate-y-full"}
+
[:div {:class "p-4 text-lg w-full" :role "alert"}
[:div.text-sm
[:pre#notification-details.text-xs {:x-html "notificationDetails"}]]]]]]
- [:div {:x-show "showError"
- :x-init ""}
+ [:div {:x-show "showError"
+ :x-init ""}
[:div.fixed.top-0.right-0.left-0.z-30.mx-auto.max-w-screen-lg.w-screen-lg.my-0.pt-8.rounded-lg
[:div.relative
[:button.absolute.right-2.top-2.w-6.h-6.z-50.text-red-600
- { "@click" "showError=false"}
+ {"@click" "showError=false"}
svg/filled-x]]
[:div.m-4.overflow-auto.z-30.flex.center-items.justify-center.text-red-800.bg-red-50.dark:bg-gray-800.dark:text-red-400.border-red-300.rounded-lg.border.max-h-96
@@ -63,7 +61,7 @@
"x-transition:enter" "transition duration-300"
"x-transition:enter-start" "opacity-0"
"x-transition:enter-end" "opacity-100"}
-
+
[:div {:class "p-4 mb-4 text-lg w-full" :role "alert"}
[:div.inline-block.w-8.h-8.mr-2 svg/alert]
[:span.font-medium "Oh, drat! An unexpected error has occurred."]
@@ -73,6 +71,4 @@
[:pre#error-details.text-xs {:x-show "expandError" :x-text "errorDetails"}]]]]]]
(into
[:div.p-4]
- children)]]
-
- ])
+ children)]]])
diff --git a/src/clj/auto_ap/ssr/components/paginator.clj b/src/clj/auto_ap/ssr/components/paginator.clj
index 21dfe445..cf2c341c 100644
--- a/src/clj/auto_ap/ssr/components/paginator.clj
+++ b/src/clj/auto_ap/ssr/components/paginator.clj
@@ -10,7 +10,7 @@
x
(> y z)
z
- :else
+ :else
y))
(def elipsis-button
@@ -24,42 +24,41 @@
current-page (long (Math/floor (/ start per-page)))
first-page-button (bound 0 (- current-page buttons-before) (- total-pages max-buttons))
all-buttons (into [] (for [x (range total-pages)]
- [:li
+ [:li
[:a (-> (a-params x)
- (update
- :class #(cond-> %
- true (str " flex items-center justify-center px-3 py-2 text-sm leading-tight border ")
+ (update
+ :class #(cond-> %
+ true (str " flex items-center justify-center px-3 py-2 text-sm leading-tight border ")
- (= current-page x)
- (str " text-primary-600 bg-primary-50 border-primary-300 hover:bg-primary-100 hover:text-primary-700 dark:border-gray-700 dark:bg-gray-700 dark:text-white")
+ (= current-page x)
+ (str " text-primary-600 bg-primary-50 border-primary-300 hover:bg-primary-100 hover:text-primary-700 dark:border-gray-700 dark:bg-gray-700 dark:text-white")
- (not= current-page x)
- (str " text-gray-500 bg-white border-gray-300 hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white")))
+ (not= current-page x)
+ (str " text-gray-500 bg-white border-gray-300 hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white")))
(assoc :href "#"))
[:div.htmx-indicator.flex.items-center
(svg/spinner {:class "inline w-4 h-4 text-black"})]
- [:div.htmx-indicator-hidden
+ [:div.htmx-indicator-hidden
(inc x)]]]))
-
last-page-button (Math/min (long total-pages) (long (+ max-buttons first-page-button)))
extended-last-page-button (when (not= last-page-button total-pages)
(list
- elipsis-button
- (last all-buttons)))
+ elipsis-button
+ (last all-buttons)))
extended-first-page-button (when (not= first-page-button 0)
(list
- (first all-buttons)
- elipsis-button))]
+ (first all-buttons)
+ elipsis-button))]
[:nav.flex.items-center.space-x-3
[:span.text-sm.text-gray-500 "Per page"]
(inputs/select- (merge per-page-params
{:options [[25 "25"]
- [50 "50"]
- [100 "100"]
- [200 "200"]]
+ [50 "50"]
+ [100 "100"]
+ [200 "200"]]
:value per-page
:name "per-page"}))
[:ul {:class "inline-flex items-stretch -space-x-px"}
diff --git a/src/clj/auto_ap/ssr/components/periods.clj b/src/clj/auto_ap/ssr/components/periods.clj
index d7aa4217..fa725462 100644
--- a/src/clj/auto_ap/ssr/components/periods.clj
+++ b/src/clj/auto_ap/ssr/components/periods.clj
@@ -1,4 +1,4 @@
-(ns auto-ap.ssr.components.periods
+(ns auto-ap.ssr.components.periods
(:require
[auto-ap.ssr.components.buttons :as buttons]
[auto-ap.ssr.components.inputs :as inputs]
@@ -19,7 +19,7 @@
(atime/unparse-local atime/normal-date))})
:x-effect "console.log('periods are', periods)"
- :x-init "$watch('periods', ds => source_date= ds.length > 0 ? ds[0].end : null)" }
+ :x-init "$watch('periods', ds => source_date= ds.length > 0 ? ds[0].end : null)"}
[:template {:x-for "(v,n) in periods" ":key" "n"}
[:div
[:input {:type "hidden"
@@ -29,62 +29,61 @@
":name" "'periods[' + n + '][end]'"
:x-model "v.end"}]]]
(buttons/a-button- {"x-tooltip.on.click.theme.dropdown.placement.bottom.interactive" "{content: ()=> $refs.tooltip.innerHTML, allowHTML: true, appendTo: $root}"
- :indicator? false}
- [:template {:x-if "periods.length == 0"}
- [:span.text-left.text-gray-400 "None selected"]]
- [:template {:x-if "periods.length < 3 && periods.length > 0"}
- [:span.inline-flex.gap-2
- [:template {:x-for "p in periods"}
- (tags/pill- {:color :secondary}
- [:span {:x-text "p.start"}]
- " - "
- [:span {:x-text "p.end"}])]]]
- [:template {:x-if "periods.length >= 3"}
- (tags/pill- {:color :secondary}
- [:span {:x-text "periods.length"}]
- " periods selected")]
- [:div {:class "w-3 h-3 m-1 inline ml-1 justify-self-end text-gray-500 self-center"}
- svg/drop-down])
+ :indicator? false}
+ [:template {:x-if "periods.length == 0"}
+ [:span.text-left.text-gray-400 "None selected"]]
+ [:template {:x-if "periods.length < 3 && periods.length > 0"}
+ [:span.inline-flex.gap-2
+ [:template {:x-for "p in periods"}
+ (tags/pill- {:color :secondary}
+ [:span {:x-text "p.start"}]
+ " - "
+ [:span {:x-text "p.end"}])]]]
+ [:template {:x-if "periods.length >= 3"}
+ (tags/pill- {:color :secondary}
+ [:span {:x-text "periods.length"}]
+ " periods selected")]
+ [:div {:class "w-3 h-3 m-1 inline ml-1 justify-self-end text-gray-500 self-center"}
+ svg/drop-down])
[:template {:x-ref "tooltip"}
[:div.p-4.gap-2 {:class "bg-gray-100 dark:bg-gray-600 rounded-lg shadow-2xl w-max z-50 ring-1 p-4 w-[700px] "}
[:div.flex.flex-col.gap-2
(tabs/tabs-
- {:tabs [{:name "Quick"
- :content [:div.flex.flex.gap-2
- (inputs/calendar-input- {:placeholder "12/21/2020" :x-model "source_date"})
- [:div.flex.flex-col.gap-2
- (buttons/a-button- {"@click" "periods=getFourWeekPeriodsPeriods(source_date)"} [:span "13 periods, ending "
- [:span {:x-text "source_date"}]])
- (buttons/a-button- {"@click" "periods=[calendarYearPeriod(source_date)]"} [:span "Calendar year ("
- [:span {:x-text "parseMMDDYYYY(source_date).getFullYear()"}]
- ")"])
- (buttons/a-button- {"@click" "periods=getTwelveCalendarMonthsPeriods(source_date)"} [:span "12 months, ending "
- [:span {:x-text "parseMMDDYYYY(source_date).toLocaleString('default', { month: 'long' })"}]])
- [:hr {:class "h-px my-1 bg-gray-200 border-0 dark:bg-gray-700"} ]
- (buttons/a-button- {"@click" "periods=getLastMonthPeriods()"} "Last Month")
- (buttons/a-button- {"@click" "periods=getMonthToDatePeriods()"} "Month to date")
- (buttons/a-button- {"@click" "periods=getYearToDatePeriods()"} "Year to date")
- (buttons/a-button- {"@click" "periods=[]"} "Clear")]]}
- {:name "Advanced"
- :content [:div.flex.gap-4 {:class "overflow-hidden max-h-[300px]"
- :x-data (hx/json {:calendarTarget "0"
- :calendarWhich "start"})
- "@change-date.camel" "$el.querySelectorAll('.text-inputs.' + calendarWhich)[calendarTarget].focus()"}
- (inputs/calendar-input- {:x-model "periods[calendarTarget][calendarWhich]"})
- [:div.flex.flex-col.gap-4.p-2
- [:div.overflow-y-scroll.flex.flex-col.gap-4
- [:template {:x-for "(p, i) in periods" ":key" "i"}
- [:div.flex.gap-4.
- (inputs/text-input- { :class "text-inputs start" :x-model "periods[i].start" "@focus" "calendarTarget =i; calendarWhich='start'" })
- (inputs/text-input- { :class "text-inputs end" :x-model "periods[i].end" "@focus" "calendarTarget =i; calendarWhich='end'"})
- (buttons/a-icon-button- {"@click.prevent.stop" "periods=periods.filter((_, i2) => i !== i2); calendarTarget=0"} svg/x)]
- #_(com/pill {:color :secondary}
- [:span {:x-text "p.start"}]
- " - "
- [:span {:x-text "p.end"}])]]
- (buttons/button- {"@click.prevent.stop" "periods.push({start: '', end: ''}); calendarTarget=0" :class "w-32"} "Add new period")]
- ]}]
- :active "Quick"}) ]]]])
+ {:tabs [{:name "Quick"
+ :content [:div.flex.flex.gap-2
+ (inputs/calendar-input- {:placeholder "12/21/2020" :x-model "source_date"})
+ [:div.flex.flex-col.gap-2
+ (buttons/a-button- {"@click" "periods=getFourWeekPeriodsPeriods(source_date)"} [:span "13 periods, ending "
+ [:span {:x-text "source_date"}]])
+ (buttons/a-button- {"@click" "periods=[calendarYearPeriod(source_date)]"} [:span "Calendar year ("
+ [:span {:x-text "parseMMDDYYYY(source_date).getFullYear()"}]
+ ")"])
+ (buttons/a-button- {"@click" "periods=getTwelveCalendarMonthsPeriods(source_date)"} [:span "12 months, ending "
+ [:span {:x-text "parseMMDDYYYY(source_date).toLocaleString('default', { month: 'long' })"}]])
+ [:hr {:class "h-px my-1 bg-gray-200 border-0 dark:bg-gray-700"}]
+ (buttons/a-button- {"@click" "periods=getLastMonthPeriods()"} "Last Month")
+ (buttons/a-button- {"@click" "periods=getMonthToDatePeriods()"} "Month to date")
+ (buttons/a-button- {"@click" "periods=getYearToDatePeriods()"} "Year to date")
+ (buttons/a-button- {"@click" "periods=[]"} "Clear")]]}
+ {:name "Advanced"
+ :content [:div.flex.gap-4 {:class "overflow-hidden max-h-[300px]"
+ :x-data (hx/json {:calendarTarget "0"
+ :calendarWhich "start"})
+ "@change-date.camel" "$el.querySelectorAll('.text-inputs.' + calendarWhich)[calendarTarget].focus()"}
+ (inputs/calendar-input- {:x-model "periods[calendarTarget][calendarWhich]"})
+ [:div.flex.flex-col.gap-4.p-2
+ [:div.overflow-y-scroll.flex.flex-col.gap-4
+ [:template {:x-for "(p, i) in periods" ":key" "i"}
+ [:div.flex.gap-4.
+ (inputs/text-input- {:class "text-inputs start" :x-model "periods[i].start" "@focus" "calendarTarget =i; calendarWhich='start'"})
+ (inputs/text-input- {:class "text-inputs end" :x-model "periods[i].end" "@focus" "calendarTarget =i; calendarWhich='end'"})
+ (buttons/a-icon-button- {"@click.prevent.stop" "periods=periods.filter((_, i2) => i !== i2); calendarTarget=0"} svg/x)]
+ #_(com/pill {:color :secondary}
+ [:span {:x-text "p.start"}]
+ " - "
+ [:span {:x-text "p.end"}])]]
+ (buttons/button- {"@click.prevent.stop" "periods.push({start: '', end: ''}); calendarTarget=0" :class "w-32"} "Add new period")]]}]
+ :active "Quick"})]]]])
(defn dates-dropdown- [{:keys [value name]}]
[:div {:x-data (hx/json {:dates (map #(atime/unparse-local % atime/normal-date) value)})}
@@ -122,16 +121,16 @@
(buttons/a-button- {"@click" "dates=[]"} "Clear")]]}
{:name "Advanced oooo"
:content [:div.flex.gap-4 {:class "overflow-hidden max-h-[300px]"
- :x-data (hx/json {:calendarTarget "0" })
+ :x-data (hx/json {:calendarTarget "0"})
"@change-date.camel" "$el.querySelectorAll('.text-inputs')[calendarTarget].focus();"}
- (inputs/calendar-input- {:x-model "dates[calendarTarget]" })
+ (inputs/calendar-input- {:x-model "dates[calendarTarget]"})
[:div.flex.flex-col.gap-4.p-2
[:div.overflow-y-scroll.flex.flex-col.gap-4
[:template {:x-for "(p, i) in dates" ":key" "i"}
[:div.flex.gap-4.
- (inputs/text-input- {:x-model "dates[i]"
+ (inputs/text-input- {:x-model "dates[i]"
"@focus" "calendarTarget =i; "
:class "text-inputs"})
- (buttons/a-icon-button- {"@click.prevent.stop" "dates=dates.filter((_, i2) => i !== i2); calendarTarget=0"} svg/x)] ]]
+ (buttons/a-icon-button- {"@click.prevent.stop" "dates=dates.filter((_, i2) => i !== i2); calendarTarget=0"} svg/x)]]]
(buttons/button- {"@click.prevent.stop" "dates.push(null); calendarTarget=0" :class "w-32"} "Add new period")]]}]
:active "Quick"})]]]])
\ No newline at end of file
diff --git a/src/clj/auto_ap/ssr/components/tabs.clj b/src/clj/auto_ap/ssr/components/tabs.clj
index 116206f6..03fe2315 100644
--- a/src/clj/auto_ap/ssr/components/tabs.clj
+++ b/src/clj/auto_ap/ssr/components/tabs.clj
@@ -1,28 +1,26 @@
-(ns auto-ap.ssr.components.tabs
+(ns auto-ap.ssr.components.tabs
(:require
[auto-ap.ssr.hx :as hx]))
(defn tabs- [{:keys [tabs active]}]
[:div.flex.flex-col.gap-2 {:x-data (hx/json {:activeTab active})}
- [:div {:class "text-sm font-medium text-center text-gray-500 border-b border-gray-200 dark:text-gray-400 dark:border-gray-700" }
+ [:div {:class "text-sm font-medium text-center text-gray-500 border-b border-gray-200 dark:text-gray-400 dark:border-gray-700"}
[:ul {:class "flex flex-wrap -mb-px"}
(for [tab tabs]
[:li {:class "me-2"}
[:a {:href "#"
:x-data (hx/json {:tabName (:name tab)})
":data-active" (format "activeTab==tabName")
- "@click" (format "activeTab=tabName" )
- :class "inline-block data-[active]:text-blue-600 data-[active]:border-blue-600 p-4 border-b-2 rounded-t-lg hover:text-gray-600 hover:border-gray-300 dark:hover:text-gray-300"}
+ "@click" (format "activeTab=tabName")
+ :class "inline-block data-[active]:text-blue-600 data-[active]:border-blue-600 p-4 border-b-2 rounded-t-lg hover:text-gray-600 hover:border-gray-300 dark:hover:text-gray-300"}
(:name tab)]])
-
-
-
+
#_[:li
[:a {:class "inline-block p-4 text-gray-400 rounded-t-lg cursor-not-allowed dark:text-gray-500"} "Disabled"]]]]
(for [tab tabs]
[:div {:x-data (hx/json {:tabName (:name tab)})
- :x-show (format "activeTab==tabName")
+ :x-show (format "activeTab==tabName")
"x-transition:enter" "transition-opacity duration-300"
- "x-transition:enter-start" "opacity-0"
- "x-transition:enter-end" "opacity-100"}
- (:content tab) ])])
\ No newline at end of file
+ "x-transition:enter-start" "opacity-0"
+ "x-transition:enter-end" "opacity-100"}
+ (:content tab)])])
\ No newline at end of file
diff --git a/src/clj/auto_ap/ssr/components/tags.clj b/src/clj/auto_ap/ssr/components/tags.clj
index 7d1c0119..ee40071b 100644
--- a/src/clj/auto_ap/ssr/components/tags.clj
+++ b/src/clj/auto_ap/ssr/components/tags.clj
@@ -1,7 +1,6 @@
(ns auto-ap.ssr.components.tags
(:require [auto-ap.ssr.hiccup-helper :as hh]))
-
(defn pill- [params & children]
(into
[:span (cond-> params
@@ -23,7 +22,6 @@
(defn badge- [params & children]
[:div (merge params {:class (-> (hh/add-class "absolute inline-flex items-center z-10 justify-center w-6 h-6 text-xs font-black text-white
border-3 border-white rounded-full -top-2 -right-2 dark:border-gray-900"
- (:class params)
- )
+ (:class params))
(hh/add-class (or (some-> (:color params) (#(str "bg-" % "-300")))
"bg-red-300")))}) children])
diff --git a/src/clj/auto_ap/ssr/components/timeline.clj b/src/clj/auto_ap/ssr/components/timeline.clj
index 1e78553f..a12e5265 100644
--- a/src/clj/auto_ap/ssr/components/timeline.clj
+++ b/src/clj/auto_ap/ssr/components/timeline.clj
@@ -30,14 +30,14 @@
(if active?
[:li {:class "flex items-center text-primary-600 font-medium dark:text-primary-500"}
[:span {:class "flex items-center justify-center w-5 h-5 mr-2 text-xs border-2 border-primary-600 rounded-full shrink-0 dark:border-primary-500"}]
- children ]
+ children]
[:li {:class (cond-> "flex items-center"
(not visited?) (hh/add-class "text-gray-400"))}
[:span {:class "flex items-center justify-center w-5 h-5 mr-2 text-xs border border-gray-500 rounded-full shrink-0 dark:border-gray-400"}
(when visited?
[:svg {:class "w-3 h-3 text-primary-600 dark:text-primary-500", :aria-hidden "true", :xmlns "http://www.w3.org/2000/svg", :fill "none", :viewbox "0 0 16 12"}
[:path {:stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "2", :d "M1 5.917 5.724 10.5 15 1.5"}]])]
- children ]))
+ children]))
(defn vertical-timeline [params & children]
[:ol {:class (hh/add-class "flex flex-col items-start space-y-2 text-xs text-center text-gray-500 bg-gray-100 dark:text-gray-400 sm:text-base dark:bg-gray-800 sm:space-y-4 px-2"
diff --git a/src/clj/auto_ap/ssr/components/user_dropdown.clj b/src/clj/auto_ap/ssr/components/user_dropdown.clj
index 8b18e533..881a7022 100644
--- a/src/clj/auto_ap/ssr/components/user_dropdown.clj
+++ b/src/clj/auto_ap/ssr/components/user_dropdown.clj
@@ -10,15 +10,14 @@
[:div {:class "flex items-center ml-3 mr-10"}
[:div
[:button#user-menu-button {:type "button", :class "flex text-sm bg-gray-800 rounded-full focus:ring-4 focus:ring-gray-300 dark:focus:ring-gray-600", :aria-expanded "false"
- "@click" "$tooltip($refs.tooltip, {content: ()=>$refs.tooltip.innerHTML, theme: $store.darkMode.on ? 'dark' : 'light', allowHTML: true, interactive:true})"
- }
+ "@click" "$tooltip($refs.tooltip, {content: ()=>$refs.tooltip.innerHTML, theme: $store.darkMode.on ? 'dark' : 'light', allowHTML: true, interactive:true})"}
[:span {:class "sr-only"} "Open user menu"]
[:img {:class "w-8 h-8 rounded-full", :src (pull-attr (dc/db conn) :user/profile-image-url (:db/id identity)) :alt "user photo" :referrerpolicy "no-referrer"}]]]
[:template {:class ""
- :x-ref "tooltip"}
+ :x-ref "tooltip"}
[:div {:class "px-4 py-3", :role "none"}
[:p {:class "text-sm text-gray-900 dark:text-white", :role "none"} (:user/name identity)]
- [:p {:class "text-sm font-medium text-gray-900 truncate dark:text-gray-300", :role "none"} (pull-attr (dc/db conn) :user/email (:db/id identity))] ]
+ [:p {:class "text-sm font-medium text-gray-900 truncate dark:text-gray-300", :role "none"} (pull-attr (dc/db conn) :user/email (:db/id identity))]]
[:ul {:class "py-1", :role "none"}
[:li
[:a {:href (bidi/path-for ssr-routes/only-routes :company), :class "block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-600 dark:hover:text-white", :role "menuitem"} "My Company"]]
@@ -27,7 +26,7 @@
:class "block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-600 dark:hover:text-white", :role "menuitem"} "Admin"])
[:li
[:a {:href "#", :class "block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-600 dark:hover:text-white", :role "menuitem"
- "@click.prevent" "$store.darkMode.toggle()" }
+ "@click.prevent" "$store.darkMode.toggle()"}
"Night Mode"]]
[:li
- [:a {:href "/logout", :class "block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-600 dark:hover:text-white", :role "menuitem"} "Sign out"]]]] ])
+ [:a {:href "/logout", :class "block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-600 dark:hover:text-white", :role "menuitem"} "Sign out"]]]]])
diff --git a/src/clj/auto_ap/ssr/core.clj b/src/clj/auto_ap/ssr/core.clj
index 70e993a2..e8b39ef1 100644
--- a/src/clj/auto_ap/ssr/core.clj
+++ b/src/clj/auto_ap/ssr/core.clj
@@ -2,7 +2,7 @@
(:require [auto-ap.permissions :refer [wrap-must]]
[auto-ap.routes.ezcater-xls :as ezcater-xls]
[auto-ap.routes.utils
- :refer [wrap-admin wrap-client-redirect-unauthenticated wrap-secure]]
+ :refer [wrap-admin wrap-client-redirect-unauthenticated wrap-secure]]
[auto-ap.ssr.account :as account]
[auto-ap.ssr.admin :as admin]
[auto-ap.ssr.not-found :as not-found]
@@ -43,7 +43,6 @@
;; from auto-ap.ssr-routes, because they're shared
-
(def key->handler
(-> {:logout auth/logout
:login auth/login
@@ -86,17 +85,17 @@
(into company-1099/key->handler)
(into invoice/key->handler)
(into import-batch/key->handler)
- (into pos-sales/key->handler)
- (into pos-expected-deposits/key->handler)
- (into pos-tenders/key->handler)
- (into pos-cash-drawer-shifts/key->handler)
- (into pos-refunds/key->handler)
- (into pos-sales-summaries/key->handler)
- (into users/key->handler)
- (into admin-accounts/key->handler)
- (into admin-excel-invoices/key->handler)
- (into admin/key->handler)
- (into admin-jobs/key->handler)
+ (into pos-sales/key->handler)
+ (into pos-expected-deposits/key->handler)
+ (into pos-tenders/key->handler)
+ (into pos-cash-drawer-shifts/key->handler)
+ (into pos-refunds/key->handler)
+ (into pos-sales-summaries/key->handler)
+ (into users/key->handler)
+ (into admin-accounts/key->handler)
+ (into admin-excel-invoices/key->handler)
+ (into admin/key->handler)
+ (into admin-jobs/key->handler)
(into admin-vendors/key->handler)
(into admin-clients/key->handler)
(into admin-rules/key->handler)
diff --git a/src/clj/auto_ap/ssr/dashboard.clj b/src/clj/auto_ap/ssr/dashboard.clj
index 4dc55315..13101c6e 100644
--- a/src/clj/auto_ap/ssr/dashboard.clj
+++ b/src/clj/auto_ap/ssr/dashboard.clj
@@ -1,4 +1,4 @@
-(ns auto-ap.ssr.dashboard
+(ns auto-ap.ssr.dashboard
(:require
[auto-ap.datomic :refer [conn]]
[auto-ap.graphql.ledger :refer [get-profit-and-loss-raw]]
@@ -25,11 +25,11 @@
[hiccup.util :as hu]))
(defn bank-accounts-card [request]
- (html-response
+ (html-response
(com/card {:class "h-full"}
[:div.p-4.h-full
[:h1.text-2xl.font-bold "Bank Accounts"]
- [:div (hx/htmx-transition-appear {:class "h-full overflow-scroll" })
+ [:div (hx/htmx-transition-appear {:class "h-full overflow-scroll"})
(for [c (:valid-trimmed-client-ids request)
b (:client/bank-accounts (dc/pull (dc/db conn) '[{:client/bank-accounts
@@ -58,43 +58,42 @@
(#(str "Synced " %)))]
#_(when-let [n (cond (-> b :bank-account/intuit-bank-account)
- "Intuit"
- (-> b :bank-account/yodlee-account)
- "Yodlee"
- (-> b :bank-account/plaid-account)
- "Plaid"
- :else
- nil)]
- (list
- [:div (str n " Balance")]
- [:div.text-right (format "$%,.2f" (or (-> b :bank-account/intuit-bank-account :intuit-bank-account/current-balance)
- (-> b :bank-account/yodlee-account :yodlee-account/available-balance)
- (-> b :bank-account/plaid-account :plaid-account/balance)
- 0.0))]
-
+ "Intuit"
+ (-> b :bank-account/yodlee-account)
+ "Yodlee"
+ (-> b :bank-account/plaid-account)
+ "Plaid"
+ :else
+ nil)]
+ (list
+ [:div (str n " Balance")]
+ [:div.text-right (format "$%,.2f" (or (-> b :bank-account/intuit-bank-account :intuit-bank-account/current-balance)
+ (-> b :bank-account/yodlee-account :yodlee-account/available-balance)
+ (-> b :bank-account/plaid-account :plaid-account/balance)
+ 0.0))]
- [:div.text-xs.text-gray-400.text-right (or (some-> (:bank-account/intuit-bank-account b)
- (:intuit-bank-account/last-synced)
- (atime/unparse-local atime/standard-time)
- (#(str "Synced " %)))
- (some-> (:bank-account/yodlee-account b)
- (:yodlee-account/last-synced)
- (atime/unparse-local atime/standard-time)
- (#(str "Synced " %)))
- (some-> (:bank-account/plaid-account b)
- (:plaid-account/last-synced)
- (atime/unparse-local atime/standard-time)
- (#(str "Synced " %))))]
- (when-let [pending-balance (-> b :bank-account/yodlee-account :yodlee-account/pending-balance)]
- (list
- [:div (str n " Pending Txs")]
- [:div.text-right (format "$%,.2f" pending-balance)]))
- [:div.inline-flex.justify-end.text-xs.text-gray-400.it]))
+ [:div.text-xs.text-gray-400.text-right (or (some-> (:bank-account/intuit-bank-account b)
+ (:intuit-bank-account/last-synced)
+ (atime/unparse-local atime/standard-time)
+ (#(str "Synced " %)))
+ (some-> (:bank-account/yodlee-account b)
+ (:yodlee-account/last-synced)
+ (atime/unparse-local atime/standard-time)
+ (#(str "Synced " %)))
+ (some-> (:bank-account/plaid-account b)
+ (:plaid-account/last-synced)
+ (atime/unparse-local atime/standard-time)
+ (#(str "Synced " %))))]
+ (when-let [pending-balance (-> b :bank-account/yodlee-account :yodlee-account/pending-balance)]
+ (list
+ [:div (str n " Pending Txs")]
+ [:div.text-right (format "$%,.2f" pending-balance)]))
+ [:div.inline-flex.justify-end.text-xs.text-gray-400.it]))
#_[:div.inline-flex.justify-between.items-baseline]]])]])))
(defn sales-chart-card [request]
- (html-response
- (let [ totals
+ (html-response
+ (let [totals
(->> (dc/q '[:find ?sd (sum ?total)
:with ?e
:in $ [?clients ?start-date ?end-date]
@@ -113,7 +112,7 @@
[:canvas.w-full.h-full.p-8 {:x-data (hx/json {:chart nil
:labels (map first totals)
:data (map second totals)})
- :x-init
+ :x-init
"new Chart($el, {
type: 'bar',
data: {
@@ -136,8 +135,8 @@
});"}]]))))
(defn expense-pie-card [request]
- (html-response
- (let [ totals
+ (html-response
+ (let [totals
(->> (dc/q '[:find ?an (sum ?amt)
:with ?iea
:in $ [?clients ?start-date ?end-date]
@@ -179,19 +178,18 @@
});"}]]))))
(defn pnl-card [request]
- (html-response
- (com/card {:class "w-full h-full p-4"}
+ (html-response
+ (com/card {:class "w-full h-full p-4"}
[:h1.text-2xl.font-bold.text-gray-700
- "Profit and Loss, last month" ]
- (let [ data (<-graphql (get-profit-and-loss-raw (:valid-trimmed-client-ids request)
- [{:start (time/plus (time/now) (time/months -1))
- :end (time/now)}]))
+ "Profit and Loss, last month"]
+ (let [data (<-graphql (get-profit-and-loss-raw (:valid-trimmed-client-ids request)
+ [{:start (time/plus (time/now) (time/months -1))
+ :end (time/now)}]))
data (r/->PNLData {} (:accounts (first (:periods data))) {})
- sales (r/aggregate-accounts (r/filter-categories data [ :sales]))
- expenses (r/aggregate-accounts (r/filter-categories data [ :cogs :payroll :controllable :fixed-overhead :ownership-controllable ]))]
- (list
- #_(when (not= (count all-clients) (count clients))
- )
+ sales (r/aggregate-accounts (r/filter-categories data [:sales]))
+ expenses (r/aggregate-accounts (r/filter-categories data [:cogs :payroll :controllable :fixed-overhead :ownership-controllable]))]
+ (list
+ #_(when (not= (count all-clients) (count clients)))
[:canvas.w-full.h-full.p-8 {:x-data (hx/json {:chart nil
:labels [(format "Income $%,.2f" sales) (format "Expenses $%,.2f" expenses)]
:data [sales expenses]})
@@ -217,12 +215,12 @@
}
}
});"}]
- [:div
+ [:div
"Income: " (format "$%,.2f" sales)]
- [:div
+ [:div
"Expenses: " (format "$%,.2f" expenses)])))))
(defn tasks-card [request]
- (html-response
+ (html-response
(com/card {:class "w-full h-full p-4"}
[:h1.text-2xl.font-bold.text-gray-700
"Tasks"]
@@ -237,7 +235,7 @@
[(:valid-trimmed-client-ids request)
(coerce/to-date (time/with-time-at-start-of-day (time/plus (time/now) (time/years -1))))
nil]))
-
+
[uncategorized-transaction-count uncategorized-transaction-amount]
(first (dc/q '[:find (count ?e) (sum ?am)
:in $ [?clients ?start-date ?end-date]
@@ -248,25 +246,23 @@
[(:valid-trimmed-client-ids request)
(coerce/to-date (time/with-time-at-start-of-day (time/plus (time/now) (time/years -1))))
nil]))]
- (list
+ (list
(when (not= 0 (or unpaid-invoice-count 0))
[:div.bg-gray-50.rounded.p-4
- [:span "You have " (str unpaid-invoice-count) " unpaid invoices with an outstanding balance of " (format "$%,.2f" unpaid-invoice-amount) ". " ]
-
+ [:span "You have " (str unpaid-invoice-count) " unpaid invoices with an outstanding balance of " (format "$%,.2f" unpaid-invoice-amount) ". "]
+
(com/link {:href (hu/url (bidi.bidi/path-for ssr-routes/only-routes ::i-routes/unpaid-page)
- {:date-range "year"})
- }
-
- "Pay now")
- ])
+ {:date-range "year"})}
+
+ "Pay now")])
(when (not= 0 (or uncategorized-transaction-count 0))
[:div.bg-gray-50.rounded.p-4
- [:span "You have " (str uncategorized-transaction-count) " transactions needing your feedback. " ]
-
+ [:span "You have " (str uncategorized-transaction-count) " transactions needing your feedback. "]
+
(com/link {:href (str (bidi.bidi/path-for ssr-routes/only-routes ::transaction-routes/requires-feedback-page)
"?date-range="
- (url/url-encode (pr-str {:start (atime/unparse-local (time/plus (time/now) (time/years -1)) atime/iso-date) :end (atime/unparse-local (time/now) atime/iso-date)}))) }
-
+ (url/url-encode (pr-str {:start (atime/unparse-local (time/plus (time/now) (time/years -1)) atime/iso-date) :end (atime/unparse-local (time/now) atime/iso-date)})))}
+
"Review now")])))])))
(defn stub-card [params & children]
@@ -280,35 +276,33 @@
[:div.htmx-indicator (svg/spinner {:class "inline w-32 h-32 text-green-500"})]]))
(defn- page-contents [request]
- [:div.mb-8
- [:div {:class "grid grid-cols-1 lg:grid-cols-2 2xl:grid-cols-3 gap-4 mb-8"}
- [:div.h-96 (stub-card {:title "Expenses"
- :hx-get (bidi.bidi/path-for ssr-routes/only-routes ::d-routes/expense-card)
- :hx-trigger "load"} )]
- [:div.h-96
- (stub-card {:title "Tasks"
- :hx-get (bidi.bidi/path-for ssr-routes/only-routes ::d-routes/tasks-card)
- :hx-trigger "load"} )]
- [:div {:class " row-span-2 h-[49rem]"}
-(stub-card {:title "Bank Accounts"
- :hx-get (bidi.bidi/path-for ssr-routes/only-routes ::d-routes/bank-accounts-card)
- :hx-trigger "load"} )
- ]
-
- [:div.h-96
- (stub-card {:title "Gross Sales, last 14 days"
- :hx-get (bidi.bidi/path-for ssr-routes/only-routes ::d-routes/sales-card)
- :hx-trigger "load"})
- ]
- [:div.h-96
- (stub-card {:title "Profit and Loss, last month"
- :hx-get (bidi.bidi/path-for ssr-routes/only-routes ::d-routes/pnl-card)
- :hx-trigger "load"}) ]
- [:div.col-span-2.h-96
- (stub-card {:title "Expense breakdown"
- :hx-get (bidi.bidi/path-for ssr-routes/only-routes :company-expense-report-breakdown-card)
- :hx-trigger "load"} )]
- [:div]] ])
+ [:div.mb-8
+ [:div {:class "grid grid-cols-1 lg:grid-cols-2 2xl:grid-cols-3 gap-4 mb-8"}
+ [:div.h-96 (stub-card {:title "Expenses"
+ :hx-get (bidi.bidi/path-for ssr-routes/only-routes ::d-routes/expense-card)
+ :hx-trigger "load"})]
+ [:div.h-96
+ (stub-card {:title "Tasks"
+ :hx-get (bidi.bidi/path-for ssr-routes/only-routes ::d-routes/tasks-card)
+ :hx-trigger "load"})]
+ [:div {:class " row-span-2 h-[49rem]"}
+ (stub-card {:title "Bank Accounts"
+ :hx-get (bidi.bidi/path-for ssr-routes/only-routes ::d-routes/bank-accounts-card)
+ :hx-trigger "load"})]
+
+ [:div.h-96
+ (stub-card {:title "Gross Sales, last 14 days"
+ :hx-get (bidi.bidi/path-for ssr-routes/only-routes ::d-routes/sales-card)
+ :hx-trigger "load"})]
+ [:div.h-96
+ (stub-card {:title "Profit and Loss, last month"
+ :hx-get (bidi.bidi/path-for ssr-routes/only-routes ::d-routes/pnl-card)
+ :hx-trigger "load"})]
+ [:div.col-span-2.h-96
+ (stub-card {:title "Expense breakdown"
+ :hx-get (bidi.bidi/path-for ssr-routes/only-routes :company-expense-report-breakdown-card)
+ :hx-trigger "load"})]
+ [:div]]])
(defn page [request]
(base-page
@@ -334,12 +328,12 @@
"Dashboard"))
(def key->handler
- ( apply-middleware-to-all-handlers
- {::d-routes/page page
- ::d-routes/expense-card expense-pie-card
- ::d-routes/pnl-card pnl-card
- ::d-routes/sales-card sales-chart-card
- ::d-routes/bank-accounts-card bank-accounts-card
- ::d-routes/tasks-card tasks-card }
- (fn [h]
- (wrap-client-redirect-unauthenticated (wrap-admin h)))))
\ No newline at end of file
+ (apply-middleware-to-all-handlers
+ {::d-routes/page page
+ ::d-routes/expense-card expense-pie-card
+ ::d-routes/pnl-card pnl-card
+ ::d-routes/sales-card sales-chart-card
+ ::d-routes/bank-accounts-card bank-accounts-card
+ ::d-routes/tasks-card tasks-card}
+ (fn [h]
+ (wrap-client-redirect-unauthenticated (wrap-admin h)))))
\ No newline at end of file
diff --git a/src/clj/auto_ap/ssr/form_cursor.clj b/src/clj/auto_ap/ssr/form_cursor.clj
index 36b4f2e1..82f4d785 100644
--- a/src/clj/auto_ap/ssr/form_cursor.clj
+++ b/src/clj/auto_ap/ssr/form_cursor.clj
@@ -8,8 +8,6 @@
(def ^:dynamic *prev-cursor* nil)
(def ^:dynamic *current* nil)
-
-
(defmacro start-form [form-data errors & rest]
`(binding [*form-data* ~form-data
*form-errors* (or ~errors {})]
@@ -37,13 +35,13 @@
(defmacro with-field-default [field default & rest]
`(let [new-cursor# (get *current* ~field ~default)
new-cursor2# (if (not (deref new-cursor#))
- (do
- (cursor/transact! *current*
- (fn [c#]
- (assoc c# ~field ~default)))
- (get *current* ~field ~default))
-
- new-cursor#)]
+ (do
+ (cursor/transact! *current*
+ (fn [c#]
+ (assoc c# ~field ~default)))
+ (get *current* ~field ~default))
+
+ new-cursor#)]
(with-cursor new-cursor2#
~@rest)))
@@ -71,7 +69,6 @@
(and (sequential? errors)
(every? string? errors)))))
-
(defn cursor-map
([f] (cursor-map *current* f))
([cursor f]
diff --git a/src/clj/auto_ap/ssr/grid_page_helper.clj b/src/clj/auto_ap/ssr/grid_page_helper.clj
index 5c68ed3a..0af5b704 100644
--- a/src/clj/auto_ap/ssr/grid_page_helper.clj
+++ b/src/clj/auto_ap/ssr/grid_page_helper.clj
@@ -1,39 +1,37 @@
-(ns auto-ap.ssr.grid-page-helper
- (:require
- [auto-ap.graphql.utils :refer [extract-client-ids]]
- [auto-ap.logging :as alog]
- [auto-ap.query-params :as query-params]
- [auto-ap.routes.utils
- :refer [wrap-client-redirect-unauthenticated wrap-secure]]
- [auto-ap.ssr-routes :as ssr-routes]
- [auto-ap.ssr.components :as com]
- [auto-ap.ssr.hiccup-helper :as hh]
- [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 main-transformer]]
- [auto-ap.time :as atime]
- [bidi.bidi :as bidi]
- [cemerick.url :as url]
- [clojure.string :as str]
- [hiccup.util :as hu]
- [malli.core :as m]
- [malli.transform :as mt2]
- [taoensso.encore :refer [filter-vals]]
- [clojure.java.io :as io]
- [clojure.data.csv :as csv]))
-
-
+(ns auto-ap.ssr.grid-page-helper
+ (:require
+ [auto-ap.graphql.utils :refer [extract-client-ids]]
+ [auto-ap.logging :as alog]
+ [auto-ap.query-params :as query-params]
+ [auto-ap.routes.utils
+ :refer [wrap-client-redirect-unauthenticated wrap-secure]]
+ [auto-ap.ssr-routes :as ssr-routes]
+ [auto-ap.ssr.components :as com]
+ [auto-ap.ssr.hiccup-helper :as hh]
+ [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 main-transformer]]
+ [auto-ap.time :as atime]
+ [bidi.bidi :as bidi]
+ [cemerick.url :as url]
+ [clojure.string :as str]
+ [hiccup.util :as hu]
+ [malli.core :as m]
+ [malli.transform :as mt2]
+ [taoensso.encore :refer [filter-vals]]
+ [clojure.java.io :as io]
+ [clojure.data.csv :as csv]))
(defn row* [{:keys [check-box-warning? check-boxes?] :as gridspec} user entity {:keys [flash? delete-after-settle? request class] :as options}]
(let [cells (if check-boxes?
- [(com/data-grid-cell {:class "relative"}
+ [(com/data-grid-cell {:class "relative"}
(let [cb (com/checkbox {:name "id" :value ((:id-fn gridspec) entity)
:x-model "selected"})]
- (if (and check-box-warning? (check-box-warning? entity))
- (do
- [:div.bg-yellow-100.absolute.inset-0.flex.items-center.px-4.py-2
- [:div {:class "absolute inset-0 bg-yellow-50 z-0",
+ (if (and check-box-warning? (check-box-warning? entity))
+ (do
+ [:div.bg-yellow-100.absolute.inset-0.flex.items-center.px-4.py-2
+ [:div {:class "absolute inset-0 bg-yellow-50 z-0",
:style "background-image: linear-gradient(135deg, rgba(0, 0, 0, 0.1) 12.5%, transparent 12.5%, transparent 50%, rgba(0, 0, 0, 0.1) 50%, rgba(0, 0, 0, 0.1) 62.5%, transparent 62.5%, transparent);\n background-size: 10px 10px;"}]
@@ -63,7 +61,7 @@
(cond-> {:class (cond-> (or class "")
flash? (hh/add-class "live-added group"))
:data-id ((:id-fn gridspec) entity)}
- delete-after-settle?
+ delete-after-settle?
(assoc "@htmx:after-settle.camel" "setTimeout(() => $el.remove(), 400)"))
cells)))
@@ -89,7 +87,7 @@
[:div.h-4.w-4 svg/x]]]]))
"default sort"))
-(defn create-break-table-fn [break-table grid-spec ]
+(defn create-break-table-fn [break-table grid-spec]
(let [last (atom nil)]
(fn [request entity]
(let [break-table-value (break-table request entity)]
@@ -106,7 +104,6 @@
"desc")))
s)))
-
(defn table* [grid-spec user {{:keys [start per-page flash-id sort]} :parsed-query-params :as request}]
(alog/info ::TABLE-QP
:qp (:query-params request)
@@ -123,7 +120,7 @@
:raw? (:raw? grid-spec)
:title [:div.flex.gap-2 (if (string? (:title grid-spec))
(:title grid-spec)
- ((:title grid-spec) request)) ]
+ ((:title grid-spec) request))]
:route (:route grid-spec)
:root-params {:x-data (hx/json {:sort (sort->query sort)})
"x-hx-val:sort" "sort"}
@@ -154,11 +151,11 @@
"selected" "all-selected"))
:color :secondary-light}
[:div.w-4.h-4 svg/download])))
- :rows
- (let [break-table-fn (some-> grid-spec :break-table ( create-break-table-fn grid-spec))]
+ :rows
+ (let [break-table-fn (some-> grid-spec :break-table (create-break-table-fn grid-spec))]
(for [entity entities
row (if-let [break-table-row (when break-table-fn (break-table-fn request entity))]
-
+
[break-table-row (row* grid-spec user entity {:flash? (= flash-id ((:id-fn grid-spec) entity)) :request request})]
[(row* grid-spec user entity {:flash? (= flash-id ((:id-fn grid-spec) entity)) :request request})])]
row))
@@ -203,9 +200,6 @@
[])))
(com/data-grid-header {}))})))
-
-
-
(defn wrap-trim-client-ids [handler]
(fn trim-client-ids [request]
(let [valid-clients (extract-client-ids (:clients request)
@@ -218,8 +212,7 @@
set)]
(handler (assoc request :trimmed-clients valid-clients)))))
-
-(defn table-route [grid-spec & {:keys [push-url?] :or { push-url? true}}]
+(defn table-route [grid-spec & {:keys [push-url?] :or {push-url? true}}]
(cond-> (fn table [{:keys [identity] :as request}]
(html-response (table*
@@ -247,7 +240,7 @@
;; make it so that it rerenders the date range component, along with a hx-trigger change header
(defn csv-route [{:keys [fetch-page headers page->csv-entities]} & {:keys []}]
(cond-> (fn csv-route [{:keys [identity] :as request}]
-
+
(let [page-results (fetch-page (assoc-in request [:query-params :per-page] Long/MAX_VALUE))
csv-entities ((or page->csv-entities (fn [[entities]] entities)) page-results)
csv-content (with-open [i (java.io.StringWriter.)]
@@ -255,13 +248,13 @@
(into [(for [h headers
:when ((:render-for h #{:html :csv}) :csv)]
(:name h))]
- (for [e csv-entities ]
+ (for [e csv-entities]
(for [h headers
:when ((:render-for h #{:html :csv})
:csv)]
((or (:render-csv h) (comp str (:render h))) e)))))
(.toString i))]
-
+
{:headers {"Content-Type" "text/csv"}
:body csv-content}))
true (wrap-trim-client-ids)
@@ -285,7 +278,7 @@
:request request}
(apply com/breadcrumbs {} (:breadcrumbs grid-spec))
(when (:above-grid grid-spec)
- ( (:above-grid grid-spec) request))
+ ((:above-grid grid-spec) request))
[:div {:x-data (hx/json {:selected [] :all_selected false :type (:entity-name grid-spec)})
"x-on:copy" "if (selected.length > 0) {$clipboard(JSON.stringify({'type': type, 'selected': selected}))}"
"x-on:client-selected.document" "selected=[]; all_selected=false"
diff --git a/src/clj/auto_ap/ssr/hiccup_helper.clj b/src/clj/auto_ap/ssr/hiccup_helper.clj
index 812ffb63..5b74fbd2 100644
--- a/src/clj/auto_ap/ssr/hiccup_helper.clj
+++ b/src/clj/auto_ap/ssr/hiccup_helper.clj
@@ -4,7 +4,6 @@
[hiccup.util :as hu]
[clojure.set :as set]))
-
(defprotocol ClassHelper
(add-class [this add])
(remove-class [this remove])
@@ -70,7 +69,6 @@
(replace-tw (string->class-list this)
tw)))
-
(str (hiccup/html [:div {:class (-> "hello bryce hello-1 hello-2"
(replace-wildcard ["hello-" "b"] ["hi" "there"]))}]))
(str (hiccup/html [:div {:class (-> "p-1.5 "
diff --git a/src/clj/auto_ap/ssr/hx.clj b/src/clj/auto_ap/ssr/hx.clj
index 08a88a72..30df886c 100644
--- a/src/clj/auto_ap/ssr/hx.clj
+++ b/src/clj/auto_ap/ssr/hx.clj
@@ -4,7 +4,6 @@
[cheshire.generate :refer [add-encoder]]
[clojure.string :as str]))
-
(defn vals [m]
(cheshire/generate-string m))
@@ -15,18 +14,17 @@
(add-encoder jsfn jsf)
-
(defn json [m]
(let [starting-point (cheshire/generate-string m)]
(if (map? m)
- (reduce
- (fn [starting-point [k v]]
- (if (instance? jsfn v)
- (-> (str/replace starting-point (re-pattern (str "(?s)\"__" (.-name v) "__(.*?)__end__\"")) "$1" )
- (str/replace "\\n" "\n"))
- starting-point))
- starting-point
- m)
+ (reduce
+ (fn [starting-point [k v]]
+ (if (instance? jsfn v)
+ (-> (str/replace starting-point (re-pattern (str "(?s)\"__" (.-name v) "__(.*?)__end__\"")) "$1")
+ (str/replace "\\n" "\n"))
+ starting-point))
+ starting-point
+ m)
starting-point)))
(defn random-alpha-string []
@@ -67,7 +65,7 @@
alpine-disappear)
(dissoc params :data-key)))
-(defn alpine-mount-then-disappear [{:keys [data-key] :as params :or {data-key "show"}} ]
+(defn alpine-mount-then-disappear [{:keys [data-key] :as params :or {data-key "show"}}]
(merge (-> {:x-data (json {data-key true})
:x-init (format "$nextTick(() => %s=false)" (name data-key))
:x-show (name data-key)}
@@ -85,13 +83,12 @@
(format "\"%s\": $data.%s || ''" field alpine-field))
field->alpine-field)))))
-
+
(defn trigger-click-or-enter [m]
(assoc m :hx-trigger "click, keyup[keyCode==13]"))
(defn htmx-transition-appear [params]
- (-> params
+ (-> params
(update :class (fn [c]
(-> (or c "")
- (hh/add-class "opacity-100 transition htmx-added:opacity-0 duration-300")))))
- )
+ (hh/add-class "opacity-100 transition htmx-added:opacity-0 duration-300"))))))
diff --git a/src/clj/auto_ap/ssr/indicators.clj b/src/clj/auto_ap/ssr/indicators.clj
index 9b6dea16..1976630c 100644
--- a/src/clj/auto_ap/ssr/indicators.clj
+++ b/src/clj/auto_ap/ssr/indicators.clj
@@ -7,7 +7,7 @@
[clj-time.core :as t]))
(defn days-ago* [date]
- (if date
+ (if date
(let [start (c/to-date-time date)
today (t/now)]
@@ -33,4 +33,4 @@
{::route/days-ago (wrap-schema-enforce days-ago
:query-schema
[:map [:date {:optional false}
- clj-date-schema ]])})
\ No newline at end of file
+ clj-date-schema]])})
\ No newline at end of file
diff --git a/src/clj/auto_ap/ssr/invoice/common.clj b/src/clj/auto_ap/ssr/invoice/common.clj
index 3081deb0..e56549f6 100644
--- a/src/clj/auto_ap/ssr/invoice/common.clj
+++ b/src/clj/auto_ap/ssr/invoice/common.clj
@@ -3,7 +3,7 @@
(def default-read '[:db/id
:invoice/invoice-number
:invoice/total
- { :invoice/uploader [:user/name]}
+ {:invoice/uploader [:user/name]}
:invoice/outstanding-balance
:invoice/source-url
:invoice/location
diff --git a/src/clj/auto_ap/ssr/invoice/glimpse.clj b/src/clj/auto_ap/ssr/invoice/glimpse.clj
index 85572188..83576d2a 100644
--- a/src/clj/auto_ap/ssr/invoice/glimpse.clj
+++ b/src/clj/auto_ap/ssr/invoice/glimpse.clj
@@ -32,9 +32,9 @@
(def bucket-name (:data-bucket env))
(defn lookup [tx]
- (->> (:expense-documents tx)
+ (->> (:expense-documents tx)
(mapcat :summary-fields)
- (concat (->> tx :expense-documents ))
+ (concat (->> tx :expense-documents))
(map (fn [sf]
(-> sf
(update :label-detection dissoc :geometry)
@@ -53,22 +53,22 @@
(clojure.string/replace c #"\W+" " "))
(defn deduplicate [xs]
- (first
- (reduce
- (fn [[so-far seen-parsed?] [raw parsed]]
- (if (seen-parsed? parsed)
- [so-far seen-parsed?]
- [(conj so-far [raw parsed])
- (conj seen-parsed? parsed)]))
- [[] #{}]
- xs)))
+ (first
+ (reduce
+ (fn [[so-far seen-parsed?] [raw parsed]]
+ (if (seen-parsed? parsed)
+ [so-far seen-parsed?]
+ [(conj so-far [raw parsed])
+ (conj seen-parsed? parsed)]))
+ [[] #{}]
+ xs)))
(defn textract->textract-invoice [request id tx]
(let [lookup (lookup tx)
valid-client-ids (extract-client-ids (:clients request)
- (:client-id request)
- (when (:client-code request)
- [:client/code (:client-code request)]))
+ (:client-id request)
+ (when (:client-code request)
+ [:client/code (:client-code request)]))
total-options (->> (stack-rank #{"AMOUNT_DUE"} lookup)
(map (fn [t]
[t (some->> t
@@ -103,10 +103,10 @@
[(pull-attr (dc/db conn) :client/name c) c]))))
vendor-name-options (->> (stack-rank #{"VENDOR_NAME"} lookup)
(mapcat (fn [t]
- (for [m (->> (solr/query solr/impl "vendors" {"query" (format "name:(%s) ", t) "fields" "score, *"})
- (filter (fn [d] (> (:score d) 2.0)))
- (map (comp #(Long/parseLong %) :id)))]
- [t m])))
+ (for [m (->> (solr/query solr/impl "vendors" {"query" (format "name:(%s) ", t) "fields" "score, *"})
+ (filter (fn [d] (> (:score d) 2.0)))
+ (map (comp #(Long/parseLong %) :id)))]
+ [t m])))
(deduplicate))
date-options (->> (stack-rank #{"INVOICE_RECEIPT_DATE" "ORDER_DATE" "DELIVERY_DATE"} lookup)
(map (fn [t]
@@ -120,22 +120,22 @@
[t t]))
(deduplicate))]
#:textract-invoice
- {:db/id id
- :textract-status "SUCCEEDED"
- :total (first total-options)
- :total-options (seq total-options)
- :customer-identifier (first customer-identifier-options)
- :customer-identifier-options (seq customer-identifier-options)
- :location [nil ""]
- :vendor-name (first vendor-name-options)
- :vendor-name-options (seq vendor-name-options)
- :date (first date-options)
- :date-options (seq date-options)
- :invoice-number (first invoice-number-options)
- :invoice-number-options (seq invoice-number-options)}))
+ {:db/id id
+ :textract-status "SUCCEEDED"
+ :total (first total-options)
+ :total-options (seq total-options)
+ :customer-identifier (first customer-identifier-options)
+ :customer-identifier-options (seq customer-identifier-options)
+ :location [nil ""]
+ :vendor-name (first vendor-name-options)
+ :vendor-name-options (seq vendor-name-options)
+ :date (first date-options)
+ :date-options (seq date-options)
+ :invoice-number (first invoice-number-options)
+ :invoice-number-options (seq invoice-number-options)}))
(defn upload-form* []
- [:div
+ [:div
[:form.bg-blue-100.border-2.border-dashed.rounded-lg.border-blue-300.p-4.max-w-md.w-md.text-center.cursor-pointer
{:action (bidi/path-for ssr-routes/only-routes
:invoice-glimpse-upload)
@@ -144,7 +144,7 @@
"Drop an invoice here"]
[:script
(hiccup/raw
- "
+ "
invoice_dropzone = new Dropzone(\"#invoice\", {
success: function(file, response) {
window.location.href = file.xhr.responseURL;
@@ -154,14 +154,14 @@ invoice_dropzone = new Dropzone(\"#invoice\", {
}); ")]])
(defn customer-identifier-id->customer-identifier-client [[ci client]]
- (when client
+ (when client
(let [real-client (dc/pull (dc/db conn)
[:client/name :db/id]
client)]
[ci [(:db/id real-client) (:client/name real-client)]])))
(defn vendor-name-tuple->vendor-tuple [[vn vendor]]
- (when vendor
+ (when vendor
(let [real-vendor (dc/pull (dc/db conn)
[:vendor/name :db/id]
vendor)]
@@ -170,9 +170,9 @@ invoice_dropzone = new Dropzone(\"#invoice\", {
(defn get-job [id]
(-> (dc/pull (dc/db conn) '[*] id)
(update :textract-invoice/customer-identifier customer-identifier-id->customer-identifier-client)
- (update :textract-invoice/customer-identifier-options #(map customer-identifier-id->customer-identifier-client %) )
+ (update :textract-invoice/customer-identifier-options #(map customer-identifier-id->customer-identifier-client %))
(update :textract-invoice/vendor-name vendor-name-tuple->vendor-tuple)
- (update :textract-invoice/vendor-name-options #(map vendor-name-tuple->vendor-tuple %) )))
+ (update :textract-invoice/vendor-name-options #(map vendor-name-tuple->vendor-tuple %))))
(defn refresh-job [request id]
(let [{:keys [:db/id :textract-invoice/job-id :textract-invoice/textract-status]} (get-job id)]
@@ -185,7 +185,6 @@ invoice_dropzone = new Dropzone(\"#invoice\", {
@(dc/transact conn [{:db/id id :textract-invoice/textract-status new-status}]))))
(get-job id)))
-
(defn pill-list* [{:keys [selected options class ->text ->value id field]}]
(let [options (->> options
(filter (complement #{selected}))
@@ -194,7 +193,7 @@ invoice_dropzone = new Dropzone(\"#invoice\", {
(com/pill {:color :secondary}
(com/link {:hx-patch (str (bidi/path-for ssr-routes/only-routes :invoice-glimpse-update-textract-invoice :textract-invoice-id id) "?" (url/map->query {field (if ->value (->value x) (->text x))}))
:hx-target "closest form"
- :href "#"} (->text x)))]) ))]
+ :href "#"} (->text x)))])))]
(when (seq options)
[:div.col-span-6.col-start-1.text-xs
"Alternates: "
@@ -224,21 +223,21 @@ invoice_dropzone = new Dropzone(\"#invoice\", {
(format "%s (%s)" client-name customer-identifier))
:->value (fn [[client-identifier [id client-name]]]
id)})
-
-[:div.col-span-2.col-start-1
+
+ [:div.col-span-2.col-start-1
(com/field {:label "Location (blank will use default location)"}
(com/text-input {:name "location"
- :value (-> textract-invoice
- :textract-invoice/location
- second)
- :placeholder "Location"}))]
+ :value (-> textract-invoice
+ :textract-invoice/location
+ second)
+ :placeholder "Location"}))]
#_(pill-list* {:selected (:textract-invoice/location textract-invoice)
- :options (:textract-invoice/location-options textract-invoice)
- :id (:db/id textract-invoice)
- :field "location"
- :->text (fn [[_ amount]]
- (str amount))})
-
+ :options (:textract-invoice/location-options textract-invoice)
+ :id (:db/id textract-invoice)
+ :field "location"
+ :->text (fn [[_ amount]]
+ (str amount))})
+
[:div.col-span-6
(com/field {:label "Vendor"}
(com/text-input {:name (path->name [:invoice/vendor])
@@ -269,9 +268,9 @@ invoice_dropzone = new Dropzone(\"#invoice\", {
:id (:db/id textract-invoice)
:field "date"
:->text (fn [[_ date]]
- (-> date
- (coerce/to-date-time)
- (atime/unparse-local atime/normal-date)))})
+ (-> date
+ (coerce/to-date-time)
+ (atime/unparse-local atime/normal-date)))})
[:div.col-span-2.col-start-1
(com/field {:label "Total"}
(com/money-input {:name "total"
@@ -284,7 +283,7 @@ invoice_dropzone = new Dropzone(\"#invoice\", {
:id (:db/id textract-invoice)
:field "total"
:->text (fn [[_ amount]]
- (str amount))})
+ (str amount))})
[:div.col-span-2.col-start-1
(com/field {:label "Invoice Number"}
(com/text-input {:name "invoice-number"
@@ -311,11 +310,11 @@ invoice_dropzone = new Dropzone(\"#invoice\", {
:hx-trigger "load delay:5s"
:hx-swap "outerHTML"}
"Analyzing job " (some-> textract-invoice
- :textract-invoice/job-id
- (subs 0 8)) "..."]
+ :textract-invoice/job-id
+ (subs 0 8)) "..."]
(= "SUCCEEDED" (:textract-invoice/textract-status textract-invoice))
[:div.px-4
-
+
[:div.flex.flex-row.space-x-4
[:div {:style {:width "805"}}
(com/card {}
@@ -335,15 +334,15 @@ invoice_dropzone = new Dropzone(\"#invoice\", {
:invoice-glimpse)}
(com/button {:color :secondary} "New glimpse")]])]
[:p.text-sm.italic "Import your invoices with the power of AI. Please only use PDFs with a single invoice in them."]
-
- (when id
+
+ (when id
(job-progress* request id))
- (when-not id
+ (when-not id
(upload-form*))])])
(defn begin-textract-file [s3-location]
(let [tempid (random-tempid)
-
+
id (get-in @(dc/transact conn [{:db/id tempid
:textract-invoice/textract-status "IN_PROGRESS"
:textract-invoice/pdf-url (str "https://" bucket-name "/" s3-location)}])
@@ -364,7 +363,7 @@ invoice_dropzone = new Dropzone(\"#invoice\", {
[_ invoice-number] (:textract-invoice/invoice-number textract-invoice)
vendor (dc/pull (dc/db conn) d-vendors/default-read vendor-id)
location (when (and client-id)
- (or location
+ (or location
(->> (dc/pull (dc/db conn) '[:client/locations] client-id)
:client/locations
first)))
@@ -374,7 +373,7 @@ invoice_dropzone = new Dropzone(\"#invoice\", {
due)]
(alog/peek ::temp-textract-invoice textract-invoice)
(when (and client-id date invoice-number vendor-id total)
- (alog/peek ::TEMP-invoice
+ (alog/peek ::TEMP-invoice
(cond-> {:db/id (random-tempid)
:invoice/client client-id
:invoice/client-identifier (first (:textract-invoice/customer-identifier textract-invoice))
@@ -382,7 +381,7 @@ invoice_dropzone = new Dropzone(\"#invoice\", {
:invoice/invoice-number invoice-number
:invoice/total total
:invoice/date date
-
+
:invoice/location location
:invoice/import-status :import-status/imported
:invoice/outstanding-balance total
@@ -408,14 +407,14 @@ invoice_dropzone = new Dropzone(\"#invoice\", {
(get (:params request) "file"))]
(mu/log ::uploading-file
:file file)
- (try
+ (try
(let [s3-location (str "textract-files/" (UUID/randomUUID) "." (last (str/split (:filename file) #"[\\.]")))
_ (with-open [stream (io/input-stream (:tempfile file))]
- (s3/put-object (:data-bucket env)
- s3-location
- stream
- {:content-type "application/pdf"
- :content-length (.length (:tempfile file))}))
+ (s3/put-object (:data-bucket env)
+ s3-location
+ stream
+ {:content-type "application/pdf"
+ :content-length (.length (:tempfile file))}))
textract-invoice (begin-textract-file s3-location)]
{:headers {"Location"
(str (bidi/path-for ssr-routes/only-routes
@@ -437,7 +436,7 @@ invoice_dropzone = new Dropzone(\"#invoice\", {
new-invoice-id (get-in @(dc/transact conn [[:propose-invoice new-invoice]])
[:tempids (:db/id new-invoice)])
_ (when new-invoice-id @(dc/transact conn [{:db/id (:db/id current-job)
- :textract-invoice/invoice new-invoice-id}]))]
+ :textract-invoice/invoice new-invoice-id}]))]
(if new-invoice-id
(html-response (page* request nil)
:headers {"hx-push-url" (bidi/path-for ssr-routes/only-routes :invoice-glimpse)
@@ -456,36 +455,36 @@ invoice_dropzone = new Dropzone(\"#invoice\", {
(mu/log ::method
:method request-method)
(base-page
- request
- (com/page {:nav com/main-aside-nav
- :client-selection (:client-selection request)
- :client (:client request)
- :clients (:clients request)
- :request request
- :identity (:identity request)
- :app-params {:hx-get (bidi/path-for ssr-routes/only-routes
- :invoice-glimpse)
- :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
- :admin)}
- "Invoice"]
- [:a {:href (bidi/path-for ssr-routes/only-routes
- :invoice-glimpse)}
- "Glimpse"])
- (page* request (some-> request
- :route-params
- :textract-invoice-id
- Long/parseLong)))
+ request
+ (com/page {:nav com/main-aside-nav
+ :client-selection (:client-selection request)
+ :client (:client request)
+ :clients (:clients request)
+ :request request
+ :identity (:identity request)
+ :app-params {:hx-get (bidi/path-for ssr-routes/only-routes
+ :invoice-glimpse)
+ :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
+ :admin)}
+ "Invoice"]
+ [:a {:href (bidi/path-for ssr-routes/only-routes
+ :invoice-glimpse)}
+ "Glimpse"])
+ (page* request (some-> request
+ :route-params
+ :textract-invoice-id
+ Long/parseLong)))
- "Invoice Glimpse"))
+ "Invoice Glimpse"))
(defn textract-invoice [request]
(if (get-in request [:headers "hx-request"])
(html-response (job-progress* request (some-> request
- :route-params
- :textract-invoice-id
- Long/parseLong)))
+ :route-params
+ :textract-invoice-id
+ Long/parseLong)))
(page request)))
diff --git a/src/clj/auto_ap/ssr/invoice/import.clj b/src/clj/auto_ap/ssr/invoice/import.clj
index b44a2f47..dfabd346 100644
--- a/src/clj/auto_ap/ssr/invoice/import.clj
+++ b/src/clj/auto_ap/ssr/invoice/import.clj
@@ -2,9 +2,9 @@
(:require
[amazonica.aws.s3 :as s3]
[auto-ap.datomic
- :refer [add-sorter-fields apply-pagination apply-sort-3
- audit-transact conn merge-query observable-query
- pull-attr pull-many random-tempid]]
+ :refer [add-sorter-fields apply-pagination apply-sort-3
+ audit-transact conn merge-query observable-query
+ pull-attr pull-many random-tempid]]
[auto-ap.datomic.clients :as d-clients]
[auto-ap.datomic.invoices :as d-invoices]
[auto-ap.datomic.vendors :as d-vendors]
@@ -24,24 +24,23 @@
[auto-ap.ssr.pos.common :refer [date-range-field*]]
[auto-ap.ssr.svg :as svg]
[auto-ap.ssr.utils
- :refer [apply-middleware-to-all-handlers clj-date-schema
- entity-id html-response main-transformer ref->enum-schema
- strip wrap-entity wrap-implied-route-param
- wrap-schema-enforce]]
- [auto-ap.time :as atime]
- [auto-ap.utils :refer [dollars=]]
- [bidi.bidi :as bidi]
- [clj-time.coerce :as coerce :refer [to-date]]
- [clojure.java.io :as io]
- [clojure.string :as str]
- [com.brunobonacci.mulog :as mu]
- [config.core :refer [env]]
- [datomic.api :as dc]
- [hiccup2.core :as hiccup]
- [malli.core :as mc])
+ :refer [apply-middleware-to-all-handlers clj-date-schema
+ entity-id html-response main-transformer ref->enum-schema
+ strip wrap-entity wrap-implied-route-param
+ wrap-schema-enforce]]
+ [auto-ap.time :as atime]
+ [auto-ap.utils :refer [dollars=]]
+ [bidi.bidi :as bidi]
+ [clj-time.coerce :as coerce :refer [to-date]]
+ [clojure.java.io :as io]
+ [clojure.string :as str]
+ [com.brunobonacci.mulog :as mu]
+ [config.core :refer [env]]
+ [datomic.api :as dc]
+ [hiccup2.core :as hiccup]
+ [malli.core :as mc])
(:import [java.util UUID]))
-
(defn exact-match-id* [request]
(if (nat-int? (:exact-match-id (:query-params request)))
[:div {:x-data (hx/json {:exact_match (:exact-match-id (:query-params request))}) :id "exact-match-id-tag"}
@@ -108,7 +107,6 @@
:size :small})])
(exact-match-id* request)]])
-
(defn fetch-ids [db {:keys [query-params route-params] :as request}]
(let [valid-clients (extract-client-ids (:clients request)
(:client-id request)
@@ -131,8 +129,6 @@
(some-> (:start-date query-params) coerce/to-date)
(some-> (:end-date query-params) coerce/to-date)]]}
-
-
(:client-id query-params)
(merge-query {:query {:in ['?client-id]
:where ['[?e :invoice/client ?client-id]]}
@@ -144,7 +140,6 @@
'[?client-id :client/code ?client-code]]}
:args [(:client-code query-params)]})
-
(:start (:due-range query-params)) (merge-query {:query {:in '[?start-due]
:where ['[?e :invoice/due ?due]
'[(>= ?due ?start-due)]]}
@@ -155,7 +150,6 @@
'[(<= ?due ?end-due)]]}
:args [(coerce/to-date (:end (:due-range query-params)))]})
-
(:import-status query-params)
(merge-query {:query {:in ['?import-status]
:where ['[?e :invoice/import-status ?import-status]]}
@@ -232,7 +226,6 @@
(apply-sort-3 (assoc query-params :default-asc? false))
(apply-pagination query-params))))
-
(defn hydrate-results [ids db _]
(let [results (->> (pull-many db default-read ids)
(group-by :db/id))
@@ -307,7 +300,6 @@
(assoc-in [:query-params :start] 0)
(assoc-in [:query-params :per-page] 250))))
-
:else
selected)
ids (->> (dc/q '[:find ?i
@@ -318,29 +310,27 @@
(map first))]
ids))
-(def upload-schema
- [:map
+(def upload-schema
+ [:map
[:force-client {:optional true}
[:maybe entity-id]]
[:force-vendor {:optional true}
[:maybe entity-id]]
-[:force-chatgpt {:optional true :default false}
- [:maybe [ :boolean {:decode/string {:enter #(if (= % "on") true
+ [:force-chatgpt {:optional true :default false}
+ [:maybe [:boolean {:decode/string {:enter #(if (= % "on") true
- (boolean %))}}]]]
+ (boolean %))}}]]]
[:force-location {:optional true}
[:maybe [:string {:decode/string strip :min 2 :max 2}]]]])
(defn upload-form [{:keys [form-params form-errors] :as request}]
(com/content-card {}
-
+
[:div.px-4.py-3.space-y-4
-
- [:div.flex.justify-between.items-center [:h1.text-2xl.mb-3.font-bold "Import new invoices"]
- ]
+
+ [:div.flex.justify-between.items-center [:h1.text-2xl.mb-3.font-bold "Import new invoices"]]
[:div#page-notification.notification.block {:style {:display "none"}}]
-
-
+
[:form
{:hx-post (bidi/path-for ssr-routes/only-routes
::route/import-file)
@@ -351,7 +341,7 @@
(fc/start-form
form-params form-errors
[:div.flex.gap-4.items-center
-
+
(fc/with-field :force-client
(com/validated-field {:label "Force client"
:errors (fc/field-errors)}
@@ -366,7 +356,7 @@
(fc/with-field :force-location
(com/validated-field {:label "Force location"
:errors (fc/field-errors)}
-
+
(com/text-input {:name (fc/field-name)
:value (fc/field-value)
:size 2})))
@@ -382,15 +372,15 @@
:value (fc/field-value)
:content-fn (fn [c] (pull-attr (dc/db conn) :vendor/name c))})]))
(fc/with-field :force-chatgpt
- (com/validated-field { :errors (fc/field-errors)
+ (com/validated-field {:errors (fc/field-errors)
:label " "}
(com/checkbox {:name (fc/field-name)
- :error? (fc/error?) }
+ :error? (fc/error?)}
"Only use ChatGPT")))])
-
+
[: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-data (hx/json {"files" nil
+ "hovering" false})
":class" "{'bg-blue-100': !hovering,
'border-blue-300': !hovering,
'text-blue-700': !hovering,
@@ -399,8 +389,6 @@
'text-green-700': hovering
}"
:x-ref "box"}
-
-
[:input {:type "file"
:name "file"
@@ -410,13 +398,12 @@
:x-on:dragover "hovering = true",
:x-on:dragleave "hovering = false",
:x-on:drop "hovering = false"}]
- [:div.flex.flex-col.space-2
- [:div
+ [: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"}) ]
- ]]]
-
+ [:template {:x-for "f in files"}
+ [:li (com/pill {:color :primary :x-text "f.name"})]]]]
+
[:div.htmx-indicator-hidden "Drop files to upload here"]]]
(com/button {:color :primary :class "w-32 mt-3"} "Upload")]]))
@@ -435,14 +422,12 @@
:query-schema query-schema
:action-buttons (fn [request]
(let [[_ _ outstanding total] (:page-results request)]
- [
- (when (can? (:identity request) {:subject :invoice :activity :import})
+ [(when (can? (:identity request) {:subject :invoice :activity :import})
(com/button {:hx-put (str (bidi/path-for ssr-routes/only-routes ::route/bulk-approve))
"x-bind:hx-vals" "JSON.stringify({selected: $data.selected, 'all-selected': $data.all_selected})"
"hx-include" "#invoice-filters"
:color :primary
- ":disabled" "$data.all_selected || ($data.selected && $data.selected.length > 0) ? false: true "
- }
+ ":disabled" "$data.all_selected || ($data.selected && $data.selected.length > 0) ? false: true "}
"Approve selected"))
(when (can? (:identity request) {:subject :invoice :activity :import})
(com/button {:hx-delete (str (bidi/path-for ssr-routes/only-routes ::route/bulk-disapprove))
@@ -463,8 +448,8 @@
(when (and (= :import-status/pending (:invoice/import-status entity))
(can? (:identity request) {:subject :invoice :activity :import}))
(com/icon-button {:hx-put (bidi/path-for ssr-routes/only-routes
- ::route/approve
- :db/id (:db/id entity))}
+ ::route/approve
+ :db/id (:db/id entity))}
svg/thumbs-up))])
:breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes ::route/page)}
@@ -515,11 +500,11 @@
(defn disapprove [{invoice :entity :as request identity :identity}]
(when-not (= :import-status/pending (:invoice/import-status invoice))
- (throw (ex-info (str "Cannot disapprove an invoice if it is not pending." (:invoice/import-status invoice))
+ (throw (ex-info (str "Cannot disapprove an invoice if it is not pending." (:invoice/import-status invoice))
{:type :notification})))
(exception->notification
#(assert-can-see-client identity (:db/id (:invoice/client invoice))))
-
+
(audit-transact [[:db/retractEntity (:db/id invoice)]] identity)
(html-response (row* (:identity request) invoice
@@ -528,13 +513,13 @@
(defn approve [{invoice :entity :as request identity :identity}]
(when-not (= :import-status/pending (:invoice/import-status invoice))
- (throw (ex-info (str "Cannot approve an invoice if it is not pending." (:invoice/import-status invoice))
+ (throw (ex-info (str "Cannot approve an invoice if it is not pending." (:invoice/import-status invoice))
{:type :notification})))
(exception->notification
#(do (assert-can-see-client identity (:db/id (:invoice/client invoice)))
(assert-not-locked (-> invoice :invoice/client :db/id) (-> invoice :invoice/date))))
-
- (audit-transact [ [:upsert-invoice {:db/id (:db/id invoice) :invoice/import-status :import-status/imported}]] identity)
+
+ (audit-transact [[:upsert-invoice {:db/id (:db/id invoice) :invoice/import-status :import-status/imported}]] identity)
(html-response (row* (:identity request) invoice
{:class "live-added"})
@@ -542,51 +527,49 @@
(defn bulk-disapprove [request]
(let [ids (selected->ids request (:form-params request))
- updates (map
- (fn [i] [:db/retractEntity i])
- ids) ]
- (audit-transact updates (:identity request) )
+ updates (map
+ (fn [i] [:db/retractEntity i])
+ ids)]
+ (audit-transact updates (:identity request))
(html-response [:div]
- :headers {"hx-trigger" (hx/json { :notification (format "Successfully disapproved %d invoices."
+ :headers {"hx-trigger" (hx/json {:notification (format "Successfully disapproved %d invoices."
(count ids))
:invalidated "invalidated"})})))
(defn bulk-approve [request]
(let [ids (selected->ids request (:form-params request))]
- (exception->notification
+ (exception->notification
#(doseq [i ids
- :let [invoice (dc/pull (dc/db conn) '[{:invoice/client [:db/id]}
- :invoice/date] i)]]
- (assert-can-see-client (:identity request) (-> invoice :invoice/client :db/id))
- (assert-not-locked (-> invoice :invoice/client :db/id) (-> invoice :invoice/date))))
+ :let [invoice (dc/pull (dc/db conn) '[{:invoice/client [:db/id]}
+ :invoice/date] i)]]
+ (assert-can-see-client (:identity request) (-> invoice :invoice/client :db/id))
+ (assert-not-locked (-> invoice :invoice/client :db/id) (-> invoice :invoice/date))))
(let [transactions (map (fn [i] [:upsert-invoice {:db/id i :invoice/import-status :import-status/imported}]) ids)]
(audit-transact transactions (:identity request)))
(html-response [:div]
- :headers {"hx-trigger" (hx/json { :notification (format "Successfully approved %d invoices."
+ :headers {"hx-trigger" (hx/json {:notification (format "Successfully approved %d invoices."
(count ids))
:invalidated "invalidated"})})))
#_(defn upload-invoices [{{files :file
- files-2 "file"
- client :client
- client-2 "client"
- location :location
- location-2 "location"
- vendor :vendor
- vendor-2 "vendor"} :params
- user :identity}]
- (let [files (or files files-2)
- client (or client client-2)
- location (or location location-2)
- vendor (some-> (or vendor vendor-2)
- (Long/parseLong))
- ]
- ))
+ files-2 "file"
+ client :client
+ client-2 "client"
+ location :location
+ location-2 "location"
+ vendor :vendor
+ vendor-2 "vendor"} :params
+ user :identity}]
+ (let [files (or files files-2)
+ client (or client client-2)
+ location (or location location-2)
+ vendor (some-> (or vendor vendor-2)
+ (Long/parseLong))]))
(defn match-vendor [vendor-code forced-vendor vendor-search]
(when (and (not forced-vendor) (str/blank? vendor-code))
- (if vendor-search
+ (if vendor-search
(throw (ex-info (format "No vendor found. Searched for '%s'. Please supply an forced vendor."
vendor-search)
{:vendor-code vendor-code}))
@@ -594,10 +577,10 @@
{:vendor-code vendor-code}))))
(let [vendor-id (or forced-vendor
(->> (dc/q
- {:find ['?vendor]
- :in ['$ '?vendor-name]
- :where ['[?vendor :vendor/name ?vendor-name]]}
- (dc/db conn) vendor-code)
+ {:find ['?vendor]
+ :in ['$ '?vendor-name]
+ :where ['[?vendor :vendor/name ?vendor-name]]}
+ (dc/db conn) vendor-code)
first
first))]
(when-not vendor-id
@@ -605,9 +588,9 @@
{:vendor-code vendor-code})))
(if-let [matching-vendor (->> (dc/q
- {:find [(list 'pull '?vendor-id d-vendors/default-read)]
- :in ['$ '?vendor-id]}
- (dc/db conn) vendor-id)
+ {:find [(list 'pull '?vendor-id d-vendors/default-read)]
+ :in ['$ '?vendor-id]}
+ (dc/db conn) vendor-id)
first
first)]
matching-vendor
@@ -617,7 +600,7 @@
(defn import->invoice [{:keys [invoice-number source-url customer-identifier account-number total date vendor-code text full-text client-override vendor-search vendor-override location-override import-status]} user]
(when-not total
(throw (Exception. "Couldn't parse total from file.")))
-(when-not date
+ (when-not date
(throw (Exception. "Couldn't parse date from file.")))
(let [matching-client (cond
client-override client-override
@@ -629,9 +612,9 @@
:client-override client-override
:matching (when matching-client
(dc/pull (dc/db conn) [:client/name :client/code] matching-client)))
-
+
matching-vendor (match-vendor vendor-code vendor-override vendor-search)
-
+
matching-location (or (when-not (str/blank? location-override)
location-override)
(parse/best-location-match (dc/pull (dc/db conn)
@@ -658,22 +641,20 @@
(defn validate-invoice [invoice user]
(let [missing-keys (for [k [:invoice/invoice-number :invoice/client :invoice/vendor :invoice/total :invoice/outstanding-balance :invoice/date]
:when (not (get invoice k))]
- k
- )]
- (cond
- (not (:invoice/client invoice))
- (do
+ k)]
+ (cond
+ (not (:invoice/client invoice))
+ (do
(alog/warn ::no-client :invoice invoice)
(assoc invoice :error-message (str "Searched clients for '" (:invoice/client-identifier invoice) "'. No client found in file. Select a client first.")))
(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.")
- )
-
+ (do
+ (alog/warn ::cant-see-client :invoice invoice)
+ (assoc invoice :error-message "No access for the client in this file."))
+
(seq missing-keys)
- (do
+ (do
(alog/warn ::mising-keys :keys missing-keys)
(assoc invoice :error-message (str "Missing the key " missing-keys)))
:else
@@ -686,33 +667,31 @@
count)]
(map #(assoc % :invoice/source-url-admin-only (boolean (> client-count 1))) is)))
-
(defn import-uploaded-invoice [user imports]
(alog/info ::importing-uploaded :count (count imports)
:bc (or user "NOO"))
(let [potential-invoices (->> imports
(map #(import->invoice % user))
(map #(validate-invoice % user))
- admin-only-if-multiple-clients
- )
- errored-invoices (->> potential-invoices
+ 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 (fn [i] [:propose-invoice i])))]
-
+ (mapv (fn [i] [:propose-invoice i])))]
+
(alog/info ::creating-invoice :invoices proposed-invoices)
(let [tx (audit-transact proposed-invoices user)]
#_(when-not (seq (dc/q '[:find ?i
- :in $ [?i ...]
- :where [?i :invoice/invoice-number]]
- (:db-after tx)
- (map :e (:tx-data tx))))
- (throw (ex-info "No new invoices found."
- {:template (:template (first imports))})))
+ :in $ [?i ...]
+ :where [?i :invoice/invoice-number]]
+ (:db-after tx)
+ (map :e (:tx-data tx))))
+ (throw (ex-info "No new invoices found."
+ {:template (:template (first imports))})))
{:tx tx
:errored-invoices errored-invoices
:successful-invoices successful-invoices
@@ -730,7 +709,7 @@
"text/csv"
"application/pdf")
:content-length (.length tempfile)})
- imports (->> (if force-chatgpt
+ imports (->> (if force-chatgpt
(parse/glimpse2 (.getPath tempfile))
(parse/parse-file (.getPath tempfile) filename :allow-glimpse? true))
(map #(assoc %
@@ -740,16 +719,16 @@
:source-url (str "https://" (:data-bucket env)
"/"
s3-location))))]
- (try
-
+ (try
+
(import-uploaded-invoice identity imports)
(catch Exception e
(alog/warn ::couldnt-import-upload
:error e
- :template (:template ( first imports)))
+ :template (:template (first imports)))
(throw (ex-info (ex-message e)
- {:template (:template ( first imports))
+ {:template (:template (first imports))
:sample (first imports)}
e)))))
(catch Exception e
@@ -767,23 +746,21 @@
(fn [result {:keys [filename tempfile]}]
(try
(let [i (import-internal tempfile filename force-client force-location force-vendor force-chatgpt (:identity request))]
- (alog/info ::failure-error-count :count (count (:errored-invoices i)) )
-
- (-> result
- (update :error? #(or %
+ (alog/info ::failure-error-count :count (count (:errored-invoices 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
+ :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)]) ]]
+ [:li (:error-message e)])]]
:template (:template (first (:imports i)))})))
(catch Exception e
(-> result
@@ -793,11 +770,10 @@
:response (.getMessage e)
:sample (:sample (ex-data e))
:template (:template (ex-data e))})))))
- {:error? false
- :files []
- }
+ {:error? false
+ :files []}
file)]
-
+
(html-response [:div#page-notification.p-4.rounded-lg
[:table
[:thead
@@ -835,34 +811,34 @@
{"hx-trigger" "invalidated"})))
#_(defn wrap-test [handler]
- (fn [request]
- (clojure.pprint/pprint (:multipart-params request))
- (handler request )))
+ (fn [request]
+ (clojure.pprint/pprint (:multipart-params request))
+ (handler request)))
(def key->handler
- (apply-middleware-to-all-handlers
+ (apply-middleware-to-all-handlers
{::route/import-page
(->
(helper/page-route grid-page)
(wrap-implied-route-param :status nil))
- ::route/import-table
+ ::route/import-table
(-> (helper/table-route grid-page)
(wrap-implied-route-param :status nil))
-
+
::route/disapprove (-> disapprove
(wrap-entity [:route-params :db/id] default-read)
(wrap-schema-enforce :route-params [:map [:db/id entity-id]]))
::route/approve (-> approve
- (wrap-entity [:route-params :db/id] default-read)
- (wrap-schema-enforce :route-params [:map [:db/id entity-id]]))
+ (wrap-entity [:route-params :db/id] default-read)
+ (wrap-schema-enforce :route-params [:map [:db/id entity-id]]))
::route/bulk-disapprove (-> bulk-disapprove
(wrap-schema-enforce :form-schema query-schema))
::route/bulk-approve (-> bulk-approve
- (wrap-schema-enforce :form-schema query-schema))
+ (wrap-schema-enforce :form-schema query-schema))
::route/import-file (-> import-file
(wrap-schema-enforce :multipart-schema upload-schema))}
(fn [a]
- (-> a
+ (-> a
(wrap-must {:subject :invoice :activity :import})))))
diff --git a/src/clj/auto_ap/ssr/invoice/new_invoice_wizard.clj b/src/clj/auto_ap/ssr/invoice/new_invoice_wizard.clj
index c5c8859a..d7ed7829 100644
--- a/src/clj/auto_ap/ssr/invoice/new_invoice_wizard.clj
+++ b/src/clj/auto_ap/ssr/invoice/new_invoice_wizard.clj
@@ -1,7 +1,7 @@
(ns auto-ap.ssr.invoice.new-invoice-wizard
(:require
[auto-ap.datomic
- :refer [audit-transact conn pull-attr]]
+ :refer [audit-transact conn pull-attr]]
[auto-ap.datomic.accounts :as d-accounts]
[auto-ap.datomic.invoices :as d-invoices]
[auto-ap.graphql.utils :refer [assert-can-see-client assert-not-locked
@@ -9,7 +9,7 @@
[auto-ap.logging :as alog]
[auto-ap.routes.invoice :as route]
[auto-ap.routes.utils
- :refer [wrap-client-redirect-unauthenticated]]
+ :refer [wrap-client-redirect-unauthenticated]]
[auto-ap.solr :as solr]
[auto-ap.ssr-routes :as ssr-routes]
[auto-ap.ssr.common-handlers :refer [add-new-entity-handler]]
@@ -21,10 +21,10 @@
[auto-ap.ssr.nested-form-params :refer [wrap-nested-form-params]]
[auto-ap.ssr.svg :as svg]
[auto-ap.ssr.utils
- :refer [->db-id apply-middleware-to-all-handlers check-allowance
- check-location-belongs clj-date-schema entity-id
- form-validation-error html-response money strip
- wrap-schema-enforce]]
+ :refer [->db-id apply-middleware-to-all-handlers check-allowance
+ check-location-belongs clj-date-schema entity-id
+ form-validation-error html-response money strip
+ wrap-schema-enforce]]
[auto-ap.time :as atime]
[bidi.bidi :as bidi]
[clj-time.coerce :as coerce]
@@ -48,10 +48,6 @@
[:vendor-terms-override/client :vendor-terms-override/terms]}]
vendor-id))
-
-
-
-
(defn check-vendor-default-account [vendor-id]
(some? (:vendor/default-account (get-vendor vendor-id))))
@@ -83,7 +79,7 @@
[:invoice-expense-account/location :string]
[:invoice-expense-account/amount :double]]
[:fn {:error/fn (fn [r x] (:type r))
- :error/path [:invoice-expense-account/location]}
+ :error/path [:invoice-expense-account/location]}
(fn [iea]
(check-location-belongs (:invoice-expense-account/location iea)
(:invoice-expense-account/account iea)))]]]]])
@@ -95,7 +91,6 @@
true
(and invoice-number vendor client)))]])
-
(defn clientize-vendor [{:vendor/keys [terms-overrides automatically-paid-when-due default-account account-overrides] :as vendor} client-id]
(if (nil? vendor)
nil
@@ -142,7 +137,6 @@
{:value (name :customize)
:content [:div "Customize accounts"]}])}))))
-
(defrecord BasicDetailsStep [linear-wizard]
mm/ModalWizardStep
(step-name [_]
@@ -181,7 +175,6 @@
(com/hidden {:name (fc/field-name)
:value (fc/field-value)})))
-
(fc/with-field :customize-due-and-scheduled?
(com/hidden {:name (fc/field-name)
:value (fc/field-value)
@@ -220,11 +213,10 @@
[:div.mb-4
;; TODO DO NOT MERGE UNTIL THIS IS FIXED
#_[:span.text-sm.text-gray-500 "Can't find the vendor? "
- (com/link {:href ...
- :target "new"}
- "Add new vendor")
- " in a new window, then return here."]]
-
+ (com/link {:href ...
+ :target "new"}
+ "Add new vendor")
+ " in a new window, then return here."]]
[:div.flex.items-center.gap-2
(fc/with-field :invoice/date
@@ -333,7 +325,6 @@
#_(mm/navigate-handler {:request request
:to-step :next-steps}))))
-
(defn location-select*
[{:keys [name account-location client-locations value]}]
(let [options (into (cond account-location
@@ -433,8 +424,8 @@
(filter number?)
(reduce + 0.0))
balance (-
- (-> request :multi-form-state :snapshot :invoice/total)
- total)]
+ (-> request :multi-form-state :snapshot :invoice/total)
+ total)]
[:span {:class (when-not (dollars= 0.0 balance)
"text-red-300")}
(format "$%,.2f" balance)]))
@@ -569,7 +560,6 @@
nil
:validation-route ::route/new-wizard-navigate)))
-
(defn assert-no-conflicting [{:invoice/keys [invoice-number client vendor] :db/keys [id]}]
(when (seq (d-invoices/find-conflicting {:invoice/invoice-number invoice-number
:invoice/vendor (->db-id vendor)
@@ -577,13 +567,11 @@
:db/id id}))
(form-validation-error (str "Invoice '" invoice-number "' already exists."))))
-
(defn assert-invoice-amounts-add-up [{:keys [:invoice/expense-accounts :invoice/total]}]
(let [expense-account-total (reduce + 0 (map (fn [x] (:invoice-expense-account/amount x)) expense-accounts))]
(when-not (dollars= total expense-account-total)
(form-validation-error (str "Expense account total (" expense-account-total ") does not equal invoice total (" total ")")))))
-
(defn- calculate-spread
"Helper function to calculate the amount to be assigned to each location"
[shared-amount total-locations]
@@ -592,7 +580,6 @@
{:base-amount base-amount
:remainder remainder}))
-
(defn- spread-expense-account
"Spreads the expense account amount across the given locations"
[locations expense-account]
@@ -628,7 +615,6 @@
(update first-eas :invoice-expense-account/amount #(+ % leftover))
rest))))
-
(defn maybe-spread-locations
"Converts any expense account for a \"Shared\" location into a separate expense account for all valid locations for that client"
([invoice]
@@ -643,8 +629,6 @@
(apply-total-delta-to-account ($->cents (:invoice/total invoice)))
(map (fn [ea] (update ea :invoice-expense-account/amount cents->$))))))))
-
-
(defrecord NewWizard2 [_ current-step]
mm/LinearModalWizard
(hydrate-from-request
@@ -728,13 +712,12 @@
(exception->4xx #(assert-not-locked client-id (:invoice/date invoice)))
(let [transaction-result (audit-transact [transaction] (:identity request))]
- (try
+ (try
(solr/touch-with-ledger (get-in transaction-result [:tempids "invoice"]))
(catch Exception e
- (alog/error ::cant-save-solr
- :error e
- ))
- )
+ (alog/error ::cant-save-solr
+ :error e)))
+
(if extant?
(html-response
@@ -750,7 +733,6 @@
(def new-wizard (->NewWizard2 nil nil))
-
(defn initial-new-wizard-state [request]
(mm/->MultiStepFormState {:invoice/date (time/now)
:customize-accounts :default}
@@ -758,9 +740,6 @@
{:invoice/date (time/now)
:customize-accounts :default}))
-
-
-
(defn initial-edit-wizard-state [request]
(let [entity (dc/pull (dc/db conn) default-read (:db/id (:route-params request)))
entity (select-keys entity (mut/keys new-form-schema))]
@@ -779,7 +758,6 @@
:client-locations (some->> client-id
(pull-attr (dc/db conn) :client/locations))})))
-
(defn due-date [{:keys [multi-form-state]}]
(let [vendor (clientize-vendor (get-vendor (:invoice/vendor (:step-params multi-form-state)))
(->db-id (:invoice/client (:step-params multi-form-state))))
@@ -816,7 +794,6 @@
:error? false
:placeholder "1/1/2024"}))))
-
(defn account-prediction [{:keys [multi-form-state form-errors] :as request}]
(html-response
(account-prediction* request)))
diff --git a/src/clj/auto_ap/ssr/invoices.clj b/src/clj/auto_ap/ssr/invoices.clj
index ce68ed83..6c8e06da 100644
--- a/src/clj/auto_ap/ssr/invoices.clj
+++ b/src/clj/auto_ap/ssr/invoices.clj
@@ -1,9 +1,9 @@
(ns auto-ap.ssr.invoices
(:require
[auto-ap.datomic
- :refer [add-sorter-fields apply-pagination apply-sort-3
- audit-transact audit-transact-batch conn merge-query
- observable-query pull-many]]
+ :refer [add-sorter-fields apply-pagination apply-sort-3
+ audit-transact audit-transact-batch conn merge-query
+ observable-query pull-many]]
[auto-ap.datomic.accounts :as d-accounts]
[auto-ap.datomic.bank-accounts :as d-bank-accounts]
[auto-ap.datomic.clients :as d-clients]
@@ -23,7 +23,7 @@
[auto-ap.routes.payments :as payment-route]
[auto-ap.routes.transactions :as transaction-routes]
[auto-ap.routes.utils
- :refer [wrap-admin wrap-client-redirect-unauthenticated]]
+ :refer [wrap-admin wrap-client-redirect-unauthenticated]]
[auto-ap.rule-matching :as rm]
[auto-ap.solr :as solr]
[auto-ap.ssr-routes :as ssr-routes]
@@ -41,13 +41,13 @@
[auto-ap.ssr.components.date-range :as dr]
[auto-ap.ssr.svg :as svg]
[auto-ap.ssr.utils
- :refer [apply-middleware-to-all-handlers assert-schema
- clj-date-schema dissoc-nil-transformer entity-id
- form-validation-error html-response main-transformer
- many-entity modal-response money percentage
- ref->enum-schema round-money strip wrap-entity
- wrap-implied-route-param wrap-merge-prior-hx
- wrap-schema-enforce]]
+ :refer [apply-middleware-to-all-handlers assert-schema
+ clj-date-schema dissoc-nil-transformer entity-id
+ form-validation-error html-response main-transformer
+ many-entity modal-response money percentage
+ ref->enum-schema round-money strip wrap-entity
+ wrap-implied-route-param wrap-merge-prior-hx
+ wrap-schema-enforce]]
[auto-ap.time :as atime]
[auto-ap.utils :refer [by dollars-0? dollars=]]
[bidi.bidi :as bidi]
@@ -63,7 +63,6 @@
[malli.util :as mut]
[slingshot.slingshot :refer [try+]]))
-
(defn exact-match-id* [request]
(if (nat-int? (:exact-match-id (:query-params request)))
[:div {:x-data (hx/json {:exact_match (:exact-match-id (:query-params request))}) :id "exact-match-id-tag"}
@@ -105,9 +104,9 @@
(:db/id (:client request))))
:class "filter-trigger"}))
(dr/date-range-field {:value {:start (:start-date (:query-params request))
- :end (:end-date (:query-params request))}
- :id "date-range"
- :apply-button? true})
+ :end (:end-date (:query-params request))}
+ :id "date-range"
+ :apply-button? true})
(com/field {:label "Check #"}
(com/text-input {:name "check-number"
:id "check-number"
@@ -122,7 +121,7 @@
:value (:invoice-number (:query-params request))
:placeholder "e.g., ABC-456"
:size :small}))
-
+
(com/field {:label "Amount"}
[:div.flex.space-x-4.items-baseline
(com/money-input {:name "amount-gte"
@@ -143,7 +142,6 @@
:size :small})])
(exact-match-id* request)]])
-
(defn fetch-ids [db {:keys [query-params route-params] :as request}]
(let [valid-clients (extract-client-ids (:clients request)
(:client-id request)
@@ -165,7 +163,6 @@
(some-> (:start-date query-params) coerce/to-date)
(some-> (:end-date query-params) coerce/to-date)]]}
-
(:client-id query-params)
(merge-query {:query {:in ['?client-id]
:where ['[?e :invoice/client ?client-id]]}
@@ -177,7 +174,6 @@
'[?client-id :client/code ?client-code]]}
:args [(:client-code query-params)]})
-
(:start (:due-range query-params)) (merge-query {:query {:in '[?start-due]
:where ['[?e :invoice/due ?due]
'[(>= ?due ?start-due)]]}
@@ -188,14 +184,13 @@
'[(<= ?due ?end-due)]]}
:args [(coerce/to-date (:end (:due-range query-params)))]})
-
(:import-status query-params)
(merge-query {:query {:in ['?import-status]
:where ['[?e :invoice/import-status ?import-status]]}
:args [(:import-status query-params)]})
(not (:import-status query-params))
- (merge-query {:query { :where ['[?e :invoice/import-status :import-status/imported]]} })
+ (merge-query {:query {:where ['[?e :invoice/import-status :import-status/imported]]}})
(:status route-params)
(merge-query {:query {:in ['?status]
@@ -269,7 +264,6 @@
(apply-sort-3 (assoc query-params :default-asc? false))
(apply-pagination query-params))))
-
(defn hydrate-results [ids db _]
(let [results (->> (pull-many db default-read ids)
(group-by :db/id))
@@ -279,31 +273,30 @@
refunds))
(defn sum-outstanding [ids]
-
- (->>
- (dc/q {:find ['?id '?o]
- :in ['$ '[?id ...]]
- :where ['[?id :invoice/outstanding-balance ?o]]}
- (dc/db conn)
- ids)
+
+ (->>
+ (dc/q {:find ['?id '?o]
+ :in ['$ '[?id ...]]
+ :where ['[?id :invoice/outstanding-balance ?o]]}
+ (dc/db conn)
+ ids)
(map last)
(reduce
+
0.0)))
(defn sum-total-amount [ids]
-
- (->>
- (dc/q {:find ['?id '?o]
- :in ['$ '[?id ...]]
- :where ['[?id :invoice/total ?o]]
- }
- (dc/db conn)
- ids)
- (map last)
- (reduce
- +
- 0.0)))
+
+ (->>
+ (dc/q {:find ['?id '?o]
+ :in ['$ '[?id ...]]
+ :where ['[?id :invoice/total ?o]]}
+ (dc/db conn)
+ ids)
+ (map last)
+ (reduce
+ +
+ 0.0)))
(defn fetch-page [request]
(let [db (dc/db conn)
@@ -351,7 +344,6 @@
(assoc-in [:query-params :start] 0)
(assoc-in [:query-params :per-page] 250))))
-
:else
selected)]
ids))
@@ -369,21 +361,20 @@
(defn can-undo-autopayment [invoice]
(try+
- (assert-can-undo-autopayment invoice)
+ (assert-can-undo-autopayment invoice)
true
(catch [:type :warning] {}
- false)))
-
+ false)))
(defn pay-button* [params]
(let [ids (:ids params)
- ids (if (seq ids)
+ ids (if (seq ids)
(map first
- (dc/q '[:find ?i
- :in $ [?i ...]
- :where (not [?i :invoice/scheduled-payment])]
- (dc/db conn)
- ids))
+ (dc/q '[:find ?i
+ :in $ [?i ...]
+ :where (not [?i :invoice/scheduled-payment])]
+ (dc/db conn)
+ ids))
ids)
selected-client-count (if (seq ids)
(ffirst
@@ -417,18 +408,17 @@
outstanding-balances)
total (reduce + 0.0 vendor-totals)
paying-credit? (and (> (count ids) 1)
- (= 1 (count vendor-totals))
- at-least-one-positive-payment
- (dollars-0? total))]
-
+ (= 1 (count vendor-totals))
+ at-least-one-positive-payment
+ (dollars-0? total))]
[:div (cond-> {:hx-target "this"
-
+
:hx-trigger "click from:#pay-button"
:x-tooltip "{allowHTML: true, content: () => $refs.template.innerHTML, appendTo: $root}"}
paying-credit? (assoc :hx-post (bidi/path-for ssr-routes/only-routes ::route/pay-using-credit))
- (not paying-credit? ) (assoc :hx-get (bidi/path-for ssr-routes/only-routes
- ::route/pay-wizard)))
+ (not paying-credit?) (assoc :hx-get (bidi/path-for ssr-routes/only-routes
+ ::route/pay-wizard)))
(com/button {:color :primary
:id "pay-button"
:disabled (or (= (count (:ids params)) 0)
@@ -445,14 +435,13 @@
(cond
paying-credit?
"Pay invoices using credit"
-
+
(> (count ids) 0)
-
+
(format "Pay %d invoices ($%,.2f)"
(count ids)
(or total 0.0))
-
-
+
(or (= 0 (count ids))
(> selected-client-count 1))
(list "Pay " (com/badge {} "!"))
@@ -474,13 +463,11 @@
:else
[:div "Click to choose a bank account"])]]))
-
(defn pay-button [request]
(html-response
(pay-button* {:ids (selected->ids request
(:query-params request))})))
-
;; TODO test as a real user
(def grid-page
(helper/build {:id "entity-table"
@@ -493,9 +480,9 @@
:oob-render
(fn [request]
[(assoc-in (dr/date-range-field {:value {:start (:start-date (:query-params request))
- :end (:end-date (:query-params request))}
- :id "date-range"
- :apply-button? true}) [1 :hx-swap-oob] true)
+ :end (:end-date (:query-params request))}
+ :id "date-range"
+ :apply-button? true}) [1 :hx-swap-oob] true)
(assoc-in (exact-match-id* request) [1 :hx-swap-oob] true)])
:query-schema query-schema
:parse-query-params (fn [p]
@@ -547,12 +534,11 @@
:db/id (:db/id entity))}
svg/undo))
(when (and (can? (:identity request) {:subject :invoice :activity :edit})
- (can-undo-autopayment entity)
- )
+ (can-undo-autopayment entity))
(com/button {:hx-put (bidi/path-for ssr-routes/only-routes
- ::route/undo-autopay
- :db/id (:db/id entity))}
- "Undo autopay"))])
+ ::route/undo-autopay
+ :db/id (:db/id entity))}
+ "Undo autopay"))])
:breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes ::route/page)}
"Invoices"]]
@@ -573,7 +559,7 @@
(= 1 (count (:client/locations (:client args))))))
:render (fn [x] [:div.flex.items-center.gap-2 (-> x :invoice/client :client/name)
(map #(com/pill {:color :primary} (-> % :invoice-expense-account/location))
- (:invoice/expense-accounts x)) ])}
+ (:invoice/expense-accounts x))])}
{:key "vendor"
:name "Vendor"
:sort-key "vendor"
@@ -593,13 +579,12 @@
:name "Due"
:show-starting "xl" ;; xl:table-cell
:render (fn [{:invoice/keys [due]}]
- (if-let [due-date (some-> due (atime/unparse-local atime/normal-date)) ]
- (let [
- today (time/now)
+ (if-let [due-date (some-> due (atime/unparse-local atime/normal-date))]
+ (let [today (time/now)
[start end] (if (time/before? due today)
[due today]
[today due])
- i (time/interval start end )
+ i (time/interval start end)
days (if (time/before? due today)
(- (time/in-days i))
(time/in-days i))]
@@ -607,23 +592,23 @@
[:div.text-primary-700 "today"]
(> days 0)
[:div.text-primary-700 (format "in %d days", days)]
- :else
- [:div.text-red-700 (format "%d days ago", (- days))]))))}
+ :else
+ [:div.text-red-700 (format "%d days ago", (- days))]))))}
{:key "status"
:name "Status"
:render (fn [{:invoice/keys [status scheduled-payment]}]
(cond (= status :invoice-status/paid)
- (com/pill {:color :primary} "Paid")
- (= status :invoice-status/voided)
- (com/pill {:color :red} "Voided")
-
- scheduled-payment
- (com/pill {:color :yellow} "Scheduled")
+ (com/pill {:color :primary} "Paid")
+ (= status :invoice-status/voided)
+ (com/pill {:color :red} "Voided")
- (= status :invoice-status/unpaid)
- (com/pill {:color :secondary} "Unpaid")
- :else
- ""))}
+ scheduled-payment
+ (com/pill {:color :yellow} "Scheduled")
+
+ (= status :invoice-status/unpaid)
+ (com/pill {:color :secondary} "Unpaid")
+ :else
+ ""))}
{:key "accounts"
:name "Account"
:show-starting "lg"
@@ -656,32 +641,32 @@
:class "w-8"
:render (fn [i]
(link-dropdown
- (into []
- (concat (->> i
- :invoice/payments
- (map :invoice-payment/payment)
- (filter (fn [p]
- (not= :payment-status/voided
- (:payment/status p))))
- (mapcat (fn [p]
- (cond-> [{:link (hu/url (bidi/path-for ssr-routes/only-routes
- ::payment-route/all-page)
- {:exact-match-id (:db/id p)})
- :content (str (format "$%,.2f" (:payment/amount p))
- (some-> (:payment/date p) coerce/to-date-time (atime/unparse-local atime/normal-date) (#(str " payment on " %))))}]
- (:payment/transaction p) (conj {:link (hu/url (bidi/path-for ssr-routes/only-routes ::transaction-routes/all-page)
- {:exact-match-id (:db/id (first (:payment/transaction p)))})
- :color :secondary
- :content "Transaction"})))))
- (when (:invoice/journal-entry i)
- [{:link (hu/url (bidi/path-for ssr-routes/only-routes ::ledger-routes/all-page)
- {:exact-match-id (:db/id (first (:invoice/journal-entry i)))})
- :color :yellow
- :content "Ledger entry"}])
- (when (:invoice/source-url i)
- [{:link (:invoice/source-url i)
- :color :secondary
- :content "File"}])))))}]}))
+ (into []
+ (concat (->> i
+ :invoice/payments
+ (map :invoice-payment/payment)
+ (filter (fn [p]
+ (not= :payment-status/voided
+ (:payment/status p))))
+ (mapcat (fn [p]
+ (cond-> [{:link (hu/url (bidi/path-for ssr-routes/only-routes
+ ::payment-route/all-page)
+ {:exact-match-id (:db/id p)})
+ :content (str (format "$%,.2f" (:payment/amount p))
+ (some-> (:payment/date p) coerce/to-date-time (atime/unparse-local atime/normal-date) (#(str " payment on " %))))}]
+ (:payment/transaction p) (conj {:link (hu/url (bidi/path-for ssr-routes/only-routes ::transaction-routes/all-page)
+ {:exact-match-id (:db/id (first (:payment/transaction p)))})
+ :color :secondary
+ :content "Transaction"})))))
+ (when (:invoice/journal-entry i)
+ [{:link (hu/url (bidi/path-for ssr-routes/only-routes ::ledger-routes/all-page)
+ {:exact-match-id (:db/id (first (:invoice/journal-entry i)))})
+ :color :yellow
+ :content "Ledger entry"}])
+ (when (:invoice/source-url i)
+ [{:link (:invoice/source-url i)
+ :color :secondary
+ :content "File"}])))))}]}))
(def row* (partial helper/row* grid-page))
@@ -722,18 +707,16 @@
(defn undo-autopay [{:as request :keys [identity entity]}]
(let [invoice entity
id (:db/id entity)
- _ (assert-can-see-client identity (:db/id (:invoice/client invoice)))
- ]
+ _ (assert-can-see-client identity (:db/id (:invoice/client invoice)))]
(alog/info ::undoing-autopay :transaction :tx)
(assert-can-undo-autopayment invoice)
- (audit-transact
- [[:upsert-invoice {:db/id id
- :invoice/status :invoice-status/unpaid
- :invoice/outstanding-balance (:invoice/total entity)
- :invoice/scheduled-payment nil}]]
+ (audit-transact
+ [[:upsert-invoice {:db/id id
+ :invoice/status :invoice-status/unpaid
+ :invoice/outstanding-balance (:invoice/total entity)
+ :invoice/scheduled-payment nil}]]
identity)
-
(html-response
(row* identity (dc/pull (dc/db conn) default-read id) {:flash? true
:request request})
@@ -847,8 +830,6 @@
id)
(count all-ids)))
-
-
(defn bulk-delete-dialog-confirm [request]
(alog/peek (:form-params request))
(let [ids (selected->ids request (:form-params request))
@@ -861,8 +842,7 @@
(count ids))})})))
#_(defn pay-invoices-from-balance [context {invoices :invoices
- client-id :client_id} _]
- )
+ client-id :client_id} _])
(defn pay-using-credit [request]
(alog/peek (:form-params request))
@@ -896,7 +876,6 @@
0.001))
invoices)
-
total-to-pay (reduce + 0 (map :invoice/outstanding-balance invoices-to-be-paid))
_ (when (<= total-to-pay 0.001)
(throw (ex-info "Select some invoices that need to be paid" {:type :form-validation})))
@@ -926,8 +905,6 @@
[total-to-pay []])))
(into {}))
-
-
vendor-id (:db/id (:invoice/vendor (first invoices)))
payment {:db/id (str vendor-id)
:payment/amount total-to-pay
@@ -949,7 +926,6 @@
:notification (format "Successfully paid %d invoices."
(count invoices))})})))
-
(defn does-amount-exceed-outstanding? [amount outstanding-balance]
(let [outstanding-balance (round-money outstanding-balance)
amount (round-money amount)]
@@ -1018,12 +994,11 @@
:to (mm/encode-step-key :payment-details)})}
"Credit")
(com/button {:x-ref "button"
- "@click.prevent.capture" "$tooltip($refs.tooltip.innerHTML, {allowHTML: true, onMount(i) {htmx.process(i.popper)}, interactive:true, theme:\"light\", timeout:5000})" }
+ "@click.prevent.capture" "$tooltip($refs.tooltip.innerHTML, {allowHTML: true, onMount(i) {htmx.process(i.popper)}, interactive:true, theme:\"light\", timeout:5000})"}
"Pay"))
- [:template { :x-ref "tooltip"}
- [:div.flex.flex-col.gap-2 {
- :data-key "vis"
- :class "p-4 w-max" }
+ [:template {:x-ref "tooltip"}
+ [:div.flex.flex-col.gap-2 {:data-key "vis"
+ :class "p-4 w-max"}
(when (= :bank-account-type/check
(:bank-account/type bank-account))
(com/button {:color :primary
@@ -1094,7 +1069,6 @@
:can-handwrite? can-handwrite?
:credit-only? credit-only?}))
-
(defn can-handwrite? [invoices]
(let [selected-vendors (set (map (comp :db/id :invoice/vendor) invoices))]
(and
@@ -1110,7 +1084,6 @@
(reduce + 0.0 (map :invoice/outstanding-balance is))))
(every? #(<= % 0.0))))
-
(defrecord ChoosePaymentMethodModal [linear-wizard]
mm/ModalWizardStep
(step-name [_]
@@ -1195,7 +1168,7 @@
(format "Pay in full ($%,.2f)" total)))}
{:value "advanced"
:content "Customize payments"}]})
-
+
[:div.space-y-4
(fc/with-field :invoices
(com/validated-field
@@ -1345,7 +1318,6 @@
(:snapshot multi-form-state)
mt/strip-extra-keys-transformer)
-
_ (assert-schema payment-form-schema snapshot)
_ (exception->4xx
@@ -1354,7 +1326,7 @@
(= "" (:check-number snapshot)))
(throw (Exception. "Check number is required")))
true))
-
+
result (exception->4xx
#(do
(when (:handwritten-date snapshot)
@@ -1378,7 +1350,7 @@
:payment-type/credit
:else :payment-type/debit)
identity
- (:handwritten-date snapshot))
+ (:handwritten-date snapshot))
(catch Exception e
(println e))))))]
(modal-response
@@ -1455,11 +1427,10 @@
(defn redirect-handler [target-route]
(fn handle [request]
{:status 302
- :headers {"Location" (str (hu/url (bidi.bidi/path-for ssr-routes/only-routes
+ :headers {"Location" (str (hu/url (bidi.bidi/path-for ssr-routes/only-routes
target-route)
(:query-params request)))}}))
-
(defn initial-bulk-edit-state [request]
(mm/->MultiStepFormState {:search-params (:query-params request)
:expense-accounts [{:db/id "123"
@@ -1479,7 +1450,7 @@
(com/typeahead {:name name
:placeholder "Search..."
:url (hu/url (bidi/path-for ssr-routes/only-routes :account-search)
- { :purpose "invoice"})
+ {:purpose "invoice"})
:id name
:x-model x-model
:value value
@@ -1487,8 +1458,6 @@
(:account/name (d-accounts/clientize (dc/pull (dc/db conn) d-accounts/default-read value)
client-id)))})])
-
-
;; TODO clientize
(defn all-ids-not-locked [all-ids]
(->> all-ids
@@ -1527,7 +1496,7 @@
(com/validated-field
{:errors (fc/field-errors)
:x-hx-val:account-id "accountId"
- :hx-vals (hx/json {:name (fc/field-name) })
+ :hx-vals (hx/json {:name (fc/field-name)})
:x-dispatch:changed "accountId"
:hx-trigger "changed"
:hx-get (bidi/path-for ssr-routes/only-routes ::route/location-select)
@@ -1536,7 +1505,7 @@
(location-select* {:name (fc/field-name)
:account-location (:account/location (cond->> (:account @value)
(nat-int? (:account @value)) (dc/pull (dc/db conn)
- '[:account/location])))
+ '[:account/location])))
:value (fc/field-value)}))))
(fc/with-field :percentage
(com/data-grid-cell
@@ -1544,10 +1513,10 @@
(com/validated-field
{:errors (fc/field-errors)}
(com/money-input {:name (fc/field-name)
- :class "w-16 amount-field"
- :value (some-> (fc/field-value)
- (* 100)
- (long))}))))
+ :class "w-16 amount-field"
+ :value (some-> (fc/field-value)
+ (* 100)
+ (long))}))))
(com/data-grid-cell {:class "align-top"}
(com/a-icon-button {"@click.prevent.stop" "show=false; setTimeout(() => $refs.p.remove(), 500)"} svg/x))))
@@ -1587,7 +1556,7 @@
:hx-get (bidi/path-for ssr-routes/only-routes
::route/bulk-edit-new-account)
:row-offset 0
- :index (count (fc/field-value)) }
+ :index (count (fc/field-value))}
"New account")
(com/data-grid-row {}
(com/data-grid-cell {})
@@ -1617,7 +1586,6 @@
:next-button (com/button {:color :primary :x-ref "next" :class "w-32"} "Save"))
:validation-route ::route/new-wizard-navigate))))
-
(defn maybe-code-accounts [invoice account-rules valid-locations]
(with-precision 2
(let [accounts (vec (mapcat
@@ -1667,9 +1635,9 @@
(navigate [this step-key]
(assoc this :current-step step-key))
(get-current-step [this]
- (if current-step
- (mm/get-step this current-step)
- (mm/get-step this :accounts)))
+ (if current-step
+ (mm/get-step this current-step)
+ (mm/get-step this :accounts)))
(render-wizard [this {:keys [multi-form-state] :as request}]
(mm/default-render-wizard
this request
@@ -1683,44 +1651,43 @@
(get-step [this step-key]
(let [step-key-result (mc/parse mm/step-key-schema step-key)
[step-key-type step-key] step-key-result]
- (get {:accounts (->AccountsStep this) }
+ (get {:accounts (->AccountsStep this)}
step-key)))
(form-schema [_]
(mc/schema [:map
[:expense-accounts
- (many-entity {:min 1}
- [:account entity-id]
- [:location [:string {:min 1 :error/message "required"}]]
- [:percentage percentage])]]))
+ (many-entity {:min 1}
+ [:account entity-id]
+ [:location [:string {:min 1 :error/message "required"}]]
+ [:percentage percentage])]]))
(submit [this {:keys [multi-form-state request-method identity] :as request}]
- (let [selected-ids (selected->ids (assoc request :query-params (:search-params (:snapshot multi-form-state))) (:search-params (:snapshot multi-form-state)))
- all-ids (all-ids-not-locked selected-ids)
- invoices (pull-many (dc/db conn) '[:db/id :invoice/total {:invoice/client [:client/locations]}] (vec all-ids)) ]
-(assert-percentages-add-up (:snapshot multi-form-state))
-
- (doseq [a (-> multi-form-state :snapshot :expense-accounts)
- :let [{:keys [:account/location :account/name]} (dc/pull (dc/db conn) [:account/location :account/name] (:account a))]]
- (when (and location (not= location (:location a)))
- (let [err (str "Account " name " uses location " (:location a) ", but is supposed to be " location)]
- (throw (ex-info err {:validation-error err})))))
- (alog/info ::bulk-code :count (count all-ids))
- (audit-transact-batch
- (map (fn [i]
- [:upsert-invoice {:db/id (:db/id i)
- :invoice/expense-accounts (maybe-code-accounts i (-> multi-form-state :snapshot :expense-accounts) (-> i :invoice/client :client/locations))}])
- invoices)
- (:identity request))
-
- (html-response
- [:div]
- :headers (cond-> {"hx-trigger" (hx/json { "modalclose" ""
- "invalidated" ""
- "notification" (str "Successfully coded " (count all-ids) " invoices.")})
- "hx-reswap" "outerHTML"})))))
+ (let [selected-ids (selected->ids (assoc request :query-params (:search-params (:snapshot multi-form-state))) (:search-params (:snapshot multi-form-state)))
+ all-ids (all-ids-not-locked selected-ids)
+ invoices (pull-many (dc/db conn) '[:db/id :invoice/total {:invoice/client [:client/locations]}] (vec all-ids))]
+ (assert-percentages-add-up (:snapshot multi-form-state))
+
+ (doseq [a (-> multi-form-state :snapshot :expense-accounts)
+ :let [{:keys [:account/location :account/name]} (dc/pull (dc/db conn) [:account/location :account/name] (:account a))]]
+ (when (and location (not= location (:location a)))
+ (let [err (str "Account " name " uses location " (:location a) ", but is supposed to be " location)]
+ (throw (ex-info err {:validation-error err})))))
+ (alog/info ::bulk-code :count (count all-ids))
+ (audit-transact-batch
+ (map (fn [i]
+ [:upsert-invoice {:db/id (:db/id i)
+ :invoice/expense-accounts (maybe-code-accounts i (-> multi-form-state :snapshot :expense-accounts) (-> i :invoice/client :client/locations))}])
+ invoices)
+ (:identity request))
+
+ (html-response
+ [:div]
+ :headers (cond-> {"hx-trigger" (hx/json {"modalclose" ""
+ "invalidated" ""
+ "notification" (str "Successfully coded " (count all-ids) " invoices.")})
+ "hx-reswap" "outerHTML"})))))
(def bulk-edit-wizard (->BulkEditWizard nil nil))
-
(defn bulk-edit-total* [request]
(let [total (->> (-> request
:multi-form-state
@@ -1740,7 +1707,7 @@
(filter number?)
(reduce + 0.0))
balance (- 100.0
- (* 100.0 total))]
+ (* 100.0 total))]
[:span {:class (when-not (dollars= 0.0 balance)
"text-red-300")}
(format "%.1f%%" balance)]))
@@ -1769,31 +1736,31 @@
::route/legacy-voided-invoices (redirect-handler ::route/voided-page)
::route/legacy-new-invoice (redirect-handler ::route/new-wizard)
::route/bulk-edit (-> mm/open-wizard-handler
- (mm/wrap-wizard bulk-edit-wizard)
- (mm/wrap-init-multi-form-state initial-bulk-edit-state))
-::route/bulk-edit-submit (-> mm/submit-handler
- (mm/wrap-wizard bulk-edit-wizard)
- (mm/wrap-decode-multi-form-state)
- (wrap-must {:subject :invoice :activity :bulk-edit}))
-::route/bulk-edit-total (-> bulk-edit-total
- (mm/wrap-wizard bulk-edit-wizard)
- (mm/wrap-decode-multi-form-state)
- (wrap-must {:subject :invoice :activity :bulk-edit}))
-::route/bulk-edit-balance (-> bulk-edit-balance
-
- (mm/wrap-wizard bulk-edit-wizard)
- (mm/wrap-decode-multi-form-state)
- (wrap-must {:subject :invoice :activity :bulk-edit}))
-::route/bulk-edit-new-account (->
- (add-new-entity-handler [:step-params :expense-accounts]
- (fn render [cursor request]
- (bulk-edit-account-row*
- {:value cursor }))
- (fn build-new-row [base _]
- (assoc base :invoice-expense-account/location "Shared")))
- (wrap-schema-enforce :query-schema [:map
- [:client-id {:optional true}
- [:maybe entity-id]]]))
+ (mm/wrap-wizard bulk-edit-wizard)
+ (mm/wrap-init-multi-form-state initial-bulk-edit-state))
+ ::route/bulk-edit-submit (-> mm/submit-handler
+ (mm/wrap-wizard bulk-edit-wizard)
+ (mm/wrap-decode-multi-form-state)
+ (wrap-must {:subject :invoice :activity :bulk-edit}))
+ ::route/bulk-edit-total (-> bulk-edit-total
+ (mm/wrap-wizard bulk-edit-wizard)
+ (mm/wrap-decode-multi-form-state)
+ (wrap-must {:subject :invoice :activity :bulk-edit}))
+ ::route/bulk-edit-balance (-> bulk-edit-balance
+
+ (mm/wrap-wizard bulk-edit-wizard)
+ (mm/wrap-decode-multi-form-state)
+ (wrap-must {:subject :invoice :activity :bulk-edit}))
+ ::route/bulk-edit-new-account (->
+ (add-new-entity-handler [:step-params :expense-accounts]
+ (fn render [cursor request]
+ (bulk-edit-account-row*
+ {:value cursor}))
+ (fn build-new-row [base _]
+ (assoc base :invoice-expense-account/location "Shared")))
+ (wrap-schema-enforce :query-schema [:map
+ [:client-id {:optional true}
+ [:maybe entity-id]]]))
::route/undo-autopay (-> undo-autopay
(wrap-entity [:route-params :db/id] default-read)
diff --git a/src/clj/auto_ap/ssr/ledger.clj b/src/clj/auto_ap/ssr/ledger.clj
index f89ac592..4a24f42d 100644
--- a/src/clj/auto_ap/ssr/ledger.clj
+++ b/src/clj/auto_ap/ssr/ledger.clj
@@ -1,8 +1,8 @@
(ns auto-ap.ssr.ledger
(:require
[auto-ap.datomic
- :refer [audit-transact audit-transact-batch conn pull-many
- remove-nils]]
+ :refer [audit-transact audit-transact-batch conn pull-many
+ remove-nils]]
[auto-ap.datomic.accounts :as a]
[auto-ap.graphql.utils :refer [assert-admin assert-can-see-client
exception->notification notify-if-locked]]
@@ -11,7 +11,7 @@
[auto-ap.query-params :refer [wrap-copy-qp-pqp]]
[auto-ap.routes.ledger :as route]
[auto-ap.routes.utils
- :refer [wrap-client-redirect-unauthenticated]]
+ :refer [wrap-client-redirect-unauthenticated]]
[auto-ap.solr :as solr]
[auto-ap.ssr-routes :as ssr-routes]
[auto-ap.ssr.components :as com]
@@ -30,11 +30,11 @@
[auto-ap.ssr.svg :as svg]
[auto-ap.ssr.ui :refer [base-page]]
[auto-ap.ssr.utils
- :refer [apply-middleware-to-all-handlers clj-date-schema
- html-response main-transformer money strip
- wrap-form-4xx-2 wrap-implied-route-param
- wrap-merge-prior-hx wrap-schema-decode
- wrap-schema-enforce]]
+ :refer [apply-middleware-to-all-handlers clj-date-schema
+ html-response main-transformer money strip
+ wrap-form-4xx-2 wrap-implied-route-param
+ wrap-merge-prior-hx wrap-schema-decode
+ wrap-schema-enforce]]
[auto-ap.time :as atime]
[auto-ap.utils :refer [dollars=]]
[bidi.bidi :as bidi]
@@ -50,8 +50,6 @@
[malli.core :as mc]
[slingshot.slingshot :refer [throw+]]))
-
-
(comment
(mc/decode query-schema
{:start " "}
@@ -67,14 +65,10 @@
(assoc-in [:query-params :start] 0)
(assoc-in [:query-params :per-page] 250))))
-
:else
selected)]
ids))
-
-
-
(defn delete [{invoice :entity :as request identity :identity}]
(exception->notification
#(when-not (= :invoice-status/unpaid (:invoice/status invoice))
@@ -101,10 +95,9 @@
identity)
(html-response (ledger.common/row* (:identity request) (dc/pull (dc/db conn) default-read (:db/id invoice))
- {:class "live-removed"})
+ {:class "live-removed"})
:headers {"hx-retarget" (format "#entity-table tr[data-id=\"%d\"]" (:db/id invoice))}))
-
(defn wrap-ensure-bank-account-belongs [handler]
(fn [{:keys [query-params client] :as request}]
(let [bank-account-belongs? (get (set (map :db/id (:client/bank-accounts client)))
@@ -131,7 +124,7 @@
(clojure.pprint/pprint (fc/field-errors))
(when (seq (fc/field-value))
- [:div {:x-data (hx/json { "showTable" false})}
+ [:div {:x-data (hx/json {"showTable" false})}
[:form {:hx-post (bidi.bidi/path-for ssr-routes/only-routes ::route/external-import-import)
:autocomplete "off"}
(when (:just-parsed? request)
@@ -140,7 +133,7 @@
[:div.inline-flex.gap-2
(->> (:form-errors request)
:table
- ( #(if (map? %) ( vals %) %))
+ (#(if (map? %) (vals %) %))
(mapcat identity)
(group-by last)
(map (fn [[k v]]
@@ -148,12 +141,12 @@
(com/pill {:color :yellow}
(format "%d warnings" (count v)))
(com/pill {:color :red}
- (format "%d errors" (count v)))))))] ])
+ (format "%d errors" (count v)))))))]])
[:div.flex.gap-4.items-center
(com/checkbox {"@click" "showTable=!showTable"}
"Show table")
(com/button {:color :primary} "Import")]
- [:div { :x-show "showTable"}
+ [:div {:x-show "showTable"}
(com/data-grid-card {:id "ledger-import-data"
:route nil
:title "Data to import"
@@ -230,11 +223,11 @@
(let [errors (seq (fc/field-errors))]
(cond errors
[:div
- { "x-tooltip" "{content: ()=>$refs.tt.innerHTML , allowHTML: true}"}
+ {"x-tooltip" "{content: ()=>$refs.tt.innerHTML , allowHTML: true}"}
[:div.w-8.h-8.rounded-full.p-2.flex.items-start {:class
(if (seq (filter
(fn [[_ status]]
-
+
(= :error status))
errors))
"bg-red-50 text-red-300"
@@ -246,29 +239,29 @@
[:li m])]]]
:else
nil))]))))}
-
+
[:div.flex.m-4.flex-row-reverse
(com/button {:color :primary} "Import")])]]])))])
(defn external-import-text-form* [request]
- (fc/start-form
- (or (:form-params request) {}) (:form-errors request)
- [:form#parse-form {:x-data (hx/json {"clipboard" nil})
- :hx-post (bidi.bidi/path-for ssr-routes/only-routes ::route/external-import-parse)
- :hx-swap "outerHTML"
- :hx-trigger "pasted"}
- (fc/with-field :table
- [:div
- (com/errors {:errors (fc/field-errors)})
- (com/text-area {:x-model "clipboard" :name (fc/field-name) :value (fc/field-value) :class "hidden"})])
- (com/button {"@click.prevent" "clipboard = (await getclpboard()); $nextTick(() => $dispatch('pasted'))"
- "x-on:paste.document" "clipboard = (await getclpboard()); console.log(clipboard); $nextTick(() => $dispatch('pasted'))"}
- "Load from clipboard")]))
-
+ (fc/start-form
+ (or (:form-params request) {}) (:form-errors request)
+ [:form#parse-form {:x-data (hx/json {"clipboard" nil})
+ :hx-post (bidi.bidi/path-for ssr-routes/only-routes ::route/external-import-parse)
+ :hx-swap "outerHTML"
+ :hx-trigger "pasted"}
+ (fc/with-field :table
+ [:div
+ (com/errors {:errors (fc/field-errors)})
+ (com/text-area {:x-model "clipboard" :name (fc/field-name) :value (fc/field-value) :class "hidden"})])
+ (com/button {"@click.prevent" "clipboard = (await getclpboard()); $nextTick(() => $dispatch('pasted'))"
+ "x-on:paste.document" "clipboard = (await getclpboard()); console.log(clipboard); $nextTick(() => $dispatch('pasted'))"}
+ "Load from clipboard")]))
+
(defn external-import-form* [request]
[:div#forms {:hx-target "this"
:hx-swap "outerHTML"}
- (when (and (not (:just-parsed? request))
+ (when (and (not (:just-parsed? request))
(seq (->> (:form-errors request)
:table
vals
@@ -289,14 +282,14 @@
:client (:client request)
:identity (:identity request)
:request request}
- (com/breadcrumbs {}
+ (com/breadcrumbs {}
[:a {:href (bidi/path-for ssr-routes/only-routes ::route/all-page)}
"Ledger"]
[:a {:href (bidi/path-for ssr-routes/only-routes ::route/external-import-page)}
"Import"])
#_(when (:above-grid grid-spec)
- ( (:above-grid grid-spec) request))
-
+ ((:above-grid grid-spec) request))
+
[:script
(hiccup/raw
"async function getclpboard() {
@@ -306,7 +299,7 @@
console.log(r)
return await r.text()
}")]
-
+
(external-import-form* request)
[:div #_{:x-data (hx/json {:selected [] :all_selected false :type (:entity-name grid-spec)})
"x-on:copy" "if (selected.length > 0) {$clipboard(JSON.stringify({'type': type, 'selected': selected}))}"
@@ -322,23 +315,21 @@
#_(if (string? (:title grid-spec))
(:title grid-spec)
((:title grid-spec) request))))
-
-
(defn trim-header [t]
(if (->> t
- first
- (map clojure.string/lower-case)
- (filter #{"id" "client" "source" "vendor" "date" "account" "location" "debit"})
- seq)
- (drop 1 t)
- t))
+ first
+ (map clojure.string/lower-case)
+ (filter #{"id" "client" "source" "vendor" "date" "account" "location" "debit"})
+ seq)
+ (drop 1 t)
+ t))
(defn tsv->import-data [data]
(if (string? data)
(with-open [r (io/reader (char-array data))]
(into [] (filter (fn filter-row [r]
- (seq (filter (comp not-empty #(str/replace % #"\s+" "")) r))))
+ (seq (filter (comp not-empty #(str/replace % #"\s+" "")) r))))
(trim-header (csv/read-csv r :separator \tab))))
data))
@@ -347,52 +338,45 @@
[:bank-account
[:string]]]))
-
-
(def parse-form-schema (mc/schema
- [:map
+ [:map
[:table {:min 1 :error/message "Clipboard should contain rows to import"
- :decode/string tsv->import-data}
+ :decode/string tsv->import-data}
[:vector {:coerce? true}
- [:map { :decode/arbitrary (fn [t]
+ [:map {:decode/arbitrary (fn [t]
(if (vector? t)
(into {} (map vector [:external-id :client-code :source :vendor-name :date :account-code :location :debit :credit] t))
t))}
- [:external-id [:string {:title "external id"
- :min 1
- :decode/string strip}]]
- [:client-code [:string {:title "client code"
- :min 1
- :decode/string strip}]]
- [:source [:string {:title "source"
- :min 1
- :decode/string strip}]]
- [:vendor-name [:string {:min 1 :decode/string strip}]]
- [:date [:and clj-date-schema
- [:any {:title "date"}]]]
- [:account-code account-schema]
-
- [:location [:string { :min 1
- :max 2
- :decode/string strip}]]
- [:debit [:maybe money]]
- [:credit [:maybe money]]]]
-
+ [:external-id [:string {:title "external id"
+ :min 1
+ :decode/string strip}]]
+ [:client-code [:string {:title "client code"
+ :min 1
+ :decode/string strip}]]
+ [:source [:string {:title "source"
+ :min 1
+ :decode/string strip}]]
+ [:vendor-name [:string {:min 1 :decode/string strip}]]
+ [:date [:and clj-date-schema
+ [:any {:title "date"}]]]
+ [:account-code account-schema]
+
+ [:location [:string {:min 1
+ :max 2
+ :decode/string strip}]]
+ [:debit [:maybe money]]
+ [:credit [:maybe money]]]]
+
#_[:string {:decode/string tsv->import-data
:error/message "Clipboard should contain rows to import"}]]]))
-
-
-
-
(defn external-import-parse [request]
- (html-response
- ( external-import-form* (assoc request :just-parsed? true))))
+ (html-response
+ (external-import-form* (assoc request :just-parsed? true))))
(defn line->id [{:keys [source external-id client-code]}]
(str client-code "-" source "-" external-id))
-
(defn add-errors [entry all-vendors all-accounts client-locked-lookup all-client-bank-accounts all-client-locations]
(let [vendor (all-vendors (:vendor-name entry))
locked-until (client-locked-lookup (:client-code entry))
@@ -413,29 +397,29 @@
entry (cond
(not locked-until)
(all-row-error (str "Client '" (:client-code entry) "' not found."))
-
+
(not vendor)
(all-row-error (str "Vendor '" (:vendor-name entry) "' not found."))
-
+
(and locked-until
(and (not (t/after? (:date entry)
(coerce/to-date-time locked-until)))
(not (t/equal? (:date entry)
(coerce/to-date-time locked-until)))))
(all-row-error (str "Client's data is locked until " locked-until))
- (not (dollars= (reduce (fnil + 0.0 0.0) 0.0 (map :debit (:line-items entry)))
- (reduce (fnil + 0.0 0.0) 0.0 (map :credit (:line-items entry)))))
- (all-row-error (str "Debits '"
- (reduce (fnil + 0.0 0.0) 0 (map :debit (:line-items entry)))
- "' and credits '"
- (reduce (fnil + 0.0 0.0) 0 (map :credit (:line-items entry)))
- "' do not add up."))
- (dollars= (reduce (fnil + 0.0 0.0) 0.0 (map :debit (:line-items entry)))
- 0.0)
- (all-row-error (str "Cannot have ledger entries that total $0.00") :warn)
-
- :else
- entry)]
+ (not (dollars= (reduce (fnil + 0.0 0.0) 0.0 (map :debit (:line-items entry)))
+ (reduce (fnil + 0.0 0.0) 0.0 (map :credit (:line-items entry)))))
+ (all-row-error (str "Debits '"
+ (reduce (fnil + 0.0 0.0) 0 (map :debit (:line-items entry)))
+ "' and credits '"
+ (reduce (fnil + 0.0 0.0) 0 (map :credit (:line-items entry)))
+ "' do not add up."))
+ (dollars= (reduce (fnil + 0.0 0.0) 0.0 (map :debit (:line-items entry)))
+ 0.0)
+ (all-row-error (str "Cannot have ledger entries that total $0.00") :warn)
+
+ :else
+ entry)]
(update
entry
:line-items
@@ -466,7 +450,6 @@
(:account-code ea))))
(row-error ea (str "Bank Account '" (:account-code ea) "' not found."))
-
(and matching-account
(:account/location matching-account)
(not= (:account/location matching-account)
@@ -494,12 +477,11 @@
(let [lines-with-indexes (for [[i l] (map vector (range) table)]
(assoc l :index i))]
(into []
- (for [
- [_ lines] (group-by line->id lines-with-indexes)
- :let [{:keys [source client-code date vendor-name note cleared-against] :as line} (first lines)]]
+ (for [[_ lines] (group-by line->id lines-with-indexes)
+ :let [{:keys [source client-code date vendor-name note cleared-against] :as line} (first lines)]]
(add-errors {:source source
:indices (map :index lines)
- :external-id (line->id line)
+ :external-id (line->id line)
:client-code client-code
:date date
:note note
@@ -515,9 +497,9 @@
:debit debit
:credit credit})
lines)}
- all-vendors
- all-accounts
- client-locked-lookup
+ all-vendors
+ all-accounts
+ client-locked-lookup
all-client-bank-accounts
all-client-locations)))))
@@ -645,7 +627,7 @@
good-entries (filter (fn [e] (and (not (:error (entry-error-types e))) (not (:warn (entry-error-types e))))) entries)
bad-entries (filter (fn [e] (:error (entry-error-types e))) entries)
form-errors (reduce (fn [acc [path m status]]
- (update-in acc path conj [ m status]))
+ (update-in acc path conj [m status]))
{}
errors)
_ (when (seq bad-entries)
@@ -654,7 +636,7 @@
{:type :field-validation
:form-errors form-errors
:form-params form-params})))
-
+
retraction (mapv (fn [x] [:db/retractEntity [:journal-entry/external-id (:external-id x)]])
good-entries)
ignore-retraction (->> ignored-entries
@@ -696,21 +678,20 @@
(defn external-import-import [request]
(let [result (import-ledger request)]
- (html-response
- [:div
+ (html-response
+ [:div
(external-import-form* (assoc request :form-errors (:form-errors result)))]
- :headers {"hx-trigger" (hx/json { "notification" (format "%d successful, %d with warnings. Any ledger entries with warnings have been removed." (:successful result) (:ignored result))})})))
-
+ :headers {"hx-trigger" (hx/json {"notification" (format "%d successful, %d with warnings. Any ledger entries with warnings have been removed." (:successful result) (:ignored result))})})))
(def key->handler
- (merge
+ (merge
(apply-middleware-to-all-handlers
(->
{::route/all-page (-> (helper/page-route grid-page)
(wrap-implied-route-param :external? false))
::route/external-page (-> (helper/page-route grid-page)
(wrap-implied-route-param :external? true))
-
+
::route/table (helper/table-route grid-page)
::route/csv (helper/csv-route grid-page)
::route/external-import-page external-import-page
@@ -739,4 +720,4 @@
profit-and-loss/key->handler
cash-flows/key->handler
investigate/key->handler
- new/key->handler))
\ No newline at end of file
+ new/key->handler))
\ No newline at end of file
diff --git a/src/clj/auto_ap/ssr/ledger/balance_sheet.clj b/src/clj/auto_ap/ssr/ledger/balance_sheet.clj
index 491fbfec..45f6b56d 100644
--- a/src/clj/auto_ap/ssr/ledger/balance_sheet.clj
+++ b/src/clj/auto_ap/ssr/ledger/balance_sheet.clj
@@ -2,7 +2,7 @@
(:require
[amazonica.aws.s3 :as s3]
[auto-ap.datomic
- :refer [conn pull-many]]
+ :refer [conn pull-many]]
[auto-ap.graphql.utils :refer [assert-can-see-client]]
[auto-ap.ledger :refer [build-account-lookup upsert-running-balance]]
[auto-ap.ledger.reports :as l-reports]
@@ -11,7 +11,7 @@
[auto-ap.permissions :refer [wrap-must]]
[auto-ap.routes.ledger :as route]
[auto-ap.routes.utils
- :refer [wrap-client-redirect-unauthenticated]]
+ :refer [wrap-client-redirect-unauthenticated]]
[auto-ap.ssr-routes :as ssr-routes]
[auto-ap.ssr.components :as com]
[auto-ap.ssr.form-cursor :as fc]
@@ -20,9 +20,9 @@
[auto-ap.ssr.svg :as svg]
[auto-ap.ssr.ui :refer [base-page]]
[auto-ap.ssr.utils
- :refer [apply-middleware-to-all-handlers clj-date-schema
- html-response modal-response wrap-form-4xx-2
- wrap-schema-enforce]]
+ :refer [apply-middleware-to-all-handlers clj-date-schema
+ html-response modal-response wrap-form-4xx-2
+ wrap-schema-enforce]]
[auto-ap.time :as atime]
[bidi.bidi :as bidi]
[clj-pdf.core :as pdf]
@@ -38,42 +38,38 @@
[java.util UUID]
[org.apache.commons.io.output ByteArrayOutputStream]))
-
-
-
-
(def query-schema (mc/schema
[:maybe [:map
- [:client {:unspecified/value :all}
- [:or
- [:enum :all]
- [:vector {:coerce? true :min 1}
- [:entity-map {:pull [:db/id :client/name]}]]]]
+ [:client {:unspecified/value :all}
+ [:or
+ [:enum :all]
+ [:vector {:coerce? true :min 1}
+ [:entity-map {:pull [:db/id :client/name]}]]]]
[:include-deltas {:default false}
- [:boolean {:decode/string {:enter #(if (= % "on") true
-
- (boolean %))}}]]
- [:date {:unspecified/fn (fn [] [(atime/local-now)])}
- [:vector {:coerce? true
- :decode/string (fn [s] (if (string? s) (str/split s #", ")
- s))}
- clj-date-schema]] ]]))
+ [:boolean {:decode/string {:enter #(if (= % "on") true
+
+ (boolean %))}}]]
+ [:date {:unspecified/fn (fn [] [(atime/local-now)])}
+ [:vector {:coerce? true
+ :decode/string (fn [s] (if (string? s) (str/split s #", ")
+ s))}
+ clj-date-schema]]]]))
;; TODO
;; 1. Rerender form when running
;; 2. Don't throw crazy errors when missing a field
;; 3. General cleanup of the patterns in run-balance-sheet
;; 4. Review ledger dialog
-(defn get-report [{ {:keys [date client] :as qp} :query-params :as request}]
+(defn get-report [{{:keys [date client] :as qp} :query-params :as request}]
(when (and date client)
(let [client (if (= :all client) (take 5 (:clients request)) client)
- date (reverse (sort date ))
+ date (reverse (sort date))
client-ids (map :db/id client)
_ (doseq [client-id client-ids]
(assert-can-see-client (:identity request) client-id))
-
+
_ (upsert-running-balance (into #{} client-ids))
-
+
lookup-account (->> client-ids
(map (fn build-lookup [client-id]
[client-id (build-account-lookup client-id)]))
@@ -97,50 +93,50 @@
args (assoc (:query-params request)
:periods (map coerce/to-date (filter identity date)))
clients (pull-many (dc/db conn) [:client/code :client/name :db/id] client-ids)
-
+
pnl-data (l-reports/->PNLData args data (by :db/id :client/code clients))
- report (l-reports/summarize-balance-sheet pnl-data) ]
+ report (l-reports/summarize-balance-sheet pnl-data)]
(alog/info ::balance-sheet :params args)
{:data report
:report report})))
-(defn maybe-trim-clients [request client ]
+(defn maybe-trim-clients [request client]
(if (= :all client)
(cond-> {:client (take 20 (:clients request))}
(> (count (:clients request)) 20)
(assoc :warning "You requested a report with more than 20 clients. This report will only contain the first 20."))
{:client client}))
-(defn balance-sheet* [{ {:keys [date client] } :query-params :as request}]
- [:div#report
+(defn balance-sheet* [{{:keys [date client]} :query-params :as request}]
+ [:div#report
(when (and date client)
(let [{:keys [client warning]} (maybe-trim-clients request client)
{:keys [data report]} (get-report (assoc-in request [:query-params :client] client))
- client-count (count (set (map :client-id (:data data)))) ]
- (list
- [:div.text-2xl.font-bold.text-gray-600 (str "Balance Sheet - " (str/join ", " (map :client/name client))) ]
- (rtable/table {:widths (cond-> (into [30 ] (repeat 13 client-count))
- (> (count date) 1) (into (repeat 13 (* 2 client-count (dec (count date)))))
- (and (> client-count 1) (= (count date) 1)) (conj 13))
- :investigate-url (bidi.bidi/path-for ssr-routes/only-routes ::route/investigate)
- :table report
- :warning (not-empty (str/join "\n " (filter not-empty [warning (:warning report)])))} ))))])
+ client-count (count (set (map :client-id (:data data))))]
+ (list
+ [:div.text-2xl.font-bold.text-gray-600 (str "Balance Sheet - " (str/join ", " (map :client/name client)))]
+ (rtable/table {:widths (cond-> (into [30] (repeat 13 client-count))
+ (> (count date) 1) (into (repeat 13 (* 2 client-count (dec (count date)))))
+ (and (> client-count 1) (= (count date) 1)) (conj 13))
+ :investigate-url (bidi.bidi/path-for ssr-routes/only-routes ::route/investigate)
+ :table report
+ :warning (not-empty (str/join "\n " (filter not-empty [warning (:warning report)])))}))))])
(defn form* [request & children]
(let [params (or (:query-params request) {})]
(fc/start-form
- params
+ params
(:form-errors request)
[:div#balance-sheet-form.flex.flex-col.gap-4.mt-4
- [:div.flex.gap-8
- [:form {:hx-get (bidi.bidi/path-for ssr-routes/only-routes ::route/run-balance-sheet)
+ [:div.flex.gap-8
+ [:form {:hx-get (bidi.bidi/path-for ssr-routes/only-routes ::route/run-balance-sheet)
:hx-target "#balance-sheet-form"
:hx-swap "outerHTML"
:hx-disabled-elt "find fieldset"}
- [:fieldset
+ [:fieldset
[:div.flex.gap-8 {:x-data (hx/json {})}
(fc/with-field :client
- (com/validated-inline-field
+ (com/validated-inline-field
{:label "Customers" :errors (fc/field-errors)}
(com/multi-typeahead {:name (fc/field-name)
:placeholder "Search for companies..."
@@ -152,7 +148,7 @@
:content-fn :client/name})))
(fc/with-field :date
(com/validated-inline-field {:label "Date"
- :errors (fc/field-errors)}
+ :errors (fc/field-errors)}
(com/dates-dropdown {:value (fc/field-value)
:name (fc/field-name)})))
(fc/with-field :include-deltas
@@ -161,7 +157,7 @@
"Include Deltas"))
(com/button {:color :primary :class "w-32"}
"Run")
- (com/button {:formaction (bidi.bidi/path-for ssr-routes/only-routes ::route/export-balance-sheet) } "Export PDF")]]] ]
+ (com/button {:formaction (bidi.bidi/path-for ssr-routes/only-routes ::route/export-balance-sheet)} "Export PDF")]]]]
children])))
(defn form [request]
@@ -174,47 +170,47 @@
(base-page
request
(com/page {:nav com/main-aside-nav
-
+
:client-selection (:client-selection request)
:clients (:clients request)
:client (:client request)
:identity (:identity request)
:request request}
(apply com/breadcrumbs {} [[:a {:href (bidi/path-for ssr-routes/only-routes ::route/page)}
- "Ledger"]])
+ "Ledger"]])
(form* request))
"Balance Sheet"))
(defn make-balance-sheet-pdf [request report]
-
+
(let [output-stream (ByteArrayOutputStream.)
client-count (count (or (seq (:client (:query-params request)))
(seq (:client (:form-params request)))))
- date (:date (:query-params request)) ]
+ date (:date (:query-params request))]
(pdf/pdf
- (-> [{:left-margin 10 :right-margin 10 :top-margin 15 :bottom-margin 15
- :size :letter
- :font {:size 6
- :ttf-name "fonts/calibri-light.ttf"}}
- [:heading (str "Balance Sheet - " (str/join ", " (map :client/name (or (seq (:client (:query-params request)))
- (seq (:client (:form-params request)))))))]]
+ (-> [{:left-margin 10 :right-margin 10 :top-margin 15 :bottom-margin 15
+ :size :letter
+ :font {:size 6
+ :ttf-name "fonts/calibri-light.ttf"}}
+ [:heading (str "Balance Sheet - " (str/join ", " (map :client/name (or (seq (:client (:query-params request)))
+ (seq (:client (:form-params request)))))))]]
- (conj [:paragraph {:color [128 0 0] :size 9} (:warning report)])
- (conj
- (table->pdf report
- (cond-> (into [30 ] (repeat client-count 13))
- (> (count date) 1) (into (repeat (* 2 client-count (dec (count date))) 13 ))
- (and (> client-count 1) (= (count date) 1)) (conj 13)))))
- output-stream)
+ (conj [:paragraph {:color [128 0 0] :size 9} (:warning report)])
+ (conj
+ (table->pdf report
+ (cond-> (into [30] (repeat client-count 13))
+ (> (count date) 1) (into (repeat (* 2 client-count (dec (count date))) 13))
+ (and (> client-count 1) (= (count date) 1)) (conj 13)))))
+ output-stream)
(.toByteArray output-stream)))
(defn join-names [client-ids]
- (str/replace (->> client-ids (pull-many (dc/db conn) [:client/name]) (map :client/name) (str/join "-")) #"[^\w]" "_" ))
+ (str/replace (->> client-ids (pull-many (dc/db conn) [:client/name]) (map :client/name) (str/join "-")) #"[^\w]" "_"))
(defn balance-sheet-args->name [request]
(let [date (atime/unparse-local
- (:date (:query-params request))
- atime/iso-date)
+ (:date (:query-params request))
+ atime/iso-date)
name (->> request :query-params :client (map :db/id) join-names)]
(format "Balance-sheet-%s-for-%s" date name)))
@@ -258,8 +254,7 @@
[:span.text-gray-800
"Click "
(com/link {:href (:report/url bs)} "here")
- " to download"]
- ]))
+ " to download"]]))
nil))
:headers (-> {}
(assoc "hx-retarget" ".modal-stack")
@@ -269,15 +264,14 @@
(apply-middleware-to-all-handlers
(->
{::route/balance-sheet (-> balance-sheet
- (wrap-schema-enforce :query-schema query-schema)
- (wrap-form-4xx-2 balance-sheet))
+ (wrap-schema-enforce :query-schema query-schema)
+ (wrap-form-4xx-2 balance-sheet))
::route/run-balance-sheet (-> form
(wrap-schema-enforce :query-schema query-schema)
(wrap-form-4xx-2 form))
::route/export-balance-sheet (-> export
- (wrap-schema-enforce :query-schema query-schema)
- (wrap-form-4xx-2 form))}
- )
+ (wrap-schema-enforce :query-schema query-schema)
+ (wrap-form-4xx-2 form))})
(fn [h]
(-> h
#_(wrap-merge-prior-hx)
diff --git a/src/clj/auto_ap/ssr/ledger/cash_flows.clj b/src/clj/auto_ap/ssr/ledger/cash_flows.clj
index 53fc1e2c..c5ad8ed3 100644
--- a/src/clj/auto_ap/ssr/ledger/cash_flows.clj
+++ b/src/clj/auto_ap/ssr/ledger/cash_flows.clj
@@ -38,39 +38,37 @@
[java.util UUID]
[org.apache.commons.io.output ByteArrayOutputStream]))
-
(def query-schema (mc/schema
[:maybe [:map
- [:client {:unspecified/value :all}
- [:or
- [:enum :all]
- [:vector {:coerce? true :min 1}
- [:entity-map {:pull [:db/id :client/name]}]]]]
-
- [:periods {:unspecified/fn (fn [] (let [now (atime/local-now)]
- [{:start (atime/as-local-time (time/date-time (time/year now)
- 1
- 1))
- :end (atime/local-now)}])
+ [:client {:unspecified/value :all}
+ [:or
+ [:enum :all]
+ [:vector {:coerce? true :min 1}
+ [:entity-map {:pull [:db/id :client/name]}]]]]
- )}
- [:vector {:coerce? true}
- [:map
- [:start clj-date-schema]
- [:end clj-date-schema]]]]]]))
+ [:periods {:unspecified/fn (fn [] (let [now (atime/local-now)]
+ [{:start (atime/as-local-time (time/date-time (time/year now)
+ 1
+ 1))
+ :end (atime/local-now)}]))}
+
+ [:vector {:coerce? true}
+ [:map
+ [:start clj-date-schema]
+ [:end clj-date-schema]]]]]]))
;; TODO
;; 1. Rerender form when running
;; 2. Don't throw crazy errors when missing a field
;; 3. General cleanup of the patterns in run-balance-sheet
;; 4. Review ledger dialog
-(defn get-report [{ {:keys [periods client] :as qp} :form-params :as request}]
+(defn get-report [{{:keys [periods client] :as qp} :form-params :as request}]
(when (and (seq periods) client)
(let [client (if (= :all client) (take 5 (:clients request)) client)
client-ids (map :db/id client)
_ (doseq [client-id client-ids]
(assert-can-see-client (:identity request) client-id))
-
+
lookup-account (->> client-ids
(map (fn build-lookup [client-id]
[client-id (build-account-lookup client-id)]))
@@ -90,11 +88,11 @@
:account-type (:account_type account)
:numeric-code (:numeric_code account)
:name (:name account)
- :period {:start ( coerce/to-date (:start p)) :end ( coerce/to-date (:end p))}}))
+ :period {:start (coerce/to-date (:start p)) :end (coerce/to-date (:end p))}}))
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] {: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-ids)
-
+
pnl-data (l-reports/->PNLData args data (by :db/id :client/code clients))
report (l-reports/summarize-cash-flows pnl-data)]
(alog/info ::cash-flows :params args)
@@ -108,14 +106,14 @@
(assoc :warning "You requested a report with more than 20 clients. This report will only contain the first 20."))
{:client client}))
-(defn cash-flows* [{ {:keys [periods client] } :form-params :as request}]
- [:div#report
+(defn cash-flows* [{{:keys [periods client]} :form-params :as request}]
+ [:div#report
(when (and periods client)
(let [{:keys [client warning]} (maybe-trim-clients request client)
{:keys [data report]} (get-report (assoc-in request [:form-params :client] client))
- client-count (count (set (map :client-id (:data data))))
+ client-count (count (set (map :client-id (:data data))))
table-contents (concat-tables (:details report))]
- (list
+ (list
[:div.text-2xl.font-bold.text-gray-600 (str "Cash flows - " (str/join ", " (map :client/name client)))]
(table {:widths (into [20] (take (dec (cell-count table-contents))
(mapcat identity
@@ -128,18 +126,18 @@
(defn form* [request & children]
(let [params (or (:query-params request) {})]
(fc/start-form
- params
+ params
(:form-errors request)
[:div#cash-flows-form.flex.flex-col.gap-4.mt-4
- [:div.flex.gap-8
- [:form {:hx-put (bidi.bidi/path-for ssr-routes/only-routes ::route/run-cash-flows)
+ [:div.flex.gap-8
+ [:form {:hx-put (bidi.bidi/path-for ssr-routes/only-routes ::route/run-cash-flows)
:hx-target "#cash-flows-form"
:hx-swap "outerHTML"
:hx-disabled-elt "find fieldset"}
- [:fieldset
+ [:fieldset
[:div.flex.gap-8 {:x-data (hx/json {})}
(fc/with-field :client
- (com/validated-inline-field
+ (com/validated-inline-field
{:label "Customers" :errors (fc/field-errors)}
(com/multi-typeahead {:name (fc/field-name)
:placeholder "Search for companies..."
@@ -151,12 +149,12 @@
:content-fn :client/name})))
(fc/with-field :periods
(com/validated-inline-field {:label "Periods"
- :errors (fc/field-errors)}
+ :errors (fc/field-errors)}
(com/periods-dropdown {:value (fc/field-value)})))
-
+
(com/button {:color :primary :class "w-32"}
"Run")
- (com/button {:formaction (bidi.bidi/path-for ssr-routes/only-routes ::route/export-cash-flows) } "Export PDF")]]]]
+ (com/button {:formaction (bidi.bidi/path-for ssr-routes/only-routes ::route/export-cash-flows)} "Export PDF")]]]]
children])))
(defn form [request]
@@ -169,7 +167,7 @@
(base-page
request
(com/page {:nav com/main-aside-nav
-
+
:client-selection (:client-selection request)
:clients (:clients request)
:client (:client request)
@@ -181,20 +179,20 @@
"Cash Flows"))
(defn make-cash-flows-pdf [request report]
- (let [ output-stream (ByteArrayOutputStream.)
+ (let [output-stream (ByteArrayOutputStream.)
date (:periods (:form-params request))]
(pdf/pdf
- (-> [{:left-margin 10 :right-margin 10 :top-margin 15 :bottom-margin 15
- :size :letter
- :font {:size 6
- :ttf-name "fonts/calibri-light.ttf"}}
- [:heading (str "Balance Sheet - " (str/join ", " (map :client/name (seq (:client (:form-params request))))))]]
+ (-> [{:left-margin 10 :right-margin 10 :top-margin 15 :bottom-margin 15
+ :size :letter
+ :font {:size 6
+ :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
- (table->pdf (concat-tables (:details report))
- (into [20 ] (mapcat identity (repeat (count date) [ 13 13 13]))))))
- output-stream)
+ (conj [:paragraph {:color [128 0 0] :size 9} (:warning report)])
+ (conj
+ (table->pdf (concat-tables (:details report))
+ (into [20] (mapcat identity (repeat (count date) [13 13 13]))))))
+ output-stream)
(.toByteArray output-stream)))
(defn join-names [client-ids]
@@ -202,8 +200,8 @@
(defn cash-flows-args->name [request]
(let [date (atime/unparse-local
- (:date (:query-params request))
- atime/iso-date)
+ (:date (:query-params request))
+ atime/iso-date)
name (->> request :query-params :client (map :db/id) join-names)]
(format "Balance-sheet-%s-for-%s" date name)))
@@ -248,7 +246,7 @@
"Click "
(com/link {:href (:report/url bs)} "here")
" to download"]]))
-
+
nil))
:headers (-> {}
(assoc "hx-retarget" ".modal-stack")
@@ -258,15 +256,15 @@
(apply-middleware-to-all-handlers
(->
{::route/cash-flows (-> cash-flows
- (wrap-schema-enforce :query-schema query-schema)
- (wrap-form-4xx-2 cash-flows))
+ (wrap-schema-enforce :query-schema query-schema)
+ (wrap-form-4xx-2 cash-flows))
::route/run-cash-flows (-> form
- (wrap-schema-enforce :form-schema query-schema)
- (wrap-form-4xx-2 form))
+ (wrap-schema-enforce :form-schema query-schema)
+ (wrap-form-4xx-2 form))
::route/export-cash-flows (-> export
(wrap-schema-enforce :form-schema query-schema)
(wrap-form-4xx-2 form))})
-
+
(fn [h]
(-> h
#_(wrap-merge-prior-hx)
diff --git a/src/clj/auto_ap/ssr/ledger/common.clj b/src/clj/auto_ap/ssr/ledger/common.clj
index 3e284966..5209f6fe 100644
--- a/src/clj/auto_ap/ssr/ledger/common.clj
+++ b/src/clj/auto_ap/ssr/ledger/common.clj
@@ -1,8 +1,8 @@
-(ns auto-ap.ssr.ledger.common
+(ns auto-ap.ssr.ledger.common
(:require
[auto-ap.datomic
- :refer [add-sorter-fields apply-pagination apply-sort-4 conn
- merge-query observable-query pull-many]]
+ :refer [add-sorter-fields apply-pagination apply-sort-4 conn
+ merge-query observable-query pull-many]]
[auto-ap.datomic.accounts :as d-accounts]
[auto-ap.graphql.utils :refer [extract-client-ids]]
[auto-ap.permissions :refer [can?]]
@@ -17,8 +17,8 @@
[auto-ap.ssr.pos.common :refer [date-range-field*]]
[auto-ap.ssr.svg :as svg]
[auto-ap.ssr.utils
- :refer [clj-date-schema entity-id html-response ref->enum-schema
- strip]]
+ :refer [clj-date-schema entity-id html-response ref->enum-schema
+ strip]]
[auto-ap.time :as atime]
[auto-ap.utils :refer [dollars-0?]]
[bidi.bidi :as bidi]
@@ -68,10 +68,10 @@
(defn filters [request]
[:form#ledger-filters {"hx-trigger" "change delay:500ms, keyup changed from:.hot-filter delay:1000ms"
- "hx-get" (bidi/path-for ssr-routes/only-routes
- ::route/table)
- "hx-target" "#entity-table"
- "hx-indicator" "#entity-table"}
+ "hx-get" (bidi/path-for ssr-routes/only-routes
+ ::route/table)
+ "hx-target" "#entity-table"
+ "hx-indicator" "#entity-table"}
(com/hidden {:name "status"
:value (some-> (:status (:query-params request)) name)})
@@ -102,7 +102,7 @@
:value (:invoice-number (:query-params request))
:placeholder "e.g., ABC-456"
:size :small}))
-
+
(com/field {:label "Account Code"}
[:div.flex.space-x-4.items-baseline
(com/int-input {:name "numeric-code-gte"
@@ -140,10 +140,10 @@
:value (:amount-lte (:query-params request))
:placeholder "9999.34"
:size :small})])
- [:div.mt-4 {:x-data (hx/json { :onlyUnbalanced (:only-unbalanced (:query-params request))})}
+ [:div.mt-4 {:x-data (hx/json {:onlyUnbalanced (:only-unbalanced (:query-params request))})}
(com/hidden {:name "only-unbalanced"
":value" "onlyUnbalanced ? 'on' : ''"})
- (com/checkbox {:value (:only-unbalanced (:query-params request))
+ (com/checkbox {:value (:only-unbalanced (:query-params request))
:x-model "onlyUnbalanced"}
"Show unbalanced")]
(exact-match-id* request)]])
@@ -163,8 +163,8 @@
(filter (fn [[debits credits]]
(not (dollars= debits credits))))
(map last)
- (into #{})) ]
- (for [ result results
+ (into #{}))]
+ (for [result results
:when (get unbalanced-ids (last result))]
result))
results))
@@ -245,20 +245,19 @@
:args [(or (:numeric-code-gte args) 0) (or (:numeric-code-lte args) 99999)]})
(seq (:numeric-code args))
- (merge-query {:query {:in '[ [ [?from-numeric-code ?to-numeric-code] ...]]
+ (merge-query {:query {:in '[[[?from-numeric-code ?to-numeric-code] ...]]
:where ['[?li :journal-entry-line/account ?a]
'(or-join [?a ?c]
[?a :account/numeric-code ?c]
[?a :bank-account/numeric-code ?c])
'[(>= ?c ?from-numeric-code)]
'[(<= ?c ?to-numeric-code)]]}
- :args [ (map (juxt :from :to ) (:numeric-code args))]})
+ :args [(map (juxt :from :to) (:numeric-code args))]})
(seq (:account args))
(merge-query {:query {:in ['?a3]
:where ['[?li :journal-entry-line/account ?a3]]}
:args [(:db/id (:account args))]})
-
(:amount-gte args)
(merge-query {:query {:in ['?amount-gte]
:where ['[?e :journal-entry/amount ?a]
@@ -317,17 +316,15 @@
(apply-only-unbalanced query-params)
(apply-pagination query-params))))
-
-#_(dc/q '{:find [ ?sort-vendor (count ?e)],
-:in [$ [?clients ?start ?end]],
-:where [[(iol-ion.query/scan-ledger $ ?clients ?start ?end)
- [[?e _ ?sort-default] ...]]
- #_(not [?e :journal-entry/vendor])
- [(missing? $ ?e :journal-entry/vendor)]
- [(ground "ih") ?sort-vendor]]}
-(dc/db conn)
-args
-)
+#_(dc/q '{:find [?sort-vendor (count ?e)],
+ :in [$ [?clients ?start ?end]],
+ :where [[(iol-ion.query/scan-ledger $ ?clients ?start ?end)
+ [[?e _ ?sort-default] ...]]
+ #_(not [?e :journal-entry/vendor])
+ [(missing? $ ?e :journal-entry/vendor)]
+ [(ground "ih") ?sort-vendor]]}
+ (dc/db conn)
+ args)
(def default-read
'[:journal-entry/amount
@@ -338,7 +335,7 @@ args
:db/id
[:journal-entry/date :xform clj-time.coerce/from-date]
{:journal-entry/vendor [:vendor/name :db/id]
- :journal-entry/original-entity [:invoice/invoice-number
+ :journal-entry/original-entity [:invoice/invoice-number
:invoice/source-url
:transaction/description-original :db/id]
:journal-entry/client [:client/name :client/code :db/id]
@@ -363,32 +360,30 @@ args
refunds))
(defn sum-outstanding [ids]
-
- (->>
- (dc/q {:find ['?id '?o]
- :in ['$ '[?id ...]]
- :where ['[?id :invoice/outstanding-balance ?o]]}
- (dc/db conn)
- ids)
+
+ (->>
+ (dc/q {:find ['?id '?o]
+ :in ['$ '[?id ...]]
+ :where ['[?id :invoice/outstanding-balance ?o]]}
+ (dc/db conn)
+ ids)
(map last)
(reduce
+
0.0)))
(defn sum-total-amount [ids]
-
- (->>
- (dc/q {:find ['?id '?o]
- :in ['$ '[?id ...]]
- :where ['[?id :invoice/total ?o]]
- }
- (dc/db conn)
- ids)
- (map last)
- (reduce
- +
- 0.0)))
+ (->>
+ (dc/q {:find ['?id '?o]
+ :in ['$ '[?id ...]]
+ :where ['[?id :invoice/total ?o]]}
+ (dc/db conn)
+ ids)
+ (map last)
+ (reduce
+ +
+ 0.0)))
(defn fetch-page [request]
(let [db (dc/db conn)
@@ -413,12 +408,12 @@ args
(list
(if account-name
- [:div { :x-tooltip (hx/json (str "Running Balance: " (some->> (:journal-entry-line/running-balance jel)
- (format "$%,.2f"))))}
+ [:div {:x-tooltip (hx/json (str "Running Balance: " (some->> (:journal-entry-line/running-balance jel)
+ (format "$%,.2f"))))}
[:div.text-left.underline.cursor-pointer {:x-ref "source"}
(:journal-entry-line/location jel) ": "
(or (:account/numeric-code account) (:bank-account/numeric-code account))
- " - " account-name] ]
+ " - " account-name]]
[:div.text-left (com/pill {:color :yellow} "Unassigned")])
[:div.text-right.text-underline (format "$%,.2f" (key jel))]))
@@ -436,12 +431,12 @@ args
[:amount-gte {:optional true} [:maybe :double]]
[:amount-lte {:optional true} [:maybe :double]]
[:client-id {:optional true} [:maybe entity-id]]
- [:only-unbalanced {:optional true }
+ [:only-unbalanced {:optional true}
[:maybe [:boolean {:decode/string {:enter #(cond (= % "on") true
- (= % "") false
- :else
-
- (boolean %))}
+ (= % "") false
+ :else
+
+ (boolean %))}
:encode/string {:enter #(if % "on" "")}}]]]
[:numeric-code {:optional true :decode/string clojure.edn/read-string}
[:maybe [:vector [:map [:from nat-int?]
@@ -527,103 +522,101 @@ args
jel (:journal-entry/line-items je)]
(merge jel je)))
:headers [{:key "id"
- :name "Id"
- :render-csv :db/id
- :render-for #{:csv}}
- {:key "client"
- :name "Client"
- :sort-key "client"
- :hide? (fn [args]
- (and (= (count (:clients args)) 1)
- (= 1 (count (:client/locations (:client args))))))
- :render (fn [x] [:div.flex.items-center.gap-2 (-> x :journal-entry/client :client/name)])
- :render-csv (fn [x] (-> x :journal-entry/client :client/name))}
+ :name "Id"
+ :render-csv :db/id
+ :render-for #{:csv}}
+ {:key "client"
+ :name "Client"
+ :sort-key "client"
+ :hide? (fn [args]
+ (and (= (count (:clients args)) 1)
+ (= 1 (count (:client/locations (:client args))))))
+ :render (fn [x] [:div.flex.items-center.gap-2 (-> x :journal-entry/client :client/name)])
+ :render-csv (fn [x] (-> x :journal-entry/client :client/name))}
- {:key "vendor"
- :name "Vendor"
- :sort-key "vendor"
- :render (fn [e] (or (-> e :journal-entry/vendor :vendor/name)
- [:span.italic.text-gray-400 (-> e :journal-entry/alternate-description)]))
- :render-csv (fn [e] (or (-> e :journal-entry/vendor :vendor/name)
- (-> e :journal-entry/alternate-description)))}
- {:key "source"
- :name "Source"
- :sort-key "source"
- :hide? (fn [args]
- (not (:external? (:route-params args))))
- :render :journal-entry/source
- :render-csv :journal-entry/source}
- {:key "external-id"
- :name "External Id"
- :sort-key "external-id"
- :class "max-w-[12rem]"
- :hide? (fn [args]
- (not (:external? (:route-params args))))
- :render (fn [x] [:p.truncate (:journal-entry/external-id x)])
- :render-csv :journal-entry/external-id}
- {:key "date"
- :sort-key "date"
- :name "Date"
- :show-starting "lg"
- :render (fn [{:journal-entry/keys [date]}]
- (some-> date (atime/unparse-local atime/normal-date)))}
- {:key "amount"
- :sort-key "amount"
- :name "Amount"
- :show-starting "lg"
- :render (fn [{:journal-entry/keys [amount]}]
- (some->> amount
- (format "$%,.2f")))}
- {:key "account"
- :name "Account"
- :sort-key "account"
- :class "text-right"
- :render-csv #(or (-> % :journal-entry-line/account :account/name)
- (-> % :journal-entry-line/account :bank-account/name))
- :render-for #{:csv}}
- {:key "debit"
- :name "Debit"
- :class "text-right"
- :render (partial render-lines :journal-entry-line/debit)
- :render-csv :journal-entry-line/debit}
+ {:key "vendor"
+ :name "Vendor"
+ :sort-key "vendor"
+ :render (fn [e] (or (-> e :journal-entry/vendor :vendor/name)
+ [:span.italic.text-gray-400 (-> e :journal-entry/alternate-description)]))
+ :render-csv (fn [e] (or (-> e :journal-entry/vendor :vendor/name)
+ (-> e :journal-entry/alternate-description)))}
+ {:key "source"
+ :name "Source"
+ :sort-key "source"
+ :hide? (fn [args]
+ (not (:external? (:route-params args))))
+ :render :journal-entry/source
+ :render-csv :journal-entry/source}
+ {:key "external-id"
+ :name "External Id"
+ :sort-key "external-id"
+ :class "max-w-[12rem]"
+ :hide? (fn [args]
+ (not (:external? (:route-params args))))
+ :render (fn [x] [:p.truncate (:journal-entry/external-id x)])
+ :render-csv :journal-entry/external-id}
+ {:key "date"
+ :sort-key "date"
+ :name "Date"
+ :show-starting "lg"
+ :render (fn [{:journal-entry/keys [date]}]
+ (some-> date (atime/unparse-local atime/normal-date)))}
+ {:key "amount"
+ :sort-key "amount"
+ :name "Amount"
+ :show-starting "lg"
+ :render (fn [{:journal-entry/keys [amount]}]
+ (some->> amount
+ (format "$%,.2f")))}
+ {:key "account"
+ :name "Account"
+ :sort-key "account"
+ :class "text-right"
+ :render-csv #(or (-> % :journal-entry-line/account :account/name)
+ (-> % :journal-entry-line/account :bank-account/name))
+ :render-for #{:csv}}
+ {:key "debit"
+ :name "Debit"
+ :class "text-right"
+ :render (partial render-lines :journal-entry-line/debit)
+ :render-csv :journal-entry-line/debit}
+ {:key "credit"
+ :name "Credit"
+ :class "text-right"
+ :render (partial render-lines :journal-entry-line/credit)
+ :render-csv :journal-entry-line/credit}
+ {:key "links"
+ :name "Links"
+ :show-starting "lg"
+ :class "w-8"
+ :render (fn [i]
+ (link-dropdown
+ (cond-> []
+ (-> i :journal-entry/original-entity :invoice/invoice-number)
+ (conj
+ {:link (hu/url (bidi/path-for ssr-routes/only-routes
+ ::invoice-route/all-page)
+ {:exact-match-id (:db/id (:journal-entry/original-entity i))})
+ :color :primary
+ :content (format "Invoice '%s'" (-> i :journal-entry/original-entity :invoice/invoice-number))})
+ (-> i :journal-entry/original-entity :invoice/source-url)
+ {:link (-> i :journal-entry/original-entity :invoice/source-url)
+ :color :secondary
+ :content (str "File")}
- {:key "credit"
- :name "Credit"
- :class "text-right"
- :render (partial render-lines :journal-entry-line/credit)
- :render-csv :journal-entry-line/credit}
-
- {:key "links"
- :name "Links"
- :show-starting "lg"
- :class "w-8"
- :render (fn [i]
- (link-dropdown
- (cond-> []
- (-> i :journal-entry/original-entity :invoice/invoice-number)
- (conj
- {:link (hu/url (bidi/path-for ssr-routes/only-routes
- ::invoice-route/all-page)
- {:exact-match-id (:db/id (:journal-entry/original-entity i))})
- :color :primary
- :content (format "Invoice '%s'" (-> i :journal-entry/original-entity :invoice/invoice-number))})
- (-> i :journal-entry/original-entity :invoice/source-url)
- {:link (-> i :journal-entry/original-entity :invoice/source-url)
- :color :secondary
- :content (str "File")}
-
- (-> i :journal-entry/original-entity :transaction/description-original)
- (conj
- {:link (hu/url (bidi/path-for ssr-routes/only-routes
- ::transaction-routes/all-page)
- {:exact-match-id (:db/id (:journal-entry/original-entity i))})
- :color :primary
- :content (format "Transaction '%s'" (-> i :journal-entry/original-entity :transaction/description-original))})
- (-> i :journal-entry/memo)
- (conj {:color :secondary
- :content (str "Memo: " (:journal-entry/memo i))}))))
- :render-for #{:html}}]}))
+ (-> i :journal-entry/original-entity :transaction/description-original)
+ (conj
+ {:link (hu/url (bidi/path-for ssr-routes/only-routes
+ ::transaction-routes/all-page)
+ {:exact-match-id (:db/id (:journal-entry/original-entity i))})
+ :color :primary
+ :content (format "Transaction '%s'" (-> i :journal-entry/original-entity :transaction/description-original))})
+ (-> i :journal-entry/memo)
+ (conj {:color :secondary
+ :content (str "Memo: " (:journal-entry/memo i))}))))
+ :render-for #{:html}}]}))
(def row* (partial helper/row* grid-page))
\ No newline at end of file
diff --git a/src/clj/auto_ap/ssr/ledger/investigate.clj b/src/clj/auto_ap/ssr/ledger/investigate.clj
index cbdefde6..9639013c 100644
--- a/src/clj/auto_ap/ssr/ledger/investigate.clj
+++ b/src/clj/auto_ap/ssr/ledger/investigate.clj
@@ -1,4 +1,4 @@
-(ns auto-ap.ssr.ledger.investigate
+(ns auto-ap.ssr.ledger.investigate
(:require
[auto-ap.permissions :refer [wrap-must]]
[auto-ap.query-params :refer [wrap-copy-qp-pqp]]
@@ -14,37 +14,33 @@
wrap-schema-enforce]]
[auto-ap.time :as atime]))
-
-(def altered-grid-page
- (assoc grid-page
+(def altered-grid-page
+ (assoc grid-page
:id "yoho"
- :raw? true
+ :raw? true
:check-boxes? false
:route ::route/investigate-results))
-
(defn investigate [request]
- (modal-response
+ (modal-response
(com/modal {:class "max-h-[600px]"}
- (com/modal-card {:hx-vals (hx/json (cond-> (:query-params request)
- true (update :numeric-code pr-str)
- (:start-date (:query-params request)) (update :start-date #(some-> (atime/unparse-local % atime/normal-date)))
- (:end-date (:query-params request)) (update :end-date #(some-> (atime/unparse-local % atime/normal-date))))) }
- [:div "Ledger entries"]
- (table*
- altered-grid-page
- identity
- request
- #_(assoc-in request [:query-params :sort] [{:sort-key "date" :asc? false :name "Date"}]))
- nil)
- )))
+ (com/modal-card {:hx-vals (hx/json (cond-> (:query-params request)
+ true (update :numeric-code pr-str)
+ (:start-date (:query-params request)) (update :start-date #(some-> (atime/unparse-local % atime/normal-date)))
+ (:end-date (:query-params request)) (update :end-date #(some-> (atime/unparse-local % atime/normal-date)))))}
+ [:div "Ledger entries"]
+ (table*
+ altered-grid-page
+ identity
+ request
+ #_(assoc-in request [:query-params :sort] [{:sort-key "date" :asc? false :name "Date"}]))
+ nil))))
(def key->handler
(apply-middleware-to-all-handlers
(->
- {::route/investigate investigate
- ::route/investigate-results (helper/table-route altered-grid-page :push-url? false)}
- )
+ {::route/investigate investigate
+ ::route/investigate-results (helper/table-route altered-grid-page :push-url? false)})
(fn [h]
(-> h
(wrap-apply-sort grid-page)
diff --git a/src/clj/auto_ap/ssr/ledger/new.clj b/src/clj/auto_ap/ssr/ledger/new.clj
index c1f8bc63..8740e750 100644
--- a/src/clj/auto_ap/ssr/ledger/new.clj
+++ b/src/clj/auto_ap/ssr/ledger/new.clj
@@ -1,4 +1,4 @@
-(ns auto-ap.ssr.ledger.new
+(ns auto-ap.ssr.ledger.new
(:require
[auto-ap.datomic :refer [audit-transact conn pull-attr]]
[auto-ap.datomic.accounts :as d-accounts]
@@ -26,25 +26,25 @@
[datomic.api :as dc]
[iol-ion.query :refer [dollars=]]
[iol-ion.utils :refer [remove-nils]])
- (:import
- [java.util UUID]))
+ (:import
+ [java.util UUID]))
(def new-ledger-schema
[:and
[:map
[:db/id {:optional true} [:maybe entity-id]]
- [:journal-entry/client {:optional false} [:entity-map {:pull [:db/id :client/name :client/locations] }]]
+ [:journal-entry/client {:optional false} [:entity-map {:pull [:db/id :client/name :client/locations]}]]
[:journal-entry/date clj-date-schema]
- [:journal-entry/memo {:optional true} [:maybe [ :string {:decode/string strip}]]]
+ [:journal-entry/memo {:optional true} [:maybe [:string {:decode/string strip}]]]
[:journal-entry/vendor {:optional false :default nil}
- [:entity-map {:pull [:db/id :vendor/name] }]]
+ [:entity-map {:pull [:db/id :vendor/name]}]]
[:journal-entry/amount {:min 0.01}
money]
[:journal-entry/line-items
[:vector {:coerce? true}
[:and
[:map
- [:journal-entry-line/account [:and [:entity-map {:pull a/default-read }]
+ [:journal-entry-line/account [:and [:entity-map {:pull a/default-read}]
[:fn {:error/message "Not an allowed account."}
(fn check-allow [x]
(check-allowance (:db/id x) :account/default-allowance))]]]
@@ -81,18 +81,18 @@
:value value
:content-fn (fn [value]
(when value
- (str
- (:account/numeric-code value)
- " - "
- (:account/name (d-accounts/clientize value
- client-id)))))})])
+ (str
+ (:account/numeric-code value)
+ " - "
+ (:account/name (d-accounts/clientize value
+ client-id)))))})])
(defn- location-select*
[{:keys [name account-location client-locations value]}]
(com/select {:options (into [["" ""]]
(cond account-location
[[account-location account-location]]
-
+
:else
(for [c (seq client-locations)]
[c c])))
@@ -198,19 +198,19 @@
(com/hidden {:name (fc/field-name)
:value (:db/id (:client request))})
[:div.w-96
- (com/validated-field
- {:label "Client"
- :errors (fc/field-errors)}
- [:div.w-96
- (com/typeahead {:name (fc/field-name)
- :error? (fc/error?)
- :class "w-96"
- :placeholder "Search..."
- :url (bidi/path-for ssr-routes/only-routes :company-search)
- :value (fc/field-value)
- :value-fn :db/id
- :content-fn :client/name
- :x-model "clientId"})])]))
+ (com/validated-field
+ {:label "Client"
+ :errors (fc/field-errors)}
+ [:div.w-96
+ (com/typeahead {:name (fc/field-name)
+ :error? (fc/error?)
+ :class "w-96"
+ :placeholder "Search..."
+ :url (bidi/path-for ssr-routes/only-routes :company-search)
+ :value (fc/field-value)
+ :value-fn :db/id
+ :content-fn :client/name
+ :x-model "clientId"})])]))
(fc/with-field :journal-entry/date
(com/validated-field
{:label "Date"
@@ -245,18 +245,18 @@
:class "w-24"
:error? (fc/field-errors)
:placeholder "212.44"})]))
- (fc/with-field :journal-entry/memo
+ (fc/with-field :journal-entry/memo
[:div.w-96
(com/validated-field
{:label "Memo"
:errors (fc/field-errors)}
[:div.w-96
(com/text-input {:name (fc/field-name)
- :error? (fc/error?)
- :class "w-96"
- :placeholder "A custom note"
- :url (bidi/path-for ssr-routes/only-routes :company-search)
- :value (fc/field-value) })])])
+ :error? (fc/error?)
+ :class "w-96"
+ :placeholder "A custom note"
+ :url (bidi/path-for ssr-routes/only-routes :company-search)
+ :value (fc/field-value)})])])
(fc/with-field :journal-entry/line-items
(com/validated-field
{:errors (fc/field-errors)}
@@ -273,7 +273,6 @@
:tr-params (hx/bind-alpine-vals {} {"client-id" "clientId"})}
"New account"))))])))
-
(defn new [request]
(modal-response
(com/modal {:hx-target "this"
@@ -282,7 +281,7 @@
::route/new-submit)}
(com/modal-card {:class "md:h-[800px] md:w-[750px] flex-col relative"
:error (when (vector? (:form-errors request))
- (str/join ", "(:form-errors request) ))}
+ (str/join ", " (:form-errors request)))}
[:div "New ledger entry"]
[:div.overflow-y-scroll.relative (form* request)]
[:div (com/button {:color :primary} "Save")])])))
@@ -296,10 +295,10 @@
(update :journal-entry/line-items
(fn [lis]
(mapv
- #(remove-nils (-> %
- (update :journal-entry-line/account :db/id)
- (assoc :journal-entry-line/client (-> request :form-params :journal-entry/client :db/id)
- :journal-entry-line/date (-> request :form-params :journal-entry/date coerce/to-date))))
+ #(remove-nils (-> %
+ (update :journal-entry-line/account :db/id)
+ (assoc :journal-entry-line/client (-> request :form-params :journal-entry/client :db/id)
+ :journal-entry-line/date (-> request :form-params :journal-entry/date coerce/to-date))))
lis)))
(assoc :journal-entry/external-id (str "manual-" (UUID/randomUUID))))
(= :post (:request-method request)) (assoc :db/id "new"))
@@ -308,9 +307,9 @@
:client/ledger-last-change (iol-ion.tx.upsert-ledger/current-date (dc/db conn))}]
(:identity request))
updated-entity (dc/pull (dc/db conn)
- ledger.common/default-read
- (or (get tempids (:db/id entity)) (:db/id entity)))]
-
+ ledger.common/default-read
+ (or (get tempids (:db/id entity)) (:db/id entity)))]
+
(html-response
(ledger.common/row* identity updated-entity
{:flash? true
@@ -323,7 +322,6 @@
(assoc "hx-retarget" "#entity-table tbody"
"hx-reswap" "afterbegin")))))
-
(def key->handler
(apply-middleware-to-all-handlers
(->
diff --git a/src/clj/auto_ap/ssr/ledger/profit_and_loss.clj b/src/clj/auto_ap/ssr/ledger/profit_and_loss.clj
index e52a5e10..eedf9a4c 100644
--- a/src/clj/auto_ap/ssr/ledger/profit_and_loss.clj
+++ b/src/clj/auto_ap/ssr/ledger/profit_and_loss.clj
@@ -38,7 +38,6 @@
[java.util UUID]
[org.apache.commons.io.output ByteArrayOutputStream]))
-
(def query-schema (mc/schema
[:maybe [:map
[:client {:unspecified/value :all}
@@ -71,14 +70,14 @@
;; 4. Review ledger dialog
;; 5. pagination and filtering within dialog. looks weird with the full screen refresh
-(defn get-report [{ {:keys [periods client] :as qp} :form-params :as request}]
+(defn get-report [{{:keys [periods client] :as qp} :form-params :as request}]
(when (and (seq periods) client)
(let [client (if (= :all client) (take 5 (:clients request)) client)
client-ids (map :db/id client)
_ (upsert-running-balance (into #{} client-ids))
_ (doseq [client-id client-ids]
(assert-can-see-client (:identity request) client-id))
-
+
lookup-account (->> client-ids
(map (fn build-lookup [client-id]
[client-id (build-account-lookup client-id)]))
@@ -103,13 +102,13 @@
:numeric-code (:numeric_code account)
:name (:name account)
:sample sample
- :period {:start ( coerce/to-date (:start p)) :end (coerce/to-date (:end p))}}))
-
+ :period {:start (coerce/to-date (:start p)) :end (coerce/to-date (:end p))}}))
+
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]
+ {: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))
#_#__ (clojure.pprint/pprint pnl-data)
report (l-reports/summarize-pnl pnl-data)]
@@ -124,14 +123,14 @@
(assoc :warning "You requested a report with more than 20 clients. This report will only contain the first 20."))
{:client client}))
-(defn profit-and-loss* [{ {:keys [periods client] } :form-params :as request}]
- [:div#report
+(defn profit-and-loss* [{{:keys [periods client]} :form-params :as request}]
+ [:div#report
(when (and periods client)
(let [{:keys [client warning]} (maybe-trim-clients request client)
{:keys [data report]} (get-report (assoc-in request [:form-params :client] client))
- client-count (count (set (map :client-id (:data data))))
+ client-count (count (set (map :client-id (:data data))))
table-contents (concat-tables (concat (:summaries report) (:details report)))]
- (list
+ (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
@@ -148,14 +147,11 @@
{:subject :history
:activity :view})
(for [n (:invalid-ids report)]
- [:div
+ [:div
(com/link {:href (str (bidi/path-for ssr-routes/only-routes
:admin-history)
"/" n)}
- "Sample")]))]
- }))))])
-
-
+ "Sample")]))]}))))])
(defn form* [request & children]
(let [params (or (:query-params request) {})]
@@ -209,7 +205,7 @@
(base-page
request
(com/page {:nav com/main-aside-nav
-
+
:client-selection (:client-selection request)
:clients (:clients request)
:client (:client request)
@@ -259,8 +255,8 @@
(defn profit-and-loss-args->name [request]
(let [date (atime/unparse-local
- (:date (:query-params request))
- atime/iso-date)
+ (:date (:query-params request))
+ atime/iso-date)
name (->> request :query-params :client (map :db/id) join-names)]
(format "Profit-and-loss-%s-for-%s" date name)))
@@ -268,7 +264,7 @@
(let [uuid (str (UUID/randomUUID))
{:keys [client warning]} (maybe-trim-clients request (:client (:form-params request)))
request (assoc-in request [:form-params :client] client)
- pdf-data (binding [*report-pedantic* (boolean ((set (:client/feature-flags (first client)))
+ 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)
key (str "reports/profit-and-loss/" uuid "/" name ".pdf")
@@ -306,7 +302,7 @@
"Click "
(com/link {:href (:report/url bs)} "here")
" to download"]]))
-
+
nil))
:headers (-> {}
(assoc "hx-retarget" ".modal-stack")
@@ -316,15 +312,15 @@
(apply-middleware-to-all-handlers
(->
{::route/profit-and-loss (-> profit-and-loss
- (wrap-schema-enforce :query-schema query-schema)
- (wrap-form-4xx-2 profit-and-loss))
+ (wrap-schema-enforce :query-schema query-schema)
+ (wrap-form-4xx-2 profit-and-loss))
::route/run-profit-and-loss (-> form
- (wrap-schema-enforce :form-schema query-schema)
- (wrap-form-4xx-2 form))
+ (wrap-schema-enforce :form-schema query-schema)
+ (wrap-form-4xx-2 form))
::route/export-profit-and-loss (-> export
- (wrap-schema-enforce :form-schema query-schema)
- (wrap-form-4xx-2 form))})
-
+ (wrap-schema-enforce :form-schema query-schema)
+ (wrap-form-4xx-2 form))})
+
(fn [h]
(-> h
#_(wrap-merge-prior-hx)
diff --git a/src/clj/auto_ap/ssr/ledger/report_table.clj b/src/clj/auto_ap/ssr/ledger/report_table.clj
index 0c6d556b..22a50efc 100644
--- a/src/clj/auto_ap/ssr/ledger/report_table.clj
+++ b/src/clj/auto_ap/ssr/ledger/report_table.clj
@@ -1,4 +1,4 @@
-(ns auto-ap.ssr.ledger.report-table
+(ns auto-ap.ssr.ledger.report-table
(:require
[auto-ap.ssr.components :as com]
[auto-ap.time :as atime]
@@ -7,22 +7,19 @@
[hiccup.util :as hu]
[iol-ion.query :as query]))
-
-
(defn cell [{:keys [width investigate-url other-style]} c]
(let [cell-contents (cond
-
+
(= :dollar (:format c))
(format "$%,.2f" (if (query/dollars-0? (:value c))
0.0
(:value c)))
-
-
+
(= :percent (:format c))
(format "%%%.1f" (if (query/dollars-0? (:value c))
0.0
(* 100.0 (or (:value c) 0.0))))
-
+
:else
(str (:value c)))
cell-contents (if (:filters c)
@@ -32,8 +29,7 @@
(inst? (:date-range (:filters c))) (assoc :end-date (atime/unparse-local (coerce/to-date-time (:date-range (:filters c))) atime/normal-date))
(:end (:date-range (:filters c))) (assoc :end-date (atime/unparse-local (coerce/to-date-time (:end (:date-range (:filters c)))) atime/normal-date))
(:start (:date-range (:filters c))) (assoc :start-date (atime/unparse-local (coerce/to-date-time (:start (:date-range (:filters c)))) atime/normal-date))
- (:client-id (:filters c)) (assoc :client-id (:client-id (:filters c))))
- )}
+ (:client-id (:filters c)) (assoc :client-id (:client-id (:filters c)))))}
cell-contents)
cell-contents)]
[:td.px-4.py-2
@@ -44,10 +40,9 @@
(fn [s]
(->> (:border c)
(map
- (fn [b]
- [(keyword (str "border-" (name b))) "1px solid black"])
- )
- (into s))))
+ (fn [b]
+ [(keyword (str "border-" (name b))) "1px solid black"]))
+ (into s))))
(:colspan c) (assoc :colspan (:colspan c))
(:align c) (assoc :align (:align c))
(= :dollar (:format c)) (assoc :align :right)
@@ -57,10 +52,10 @@
(str/join ","
(:color c))
")"))
- true (assoc-in [:style :background-color] (str "rgb("
- (str/join ","
- (or (:bg-color c) [255 255 255]))
- ")")))
+ true (assoc-in [:style :background-color] (str "rgb("
+ (str/join ","
+ (or (:bg-color c) [255 255 255]))
+ ")")))
cell-contents]))
@@ -72,47 +67,47 @@
(defn table [{:keys [table widths investigate-url warning]}]
(let [cell-count (cell-count table)]
- (com/content-card {:class "inline-block overflow-scroll"}
- [:div {:class "overflow-scroll h-[70vh] m-4 inline-block"}
- (when warning [:div.rounded.bg-red-50.text-red-800.p-4.m-2
- warning])
- (-> [:table {:class "text-sm text-left text-gray-500 dark:text-gray-400"}
- [:thead {:class "text-xs text-gray-800 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400 font-bold"}
- (map
- (fn [header-row header]
- (into
- [:tr {:class " dark:border-gray-600 hover:bg-gray-100 dark:hover:bg-gray-700"}]
- (map
- (fn [w header i]
- (cell {:width w
- :investigate-url investigate-url
- :other-style {:position "sticky"
- :top (* header-row (+ 22 18))}} header))
- widths
- header
- (range))))
- (range)
- (:header table))]]
-
- (conj
- (-> [:tbody {:style {}}]
- (into
- (for [[i row] (map vector (range) (:rows table))]
-
- [:tr {:class " dark:border-gray-600 hover:bg-gray-100 dark:hover:bg-gray-700"}
- (for [[i c] (map vector (range) (take cell-count
- (reduce
- (fn [[acc cnt] cur]
- (if (>= (+ cnt (:colspan cur 1)) cell-count)
- (reduced (conj acc cur))
- [(conj acc cur) (+ cnt (:colspan cur 1))]))
- [[] 0]
- (concat row (repeat nil)))))]
-
- (cell {:investigate-url investigate-url} c))]))
- (conj [:tr (for [i (range cell-count)]
-
- (cell {:investigate-url investigate-url} {:value " "}))]))))])))
+ (com/content-card {:class "inline-block overflow-scroll"}
+ [:div {:class "overflow-scroll h-[70vh] m-4 inline-block"}
+ (when warning [:div.rounded.bg-red-50.text-red-800.p-4.m-2
+ warning])
+ (-> [:table {:class "text-sm text-left text-gray-500 dark:text-gray-400"}
+ [:thead {:class "text-xs text-gray-800 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400 font-bold"}
+ (map
+ (fn [header-row header]
+ (into
+ [:tr {:class " dark:border-gray-600 hover:bg-gray-100 dark:hover:bg-gray-700"}]
+ (map
+ (fn [w header i]
+ (cell {:width w
+ :investigate-url investigate-url
+ :other-style {:position "sticky"
+ :top (* header-row (+ 22 18))}} header))
+ widths
+ header
+ (range))))
+ (range)
+ (:header table))]]
+
+ (conj
+ (-> [:tbody {:style {}}]
+ (into
+ (for [[i row] (map vector (range) (:rows table))]
+
+ [:tr {:class " dark:border-gray-600 hover:bg-gray-100 dark:hover:bg-gray-700"}
+ (for [[i c] (map vector (range) (take cell-count
+ (reduce
+ (fn [[acc cnt] cur]
+ (if (>= (+ cnt (:colspan cur 1)) cell-count)
+ (reduced (conj acc cur))
+ [(conj acc cur) (+ cnt (:colspan cur 1))]))
+ [[] 0]
+ (concat row (repeat nil)))))]
+
+ (cell {:investigate-url investigate-url} c))]))
+ (conj [:tr (for [i (range cell-count)]
+
+ (cell {:investigate-url investigate-url} {:value " "}))]))))])))
(defn concat-tables [tables]
(let [[first & rest] tables]
@@ -120,8 +115,8 @@
:rows (concat (:rows first)
[[]]
(mapcat
- (fn [table]
- (-> (:header table)
- (into (:rows table))
- (conj [])))
- rest))}))
+ (fn [table]
+ (-> (:header table)
+ (into (:rows table))
+ (conj [])))
+ rest))}))
diff --git a/src/clj/auto_ap/ssr/nested_form_params.clj b/src/clj/auto_ap/ssr/nested_form_params.clj
index 1fa4648e..be15274e 100644
--- a/src/clj/auto_ap/ssr/nested_form_params.clj
+++ b/src/clj/auto_ap/ssr/nested_form_params.clj
@@ -39,11 +39,11 @@
"Return a list of name-value pairs for a parameter map."
[params]
(mapcat
- (fn [[name value]]
- (if (and (sequential? value) (not (coll? (first value))))
- (for [v value] [name v])
- [[name value]]))
- params))
+ (fn [[name value]]
+ (if (and (sequential? value) (not (coll? (first value))))
+ (for [v value] [name v])
+ [[name value]]))
+ params))
(defn- nest-params
"Takes a flat map of parameters and turns it into a nested map of
@@ -51,10 +51,10 @@
into keys."
[params parse]
(reduce
- (fn [m [k v]]
- (assoc-nested m (parse k) v))
- {}
- (param-pairs params)))
+ (fn [m [k v]]
+ (assoc-nested m (parse k) v))
+ {}
+ (param-pairs params)))
(defn nested-params-request
"Converts a request with a flat map of parameters to a nested map.
diff --git a/src/clj/auto_ap/ssr/not_found.clj b/src/clj/auto_ap/ssr/not_found.clj
index 4c75c895..b33c5eff 100644
--- a/src/clj/auto_ap/ssr/not_found.clj
+++ b/src/clj/auto_ap/ssr/not_found.clj
@@ -2,21 +2,20 @@
(:require [auto-ap.ssr.components :as com]
[auto-ap.ssr.ui :refer [base-page]]))
-
(defn page [{:keys [identity matched-route] :as request}]
(base-page
request
- (com/page { :request request
+ (com/page {:request request
:client (:client request)
:clients (:clients request)
:identity (:identity request)
:app-params {}}
#_(com/breadcrumbs {}
- [:a {:href (bidi/path-for ssr-routes/only-routes
- :company)}
- "My Company"])
+ [:a {:href (bidi/path-for ssr-routes/only-routes
+ :company)}
+ "My Company"])
[:div.flex.items-center.justify-center.flex-col
-
+
[:div.text-2xl.font-bold.text-gray-600 "Page not found"]
[:p.text-gray-500 "Sorry, we can't find the page you're looking for. Try going " (com/link {:href "/"} "home") " and try again."]])
"Not found"))
\ No newline at end of file
diff --git a/src/clj/auto_ap/ssr/outgoing_invoice/new.clj b/src/clj/auto_ap/ssr/outgoing_invoice/new.clj
index 16e8249b..1360d1d1 100644
--- a/src/clj/auto_ap/ssr/outgoing_invoice/new.clj
+++ b/src/clj/auto_ap/ssr/outgoing_invoice/new.clj
@@ -120,8 +120,6 @@
:value (-> (fc/field-value)
(atime/unparse-local atime/normal-date))})]))
-
-
(fc/with-field-default :outgoing-invoice/line-items [{:db/id "first"}]
(com/validated-field {:errors (fc/field-errors)
:label "Line items"}
@@ -280,6 +278,6 @@
(add-new-entity-handler [:outgoing-invoice/line-items]
(fn render [cursor request]
(line-item
- {:value cursor }))
+ {:value cursor}))
(fn build-new-row [base _]
base)))})
\ No newline at end of file
diff --git a/src/clj/auto_ap/ssr/payments.clj b/src/clj/auto_ap/ssr/payments.clj
index f2978cdd..65d4d587 100644
--- a/src/clj/auto_ap/ssr/payments.clj
+++ b/src/clj/auto_ap/ssr/payments.clj
@@ -1,9 +1,9 @@
(ns auto-ap.ssr.payments
(:require
[auto-ap.datomic
- :refer [add-sorter-fields apply-pagination apply-sort-3
- audit-transact conn merge-query observable-query
- pull-many]]
+ :refer [add-sorter-fields apply-pagination apply-sort-3
+ audit-transact conn merge-query observable-query
+ pull-many]]
[auto-ap.graphql.utils :refer [assert-can-see-client
exception->notification extract-client-ids
notify-if-locked]]
@@ -14,7 +14,7 @@
[auto-ap.routes.payments :as route]
[auto-ap.routes.transactions :as transaction-routes]
[auto-ap.routes.utils
- :refer [wrap-admin wrap-client-redirect-unauthenticated]]
+ :refer [wrap-admin wrap-client-redirect-unauthenticated]]
[auto-ap.ssr-routes :as ssr-routes]
[auto-ap.ssr.components :as com]
[auto-ap.ssr.components.bank-account-icon :as bank-account-icon]
@@ -24,11 +24,11 @@
[auto-ap.ssr.pos.common :refer [date-range-field*]]
[auto-ap.ssr.svg :as svg]
[auto-ap.ssr.utils
- :refer [apply-middleware-to-all-handlers clj-date-schema
- dissoc-nil-transformer entity-id html-response
- main-transformer modal-response ref->enum-schema strip
- wrap-entity wrap-implied-route-param wrap-merge-prior-hx
- wrap-schema-enforce]]
+ :refer [apply-middleware-to-all-handlers clj-date-schema
+ dissoc-nil-transformer entity-id html-response
+ main-transformer modal-response ref->enum-schema strip
+ wrap-entity wrap-implied-route-param wrap-merge-prior-hx
+ wrap-schema-enforce]]
[auto-ap.time :as atime]
[bidi.bidi :as bidi]
[clj-time.coerce :as coerce]
@@ -105,19 +105,18 @@
:size :small})])
(com/field {:label "Payment Type"}
(com/radio-card {:size :small
- :name "payment-type"
- :value (:payment-type (:query-params request))
- :options [{:value ""
- :content "All"}
- {:value "cash"
- :content "Cash"}
- {:value "check"
- :content "Check"}
- {:value "debit"
- :content "Debit"}]}))
+ :name "payment-type"
+ :value (:payment-type (:query-params request))
+ :options [{:value ""
+ :content "All"}
+ {:value "cash"
+ :content "Cash"}
+ {:value "check"
+ :content "Check"}
+ {:value "debit"
+ :content "Debit"}]}))
(exact-match-id* request)]])
-
(def default-read '[*
[:payment/date :xform clj-time.coerce/from-date]
{:invoice-payment/_payment [* {:invoice-payment/invoice [*]}]}
@@ -213,7 +212,6 @@
'[(iol-ion.query/dollars= ?transaction-amount ?amount)]]}
:args [(:amount query-params)]})
-
(:status route-params)
(merge-query {:query {:in ['?status]
:where ['[?e :payment/status ?status]]}
@@ -244,30 +242,30 @@
refunds))
(defn sum-visible-pending [ids]
- (->>
- (dc/q {:find ['?id '?o]
- :in ['$ '[?id ...]]
- :where ['[?id :payment/amount ?o]
- '[?id :payment/status :payment-status/pending]]}
- (dc/db conn)
- ids)
+ (->>
+ (dc/q {:find ['?id '?o]
+ :in ['$ '[?id ...]]
+ :where ['[?id :payment/amount ?o]
+ '[?id :payment/status :payment-status/pending]]}
+ (dc/db conn)
+ ids)
(map last)
(reduce
+
0.0)))
(defn sum-client-pending [clients]
- (->>
- (dc/q {:find '[?e ?a]
- :in '[$ [?clients ?start ?end]]
- :where '[[(iol-ion.query/scan-payments $ ?clients ?start ?end) [[?e _ ?sort-default] ...]]
- [?e :payment/status :payment-status/pending]
- [?e :payment/amount ?a]]}
- (dc/db conn)
- [clients
- nil
- nil])
-
+ (->>
+ (dc/q {:find '[?e ?a]
+ :in '[$ [?clients ?start ?end]]
+ :where '[[(iol-ion.query/scan-payments $ ?clients ?start ?end) [[?e _ ?sort-default] ...]]
+ [?e :payment/status :payment-status/pending]
+ [?e :payment/amount ?a]]}
+ (dc/db conn)
+ [clients
+ nil
+ nil])
+
(map last)
(reduce
+
@@ -278,16 +276,14 @@
{ids-to-retrieve :ids matching-count :count
all-ids :all-ids} (fetch-ids db request)]
-
[(->> (hydrate-results ids-to-retrieve db request))
matching-count
(sum-visible-pending all-ids)
(sum-client-pending (extract-client-ids (:clients request)
- (:client request)
- (:client-id (:query-params request))
- (when (:client-code (:query-params request))
- [:client/code (:client-code (:query-params request))])))
- ]))
+ (:client request)
+ (:client-id (:query-params request))
+ (when (:client-code (:query-params request))
+ [:client/code (:client-code (:query-params request))])))]))
(def query-schema (mc/schema
[:maybe [:map {:date-range [:date-range :start-date :end-date]}
@@ -328,7 +324,7 @@
(assoc-in (exact-match-id* request) [1 :hx-swap-oob] true)])
:query-schema query-schema
:action-buttons (fn [request]
- (let [[_ _ visible-in-float total-in-float ] (:page-results request)]
+ (let [[_ _ visible-in-float total-in-float] (:page-results request)]
[(com/pill {:color :primary} " Visible in float "
(format "$%,.2f" visible-in-float))
(com/pill {:color :secondary} " Total in float "
@@ -355,7 +351,7 @@
(= (-> request :query-params :sort first :name) "Bank account")
(-> entity :payment/bank-account :bank-account/name)
-
+
:else nil))
:title (fn [r]
(str
@@ -410,7 +406,7 @@
:render (fn [{:payment/keys [date]}]
(some-> date (atime/unparse-local atime/normal-date)))}
{:key "amount"
- :sort-key "amount"
+ :sort-key "amount"
:name "Amount"
:render (fn [{:payment/keys [amount]}]
(some->> amount (format "$%.2f")))}
@@ -422,10 +418,10 @@
(map :invoice-payment/invoice)
(filter identity)
(map (fn [invoice]
- {:link (hu/url (bidi/path-for ssr-routes/only-routes
- ::invoice-route/all-page)
- {:exact-match-id (:db/id invoice)})
- :content (str "Inv. " (:invoice/invoice-number invoice))})))
+ {:link (hu/url (bidi/path-for ssr-routes/only-routes
+ ::invoice-route/all-page)
+ {:exact-match-id (:db/id invoice)})
+ :content (str "Inv. " (:invoice/invoice-number invoice))})))
(some-> p :transaction/_payment ((fn [t]
[{:link (hu/url (bidi/path-for ssr-routes/only-routes ::transaction-routes/all-page)
{:exact-match-id (:db/id (first t))})
@@ -434,8 +430,6 @@
(def row* (partial helper/row* grid-page))
-
-
(comment
(mc/decode query-schema {"exact-match-id" "123"} (mt/transformer main-transformer mt/strip-extra-keys-transformer))
(mc/decode query-schema {} (mt/transformer main-transformer mt/strip-extra-keys-transformer))
@@ -445,7 +439,6 @@
(mc/decode query-schema {"payment-type" "food"} (mt/transformer main-transformer mt/strip-extra-keys-transformer))
(mc/decode query-schema {"vendor" "87"} (mt/transformer main-transformer mt/strip-extra-keys-transformer))
-
(mc/decode query-schema {"start-date" #inst "2023-12-21T08:00:00.000-00:00"} (mt/transformer main-transformer mt/strip-extra-keys-transformer)))
(defn delete [{check :entity :as request identity :identity}]
@@ -459,7 +452,7 @@
#(assert-can-see-client identity (:db/id (:payment/client check))))
(notify-if-locked (:db/id (:payment/client check))
(:payment/date check))
- (let [ removing-payments (mapcat (fn [x]
+ (let [removing-payments (mapcat (fn [x]
(let [invoice (:invoice-payment/invoice x)
new-balance (+ (:invoice/outstanding-balance invoice)
(:invoice-payment/amount x))]
@@ -475,9 +468,9 @@
:payment/status :payment-status/voided}]
(audit-transact (cond-> removing-payments
true (conj updated-payment)
- (:transaction/_payment check) (conj [:db/retract (:db/id (first (:transaction/_payment check)))
+ (:transaction/_payment check) (conj [:db/retract (:db/id (first (:transaction/_payment check)))
:transaction/payment
- (:db/id check)]))
+ (:db/id check)]))
identity)
(html-response (row* (:identity request) updated-payment {:delete-after-settle? true :class "live-removed"
@@ -578,7 +571,6 @@
(assoc-in [:query-params :start] 0)
(assoc-in [:query-params :per-page] 250))))
-
:else
selected)
updated-count (void-payments-internal ids (:identity request))]
@@ -591,7 +583,7 @@
(defn wrap-status-from-source [handler]
(fn [{:keys [matched-current-page-route] :as request}]
- (let [ request (cond-> request
+ (let [request (cond-> request
(= ::route/cleared-page matched-current-page-route) (assoc-in [:route-params :status] :payment-status/cleared)
(= ::route/pending-page matched-current-page-route) (assoc-in [:route-params :status] :payment-status/pending)
(= ::route/voided-page matched-current-page-route) (assoc-in [:route-params :status] :payment-status/voided)
@@ -605,7 +597,7 @@
::route/pending-page (-> (helper/page-route grid-page)
(wrap-implied-route-param :status :payment-status/pending))
::route/voided-page (-> (helper/page-route grid-page)
- (wrap-implied-route-param :status :payment-status/voided))
+ (wrap-implied-route-param :status :payment-status/voided))
::route/all-page (-> (helper/page-route grid-page)
(wrap-implied-route-param :status nil))
@@ -618,7 +610,6 @@
::route/bulk-delete (-> bulk-delete-dialog
(wrap-admin))
-
::route/table (helper/table-route grid-page)}
(fn [h]
(-> h
diff --git a/src/clj/auto_ap/ssr/pos/cash_drawer_shifts.clj b/src/clj/auto_ap/ssr/pos/cash_drawer_shifts.clj
index 4dc52eed..e880a135 100644
--- a/src/clj/auto_ap/ssr/pos/cash_drawer_shifts.clj
+++ b/src/clj/auto_ap/ssr/pos/cash_drawer_shifts.clj
@@ -25,7 +25,7 @@
[:maybe clj-date-schema]]
[:end-date {:optional true}
[:maybe clj-date-schema]]
- [:client {:optional true :default nil} [:maybe [:entity-map {:pull [:db/id :client/name]}]]] ]
+ [:client {:optional true :default nil} [:maybe [:entity-map {:pull [:db/id :client/name]}]]]]
default-grid-fields-schema)]))
(defn filters [params]
@@ -36,7 +36,7 @@
"hx-indicator" "#cash-drawer-shift-table"
#_#_:hx-disabled-elt "find fieldset"}
- [:fieldset.space-y-6
+ [:fieldset.space-y-6
(date-range-field* params)
(total-field* params)]])
@@ -52,15 +52,14 @@
:where '[[(iol-ion.query/scan-cash-drawer-shifts $ ?clients ?start-date ?end-date) [[?e _ ?sort-default] ...]]]}
:args [db [(:trimmed-clients request)
(some-> (:start-date query-params) c/to-date)
- (some-> (:end-date query-params) c/to-date )]]}
+ (some-> (:end-date query-params) c/to-date)]]}
(:sort query-params) (add-sorter-fields {"client" ['[?e :cash-drawer-shift/client ?c]
'[?c :client/name ?sort-client]]
"date" ['[?e :cash-drawer-shift/date ?sort-date]]
"paid-in" ['[?e :cash-drawer-shift/paid-in ?sort-paid-in]]
"paid-out" ['[?e :cash-drawer-shift/paid-out ?sort-paid-out]]
"expected-cash" ['[?e :cash-drawer-shift/expected-cash ?sort-expected-cash]]
- "opened-cash" ['[?e :cash-drawer-shift/opened-cash ?sort-opened-cash]]
- }
+ "opened-cash" ['[?e :cash-drawer-shift/opened-cash ?sort-opened-cash]]}
query-params)
(:exact-match-id query-params)
@@ -71,7 +70,7 @@
true
(merge-query {:query {:find ['?sort-default '?e]
:where ['[?e :cash-drawer-shift/date ?sort-default]]}}))]
-
+
(cond->> (query2 query)
true (apply-sort-3 query-params)
true (apply-pagination query-params))))
@@ -80,8 +79,8 @@
(let [results (->> (pull-many db default-read ids)
(group-by :db/id))
cash-drawer-shifts (->> ids
- (map results)
- (map first))]
+ (map results)
+ (map first))]
cash-drawer-shifts))
(defn fetch-page [request]
@@ -109,7 +108,7 @@
"Cash Drawer Shifts"]]
:title "Cash drawer shifts"
:entity-name "Cash drawer shift"
- :query-schema query-schema
+ :query-schema query-schema
:route :pos-cash-drawer-shift-table
:headers [{:key "client"
:name "Client"
@@ -138,12 +137,11 @@
:sort-key "opened-cash"
:render #(some->> % :cash-drawer-shift/opened-cash (format "$%.2f"))}]}))
-
(def row* (partial helper/row* grid-page))
(def table* (partial helper/table* grid-page))
(def key->handler
- (apply-middleware-to-all-handlers
+ (apply-middleware-to-all-handlers
{:pos-cash-drawer-shifts (helper/page-route grid-page)
:pos-cash-drawer-shift-table (helper/table-route grid-page)}
(fn [h]
diff --git a/src/clj/auto_ap/ssr/pos/common.clj b/src/clj/auto_ap/ssr/pos/common.clj
index 57ef83d5..fff74367 100644
--- a/src/clj/auto_ap/ssr/pos/common.clj
+++ b/src/clj/auto_ap/ssr/pos/common.clj
@@ -5,30 +5,30 @@
[auto-ap.ssr.svg :as svg]))
(defn date-range-field* [request]
(dr/date-range-field {:value {:start (:start-date (:query-params request))
- :end (:end-date (:query-params request))}
- :id "date-range"}))
+ :end (:end-date (:query-params request))}
+ :id "date-range"}))
(defn processor-field* [request]
(com/field {:label "Processor"}
(com/radio-card {:size :small
- :name "processor"
- :value (:processor (:query-params request))
- :options [{:value ""
- :content "All"}
- {:value "square"
- :content [:div.flex.space-x-2 [:img.align-center {:src "/img/square.png" :style {:width "16px" :height "16px"}}] [:div "Square"]]}
- {:value "doordash"
- :content [:div.flex.space-x-2 [:img.align-center {:src "/img/doordash.png" :style {:width "16px" :height "16px"}}] [:div "Doordash"]]}
- {:value "uber-eats"
- :content [:div.flex.space-x-2 [:img.align-center {:src "/img/ubereats.png" :style {:width "16px" :height "16px"}}] [:div "Uber eats"]]}
- {:value "grubhub"
- :content [:div.flex.space-x-2 [:img.align-center {:src "/img/grubhub.png" :style {:width "16px" :height "16px"}}] [:div "Grubhub"]]}
- {:value "koala"
- :content [:div.flex.space-x-2 [:img.align-center {:src "/img/koala.png" :style {:width "16px" :height "16px"}}] [:div "Koala"]]}
- {:value "ezcater"
- :content [:div.flex.space-x-2 [:img.align-center {:src "/img/ezcater.png" :style {:width "16px" :height "16px"}}] [:div "EZCater"]]}
- {:value "na"
- :content "No Processor"}]})))
+ :name "processor"
+ :value (:processor (:query-params request))
+ :options [{:value ""
+ :content "All"}
+ {:value "square"
+ :content [:div.flex.space-x-2 [:img.align-center {:src "/img/square.png" :style {:width "16px" :height "16px"}}] [:div "Square"]]}
+ {:value "doordash"
+ :content [:div.flex.space-x-2 [:img.align-center {:src "/img/doordash.png" :style {:width "16px" :height "16px"}}] [:div "Doordash"]]}
+ {:value "uber-eats"
+ :content [:div.flex.space-x-2 [:img.align-center {:src "/img/ubereats.png" :style {:width "16px" :height "16px"}}] [:div "Uber eats"]]}
+ {:value "grubhub"
+ :content [:div.flex.space-x-2 [:img.align-center {:src "/img/grubhub.png" :style {:width "16px" :height "16px"}}] [:div "Grubhub"]]}
+ {:value "koala"
+ :content [:div.flex.space-x-2 [:img.align-center {:src "/img/koala.png" :style {:width "16px" :height "16px"}}] [:div "Koala"]]}
+ {:value "ezcater"
+ :content [:div.flex.space-x-2 [:img.align-center {:src "/img/ezcater.png" :style {:width "16px" :height "16px"}}] [:div "EZCater"]]}
+ {:value "na"
+ :content "No Processor"}]})))
(defn total-field* [request]
(com/field {:label "Total"}
@@ -40,7 +40,7 @@
:value (:total-gte (:query-params request))
:placeholder "0.01"
:size :small})
- [:div.align-baseline
+ [:div.align-baseline
"to"]
(com/money-input {:name "total-lte"
:hx-preserve "true"
@@ -52,7 +52,7 @@
(defn exact-match-id-field* [request]
(when-let [exact-match-id (:exact-match-id (:query-params request))]
- [:div
+ [:div
(com/field {:label "Exact match"}
(com/pill {:color :primary}
[:span.inline-flex.gap-2
diff --git a/src/clj/auto_ap/ssr/pos/expected_deposits.clj b/src/clj/auto_ap/ssr/pos/expected_deposits.clj
index 546aebdb..c664a90d 100644
--- a/src/clj/auto_ap/ssr/pos/expected_deposits.clj
+++ b/src/clj/auto_ap/ssr/pos/expected_deposits.clj
@@ -33,7 +33,6 @@
[:processor {:optional true} [:maybe (ref->enum-schema "ccp-processor")]]]
default-grid-fields-schema)]))
-
(defn filters [request]
[:form {"hx-trigger" "change delay:500ms, keyup changed from:.hot-filter delay:1000ms"
"hx-get" (bidi/path-for ssr-routes/only-routes
@@ -42,7 +41,7 @@
"hx-indicator" "#expected-deposit-table"
#_#_:hx-disabled-elt "find fieldset"}
- [:fieldset.space-y-6
+ [:fieldset.space-y-6
(date-range-field* request)
(exact-match-id-field* request)]])
@@ -70,26 +69,25 @@
(some-> (:start-date query-params) c/to-date)
(some-> (:end-date query-params) c/to-date)]]}
(:sort query-params) (add-sorter-fields {"client" ['[?e :expected-deposit/client ?c]
- '[?c :client/name ?sort-client]]
- "location" ['[?e :expected-deposit/location ?sort-location]]
- "date" ['[?e :expected-deposit/date ?sort-date]]
- "total" ['[?e :expected-deposit/total ?sort-total]]
- "fee" ['[?e :expected-deposit/fee ?sort-fee]]}
- query-params)
+ '[?c :client/name ?sort-client]]
+ "location" ['[?e :expected-deposit/location ?sort-location]]
+ "date" ['[?e :expected-deposit/date ?sort-date]]
+ "total" ['[?e :expected-deposit/total ?sort-total]]
+ "fee" ['[?e :expected-deposit/fee ?sort-fee]]}
+ query-params)
(:exact-match-id query-params)
(merge-query {:query {:in ['?e]
:where []}
:args [(:exact-match-id query-params)]})
-
- (:total-gte query-params)
+ (:total-gte query-params)
(merge-query {:query {:in ['?total-gte]
:where ['[?e :expected-deposit/total ?a]
'[(>= ?a ?total-gte)]]}
:args [(:total-gte query-params)]})
- (:total-lte query-params)
+ (:total-lte query-params)
(merge-query {:query {:in ['?total-lte]
:where ['[?e :expected-deposit/total ?a]
'[(<= ?a ?total-lte)]]}
@@ -104,7 +102,7 @@
true
(merge-query {:query {:find ['?sort-default '?e]
:where ['[?e :expected-deposit/date ?sort-default]]}}))]
-
+
(cond->> (query2 query)
true (apply-sort-3 query-params)
true (apply-pagination query-params))))
@@ -113,25 +111,25 @@
(let [results (->> (pull-many db default-read ids)
(group-by :db/id))
payments (->> ids
- (map results)
- (map first)
- (map (fn get-totals [ed]
- (assoc ed :totals
- (->> (dc/q '[:find ?d4 (count ?c) (sum ?a)
- :in $ ?ed
- :where [?ed :expected-deposit/charges ?c]
- [?c :charge/total ?a]
- [?o :sales-order/charges ?c]
- [?o :sales-order/date ?d]
- [(clj-time.coerce/from-date ?d) ?d2]
- [(auto-ap.time/localize ?d2) ?d3]
- [(clj-time.coerce/to-local-date ?d3) ?d4]]
- (dc/db conn)
- (:db/id ed))
- (map (fn [[date count amount]]
- {:date (c/to-date-time date)
- :count count
- :amount amount})))))))]
+ (map results)
+ (map first)
+ (map (fn get-totals [ed]
+ (assoc ed :totals
+ (->> (dc/q '[:find ?d4 (count ?c) (sum ?a)
+ :in $ ?ed
+ :where [?ed :expected-deposit/charges ?c]
+ [?c :charge/total ?a]
+ [?o :sales-order/charges ?c]
+ [?o :sales-order/date ?d]
+ [(clj-time.coerce/from-date ?d) ?d2]
+ [(auto-ap.time/localize ?d2) ?d3]
+ [(clj-time.coerce/to-local-date ?d3) ?d4]]
+ (dc/db conn)
+ (:db/id ed))
+ (map (fn [[date count amount]]
+ {:date (c/to-date-time date)
+ :count count
+ :amount amount})))))))]
payments))
(defn fetch-page [args]
@@ -142,66 +140,64 @@
matching-count]))
(def grid-page
- (helper/build
- {:id "expected-deposit-table"
- :nav com/main-aside-nav
- :page-specific-nav filters
- :fetch-page fetch-page
- :oob-render
- (fn [request]
- [(assoc-in (date-range-field* request) [1 :hx-swap-oob] true)])
- :breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes
- :company)}
- "POS"]
+ (helper/build
+ {:id "expected-deposit-table"
+ :nav com/main-aside-nav
+ :page-specific-nav filters
+ :fetch-page fetch-page
+ :oob-render
+ (fn [request]
+ [(assoc-in (date-range-field* request) [1 :hx-swap-oob] true)])
+ :breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes
+ :company)}
+ "POS"]
- [:a {:href (bidi/path-for ssr-routes/only-routes
- :pos-expected-deposits)}
- "Expected deposits"]]
- :title "Expected deposits"
- :entity-name "Expected deposit"
- :query-schema query-schema
- :route :pos-expected-deposit-table
- :row-buttons (fn [_ e]
- [
- (when (:expected-deposit/reference-link e)
- (com/a-icon-button {:href (:expected-deposit/reference-link e)}
- svg/external-link))
- (when-let [transaction-id (-> e (:transaction/_expected-deposit) first :db/id)]
- (com/a-button {:href (str (bidi/path-for ssr-routes/only-routes
- ::transaction-routes/all-page)
- "?exact-match-id="
- transaction-id)} "Transaction"))])
- :headers [{:key "client"
- :name "Client"
- :sort-key "client"
- :hide? (fn [args]
- (= (count (:clients args)) 1))
- :render #(-> % :expected-deposit/client :client/code)}
- {:key "date"
- :name "Date"
- :sort-key "date"
- :render #(atime/unparse-local (:expected-deposit/date %) atime/standard-time)}
- {:key "sales-date"
- :name "Sales Date"
- :sort-key "sales-date"
- :render #(atime/unparse-local (:expected-deposit/sales-date %) atime/standard-time)}
- {:key "total"
- :name "Total"
- :sort-key "total"
- :render #(some->> % :expected-deposit/total (format "$%.2f"))}
- {:key "fee"
- :name "Fee"
- :sort-key "fee"
- :render #(some->> % :expected-deposit/fee (format "$%.2f"))}]}))
+ [:a {:href (bidi/path-for ssr-routes/only-routes
+ :pos-expected-deposits)}
+ "Expected deposits"]]
+ :title "Expected deposits"
+ :entity-name "Expected deposit"
+ :query-schema query-schema
+ :route :pos-expected-deposit-table
+ :row-buttons (fn [_ e]
+ [(when (:expected-deposit/reference-link e)
+ (com/a-icon-button {:href (:expected-deposit/reference-link e)}
+ svg/external-link))
+ (when-let [transaction-id (-> e (:transaction/_expected-deposit) first :db/id)]
+ (com/a-button {:href (str (bidi/path-for ssr-routes/only-routes
+ ::transaction-routes/all-page)
+ "?exact-match-id="
+ transaction-id)} "Transaction"))])
+ :headers [{:key "client"
+ :name "Client"
+ :sort-key "client"
+ :hide? (fn [args]
+ (= (count (:clients args)) 1))
+ :render #(-> % :expected-deposit/client :client/code)}
+ {:key "date"
+ :name "Date"
+ :sort-key "date"
+ :render #(atime/unparse-local (:expected-deposit/date %) atime/standard-time)}
+ {:key "sales-date"
+ :name "Sales Date"
+ :sort-key "sales-date"
+ :render #(atime/unparse-local (:expected-deposit/sales-date %) atime/standard-time)}
+ {:key "total"
+ :name "Total"
+ :sort-key "total"
+ :render #(some->> % :expected-deposit/total (format "$%.2f"))}
+ {:key "fee"
+ :name "Fee"
+ :sort-key "fee"
+ :render #(some->> % :expected-deposit/fee (format "$%.2f"))}]}))
(def row* (partial helper/row* grid-page))
(def table* (partial helper/table* grid-page))
-
(def key->handler
(apply-middleware-to-all-handlers
{:pos-expected-deposits (helper/page-route grid-page)
- :pos-expected-deposit-table (helper/table-route grid-page)}
+ :pos-expected-deposit-table (helper/table-route grid-page)}
(fn [h]
(-> h
(wrap-copy-qp-pqp)
diff --git a/src/clj/auto_ap/ssr/pos/refunds.clj b/src/clj/auto_ap/ssr/pos/refunds.clj
index c33b6784..617d46f7 100644
--- a/src/clj/auto_ap/ssr/pos/refunds.clj
+++ b/src/clj/auto_ap/ssr/pos/refunds.clj
@@ -36,7 +36,7 @@
"hx-indicator" "#refund-table"
#_#_:hx-disabled-elt "find fieldset"}
- [:fieldset.space-y-6
+ [:fieldset.space-y-6
(date-range-field* request)
(total-field* request)]])
@@ -54,7 +54,7 @@
:where '[[(iol-ion.query/scan-sales-refunds $ ?clients ?start-date ?end-date) [[?e _ ?sort-default] ...]]]}
:args [db [(:trimmed-clients request)
(some-> query-params :start-date c/to-date)
- (some-> query-params :end-date c/to-date )]]}
+ (some-> query-params :end-date c/to-date)]]}
(:sort query-params) (add-sorter-fields {"client" ['[?e :sales-refund/client ?c]
'[?c :client/name ?sort-client]]
"date" ['[?e :sales-refund/date ?sort-date]]
@@ -68,13 +68,13 @@
:where []}
:args [(:exact-match-id query-params)]})
- (:total-gte query-params)
+ (:total-gte query-params)
(merge-query {:query {:in ['?total-gte]
:where ['[?e :sales-refund/total ?a]
'[(>= ?a ?total-gte)]]}
:args [(:total-gte query-params)]})
- (:total-lte query-params)
+ (:total-lte query-params)
(merge-query {:query {:in ['?total-lte]
:where ['[?e :sales-refund/total ?a]
'[(<= ?a ?total-lte)]]}
@@ -83,7 +83,7 @@
true
(merge-query {:query {:find ['?sort-default '?e]
:where ['[?e :sales-refund/date ?sort-default]]}}))]
-
+
(cond->> (query2 query)
true (apply-sort-3 query-params)
true (apply-pagination query-params))))
@@ -149,13 +149,13 @@
(def table* (partial helper/table* grid-page))
(def key->handler
- (apply-middleware-to-all-handlers
+ (apply-middleware-to-all-handlers
{:pos-refunds (helper/page-route grid-page)
:pos-refund-table (helper/table-route grid-page)}
(fn [h]
- (-> h
- (wrap-copy-qp-pqp)
- (wrap-apply-sort grid-page)
- (wrap-merge-prior-hx)
- (wrap-schema-enforce :query-schema query-schema)
- (wrap-schema-enforce :hx-schema query-schema)))))
+ (-> h
+ (wrap-copy-qp-pqp)
+ (wrap-apply-sort grid-page)
+ (wrap-merge-prior-hx)
+ (wrap-schema-enforce :query-schema query-schema)
+ (wrap-schema-enforce :hx-schema query-schema)))))
diff --git a/src/clj/auto_ap/ssr/pos/sales_orders.clj b/src/clj/auto_ap/ssr/pos/sales_orders.clj
index 8fb5ec31..6e5b4be2 100644
--- a/src/clj/auto_ap/ssr/pos/sales_orders.clj
+++ b/src/clj/auto_ap/ssr/pos/sales_orders.clj
@@ -36,29 +36,28 @@
(defn filters [request]
[:form {"hx-trigger" "change delay:500ms, keyup changed from:.hot-filter delay:1000ms"
"hx-get" (bidi/path-for ssr-routes/only-routes
- :pos-sales-table)
+ :pos-sales-table)
"hx-target" "#sales-table"
"hx-indicator" "#sales-table"
#_#_:hx-disabled-elt "find fieldset"}
- [:fieldset.space-y-6
+ [:fieldset.space-y-6
(date-range-field* request)
(total-field* request)
[:div
(com/field {:label "Payment Method"}
(com/radio-card {:size :small
- :name "payment-method"
- :options [{:value ""
- :content "All"}
- {:value "CASH"
- :content "Cash"}
- {:value "CARD"
- :content "Card"}
- {:value "SQUARE_GIFT_CARD"
- :content "Gift Card"}
- {:value "OTHER"
- :content "Other"}
- ]}))]
+ :name "payment-method"
+ :options [{:value ""
+ :content "All"}
+ {:value "CASH"
+ :content "Cash"}
+ {:value "CARD"
+ :content "Card"}
+ {:value "SQUARE_GIFT_CARD"
+ :content "Gift Card"}
+ {:value "OTHER"
+ :content "Other"}]}))]
[:div
(processor-field* request)]
@@ -87,8 +86,7 @@
:sales-order/source,
:sales-order/reference-link,
{:sales-order/client [:client/name :db/id :client/code]
- :sales-order/charges [
- :charge/type-name,
+ :sales-order/charges [:charge/type-name,
:charge/total,
:charge/tax,
:charge/tip,
@@ -125,13 +123,13 @@
:where []}
:args [(:exact-match-id query-params)]})
- (:total-gte query-params)
+ (:total-gte query-params)
(merge-query {:query {:in ['?total-gte]
:where ['[?e :sales-order/total ?a]
'[(>= ?a ?total-gte)]]}
:args [(:total-gte query-params)]})
- (:total-lte query-params)
+ (:total-lte query-params)
(merge-query {:query {:in ['?total-lte]
:where ['[?e :sales-order/total ?a]
'[(<= ?a ?total-lte)]]}
@@ -155,7 +153,6 @@
'[?chg :charge/processor ?processor]]}
:args [(:processor query-params)]})
-
true
(merge-query {:query {:find ['?sort-default '?e]}}))]
(clojure.pprint/pprint (update-in query [:args] #(drop 1 %)))
@@ -178,7 +175,6 @@
[(->> (hydrate-results ids-to-retrieve db request))
matching-count]))
-
(def grid-page
(helper/build
{:id "sales-table"
@@ -255,7 +251,6 @@
"OTHER" "other"
nil)))])}]}))
-
(def row* (partial helper/row* grid-page))
(def table* (partial helper/table* grid-page))
diff --git a/src/clj/auto_ap/ssr/pos/sales_summaries.clj b/src/clj/auto_ap/ssr/pos/sales_summaries.clj
index 45988b07..315e6beb 100644
--- a/src/clj/auto_ap/ssr/pos/sales_summaries.clj
+++ b/src/clj/auto_ap/ssr/pos/sales_summaries.clj
@@ -158,13 +158,13 @@
[:span.text-sm account-name]
(com/pill {:color :red} "Missing acct"))
(com/a-icon-button {:class "p-1"
- :hx-get (bidi/path-for ssr-routes/only-routes ::route/edit-item-account)
- :hx-target "closest .account-cell"
- :hx-swap "outerHTML"
- :hx-vals (hx/json {:item-index (or (:item-index item) 0)
- :client-id client-id
- :current-account-id (or account-id "")})}
- svg/pencil)]))
+ :hx-get (bidi/path-for ssr-routes/only-routes ::route/edit-item-account)
+ :hx-target "closest .account-cell"
+ :hx-swap "outerHTML"
+ :hx-vals (hx/json {:item-index (or (:item-index item) 0)
+ :client-id client-id
+ :current-account-id (or account-id "")})}
+ svg/pencil)]))
(defn account-edit-cell [{:keys [field-name-prefix client-id current-account-id]}]
(let [account-input-name (str field-name-prefix "[ledger-mapped/account]")]
@@ -172,23 +172,23 @@
(account-typeahead* {:name account-input-name
:value current-account-id
:client-id client-id})
- [:div.flex.gap-1
- (com/a-icon-button {:class "p-1"
- :hx-put (bidi/path-for ssr-routes/only-routes ::route/save-item-account)
- :hx-target "closest .account-cell"
- :hx-swap "outerHTML"
- :hx-include "closest .account-cell"
- :hx-vals (hx/json {:field-name-prefix field-name-prefix
- :client-id client-id})}
- svg/check)
- (com/a-icon-button {:class "p-1"
- :hx-get (bidi/path-for ssr-routes/only-routes ::route/cancel-item-account)
- :hx-target "closest .account-cell"
- :hx-swap "outerHTML"
- :hx-vals (hx/json {:field-name-prefix field-name-prefix
- :client-id client-id
- :current-account-id (or current-account-id "")})}
- svg/x)]]))
+ [:div.flex.gap-1
+ (com/a-icon-button {:class "p-1"
+ :hx-put (bidi/path-for ssr-routes/only-routes ::route/save-item-account)
+ :hx-target "closest .account-cell"
+ :hx-swap "outerHTML"
+ :hx-include "closest .account-cell"
+ :hx-vals (hx/json {:field-name-prefix field-name-prefix
+ :client-id client-id})}
+ svg/check)
+ (com/a-icon-button {:class "p-1"
+ :hx-get (bidi/path-for ssr-routes/only-routes ::route/cancel-item-account)
+ :hx-target "closest .account-cell"
+ :hx-swap "outerHTML"
+ :hx-vals (hx/json {:field-name-prefix field-name-prefix
+ :client-id client-id
+ :current-account-id (or current-account-id "")})}
+ svg/x)]]))
(def grid-page
(helper/build {:id "entity-table"
@@ -576,8 +576,8 @@
[:span.text-gray-500 (truncate (:sales-summary-item/category item) 30)]
(account-display-cell {:item (assoc item :item-index actual-idx)
:field-name-prefix (str "step-params[sales-summary/items][" actual-idx "]")
- :client-id client-id})
- [:span.ml-auto.font-mono.tabular-nums.text-gray-900 (format "$%,.2f" (:ledger-mapped/amount item))]]))
+ :client-id client-id})
+ [:span.ml-auto.font-mono.tabular-nums.text-gray-900 (format "$%,.2f" (:ledger-mapped/amount item))]]))
[:div.h-6]))]
[:div.mt-2.border-t.pt-1
(summary-total-display request)
@@ -619,13 +619,13 @@
[:span.text-gray-500 (truncate (:sales-summary-item/category item) 30)]
(account-display-cell {:item (assoc item :item-index actual-idx)
:field-name-prefix (str "step-params[sales-summary/items][" actual-idx "]")
- :client-id client-id})
- [:span.ml-auto.font-mono.tabular-nums.text-gray-900 (format "$%,.2f" (:ledger-mapped/amount item))]]))
+ :client-id client-id})
+ [:span.ml-auto.font-mono.tabular-nums.text-gray-900 (format "$%,.2f" (:ledger-mapped/amount item))]]))
[:div.h-6]))]
[:div.mt-2.border-t.pt-1
(summary-total-display request)
(unbalanced-display request)]]]
- [:div.mt-4.border-t.pt-2
+ [:div.mt-4.border-t.pt-2
(fc/with-field :sales-summary/items
(com/data-grid-new-row {:colspan 2
:hx-get (bidi/path-for ssr-routes/only-routes ::route/new-summary-item)
@@ -761,16 +761,16 @@
::route/edit-wizard-navigate (-> mm/next-handler
(mm/wrap-wizard edit-wizard)
(mm/wrap-decode-multi-form-state))
- ::route/new-summary-item (-> (add-new-entity-handler [:step-params :sales-summary/items]
- (fn render [cursor request]
- (sales-summary-item-row*
- {:value cursor
- :client-id (:client-id (:query-params request))}))
- (fn build-new-row [base _]
- (assoc base :sales-summary-item/manual? true)))
- (wrap-schema-enforce :query-schema [:map
- [:client-id {:optional true}
- [:maybe entity-id]]]))
+ ::route/new-summary-item (-> (add-new-entity-handler [:step-params :sales-summary/items]
+ (fn render [cursor request]
+ (sales-summary-item-row*
+ {:value cursor
+ :client-id (:client-id (:query-params request))}))
+ (fn build-new-row [base _]
+ (assoc base :sales-summary-item/manual? true)))
+ (wrap-schema-enforce :query-schema [:map
+ [:client-id {:optional true}
+ [:maybe entity-id]]]))
::route/edit-item-account (-> edit-item-account
(wrap-schema-enforce :query-schema [:map
[:item-index nat-int?]
diff --git a/src/clj/auto_ap/ssr/pos/tenders.clj b/src/clj/auto_ap/ssr/pos/tenders.clj
index 4b42ec4f..664cbf57 100644
--- a/src/clj/auto_ap/ssr/pos/tenders.clj
+++ b/src/clj/auto_ap/ssr/pos/tenders.clj
@@ -29,7 +29,7 @@
"hx-indicator" "#tender-table"
#_#_:hx-disabled-elt "find fieldset"}
- [:fieldset.space-y-6
+ [:fieldset.space-y-6
(date-range-field* request)
(processor-field* request)
(total-field* request)]])
@@ -79,13 +79,13 @@
:where []}
:args [(:exact-match-id query-params)]})
- (:total-gte query-params)
+ (:total-gte query-params)
(merge-query {:query {:in ['?total-gte]
:where ['[?e :charge/total ?a]
'[(>= ?a ?total-gte)]]}
:args [(:total-gte query-params)]})
- (:total-lte query-params)
+ (:total-lte query-params)
(merge-query {:query {:in ['?total-lte]
:where ['[?e :charge/total ?a]
'[(<= ?a ?total-lte)]]}
@@ -96,10 +96,9 @@
:where ['[?e :charge/processor ?processor]]}
:args [(:processor query-params)]})
-
true
(merge-query {:query {:find ['?sort-default '?e]}}))]
-
+
(cond->> (query2 query)
true (apply-sort-3 query-params)
true (apply-pagination query-params))))
@@ -121,63 +120,62 @@
(def grid-page
(helper/build
- {:id "tender-table"
- :nav com/main-aside-nav
- :page-specific-nav filters
- :fetch-page fetch-page
- :oob-render
- (fn [request]
- [(assoc-in (date-range-field* request) [1 :hx-swap-oob] true)])
- :breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes
- :company)}
- "POS"]
-
- [:a {:href (bidi/path-for ssr-routes/only-routes
- :pos-tenders)}
- "Tenders"]]
- :title "Tenders"
- :entity-name "Tender"
- :query-schema query-schema
- :route :pos-tender-table
- :row-buttons (fn [request e]
- (when (:charge/reference-link e)
- [(com/a-icon-button {:href (:charge/reference-link e)}
- svg/external-link)]))
- :headers [{:key "client"
- :name "Client"
- :sort-key "client"
- :hide? (fn [request]
- (= (count (:clients request)) 1))
- :render #(-> % :charge/client :client/code)}
- {:key "date"
- :name "Date"
- :sort-key "date"
- :render #(atime/unparse-local (:charge/date %) atime/standard-time)}
- {:key "total"
- :name "Total"
- :sort-key "total"
- :render #(some->> % :charge/total (format "$%.2f"))}
- {:key "processor"
- :name "Processor"
- :sort-key "processor"
- :render (fn [sales-order]
- (when (:charge/processor sales-order)
- (com/pill {:color :primary }
- (name (:charge/processor sales-order)))))}
- {:key "tip"
- :name "Tip"
- :sort-key "tip"
- :render #(some->> % :charge/tip (format "$%.2f"))}
- {:key "links"
- :name "Links"
- :render (fn [entity]
- (when-let [expected-deposit-id (some->> entity :expected-deposit/_charges first :db/id)]
- [:a {:href (str (bidi/path-for ssr-routes/only-routes
- :pos-expected-deposits)
- "?exact-match-id=" expected-deposit-id)
- :hx-boost "true"}
- (com/pill {:color :secondary} "expected deposit")]))}]}))
+ {:id "tender-table"
+ :nav com/main-aside-nav
+ :page-specific-nav filters
+ :fetch-page fetch-page
+ :oob-render
+ (fn [request]
+ [(assoc-in (date-range-field* request) [1 :hx-swap-oob] true)])
+ :breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes
+ :company)}
+ "POS"]
+ [:a {:href (bidi/path-for ssr-routes/only-routes
+ :pos-tenders)}
+ "Tenders"]]
+ :title "Tenders"
+ :entity-name "Tender"
+ :query-schema query-schema
+ :route :pos-tender-table
+ :row-buttons (fn [request e]
+ (when (:charge/reference-link e)
+ [(com/a-icon-button {:href (:charge/reference-link e)}
+ svg/external-link)]))
+ :headers [{:key "client"
+ :name "Client"
+ :sort-key "client"
+ :hide? (fn [request]
+ (= (count (:clients request)) 1))
+ :render #(-> % :charge/client :client/code)}
+ {:key "date"
+ :name "Date"
+ :sort-key "date"
+ :render #(atime/unparse-local (:charge/date %) atime/standard-time)}
+ {:key "total"
+ :name "Total"
+ :sort-key "total"
+ :render #(some->> % :charge/total (format "$%.2f"))}
+ {:key "processor"
+ :name "Processor"
+ :sort-key "processor"
+ :render (fn [sales-order]
+ (when (:charge/processor sales-order)
+ (com/pill {:color :primary}
+ (name (:charge/processor sales-order)))))}
+ {:key "tip"
+ :name "Tip"
+ :sort-key "tip"
+ :render #(some->> % :charge/tip (format "$%.2f"))}
+ {:key "links"
+ :name "Links"
+ :render (fn [entity]
+ (when-let [expected-deposit-id (some->> entity :expected-deposit/_charges first :db/id)]
+ [:a {:href (str (bidi/path-for ssr-routes/only-routes
+ :pos-expected-deposits)
+ "?exact-match-id=" expected-deposit-id)
+ :hx-boost "true"}
+ (com/pill {:color :secondary} "expected deposit")]))}]}))
(def row* (partial helper/row* grid-page))
(def table* (partial helper/table* grid-page))
diff --git a/src/clj/auto_ap/ssr/search.clj b/src/clj/auto_ap/ssr/search.clj
index 92cd9e06..15fc181b 100644
--- a/src/clj/auto_ap/ssr/search.clj
+++ b/src/clj/auto_ap/ssr/search.clj
@@ -11,15 +11,15 @@
(defn try-cleanse-date [d]
(try
- (or
- (some-> (atime/parse-utc d atime/normal-date) (atime/unparse atime/solr-date))
- (some-> (atime/parse-utc d atime/iso-date) (atime/unparse atime/solr-date))
- d)
+ (or
+ (some-> (atime/parse-utc d atime/normal-date) (atime/unparse atime/solr-date))
+ (some-> (atime/parse-utc d atime/iso-date) (atime/unparse atime/solr-date))
+ d)
(catch Exception _
d)))
(defn try-parse-number [n]
- (if (re-find #"^[\-]?\d+\.\d+$" n )
+ (if (re-find #"^[\-]?\d+\.\d+$" n)
(str (with-precision 2
(some-> n
(Double/parseDouble)
@@ -28,7 +28,6 @@
(double))))
n))
-
(defn q->solr-q [q]
(let [matches (re-seq #"(?:\".*?\"|\S)+" q)]
(str/join " AND "
@@ -46,10 +45,8 @@
(= "journal-entry" m)
"type:journal-entry"
-
:else
- (str "_text_:\"" (try-parse-number (try-cleanse-date m)) ""\"))))))))
-
+ (str "_text_:\"" (try-parse-number (try-cleanse-date m)) "" \"))))))))
(defn search-results [q id]
(into []
@@ -58,96 +55,89 @@
(solr/query solr/impl "invoices" {"query" (q->solr-q q)
"fields" "id, date, amount, type, description, number, client_code, client_id, vendor_name"})))
-
(defn search-results* [q id]
- (let [results (search-results q id)]
- [:div
+ (let [results (search-results q id)]
+ [:div
(if (seq results)
[:div.flex.gap-8.flex-col
(for [doc results]
(com/card {}
- [:div.flex.flex-col.gap-4
- [:div.flex.items-center.p-2.gap-4.bg-gray-50.dark:bg-gray-800
- [:div.h-8.w-8.p-2
- (cond (= "transaction" (:type doc))
- svg/bank
-
+ [:div.flex.flex-col.gap-4
+ [:div.flex.items-center.p-2.gap-4.bg-gray-50.dark:bg-gray-800
+ [:div.h-8.w-8.p-2
+ (cond (= "transaction" (:type doc))
+ svg/bank
- (= "invoice" (:type doc))
- svg/accounting-invoice-mail
-
+ (= "invoice" (:type doc))
+ svg/accounting-invoice-mail
- (= "payment" (:type doc))
- svg/payments
+ (= "payment" (:type doc))
+ svg/payments
- (= "journal-entry" (:type doc))
- svg/receipt
+ (= "journal-entry" (:type doc))
+ svg/receipt
- :else
- nil)]
- (clojure.string/capitalize (:type doc))
- (com/pill {:color :primary}
- "client: " (:client_code doc))
- (com/pill {:color :secondary}
- "amount: $" (first (:amount doc)))
- (when-let [vendor-name (first (:vendor_name doc))]
- (com/pill {:color :yellow}
- "vendor: " vendor-name))
- [:div
- (com/link {:href (str "/" (cond (= "invoice"
- (:type doc))
- "invoices"
+ :else
+ nil)]
+ (clojure.string/capitalize (:type doc))
+ (com/pill {:color :primary}
+ "client: " (:client_code doc))
+ (com/pill {:color :secondary}
+ "amount: $" (first (:amount doc)))
+ (when-let [vendor-name (first (:vendor_name doc))]
+ (com/pill {:color :yellow}
+ "vendor: " vendor-name))
+ [:div
+ (com/link {:href (str "/" (cond (= "invoice"
+ (:type doc))
+ "invoices"
- (= "transaction"
- (:type doc))
- "transactions"
+ (= "transaction"
+ (:type doc))
+ "transactions"
- (= "journal-entry"
- (:type doc))
- "ledger"
+ (= "journal-entry"
+ (:type doc))
+ "ledger"
- :else
- "payments") "/?exact-match-id=" (:id doc))
- :target "_blank"}
- [:div.h-8.w-8.p-2
- svg/external-link])]
- ]
-
-
- [:div.px-4.pb-2
- [:span
-
- [:strong (atime/unparse (atime/parse (:date doc) atime/solr-date) atime/normal-date)]
- ": "
- (str (or (first (:description doc))
- (first (:number doc))))]]])
- )]
+ :else
+ "payments") "/?exact-match-id=" (:id doc))
+ :target "_blank"}
+ [:div.h-8.w-8.p-2
+ svg/external-link])]]
+
+ [:div.px-4.pb-2
+ [:span
+
+ [:strong (atime/unparse (atime/parse (:date doc) atime/solr-date) atime/normal-date)]
+ ": "
+ (str (or (first (:description doc))
+ (first (:number doc))))]]]))]
[:div.block "No results found."])]))
(defn dialog-contents [request]
(if-let [q (get (:form-params request) "q")]
(html-response (search-results* q (:identity request)))
(modal-response
- (com/modal {}
- (com/modal-card {:class "w-full h-full"}
- [:div.p-2 "Search"]
- [:div#search.overflow-auto.space-y-6.p-2.w-full
-
- (com/text-input {:id "search-input"
- :type "search"
- :placeholder "5/5/2034 Magheritas"
- :name "q"
- :hx-post "/search"
- :hx-trigger "keyup changed delay:300ms, search"
- :hx-target "#search-results"
- :hx-indicator "#search"
- :value (:q (:params request))
- :autofocus true})
- [:i.text-sm.text-gray-600.dark:text-gray-50 "Try dates, numbers, vendors. To filter to specific type, use 'invoice', 'transaction', 'journal-entry', 'payment'."]
- #_[:style
- ".htmx-request #search-results {display: none} .htmx-request .htmx-indicator { display: block !important; }"]
- [:div#search-results
- ]
- [:div.loader.is-loading.big.htmx-indicator ]]
- nil)))))
+ (com/modal {}
+ (com/modal-card {:class "w-full h-full"}
+ [:div.p-2 "Search"]
+ [:div#search.overflow-auto.space-y-6.p-2.w-full
+
+ (com/text-input {:id "search-input"
+ :type "search"
+ :placeholder "5/5/2034 Magheritas"
+ :name "q"
+ :hx-post "/search"
+ :hx-trigger "keyup changed delay:300ms, search"
+ :hx-target "#search-results"
+ :hx-indicator "#search"
+ :value (:q (:params request))
+ :autofocus true})
+ [:i.text-sm.text-gray-600.dark:text-gray-50 "Try dates, numbers, vendors. To filter to specific type, use 'invoice', 'transaction', 'journal-entry', 'payment'."]
+ #_[:style
+ ".htmx-request #search-results {display: none} .htmx-request .htmx-indicator { display: block !important; }"]
+ [:div#search-results]
+ [:div.loader.is-loading.big.htmx-indicator]]
+ nil)))))
diff --git a/src/clj/auto_ap/ssr/svg.clj b/src/clj/auto_ap/ssr/svg.clj
index c1f2e9bf..5a95d601 100644
--- a/src/clj/auto_ap/ssr/svg.clj
+++ b/src/clj/auto_ap/ssr/svg.clj
@@ -8,7 +8,6 @@
[:path {:d "M10.5,14.25v-9a9,9,0,1,0,5.561,16.077Z", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1.5px"}]
[:path {:d "M22.5,12.75h-9l5.561,7.077A8.986,8.986,0,0,0,22.5,12.75Z", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1.5px"}]])
-
(def accounting-invoice-mail
[:svg {:xmlns "http://www.w3.org/2000/svg", :viewbox "0 0 24 24"}
[:defs]
@@ -91,7 +90,6 @@
[:svg {:id "theme-toggle-light-icon", :fill "currentColor", :viewbox "0 0 20 20", :xmlns "http://www.w3.org/2000/svg"}
[:path {:d "M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z", :fill-rule "evenodd", :clip-rule "evenodd"}]])
-
(def home
[:svg {:fill "currentColor", :viewbox "0 0 20 20", :xmlns "http://www.w3.org/2000/svg"}
[:path {:d "M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z"}]])
@@ -112,7 +110,6 @@
[:svg {:xmlns "http://www.w3.org/2000/svg", :aria-hidden "true", :fill "none", :viewbox "0 0 24 24", :stroke-width "1.5", :stroke "currentColor"}
[:path {:stroke-linecap "round", :stroke-linejoin "round", :d "M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99"}]])
-
(def upload
[:svg {:xmlns "http://www.w3.org/2000/svg", :fill "none", :viewbox "0 0 24 24", :stroke-width "2", :stroke "currentColor", :aria-hidden "true"}
[:path {:stroke-linecap "round", :stroke-linejoin "round", :d "M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5m-13.5-9L12 3m0 0l4.5 4.5M12 3v13.5"}]])
@@ -315,7 +312,6 @@
[:line {:x1 "7", :y1 "7", :x2 "17", :y2 "17", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
[:line {:x1 "17", :y1 "7", :x2 "7", :y2 "17", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]])
-
(def filled-x
[:svg {:xmlns "http://www.w3.org/2000/svg", :viewbox "0 0 24 24"}
[:circle {:cx "12", :cy "12", :r "11.5", :fill "#FFF", :stroke-linecap "round", :stroke-linejoin "round"}]
@@ -384,7 +380,6 @@
[:circle {:cx "12", :cy "6.75", :r "5.5", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
[:path {:d "M7.261,3.958A9.124,9.124,0,0,0,13.833,6.75a9.138,9.138,0,0,0,3.617-.744", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]])
-
(def accounts
[:svg {:xmlns "http://www.w3.org/2000/svg", :viewbox "0 0 24 24"}
[:defs]
@@ -404,7 +399,6 @@
[:line {:x1 "15.504", :y1 "5.5", :x2 "15.504", :y2 "12.5", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
[:line {:x1 "15.504", :y1 "0.5", :x2 "15.504", :y2 "3.5", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]])
-
(def cog
[:svg {:xmlns "http://www.w3.org/2000/svg", :viewbox "0 0 24 24"}
[:defs]
@@ -432,7 +426,6 @@
[:path {:stroke "currentColor", :d "M20.458 13.742C20.3206 13.742 20.2092 13.6305 20.2092 13.4931C20.2092 13.3557 20.3206 13.2443 20.458 13.2443"}]
[:path {:stroke "currentColor", :d "M20.458 13.742C20.5955 13.742 20.7069 13.6305 20.7069 13.4931C20.7069 13.3557 20.5955 13.2443 20.458 13.2443"}]])
-
(def arrow-in
[:svg {:xmlns "http://www.w3.org/2000/svg", :viewbox "0 0 24 24"}
[:defs]
@@ -528,4 +521,4 @@
[:path {:d "M11 15a1 1 0 1 0 2 0 1 1 0 1 0 -2 0", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1"}]
[:path {:d "m12 16 0 3", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1"}]
[:path {:d "M4.5 9.5h15s1 0 1 1v12s0 1 -1 1h-15s-1 0 -1 -1v-12s0 -1 1 -1", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1"}]
- [:path {:d "M6.5 6a5.5 5.5 0 0 1 11 0v3.5h-11Z", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1"}]])
+ [:path {:d "M6.5 6a5.5 5.5 0 0 1 11 0v3.5h-11Z", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1"}]])
diff --git a/src/clj/auto_ap/ssr/transaction.clj b/src/clj/auto_ap/ssr/transaction.clj
index d21606dd..521a6f62 100644
--- a/src/clj/auto_ap/ssr/transaction.clj
+++ b/src/clj/auto_ap/ssr/transaction.clj
@@ -1,8 +1,8 @@
(ns auto-ap.ssr.transaction
(:require
[auto-ap.datomic
- :refer [audit-transact audit-transact-batch conn pull-attr
- pull-many]]
+ :refer [audit-transact audit-transact-batch conn pull-attr
+ pull-many]]
[auto-ap.logging :as alog]
[auto-ap.permissions :refer [wrap-must]]
[auto-ap.query-params :refer [wrap-copy-qp-pqp]]
@@ -20,10 +20,10 @@
wrap-status-from-source]]
[auto-ap.ssr.transaction.edit :as edit :refer [transaction-account-row*]]
[auto-ap.ssr.utils
- :refer [apply-middleware-to-all-handlers entity-id html-response
- many-entity modal-response percentage ref->enum-schema
- wrap-implied-route-param wrap-merge-prior-hx
- wrap-schema-enforce]]
+ :refer [apply-middleware-to-all-handlers entity-id html-response
+ many-entity modal-response percentage ref->enum-schema
+ wrap-implied-route-param wrap-merge-prior-hx
+ wrap-schema-enforce]]
[bidi.bidi :as bidi]
[clojure.string :as str]
[datomic.api :as dc]
@@ -39,8 +39,6 @@
(def page (helper/page-route grid-page))
-
-
(def table (helper/table-route grid-page))
(def csv (helper/csv-route grid-page))
@@ -60,29 +58,29 @@
selected)
all-ids (all-ids-not-locked ids)
db (dc/db conn)]
-
+
(alog/info ::bulk-delete-transactions
:count (count all-ids)
:sample (take 3 all-ids))
-
+
;; First retract journal entries and handle payment relationships
- (audit-transact
- (mapcat (fn [i]
- (let [transaction (dc/pull db [:transaction/payment
- :transaction/expected-deposit
- :db/id] i)
- payment-id (-> transaction :transaction/payment :db/id)
- expected-deposit-id (-> transaction :transaction/expected-deposit :db/id)]
- (cond->> [[:db/retractEntity [:journal-entry/original-entity i]]]
- payment-id (into [{:db/id payment-id
- :payment/status :payment-status/pending}
- [:db/retract (:db/id transaction) :transaction/payment payment-id]])
- expected-deposit-id (into [{:db/id expected-deposit-id
+ (audit-transact
+ (mapcat (fn [i]
+ (let [transaction (dc/pull db [:transaction/payment
+ :transaction/expected-deposit
+ :db/id] i)
+ payment-id (-> transaction :transaction/payment :db/id)
+ expected-deposit-id (-> transaction :transaction/expected-deposit :db/id)]
+ (cond->> [[:db/retractEntity [:journal-entry/original-entity i]]]
+ payment-id (into [{:db/id payment-id
+ :payment/status :payment-status/pending}
+ [:db/retract (:db/id transaction) :transaction/payment payment-id]])
+ expected-deposit-id (into [{:db/id expected-deposit-id
:expected-deposit/status :expected-deposit-status/pending}
[:db/retract (:db/id transaction) :transaction/expected-deposit expected-deposit-id]]))))
- all-ids)
- (:identity request))
-
+ all-ids)
+ (:identity request))
+
;; Then retract or suppress the transactions
(audit-transact
(mapcat (fn [i]
@@ -94,14 +92,12 @@
[:db/retractEntity [:journal-entry/original-entity i]]]))
all-ids)
(:identity request))
-
- (html-response
+
+ (html-response
(com/success-modal {:title "Transactions Updated"}
[:p (str "Successfully " (if suppress "suppressed" "deleted") " " (count all-ids) " transactions.")])
:headers {"hx-trigger" "invalidated"})))
-
-
(def key->handler
(merge edit/key->handler
bulk-code/key->handler
diff --git a/src/clj/auto_ap/ssr/transaction/bulk_code.clj b/src/clj/auto_ap/ssr/transaction/bulk_code.clj
index 5cc2f268..53d0e802 100644
--- a/src/clj/auto_ap/ssr/transaction/bulk_code.clj
+++ b/src/clj/auto_ap/ssr/transaction/bulk_code.clj
@@ -1,7 +1,7 @@
(ns auto-ap.ssr.transaction.bulk-code
(:require
[auto-ap.datomic
- :refer [audit-transact-batch conn pull-attr pull-many]]
+ :refer [audit-transact-batch conn pull-attr pull-many]]
[auto-ap.logging :as alog]
[auto-ap.permissions :refer [wrap-must]]
[auto-ap.query-params :refer [wrap-copy-qp-pqp]]
@@ -23,9 +23,9 @@
[auto-ap.ssr.transaction.edit :as edit :refer [account-typeahead*
location-select*]]
[auto-ap.ssr.utils
- :refer [apply-middleware-to-all-handlers entity-id
- form-validation-error html-response percentage
- ref->enum-schema wrap-merge-prior-hx wrap-schema-enforce]]
+ :refer [apply-middleware-to-all-handlers entity-id
+ form-validation-error html-response percentage
+ ref->enum-schema wrap-merge-prior-hx wrap-schema-enforce]]
[bidi.bidi :as bidi]
[datomic.api :as dc]
[iol-ion.query :refer [dollars=]]
@@ -34,52 +34,52 @@
(defn transaction-account-row* [{:keys [value client-id]}]
(com/data-grid-row
- (-> {:x-data (hx/json {:show (boolean (not (fc/field-value (:new? value))))
- :accountId (fc/field-value (:account value))})
+ (-> {:x-data (hx/json {:show (boolean (not (fc/field-value (:new? value))))
+ :accountId (fc/field-value (:account value))})
:data-key "show"
- :x-ref "p"}
+ :x-ref "p"}
hx/alpine-mount-then-appear)
(fc/with-field :db/id
- (com/hidden {:name (fc/field-name)
+ (com/hidden {:name (fc/field-name)
:value (fc/field-value)}))
(fc/with-field :account
(com/data-grid-cell
{}
(com/validated-field
{:errors (fc/field-errors)}
- (account-typeahead* {:value (fc/field-value)
+ (account-typeahead* {:value (fc/field-value)
:client-id client-id
- :name (fc/field-name)
- :x-model "accountId"}))))
+ :name (fc/field-name)
+ :x-model "accountId"}))))
(fc/with-field :location
(com/data-grid-cell
{}
(com/validated-field
{:errors (fc/field-errors)
:x-hx-val:account-id "accountId"
- :hx-vals (hx/json (cond-> {:name (fc/field-name) }
+ :hx-vals (hx/json (cond-> {:name (fc/field-name)}
client-id (assoc :client-id client-id)))
:x-dispatch:changed "accountId"
:hx-trigger "changed"
- :hx-get (bidi/path-for ssr-routes/only-routes ::route/location-select)
+ :hx-get (bidi/path-for ssr-routes/only-routes ::route/location-select)
:hx-target "find *"
:hx-swap "outerHTML"}
- (location-select* {:name (fc/field-name)
- :account-location (:account/location (cond->> (:account @value)
- (nat-int? (:account @value)) (dc/pull (dc/db conn)
- '[:account/location])))
+ (location-select* {:name (fc/field-name)
+ :account-location (let [account-id (:account @value)]
+ (when (nat-int? account-id)
+ (:account/location (dc/pull (dc/db conn) '[:account/location] account-id))))
:client-locations (pull-attr (dc/db conn) :client/locations client-id)
- :value (fc/field-value)}))))
+ :value (fc/field-value)}))))
(fc/with-field :percentage
(com/data-grid-cell
{}
(com/validated-field
{:errors (fc/field-errors)}
- (com/money-input {:name (fc/field-name)
+ (com/money-input {:name (fc/field-name)
:class "w-16"
:value (some-> (fc/field-value)
- (* 100)
- (long))}))))
+ (* 100)
+ (long))}))))
(com/data-grid-cell {:class "align-top"}
(com/a-icon-button {"@click.prevent.stop" "show=false; setTimeout(() => $refs.p.remove(), 500)"} svg/x))))
@@ -90,64 +90,58 @@
{:search-params (:query-params request)
:accounts []}))
-
(defn all-ids-not-locked
"Filters transaction IDs to only those that aren't locked (client locked date earlier than transaction date)"
[all-ids]
(->> all-ids
(dc/q '[:find ?t
- :in $ [?t ...]
- :where
- [?t :transaction/client ?c]
- [(get-else $ ?c :client/locked-until #inst "2000-01-01") ?lu]
- [?t :transaction/date ?d]
- [(>= ?d ?lu)]]
- (dc/db conn))
+ :in $ [?t ...]
+ :where
+ [?t :transaction/client ?c]
+ [(get-else $ ?c :client/locked-until #inst "2000-01-01") ?lu]
+ [?t :transaction/date ?d]
+ [(>= ?d ?lu)]]
+ (dc/db conn))
(map first)))
-
-
-
-
-(def bulk-code-schema
+(def bulk-code-schema
(mc/schema [:map
[:vendor {:optional true} [:maybe entity-id]]
- [:approval-status {:optional true} [:maybe (ref->enum-schema "transaction-approval-status")] ]
+ [:approval-status {:optional true} [:maybe (ref->enum-schema "transaction-approval-status")]]
[:accounts {:optional true}
- [:maybe
+ [:maybe
[:vector {:coerce? true}
[:map [:account entity-id]
[:location [:string {:min 1 :error/message "required"}]]
- [:percentage percentage]]]] ]]))
-
+ [:percentage percentage]]]]]]))
(defn maybe-code-accounts [transaction account-rules valid-locations]
(with-precision 2
(let [accounts (vec (mapcat
- (fn [ar]
- (let [cents-to-distribute (int (Math/round (Math/abs (* (:percentage ar)
- (:transaction/amount transaction)
- 100))))]
- (if (= "Shared" (:location ar))
- (->> valid-locations
- (map
- (fn [cents location]
- {:db/id (random-tempid)
- :transaction-account/account (:account ar)
- :transaction-account/amount (* 0.01 cents)
- :transaction-account/location location})
- (rm/spread-cents cents-to-distribute (count valid-locations))))
- [(cond-> {:db/id (random-tempid)
- :transaction-account/account (:account ar)
- :transaction-account/amount (* 0.01 cents-to-distribute)}
- (:location ar) (assoc :transaction-account/location (:location ar)))])))
- account-rules))
+ (fn [ar]
+ (let [cents-to-distribute (int (Math/round (Math/abs (* (:percentage ar)
+ (:transaction/amount transaction)
+ 100))))]
+ (if (= "Shared" (:location ar))
+ (->> valid-locations
+ (map
+ (fn [cents location]
+ {:db/id (random-tempid)
+ :transaction-account/account (:account ar)
+ :transaction-account/amount (* 0.01 cents)
+ :transaction-account/location location})
+ (rm/spread-cents cents-to-distribute (count valid-locations))))
+ [(cond-> {:db/id (random-tempid)
+ :transaction-account/account (:account ar)
+ :transaction-account/amount (* 0.01 cents-to-distribute)}
+ (:location ar) (assoc :transaction-account/location (:location ar)))])))
+ account-rules))
accounts (mapv
- (fn [a]
- (update a :transaction-account/amount
- #(with-precision 2
- (double (.setScale (bigdec %) 2 java.math.RoundingMode/HALF_UP)))))
- accounts)
+ (fn [a]
+ (update a :transaction-account/amount
+ #(with-precision 2
+ (double (.setScale (bigdec %) 2 java.math.RoundingMode/HALF_UP)))))
+ accounts)
leftover (with-precision 2 (.round (bigdec (- (Math/abs (:transaction/amount transaction))
(Math/abs (reduce + 0.0 (map #(:transaction-account/amount %) accounts)))))
*math-context*))
@@ -167,74 +161,76 @@
[])
(step-schema [_]
- (mm/form-schema linear-wizard))
+ (mm/form-schema linear-wizard))
(render-step [this {{:keys [snapshot] :as multi-form-state} :multi-form-state :as request}]
- (let [_ (alog/peek ::SEARCH_PARAMS (:search-params snapshot))
- selected-ids (selected->ids (assoc request :query-params (:search-params snapshot)) (:search-params snapshot))
- all-ids (all-ids-not-locked selected-ids)]
- (mm/default-render-step
- linear-wizard this
- :head [:div.p-2 "Bulk editing " (count all-ids) " transactions"]
- :body (mm/default-step-body
- {}
- [:div
- #_(com/hidden {:name "ids" :value (pr-str ids)})
-
- [:div.space-y-4.p-4
- [:div.grid.grid-cols-2.gap-4
-
- ;; Vendor field
- [:div
- (fc/with-field :vendor
- (com/validated-field {:label "Vendor"
- :errors (fc/field-errors)}
- (com/typeahead {:name (fc/field-name)
- :placeholder "Search for vendor..."
- :url (bidi/path-for ssr-routes/only-routes :vendor-search)
- :content-fn (fn [c] (pull-attr (dc/db conn) :vendor/name c))})))]
-
- ;; Status field
- [:div
- (fc/with-field :approval-status
- (com/validated-field {:label "Status"
- :errors (fc/field-errors)}
- (com/select {:name (fc/field-name)
- :options [["" "No Change"]
- ["approved" "Approved"]
- ["unapproved" "Unapproved"]
- ["suppressed" "Suppressed"]
- ["requires_feedback" "Requires Feedback"]]})))]
-
- ;; Accounts section
- [:div.col-span-2.pt-4
- [:h3.text-lg.font-medium.mb-3 "Expense Accounts"]
-
- [:div#account-entries.space-y-3
- (fc/with-field :accounts
- (com/validated-field
- {:errors (fc/field-errors)}
- (com/data-grid {:headers [(com/data-grid-header {} "Account")
- (com/data-grid-header {:class "w-32"} "Location")
- (com/data-grid-header {:class "w-16"} "$")
- (com/data-grid-header {:class "w-16"})]}
- (fc/cursor-map #(transaction-account-row* {:value %}))
-
- (com/data-grid-new-row {:colspan 4
- :hx-get (bidi/path-for ssr-routes/only-routes
- ::route/bulk-code-new-account)
- :row-offset 0
- :index (count (fc/field-value))}
- "New account")
- )))
-
- ;; Button to add more accounts
- ]]]]])
- :footer
- (mm/default-step-footer linear-wizard this :validation-route ::route/new-wizard-navigate
- :next-button (com/button {:color :primary :x-ref "next" :class "w-32"} "Save"))
- :validation-route ::route/new-wizard-navigate))))
+ (let [_ (alog/peek ::SEARCH_PARAMS (:search-params snapshot))
+ selected-ids (selected->ids (assoc request :query-params (:search-params snapshot)) (:search-params snapshot))
+ all-ids (all-ids-not-locked selected-ids)]
+ (mm/default-render-step
+ linear-wizard this
+ :head [:div.p-2 "Bulk editing " (count all-ids) " transactions"]
+ :body (mm/default-step-body
+ {}
+ [:div
+ #_(com/hidden {:name "ids" :value (pr-str ids)})
+ [:div.space-y-4.p-4
+ [:div.grid.grid-cols-2.gap-4
+
+ ;; Vendor field
+ [:div {:hx-trigger "change"
+ :hx-post (bidi/path-for ssr-routes/only-routes ::route/bulk-code-vendor-changed)
+ :hx-target "#account-entries"
+ :hx-swap "innerHTML"
+ :hx-include "closest form"}
+ (fc/with-field :vendor
+ (com/validated-field {:label "Vendor"
+ :errors (fc/field-errors)}
+ (com/typeahead {:name (fc/field-name)
+ :placeholder "Search for vendor..."
+ :url (bidi/path-for ssr-routes/only-routes :vendor-search)
+ :content-fn (fn [c] (pull-attr (dc/db conn) :vendor/name c))})))]
+
+ ;; Status field
+ [:div
+ (fc/with-field :approval-status
+ (com/validated-field {:label "Status"
+ :errors (fc/field-errors)}
+ (com/select {:name (fc/field-name)
+ :options [["" "No Change"]
+ ["approved" "Approved"]
+ ["unapproved" "Unapproved"]
+ ["suppressed" "Suppressed"]
+ ["requires_feedback" "Requires Feedback"]]})))]
+
+ ;; Accounts section
+ [:div.col-span-2.pt-4
+ [:h3.text-lg.font-medium.mb-3 "Expense Accounts"]
+
+ [:div#account-entries.space-y-3
+ (fc/with-field :accounts
+ (com/validated-field
+ {:errors (fc/field-errors)}
+ (com/data-grid {:headers [(com/data-grid-header {} "Account")
+ (com/data-grid-header {:class "w-32"} "Location")
+ (com/data-grid-header {:class "w-16"} "$")
+ (com/data-grid-header {:class "w-16"})]}
+ (fc/cursor-map #(transaction-account-row* {:value %}))
+
+ (com/data-grid-new-row {:colspan 4
+ :hx-get (bidi/path-for ssr-routes/only-routes
+ ::route/bulk-code-new-account)
+ :row-offset 0
+ :index (count (fc/field-value))}
+ "New account"))))]]]]])
+
+;; Button to add more accounts
+
+ :footer
+ (mm/default-step-footer linear-wizard this :validation-route ::route/new-wizard-navigate
+ :next-button (com/button {:color :primary :x-ref "next" :class "w-32"} "Save"))
+ :validation-route ::route/new-wizard-navigate))))
(defn assert-percentages-add-up [{:keys [accounts]}]
(let [account-total (reduce + 0 (map (fn [x] (:percentage x)) accounts))]
@@ -263,78 +259,128 @@
(steps [_]
[:accounts])
(get-step [this step-key]
- (let [step-key-result (mc/parse mm/step-key-schema step-key)
+ (let [step-key-result (mc/parse mm/step-key-schema step-key)
[step-key-type step-key] step-key-result]
(get {:accounts (->AccountsStep this)}
step-key)))
(form-schema [_]
bulk-code-schema)
(submit [this {:keys [multi-form-state request-method identity] :as request}]
- (let [ ids (selected->ids (assoc request :query-params (:search-params (:snapshot multi-form-state))) (:search-params (:snapshot multi-form-state)))
- all-ids (all-ids-not-locked ids)
- vendor (-> request :multi-form-state :snapshot :vendor)
- approval-status (-> request :multi-form-state :snapshot :approval-status)
- accounts (-> request :multi-form-state :snapshot :accounts) ]
- (when (seq accounts)
- (assert-percentages-add-up (:snapshot multi-form-state)))
- (alog/peek ::ACCOUNTS (-> request :multi-form-state :snapshot))
-
+ (let [ids (selected->ids (assoc request :query-params (:search-params (:snapshot multi-form-state))) (:search-params (:snapshot multi-form-state)))
+ all-ids (all-ids-not-locked ids)
+ vendor (-> request :multi-form-state :snapshot :vendor)
+ approval-status (-> request :multi-form-state :snapshot :approval-status)
+ accounts (-> request :multi-form-state :snapshot :accounts)]
+ (when (seq accounts)
+ (assert-percentages-add-up (:snapshot multi-form-state)))
+ (alog/peek ::ACCOUNTS (-> request :multi-form-state :snapshot))
+
;; Get transactions and filter for locked ones
- (let [db (dc/db conn)
- transactions (pull-many db [:db/id :transaction/amount {:transaction/client [:db/id]}] (vec all-ids))
+ (let [db (dc/db conn)
+ transactions (pull-many db [:db/id :transaction/amount {:transaction/client [:db/id]}] (vec all-ids))
;; Get client locations
- client->locations (->> (map (comp :db/id :transaction/client) transactions)
- (distinct)
- (dc/q '[:find (pull ?e [:db/id :client/locations])
- :in $ [?e ...]]
- db)
- (map (fn [[client]]
- [(:db/id client) (:client/locations client)]))
- (into {}))]
+ client->locations (->> (map (comp :db/id :transaction/client) transactions)
+ (distinct)
+ (dc/q '[:find (pull ?e [:db/id :client/locations])
+ :in $ [?e ...]]
+ db)
+ (map (fn [[client]]
+ [(:db/id client) (:client/locations client)]))
+ (into {}))]
;; Validate account locations
- (doseq [a accounts
- :let [{:keys [:account/location :account/name]} (dc/pull db
- [:account/location :account/name]
- (:account a))]]
- (when (and location (not= location (:location a)))
- (form-validation-error (str "Account " name " uses location " (:location a) ", but is supposed to be " location)))
- (doseq [[_ locations] client->locations]
- (when (and (not location)
- (not (get (into #{"Shared"} locations)
- (:location a))))
- (form-validation-error (str "Account " name " uses location " (:location a) ", but doesn't belong to the client.")))))
+ (doseq [a accounts
+ :let [{:keys [:account/location :account/name]} (dc/pull db
+ [:account/location :account/name]
+ (:account a))]]
+ (when (and location (not= location (:location a)))
+ (form-validation-error (str "Account " name " uses location " (:location a) ", but is supposed to be " location)))
+ (doseq [[_ locations] client->locations]
+ (when (and (not location)
+ (not (get (into #{"Shared"} locations)
+ (:location a))))
+ (form-validation-error (str "Account " name " uses location " (:location a) ", but doesn't belong to the client.")))))
- (audit-transact-batch
- (map (fn [t]
- (let [locations (client->locations (-> t :transaction/client :db/id))]
- [:upsert-transaction (cond-> t
- approval-status
- (assoc :transaction/approval-status approval-status)
+ (audit-transact-batch
+ (map (fn [t]
+ (let [locations (client->locations (-> t :transaction/client :db/id))]
+ [:upsert-transaction (cond-> t
+ approval-status
+ (assoc :transaction/approval-status approval-status)
- vendor
- (assoc :transaction/vendor vendor)
+ vendor
+ (assoc :transaction/vendor vendor)
- (seq accounts)
- (assoc :transaction/accounts
- (maybe-code-accounts t accounts locations)))]))
- transactions)
- (:identity request))
+ (seq accounts)
+ (assoc :transaction/accounts
+ (maybe-code-accounts t accounts locations)))]))
+ transactions)
+ (:identity request))
;; Return success modal
- (html-response
- (com/success-modal {:title "Transactions Coded"}
- [:p (str "Successfully coded " (count all-ids) " transactions.")])
- :headers {"hx-trigger" "refreshTable"})))))
+ (html-response
+ (com/success-modal {:title "Transactions Coded"}
+ [:p (str "Successfully coded " (count all-ids) " transactions.")])
+ :headers {"hx-trigger" "refreshTable"})))))
+
+(defn- get-client-id [request]
+ (-> request :clients first :db/id))
+
+(defn- vendor-default-account [vendor-id client-id]
+ (when vendor-id
+ (let [vendor (edit/get-vendor vendor-id)
+ clientized (edit/clientize-vendor vendor client-id)]
+ (:vendor/default-account clientized))))
+
+(defn- build-default-account-row [account]
+ {:db/id (str (java.util.UUID/randomUUID))
+ :account (:db/id account)
+ :location (or (:account/location account) "Shared")
+ :percentage 1.0})
+
+(defn- render-accounts-section [request]
+ (let [step-params (:step-params (:multi-form-state request))]
+ (html-response
+ [:div
+ (fc/start-form step-params
+ (when (:form-errors request) {:step-params (:form-errors request)})
+ (fc/with-field :accounts
+ (com/validated-field
+ {:errors (fc/field-errors)}
+ (com/data-grid {:headers [(com/data-grid-header {} "Account")
+ (com/data-grid-header {:class "w-32"} "Location")
+ (com/data-grid-header {:class "w-16"} "$")
+ (com/data-grid-header {:class "w-16"})]}
+ (fc/cursor-map #(transaction-account-row* {:value %}))
+ (com/data-grid-new-row {:colspan 4
+ :hx-get (bidi/path-for ssr-routes/only-routes
+ ::route/bulk-code-new-account)
+ :row-offset 0
+ :index (count (fc/field-value))}
+ "New account")))))])))
+
+(defn vendor-changed-handler [request]
+ (let [snapshot (:snapshot (:multi-form-state request))
+ step-params (:step-params (:multi-form-state request))
+ client-id (get-client-id request)
+ vendor-id (or (:vendor step-params) (:vendor snapshot))
+ updated-step-params (if (and (empty? (:accounts step-params))
+ vendor-id
+ client-id)
+ (if-let [default-account (vendor-default-account vendor-id client-id)]
+ (assoc step-params :accounts [(build-default-account-row default-account)])
+ step-params)
+ step-params)]
+ (render-accounts-section (assoc-in request [:multi-form-state :step-params] updated-step-params))))
(def bulk-code-wizard (->BulkCodeWizard nil nil))
(def key->handler
(apply-middleware-to-all-handlers
- {::route/bulk-code (-> mm/open-wizard-handler
- (mm/wrap-wizard bulk-code-wizard)
- (mm/wrap-init-multi-form-state initial-bulk-edit-state))
+ {::route/bulk-code (-> mm/open-wizard-handler
+ (mm/wrap-wizard bulk-code-wizard)
+ (mm/wrap-init-multi-form-state initial-bulk-edit-state))
::route/bulk-code-new-account (->
(add-new-entity-handler [:step-params :accounts]
(fn render [cursor request]
@@ -345,9 +391,12 @@
(wrap-schema-enforce :query-schema [:map
[:client-id {:optional true}
[:maybe entity-id]]]))
- ::route/bulk-code-submit (-> mm/submit-handler
- (wrap-wizard bulk-code-wizard)
- (mm/wrap-decode-multi-form-state))}
+ ::route/bulk-code-vendor-changed (-> vendor-changed-handler
+ (mm/wrap-wizard bulk-code-wizard)
+ (mm/wrap-decode-multi-form-state))
+ ::route/bulk-code-submit (-> mm/submit-handler
+ (wrap-wizard bulk-code-wizard)
+ (mm/wrap-decode-multi-form-state))}
(fn [h]
(-> h
(wrap-copy-qp-pqp)
diff --git a/src/clj/auto_ap/ssr/transaction/common.clj b/src/clj/auto_ap/ssr/transaction/common.clj
index 5e303410..ae066f46 100644
--- a/src/clj/auto_ap/ssr/transaction/common.clj
+++ b/src/clj/auto_ap/ssr/transaction/common.clj
@@ -1,9 +1,9 @@
-(ns auto-ap.ssr.transaction.common
+(ns auto-ap.ssr.transaction.common
(:require
- [auto-ap.datomic :refer [add-sorter-fields apply-pagination apply-sort-4
- conn merge-query observable-query pull-many]]
- [auto-ap.datomic.accounts :as d-accounts]
- [auto-ap.graphql.utils :refer [extract-client-ids is-admin?]]
+ [auto-ap.datomic :refer [add-sorter-fields apply-pagination apply-sort-4
+ conn merge-query observable-query pull-many]]
+ [auto-ap.datomic.accounts :as d-accounts]
+ [auto-ap.graphql.utils :refer [extract-client-ids is-admin?]]
[auto-ap.routes.invoice :as invoice-routes]
[auto-ap.routes.ledger :as ledger-routes]
[auto-ap.routes.payments :as payment-routes]
@@ -33,26 +33,26 @@
[:amount-gte {:optional true} [:maybe :double]]
[:amount-lte {:optional true} [:maybe :double]]
[:client-id {:optional true} [:maybe entity-id]]
- [:import-batch-id {:optional true} [:maybe entity-id]]
- [:unresolved {:optional true}
- [:maybe [:boolean {:decode/string {:enter #(cond (= % "on") true
- (= % "") false
- :else
- (boolean %))}}]]]
- [:description {:optional true} [:maybe [:string {:decode/string strip}]]]
- [:vendor {:optional true :default nil} [:maybe [:entity-map {:pull [:db/id :vendor/name]}]]]
- [:bank-account {:optional true :default nil} [:maybe [:entity-map {:pull [:db/id :bank-account/numeric-code]}]]]
- [:account {:optional true :default nil} [:maybe [:entity-map {:pull [:db/id :account/name]}]]]
- [:linked-to {:optional true}
- [:maybe [:enum {:decode/string {:enter #(if (seq %) % nil)}}
- "payment" "expected-deposit" "invoice" "none"]]]
- [:location {:optional true} [:maybe [:string {:decode/string strip}]]]
- [:potential-duplicates {:optional true}
- [:maybe [:boolean {:decode/string {:enter #(cond (= % "on") true
- (= % "") false
- :else
- (boolean %))}}]]]
- #_[:status {:optional true} [:maybe (ref->enum-schema "transaction-status")]]
+ [:import-batch-id {:optional true} [:maybe entity-id]]
+ [:unresolved {:optional true}
+ [:maybe [:boolean {:decode/string {:enter #(cond (= % "on") true
+ (= % "") false
+ :else
+ (boolean %))}}]]]
+ [:description {:optional true} [:maybe [:string {:decode/string strip}]]]
+ [:vendor {:optional true :default nil} [:maybe [:entity-map {:pull [:db/id :vendor/name]}]]]
+ [:bank-account {:optional true :default nil} [:maybe [:entity-map {:pull [:db/id :bank-account/numeric-code]}]]]
+ [:account {:optional true :default nil} [:maybe [:entity-map {:pull [:db/id :account/name]}]]]
+ [:linked-to {:optional true}
+ [:maybe [:enum {:decode/string {:enter #(if (seq %) % nil)}}
+ "payment" "expected-deposit" "invoice" "none"]]]
+ [:location {:optional true} [:maybe [:string {:decode/string strip}]]]
+ [:potential-duplicates {:optional true}
+ [:maybe [:boolean {:decode/string {:enter #(cond (= % "on") true
+ (= % "") false
+ :else
+ (boolean %))}}]]]
+ #_[:status {:optional true} [:maybe (ref->enum-schema "transaction-status")]]
[:exact-match-id {:optional true} [:maybe entity-id]]
[:all-selected {:optional true :default nil} [:maybe :boolean]]
[:selected {:optional true :default nil} [:maybe [:vector {:coerce? true}
@@ -66,14 +66,14 @@
'[:transaction/amount
:transaction/description-original
:transaction/description-simple
- [ :transaction/date :xform clj-time.coerce/from-date]
- [ :transaction/post-date :xform clj-time.coerce/from-date]
+ [:transaction/date :xform clj-time.coerce/from-date]
+ [:transaction/post-date :xform clj-time.coerce/from-date]
:transaction/type
:transaction/status
:transaction/client-overrides
:db/id
{:transaction/vendor [:vendor/name :db/id]
- :transaction/client [:client/name :client/code :db/id [ :client/locked-until :xform clj-time.coerce/from-date]]
+ :transaction/client [:client/name :client/code :db/id [:client/locked-until :xform clj-time.coerce/from-date]]
:transaction/bank-account [:bank-account/numeric-code :bank-account/name]
:transaction/accounts [{:transaction-account/account [:account/name :db/id]}
:transaction-account/location
@@ -104,13 +104,13 @@
[all-ids]
(->> all-ids
(dc/q '[:find ?t
- :in $ [?t ...]
- :where
- [?t :transaction/client ?c]
- [(get-else $ ?c :client/locked-until #inst "2000-01-01") ?lu]
- [?t :transaction/date ?d]
- [(>= ?d ?lu)]]
- (dc/db conn))
+ :in $ [?t ...]
+ :where
+ [?t :transaction/client ?c]
+ [(get-else $ ?c :client/locked-until #inst "2000-01-01") ?lu]
+ [?t :transaction/date ?d]
+ [(>= ?d ?lu)]]
+ (dc/db conn))
(map first)))
(defn fetch-ids [db {:keys [query-params route-params] :as request}]
@@ -161,75 +161,75 @@
:where ['[?e :transaction/bank-account ?ba]]}
:args [(:db/id (:bank-account args))]})
- (:vendor args)
- (merge-query {:query {:in ['?vendor-id]
- :where ['[?e :transaction/vendor ?vendor-id]]}
- :args [(:db/id (:vendor args))]})
+ (:vendor args)
+ (merge-query {:query {:in ['?vendor-id]
+ :where ['[?e :transaction/vendor ?vendor-id]]}
+ :args [(:db/id (:vendor args))]})
- (:db/id (:account args))
- (merge-query {:query {:in ['?account-id]
- :where ['[?e :transaction/accounts ?tas]
- '[?tas :transaction-account/account ?account-id]]}
- :args [(:db/id (:account args))]})
- (:import-batch-id args)
- (merge-query {:query {:in ['?import-batch-id]
- :where ['[?import-batch-id :import-batch/entry ?e]]}
- :args [(:import-batch-id args)]})
+ (:db/id (:account args))
+ (merge-query {:query {:in ['?account-id]
+ :where ['[?e :transaction/accounts ?tas]
+ '[?tas :transaction-account/account ?account-id]]}
+ :args [(:db/id (:account args))]})
+ (:import-batch-id args)
+ (merge-query {:query {:in ['?import-batch-id]
+ :where ['[?import-batch-id :import-batch/entry ?e]]}
+ :args [(:import-batch-id args)]})
- (:unresolved args)
- (merge-query {:query {:where ['[?e :transaction/date]
- '(or-join [?e]
- (not [?e :transaction/accounts])
- (and [?e :transaction/accounts ?tas]
- (not [?tas :transaction-account/account]))) ]}})
+ (:unresolved args)
+ (merge-query {:query {:where ['[?e :transaction/date]
+ '(or-join [?e]
+ (not [?e :transaction/accounts])
+ (and [?e :transaction/accounts ?tas]
+ (not [?tas :transaction-account/account])))]}})
- (seq (:location args))
- (merge-query {:query {:in ['?location]
- :where ['[?e :transaction/accounts ?tas]
- '[?tas :transaction-account/location ?location]]}
- :args [(:location args)]})
+ (seq (:location args))
+ (merge-query {:query {:in ['?location]
+ :where ['[?e :transaction/accounts ?tas]
+ '[?tas :transaction-account/location ?location]]}
+ :args [(:location args)]})
- (= (:linked-to args) "payment")
- (merge-query {:query {:where ['[?e :transaction/payment]]}})
+ (= (:linked-to args) "payment")
+ (merge-query {:query {:where ['[?e :transaction/payment]]}})
- (= (:linked-to args) "expected-deposit")
- (merge-query {:query {:where ['[?e :transaction/expected-deposit]]}})
+ (= (:linked-to args) "expected-deposit")
+ (merge-query {:query {:where ['[?e :transaction/expected-deposit]]}})
- (= (:linked-to args) "invoice")
- (merge-query {:query {:where ['[?e :transaction/payment ?p]
- '[_ :invoice-payment/payment ?p]]}})
+ (= (:linked-to args) "invoice")
+ (merge-query {:query {:where ['[?e :transaction/payment ?p]
+ '[_ :invoice-payment/payment ?p]]}})
- (= (:linked-to args) "none")
- (merge-query {:query {:where ['(not [?e :transaction/payment])
- '(not [?e :transaction/expected-deposit])]}})
+ (= (:linked-to args) "none")
+ (merge-query {:query {:where ['(not [?e :transaction/payment])
+ '(not [?e :transaction/expected-deposit])]}})
- (:potential-duplicates args)
- (merge-query (let [bank-account-id (:db/id (:bank-account args))
- _ (when-not bank-account-id
- (throw (ex-info "In order to select potential duplicates, you must choose a bank account."
- {:validation-error "In order to select potential duplicates, you must choose a bank account."})))
- duplicate-ids (->> (dc/q '[:find ?tx ?amount ?date
- :in $ ?ba
- :where
- [?tx :transaction/bank-account ?ba]
- [?tx :transaction/amount ?amount]
- [?tx :transaction/date ?date]
- (not [?tx :transaction/approval-status :transaction-approval-status/suppressed])]
- db
- bank-account-id)
- (group-by (fn [[_ amount date]]
- [amount date]))
- (filter (fn [[_ txes]]
- (> (count txes) 1)))
- (vals)
- (mapcat identity)
- (map first)
- set)]
- {:query {:in '[[?e ...]]
- :where []}
- :args [duplicate-ids]}))
+ (:potential-duplicates args)
+ (merge-query (let [bank-account-id (:db/id (:bank-account args))
+ _ (when-not bank-account-id
+ (throw (ex-info "In order to select potential duplicates, you must choose a bank account."
+ {:validation-error "In order to select potential duplicates, you must choose a bank account."})))
+ duplicate-ids (->> (dc/q '[:find ?tx ?amount ?date
+ :in $ ?ba
+ :where
+ [?tx :transaction/bank-account ?ba]
+ [?tx :transaction/amount ?amount]
+ [?tx :transaction/date ?date]
+ (not [?tx :transaction/approval-status :transaction-approval-status/suppressed])]
+ db
+ bank-account-id)
+ (group-by (fn [[_ amount date]]
+ [amount date]))
+ (filter (fn [[_ txes]]
+ (> (count txes) 1)))
+ (vals)
+ (mapcat identity)
+ (map first)
+ set)]
+ {:query {:in '[[?e ...]]
+ :where []}
+ :args [duplicate-ids]}))
- (:status route-params)
+ (:status route-params)
(merge-query {:query {:in ['?status]
:where ['[?e :transaction/approval-status ?status]]}
:args [(:status route-params)]})
@@ -253,8 +253,8 @@
(->> (observable-query query)
(apply-sort-4 (assoc query-params :default-asc? true))
(apply-pagination query-params))))
-
- (defn fetch-page [request]
+
+(defn fetch-page [request]
(let [db (dc/db conn)
{ids-to-retrieve :ids matching-count :count
all-ids :all-ids} (fetch-ids db request)]
@@ -263,8 +263,6 @@
matching-count
(sum-amount all-ids)]))
-
-
(defn exact-match-id* [request]
(if (nat-int? (:exact-match-id (:query-params request)))
[:div {:x-data (hx/json {:exact_match (:exact-match-id (:query-params request))}) :id "exact-match-id-tag"}
@@ -290,8 +288,6 @@
(com/link {"@click" "import_batch_id=null; $nextTick(() => $dispatch('change'))"}
svg/x)]])]))
-
-
(defn bank-account-filter* [request]
[:div {:hx-trigger "clientSelected from:body"
:hx-get (bidi.bidi/path-for ssr-routes/only-routes ::route/bank-account-filter)
@@ -313,7 +309,6 @@
{:value (:db/id ba)
:content (:bank-account/name ba)}))}))))])
-
(defn filters [request]
[:form#transaction-filters {"hx-trigger" "change delay:500ms, keyup changed from:.hot-filter delay:1000ms"
"hx-get" (bidi/path-for ssr-routes/only-routes
@@ -324,94 +319,93 @@
(com/hidden {:name "status"
:value (some-> (:status (:query-params request)) name)})
[:fieldset.space-y-6
- (com/field {:label "Vendor"}
- (com/typeahead {:name "vendor"
- :id "vendor"
- :url (bidi/path-for ssr-routes/only-routes :vendor-search)
- :value (:vendor (:query-params request))
- :value-fn :db/id
- :content-fn :vendor/name}))
- (com/field {:label "Financial Account"}
- (com/typeahead {:name "account"
- :id "account"
- :url (bidi/path-for ssr-routes/only-routes :account-search)
- :value (:account (:query-params request))
- :value-fn :db/id
- :content-fn #(:account/name (d-accounts/clientize (dc/pull (dc/db conn) d-accounts/default-read (:db/id %))
- (:db/id (:client request))))}))
- (bank-account-filter* request)
+ (com/field {:label "Vendor"}
+ (com/typeahead {:name "vendor"
+ :id "vendor"
+ :url (bidi/path-for ssr-routes/only-routes :vendor-search)
+ :value (:vendor (:query-params request))
+ :value-fn :db/id
+ :content-fn :vendor/name}))
+ (com/field {:label "Financial Account"}
+ (com/typeahead {:name "account"
+ :id "account"
+ :url (bidi/path-for ssr-routes/only-routes :account-search)
+ :value (:account (:query-params request))
+ :value-fn :db/id
+ :content-fn #(:account/name (d-accounts/clientize (dc/pull (dc/db conn) d-accounts/default-read (:db/id %))
+ (:db/id (:client request))))}))
+ (bank-account-filter* request)
(date-range-field* request)
- (com/field {:label "Description"}
- (com/text-input {:name "description"
- :id "description"
- :class "hot-filter"
- :value (:description (:query-params request))
- :placeholder "e.g., Groceries"
- :size :small}))
+ (com/field {:label "Description"}
+ (com/text-input {:name "description"
+ :id "description"
+ :class "hot-filter"
+ :value (:description (:query-params request))
+ :placeholder "e.g., Groceries"
+ :size :small}))
- (com/field {:label "Location"}
- (com/text-input {:name "location"
- :id "location"
- :class "hot-filter"
- :value (:location (:query-params request))
- :placeholder "SC"
- :size :small}))
+ (com/field {:label "Location"}
+ (com/text-input {:name "location"
+ :id "location"
+ :class "hot-filter"
+ :value (:location (:query-params request))
+ :placeholder "SC"
+ :size :small}))
- (com/field {:label "Amount"}
- [:div.flex.space-x-4.items-baseline
- (com/money-input {:name "amount-gte"
- :id "amount-gte"
- :hx-preserve "true"
- :class "hot-filter w-20"
- :value (:amount-gte (:query-params request))
- :placeholder "0.01"
- :size :small})
- [:div.align-baseline
- "to"]
- (com/money-input {:name "amount-lte"
- :hx-preserve "true"
- :id "amount-lte"
- :class "hot-filter w-20"
- :value (:amount-lte (:query-params request))
- :placeholder "9999.34"
- :size :small})])
+ (com/field {:label "Amount"}
+ [:div.flex.space-x-4.items-baseline
+ (com/money-input {:name "amount-gte"
+ :id "amount-gte"
+ :hx-preserve "true"
+ :class "hot-filter w-20"
+ :value (:amount-gte (:query-params request))
+ :placeholder "0.01"
+ :size :small})
+ [:div.align-baseline
+ "to"]
+ (com/money-input {:name "amount-lte"
+ :hx-preserve "true"
+ :id "amount-lte"
+ :class "hot-filter w-20"
+ :value (:amount-lte (:query-params request))
+ :placeholder "9999.34"
+ :size :small})])
- (com/field {:label "Linking"}
- (com/radio-card {:size :small
- :name "linked-to"
- :value (or (:linked-to (:query-params request)) "")
- :options [{:value ""
- :content "All"}
- {:value "none"
- :content "None"}
- {:value "invoice"
- :content "Invoice"}
- {:value "expected-deposit"
- :content "Expected Deposit"}
- {:value "payment"
- :content "Payment"}]}))
+ (com/field {:label "Linking"}
+ (com/radio-card {:size :small
+ :name "linked-to"
+ :value (or (:linked-to (:query-params request)) "")
+ :options [{:value ""
+ :content "All"}
+ {:value "none"
+ :content "None"}
+ {:value "invoice"
+ :content "Invoice"}
+ {:value "expected-deposit"
+ :content "Expected Deposit"}
+ {:value "payment"
+ :content "Payment"}]}))
- (when (is-admin? (:identity request))
- [:div.mt-4 {:x-data (hx/json {:unresolvedOnly (:unresolved (:query-params request))})}
- (com/hidden {:name "unresolved"
- ":value" "unresolvedOnly ? 'on' : ''"})
- (com/checkbox {:value (:unresolved (:query-params request))
- :x-model "unresolvedOnly"}
- "Unresolved only")])
+ (when (is-admin? (:identity request))
+ [:div.mt-4 {:x-data (hx/json {:unresolvedOnly (:unresolved (:query-params request))})}
+ (com/hidden {:name "unresolved"
+ ":value" "unresolvedOnly ? 'on' : ''"})
+ (com/checkbox {:value (:unresolved (:query-params request))
+ :x-model "unresolvedOnly"}
+ "Unresolved only")])
- (when (and (is-admin? (:identity request))
- (:db/id (:bank-account (:query-params request))))
- [:div.mt-4 {:x-data (hx/json {:potentialDuplicates (:potential-duplicates (:query-params request))})}
- (com/hidden {:name "potential-duplicates"
- ":value" "potentialDuplicates ? 'on' : ''"})
- (com/checkbox {:value (:potential-duplicates (:query-params request))
- :x-model "potentialDuplicates"}
- "Same Amount + Date")])
-
- (import-batch-id* request)
- (exact-match-id* request)]])
+ (when (and (is-admin? (:identity request))
+ (:db/id (:bank-account (:query-params request))))
+ [:div.mt-4 {:x-data (hx/json {:potentialDuplicates (:potential-duplicates (:query-params request))})}
+ (com/hidden {:name "potential-duplicates"
+ ":value" "potentialDuplicates ? 'on' : ''"})
+ (com/checkbox {:value (:potential-duplicates (:query-params request))
+ :x-model "potentialDuplicates"}
+ "Same Amount + Date")])
+ (import-batch-id* request)
+ (exact-match-id* request)]])
(def grid-page
(helper/build {:id "entity-table"
@@ -420,19 +414,17 @@
:page-specific-nav filters
:fetch-page fetch-page
:query-schema query-schema
- :oob-render
- (fn [request]
- [(assoc-in (date-range-field* request) [1 :hx-swap-oob] true)
- (assoc-in (exact-match-id* request) [1 :hx-swap-oob] true)
- (some-> (import-batch-id* request) (assoc-in [1 :hx-swap-oob] true))])
+ :oob-render
+ (fn [request]
+ [(assoc-in (date-range-field* request) [1 :hx-swap-oob] true)
+ (assoc-in (exact-match-id* request) [1 :hx-swap-oob] true)
+ (some-> (import-batch-id* request) (assoc-in [1 :hx-swap-oob] true))])
:action-buttons (fn [request]
- [
- (com/button {:color :primary
+ [(com/button {:color :primary
:hx-get (bidi/path-for ssr-routes/only-routes ::route/bulk-code)
:hx-target "#modal-holder"
"x-bind:hx-vals" "JSON.stringify({selected: $data.selected, 'all-selected': $data.all_selected})"
- "hx-include" "#transaction-filters"
- }
+ "hx-include" "#transaction-filters"}
"Code")
(com/button {:color :primary
:hx-post (bidi/path-for ssr-routes/only-routes ::route/bulk-delete)
@@ -454,13 +446,13 @@
tx-date (:transaction/date entity)
is-locked (and locked-until tx-date (time/before? tx-date locked-until))]
(if is-locked
- [ [:div.p-3.rounded-full.bg-gray-50.text-gray-400.w-6.h-6.box-content
- svg/lock]]
+ [[:div.p-3.rounded-full.bg-gray-50.text-gray-400.w-6.h-6.box-content
+ svg/lock]]
[(com/icon-button {:hx-get (bidi/path-for ssr-routes/only-routes
- ::route/edit-wizard
- :db/id (:db/id entity))}
- svg/pencil)])))
-
+ ::route/edit-wizard
+ :db/id (:db/id entity))}
+ svg/pencil)])))
+
:breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes ::route/page)}
"Transactions"]]
:title (fn [r]
@@ -477,9 +469,7 @@
(= (-> request :query-params :sort first :name) "Vendor")
(or (-> entity :transaction/vendor :vendor/name)
"No vendor")
-
-
-
+
:else nil))
:page->csv-entities (fn [[transactions]]
transactions)
@@ -529,20 +519,20 @@
:render (fn [i]
(let [db (dc/db conn)
journal-entries (when (:db/id i)
- (dc/q '[:find (pull ?je [:db/id :journal-entry/id])
- :in $ ?t-id
- :where
- [?je :journal-entry/original-entity ?t-id]]
- db
- (:db/id i)))
+ (dc/q '[:find (pull ?je [:db/id :journal-entry/id])
+ :in $ ?t-id
+ :where
+ [?je :journal-entry/original-entity ?t-id]]
+ db
+ (:db/id i)))
linked-invoices (when (and (:db/id i) (:transaction/payment i))
- (dc/q '[:find (pull ?inv [:db/id :invoice/invoice-number :invoice/total])
- :in $ ?payment-id
- :where
- [?ip :invoice-payment/payment ?payment-id]
- [?ip :invoice-payment/invoice ?inv]]
- db
- (:db/id (:transaction/payment i))))]
+ (dc/q '[:find (pull ?inv [:db/id :invoice/invoice-number :invoice/total])
+ :in $ ?payment-id
+ :where
+ [?ip :invoice-payment/payment ?payment-id]
+ [?ip :invoice-payment/invoice ?inv]]
+ db
+ (:db/id (:transaction/payment i))))]
(link-dropdown
(cond-> []
;; Payment link
@@ -553,41 +543,36 @@
{:exact-match-id (:db/id (:transaction/payment i))})
:color :primary
:content (format "Payment '%s'" (-> i :transaction/payment :payment/date (atime/unparse-local atime/normal-date)))})
-
+
;; Journal entry links
(seq journal-entries)
(concat
(for [[je] journal-entries]
{:link (hu/url (bidi/path-for ssr-routes/only-routes ::ledger-routes/all-page)
- {:exact-match-id (:db/id je)})
- :color :yellow
- :content "Ledger entry"}))
-
+ {:exact-match-id (:db/id je)})
+ :color :yellow
+ :content "Ledger entry"}))
+
;; Invoice links
(seq linked-invoices)
(concat
(for [[inv] linked-invoices]
{:link (hu/url (bidi/path-for ssr-routes/only-routes
- ::invoice-routes/all-page)
+ ::invoice-routes/all-page)
{:exact-match-id (:db/id inv)})
:color :secondary
- :content (format "Invoice '%s'" (:invoice/invoice-number inv))}))
-
- ))))
+ :content (format "Invoice '%s'" (:invoice/invoice-number inv))}))))))
+
:render-for #{:html}}]}))
(defn wrap-status-from-source [handler]
(fn [{:keys [matched-current-page-route] :as request}]
- (let [ request (cond-> request
+ (let [request (cond-> request
(= ::route/unapproved-page matched-current-page-route) (assoc-in [:route-params :status] :transaction-approval-status/unapproved)
(= ::route/approved-page matched-current-page-route) (assoc-in [:route-params :status] :transaction-approval-status/approved)
(= ::route/requires-feedback-page matched-current-page-route) (assoc-in [:route-params :status] :transaction-approval-status/requires-feedback)
(= ::route/page matched-current-page-route) (assoc-in [:route-params :status] nil))]
(handler request))))
-
-
-
-
(defn selected->ids [request params]
(let [all-selected (:all-selected params)
selected (:selected params)
@@ -598,7 +583,6 @@
(assoc-in [:query-params :start] 0)
(assoc-in [:query-params :per-page] 250))))
-
:else
selected)]
ids))
\ No newline at end of file
diff --git a/src/clj/auto_ap/ssr/transaction/edit.clj b/src/clj/auto_ap/ssr/transaction/edit.clj
index b4400f4b..a5d22882 100644
--- a/src/clj/auto_ap/ssr/transaction/edit.clj
+++ b/src/clj/auto_ap/ssr/transaction/edit.clj
@@ -142,6 +142,12 @@
true (dissoc :vendor/account-overrides :vendor/terms-overrides))]
vendor)))
+(defn vendor-default-account [vendor-id client-id]
+ (when vendor-id
+ (let [vendor (get-vendor vendor-id)
+ clientized (clientize-vendor vendor client-id)]
+ (:vendor/default-account clientized))))
+
(defn location-select*
[{:keys [name account-location client-locations value]}]
(let [options (into (cond account-location
@@ -904,18 +910,23 @@
(transaction-rules-view request)]
[:div {:x-show "activeForm === 'manual'", :x-transition:enter "transition ease-out duration-500", :x-transition:enter-start "opacity-0 transform scale-95", :x-transition:enter-end "opacity-100 transform scale-100"}
[:div {}
- (fc/with-field :transaction/vendor
- (com/validated-field
- {:label "Vendor"
- :errors (fc/field-errors)}
- [:div.w-96
- (com/typeahead {:name (fc/field-name)
- :error? (fc/error?)
- :class "w-96"
- :placeholder "Search..."
- :url (bidi/path-for ssr-routes/only-routes :vendor-search)
- :value (fc/field-value)
- :content-fn (fn [c] (pull-attr (dc/db conn) :vendor/name c))})]))
+ [:div {:hx-trigger "change"
+ :hx-get (bidi/path-for ssr-routes/only-routes ::route/edit-vendor-changed)
+ :hx-target "#account-grid-body"
+ :hx-swap "outerHTML"
+ :hx-include "closest form"}
+ (fc/with-field :transaction/vendor
+ (com/validated-field
+ {:label "Vendor"
+ :errors (fc/field-errors)}
+ [:div.w-96
+ (com/typeahead {:name (fc/field-name)
+ :error? (fc/error?)
+ :class "w-96"
+ :placeholder "Search..."
+ :url (bidi/path-for ssr-routes/only-routes :vendor-search)
+ :value (fc/field-value)
+ :content-fn (fn [c] (pull-attr (dc/db conn) :vendor/name c))})]))]
;; Memo field
@@ -1381,6 +1392,35 @@
[]
entity)))
+(defn edit-vendor-changed-handler [request]
+ (let [snapshot (:snapshot (:multi-form-state request))
+ client-id (or (:transaction/client snapshot)
+ (-> request :entity :transaction/client :db/id))
+ vendor-id (:transaction/vendor snapshot)
+ total (Math/abs (or (:transaction/amount snapshot) 0.0))
+ amount-mode (or (:amount-mode snapshot) "$")]
+ (if (and (empty? (:transaction/accounts snapshot))
+ vendor-id
+ client-id)
+ (if-let [default-account (vendor-default-account vendor-id client-id)]
+ (let [new-account {:db/id (str (java.util.UUID/randomUUID))
+ :transaction-account/account (:db/id default-account)
+ :transaction-account/location (or (:account/location default-account) "Shared")}
+ new-account (if (= amount-mode "%")
+ (assoc new-account :transaction-account/amount 100.0)
+ (assoc new-account :transaction-account/amount total))
+ updated-snapshot (assoc snapshot :transaction/accounts [new-account])
+ updated-request (assoc-in request [:multi-form-state :snapshot] updated-snapshot)]
+ (html-response
+ [:div#account-grid-body
+ (account-grid-body* updated-request)]))
+ (html-response
+ [:div#account-grid-body
+ (account-grid-body* request)]))
+ (html-response
+ [:div#account-grid-body
+ (account-grid-body* request)]))))
+
(def key->handler
(apply-middleware-to-all-handlers
{::route/edit-wizard (-> mm/open-wizard-handler
@@ -1399,6 +1439,9 @@
(wrap-entity [:multi-form-state :snapshot :db/id] d-transactions/default-read)
(mm/wrap-wizard edit-wizard)
(mm/wrap-decode-multi-form-state))
+ ::route/edit-vendor-changed (-> edit-vendor-changed-handler
+ (mm/wrap-wizard edit-wizard)
+ (mm/wrap-decode-multi-form-state))
::route/location-select (-> location-select
(wrap-schema-enforce :query-schema [:map
[:name :string]
diff --git a/src/clj/auto_ap/ssr/transaction/insights.clj b/src/clj/auto_ap/ssr/transaction/insights.clj
index bb91ef38..79df77c7 100644
--- a/src/clj/auto_ap/ssr/transaction/insights.clj
+++ b/src/clj/auto_ap/ssr/transaction/insights.clj
@@ -57,7 +57,7 @@
:args [(dc/db conn)
(iol-ion.query/recent-date 300)
(map :db/id clients)
-
+
pull-expr]})
(map first)
(drop-while (fn [x]
@@ -71,44 +71,42 @@
(take 50)
(into []))))
-
(defn get-pinecone [transaction-id]
- (->
- (http/get (-> "https://transactions-a8257ba.svc.us-west4-gcp-free.pinecone.io/vectors/fetch"
- url/url
- (assoc :query {:ids transaction-id})
- str)
- {:headers {"Api-Key" "f2d3a78e-bcea-4fcd-88b6-2527b8423607"}
- :as :json
- :keywordize? false})
- :body
- :vectors
- ((keyword (str transaction-id)))
- :values))
+ (->
+ (http/get (-> "https://transactions-a8257ba.svc.us-west4-gcp-free.pinecone.io/vectors/fetch"
+ url/url
+ (assoc :query {:ids transaction-id})
+ str)
+ {:headers {"Api-Key" "f2d3a78e-bcea-4fcd-88b6-2527b8423607"}
+ :as :json
+ :keywordize? false})
+ :body
+ :vectors
+ ((keyword (str transaction-id)))
+ :values))
(defn get-pinecone-similarities [transaction-id]
- (filter
- (fn [{:keys [score]}]
- (> score 0.95)
- )
- (->
- (http/post (-> "https://transactions-a8257ba.svc.us-west4-gcp-free.pinecone.io/query"
- url/url
- str)
- {:headers {"Api-Key" "f2d3a78e-bcea-4fcd-88b6-2527b8423607"}
- :form-params {"vector" (get-pinecone transaction-id)
- "topK" 100,
- "includeMetadata" true
- "namespace" ""}
- :content-type :json
- :as :json})
- :body
- :matches)))
+ (filter
+ (fn [{:keys [score]}]
+ (> score 0.95))
+ (->
+ (http/post (-> "https://transactions-a8257ba.svc.us-west4-gcp-free.pinecone.io/query"
+ url/url
+ str)
+ {:headers {"Api-Key" "f2d3a78e-bcea-4fcd-88b6-2527b8423607"}
+ :form-params {"vector" (get-pinecone transaction-id)
+ "topK" 100,
+ "includeMetadata" true
+ "namespace" ""}
+ :content-type :json
+ :as :json})
+ :body
+ :matches)))
(defn pinecone-similarity-list [transaction-id]
(for [{{:keys [amount date description vendor]} :metadata score :score id :id} (get-pinecone-similarities transaction-id)
- :let [vendor-name (:vendor/name (:transaction/vendor (dc/pull (dc/db conn) [{:transaction/vendor [:vendor/name]} ] (Long/parseLong id))))
- account-code (-> (dc/pull (dc/db conn) [{:transaction/accounts [{:transaction-account/account [:account/numeric-code]}]} ] (Long/parseLong id))
+ :let [vendor-name (:vendor/name (:transaction/vendor (dc/pull (dc/db conn) [{:transaction/vendor [:vendor/name]}] (Long/parseLong id))))
+ account-code (-> (dc/pull (dc/db conn) [{:transaction/accounts [{:transaction-account/account [:account/numeric-code]}]}] (Long/parseLong id))
:transaction/accounts
first
:transaction-account/account
@@ -121,7 +119,6 @@
:description description
:score score}))
-
(defn transaction-row [r & {:keys [hide-actions? class last? other-params]}]
(com/data-grid-row
(cond-> {:class class}
@@ -219,8 +216,8 @@
@(dc/transact conn [updated-transaction])
(html-response (transaction-row
(parse-outcome (dc/pull db-before
- pull-expr
- (Long/parseLong transaction-id)))
+ pull-expr
+ (Long/parseLong transaction-id)))
:hide-actions? true
:class "live-added"
:other-params (hx/alpine-mount-then-disappear {})))))
@@ -237,48 +234,48 @@
(defn explain [{:keys [identity session] {:keys [transaction-id]} :route-params}]
(let [r (dc/pull (dc/db conn)
- pull-expr
- (Long/parseLong transaction-id))
+ pull-expr
+ (Long/parseLong transaction-id))
similar (pinecone-similarity-list transaction-id)]
(modal-response
- (com/modal {}
- (com/modal-card {:style {:width "900px"}}
- [:div.flex [:div.p-2 "Similar Transactions"]]
- (com/data-grid {:headers [(com/data-grid-header {:name "Date"
- :key "date"})
- (com/data-grid-header {:name "Description"
- :key "description"})
- (com/data-grid-header {:name "Amount"
- :key "amount"})
- (com/data-grid-header {:name "Vendor"
- :key "vendor"})
- (com/data-grid-header {:name "Account"
- :key "account"})
- (com/data-grid-header {:name "Score"
- :key "score"})]}
+ (com/modal {}
+ (com/modal-card {:style {:width "900px"}}
+ [:div.flex [:div.p-2 "Similar Transactions"]]
+ (com/data-grid {:headers [(com/data-grid-header {:name "Date"
+ :key "date"})
+ (com/data-grid-header {:name "Description"
+ :key "description"})
+ (com/data-grid-header {:name "Amount"
+ :key "amount"})
+ (com/data-grid-header {:name "Vendor"
+ :key "vendor"})
+ (com/data-grid-header {:name "Account"
+ :key "account"})
+ (com/data-grid-header {:name "Score"
+ :key "score"})]}
- (com/data-grid-row {:class "bg-primary-200"}
- (com/data-grid-cell {:class "text-left font-bold"} (some-> r :transaction/date coerce/to-date-time (atime/unparse-local atime/normal-date)))
- (com/data-grid-cell {:class "text-left font-bold"} (-> r :transaction/description-original) )
- (com/data-grid-cell {:class "font-bold"} (if (> (-> r :transaction/amount) 0.0)
- [:div.tag.is-success.is-light (str "$" (Math/round (:transaction/amount r)))]
- [:div.tag.is-danger.is-light (str "$" (Math/round (:transaction/amount r)))]))
- (com/data-grid-cell {})
- (com/data-grid-cell {})
- (com/data-grid-cell {}))
+ (com/data-grid-row {:class "bg-primary-200"}
+ (com/data-grid-cell {:class "text-left font-bold"} (some-> r :transaction/date coerce/to-date-time (atime/unparse-local atime/normal-date)))
+ (com/data-grid-cell {:class "text-left font-bold"} (-> r :transaction/description-original))
+ (com/data-grid-cell {:class "font-bold"} (if (> (-> r :transaction/amount) 0.0)
+ [:div.tag.is-success.is-light (str "$" (Math/round (:transaction/amount r)))]
+ [:div.tag.is-danger.is-light (str "$" (Math/round (:transaction/amount r)))]))
+ (com/data-grid-cell {})
+ (com/data-grid-cell {})
+ (com/data-grid-cell {}))
- (com/data-grid-row {}
- (take 10
- (for [{:keys [amount date description vendor-name numeric-code score]} similar]
- (com/data-grid-row
- {}
- (com/data-grid-cell {:class "text-left"} (subs date 0 10))
- (com/data-grid-cell {:class "text-left"} description )
- (com/data-grid-cell {} (some->> amount double (format "$%.2f")))
- (com/data-grid-cell {} vendor-name)
- (com/data-grid-cell {} numeric-code)
- (com/data-grid-cell {} (format "%.1f%%" (* 100 (double score)))))))))
- [:div])))))
+ (com/data-grid-row {}
+ (take 10
+ (for [{:keys [amount date description vendor-name numeric-code score]} similar]
+ (com/data-grid-row
+ {}
+ (com/data-grid-cell {:class "text-left"} (subs date 0 10))
+ (com/data-grid-cell {:class "text-left"} description)
+ (com/data-grid-cell {} (some->> amount double (format "$%.2f")))
+ (com/data-grid-cell {} vendor-name)
+ (com/data-grid-cell {} numeric-code)
+ (com/data-grid-cell {} (format "%.1f%%" (* 100 (double score)))))))))
+ [:div])))))
(defn transaction-rows* [{:keys [clients identity after]}]
(let [recommendations (transaction-recommendations identity clients :after after)]
@@ -286,7 +283,7 @@
(for [r recommendations
:let [last? (= r (last recommendations))]]
(transaction-row r :last? last?))
- [:tr [:td.has-text-centered.has-text-gray {:colspan 7 }
+ [:tr [:td.has-text-centered.has-text-gray {:colspan 7}
[:i "That's the last of 'em!"]]])))
(defn transaction-rows [{:keys [session identity route-params clients]}]
diff --git a/src/clj/auto_ap/ssr/ui.clj b/src/clj/auto_ap/ssr/ui.clj
index 524c57d9..b653589e 100644
--- a/src/clj/auto_ap/ssr/ui.clj
+++ b/src/clj/auto_ap/ssr/ui.clj
@@ -13,7 +13,6 @@
{}
hiccup))})
-
(defn base-page [request contents page-name]
(html-page
[:html
@@ -28,7 +27,7 @@
[:script {:src "//cdn.jsdelivr.net/chartist.js/latest/chartist.min.js"}]
[:link {:rel "stylesheet", :href "/output.css"}]
[:script {:src "https://cdn.plaid.com/link/v2/stable/link-initialize.js"}]
- [:script { :src "https://cdn.jsdelivr.net/npm/@ryangjchandler/alpine-tooltip@1.x.x/dist/cdn.min.js" :defer true}]
+ [:script {:src "https://cdn.jsdelivr.net/npm/@ryangjchandler/alpine-tooltip@1.x.x/dist/cdn.min.js" :defer true}]
[:link {:rel "stylesheet" :href "/css/tippy/tippy.css"}]
[:link {:rel "stylesheet" :href "/css/tippy/light.css"}]
[:script {:src "/js/htmx.min.js"
@@ -71,7 +70,6 @@ input[type=number] {
contents
[:script {:src "/js/flowbite.min.js"}]
-
[:div#modal-holder
{:class "fixed top-0 left-0 z-[99] flex items-center justify-center w-screen h-screen"
"x-show" "open"
@@ -106,4 +104,4 @@ input[type=number] {
"x-transition:leave-start" "!opacity-100 !translate-y-0"
"x-transition:leave-end" "!opacity-0 !translate-y-32"}
- [:div#modal-content.flex.items-center.justify-center {:class "md:p-12"}]]]]]]))
+ [:div#modal-content.flex.items-center.justify-center {:class "md:p-12"}]]]]]]))
diff --git a/src/clj/auto_ap/ssr/users.clj b/src/clj/auto_ap/ssr/users.clj
index 8c2c6107..729c7879 100644
--- a/src/clj/auto_ap/ssr/users.clj
+++ b/src/clj/auto_ap/ssr/users.clj
@@ -30,7 +30,7 @@
(defn filters [request]
[:form {"hx-trigger" "change delay:500ms, keyup changed from:.hot-filter delay:1000ms"
"hx-get" (bidi/path-for ssr-routes/only-routes
- :user-table)
+ :user-table)
"hx-target" "#user-table"
"hx-indicator" "#user-table"}
@@ -64,7 +64,7 @@
(com/field {:label "Role"}
(com/radio-card {:size :small
:name "role"
-:value (:role (:query-params request))
+ :value (:role (:query-params request))
:options [{:value ""
:content "All"}
{:value "admin"
@@ -84,7 +84,7 @@
[:maybe
(into [:map {}
[:role {:optional true} [:maybe (ref->enum-schema "user-role")]]
- [:client {:optional true :default nil} [:maybe [:entity-map {:pull [:db/id :client/name]}]]] ]
+ [:client {:optional true :default nil} [:maybe [:entity-map {:pull [:db/id :client/name]}]]]]
default-grid-fields-schema)]))
(def default-read '[:db/id
@@ -93,19 +93,19 @@
:user/profile-image-url
[:user/last-login :xform clj-time.coerce/from-date]
{[:user/role :xform iol-ion.query/ident] [:db/ident]
-
+
:user/clients [:client/code :db/id :client/locations :client/name]}])
(defn fetch-ids [db request]
(let [query-params (:query-params request)
query (cond-> {:query {:find []
- :in '[$ ]
+ :in '[$]
:where '[]}
- :args [db ]}
+ :args [db]}
(:sort query-params) (add-sorter-fields {"name" ['[?e :user/name ?un]
'[(clojure.string/upper-case ?un) ?sort-name]]
"email" ['[(get-else $ ?e :user/email "") ?sort-email]]
-
+
"role" ['[?e :user/role ?r]
'[?r :db/ident ?ri]
'[(name ?ri) ?sort-role]]
@@ -136,16 +136,14 @@
(some->> query-params :role)
(merge-query {:query {:find []
:in ['?r]
- :where ['[?e :user/role ?r] ]}
+ :where ['[?e :user/role ?r]]}
:args [(some->> query-params :role)]})
-
-
true
(merge-query {:query {:find ['?sort-default '?e]
:where ['[?e :user/name ?un]
'[(clojure.string/upper-case ?un) ?sort-default]]}}))]
-
+
(cond->> (query2 query)
true (apply-sort-3 query-params)
true (apply-pagination query-params))))
@@ -186,14 +184,12 @@
[:div.flex.space-x-2
(for [{:client/keys [code]} (take 3 (:user/clients user))]
(com/pill {:color :primary}
- code)
- )
+ code))
(let [remainder (- (count (:user/clients user)) 3)]
(when (> remainder 0)
(com/pill {:color :white}
(format "%d more" remainder))))])
-
(def grid-page
(helper/build {:id "user-table"
:nav com/admin-aside-nav
@@ -223,10 +219,10 @@
:sort-key "name"
:render (fn [user]
[:div.flex.space-x-2.place-items-center
- (when-let [profile-image (:user/profile-image-url user) ]
+ (when-let [profile-image (:user/profile-image-url user)]
[:div.rounded-full.overflow-hidden.w-8.h-8.display-inline
- [:img {:src profile-image }]])
- [:span.inline-block ] (:user/name user)])}
+ [:img {:src profile-image}]])
+ [:span.inline-block] (:user/name user)])}
{:key "email"
:name "Email"
@@ -242,8 +238,7 @@
:render #(some-> % (:user/last-login) (atime/unparse-local atime/standard-time))}
{:key "clients"
:name "Clients"
- :render user->client-pills}
- ]}))
+ :render user->client-pills}]}))
(def row* (partial helper/row* grid-page))
(def table* (partial helper/table* grid-page))
@@ -266,19 +261,17 @@
(com/data-grid-cell {}
(com/validated-field {:errors (fc/field-errors (:db/id fc/*current*))}
(com/typeahead {:name (fc/field-name (:db/id fc/*current*))
- :class "w-full"
- :url (bidi/path-for ssr-routes/only-routes
- :company-search)
- :value (fc/field-value)
- :value-fn :db/id
+ :class "w-full"
+ :url (bidi/path-for ssr-routes/only-routes
+ :company-search)
+ :value (fc/field-value)
+ :value-fn :db/id
-
- :content-fn #(pull-attr (dc/db conn) :client/name (:db/id %))
- :size :small})))
+ :content-fn #(pull-attr (dc/db conn) :client/name (:db/id %))
+ :size :small})))
(com/data-grid-cell {:class "align-top"}
(com/a-icon-button {"@click.prevent.stop" "show=false; setTimeout(() => $refs.p.remove(), 500)"} svg/x))))
-
(defn dialog* [{:keys [form-params form-errors entity]}]
(println "FORM PARMS" form-params)
(fc/start-form
@@ -328,65 +321,64 @@
(defn user-edit-save [{:keys [form-params identity] :as request}]
(let [_ @(dc/transact conn [[:upsert-entity form-params]])
user (some-> form-params :db/id (#(dc/pull (dc/db conn) default-read %)))]
-
- (html-response
- (row* identity user {:flash? true})
- :headers {"hx-trigger" "modalclose"
- "hx-retarget" (format "#user-table tr[data-id=\"%d\"]" (:db/id user))})))
+ (html-response
+ (row* identity user {:flash? true})
+ :headers {"hx-trigger" "modalclose"
+ "hx-retarget" (format "#user-table tr[data-id=\"%d\"]" (:db/id user))})))
(def form-schema
(mc/schema
- [:map
- [:db/id entity-id]
- [:user/clients {:optional true}
- [:maybe
- (many-entity {} [:db/id entity-id])]]
- [:user/role (ref->enum-schema "user-role")]]))
+ [:map
+ [:db/id entity-id]
+ [:user/clients {:optional true}
+ [:maybe
+ (many-entity {} [:db/id entity-id])]]
+ [:user/role (ref->enum-schema "user-role")]]))
(defn user-dialog [{:keys [form-params entity form-errors]}]
(modal-response
- (dialog* {:form-params (or (when (seq form-params)
- form-params)
- (when entity
- (mc/decode form-schema entity main-transformer))
- {})
- :entity entity
- :form-errors form-errors})))
+ (dialog* {:form-params (or (when (seq form-params)
+ form-params)
+ (when entity
+ (mc/decode form-schema entity main-transformer))
+ {})
+ :entity entity
+ :form-errors form-errors})))
-(defn new-client [{ {:keys [index]} :query-params}]
- (html-response
- (fc/start-form-with-prefix [:user/clients (or index 0)] {:db/id nil
- :new? true} []
- (client-row* fc/*current*))))
+(defn new-client [{{:keys [index]} :query-params}]
+ (html-response
+ (fc/start-form-with-prefix [:user/clients (or index 0)] {:db/id nil
+ :new? true} []
+ (client-row* fc/*current*))))
(def key->handler
(apply-middleware-to-all-handlers
- {:users (helper/page-route grid-page)
- :user-table (helper/table-route grid-page)
- :user-edit-save (-> user-edit-save
- (wrap-entity [:form-params :db/id] default-read)
- (wrap-schema-enforce :form-schema form-schema)
- (wrap-nested-form-params)
- (wrap-form-4xx-2 (wrap-entity user-dialog [:form-params :db/id] default-read)))
- :user-client-new (-> new-client
+ {:users (helper/page-route grid-page)
+ :user-table (helper/table-route grid-page)
+ :user-edit-save (-> user-edit-save
+ (wrap-entity [:form-params :db/id] default-read)
+ (wrap-schema-enforce :form-schema form-schema)
+ (wrap-nested-form-params)
+ (wrap-form-4xx-2 (wrap-entity user-dialog [:form-params :db/id] default-read)))
+ :user-client-new (-> new-client
(wrap-schema-enforce :query-schema [:map
- [:index {:optional true
- :default 0} [nat-int? {:default 0}]]]))
- :user-edit-dialog (-> user-dialog
- (wrap-entity [:route-params :db/id] default-read)
- (wrap-schema-enforce
- :route-schema (mc/schema [:map [:db/id entity-id]])))
- :user-impersonate (-> impersonate
- (wrap-entity [:params :db/id] default-read)
- (wrap-schema-enforce
- :params-schema (mc/schema [:map [:db/id entity-id]])))}
- (fn [h]
- (-> h
-(wrap-copy-qp-pqp)
- (wrap-apply-sort grid-page)
- (wrap-merge-prior-hx)
- (wrap-schema-enforce :query-schema query-schema)
- (wrap-schema-enforce :hx-schema query-schema)
- (wrap-admin)
- (wrap-client-redirect-unauthenticated)))))
+ [:index {:optional true
+ :default 0} [nat-int? {:default 0}]]]))
+ :user-edit-dialog (-> user-dialog
+ (wrap-entity [:route-params :db/id] default-read)
+ (wrap-schema-enforce
+ :route-schema (mc/schema [:map [:db/id entity-id]])))
+ :user-impersonate (-> impersonate
+ (wrap-entity [:params :db/id] default-read)
+ (wrap-schema-enforce
+ :params-schema (mc/schema [:map [:db/id entity-id]])))}
+ (fn [h]
+ (-> h
+ (wrap-copy-qp-pqp)
+ (wrap-apply-sort grid-page)
+ (wrap-merge-prior-hx)
+ (wrap-schema-enforce :query-schema query-schema)
+ (wrap-schema-enforce :hx-schema query-schema)
+ (wrap-admin)
+ (wrap-client-redirect-unauthenticated)))))
diff --git a/src/clj/auto_ap/ssr/utils.clj b/src/clj/auto_ap/ssr/utils.clj
index 68ad44f9..a54da4c6 100644
--- a/src/clj/auto_ap/ssr/utils.clj
+++ b/src/clj/auto_ap/ssr/utils.clj
@@ -67,8 +67,6 @@
(assoc-in [:headers "hx-retarget"] "#modal-content")
(assoc-in [:headers "hx-reswap"] "innerHTML"))))))
-
-
(defn form-data->map [form-data]
(reduce-kv
(fn [acc k v]
@@ -91,7 +89,6 @@
(str/join "_" (map path->name k))
:else k))
-
(defn forced-vector [x]
[:vector {:decode/json {:enter (fn [x]
(if (sequential? x)
@@ -172,7 +169,6 @@
[x]))})
schema]))
-
(defn str->keyword [s]
(if (string? s)
(let [[ns k] (str/split s #"/")]
@@ -186,10 +182,9 @@
(subs (str k) 1)
(string? k)
k
- :else
+ :else
k))
-
;; TODO make this bubble the form data automatically
(defn field-validation-error [m path & {:as data}]
(throw+ (ex-info m (merge data {:type :field-validation
@@ -201,19 +196,18 @@
:form-validation-errors [m]}))))
(def clj-date-schema
- (mc/schema [:and [inst? {:date-format atime/normal-date
- }]
+ (mc/schema [:and [inst? {:date-format atime/normal-date}]
[:fn
{:error/message "Invalid date"}
(fn [d]
- (if d
+ (if d
(time/after? (coerce/to-date-time d)
(coerce/to-date-time #inst "2000-01-01"))
true))]
[:fn
{:error/message "Can not look more than four years into the future."}
(fn [d]
- (if d
+ (if d
(time/before? (coerce/to-date-time d)
(time/plus (time/now) (time/years 4)))
true))]]))
@@ -270,7 +264,7 @@
"year"
(assoc m
start-date-key (atime/as-local-time (time/date-time (time/year (atime/local-today))
- 1
+ 1
1))
end-date-key nil)
@@ -333,7 +327,7 @@
(when (:coerce? (m/properties schema))
(fn [data]
(cond
- (vector? data)
+ (vector? data)
data
(sequential? data)
data
@@ -360,7 +354,6 @@
(into {})))))
(handler request)))))
-
(def dissoc-nil-transformer
(let [e {:map {:compile (fn [schema _]
(fn [data]
@@ -374,26 +367,25 @@
:decoders e})))
(def unspecified-transformer
- (mt2/transformer
- {:decoders {:map {:compile (fn [x g]
- (fn [value]
- (if (or (nil? value)
- (map? value))
- (let [ specified-keys (set (keys value))]
- (reduce
- (fn [value [k params]]
- (cond (and (:unspecified/fn params)
- (not (get specified-keys k)))
- (assoc value k ((:unspecified/fn params)))
- (and (:unspecified/value params)
- (not (get specified-keys k)))
- (assoc value k (:unspecified/value params))
- :else
- value
- ))
- value
- (m/children x)))
- value)))}}}))
+ (mt2/transformer
+ {:decoders {:map {:compile (fn [x g]
+ (fn [value]
+ (if (or (nil? value)
+ (map? value))
+ (let [specified-keys (set (keys value))]
+ (reduce
+ (fn [value [k params]]
+ (cond (and (:unspecified/fn params)
+ (not (get specified-keys k)))
+ (assoc value k ((:unspecified/fn params)))
+ (and (:unspecified/value params)
+ (not (get specified-keys k)))
+ (assoc value k (:unspecified/value params))
+ :else
+ value))
+ value
+ (m/children x)))
+ value)))}}}))
(def main-transformer
(mt2/transformer
@@ -407,8 +399,7 @@
coerce-vector
date-range-transformer
pull-transformer
- mt2/default-value-transformer
- ))
+ mt2/default-value-transformer))
(defn strip [s]
(cond (and (string? s) (str/blank? s))
@@ -434,7 +425,6 @@
:decoded entity
:error {:explain (mc/explain schema entity)}}))))
-
(defn schema-enforce-request [{:keys [form-params query-params hx-query-params multipart-params params] :as request} & {:keys [form-schema multipart-schema hx-schema query-schema route-schema params-schema]}]
(let [request (try
(cond-> request
@@ -451,7 +441,7 @@
route-schema
(:route-params request)
main-transformer))
-
+
(and (:multipart-params request) multipart-schema)
(assoc :multipart-params
(mc/coerce
@@ -473,23 +463,22 @@
hx-query-params
main-transformer))
-
(and query-schema query-params)
(assoc :query-params
(mc/coerce
- query-schema
- query-params
- main-transformer)))
+ query-schema
+ query-params
+ main-transformer)))
(catch Exception e
- (alog/warn ::validation-error
+ (alog/warn ::validation-error
:error e
::errors (-> e
- (ex-data)
- :data
- :explain
- (me/humanize {:errors (assoc me/default-errors
- ::mc/missing-key {:error/message {:en "required"}})})))
+ (ex-data)
+ :data
+ :explain
+ (me/humanize {:errors (assoc me/default-errors
+ ::mc/missing-key {:error/message {:en "required"}})})))
(throw (ex-info (->> (-> e
(ex-data)
:data
@@ -520,7 +509,6 @@
:route-schema route-schema
:params-schema params-schema))))
-
(defn schema-decode-request [{:keys [form-params query-params params] :as request} & {:keys [form-schema query-schema route-schema params-schema]}]
(let [request (cond-> request
(and (:params request) params-schema)
@@ -588,7 +576,6 @@
:when (= n (namespace ident))]
{:value (name ident) :content (str/replace (str/capitalize (name ident)) "-" " ")})))
-
(defn wrap-form-4xx-2 [handler form-handler]
(fn [request]
(try+
@@ -613,8 +600,7 @@
(form-handler (assoc request
:form-params (or (:form e) ;; TODO is :form actually used?
(:form-params e)
- (:form-params request)
- )
+ (:form-params request))
:form-errors (:form-errors e))))
(catch [:type :form-validation] e
(form-handler (assoc request
@@ -624,7 +610,6 @@
:form-validation-errors (:form-validation-errors e)
:form-errors {:errors (:form-validation-errors e)}))))))
-
(defn apply-middleware-to-all-handlers [key->handler f]
(->> key->handler
(reduce
@@ -645,7 +630,6 @@
(str "[" (k->n k) "]"))
rest)))))
-
(defn wrap-entity [handler path read]
(fn wrap-entity-request [request]
(let [entity (some->>
@@ -664,7 +648,7 @@
:entity-map
(mc/-simple-schema {:type :entity-map
:pred map?
- :type-properties { :error/message "required"}})
+ :type-properties {:error/message "required"}})
#_[:map {:name :entity-map} [:db/id nat-int?]]}))
(comment
@@ -681,8 +665,6 @@
(with-precision 2
(double (.setScale (bigdec d) 2 java.math.RoundingMode/HALF_UP))))
-
-
(defn wrap-implied-route-param [handler & {:as route-params}]
(fn [request]
(handler (update-in request [:route-params] merge route-params))))
@@ -694,7 +676,7 @@
allowance (allowance-key (dc/pull (dc/db conn) '[{[:account/invoice-allowance :xform iol-ion.query/ident] [:db/ident]
[:account/vendor-allowance :xform iol-ion.query/ident] [:db/ident]
[:account/default-allowance :xform iol-ion.query/ident] [:db/ident]}]
- account-id))]
+ account-id))]
(not= :allowance/denied
allowance)))
@@ -713,9 +695,8 @@
(throw (ex-info "Exception." {:type "'A' not allowed"})))
true))
-(def default-grid-fields-schema
- [
- [:sort {:optional true} [:maybe [:any]]]
+(def default-grid-fields-schema
+ [[:sort {:optional true} [:maybe [:any]]]
[:per-page {:optional true :default 25} [:maybe :int]]
[:start {:optional true :default 0} [:maybe :int]]
[:exact-match-id {:optional true} [:maybe entity-id]]])
\ No newline at end of file
diff --git a/src/clj/auto_ap/ssr/vendor.clj b/src/clj/auto_ap/ssr/vendor.clj
index 0519faf1..05159231 100644
--- a/src/clj/auto_ap/ssr/vendor.clj
+++ b/src/clj/auto_ap/ssr/vendor.clj
@@ -8,7 +8,7 @@
[ring.middleware.json :refer [wrap-json-response]]))
(defn best-match [q]
-
+
(let [name-like-ids (when (not-empty q)
(map (comp #(Long/parseLong %) :id)
(solr/query solr/impl "vendors"
@@ -21,7 +21,7 @@
(first valid-clients)))
(defn search [{:keys [clients query-params identity]}]
-
+
(let [name-like-ids (when (not-empty (get query-params "q"))
(map (comp #(Long/parseLong %) :id)
(solr/query solr/impl "vendors"
@@ -30,7 +30,7 @@
"fields" "id"
"limit" 300})))
valid-clients (for [n name-like-ids]
- {"value" n "label" (pull-attr (dc/db conn) :vendor/name n)} )]
+ {"value" n "label" (pull-attr (dc/db conn) :vendor/name n)})]
{:body (take 10 valid-clients)}))
(def search (wrap-json-response search))
@@ -38,40 +38,39 @@
#_(comment
(solr/delete solr/impl "vendors")
- (count (let [valid-ids (->> (dc/q '[:find ?v
- :in $
- :where [?v :vendor/name]]
- (dc/db conn))
- (map first)
- (into #{}))]
- (for [v (solr/query solr/impl "vendors"
- {"query" "*"
- "limit" 10000})
- :when (not (valid-ids (Long/parseLong (:id v))))]
- v)))
+ (count (let [valid-ids (->> (dc/q '[:find ?v
+ :in $
+ :where [?v :vendor/name]]
+ (dc/db conn))
+ (map first)
+ (into #{}))]
+ (for [v (solr/query solr/impl "vendors"
+ {"query" "*"
+ "limit" 10000})
+ :when (not (valid-ids (Long/parseLong (:id v))))]
+ v)))
- (let [name-like-ids (when (not-empty "A&J")
- (map (comp (juxt identity #(Long/parseLong %)) :id)
- (solr/query solr/impl "vendors"
- {"query" (cond-> (format "name:(%s*)" (str/upper-case "A&J"))
- (not (is-admin? identity)) (str " hidden:false"))
- "fields" "id,name"
- "limit" 300})))
- valid-clients (for [[z n] name-like-ids]
- {"value" n "internal-label" z "label" (dc/pull (dc/db conn) '[*] n)})]
- (take 5 valid-clients))
+ (let [name-like-ids (when (not-empty "A&J")
+ (map (comp (juxt identity #(Long/parseLong %)) :id)
+ (solr/query solr/impl "vendors"
+ {"query" (cond-> (format "name:(%s*)" (str/upper-case "A&J"))
+ (not (is-admin? identity)) (str " hidden:false"))
+ "fields" "id,name"
+ "limit" 300})))
+ valid-clients (for [[z n] name-like-ids]
+ {"value" n "internal-label" z "label" (dc/pull (dc/db conn) '[*] n)})]
+ (take 5 valid-clients))
+ (solr/query solr/impl "vendors"
+ {"query" (cond-> (format "name:(%s*)" (str/upper-case (solr/escape "A&J Pr")))
+ (not true) (str " hidden:false"))
+ "fields" "id, name"
+ "limit" 300})
- (solr/query solr/impl "vendors"
- {"query" (cond-> (format "name:(%s*)" (str/upper-case (solr/escape "A&J Pr")))
- (not true) (str " hidden:false"))
- "fields" "id, name"
- "limit" 300})
+ (solr/escape "A&J")
- (solr/escape "A&J")
-
- (first (solr/query solr/impl "vendors"
- {"query" (cond-> (format "name:(A\\&J PRO*)")
- (not true) (str " hidden:false"))
- "fields" "id, name"
- "limit" 300})))
\ No newline at end of file
+ (first (solr/query solr/impl "vendors"
+ {"query" (cond-> (format "name:(A\\&J PRO*)")
+ (not true) (str " hidden:false"))
+ "fields" "id, name"
+ "limit" 300})))
\ No newline at end of file
diff --git a/src/clj/auto_ap/time.clj b/src/clj/auto_ap/time.clj
index 070c00c3..ea166fc9 100644
--- a/src/clj/auto_ap/time.clj
+++ b/src/clj/auto_ap/time.clj
@@ -46,7 +46,6 @@
(catch Exception _
nil)))
-
(defn day-of-week-seq [day]
(let [next-day (loop [d (local-now)]
(if (= (time/day-of-week d) day)
@@ -57,10 +56,8 @@
(defn local-today []
(coerce/in-time-zone (time/now) (time/time-zone-for-id "America/Los_Angeles")))
-
(defn last-monday []
(loop [current (local-today)]
(if (= 1 (time/day-of-week current))
current
(recur (time/minus current (time/days 1))))))
-
\ No newline at end of file
diff --git a/src/clj/auto_ap/yodlee/core2.clj b/src/clj/auto_ap/yodlee/core2.clj
index 087525a8..0b9816a9 100644
--- a/src/clj/auto_ap/yodlee/core2.clj
+++ b/src/clj/auto_ap/yodlee/core2.clj
@@ -57,107 +57,99 @@
(defn login-cobrand []
(retry-thrice
- (fn []
- (-> (str (:yodlee2-base-url env) "/auth/token")
- (client/post (merge {:headers (assoc base-headers
- "loginName" (:yodlee2-admin-user env)
- "Content-Type" "application/x-www-form-urlencoded")
- :body (str "clientId=" (:yodlee2-client-id env) "&secret=" (:yodlee2-client-secret env))
- :as :json}
- other-config)
- )
- :body
- :token
- :accessToken))))
+ (fn []
+ (-> (str (:yodlee2-base-url env) "/auth/token")
+ (client/post (merge {:headers (assoc base-headers
+ "loginName" (:yodlee2-admin-user env)
+ "Content-Type" "application/x-www-form-urlencoded")
+ :body (str "clientId=" (:yodlee2-client-id env) "&secret=" (:yodlee2-client-secret env))
+ :as :json}
+ other-config))
+ :body
+ :token
+ :accessToken))))
(defn login-user [client-code]
(retry-thrice
- (fn []
- (alog/info ::logging-in :client client-code)
- (-> (str (:yodlee2-base-url env) "/auth/token")
- (client/post (merge {:headers (assoc base-headers
- "loginName" (if (:yodlee2-test-user env)
- (:yodlee2-test-user env)
- (if (<= (count client-code) 3)
- (str client-code client-code)
- client-code))
- "Content-Type" "application/x-www-form-urlencoded")
- :body (str "clientId=" (:yodlee2-client-id env) "&secret=" (:yodlee2-client-secret env))
- :as :json}
- other-config)
- )
- :body
- :token
- :accessToken))))
+ (fn []
+ (alog/info ::logging-in :client client-code)
+ (-> (str (:yodlee2-base-url env) "/auth/token")
+ (client/post (merge {:headers (assoc base-headers
+ "loginName" (if (:yodlee2-test-user env)
+ (:yodlee2-test-user env)
+ (if (<= (count client-code) 3)
+ (str client-code client-code)
+ client-code))
+ "Content-Type" "application/x-www-form-urlencoded")
+ :body (str "clientId=" (:yodlee2-client-id env) "&secret=" (:yodlee2-client-secret env))
+ :as :json}
+ other-config))
+ :body
+ :token
+ :accessToken))))
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
-(defn get-accounts [client-code ]
+(defn get-accounts [client-code]
(let [cob-session (login-user (client-code->login client-code))]
- (-> (str (:yodlee2-base-url env) "/accounts")
- (client/get (merge {:headers (merge base-headers {"Authorization" (str "Bearer " cob-session)})
+ (-> (str (:yodlee2-base-url env) "/accounts")
+ (client/get (merge {:headers (merge base-headers {"Authorization" (str "Bearer " cob-session)})
:as :json}
other-config))
:body
:account)))
(defn get-accounts-for-provider-account [client-code provider-account-id]
- (try
+ (try
(let [cob-session (login-user (client-code->login client-code))]
- (-> (str (:yodlee2-base-url env) "/accounts?providerAccountId=" provider-account-id)
- (client/get (merge {:headers (merge base-headers {"Authorization" (auth-header cob-session)})
+ (-> (str (:yodlee2-base-url env) "/accounts?providerAccountId=" provider-account-id)
+ (client/get (merge {:headers (merge base-headers {"Authorization" (auth-header cob-session)})
:as :json}
other-config))
:body
:account))
(catch Exception e
- (alog/error ::error
+ (alog/error ::error
:error e)
[])))
-(defn get-provider-accounts [client-code ]
+(defn get-provider-accounts [client-code]
(retry-thrice
- (fn []
- (alog/info ::logging-in :client client-code)
- (let [cob-session (login-user (client-code->login client-code))]
- (-> (str (:yodlee2-base-url env) "/providerAccounts")
- (-> (client/get (merge {:headers (merge base-headers {"Authorization" (auth-header cob-session )})
- :as :json}
- other-config))
- :body
- :providerAccount))))))
-
-
+ (fn []
+ (alog/info ::logging-in :client client-code)
+ (let [cob-session (login-user (client-code->login client-code))]
+ (-> (str (:yodlee2-base-url env) "/providerAccounts")
+ (-> (client/get (merge {:headers (merge base-headers {"Authorization" (auth-header cob-session)})
+ :as :json}
+ other-config))
+ :body
+ :providerAccount))))))
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
(defn get-transactions [client-code]
(let [cob-session (login-user (client-code->login client-code))
batch-size 100
get-transaction-batch (fn [skip]
- (-> (str (:yodlee2-base-url env) "/transactions?top=" batch-size "&skip=" skip)
-
+ (-> (str (:yodlee2-base-url env) "/transactions?top=" batch-size "&skip=" skip)
- (client/get (merge {:headers (merge base-headers {"Authorization" (auth-header cob-session)})
+ (client/get (merge {:headers (merge base-headers {"Authorization" (auth-header cob-session)})
:as :json}
other-config))
:body
- :transaction
- ))]
+ :transaction))]
- (loop [transactions []
+ (loop [transactions []
skip 0]
(let [transaction-batch (get-transaction-batch skip)]
(if (seq transaction-batch)
(recur (concat transactions transaction-batch) (+ batch-size skip))
transactions)))))
-
-
(defn get-provider-account [client-code id]
(let [cob-session (login-user (client-code->login client-code))]
- (-> (str (:yodlee2-base-url env) "/providerAccounts/" id)
+ (-> (str (:yodlee2-base-url env) "/providerAccounts/" id)
- (client/get (merge {:headers (merge base-headers {"Authorization" (auth-header cob-session)})
+ (client/get (merge {:headers (merge base-headers {"Authorization" (auth-header cob-session)})
:as :json}
other-config))
:body
@@ -167,9 +159,9 @@
(defn get-provider-account-detail [client-code id]
(let [cob-session (login-user (client-code->login client-code))]
- (-> (str (:yodlee2-base-url env) "/providerAccounts/" id )
+ (-> (str (:yodlee2-base-url env) "/providerAccounts/" id)
- (client/get (merge {:headers (merge base-headers {"Authorization" (auth-header cob-session)})
+ (client/get (merge {:headers (merge base-headers {"Authorization" (auth-header cob-session)})
:query-params {"include" "credentials,preferences"}
:as :json}
other-config))
@@ -181,41 +173,34 @@
(defn update-provider-account [client-code pa]
(let [cob-session (login-user (client-code->login client-code))]
- (-> (str (:yodlee2-base-url env) "/providerAccounts?providerAccountIds=" pa)
+ (-> (str (:yodlee2-base-url env) "/providerAccounts?providerAccountIds=" pa)
- (client/put (merge {:headers (merge base-headers {"Authorization" (auth-header cob-session)})
+ (client/put (merge {:headers (merge base-headers {"Authorization" (auth-header cob-session)})
:body "{\"dataSetName\": [\"BASIC_AGG_DATA\"]}"
:as :json}
other-config)))))
-
-
-
-
-
(defn get-specific-transactions [client-code account]
(let [cob-session (login-user (client-code->login client-code))
batch-size 100
get-transaction-batch (fn [skip]
- (-> (str (:yodlee2-base-url env) "/transactions?top=" batch-size "&skip=" skip "&accountId=" account)
+ (-> (str (:yodlee2-base-url env) "/transactions?top=" batch-size "&skip=" skip "&accountId=" account)
- (client/get (merge {:headers (merge base-headers {"Authorization" (auth-header cob-session)})
+ (client/get (merge {:headers (merge base-headers {"Authorization" (auth-header cob-session)})
:as :json}
other-config))
:body
- :transaction
- ))]
+ :transaction))]
- (loop [transactions []
+ (loop [transactions []
skip 0]
(let [transaction-batch (get-transaction-batch skip)]
(if (seq transaction-batch)
(recur (concat transactions transaction-batch) (+ batch-size skip))
transactions)))))
-
(defn get-access-token [client-code]
- (try
+ (try
(alog/info ::getting-access-token :client client-code)
(let [cob-session (login-user (client-code->login client-code))]
(alog/info ::got-cob-session :cob-ession cob-session)
@@ -227,10 +212,9 @@
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
(defn create-user [client-code]
(let [cob-session (login-cobrand)]
- (-> (str (:yodlee2-base-url env) "/user/register")
- (client/post (merge {:headers (merge base-headers {"Authorization" (auth-header cob-session)})
- :body (json/write-str {"user" {
- "loginName" client-code
+ (-> (str (:yodlee2-base-url env) "/user/register")
+ (client/post (merge {:headers (merge base-headers {"Authorization" (auth-header cob-session)})
+ :body (json/write-str {"user" {"loginName" client-code
"email" "bryce@integreatconsult.com"
"name" {"first" client-code
"last" client-code}
@@ -247,10 +231,7 @@
other-config))
:body)))
-
-
-
-(defn get-provider-accounts-with-details [client-code ]
+(defn get-provider-accounts-with-details [client-code]
(let [provider-accounts (get-provider-accounts client-code)
concurrent 20
output-chan (async/chan)]
@@ -270,12 +251,11 @@
(get-accounts-for-provider-account client-code provider-account-id)]))
provider-account-ids)))
-
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
(defn get-provider-accounts-with-accounts [client-code]
(let [provider-accounts (by :id (get-provider-accounts-with-details client-code))
accounts (get-accounts-for-providers client-code (keys provider-accounts))]
- (->> accounts
+ (->> accounts
(reduce
(fn [provider-accounts [which accounts]]
(assoc-in provider-accounts [which :accounts] accounts))
@@ -285,9 +265,9 @@
(defn delete-provider-account [client-code id]
(let [cob-session (login-user (client-code->login client-code))]
- (-> (str (:yodlee2-base-url env) "/providerAccounts/" id )
+ (-> (str (:yodlee2-base-url env) "/providerAccounts/" id)
- (client/delete (merge {:headers (merge base-headers {"Authorization" (auth-header cob-session)})
+ (client/delete (merge {:headers (merge base-headers {"Authorization" (auth-header cob-session)})
:as :json}
other-config))
:body
@@ -301,23 +281,23 @@
([client-code provider-accounts]
(let [accounts (get-accounts-for-providers client-code (map :id provider-accounts))]
(map (fn [pa]
- (cond->
- {:yodlee-provider-account/id (:id pa)
- :yodlee-provider-account/status (:status pa)
- :yodlee-provider-account/detailed-status (or (-> pa :dataset first :additionalStatus) "unknown")
- :yodlee-provider-account/client [:client/code client-code]
-
- :yodlee-provider-account/accounts (mapv
- (fn [a]
- {:yodlee-account/id (:id a)
- :yodlee-account/name (str (:providerName a) " (" (:accountName a) ")")
- :yodlee-account/number (or (:accountNumber a) "Unknown")
- :yodlee-account/status (or (-> a :dataset first :additionalStatus) "unknown")
- :yodlee-account/available-balance (or (-> a :currentBalance :amount)
- 0.0)})
- (get accounts (:id pa)))}
+ (cond->
+ {:yodlee-provider-account/id (:id pa)
+ :yodlee-provider-account/status (:status pa)
+ :yodlee-provider-account/detailed-status (or (-> pa :dataset first :additionalStatus) "unknown")
+ :yodlee-provider-account/client [:client/code client-code]
- (-> pa :dataset first :lastUpdated) (assoc :yodlee-provider-account/last-updated (-> pa :dataset first :lastUpdated coerce/to-date)) ))
+ :yodlee-provider-account/accounts (mapv
+ (fn [a]
+ {:yodlee-account/id (:id a)
+ :yodlee-account/name (str (:providerName a) " (" (:accountName a) ")")
+ :yodlee-account/number (or (:accountNumber a) "Unknown")
+ :yodlee-account/status (or (-> a :dataset first :additionalStatus) "unknown")
+ :yodlee-account/available-balance (or (-> a :currentBalance :amount)
+ 0.0)})
+ (get accounts (:id pa)))}
+
+ (-> pa :dataset first :lastUpdated) (assoc :yodlee-provider-account/last-updated (-> pa :dataset first :lastUpdated coerce/to-date))))
provider-accounts))))
(defn refresh-provider-account [client-code id]
@@ -325,7 +305,7 @@
:client client-code
:provider-acconut-id id)
@(dc/transact conn (upsert-accounts-tx client-code
- [(get-provider-account client-code id)])))
+ [(get-provider-account client-code id)])))
(defn upsert-accounts []
(let [concurrent 20
@@ -335,29 +315,26 @@
(mapcat (fn [client]
(alog/info ::upserting-accounts
:client (:client/code client))
- (mu/with-context {:client-code (:client/code client)}
- (try
- (upsert-accounts-tx (:client/code client))
- (catch Exception e
- (alog/error ::error :error e :client (:client/code client)))))))
+ (mu/with-context {:client-code (:client/code client)}
+ (try
+ (upsert-accounts-tx (:client/code client))
+ (catch Exception e
+ (alog/error ::error :error e :client (:client/code client)))))))
(async/to-chan! (d-clients/get-all)))
(let [result (async/ (str (:yodlee2-base-url env) "/providerAccounts?providerAccountIds=" pa)
+ (-> (str (:yodlee2-base-url env) "/providerAccounts?providerAccountIds=" pa)
- (client/put (merge {:headers (merge base-headers {"Authorization" (auth-header (login-user (client-code->login client-code)))})
+ (client/put (merge {:headers (merge base-headers {"Authorization" (auth-header (login-user (client-code->login client-code)))})
:body (json/write-str data)
:as :json}
other-config)))
(catch Exception e
- (alog/error ::error :error e))) )
+ (alog/error ::error :error e))))
(defn reauthenticate-and-recache [client-code pa data]
(reauthenticate client-code pa data)
diff --git a/src/clj/user.clj b/src/clj/user.clj
index 6b7083c1..efbc6e5d 100644
--- a/src/clj/user.clj
+++ b/src/clj/user.clj
@@ -1,32 +1,32 @@
(ns user
- (:require [amazonica.aws.s3 :as s3]
- [auto-ap.server]
- [auto-ap.datomic :refer [conn pull-attr random-tempid]]
- [auto-ap.solr :as solr]
- [auto-ap.time :as atime]
- [auto-ap.utils :refer [by]]
- [clj-time.coerce :as c]
- [clj-time.core :as t]
- [clojure.core.async :as async]
- [auto-ap.handler :refer [app]]
- [ring.adapter.jetty :refer [run-jetty]]
- [clojure.data.csv :as csv]
- [clojure.java.io :as io]
- [clojure.pprint]
- [clojure.string :as str]
- [clojure.tools.namespace.repl :refer [refresh set-refresh-dirs]]
- [com.brunobonacci.mulog :as mu]
- [com.brunobonacci.mulog.buffer :as rb]
- [config.core :refer [env]]
- [datomic.api :as dc]
- [puget.printer :as puget]
- [datomic.api :as d]
- #_[figwheel.main.api]
- [hawk.core]
- [mount.core :as mount]
- [nrepl.middleware.print])
- (:import (org.apache.commons.io.input BOMInputStream)
- [org.eclipse.jetty.server.handler.gzip GzipHandler]))
+ (:require [amazonica.aws.s3 :as s3]
+ [auto-ap.server]
+ [auto-ap.datomic :refer [conn pull-attr random-tempid]]
+ [auto-ap.solr :as solr]
+ [auto-ap.time :as atime]
+ [auto-ap.utils :refer [by]]
+ [clj-time.coerce :as c]
+ [clj-time.core :as t]
+ [clojure.core.async :as async]
+ [auto-ap.handler :refer [app]]
+ [ring.adapter.jetty :refer [run-jetty]]
+ [clojure.data.csv :as csv]
+ [clojure.java.io :as io]
+ [clojure.pprint]
+ [clojure.string :as str]
+ [clojure.tools.namespace.repl :refer [refresh set-refresh-dirs]]
+ [com.brunobonacci.mulog :as mu]
+ [com.brunobonacci.mulog.buffer :as rb]
+ [config.core :refer [env]]
+ [datomic.api :as dc]
+ [puget.printer :as puget]
+ [datomic.api :as d]
+ #_[figwheel.main.api]
+ [hawk.core]
+ [mount.core :as mount]
+ [nrepl.middleware.print])
+ (:import (org.apache.commons.io.input BOMInputStream)
+ [org.eclipse.jetty.server.handler.gzip GzipHandler]))
(defn println-event [item]
#_(printf "%s: %s - %s:%s by %s\n"
@@ -44,8 +44,7 @@
item
:user)))
(when (= :auto-ap.logging/peek (:mulog/event-name item))
- (println "\u001B[31mTEST")
- )
+ (println "\u001B[31mTEST"))
(when (:error item)
(println (:error item)))
(puget/cprint (reduce
@@ -58,18 +57,15 @@
{:seq-limit 10})
(println))
-
(deftype DevPublisher [config buffer transform]
com.brunobonacci.mulog.publisher.PPublisher
(agent-buffer [_]
buffer)
-
(publish-delay [_]
200)
-
(publish [_ buffer]
;; items are pairs [offset - ]
(doseq [item (transform (map second (rb/items buffer)))]
@@ -77,8 +73,6 @@
(flush)
(rb/clear buffer)))
-
-
(defn dev-publisher
[{:keys [transform pretty?] :as config}]
(DevPublisher. config (rb/agent-buffer 10000) (or transform identity)))
@@ -87,8 +81,6 @@
[config]
(dev-publisher config))
-
-
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
(defn load-accounts [conn]
(let [[header & rows] (-> "master-account-list.csv" (io/resource) io/input-stream (BOMInputStream.) (io/reader) csv/read-csv)
@@ -161,7 +153,6 @@
(also-merge-txes also-merge old-account-id))
tx)))))
-
conj
[]
rows)]
@@ -192,7 +183,6 @@
'[(<= ?z 9999)]]}
(dc/db conn)))))
-
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
(defn find-conflicting-accounts []
(filter
@@ -226,8 +216,6 @@
:where [['?e :account-client-override/client '?client-id]]}
(dc/db conn) client-id)
-
-
_ (when-let [bad-rows (seq (->> rows
(group-by (fn [[_ account]]
account))
@@ -285,7 +273,6 @@
txes
#_@(d/transact conn txes)))
-
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
(defn fix-transactions-without-locations [client-code location]
(->>
@@ -307,7 +294,6 @@
accounts)))
vec))
-
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
(defn entity-history [i]
(vec (sort-by first (dc/q
@@ -342,17 +328,15 @@
{:start (- i 100)
:end (+ i 100)}))))
-
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
(defn start-db []
(mu/start-publisher! {:type :dev})
(mount.core/start (mount.core/only #{#'auto-ap.datomic/conn})))
-
(defn- auto-reset-handler [ctx event]
#_(require 'figwheel.main.api)
(binding [*ns* *ns*]
- (clojure.tools.namespace.repl/refresh)
+ (clojure.tools.namespace.repl/refresh)
ctx))
(defn auto-reset
@@ -363,11 +347,9 @@
(hawk.core/watch! [{:paths ["src/" "test/"]
:handler auto-reset-handler}]))
-
(defn start-http []
(mount.core/start (mount.core/only #{#'auto-ap.server/port #'auto-ap.server/jetty})))
-
(defn start-dev []
(set-refresh-dirs "src")
#_(clojure.tools.namespace.repl/disable-reload! (find-ns 'auto-ap.server))
@@ -392,7 +374,6 @@
(for [r data]
((apply juxt columns) r)))))
-
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
(defn find-queries [words]
(let [obj (s3/list-objects-v2 :bucket-name (:data-bucket env)
@@ -418,7 +399,6 @@
(println "failed " e)))
(async/> (dc/q '[:find ?i
@@ -537,7 +513,6 @@
(defn init-repl []
(set! nrepl.middleware.print/*print-fn* clojure.pprint/pprint))
-
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
(defn sample-ledger-import
([client-code]
@@ -564,7 +539,6 @@
a)
:separator \tab))))
-
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
(defn sample-manual-yodlee
([client-code]
@@ -582,8 +556,6 @@
["posted" d (str "Random Description - " id) "Travel" nil nil (- amount) nil nil nil nil nil (rand-nth bank-accounts) client-code])
:separator \tab))))
-
-
(defn index-solr
[]
(println "invoice")
diff --git a/src/cljc/auto_ap/client_routes.cljc b/src/cljc/auto_ap/client_routes.cljc
index 3a7f79fa..cb68c6d2 100644
--- a/src/cljc/auto_ap/client_routes.cljc
+++ b/src/cljc/auto_ap/client_routes.cljc
@@ -8,7 +8,7 @@
"payments/" :payments
"admin/" {"vendors" :admin-vendors}
"vendor/" {"new" :new-vendor}
-
+
"transactions/" {"" :transactions
"unapproved" :unapproved-transactions
"approved" :approved-transactions
diff --git a/src/cljc/auto_ap/expense_accounts.cljc b/src/cljc/auto_ap/expense_accounts.cljc
index 418a32cb..a2f7e49c 100644
--- a/src/cljc/auto_ap/expense_accounts.cljc
+++ b/src/cljc/auto_ap/expense_accounts.cljc
@@ -2,366 +2,366 @@
(def expense-accounts
{0 {:name "Uncategorized" :parent nil}
- 1100 { :name "1100 Cash and Bank Accounts" :parent nil :location "A" }
- 1101 { :name "A1101 Cash on Hand" :parent 1100 :location "A" }
- 1102 { :name "A1102 Petty Cash" :parent 1100 :location "A" }
- 1120 { :name "A1120 Bank 1" :parent 1100 :location "A" }
- 1121 { :name "A1121 Bank 2" :parent 1100 :location "A" }
- 1122 { :name "A1122 Bank 3" :parent 1100 :location "A" }
- 1123 { :name "A1123 Bank 4" :parent 1100 :location "A" }
- 1124 { :name "A1124 Bank 5" :parent 1100 :location "A" }
- 1125 { :name "A1125 Bank 6" :parent 1100 :location "A" }
- 1126 { :name "A1126 Bank 7" :parent 1100 :location "A" }
- 1127 { :name "A1127 Bank 8" :parent 1100 :location "A" }
- 1128 { :name "A1128 Bank 9" :parent 1100 :location "A" }
- 1129 { :name "A1129 Bank 10" :parent 1100 :location "A" }
- 1130 { :name "A1130 Bank 11" :parent 1100 :location "A" }
- 1131 { :name "A1131 Bank 12" :parent 1100 :location "A" }
- 1132 { :name "A1132 Bank 13" :parent 1100 :location "A" }
- 1133 { :name "A1133 Bank 14" :parent 1100 :location "A" }
- 1134 { :name "A1134 Bank 15" :parent 1100 :location "A" }
- 1135 { :name "A1135 Bank 16" :parent 1100 :location "A" }
- 1136 { :name "A1136 Bank 17" :parent 1100 :location "A" }
- 1137 { :name "A1137 Bank 18" :parent 1100 :location "A" }
- 1138 { :name "A1138 Bank 19" :parent 1100 :location "A" }
- 1139 { :name "A1139 Bank 20" :parent 1100 :location "A" }
- 1200 { :name "1200 Accounts Receivable" :parent nil :location "A" }
- 1210 { :name "A1210 CCP" :parent 1200 :location "A" }
- 1220 { :name "A1220 Invoice Receivable" :parent 1200 :location "A" }
- 1211 { :name "A1211 Catering Receivable" :parent 1200 :location "A" }
- 1230 { :name "A1230 Employee Loans and Advances" :parent nil :location "A" }
- 1240 { :name "A1240 Owner/ Investor Loans" :parent nil :location "A" }
- 1250 { :name "A1250 Tradeouts" :parent nil :location "A" }
- 1260 { :name "A1260 Chargebacks" :parent nil :location "A" }
- 1270 { :name "A1270 Allowance for Doubtful Accts" :parent nil :location "A" }
- 1280 { :name "A1280 Other AR 1" :parent nil :location "A" }
- 1281 { :name "A1281 Other AR 2" :parent 1280 :location "A" }
- 1282 { :name "A1282 Other AR 3" :parent 1280 :location "A" }
- 1283 { :name "A1283 Other AR 4" :parent 1280 :location "A" }
- 1284 { :name "A1284 Other AR 5" :parent 1280 :location "A" }
- 1285 { :name "A1285 Other AR 6" :parent 1280 :location "A" }
- 1286 { :name "A1286 Other AR 7" :parent 1280 :location "A" }
- 1287 { :name "A1287 Other AR 8" :parent 1280 :location "A" }
- 1288 { :name "A1288 Other AR 9" :parent 1280 :location "A" }
- 1298 { :name "A1298 Transfer in Process" :parent nil :location "A" }
- 1299 { :name "A1299 Receipts Split" :parent nil :location "A" }
- 1300 { :name "1300 Inventory" :parent nil :location "A" }
- 1310 { :name "L1 1310 Food Inventory" :parent 1300 :location "A" }
- 1330 { :name "L1 1330 Soft Beverage Inventory" :parent 1300 :location "A" }
- 1340 { :name "L1 1340 Liquor Inventory" :parent 1300 :location "A" }
- 1350 { :name "L1 1350 Beer Inventory" :parent 1300 :location "A" }
- 1360 { :name "L1 1360 Wine Inventory" :parent 1300 :location "A" }
- 1370 { :name "L1 1370 Paper Products Inventory" :parent 1300 :location "A" }
- 1380 { :name "L1 1380 Merchandise Inventory" :parent 1300 :location "A" }
- 1390 { :name "L1 1390 Supplies Inventory" :parent 1300 :location "A" }
- 1400 { :name "1400 Prepaid Expenses" :parent nil :location "A" }
- 1410 { :name "A1410 Insurance - Prepaid" :parent 1400 :location "A" }
- 1420 { :name "A1420 Taxes - Prepaid" :parent 1400 :location "A" }
- 1430 { :name "A1430 Licenses - Prepaid" :parent 1400 :location "A" }
- 1440 { :name "A1440 Other - Prepaid" :parent 1400 :location "A" }
- 1500 { :name "1500 Property and Equipment" :parent nil :location "A" }
- 1510 { :name "A1510 Land" :parent 1500 :location "A" }
- 1511 { :name "A1511 Land 2" :parent 1510 :location "A" }
- 1512 { :name "A1512 Land 3" :parent 1510 :location "A" }
- 1513 { :name "A1513 Land 4" :parent 1510 :location "A" }
- 1514 { :name "A1514 Land 5" :parent 1510 :location "A" }
- 1515 { :name "A1515 Land 6" :parent 1510 :location "A" }
- 1516 { :name "A1516 Land 7" :parent 1510 :location "A" }
- 1517 { :name "A1517 Land 8" :parent 1510 :location "A" }
- 1518 { :name "A1518 Land 9" :parent 1510 :location "A" }
- 1520 { :name "A1520 Building" :parent 1500 :location "A" }
- 1521 { :name "A1521 Building 2" :parent 1520 :location "A" }
- 1522 { :name "A1522 Building 3" :parent 1520 :location "A" }
- 1523 { :name "A1523 Building 4" :parent 1520 :location "A" }
- 1524 { :name "A1524 Building 5" :parent 1520 :location "A" }
- 1525 { :name "A1525 Building 6" :parent 1520 :location "A" }
- 1526 { :name "A1526 Building 7" :parent 1520 :location "A" }
- 1527 { :name "A1527 Building 8" :parent 1520 :location "A" }
- 1528 { :name "A1528 Building 9" :parent 1520 :location "A" }
- 1530 { :name "A1530 Leasehold Improvements" :parent 1500 :location "A" }
- 1531 { :name "A1531 Leasehold Improv 2" :parent 1530 :location "A" }
- 1532 { :name "A1532 Leasehold Improv 3" :parent 1530 :location "A" }
- 1533 { :name "A1533 Leasehold Improv 4" :parent 1530 :location "A" }
- 1534 { :name "A1534 Leasehold Improv 5" :parent 1530 :location "A" }
- 1535 { :name "A1535 Leasehold Improv 6" :parent 1530 :location "A" }
- 1536 { :name "A1536 Leasehold Improv 7" :parent 1530 :location "A" }
- 1537 { :name "A1537 Leasehold Improv 8" :parent 1530 :location "A" }
- 1538 { :name "A1538 Leasehold Improv 9" :parent 1530 :location "A" }
- 1540 { :name "A1540 Furniture and Equipment" :parent 1500 :location "A" }
- 1541 { :name "A1541 Kitchen Equipment" :parent 1540 :location "A" }
- 1542 { :name "A1542 Furniture and Equip 3" :parent 1540 :location "A" }
- 1543 { :name "A1543 Furniture and Equip 3" :parent 1540 :location "A" }
- 1544 { :name "A1544 Furniture and Equip 4" :parent 1540 :location "A" }
- 1545 { :name "A1545 Furniture and Equip 5" :parent 1540 :location "A" }
- 1546 { :name "A1546 Furniture and Equip 6" :parent 1540 :location "A" }
- 1547 { :name "A1547 Furniture and Equip 7" :parent 1540 :location "A" }
- 1548 { :name "A1548 Furniture and Equip 8" :parent 1540 :location "A" }
- 1550 { :name "A1550 Autos and Trucks" :parent 1500 :location "A" }
- 1551 { :name "A1551 Furniture and Equip 2" :parent 1550 :location "A" }
- 1552 { :name "A1552 Furniture and Equip 3" :parent 1550 :location "A" }
- 1553 { :name "A1553 Furniture and Equip 4" :parent 1550 :location "A" }
- 1554 { :name "A1554 Furniture and Equip 5" :parent 1550 :location "A" }
- 1555 { :name "A1555 Furniture and Equip 6" :parent 1550 :location "A" }
- 1556 { :name "A1556 Furniture and Equip 7" :parent 1550 :location "A" }
- 1557 { :name "A1557 Furniture and Equip 8" :parent 1550 :location "A" }
- 1558 { :name "A1558 Furniture and Equip 9" :parent 1550 :location "A" }
- 1560 { :name "A1560 Construction in Progress" :parent 1500 :location "A" }
- 1590 { :name "A1590 Accum Depr - Building" :parent 1500 :location "A" }
- 1592 { :name "A1592 Accum Depr - Leasehold Improvements" :parent 1590 :location "A" }
- 1594 { :name "A1594 Accum Depr - Furniture and Equipment" :parent 1590 :location "A" }
- 1596 { :name "A1596 Accum Depr - Autos and Trucks" :parent 1590 :location "A" }
- 1600 { :name "1600 Intangible Assets" :parent nil :location "A" }
- 1610 { :name "A1610 Deposits" :parent 1600 :location "A" }
- 1620 { :name "A1620 Organization Costs" :parent 1600 :location "A" }
- 1630 { :name "A1630 Start Up Costs" :parent 1600 :location "A" }
- 1631 { :name "A1631 Start Up Costs 2" :parent 1630 :location "A" }
- 1632 { :name "A1632 Start Up Costs 3" :parent 1630 :location "A" }
- 1633 { :name "A1633 Start Up Costs 4" :parent 1630 :location "A" }
- 1634 { :name "A1634 Start Up Costs 5" :parent 1630 :location "A" }
- 1635 { :name "A1635 Start Up Costs 6" :parent 1630 :location "A" }
- 1636 { :name "A1636 Start Up Costs 7" :parent 1630 :location "A" }
- 1637 { :name "A1637 Start Up Costs 8" :parent 1630 :location "A" }
- 1638 { :name "A1638 Start Up Costs 9" :parent 1630 :location "A" }
- 1640 { :name "A1640 Liquor License" :parent 1600 :location "A" }
- 1641 { :name "A1641 Liquor License 2" :parent 1640 :location "A" }
- 1642 { :name "A1642 Liquor License 3" :parent 1640 :location "A" }
- 1643 { :name "A1643 Liquor License 4" :parent 1640 :location "A" }
- 1644 { :name "A1644 Liquor License 5" :parent 1640 :location "A" }
- 1645 { :name "A1645 Liquor License 6" :parent 1640 :location "A" }
- 1646 { :name "A1646 Liquor License 7" :parent 1640 :location "A" }
- 1647 { :name "A1647 Liquor License 8" :parent 1640 :location "A" }
- 1648 { :name "A1648 Liquor License 9" :parent 1640 :location "A" }
- 1650 { :name "A1650 Other Intangible" :parent 1600 :location "A" }
- 1651 { :name "A1651 Other Intengible 2" :parent 1650 :location "A" }
- 1652 { :name "A1652 Other Intengible 3" :parent 1650 :location "A" }
- 1653 { :name "A1653 Other Intengible 4" :parent 1650 :location "A" }
- 1654 { :name "A1654 Other Intengible 5" :parent 1650 :location "A" }
- 1655 { :name "A1655 Other Intengible 6" :parent 1650 :location "A" }
- 1656 { :name "A1656 Other Intengible 7" :parent 1650 :location "A" }
- 1657 { :name "A1657 Other Intengible 8" :parent 1650 :location "A" }
- 1658 { :name "A1658 Other Intengible 9" :parent 1650 :location "A" }
- 1690 { :name "A1690 Accum Amortization" :parent 1500 :location "A" }
- 2100 { :name "2100 Accounts Payable" :parent nil :location "A" }
- 2110 { :name "A2110 Bills Payable" :parent 2100 :location "A" }
- 2190 { :name "A2190 Payroll Payable" :parent 2100 :location "A" }
- 2200 { :name "A2200 Payroll Taxes Payable" :parent nil :location "A" }
- 2201 { :name "A2201 Federal Payroll Taxes Payable" :parent 2200 :location "A" }
- 2202 { :name "A2202 CA Payroll Taxes Payable" :parent 2200 :location "A" }
- 2300 { :name "A2300 Sales Taxes Payable" :parent nil :location "A" }
- 2400 { :name "2400 Accrued Expenses" :parent nil :location "A" }
- 2410 { :name "A2410 Salaries Accrued" :parent 2400 :location "A" }
- 2420 { :name "A2420 Vacation Pay Accrued" :parent 2400 :location "A" }
- 2430 { :name "A2430 Rent Accrued" :parent 2400 :location "A" }
- 2440 { :name "A2440 Utilities Accrued" :parent 2400 :location "A" }
- 2500 { :name "2500 Other Liabilities" :parent nil :location "A" }
- 2510 { :name "A2510 Gift Card Outstanding" :parent 2500 :location "A" }
- 2511 { :name "A2511 Payroll Outstanding" :parent 2510 :location "A" }
- 2520 { :name "A2520 Customer Deposits" :parent 2500 :location "A" }
- 2521 { :name "A2521 Promos Outstanding" :parent 2520 :location "A" }
- 2530 { :name "A2530 Due to Employees" :parent 2500 :location "A" }
- 2531 { :name "A2531 Tips Payable" :parent 2530 :location "A" }
- 2540 { :name "A2540 Other Current Liabilites" :parent 2500 :location "A" }
- 2600 { :name "2600 Split Accounts" :parent nil :location "A" }
- 2690 { :name "A2690 Splits" :parent 2600 :location "A" }
- 2691 { :name "A2691 Payroll Split" :parent 2690 :location "A" }
- 2692 { :name "A2692 12->13 Splits" :parent 2690 :location "A" }
- 2700 { :name "2700 Current Portion of Long-Term Debt" :parent nil :location "A" }
- 2710 { :name "2710 Current Portion of Notes Payable" :parent 2700 :location "A" }
- 2800 { :name "2800 Notes Payable" :parent nil :location "A" }
- 2810 { :name "A2810 Notes Payable" :parent 2800 :location "A" }
- 2811 { :name "A2811 Note Payable 2" :parent 2810 :location "A" }
- 2812 { :name "A2812 Note Payable 3" :parent 2810 :location "A" }
- 2813 { :name "A2813 Note Payable 4" :parent 2810 :location "A" }
- 2814 { :name "A2814 Note Payable 5" :parent 2810 :location "A" }
- 2815 { :name "A2815 Note Payable 6" :parent 2810 :location "A" }
- 2816 { :name "A2816 Note Payable 7" :parent 2810 :location "A" }
- 2817 { :name "A2817 Note Payable 8" :parent 2810 :location "A" }
- 2818 { :name "A2818 Note Payable 9" :parent 2810 :location "A" }
- 2850 { :name "A2850 Loan from Member/ Partner/ Shareholder" :parent 2800 :location "A" }
- 2851 { :name "A2851 Owner Loan 2" :parent 2850 :location "A" }
- 2852 { :name "A2852 Owner Loan 3" :parent 2850 :location "A" }
- 2853 { :name "A2853 Owner Loan 4" :parent 2850 :location "A" }
- 2854 { :name "A2854 Owner Loan 5" :parent 2850 :location "A" }
- 2855 { :name "A2855 Owner Loan 6" :parent 2850 :location "A" }
- 2856 { :name "A2856 Owner Loan 7" :parent 2850 :location "A" }
- 2857 { :name "A2857 Owner Loan 8" :parent 2850 :location "A" }
- 2858 { :name "A2858 Owner Loan 9" :parent 2850 :location "A" }
- 3001 { :name "A3001 Opening Balance" :parent nil :location "A" }
- 3100 { :name "A3100 Common Stock" :parent nil :location "A" }
- 3200 { :name "A3200 Paid in Capital" :parent nil :location "A" }
- 3201 { :name "A3201 Contributions - Owner 1" :parent 3200 :location "A" }
- 3202 { :name "A3202 Contributions - Owner 2" :parent 3200 :location "A" }
- 3203 { :name "A3203 Contributions - Owner 3" :parent 3200 :location "A" }
- 3204 { :name "A3204 Contributions - Owner 4" :parent 3200 :location "A" }
- 3205 { :name "A3205 Contributions - Owner 5" :parent 3200 :location "A" }
- 3206 { :name "A3206 Contributions - Owner 6" :parent 3200 :location "A" }
- 3207 { :name "A3207 Contributions - Owner 7" :parent 3200 :location "A" }
- 3208 { :name "A3208 Contributions - Owner 8" :parent 3200 :location "A" }
- 3209 { :name "A3209 Contributions - Owner 9" :parent 3200 :location "A" }
- 3210 { :name "A3210 Contributions - Owner 10" :parent 3200 :location "A" }
- 3300 { :name "A3300 Contributions" :parent nil :location "A" }
- 3301 { :name "A3301 Contributions - Owner 1" :parent 3300 :location "A" }
- 3302 { :name "A3302 Contributions - Owner 2" :parent 3300 :location "A" }
- 3303 { :name "A3303 Contributions - Owner 3" :parent 3300 :location "A" }
- 3304 { :name "A3304 Contributions - Owner 4" :parent 3300 :location "A" }
- 3305 { :name "A3305 Contributions - Owner 5" :parent 3300 :location "A" }
- 3306 { :name "A3306 Contributions - Owner 6" :parent 3300 :location "A" }
- 3307 { :name "A3307 Contributions - Owner 7" :parent 3300 :location "A" }
- 3308 { :name "A3308 Contributions - Owner 8" :parent 3300 :location "A" }
- 3309 { :name "A3309 Contributions - Owner 9" :parent 3300 :location "A" }
- 3310 { :name "A3310 Contributions - Owner 10" :parent 3300 :location "A" }
- 3400 { :name "A3400 Retained Earnings" :parent nil :location "A" }
- 3401 { :name "A3401 Undistributed Net Inc / Loss" :parent 3400 :location "A" }
- 5110 {:name "Food Cost" :parent nil}
- 5111 {:name "Proteins Cost" :parent 5110}
- 5112 {:name "Beef/ Pork Costs" :parent 5111}
+ 1100 {:name "1100 Cash and Bank Accounts" :parent nil :location "A"}
+ 1101 {:name "A1101 Cash on Hand" :parent 1100 :location "A"}
+ 1102 {:name "A1102 Petty Cash" :parent 1100 :location "A"}
+ 1120 {:name "A1120 Bank 1" :parent 1100 :location "A"}
+ 1121 {:name "A1121 Bank 2" :parent 1100 :location "A"}
+ 1122 {:name "A1122 Bank 3" :parent 1100 :location "A"}
+ 1123 {:name "A1123 Bank 4" :parent 1100 :location "A"}
+ 1124 {:name "A1124 Bank 5" :parent 1100 :location "A"}
+ 1125 {:name "A1125 Bank 6" :parent 1100 :location "A"}
+ 1126 {:name "A1126 Bank 7" :parent 1100 :location "A"}
+ 1127 {:name "A1127 Bank 8" :parent 1100 :location "A"}
+ 1128 {:name "A1128 Bank 9" :parent 1100 :location "A"}
+ 1129 {:name "A1129 Bank 10" :parent 1100 :location "A"}
+ 1130 {:name "A1130 Bank 11" :parent 1100 :location "A"}
+ 1131 {:name "A1131 Bank 12" :parent 1100 :location "A"}
+ 1132 {:name "A1132 Bank 13" :parent 1100 :location "A"}
+ 1133 {:name "A1133 Bank 14" :parent 1100 :location "A"}
+ 1134 {:name "A1134 Bank 15" :parent 1100 :location "A"}
+ 1135 {:name "A1135 Bank 16" :parent 1100 :location "A"}
+ 1136 {:name "A1136 Bank 17" :parent 1100 :location "A"}
+ 1137 {:name "A1137 Bank 18" :parent 1100 :location "A"}
+ 1138 {:name "A1138 Bank 19" :parent 1100 :location "A"}
+ 1139 {:name "A1139 Bank 20" :parent 1100 :location "A"}
+ 1200 {:name "1200 Accounts Receivable" :parent nil :location "A"}
+ 1210 {:name "A1210 CCP" :parent 1200 :location "A"}
+ 1220 {:name "A1220 Invoice Receivable" :parent 1200 :location "A"}
+ 1211 {:name "A1211 Catering Receivable" :parent 1200 :location "A"}
+ 1230 {:name "A1230 Employee Loans and Advances" :parent nil :location "A"}
+ 1240 {:name "A1240 Owner/ Investor Loans" :parent nil :location "A"}
+ 1250 {:name "A1250 Tradeouts" :parent nil :location "A"}
+ 1260 {:name "A1260 Chargebacks" :parent nil :location "A"}
+ 1270 {:name "A1270 Allowance for Doubtful Accts" :parent nil :location "A"}
+ 1280 {:name "A1280 Other AR 1" :parent nil :location "A"}
+ 1281 {:name "A1281 Other AR 2" :parent 1280 :location "A"}
+ 1282 {:name "A1282 Other AR 3" :parent 1280 :location "A"}
+ 1283 {:name "A1283 Other AR 4" :parent 1280 :location "A"}
+ 1284 {:name "A1284 Other AR 5" :parent 1280 :location "A"}
+ 1285 {:name "A1285 Other AR 6" :parent 1280 :location "A"}
+ 1286 {:name "A1286 Other AR 7" :parent 1280 :location "A"}
+ 1287 {:name "A1287 Other AR 8" :parent 1280 :location "A"}
+ 1288 {:name "A1288 Other AR 9" :parent 1280 :location "A"}
+ 1298 {:name "A1298 Transfer in Process" :parent nil :location "A"}
+ 1299 {:name "A1299 Receipts Split" :parent nil :location "A"}
+ 1300 {:name "1300 Inventory" :parent nil :location "A"}
+ 1310 {:name "L1 1310 Food Inventory" :parent 1300 :location "A"}
+ 1330 {:name "L1 1330 Soft Beverage Inventory" :parent 1300 :location "A"}
+ 1340 {:name "L1 1340 Liquor Inventory" :parent 1300 :location "A"}
+ 1350 {:name "L1 1350 Beer Inventory" :parent 1300 :location "A"}
+ 1360 {:name "L1 1360 Wine Inventory" :parent 1300 :location "A"}
+ 1370 {:name "L1 1370 Paper Products Inventory" :parent 1300 :location "A"}
+ 1380 {:name "L1 1380 Merchandise Inventory" :parent 1300 :location "A"}
+ 1390 {:name "L1 1390 Supplies Inventory" :parent 1300 :location "A"}
+ 1400 {:name "1400 Prepaid Expenses" :parent nil :location "A"}
+ 1410 {:name "A1410 Insurance - Prepaid" :parent 1400 :location "A"}
+ 1420 {:name "A1420 Taxes - Prepaid" :parent 1400 :location "A"}
+ 1430 {:name "A1430 Licenses - Prepaid" :parent 1400 :location "A"}
+ 1440 {:name "A1440 Other - Prepaid" :parent 1400 :location "A"}
+ 1500 {:name "1500 Property and Equipment" :parent nil :location "A"}
+ 1510 {:name "A1510 Land" :parent 1500 :location "A"}
+ 1511 {:name "A1511 Land 2" :parent 1510 :location "A"}
+ 1512 {:name "A1512 Land 3" :parent 1510 :location "A"}
+ 1513 {:name "A1513 Land 4" :parent 1510 :location "A"}
+ 1514 {:name "A1514 Land 5" :parent 1510 :location "A"}
+ 1515 {:name "A1515 Land 6" :parent 1510 :location "A"}
+ 1516 {:name "A1516 Land 7" :parent 1510 :location "A"}
+ 1517 {:name "A1517 Land 8" :parent 1510 :location "A"}
+ 1518 {:name "A1518 Land 9" :parent 1510 :location "A"}
+ 1520 {:name "A1520 Building" :parent 1500 :location "A"}
+ 1521 {:name "A1521 Building 2" :parent 1520 :location "A"}
+ 1522 {:name "A1522 Building 3" :parent 1520 :location "A"}
+ 1523 {:name "A1523 Building 4" :parent 1520 :location "A"}
+ 1524 {:name "A1524 Building 5" :parent 1520 :location "A"}
+ 1525 {:name "A1525 Building 6" :parent 1520 :location "A"}
+ 1526 {:name "A1526 Building 7" :parent 1520 :location "A"}
+ 1527 {:name "A1527 Building 8" :parent 1520 :location "A"}
+ 1528 {:name "A1528 Building 9" :parent 1520 :location "A"}
+ 1530 {:name "A1530 Leasehold Improvements" :parent 1500 :location "A"}
+ 1531 {:name "A1531 Leasehold Improv 2" :parent 1530 :location "A"}
+ 1532 {:name "A1532 Leasehold Improv 3" :parent 1530 :location "A"}
+ 1533 {:name "A1533 Leasehold Improv 4" :parent 1530 :location "A"}
+ 1534 {:name "A1534 Leasehold Improv 5" :parent 1530 :location "A"}
+ 1535 {:name "A1535 Leasehold Improv 6" :parent 1530 :location "A"}
+ 1536 {:name "A1536 Leasehold Improv 7" :parent 1530 :location "A"}
+ 1537 {:name "A1537 Leasehold Improv 8" :parent 1530 :location "A"}
+ 1538 {:name "A1538 Leasehold Improv 9" :parent 1530 :location "A"}
+ 1540 {:name "A1540 Furniture and Equipment" :parent 1500 :location "A"}
+ 1541 {:name "A1541 Kitchen Equipment" :parent 1540 :location "A"}
+ 1542 {:name "A1542 Furniture and Equip 3" :parent 1540 :location "A"}
+ 1543 {:name "A1543 Furniture and Equip 3" :parent 1540 :location "A"}
+ 1544 {:name "A1544 Furniture and Equip 4" :parent 1540 :location "A"}
+ 1545 {:name "A1545 Furniture and Equip 5" :parent 1540 :location "A"}
+ 1546 {:name "A1546 Furniture and Equip 6" :parent 1540 :location "A"}
+ 1547 {:name "A1547 Furniture and Equip 7" :parent 1540 :location "A"}
+ 1548 {:name "A1548 Furniture and Equip 8" :parent 1540 :location "A"}
+ 1550 {:name "A1550 Autos and Trucks" :parent 1500 :location "A"}
+ 1551 {:name "A1551 Furniture and Equip 2" :parent 1550 :location "A"}
+ 1552 {:name "A1552 Furniture and Equip 3" :parent 1550 :location "A"}
+ 1553 {:name "A1553 Furniture and Equip 4" :parent 1550 :location "A"}
+ 1554 {:name "A1554 Furniture and Equip 5" :parent 1550 :location "A"}
+ 1555 {:name "A1555 Furniture and Equip 6" :parent 1550 :location "A"}
+ 1556 {:name "A1556 Furniture and Equip 7" :parent 1550 :location "A"}
+ 1557 {:name "A1557 Furniture and Equip 8" :parent 1550 :location "A"}
+ 1558 {:name "A1558 Furniture and Equip 9" :parent 1550 :location "A"}
+ 1560 {:name "A1560 Construction in Progress" :parent 1500 :location "A"}
+ 1590 {:name "A1590 Accum Depr - Building" :parent 1500 :location "A"}
+ 1592 {:name "A1592 Accum Depr - Leasehold Improvements" :parent 1590 :location "A"}
+ 1594 {:name "A1594 Accum Depr - Furniture and Equipment" :parent 1590 :location "A"}
+ 1596 {:name "A1596 Accum Depr - Autos and Trucks" :parent 1590 :location "A"}
+ 1600 {:name "1600 Intangible Assets" :parent nil :location "A"}
+ 1610 {:name "A1610 Deposits" :parent 1600 :location "A"}
+ 1620 {:name "A1620 Organization Costs" :parent 1600 :location "A"}
+ 1630 {:name "A1630 Start Up Costs" :parent 1600 :location "A"}
+ 1631 {:name "A1631 Start Up Costs 2" :parent 1630 :location "A"}
+ 1632 {:name "A1632 Start Up Costs 3" :parent 1630 :location "A"}
+ 1633 {:name "A1633 Start Up Costs 4" :parent 1630 :location "A"}
+ 1634 {:name "A1634 Start Up Costs 5" :parent 1630 :location "A"}
+ 1635 {:name "A1635 Start Up Costs 6" :parent 1630 :location "A"}
+ 1636 {:name "A1636 Start Up Costs 7" :parent 1630 :location "A"}
+ 1637 {:name "A1637 Start Up Costs 8" :parent 1630 :location "A"}
+ 1638 {:name "A1638 Start Up Costs 9" :parent 1630 :location "A"}
+ 1640 {:name "A1640 Liquor License" :parent 1600 :location "A"}
+ 1641 {:name "A1641 Liquor License 2" :parent 1640 :location "A"}
+ 1642 {:name "A1642 Liquor License 3" :parent 1640 :location "A"}
+ 1643 {:name "A1643 Liquor License 4" :parent 1640 :location "A"}
+ 1644 {:name "A1644 Liquor License 5" :parent 1640 :location "A"}
+ 1645 {:name "A1645 Liquor License 6" :parent 1640 :location "A"}
+ 1646 {:name "A1646 Liquor License 7" :parent 1640 :location "A"}
+ 1647 {:name "A1647 Liquor License 8" :parent 1640 :location "A"}
+ 1648 {:name "A1648 Liquor License 9" :parent 1640 :location "A"}
+ 1650 {:name "A1650 Other Intangible" :parent 1600 :location "A"}
+ 1651 {:name "A1651 Other Intengible 2" :parent 1650 :location "A"}
+ 1652 {:name "A1652 Other Intengible 3" :parent 1650 :location "A"}
+ 1653 {:name "A1653 Other Intengible 4" :parent 1650 :location "A"}
+ 1654 {:name "A1654 Other Intengible 5" :parent 1650 :location "A"}
+ 1655 {:name "A1655 Other Intengible 6" :parent 1650 :location "A"}
+ 1656 {:name "A1656 Other Intengible 7" :parent 1650 :location "A"}
+ 1657 {:name "A1657 Other Intengible 8" :parent 1650 :location "A"}
+ 1658 {:name "A1658 Other Intengible 9" :parent 1650 :location "A"}
+ 1690 {:name "A1690 Accum Amortization" :parent 1500 :location "A"}
+ 2100 {:name "2100 Accounts Payable" :parent nil :location "A"}
+ 2110 {:name "A2110 Bills Payable" :parent 2100 :location "A"}
+ 2190 {:name "A2190 Payroll Payable" :parent 2100 :location "A"}
+ 2200 {:name "A2200 Payroll Taxes Payable" :parent nil :location "A"}
+ 2201 {:name "A2201 Federal Payroll Taxes Payable" :parent 2200 :location "A"}
+ 2202 {:name "A2202 CA Payroll Taxes Payable" :parent 2200 :location "A"}
+ 2300 {:name "A2300 Sales Taxes Payable" :parent nil :location "A"}
+ 2400 {:name "2400 Accrued Expenses" :parent nil :location "A"}
+ 2410 {:name "A2410 Salaries Accrued" :parent 2400 :location "A"}
+ 2420 {:name "A2420 Vacation Pay Accrued" :parent 2400 :location "A"}
+ 2430 {:name "A2430 Rent Accrued" :parent 2400 :location "A"}
+ 2440 {:name "A2440 Utilities Accrued" :parent 2400 :location "A"}
+ 2500 {:name "2500 Other Liabilities" :parent nil :location "A"}
+ 2510 {:name "A2510 Gift Card Outstanding" :parent 2500 :location "A"}
+ 2511 {:name "A2511 Payroll Outstanding" :parent 2510 :location "A"}
+ 2520 {:name "A2520 Customer Deposits" :parent 2500 :location "A"}
+ 2521 {:name "A2521 Promos Outstanding" :parent 2520 :location "A"}
+ 2530 {:name "A2530 Due to Employees" :parent 2500 :location "A"}
+ 2531 {:name "A2531 Tips Payable" :parent 2530 :location "A"}
+ 2540 {:name "A2540 Other Current Liabilites" :parent 2500 :location "A"}
+ 2600 {:name "2600 Split Accounts" :parent nil :location "A"}
+ 2690 {:name "A2690 Splits" :parent 2600 :location "A"}
+ 2691 {:name "A2691 Payroll Split" :parent 2690 :location "A"}
+ 2692 {:name "A2692 12->13 Splits" :parent 2690 :location "A"}
+ 2700 {:name "2700 Current Portion of Long-Term Debt" :parent nil :location "A"}
+ 2710 {:name "2710 Current Portion of Notes Payable" :parent 2700 :location "A"}
+ 2800 {:name "2800 Notes Payable" :parent nil :location "A"}
+ 2810 {:name "A2810 Notes Payable" :parent 2800 :location "A"}
+ 2811 {:name "A2811 Note Payable 2" :parent 2810 :location "A"}
+ 2812 {:name "A2812 Note Payable 3" :parent 2810 :location "A"}
+ 2813 {:name "A2813 Note Payable 4" :parent 2810 :location "A"}
+ 2814 {:name "A2814 Note Payable 5" :parent 2810 :location "A"}
+ 2815 {:name "A2815 Note Payable 6" :parent 2810 :location "A"}
+ 2816 {:name "A2816 Note Payable 7" :parent 2810 :location "A"}
+ 2817 {:name "A2817 Note Payable 8" :parent 2810 :location "A"}
+ 2818 {:name "A2818 Note Payable 9" :parent 2810 :location "A"}
+ 2850 {:name "A2850 Loan from Member/ Partner/ Shareholder" :parent 2800 :location "A"}
+ 2851 {:name "A2851 Owner Loan 2" :parent 2850 :location "A"}
+ 2852 {:name "A2852 Owner Loan 3" :parent 2850 :location "A"}
+ 2853 {:name "A2853 Owner Loan 4" :parent 2850 :location "A"}
+ 2854 {:name "A2854 Owner Loan 5" :parent 2850 :location "A"}
+ 2855 {:name "A2855 Owner Loan 6" :parent 2850 :location "A"}
+ 2856 {:name "A2856 Owner Loan 7" :parent 2850 :location "A"}
+ 2857 {:name "A2857 Owner Loan 8" :parent 2850 :location "A"}
+ 2858 {:name "A2858 Owner Loan 9" :parent 2850 :location "A"}
+ 3001 {:name "A3001 Opening Balance" :parent nil :location "A"}
+ 3100 {:name "A3100 Common Stock" :parent nil :location "A"}
+ 3200 {:name "A3200 Paid in Capital" :parent nil :location "A"}
+ 3201 {:name "A3201 Contributions - Owner 1" :parent 3200 :location "A"}
+ 3202 {:name "A3202 Contributions - Owner 2" :parent 3200 :location "A"}
+ 3203 {:name "A3203 Contributions - Owner 3" :parent 3200 :location "A"}
+ 3204 {:name "A3204 Contributions - Owner 4" :parent 3200 :location "A"}
+ 3205 {:name "A3205 Contributions - Owner 5" :parent 3200 :location "A"}
+ 3206 {:name "A3206 Contributions - Owner 6" :parent 3200 :location "A"}
+ 3207 {:name "A3207 Contributions - Owner 7" :parent 3200 :location "A"}
+ 3208 {:name "A3208 Contributions - Owner 8" :parent 3200 :location "A"}
+ 3209 {:name "A3209 Contributions - Owner 9" :parent 3200 :location "A"}
+ 3210 {:name "A3210 Contributions - Owner 10" :parent 3200 :location "A"}
+ 3300 {:name "A3300 Contributions" :parent nil :location "A"}
+ 3301 {:name "A3301 Contributions - Owner 1" :parent 3300 :location "A"}
+ 3302 {:name "A3302 Contributions - Owner 2" :parent 3300 :location "A"}
+ 3303 {:name "A3303 Contributions - Owner 3" :parent 3300 :location "A"}
+ 3304 {:name "A3304 Contributions - Owner 4" :parent 3300 :location "A"}
+ 3305 {:name "A3305 Contributions - Owner 5" :parent 3300 :location "A"}
+ 3306 {:name "A3306 Contributions - Owner 6" :parent 3300 :location "A"}
+ 3307 {:name "A3307 Contributions - Owner 7" :parent 3300 :location "A"}
+ 3308 {:name "A3308 Contributions - Owner 8" :parent 3300 :location "A"}
+ 3309 {:name "A3309 Contributions - Owner 9" :parent 3300 :location "A"}
+ 3310 {:name "A3310 Contributions - Owner 10" :parent 3300 :location "A"}
+ 3400 {:name "A3400 Retained Earnings" :parent nil :location "A"}
+ 3401 {:name "A3401 Undistributed Net Inc / Loss" :parent 3400 :location "A"}
+ 5110 {:name "Food Cost" :parent nil}
+ 5111 {:name "Proteins Cost" :parent 5110}
+ 5112 {:name "Beef/ Pork Costs" :parent 5111}
5113 {:name "Chicken/ Poultry Costs" :parent 5111}
5114 {:name "Seafood Costs" :parent 5111}
5120 {:name "Produce Costs" :parent 5110}
5130 {:name "Dairy Costs" :parent 5110}
5140 {:name "Bread and Bun Costs" :parent 5110}
- 5210 {:name "Soft Beverage Cost" :parent 5110}
- 5220 {:name "Coffee Costs" :parent 5110}
- 5310 {:name "Catering Cost" :parent 5110}
- 5400 {:name "Alcolhol Cost" :parent nil}
- 5410 {:name "Beer Cost" :parent 5400}
- 5411 {:name "CO2 Costs" :parent 5400}
- 5510 {:name "Wine Cost" :parent 5400}
- 5610 {:name "Liquor Cost" :parent 5400}
- 5700 {:name "Merchandise Cost" :parent nil}
- 5710 {:name "Merchandise Cost" :parent 5700}
- 5800 {:name "Other Operating Cost" :parent nil}
- 5810 {:name "Delivery Van Rental Cost" :parent 5810}
- 5900 {:name "Paper Cost" :parent nil}
- 5910 {:name "Paperware Cost" :parent 5900}
- 7100 {:name "Ops related" :parent nil}
- 7110 {:name "Banquet and Catering Supplies" :parent 7100}
- 7120 {:name "Bar Utensils and Supplies" :parent 7100}
- 7130 {:name "Glassware" :parent 7100}
- 7140 {:name "Tableware" :parent 7100}
- 7150 {:name "Paper and Packaging" :parent 7100}
- 7160 {:name "Security Guards" :parent 7100}
+ 5210 {:name "Soft Beverage Cost" :parent 5110}
+ 5220 {:name "Coffee Costs" :parent 5110}
+ 5310 {:name "Catering Cost" :parent 5110}
+ 5400 {:name "Alcolhol Cost" :parent nil}
+ 5410 {:name "Beer Cost" :parent 5400}
+ 5411 {:name "CO2 Costs" :parent 5400}
+ 5510 {:name "Wine Cost" :parent 5400}
+ 5610 {:name "Liquor Cost" :parent 5400}
+ 5700 {:name "Merchandise Cost" :parent nil}
+ 5710 {:name "Merchandise Cost" :parent 5700}
+ 5800 {:name "Other Operating Cost" :parent nil}
+ 5810 {:name "Delivery Van Rental Cost" :parent 5810}
+ 5900 {:name "Paper Cost" :parent nil}
+ 5910 {:name "Paperware Cost" :parent 5900}
+ 7100 {:name "Ops related" :parent nil}
+ 7110 {:name "Banquet and Catering Supplies" :parent 7100}
+ 7120 {:name "Bar Utensils and Supplies" :parent 7100}
+ 7130 {:name "Glassware" :parent 7100}
+ 7140 {:name "Tableware" :parent 7100}
+ 7150 {:name "Paper and Packaging" :parent 7100}
+ 7160 {:name "Security Guards" :parent 7100}
7200 {:name "Customer Related" :parent nil}
- 7210 {:name "Flowers and ST Decorations" :parent 7200}
- 7220 {:name "Menus" :parent 7200}
- 7225 {:name "In Store Printing" :parent 7200}
- 7230 {:name "Advertising" :parent 7200}
- 7235 {:name "Door Dash Advertising" :parent 7200}
- 7240 {:name "Cable Television" :parent 7200}
- 7242 {:name "Music Licensing Fees" :parent 7200}
- 7244 {:name "Bands and DJ's" :parent 7200}
- 7246 {:name "Entertainment - Other" :parent 7200}
- 7250 {:name "Reservation System" :parent 7200}
+ 7210 {:name "Flowers and ST Decorations" :parent 7200}
+ 7220 {:name "Menus" :parent 7200}
+ 7225 {:name "In Store Printing" :parent 7200}
+ 7230 {:name "Advertising" :parent 7200}
+ 7235 {:name "Door Dash Advertising" :parent 7200}
+ 7240 {:name "Cable Television" :parent 7200}
+ 7242 {:name "Music Licensing Fees" :parent 7200}
+ 7244 {:name "Bands and DJ's" :parent 7200}
+ 7246 {:name "Entertainment - Other" :parent 7200}
+ 7250 {:name "Reservation System" :parent 7200}
7300 {:name "Employee Related" :parent nil}
- 7310 {:name "Auto and Truck Expenses" :parent 7300}
- 7315 {:name "Freight and Fuel Charges" :parent 7300}
- 7320 {:name "Kitchen Supplies" :parent 7300}
- 7325 {:name "Kitchen Utensils and Smallwares" :parent 7300}
- 7330 {:name "Parking" :parent 7300}
- 7340 {:name "Uniforms" :parent 7300}
- 7350 {:name "Recruiting" :parent 7300}
- 7360 {:name "Employee Training" :parent 7300}
+ 7310 {:name "Auto and Truck Expenses" :parent 7300}
+ 7315 {:name "Freight and Fuel Charges" :parent 7300}
+ 7320 {:name "Kitchen Supplies" :parent 7300}
+ 7325 {:name "Kitchen Utensils and Smallwares" :parent 7300}
+ 7330 {:name "Parking" :parent 7300}
+ 7340 {:name "Uniforms" :parent 7300}
+ 7350 {:name "Recruiting" :parent 7300}
+ 7360 {:name "Employee Training" :parent 7300}
7400 {:name "Building and Equipment Related" :parent nil}
- 7410 {:name "Cleaning Supplies" :parent 7400}
- 7415 {:name "Contract Cleaning" :parent 7400}
- 7420 {:name "Short Term Equipment Rental" :parent 7400}
- 7430 {:name "Laundry and Drycleaning" :parent 7400}
- 7435 {:name "Linens" :parent 7400}
- 7440 {:name "Repairs to Building" :parent 7400}
- 7450 {:name "Building Cleaning & Maintenance" :parent 7400}
- 7455 {:name "Pest Control" :parent 7400}
- 7460 {:name "Repairs to Equipment" :parent 7400}
- 7461 {:name "Contract Labor" :parent 7400}
+ 7410 {:name "Cleaning Supplies" :parent 7400}
+ 7415 {:name "Contract Cleaning" :parent 7400}
+ 7420 {:name "Short Term Equipment Rental" :parent 7400}
+ 7430 {:name "Laundry and Drycleaning" :parent 7400}
+ 7435 {:name "Linens" :parent 7400}
+ 7440 {:name "Repairs to Building" :parent 7400}
+ 7450 {:name "Building Cleaning & Maintenance" :parent 7400}
+ 7455 {:name "Pest Control" :parent 7400}
+ 7460 {:name "Repairs to Equipment" :parent 7400}
+ 7461 {:name "Contract Labor" :parent 7400}
7500 {:name "Office / Management Related" :parent nil}
- 7510 {:name "Office Supplies" :parent 7500}
- 7520 {:name "Printing - Internal" :parent 7500}
- 7530 {:name "Restaurant Software Fees" :parent 7500}
- 7540 {:name "Credit Card Processing" :parent 7500}
- 7550 {:name "Franchise Fee" :parent 7500}
- 7560 {:name "Unassigned Expenses" :parent 7500}
- 8100 {:name "Operational" :parent nil }
- 8110 {:name "Professional Fees" :parent 8100 }
- 8120 {:name "Accounting" :parent 8100 }
- 8130 {:name "Membership Dues and Associations" :parent 8100 }
- 8200 {:name "Occupancy Costs" :parent nil }
- 8210 {:name "Rent" :parent 8200 }
- 8220 {:name "CAM" :parent 8200 }
- 8230 {:name "Real Estate Taxes" :parent 8200 }
- 8300 {:name "Utilities" :parent nil }
- 8310 {:name "Electric" :parent 8300 }
- 8320 {:name "Gas" :parent 8300 }
- 8330 {:name "Trash Removal" :parent 8300 }
- 8340 {:name "Water and Sewage" :parent 8300 }
- 8350 {:name "Internet" :parent 8300 }
- 8400 {:name "Equipment Rental" :parent nil }
- 8410 {:name "Kitchen Equipment Rental" :parent 8400 }
- 8420 {:name "POS System" :parent 8400 }
- 8430 {:name "Other Rental" :parent 8400 }
- 8500 {:name "Taxes and Insurance" :parent nil }
- 8510 {:name "Liability Insurance" :parent 8500 }
- 8511 {:name "Workers Comp Insurance" :parent 8500 }
- 8610 {:name "Business License" :parent 8500 }
- 8620 {:name "Health Permit" :parent 8500 }
- 8710 {:name "Personal Property Taxes" :parent nil }
- 8800 {:name "Depriciation" :parent nil }
- 8810 {:name "Amortization of Lease" :parent 8800 }
- 8820 {:name "Amortization of Leasehold Improvements" :parent 8800 }
- 8830 {:name "Amortization of Start Up Costs" :parent 8800 }
- 8850 {:name "Depreciation on Building" :parent 8800 }
- 8860 {:name "Depreciation on Furnitire and Fixtures" :parent 8800 }
- 9100 {:name "HQ Promotion and Outreach" :parent nil :location "HQ" }
- 9110 {:name "Marketing and Advertising - HQ" :parent 9100 :location "HQ" }
- 9120 {:name "Marketing Consultant - HQ" :parent 9100 :location "HQ" }
- 9130 {:name "Advertisements - HQ" :parent 9100 :location "HQ" }
- 9140 {:name "Design - HQ" :parent 9100 :location "HQ" }
- 9150 {:name "Charitable Contricutions - HQ" :parent 9100 :location "HQ" }
- 9160 {:name "Meals and Entertainment - HQ" :parent 9100 :location "HQ" }
- 9170 {:name "Travel - HQ" :parent 9100 :location "HQ" }
- 9180 {:name "Food Research - HQ" :parent 9100 :location "HQ" }
- 9190 {:name "Membership Dues and Assocations - HQ" :parent 9100 :location "HQ" }
- 9200 {:name "HQ Employee Morale and Training" :parent nil :location "HQ" }
- 9210 {:name "Company Picnics - HQ" :parent 9200 :location "HQ" }
- 9220 {:name "Employee Gifts - HQ" :parent 9200 :location "HQ" }
- 9230 {:name "Employee Medical Expenses - HQ" :parent 9200 :location "HQ" }
- 9240 {:name "Employee Mileage Reimbursements - HQ" :parent 9200 :location "HQ" }
- 9250 {:name "Recruiting Costs - HQ" :parent 9200 :location "HQ" }
- 9260 {:name "Employee Training - HQ" :parent 9200 :location "HQ" }
- 9300 {:name "HQ Operational" :parent nil :location "HQ" }
- 9310 {:name "Legal Fees - HQ" :parent 9300 :location "HQ" }
- 9315 {:name "Accounting - HQ" :parent 9300 :location "HQ" }
- 9320 {:name "Consultants - HQ" :parent 9300 :location "HQ" }
- 9330 {:name "Liability Insurance - HQ" :parent 9300 :location "HQ" }
- 9340 {:name "Office Rent - HQ" :parent 9300 :location "HQ" }
- 9345 {:name "Office CAM - HQ" :parent 9300 :location "HQ" }
- 9350 {:name "Office Supplies - HQ" :parent 9300 :location "HQ" }
- 9355 {:name "Office Snacks - HQ" :parent 9300 :location "HQ" }
- 9360 {:name "Office Repairs - HQ" :parent 9300 :location "HQ" }
- 9365 {:name "Office Maintenance - HQ" :parent 9300 :location "HQ" }
- 9370 {:name "Utilities - HQ" :parent 9300 :location "HQ" }
- 9380 {:name "Telephone - HQ" :parent 9300 :location "HQ" }
- 9383 {:name "Storage - HQ" :parent 9380 :location "HQ" }
- 9500 {:name "HQ Interest and Bank Expenses" :parent nil :location "HQ" }
- 9510 {:name "Bank Fees - HQ" :parent 9500 :location "HQ" }
- 9520 {:name "NSF Fees - HQ" :parent 9500 :location "HQ" }
- 9530 {:name "Late Payment Fees - HQ" :parent 9500 :location "HQ" }
- 9540 {:name "Interest Expense - HQ" :parent 9500 :location "HQ" }
- 9541 {:name "Late Payment Penalties" :parent 9500 :location "HQ" }
- 9600 {:name "HQ Depreciation" :parent nil :location "HQ" }
- 9610 {:name "Amortization of Lease - HQ" :parent 9600 :location "HQ" }
- 9620 {:name "Amortization of Leasehold Improvements - HQ" :parent 9600 :location "HQ" }
- 9630 {:name "Amortization of Start Up Costs - HQ" :parent 9600 :location "HQ" }
- 9650 {:name "Depreciation on Building - HQ" :parent 9600 :location "HQ" }
- 9660 {:name "Depreciation on Furniture and Fixtures - HQ" :parent 9600 :location "HQ" }
- 9700 {:name "HQ Taxes" :parent nil :location "HQ" }
- 9710 {:name "Federal Taxes - HQ" :parent 9700 :location "HQ" }
- 9720 {:name "State Taxes - HQ" :parent 9700 :location "HQ" }
- 9725 {:name "LLC Fee - HQ" :parent 9700 :location "HQ" }
- 9730 {:name "Local Taxes - HQ" :parent 9700 :location "HQ" }
- 9800 {:name "HQ Other Expenses" :parent nil :location "HQ" }
- 9810 {:name "Sales Tax Received Adjustments - HQ" :parent 9800 :location "HQ" }
- 9820 {:name "Judgments - HQ" :parent 9800 :location "HQ" }
- 9880 {:name "Misc Payments - HQ" :parent 9800 :location "HQ" }
- 9890 {:name "Unassigned Exp - HQ" :parent 9800 :location "HQ" }})
+ 7510 {:name "Office Supplies" :parent 7500}
+ 7520 {:name "Printing - Internal" :parent 7500}
+ 7530 {:name "Restaurant Software Fees" :parent 7500}
+ 7540 {:name "Credit Card Processing" :parent 7500}
+ 7550 {:name "Franchise Fee" :parent 7500}
+ 7560 {:name "Unassigned Expenses" :parent 7500}
+ 8100 {:name "Operational" :parent nil}
+ 8110 {:name "Professional Fees" :parent 8100}
+ 8120 {:name "Accounting" :parent 8100}
+ 8130 {:name "Membership Dues and Associations" :parent 8100}
+ 8200 {:name "Occupancy Costs" :parent nil}
+ 8210 {:name "Rent" :parent 8200}
+ 8220 {:name "CAM" :parent 8200}
+ 8230 {:name "Real Estate Taxes" :parent 8200}
+ 8300 {:name "Utilities" :parent nil}
+ 8310 {:name "Electric" :parent 8300}
+ 8320 {:name "Gas" :parent 8300}
+ 8330 {:name "Trash Removal" :parent 8300}
+ 8340 {:name "Water and Sewage" :parent 8300}
+ 8350 {:name "Internet" :parent 8300}
+ 8400 {:name "Equipment Rental" :parent nil}
+ 8410 {:name "Kitchen Equipment Rental" :parent 8400}
+ 8420 {:name "POS System" :parent 8400}
+ 8430 {:name "Other Rental" :parent 8400}
+ 8500 {:name "Taxes and Insurance" :parent nil}
+ 8510 {:name "Liability Insurance" :parent 8500}
+ 8511 {:name "Workers Comp Insurance" :parent 8500}
+ 8610 {:name "Business License" :parent 8500}
+ 8620 {:name "Health Permit" :parent 8500}
+ 8710 {:name "Personal Property Taxes" :parent nil}
+ 8800 {:name "Depriciation" :parent nil}
+ 8810 {:name "Amortization of Lease" :parent 8800}
+ 8820 {:name "Amortization of Leasehold Improvements" :parent 8800}
+ 8830 {:name "Amortization of Start Up Costs" :parent 8800}
+ 8850 {:name "Depreciation on Building" :parent 8800}
+ 8860 {:name "Depreciation on Furnitire and Fixtures" :parent 8800}
+ 9100 {:name "HQ Promotion and Outreach" :parent nil :location "HQ"}
+ 9110 {:name "Marketing and Advertising - HQ" :parent 9100 :location "HQ"}
+ 9120 {:name "Marketing Consultant - HQ" :parent 9100 :location "HQ"}
+ 9130 {:name "Advertisements - HQ" :parent 9100 :location "HQ"}
+ 9140 {:name "Design - HQ" :parent 9100 :location "HQ"}
+ 9150 {:name "Charitable Contricutions - HQ" :parent 9100 :location "HQ"}
+ 9160 {:name "Meals and Entertainment - HQ" :parent 9100 :location "HQ"}
+ 9170 {:name "Travel - HQ" :parent 9100 :location "HQ"}
+ 9180 {:name "Food Research - HQ" :parent 9100 :location "HQ"}
+ 9190 {:name "Membership Dues and Assocations - HQ" :parent 9100 :location "HQ"}
+ 9200 {:name "HQ Employee Morale and Training" :parent nil :location "HQ"}
+ 9210 {:name "Company Picnics - HQ" :parent 9200 :location "HQ"}
+ 9220 {:name "Employee Gifts - HQ" :parent 9200 :location "HQ"}
+ 9230 {:name "Employee Medical Expenses - HQ" :parent 9200 :location "HQ"}
+ 9240 {:name "Employee Mileage Reimbursements - HQ" :parent 9200 :location "HQ"}
+ 9250 {:name "Recruiting Costs - HQ" :parent 9200 :location "HQ"}
+ 9260 {:name "Employee Training - HQ" :parent 9200 :location "HQ"}
+ 9300 {:name "HQ Operational" :parent nil :location "HQ"}
+ 9310 {:name "Legal Fees - HQ" :parent 9300 :location "HQ"}
+ 9315 {:name "Accounting - HQ" :parent 9300 :location "HQ"}
+ 9320 {:name "Consultants - HQ" :parent 9300 :location "HQ"}
+ 9330 {:name "Liability Insurance - HQ" :parent 9300 :location "HQ"}
+ 9340 {:name "Office Rent - HQ" :parent 9300 :location "HQ"}
+ 9345 {:name "Office CAM - HQ" :parent 9300 :location "HQ"}
+ 9350 {:name "Office Supplies - HQ" :parent 9300 :location "HQ"}
+ 9355 {:name "Office Snacks - HQ" :parent 9300 :location "HQ"}
+ 9360 {:name "Office Repairs - HQ" :parent 9300 :location "HQ"}
+ 9365 {:name "Office Maintenance - HQ" :parent 9300 :location "HQ"}
+ 9370 {:name "Utilities - HQ" :parent 9300 :location "HQ"}
+ 9380 {:name "Telephone - HQ" :parent 9300 :location "HQ"}
+ 9383 {:name "Storage - HQ" :parent 9380 :location "HQ"}
+ 9500 {:name "HQ Interest and Bank Expenses" :parent nil :location "HQ"}
+ 9510 {:name "Bank Fees - HQ" :parent 9500 :location "HQ"}
+ 9520 {:name "NSF Fees - HQ" :parent 9500 :location "HQ"}
+ 9530 {:name "Late Payment Fees - HQ" :parent 9500 :location "HQ"}
+ 9540 {:name "Interest Expense - HQ" :parent 9500 :location "HQ"}
+ 9541 {:name "Late Payment Penalties" :parent 9500 :location "HQ"}
+ 9600 {:name "HQ Depreciation" :parent nil :location "HQ"}
+ 9610 {:name "Amortization of Lease - HQ" :parent 9600 :location "HQ"}
+ 9620 {:name "Amortization of Leasehold Improvements - HQ" :parent 9600 :location "HQ"}
+ 9630 {:name "Amortization of Start Up Costs - HQ" :parent 9600 :location "HQ"}
+ 9650 {:name "Depreciation on Building - HQ" :parent 9600 :location "HQ"}
+ 9660 {:name "Depreciation on Furniture and Fixtures - HQ" :parent 9600 :location "HQ"}
+ 9700 {:name "HQ Taxes" :parent nil :location "HQ"}
+ 9710 {:name "Federal Taxes - HQ" :parent 9700 :location "HQ"}
+ 9720 {:name "State Taxes - HQ" :parent 9700 :location "HQ"}
+ 9725 {:name "LLC Fee - HQ" :parent 9700 :location "HQ"}
+ 9730 {:name "Local Taxes - HQ" :parent 9700 :location "HQ"}
+ 9800 {:name "HQ Other Expenses" :parent nil :location "HQ"}
+ 9810 {:name "Sales Tax Received Adjustments - HQ" :parent 9800 :location "HQ"}
+ 9820 {:name "Judgments - HQ" :parent 9800 :location "HQ"}
+ 9880 {:name "Misc Payments - HQ" :parent 9800 :location "HQ"}
+ 9890 {:name "Unassigned Exp - HQ" :parent 9800 :location "HQ"}})
(def chooseable-expense-accounts
(dissoc expense-accounts 0))
diff --git a/src/cljc/auto_ap/ledger/reports.cljc b/src/cljc/auto_ap/ledger/reports.cljc
index f884f6f3..7e138156 100644
--- a/src/cljc/auto_ap/ledger/reports.cljc
+++ b/src/cljc/auto_ap/ledger/reports.cljc
@@ -16,16 +16,14 @@
[auto-ap.time-utils :refer [user-friendly-date]])]))
(defn date->str [d]
- #?(:clj
+ #?(:clj
(if (inst? d)
(atime/unparse-local (coerce/to-date-time d) atime/normal-date)
(atime/unparse-local d atime/normal-date))
:cljs (au/date->str d au/pretty)))
-
-
(def groupings
- {:assets [["1100 Cash and Bank Accounts" 11000 11999]
+ {:assets [["1100 Cash and Bank Accounts" 11000 11999]
["1200 Accounts Receivable" 12000 12999]
["1300 Inventory" 13000 13999]
["1400 Prepaid Expenses" 14000 14999]
@@ -44,8 +42,7 @@
["47000 Merchandise Sales" 47000 47999]
["48000 Other Operating Income" 48000 48999]
["49000 Non-Business Income" 49000 49999]]
- :cogs [
- ["50000-54000 Food Costs" 50000 53999]
+ :cogs [["50000-54000 Food Costs" 50000 53999]
["54000-56000 Alcohol Costs" 54000 55999]
["56000 Merchandise Costs" 56000 56999]
["57000-60000 Other Costs of Sales" 57000 59999]]
@@ -75,27 +72,22 @@
["97000 Taxes" 97000 97999]
["98000 Other Expenses" 98000 98999]]
- :operating-activities [
- ;; BEN EDIT STARTING HERE
+ :operating-activities [ ;; BEN EDIT STARTING HERE
["20100-20199 Credit Card Balances" 20100 20199 :add]
["21000-24000 Accounts Payable" 21000 23999 :add]
["25000-28000 Accounts Payable" 25000 27999 :add]
["24000-25000 Accrual Liabilities" 24000 24999 :add]
- ["12000-13000 Accounts Receivable" 12000 13000 :subtract]
+ ["12000-13000 Accounts Receivable" 12000 13000 :subtract]
["96000-97000 Depreciation Expense" 96000 96999 :add]
["13000-15000 Inventory" 13000 14999 :subtract]
;; BEN ENDING HERE
]
- :investment-activities [
-
- ;; BEN EDIT STARTING HERE
+ :investment-activities [ ;; BEN EDIT STARTING HERE
["15000-18000 Investments" 15000 17999 :subtract]
;; BEN ENDING HERE
-
]
- :financing-activities [
- ;; BEN EDIT STARTING HERE
+ :financing-activities [ ;; BEN EDIT STARTING HERE
["30000-33000 Other Equity Accounts" 30000 32999 :add]
["33000-34000 Owner's Contributions" 33000 33999 :add]
["34000-35000 Owner's Distributions" 34000 34999 :add]
@@ -103,19 +95,16 @@
["28000-29000 Loans (payable)" 28000 28999 :add]
;; BEN ENDING HERE
]
- :cash [
- ;; BEN EDIT STARTING HERE
+ :cash [ ;; BEN EDIT STARTING HERE
["11000-11400 Bank Accounts / Cash" 11000 11399 :add]
;; BEN ENDING HERE
-
]})
-
-(def cashflow-aggregation
+(def cashflow-aggregation
(->> (select-keys groupings [:operating-activities :investment-activities :financing-activities])
vals
(mapcat identity)
(map (fn [[_ start end rule]]
- [start end rule]))
+ [start end rule]))
(into [])))
(defn cashflow-account->amount [account-code amount]
@@ -143,21 +132,20 @@
(map second)
(apply max)))
-
-(def flat-categories
+(def flat-categories
(for [[category groups] groupings
[_ start end] groups]
[category start end]))
(defn in-range? [code]
- (if code
+ (if code
(reduce
- (fn [acc [_ start end]]
- (if (<= start code end)
- (reduced true)
- acc))
- false
- flat-categories)
+ (fn [acc [_ start end]]
+ (if (<= start code end)
+ (reduced true)
+ acc))
+ false
+ flat-categories)
false))
(defn client-locations [pnl-data]
@@ -166,7 +154,7 @@
(filter (comp in-range? :numeric-code))
(filter #(not= "A" (:location %)))
(group-by (juxt :client-id :location))
- (filter (fn [[_ as]]
+ (filter (fn [[_ as]]
(not (dollars-0? (reduce + 0 (map (comp or-0 :amount) as))))))
(mapcat second)
(map (fn [a]
@@ -177,10 +165,10 @@
(:location a)])))
(filter identity)
(set)
-
+
(sort-by (fn [x]
[(:client-id x)
- (if (= (:location x) "HQ" )
+ (if (= (:location x) "HQ")
"ZZZZZZ"
(:location x))]))))
@@ -209,7 +197,7 @@
(defn filter-client [pnl-data client]
(-> pnl-data
(update :data (fn [data]
- ((group-by :client-id data) client)))
+ ((group-by :client-id data) client)))
(update :filters (fn [f]
(assoc f :client-id client)))))
@@ -226,11 +214,11 @@
(assoc f :locations locations)))))
(defn filter-numeric-code [pnl-data from to]
- (-> pnl-data
+ (-> pnl-data
(update :data (fn [data]
(filter
- #(<= from (or (:numeric-code %) 0) to)
- data)))
+ #(<= from (or (:numeric-code %) 0) to)
+ data)))
(update :filters (fn [f]
(assoc f
:numeric-code [{:from from
@@ -259,68 +247,64 @@
(defn filter-period [pnl-data period]
(-> pnl-data
- (update :data (fn [data]
- ((group-by :period data) period)))
- (update :filters (fn [f]
- (assoc f :date-range period)))))
+ (update :data (fn [data]
+ ((group-by :period data) period)))
+ (update :filters (fn [f]
+ (assoc f :date-range period)))))
(defn zebra [pnl-data i]
(if (odd? i)
(assoc-in pnl-data [:cell-args :bg-color] [240 240 240])
pnl-data))
-
-
(defn negate [pnl-data types]
- (update pnl-data :data
+ (update pnl-data :data
(fn [accounts]
- (map
- (fn [account]
- (if (types (best-category account))
- (update account :amount #(- (or % 0.0)))
- account))
- accounts))))
-
+ (map
+ (fn [account]
+ (if (types (best-category account))
+ (update account :amount #(- (or % 0.0)))
+ account))
+ accounts))))
(defn used-accounts [pnl-datas]
(->>
- pnl-datas
- (mapcat :data)
- (map #(select-keys % [:numeric-code :name]))
- (set)
- (sort-by :numeric-code)))
+ pnl-datas
+ (mapcat :data)
+ (map #(select-keys % [:numeric-code :name]))
+ (set)
+ (sort-by :numeric-code)))
(defn subtotal-by-column-row [pnl-datas title & [cell-args]]
(into [{:value title
:bold true}]
(map
- (fn [p]
- (merge
- {:format :dollar
- :value (aggregate-accounts p)
- :filters (when (:numeric-code (:filters p)) ;; don't allow filtering when you don't at least filter numeric codes
- (:filters p))}
- (:cell-args p)
- cell-args))
- pnl-datas)))
+ (fn [p]
+ (merge
+ {:format :dollar
+ :value (aggregate-accounts p)
+ :filters (when (:numeric-code (:filters p)) ;; don't allow filtering when you don't at least filter numeric codes
+ (:filters p))}
+ (:cell-args p)
+ cell-args))
+ pnl-datas)))
(defn cashflow-subtotal-by-column-row [pnl-datas title & [cell-args]]
(into [{:value title
:bold true}]
(mapcat
(fn [p]
- [
- (merge
+ [(merge
{:format :text
:value nil}
(:cell-args p)
cell-args)
- (merge
+ (merge
{:format :text
:value nil}
(:cell-args p)
cell-args)
- (merge
+ (merge
{:format :dollar
:value (aggregate-cashflow-accounts p)
:filters (when (:numeric-code (:filters p)) ;; don't allow filtering when you don't at least filter numeric codes
@@ -337,25 +321,25 @@
(->> table
(map (fn [[_ & values]]
(map
- (fn [v s]
- {:border (:border v)
- :bg-color (:bg-color v)
- :format (if (string? (:value v))
- :text
- :percent)
- :color [128 128 128]
- :value (cond
- (string? (:value v))
- ""
+ (fn [v s]
+ {:border (:border v)
+ :bg-color (:bg-color v)
+ :format (if (string? (:value v))
+ :text
+ :percent)
+ :color [128 128 128]
+ :value (cond
+ (string? (:value v))
+ ""
- (dollars-0? s)
+ (dollars-0? s)
0.0
:else
(/ (:value v) s))})
- values sales))))))
+ values sales))))))
(defn calc-deltas [table]
- (->> table
+ (->> table
(map (fn [[_ & values]]
(->> values
(partition 2 1)
@@ -373,19 +357,19 @@
(defn combine-tables
([[pnl-data] table percent-of-sales deltas]
- (map (fn [[title & row] percent-of-sales deltas ]
+ (map (fn [[title & row] percent-of-sales deltas]
(let [deltas (cons {:value 0.0
:format :dollar
:border (:border (first row))} deltas)]
(into [title]
(mapcat
- (fn [v p d]
- (if (:include-deltas (:args pnl-data))
- [v p d]
- [v p]))
- row
- percent-of-sales
- deltas))))
+ (fn [v p d]
+ (if (:include-deltas (:args pnl-data))
+ [v p d]
+ [v p]))
+ row
+ percent-of-sales
+ deltas))))
table
percent-of-sales
deltas)))
@@ -394,7 +378,7 @@
(let [big-header (into [{:value header-title
:bold true}]
(map-indexed (fn [i p]
- (cond-> {:value
+ (cond-> {:value
(str (date->str (:start p))
" - "
(date->str (:end p)))
@@ -411,51 +395,51 @@
:align :center
:bold true}
(odd? i) (assoc :bg-color [240 240 240])))
- (:periods (:args pnl-data))))
+ (:periods (:args pnl-data))))
sub-header (into [{:value ""}]
(if (-> pnl-data :args :column-per-location)
(mapcat
- (fn [p]
- (cond-> [(merge {:value (or (when (-> p :filters :location)
- (str ((-> p :client-codes) (-> p :filters :client-id)) "-" (-> p :filters :location)))
- "Total")
- :align :right}
- (:cell-args p))
- (merge {:value "%"
- :align :right}
- (:cell-args p))]
- (-> pnl-data :args :include-deltas) (conj (merge {:value "+/-"
- :align :right}
- (:cell-args p)))))
- pnl-datas)
+ (fn [p]
+ (cond-> [(merge {:value (or (when (-> p :filters :location)
+ (str ((-> p :client-codes) (-> p :filters :client-id)) "-" (-> p :filters :location)))
+ "Total")
+ :align :right}
+ (:cell-args p))
+ (merge {:value "%"
+ :align :right}
+ (:cell-args p))]
+ (-> pnl-data :args :include-deltas) (conj (merge {:value "+/-"
+ :align :right}
+ (:cell-args p)))))
+ pnl-datas)
(mapcat
- (fn [p]
- (cond-> [(merge {:value "Amt"
- :align :right}
- (:cell-args p))
- (merge {:value "%"
- :align :right}
- (:cell-args p))]
- (-> pnl-data :args :include-deltas) (conj (merge {:value "+/-"
- :align :right}
- (:cell-args p)))))
- pnl-datas)))]
+ (fn [p]
+ (cond-> [(merge {:value "Amt"
+ :align :right}
+ (:cell-args p))
+ (merge {:value "%"
+ :align :right}
+ (:cell-args p))]
+ (-> pnl-data :args :include-deltas) (conj (merge {:value "+/-"
+ :align :right}
+ (:cell-args p)))))
+ pnl-datas)))]
[big-header
sub-header]))
(defn location-summary-table [pnl-datas title]
(let [table [(subtotal-by-column-row (map #(filter-categories % [:sales])
- pnl-datas)
- "Sales")
- (subtotal-by-column-row (map #(filter-categories % [:cogs ]) pnl-datas)
+ pnl-datas)
+ "Sales")
+ (subtotal-by-column-row (map #(filter-categories % [:cogs]) pnl-datas)
"Cogs")
- (subtotal-by-column-row (map #(filter-categories % [:payroll ]) pnl-datas)
+ (subtotal-by-column-row (map #(filter-categories % [:payroll]) pnl-datas)
"Payroll")
(subtotal-by-column-row (map #(-> %
- (filter-categories [:sales :payroll :cogs])
- (negate #{:payroll :cogs}))
+ (filter-categories [:sales :payroll :cogs])
+ (negate #{:payroll :cogs}))
pnl-datas)
"Gross Profits")
@@ -464,8 +448,8 @@
"Overhead")
(subtotal-by-column-row (map #(-> %
- (filter-categories [:sales :cogs :payroll :controllable :fixed-overhead :ownership-controllable])
- (negate #{:cogs :payroll :controllable :fixed-overhead :ownership-controllable}))
+ (filter-categories [:sales :cogs :payroll :controllable :fixed-overhead :ownership-controllable])
+ (negate #{:cogs :payroll :controllable :fixed-overhead :ownership-controllable}))
pnl-datas)
"Net Income")]
percent-of-sales (calc-percent-of-sales table pnl-datas)
@@ -473,7 +457,6 @@
{:header (headers pnl-datas title)
:rows (combine-tables pnl-datas table percent-of-sales deltas)}))
-
(defn detail-rows
[pnl-datas grouping title]
(let [pnl-datas (map #(filter-categories % [grouping])
@@ -486,36 +469,35 @@
:when (seq account-codes)
row (-> [(into [{:value (str "---" grouping-name "---")}]
(map
- (fn [p]
- (assoc (:cell-args p) :value "" :format ""))
- pnl-datas)
- )]
+ (fn [p]
+ (assoc (:cell-args p) :value "" :format ""))
+ pnl-datas))]
(into (for [{:keys [numeric-code name]} account-codes]
(into [{:value (str name ":" numeric-code)}]
(map
- (fn [p]
- (let [pnl-data (-> p (filter-numeric-code numeric-code numeric-code))
- this-name-exists? (->> (:data p)
- (filter (comp #{name} :name))
- seq)]
- (merge
- (if this-name-exists?
- {:format :dollar
- :filters (:filters pnl-data)
- :value (aggregate-accounts pnl-data)}
- {:filters (:filters pnl-data)
- :value ""})
- (:cell-args p))))
+ (fn [p]
+ (let [pnl-data (-> p (filter-numeric-code numeric-code numeric-code))
+ this-name-exists? (->> (:data p)
+ (filter (comp #{name} :name))
+ seq)]
+ (merge
+ (if this-name-exists?
+ {:format :dollar
+ :filters (:filters pnl-data)
+ :value (aggregate-accounts pnl-data)}
+ {:filters (:filters pnl-data)
+ :value ""})
+ (:cell-args p))))
- pnl-datas))))
+ pnl-datas))))
(conj (subtotal-by-column-row pnl-datas "" {:border [:top]})))]
row)]
(-> [(into [{:value title
:bold true}]
(map
- (fn [p]
- (assoc (:cell-args p) :value "" :format ""))
- pnl-datas))]
+ (fn [p]
+ (assoc (:cell-args p) :value "" :format ""))
+ pnl-datas))]
(into individual-accounts)
(conj (subtotal-by-column-row pnl-datas title)))))
@@ -528,36 +510,36 @@
:cogs
(str prefix " COGS")))
(into (detail-rows
- pnl-datas
- :payroll
- (str prefix " Payroll")))
+ pnl-datas
+ :payroll
+ (str prefix " Payroll")))
(conj (subtotal-by-column-row (map #(filter-categories % [:payroll :cogs])
pnl-datas)
- (str prefix " Prime Costs")))
+ (str prefix " Prime Costs")))
(conj (subtotal-by-column-row (map #(-> %
(filter-categories [:sales :payroll :cogs])
(negate #{:payroll :cogs}))
pnl-datas)
- (str prefix " Gross Profits")))
+ (str prefix " Gross Profits")))
(into (detail-rows
- pnl-datas
- :controllable
- (str prefix " Controllable Expenses")))
+ pnl-datas
+ :controllable
+ (str prefix " Controllable Expenses")))
(into (detail-rows
- pnl-datas
- :fixed-overhead
- (str prefix " Fixed Overhead")))
+ pnl-datas
+ :fixed-overhead
+ (str prefix " Fixed Overhead")))
(into (detail-rows
- pnl-datas
- :ownership-controllable
- (str prefix " Ownership Controllable")))
+ pnl-datas
+ :ownership-controllable
+ (str prefix " Ownership Controllable")))
(conj (subtotal-by-column-row (map #(filter-categories % [:controllable :fixed-overhead :ownership-controllable]) pnl-datas)
- (str prefix " Overhead")))
+ (str prefix " Overhead")))
(conj (subtotal-by-column-row (map #(-> %
- (filter-categories [:sales :cogs :payroll :controllable :fixed-overhead :ownership-controllable])
- (negate #{:cogs :payroll :controllable :fixed-overhead :ownership-controllable}))
- pnl-datas)
- (str prefix " Net Income"))))
+ (filter-categories [:sales :cogs :payroll :controllable :fixed-overhead :ownership-controllable])
+ (negate #{:cogs :payroll :controllable :fixed-overhead :ownership-controllable}))
+ pnl-datas)
+ (str prefix " Net Income"))))
table (if (seq client-datas)
(conj table (subtotal-by-column-row (map #(-> %
(filter-categories [:sales :cogs :payroll :controllable :fixed-overhead :ownership-controllable])
@@ -574,7 +556,7 @@
(let [big-header (into [{:value header-title
:bold true}]
(map-indexed (fn [i p]
- (cond-> {:value
+ (cond-> {:value
(str (date->str (:start p))
" - "
(date->str (:end p)))
@@ -582,20 +564,20 @@
:align :center
:bold true}
(odd? i) (assoc :bg-color [240 240 240])))
- (:periods (:args pnl-data))))
+ (:periods (:args pnl-data))))
sub-header (into [{:value "Account"}]
(mapcat
- (fn [p]
- [(merge {:value "Increases"
- :align :right}
- (:cell-args p))
- (merge {:value "Decreases"
- :align :right}
- (:cell-args p))
- (merge {:value "+/- in Cash"
- :align :right}
- (:cell-args p))])
- pnl-datas))]
+ (fn [p]
+ [(merge {:value "Increases"
+ :align :right}
+ (:cell-args p))
+ (merge {:value "Decreases"
+ :align :right}
+ (:cell-args p))
+ (merge {:value "+/- in Cash"
+ :align :right}
+ (:cell-args p))])
+ pnl-datas))]
[big-header sub-header]))
(defn cash-flow-detail-rows
@@ -614,8 +596,7 @@
[(assoc (:cell-args p) :value "" :format "")
(assoc (:cell-args p) :value "" :format "")
(assoc (:cell-args p) :value "" :format "")])
- pnl-datas)
- )]
+ pnl-datas))]
(into (for [{:keys [numeric-code name]} account-codes]
(into [{:value name}]
(mapcat
@@ -625,36 +606,36 @@
credits (aggregate-credits pnl-data)
debits (aggregate-debits pnl-data)]
(if (dollars= (- debits credits) aggregated)
- [(merge
- {:format :dollar
- :filters (:filters pnl-data)
- :value debits}
- (:cell-args p))
- (merge
- {:format :dollar
- :filters (:filters pnl-data)
- :value credits}
- (:cell-args p))
- (merge
- {:format :dollar
- :filters (:filters pnl-data)
- :value (cashflow-account->amount numeric-code aggregated)}
- (:cell-args p))]
- [(merge
- {:format :dollar
- :filters (:filters pnl-data)
- :value credits}
- (:cell-args p))
- (merge
- {:format :dollar
- :filters (:filters pnl-data)
- :value debits}
- (:cell-args p))
- (merge
- {:format :dollar
- :filters (:filters pnl-data)
- :value (cashflow-account->amount numeric-code aggregated)}
- (:cell-args p))])))
+ [(merge
+ {:format :dollar
+ :filters (:filters pnl-data)
+ :value debits}
+ (:cell-args p))
+ (merge
+ {:format :dollar
+ :filters (:filters pnl-data)
+ :value credits}
+ (:cell-args p))
+ (merge
+ {:format :dollar
+ :filters (:filters pnl-data)
+ :value (cashflow-account->amount numeric-code aggregated)}
+ (:cell-args p))]
+ [(merge
+ {:format :dollar
+ :filters (:filters pnl-data)
+ :value credits}
+ (:cell-args p))
+ (merge
+ {:format :dollar
+ :filters (:filters pnl-data)
+ :value debits}
+ (:cell-args p))
+ (merge
+ {:format :dollar
+ :filters (:filters pnl-data)
+ :value (cashflow-account->amount numeric-code aggregated)}
+ (:cell-args p))])))
pnl-datas))))
(conj (cashflow-subtotal-by-column-row pnl-datas "" {:border [:top]})))]
@@ -673,19 +654,19 @@
(defn cash-flows-table [pnl-datas #_client-datas title prefix]
(let [table (-> []
(conj (cashflow-subtotal-by-column-row (map #(-> %
- (filter-categories [:sales :cogs :payroll :controllable :fixed-overhead :ownership-controllable])
- (negate #{:cogs :payroll :controllable :fixed-overhead :ownership-controllable}))
- pnl-datas)
- "Net Income"))
+ (filter-categories [:sales :cogs :payroll :controllable :fixed-overhead :ownership-controllable])
+ (negate #{:cogs :payroll :controllable :fixed-overhead :ownership-controllable}))
+ pnl-datas)
+ "Net Income"))
(into (cash-flow-detail-rows pnl-datas
- :operating-activities
- (str prefix " Operating Activities")))
+ :operating-activities
+ (str prefix " Operating Activities")))
(into (cash-flow-detail-rows pnl-datas
- :investment-activities
- (str prefix " Investment Activities")))
+ :investment-activities
+ (str prefix " Investment Activities")))
(into (cash-flow-detail-rows pnl-datas
- :financing-activities
- (str prefix " Financing Activities")))
+ :financing-activities
+ (str prefix " Financing Activities")))
(conj (cashflow-subtotal-by-column-row (map #(-> %
(filter-categories [:operating-activities :investment-activities :financing-activities :sales :cogs :payroll :controllable :fixed-overhead :ownership-controllable])
@@ -695,9 +676,8 @@
(into (cash-flow-detail-rows pnl-datas
:cash
- (str prefix " Bank Accounts / Cash")))
+ (str prefix " Bank Accounts / Cash"))))]
- )]
{:header (cash-flow-headers pnl-datas "Cash Flow")
:rows table}))
@@ -720,7 +700,7 @@
:data
(filter (fn [{:keys [numeric-code]}]
(nil? numeric-code)))
- (map :sample)) ]
+ (map :sample))]
errors))
(defn summarize-pnl [pnl-data]
@@ -728,19 +708,19 @@
: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))]
- (concat
- (for [[client-id location] (client-locations pnl-data)]
- (-> pnl-data
- (filter-client client-id)
- (filter-location location)
- (filter-period period)
- (zebra i)))
- [(zebra (filter-locations (filter-period pnl-data period)
- (map second (client-locations pnl-data))) i)])))
+ [(location-summary-table (mapcat identity (for [[period i] (map vector (-> pnl-data :args :periods) (range))]
+ (concat
+ (for [[client-id location] (client-locations pnl-data)]
+ (-> pnl-data
+ (filter-client client-id)
+ (filter-location location)
+ (filter-period period)
+ (zebra i)))
+ [(zebra (filter-locations (filter-period pnl-data period)
+ (map second (client-locations pnl-data))) i)])))
"All location Summary")]
(for [[client-id location] (client-locations pnl-data)]
- (location-summary-table (for [[period i] (map vector (-> pnl-data :args :periods ) (range))]
+ (location-summary-table (for [[period i] (map vector (-> pnl-data :args :periods) (range))]
(-> pnl-data
(filter-client client-id)
(filter-location location)
@@ -749,29 +729,29 @@
(str (-> pnl-data :clients-by-id (get client-id)) " (" location ") Summary"))))
:details
(doall (if (-> pnl-data :args :column-per-location)
- [(location-detail-table (mapcat identity (for [[period i] (map vector (-> pnl-data :args :periods ) (range))]
+ [(location-detail-table (mapcat identity (for [[period i] (map vector (-> pnl-data :args :periods) (range))]
(concat
- (for [[client-id location] (client-locations pnl-data)]
- (-> pnl-data
- (filter-client client-id)
- (filter-location location)
- (filter-period period)
- (zebra i)))
- [(-> pnl-data
+ (for [[client-id location] (client-locations pnl-data)]
+ (-> pnl-data
+ (filter-client client-id)
+ (filter-location location)
(filter-period period)
- (filter-locations (map second (client-locations pnl-data)))
- (zebra i))])))
+ (zebra i)))
+ [(-> pnl-data
+ (filter-period period)
+ (filter-locations (map second (client-locations pnl-data)))
+ (zebra i))])))
nil
"All location Detail"
"")]
(for [[client-id location] (client-locations pnl-data)]
- (location-detail-table (for [[period i] (map vector (-> pnl-data :args :periods ) (range))]
+ (location-detail-table (for [[period i] (map vector (-> pnl-data :args :periods) (range))]
(-> pnl-data
(filter-client client-id)
(filter-location location)
(filter-period period)
(zebra i)))
- (for [[period i] (map vector (-> pnl-data :args :periods ) (range))]
+ (for [[period i] (map vector (-> pnl-data :args :periods) (range))]
(-> pnl-data
(filter-client client-id)
(filter-period period)
@@ -784,11 +764,11 @@
(let [client-ids (->> (client-locations pnl-data)
(map first)
set)]
-
+
{:warning (warning-message pnl-data)
:details
(doall (for [client-id client-ids]
- (cash-flows-table (for [[period i] (map vector (-> pnl-data :args :periods ) (range))]
+ (cash-flows-table (for [[period i] (map vector (-> pnl-data :args :periods) (range))]
(-> pnl-data
(filter-client client-id)
(filter-period period)
@@ -796,7 +776,6 @@
(str (-> pnl-data :clients-by-id (get client-id)) " Detail")
"")))}))
-
(defn balance-sheet-headers [pnl-data]
(let [period-count (count (:periods (:args pnl-data)))
client-ids (set (map :client-id (:data pnl-data)))
@@ -805,7 +784,7 @@
(cond-> []
(> client-count 1)
(conj (cond-> (into [{:value "Client"}]
- (mapcat identity
+ (mapcat identity
(for [client client-ids]
(cond-> [{:value (str (-> pnl-data :client-codes (get client)))}]
(> period-count 1)
@@ -814,15 +793,15 @@
true
(conj (cond-> (into [{:value "Period Ending"}]
- (for [client client-ids
+ (for [client client-ids
[index p] (map vector (range) (:periods (:args pnl-data)))
:let [is-first? (= 0 index)
period-date (date->str p)
period-headers (if (or is-first?
(not (:include-deltas (:args pnl-data))))
- [{:value period-date}]
- [{:value period-date}
- {:value "+/-"}])]
+ [{:value period-date}]
+ [{:value period-date}
+ {:value "+/-"}])]
header period-headers]
header))
show-total? (conj {:value (date->str (first (:periods (:args pnl-data)))) :border [:left]}))))))
@@ -851,48 +830,47 @@
result))))))
#_(defn summarize-balance-sheet [pnl-data]
- (reduce
- (fn [result table]
- (-> result
- (update :header into (:header table))
- (update :rows
- (fn [current-rows]
- (if (seq current-rows)
- (map
+ (reduce
+ (fn [result table]
+ (-> result
+ (update :header into (:header table))
+ (update :rows
+ (fn [current-rows]
+ (if (seq current-rows)
+ (map
concat
current-rows
(:rows table))
- (:rows table))))))
- {:header []
- :rows []}
- (for [client-id (set (map :client-id (:data pnl-data)))]
- (let [pnl-datas (map (fn [p]
- (-> pnl-data
- (filter-client client-id)
- (filter-period p)))
- (:periods (:args pnl-data)))
- table (-> []
- (into (detail-rows pnl-datas
- :assets
- "Assets"))
- (into (detail-rows pnl-datas
- :liabilities
- "Liabilities"))
- (into (detail-rows pnl-datas
- :equities
- "Owner's Equity"))
- (conj (subtotal-by-column-row
+ (:rows table))))))
+ {:header []
+ :rows []}
+ (for [client-id (set (map :client-id (:data pnl-data)))]
+ (let [pnl-datas (map (fn [p]
+ (-> pnl-data
+ (filter-client client-id)
+ (filter-period p)))
+ (:periods (:args pnl-data)))
+ table (-> []
+ (into (detail-rows pnl-datas
+ :assets
+ "Assets"))
+ (into (detail-rows pnl-datas
+ :liabilities
+ "Liabilities"))
+ (into (detail-rows pnl-datas
+ :equities
+ "Owner's Equity"))
+ (conj (subtotal-by-column-row
(map #(-> %
(filter-categories [:sales :cogs :payroll :controllable :fixed-overhead :ownership-controllable])
(negate #{:cogs :payroll :controllable :fixed-overhead :ownership-controllable}))
pnl-datas)
"Retained Earnings")))
- table (if (:include-comparison (:args pnl-data))
- (append-deltas table)
- table)]
- {:header (balance-sheet-headers pnl-data)
- :rows table})))
- )
+ table (if (:include-comparison (:args pnl-data))
+ (append-deltas table)
+ table)]
+ {:header (balance-sheet-headers pnl-data)
+ :rows table}))))
(defn add-total-border [rows]
(map (fn [row]
@@ -913,7 +891,7 @@
show-total? (and (> client-count 1) (= 1 period-count))
pnl-datas (for [client-id client-ids
p (:periods (:args pnl-data))]
- (-> pnl-data
+ (-> pnl-data
(filter-client client-id)
(filter-period p)))
total-data (when show-total?
@@ -938,15 +916,13 @@
pnl-datas)
"Retained Earnings")))
table (if (and (> period-count 1)
- (:include-deltas (:args pnl-data)))
- (append-deltas table)
- table)
+ (:include-deltas (:args pnl-data)))
+ (append-deltas table)
+ table)
table (if show-total? (add-total-border table) table)]
{:warning (warning-message pnl-data)
:header (balance-sheet-headers pnl-data)
- :rows table}))
- )
-
+ :rows table})))
(defn journal-detail-report [args data client-codes]
{:header [[{:value "Category"}
@@ -956,51 +932,46 @@
{:value "Credit"}
{:value "Running Balance"}]]
:rows (reduce
- (fn [rows category]
- (into rows
+ (fn [rows category]
+ (into rows
;; TODO colspan ?
- (concat (when (seq (:journal-entries category))
- [[
- {:value (str (client-codes (:client-id category)) " - " (:location category) " - " (:name (:account category)))}
- {:value ""}
- {:value ""}
- {:value ""}
- {:value ""}
- {:value ""}]])
- (map
- (fn [je]
- [{:value ""}
- {:value (user-friendly-date (:date je))}
- {:value (:description je "")}
- {:value (get je :debit)
- :format :dollar}
- {:value (get je :credit)
- :format :dollar}
- {:value (get je :running-balance)
- :format :dollar}])
- (:journal-entries category))
- [[
- {:value (str (client-codes (:client-id category)) " - " (:location category) " - " (:name (:account category)))
- :bold true
- :border [:top]}
- {:value ""
- :border [:top]}
- {:value (str "Total" )
- :bold true
- :border [:top]}
- {:value ""
- :border [:top]}
- {:value ""
- :border [:top]}
- {:value (:total category)
- :format :dollar
- :bold true
- :border [:top]}]]))
-
- )
- []
- (:categories data))}
- )
+ (concat (when (seq (:journal-entries category))
+ [[{:value (str (client-codes (:client-id category)) " - " (:location category) " - " (:name (:account category)))}
+ {:value ""}
+ {:value ""}
+ {:value ""}
+ {:value ""}
+ {:value ""}]])
+ (map
+ (fn [je]
+ [{:value ""}
+ {:value (user-friendly-date (:date je))}
+ {:value (:description je "")}
+ {:value (get je :debit)
+ :format :dollar}
+ {:value (get je :credit)
+ :format :dollar}
+ {:value (get je :running-balance)
+ :format :dollar}])
+ (:journal-entries category))
+ [[{:value (str (client-codes (:client-id category)) " - " (:location category) " - " (:name (:account category)))
+ :bold true
+ :border [:top]}
+ {:value ""
+ :border [:top]}
+ {:value (str "Total")
+ :bold true
+ :border [:top]}
+ {:value ""
+ :border [:top]}
+ {:value ""
+ :border [:top]}
+ {:value (:total category)
+ :format :dollar
+ :bold true
+ :border [:top]}]])))
+ []
+ (:categories data))})
(defrecord PNLData [args data client-codes])
diff --git a/src/cljc/auto_ap/numeric.cljc b/src/cljc/auto_ap/numeric.cljc
index c13f7b28..ea915920 100644
--- a/src/cljc/auto_ap/numeric.cljc
+++ b/src/cljc/auto_ap/numeric.cljc
@@ -86,9 +86,8 @@
parts)))]
(recur parts n (inc mag)))))))
-
(defn num->words [num]
- (let [total-cents (int (Math/round (* 100 num)))
+ (let [total-cents (int (Math/round (* 100 num)))
dollar-num (int (quot total-cents 100))
cent-num (int (rem total-cents 100))
dollars (str (words dollar-num) " dollars")
diff --git a/src/cljc/auto_ap/permissions.cljc b/src/cljc/auto_ap/permissions.cljc
index 19ad34e1..c75a3b39 100644
--- a/src/cljc/auto_ap/permissions.cljc
+++ b/src/cljc/auto_ap/permissions.cljc
@@ -1,5 +1,5 @@
(ns auto-ap.permissions
- #?(:clj
+ #?(:clj
(:require [cemerick.url :as url])))
;; TODO after getting rid of cljs, use malli schemas to decode this
@@ -107,29 +107,28 @@
(cond
(#{:invoice-page :payment-page :my-company-page :transaction-page :ledger-page} subject)
true
-
+
(= [:vendor :create] [subject activity])
true
-
+
(= [:vendor :edit] [subject activity])
true
-
+
(= [:signature :edit] [subject activity])
true
-
-
+
(= [:invoice :create] [subject activity])
true
-
+
(= [:invoice :pay] [subject activity])
true
-
+
(= [:invoice :edit] [subject activity])
true
-
+
(= [:invoice :delete] [subject activity])
true
-
+
(= [:ledger :read] [subject activity])
true
@@ -142,19 +141,19 @@
false)))
#? (:clj
- (defn wrap-must
- ( [handler policy]
- (fn [request]
- (if (can? (:identity request) policy)
- (handler request)
- {:status 302
- :headers {"Location" (str "/login?"
- (url/map->query {"redirect-to" (:uri request)}))}})))
- ( [handler policy get-client]
- (fn [request]
- (if (can? (:identity request) (assoc policy :client (get-client request)))
- (handler request)
- {:status 302
- :headers {"Location" (str "/login?"
- (url/map->query {"redirect-to" (:uri request)}))}})))))
+ (defn wrap-must
+ ([handler policy]
+ (fn [request]
+ (if (can? (:identity request) policy)
+ (handler request)
+ {:status 302
+ :headers {"Location" (str "/login?"
+ (url/map->query {"redirect-to" (:uri request)}))}})))
+ ([handler policy get-client]
+ (fn [request]
+ (if (can? (:identity request) (assoc policy :client (get-client request)))
+ (handler request)
+ {:status 302
+ :headers {"Location" (str "/login?"
+ (url/map->query {"redirect-to" (:uri request)}))}})))))
diff --git a/src/cljc/auto_ap/routes/admin/transaction_rules.cljc b/src/cljc/auto_ap/routes/admin/transaction_rules.cljc
index cdb39a20..b625ac32 100644
--- a/src/cljc/auto_ap/routes/admin/transaction_rules.cljc
+++ b/src/cljc/auto_ap/routes/admin/transaction_rules.cljc
@@ -15,5 +15,4 @@
["/" [#"\d+" :db/id] "/delete"] ::delete
["/" [#"\d+" :db/id] "/run"] {:get ::execute-dialog
:post ::execute}
- "/check-badges" ::check-badges
- })
+ "/check-badges" ::check-badges})
diff --git a/src/cljc/auto_ap/routes/admin/vendors.cljc b/src/cljc/auto_ap/routes/admin/vendors.cljc
index 110e4c1d..f2f62eee 100644
--- a/src/cljc/auto_ap/routes/admin/vendors.cljc
+++ b/src/cljc/auto_ap/routes/admin/vendors.cljc
@@ -13,5 +13,4 @@
"/new" {:get ::new}
"/merge" {:get ::merge
:put ::merge-submit}
- ["/" [#"\d+" :db/id] "/edit"] {:get ::edit
- }})
+ ["/" [#"\d+" :db/id] "/edit"] {:get ::edit}})
diff --git a/src/cljc/auto_ap/routes/dashboard.cljc b/src/cljc/auto_ap/routes/dashboard.cljc
index 5f2d46e5..95fe033d 100644
--- a/src/cljc/auto_ap/routes/dashboard.cljc
+++ b/src/cljc/auto_ap/routes/dashboard.cljc
@@ -1,6 +1,6 @@
(ns auto-ap.routes.dashboard)
(def routes {""
- {:get ::page }
+ {:get ::page}
"/expense-card" ::expense-card
"/pnl-card" ::pnl-card
"/sales-card" ::sales-card
diff --git a/src/cljc/auto_ap/routes/invoice.cljc b/src/cljc/auto_ap/routes/invoice.cljc
index d1fd2632..4f46578c 100644
--- a/src/cljc/auto_ap/routes/invoice.cljc
+++ b/src/cljc/auto_ap/routes/invoice.cljc
@@ -21,12 +21,12 @@
"/account/prediction" ::account-prediction
"/total" ::expense-account-total
"/balance" ::expense-account-balance}
-
+
"/pay-button" ::pay-button
"/pay" {:get ::pay-wizard
"/using-credit" ::pay-using-credit
-
+
"/navigate" ::pay-wizard-navigate
:post ::pay-submit}
"/bulk-delete" {:get ::bulk-delete
@@ -35,12 +35,12 @@
:put ::bulk-edit-submit
"/account" ::bulk-edit-new-account
"/total" ::bulk-edit-total
- "/balance" ::bulk-edit-balance}
+ "/balance" ::bulk-edit-balance}
["/" [#"\d+" :db/id]] {:delete ::delete
"/undo-autopay" ::undo-autopay
"/unvoid" ::unvoid
"/edit" ::edit-wizard}
- "/table" ::table })
+ "/table" ::table})
(def legacy-routes {"" ::legacy-invoices
"import" ::legacy-import-invoices
diff --git a/src/cljc/auto_ap/routes/ledger.cljc b/src/cljc/auto_ap/routes/ledger.cljc
index 060a52e4..d602f1bf 100644
--- a/src/cljc/auto_ap/routes/ledger.cljc
+++ b/src/cljc/auto_ap/routes/ledger.cljc
@@ -13,15 +13,15 @@
"/import" ::external-import-import}
"/investigate" {"" ::investigate
"/results" ::investigate-results}
- "/table" ::table
- "/csv" ::csv
+ "/table" ::table
+ "/csv" ::csv
"/bank-account-filter" ::bank-account-filter
"/reports/balance-sheet" {"" ::balance-sheet
"/run" ::run-balance-sheet
"/export" ::export-balance-sheet}
"/reports/cash-flows" {"" ::cash-flows
- "/run" ::run-cash-flows
- "/export" ::export-cash-flows}
+ "/run" ::run-cash-flows
+ "/export" ::export-cash-flows}
"/reports/profit-and-loss" {"" ::profit-and-loss
- "/run" ::run-profit-and-loss
- "/export" ::export-profit-and-loss}})
\ No newline at end of file
+ "/run" ::run-profit-and-loss
+ "/export" ::export-profit-and-loss}})
\ No newline at end of file
diff --git a/src/cljc/auto_ap/routes/outgoing_invoice.cljc b/src/cljc/auto_ap/routes/outgoing_invoice.cljc
index cef47a0f..fc35192a 100644
--- a/src/cljc/auto_ap/routes/outgoing_invoice.cljc
+++ b/src/cljc/auto_ap/routes/outgoing_invoice.cljc
@@ -1,4 +1,4 @@
(ns auto-ap.routes.outgoing-invoice)
-(def routes {"/" {"new" {:get ::new
+(def routes {"/" {"new" {:get ::new
:post ::new-submit}
"line-item/new" {:get ::new-line-item}}})
\ No newline at end of file
diff --git a/src/cljc/auto_ap/routes/pos/sales_summaries.cljc b/src/cljc/auto_ap/routes/pos/sales_summaries.cljc
index 5f9312d9..a5b7c32a 100644
--- a/src/cljc/auto_ap/routes/pos/sales_summaries.cljc
+++ b/src/cljc/auto_ap/routes/pos/sales_summaries.cljc
@@ -2,7 +2,7 @@
(def routes {"" {:get ::page
:put ::edit-wizard-submit}
"/table" ::table
- ["/" [#"\d+" :db/id]] {:get ::edit-wizard }
+ ["/" [#"\d+" :db/id]] {:get ::edit-wizard}
"/edit/navigate" ::edit-wizard-navigate
"/edit/sales-summary-item" ::new-summary-item
"/edit/item-account" ::edit-item-account
diff --git a/src/cljc/auto_ap/routes/transactions.cljc b/src/cljc/auto_ap/routes/transactions.cljc
index f90fe6fa..1226a8b7 100644
--- a/src/cljc/auto_ap/routes/transactions.cljc
+++ b/src/cljc/auto_ap/routes/transactions.cljc
@@ -9,7 +9,8 @@
"/bulk-suppress" ::bulk-suppress
"/bulk-code" {:get ::bulk-code
:put ::bulk-code-submit
- "/new-account" ::bulk-code-new-account}}
+ "/new-account" ::bulk-code-new-account
+ "/vendor-changed" ::bulk-code-vendor-changed}}
"/new" {:get ::new
:post ::new-submit
"/location-select" ::location-select
@@ -20,19 +21,19 @@
"/external-import-new" {"" ::external-import-page
"/parse" ::external-import-parse
"/import" ::external-import-import}
-
- "/table" ::table
- "/csv" ::csv
+
+ "/table" ::table
+ "/csv" ::csv
"/bank-account-filter" ::bank-account-filter
-
- ["/" [#"\d+" :db/id]] {"/edit" {:get ::edit-wizard
- } }
+
+ ["/" [#"\d+" :db/id]] {"/edit" {:get ::edit-wizard}}
"/edit-submit" ::edit-submit
+ "/edit-vendor-changed" ::edit-vendor-changed
"/location-select" ::location-select
- "/account-total" ::account-total
- "/account-balance" ::account-balance
- "/toggle-amount-mode" ::toggle-amount-mode
- "/edit-wizard-new-account" ::edit-wizard-new-account
+ "/account-total" ::account-total
+ "/account-balance" ::account-balance
+ "/toggle-amount-mode" ::toggle-amount-mode
+ "/edit-wizard-new-account" ::edit-wizard-new-account
"/match-payment" ::link-payment
"/match-autopay-invoices" ::link-autopay-invoices
"/match-unpaid-invoices" ::link-unpaid-invoices
diff --git a/src/cljc/auto_ap/ssr_routes.cljc b/src/cljc/auto_ap/ssr_routes.cljc
index 3ca667b0..26eda78e 100644
--- a/src/cljc/auto_ap/ssr_routes.cljc
+++ b/src/cljc/auto_ap/ssr_routes.cljc
@@ -21,7 +21,7 @@
"login" :login
"search" :search
"indicators" indicator-routes/routes
-
+
"dashboard" d-routes/routes
"account" {"/search" {:get :account-search}}
"admin" {"" :auto-ap.routes.admin/page
@@ -73,13 +73,13 @@
"/table" {:get :pos-refund-table}}
"/cash-drawer-shifts" {"" {:get :pos-cash-drawer-shifts}
"/table" {:get :pos-cash-drawer-shift-table}}}
-
+
"outgoing-invoice" oi-routes/routes
"payment" p-routes/routes
"invoice" i-routes/routes
"invoices/" i-routes/legacy-routes
"invoices" i-routes/legacy-routes
-
+
"vendor" {"/search" :vendor-search}
;; TODO Include IDS in routes for company-specific things, as opposed to headers
"company" {"" :company
@@ -109,7 +109,7 @@
"/fastlink" {:get :company-yodlee-fastlink-dialog}
"/refresh" {:put :company-yodlee-provider-account-refresh}
"/reauthenticate" {:put :company-yodlee-provider-account-reauthenticate}}
-
+
"/plaid" {"" {:get :company-plaid}
"/table" {:get :company-plaid-table}
"/link" {:post :company-plaid-link}
@@ -117,6 +117,5 @@
#_#_"/fastlink" {:get :company-yodlee-fastlink-dialog}
#_#_"/refresh" {:put :company-yodlee-provider-account-refresh}}}})
-
(def only-routes ["/" routes])
diff --git a/src/cljc/auto_ap/time_utils.cljc b/src/cljc/auto_ap/time_utils.cljc
index c39d3df3..983529b8 100644
--- a/src/cljc/auto_ap/time_utils.cljc
+++ b/src/cljc/auto_ap/time_utils.cljc
@@ -10,7 +10,7 @@
(some->> d (format/unparse pretty)))
(defn next-dom [date dom]
- (when date
+ (when date
(let [candidate (time/date-time (time/year date) (time/month date)
#?(:clj (Math/min (int dom)
(time/day (time/last-day-of-the-month (time/year date) (time/month date))))
diff --git a/src/cljc/auto_ap/utils.cljc b/src/cljc/auto_ap/utils.cljc
index 0f59063b..8e03d66a 100644
--- a/src/cljc/auto_ap/utils.cljc
+++ b/src/cljc/auto_ap/utils.cljc
@@ -1,9 +1,9 @@
(ns auto-ap.utils
#?@
- (:clj
- [(:require [com.unbounce.dogstatsd.core :as statsd]
- [com.brunobonacci.mulog :as mu]
- [auto-ap.logging :as alog])]))
+ (:clj
+ [(:require [com.unbounce.dogstatsd.core :as statsd]
+ [com.brunobonacci.mulog :as mu]
+ [auto-ap.logging :as alog])]))
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
(defn by
@@ -35,7 +35,7 @@
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
(defn dollars= [amt1 amt2]
- (dollars-0? (- amt1 amt2) ))
+ (dollars-0? (- amt1 amt2)))
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
(defn deep-merge [v & vs]
@@ -55,18 +55,17 @@
(let [in-progress? (atom false)]
(fn []
(when (= false @in-progress?)
- (try
+ (try
(reset! in-progress? true)
(f)
(finally
(reset! in-progress? false)))))))
-
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
(defn heartbeat [f id]
(fn []
#?(:clj (mu/with-context {:source id}
- (try
+ (try
(alog/info ::starting-process :id id)
(f)
(alog/info ::ending-process :id id)
diff --git a/test/clj/auto_ap/ezcater_test.clj b/test/clj/auto_ap/ezcater_test.clj
index 1966198f..2348d166 100644
--- a/test/clj/auto_ap/ezcater_test.clj
+++ b/test/clj/auto_ap/ezcater_test.clj
@@ -53,28 +53,26 @@
(t/testing "It should find the order from ezcater"
(with-redefs [sut/get-caterer (fn [k]
(t/is (= k "91541331-d7ae-4634-9e8b-ccbbcfb2ce70"))
- {
- :ezcater-integration/_caterers {:ezcater-integration/api-key "bmlrdHNpZ2FyaXNAZ21haWwuY29tOmQwMzQwMjYzOWI2ODQxNmVkMjdmZWYxMWFhZTk3YzU1MDlmNTcyNjYwMDAzOTA5MDE2OGMzODllNDBjNTVkZGE"}
+ {:ezcater-integration/_caterers {:ezcater-integration/api-key "bmlrdHNpZ2FyaXNAZ21haWwuY29tOmQwMzQwMjYzOWI2ODQxNmVkMjdmZWYxMWFhZTk3YzU1MDlmNTcyNjYwMDAzOTA5MDE2OGMzODllNDBjNTVkZGE"}
:ezcater-location/_caterer [{:ezcater-location/location "DT"
- :client/_ezcater-locations {:client/code "ABC"}}]
- })]
+ :client/_ezcater-locations {:client/code "ABC"}}]})]
(t/is (= known-order
(sut/lookup-order sample-event))))))
(t/deftest order->sales-order
(t/testing "It should use the date"
(t/is (= #clj-time/date-time "2022-01-01T00:00:00-08:00"
- (-> known-order
- (assoc-in [:event :timestamp]
- "2022-01-01T08:00:00Z")
- (sut/order->sales-order)
- (:sales-order/date ))))
+ (-> known-order
+ (assoc-in [:event :timestamp]
+ "2022-01-01T08:00:00Z")
+ (sut/order->sales-order)
+ (:sales-order/date))))
(t/is (= #clj-time/date-time "2022-06-01T00:00:00-07:00"
(-> known-order
(assoc-in [:event :timestamp]
"2022-06-01T07:00:00Z")
(sut/order->sales-order)
- (:sales-order/date )))))
+ (:sales-order/date)))))
(t/testing "It should simulate a single line item for everything"
(t/is (= 1
(-> known-order
@@ -83,49 +81,48 @@
count)))
(t/is (= #{"EZCater Catering"}
(->> known-order
- sut/order->sales-order
- :sales-order/line-items
- (map :order-line-item/category)
- set))))
+ sut/order->sales-order
+ :sales-order/line-items
+ (map :order-line-item/category)
+ set))))
(t/testing "It should generate an external-id"
(t/is (= "ezcater/order/ABC-DT-9ab05fee-a9c5-483b-a7f2-14debde4b7a8"
(:sales-order/external-id (sut/order->sales-order known-order)))))
-
(t/testing "Should capture amounts"
(t/is (= 35.09 (-> known-order
sut/order->sales-order
:sales-order/tax)))
(t/is (= 0.0 (-> known-order
- sut/order->sales-order
- :sales-order/tip))))
+ sut/order->sales-order
+ :sales-order/tip))))
(t/testing "Should calculate 7% commision on ezcater orders"
- (t/is (dollars= 7.0
- (-> known-order
- (assoc :orderSourceType "EZCATER")
- (assoc-in [:totals :subTotal :subunits] 10000)
- sut/commision)))
+ (t/is (dollars= 7.0
+ (-> known-order
+ (assoc :orderSourceType "EZCATER")
+ (assoc-in [:totals :subTotal :subunits] 10000)
+ sut/commision)))
(t/testing "Should inlclude delivery fee in commision"
- (t/is (dollars= 14.0
- (-> known-order
- (assoc :orderSourceType "EZCATER")
- (assoc-in [:totals :subTotal :subunits] 10000)
- (assoc-in [:catererCart :feesAndDiscounts 0 :cost :subunits] 10000)
- sut/commision)))))
+ (t/is (dollars= 14.0
+ (-> known-order
+ (assoc :orderSourceType "EZCATER")
+ (assoc-in [:totals :subTotal :subunits] 10000)
+ (assoc-in [:catererCart :feesAndDiscounts 0 :cost :subunits] 10000)
+ sut/commision)))))
(t/testing "Should calculate 15% commision on marketplace orders"
- (t/is (dollars= 15.0
- (-> known-order
- (assoc :orderSourceType "MARKETPLACE")
- (assoc-in [:totals :subTotal :subunits] 10000)
- sut/commision)))
+ (t/is (dollars= 15.0
+ (-> known-order
+ (assoc :orderSourceType "MARKETPLACE")
+ (assoc-in [:totals :subTotal :subunits] 10000)
+ sut/commision)))
(t/testing "Should inlclude delivery fee in commision"
- (t/is (dollars= 30.0
- (-> known-order
- (assoc :orderSourceType "MARKETPLACE")
- (assoc-in [:totals :subTotal :subunits] 10000)
- (assoc-in [:catererCart :feesAndDiscounts 0 :cost :subunits] 10000)
- sut/commision)))))
+ (t/is (dollars= 30.0
+ (-> known-order
+ (assoc :orderSourceType "MARKETPLACE")
+ (assoc-in [:totals :subTotal :subunits] 10000)
+ (assoc-in [:catererCart :feesAndDiscounts 0 :cost :subunits] 10000)
+ sut/commision)))))
(t/testing "Should calculate 2.75% ccp fee"
(t/is (dollars= 8.97
(-> known-order
@@ -138,7 +135,7 @@
(t/is (dollars= 454.09
(-> known-order
sut/order->sales-order
- :sales-order/total))))
+ :sales-order/total))))
(t/testing "Should derive adjustments food-total + sales-tax - caterer-total - service fee - ccp fee"
(t/is (dollars= -42.99
(-> known-order
diff --git a/test/clj/auto_ap/import/plaid_test.clj b/test/clj/auto_ap/import/plaid_test.clj
index 8514753e..23717a01 100644
--- a/test/clj/auto_ap/import/plaid_test.clj
+++ b/test/clj/auto_ap/import/plaid_test.clj
@@ -8,9 +8,8 @@
:amount 123.45
:date "2023-01-01"})
-(t/deftest plaid->transaction
+(t/deftest plaid->transaction
-
(t/testing "Should assign a plaid merchant if a merchant is found"
(t/is (= "Home Depot" (-> (sut/plaid->transaction (assoc base-transaction
:merchant_name "Home Depot")
@@ -19,6 +18,6 @@
:plaid-merchant/name))))
(t/testing "Should assign a default vendor if a merchant is found, with a matching vendor lookup"
(t/is (= 12354 (-> (sut/plaid->transaction (assoc base-transaction
- :merchant_name "Home Depot")
- {"Home Depot" 12354})
- :transaction/default-vendor)))))
+ :merchant_name "Home Depot")
+ {"Home Depot" 12354})
+ :transaction/default-vendor)))))
diff --git a/test/clj/auto_ap/import/transactions_test.clj b/test/clj/auto_ap/import/transactions_test.clj
index ad982856..e374bc15 100644
--- a/test/clj/auto_ap/import/transactions_test.clj
+++ b/test/clj/auto_ap/import/transactions_test.clj
@@ -71,7 +71,6 @@
bank-account
{}))))))
-
(t/deftest transaction->txs
(t/testing "Should import and code transactions"
(t/testing "Should import one transaction"
@@ -83,8 +82,8 @@
:client/locations ["Z" "E"]
:client/bank-accounts ["bank-account-id"]}]))
result (sut/transaction->txs base-transaction
- (dc/pull (dc/db conn) sut/bank-account-pull bank-account-id)
- noop-rule)]
+ (dc/pull (dc/db conn) sut/bank-account-pull bank-account-id)
+ noop-rule)]
(t/is (= (assoc base-transaction
:transaction/approval-status :transaction-approval-status/unapproved
:transaction/bank-account bank-account-id
@@ -92,11 +91,11 @@
result))))
(t/testing "Should apply a default vendor"
- (let [ {:strs [test-client-id test-bank-account-id test-vendor-id]} (setup-test-data [])
+ (let [{:strs [test-client-id test-bank-account-id test-vendor-id]} (setup-test-data [])
result (sut/transaction->txs (assoc base-transaction
:transaction/default-vendor test-vendor-id)
- (dc/pull (dc/db conn) sut/bank-account-pull test-bank-account-id)
- noop-rule)]
+ (dc/pull (dc/db conn) sut/bank-account-pull test-bank-account-id)
+ noop-rule)]
(t/is (= (assoc base-transaction
:transaction/approval-status :transaction-approval-status/unapproved
:transaction/bank-account test-bank-account-id
@@ -125,21 +124,20 @@
:transaction/description-original "CHECK 10001"
:transaction/check-number 10001
:transaction/amount -30.0)
- (dc/pull (dc/db conn ) sut/bank-account-pull bank-account-id)
+ (dc/pull (dc/db conn) sut/bank-account-pull bank-account-id)
noop-rule)]
-
+
(t/is (= {:db/id payment-id
:payment/status :payment-status/cleared}
(:transaction/payment transaction-result))))
-
(t/testing "Should match a check that matches on amount if check number does not match"
(let [transaction-result (sut/transaction->txs (assoc base-transaction
:transaction/description-original "CHECK 12301"
:transaction/amount -30.0)
- (dc/pull (dc/db conn ) sut/bank-account-pull bank-account-id)
+ (dc/pull (dc/db conn) sut/bank-account-pull bank-account-id)
noop-rule)]
-
+
(t/is (= {:db/id payment-id
:payment/status :payment-status/cleared}
(:transaction/payment transaction-result)))))
@@ -151,11 +149,10 @@
:transaction/amount -30.0)
(dc/pull (dc/db conn) sut/bank-account-pull bank-account-id)
noop-rule)]
-
+
(t/is (= nil
(:transaction/payment result)))))))
-
(t/testing "Should match expected-deposits"
(let [{:strs [bank-account-id client-id expected-deposit-id]} (->> [#:expected-deposit {:client "client-id"
:date #inst "2021-07-01T00:00:00-08:00"
@@ -174,7 +171,6 @@
deref
:tempids)]
-
(t/testing "Should match within 10 days"
(let [transaction-result (sut/transaction->txs (assoc base-transaction
:transaction/date #inst "2021-07-03T00:00:00-08:00"
@@ -183,7 +179,7 @@
noop-rule)]
(t/is (= expected-deposit-id
(:db/id (sut/find-expected-deposit client-id 100.0 (clj-time.coerce/to-date-time #inst "2021-07-03T00:00:00-08:00")))))
-
+
(t/is (= {:db/id expected-deposit-id
:expected-deposit/status :expected-deposit-status/cleared}
(:transaction/expected-deposit transaction-result)))))
@@ -194,7 +190,7 @@
(dc/pull (dc/db conn) sut/bank-account-pull bank-account-id)
noop-rule)]
(t/is (= :vendor/ccp-square
- (:transaction/vendor transaction-result)))))
+ (:transaction/vendor transaction-result)))))
(t/testing "Should credit CCP"
(let [transaction-result (sut/transaction->txs (assoc base-transaction
@@ -262,7 +258,6 @@
first
:transaction/raw-id)))))
-
(t/deftest match-transaction-to-single-unfulfilled-payments
(t/testing "Auto-pay Invoices"
(let [{:strs [vendor1-id vendor2-id]} (->> [#:vendor {:name "Autopay vendor 1"
@@ -281,12 +276,11 @@
:total 30.0
:db/id "invoice-id"}
#:client {:name "Client" :db/id "client-id"}]
- (dc/transact conn)
- deref
- :tempids)
+ (dc/transact conn)
+ deref
+ :tempids)
invoices-matches (sut/match-transaction-to-single-unfulfilled-autopayments -30.0 client-id)]
- (t/is (= 1 (count invoices-matches)))
- ))
+ (t/is (= 1 (count invoices-matches)))))
(t/testing "Should not match paid invoice that isn't a scheduled payment"
(let [{:strs [client-id]} (->> [#:invoice{:status :invoice-status/paid
@@ -300,13 +294,13 @@
deref
:tempids)
invoices-matches (sut/match-transaction-to-single-unfulfilled-autopayments -30.0 client-id)]
-
+
(t/is (= [] invoices-matches))))
(t/testing "Should not match unpaid invoice"
(let [{:strs [client-id]} (->> [#:invoice {:status :invoice-status/unpaid
:scheduled-payment #inst "2019-01-04"
- :vendor vendor1-id
+ :vendor vendor1-id
:date #inst "2019-01-01"
:client "client-id"
:total 30.0
@@ -316,7 +310,7 @@
deref
:tempids)
invoices-matches (sut/match-transaction-to-single-unfulfilled-autopayments -30.0 client-id)]
-
+
(t/is (= [] invoices-matches))))
(t/testing "Should not match invoice that already has a payment"
@@ -335,7 +329,7 @@
deref
:tempids)
invoices-matches (sut/match-transaction-to-single-unfulfilled-autopayments -30.0
- client-id)]
+ client-id)]
(t/is (= [] invoices-matches))))
(t/testing "Should match multiple invoices for same vendor that total to transaction amount"
(let [{:strs [client-id]} (->> [#:invoice {:status :invoice-status/paid
@@ -436,45 +430,41 @@
(t/is (= [] (sut/match-transaction-to-single-unfulfilled-autopayments -31.0 client-id))
(str "Expected to not match, because there is invoice-3 is between invoice-1 and invoice-2.")))))))
-
-
-
-
#_(t/testing "Auto-pay Invoices"
(t/testing "Should match paid invoice that doesn't have a payment yet"
(let [{:strs [bank-account-id client-id invoice1-id invoice2-id vendor-id]} (->> [#:invoice {:status :invoice-status/paid
- :vendor "vendor-id"
- :scheduled-payment #inst "2019-01-04"
- :date #inst "2019-01-01"
- :client "client-id"
- :total 20.0
- :db/id "invoice1-id"}
- #:invoice {:status :invoice-status/paid
- :vendor "vendor-id"
- :scheduled-payment #inst "2019-01-04"
- :date #inst "2019-01-01"
- :client "client-id"
- :total 10.0
- :db/id "invoice2-id"}
- #:vendor {:name "Autopay vendor"
- :db/id "vendor-id"}
- #:bank-account {:name "Bank account"
- :db/id "bank-account-id"}
- #:client {:name "Client"
- :db/id "client-id"
- :bank-accounts ["bank-account-id"]}]
- (d/transact (d/connect uri))
- deref
- :tempids)
+ :vendor "vendor-id"
+ :scheduled-payment #inst "2019-01-04"
+ :date #inst "2019-01-01"
+ :client "client-id"
+ :total 20.0
+ :db/id "invoice1-id"}
+ #:invoice {:status :invoice-status/paid
+ :vendor "vendor-id"
+ :scheduled-payment #inst "2019-01-04"
+ :date #inst "2019-01-01"
+ :client "client-id"
+ :total 10.0
+ :db/id "invoice2-id"}
+ #:vendor {:name "Autopay vendor"
+ :db/id "vendor-id"}
+ #:bank-account {:name "Bank account"
+ :db/id "bank-account-id"}
+ #:client {:name "Client"
+ :db/id "client-id"
+ :bank-accounts ["bank-account-id"]}]
+ (d/transact (d/connect uri))
+ deref
+ :tempids)
[[transaction-tx payment-tx invoice-payments1-tx invoice-payments2-tx]] (sut/yodlees->transactions [(assoc base-yodlee-transaction
- :amount {:amount 30.0}
- :bank-account {:db/id bank-account-id
- :client/_bank-accounts {:db/id client-id
- :client/locations ["A"]}})]
- :bank-account
- noop-rule
- #{})]
-
+ :amount {:amount 30.0}
+ :bank-account {:db/id bank-account-id
+ :client/_bank-accounts {:db/id client-id
+ :client/locations ["A"]}})]
+ :bank-account
+ noop-rule
+ #{})]
+
(t/is (= :transaction-approval-status/approved
(:transaction/approval-status transaction-tx))
(str "Should have approved transaction " transaction-tx))
@@ -519,14 +509,14 @@
deref
:tempids)
[[transaction-tx payment-tx]] (sut/yodlees->transactions [(assoc base-yodlee-transaction
- :amount {:amount 30.0}
- :bank-account {:db/id bank-account-id
- :client/_bank-accounts {:db/id client-id
- :client/locations ["A"]}})]
- :bank-account
- noop-rule
- #{})]
-
+ :amount {:amount 30.0}
+ :bank-account {:db/id bank-account-id
+ :client/_bank-accounts {:db/id client-id
+ :client/locations ["A"]}})]
+ :bank-account
+ noop-rule
+ #{})]
+
(t/is (= :transaction-approval-status/unapproved
(:transaction/approval-status transaction-tx)))
(t/is (nil? (:transaction/payment transaction-tx))))))
diff --git a/test/clj/auto_ap/import/yodlee_test.clj b/test/clj/auto_ap/import/yodlee_test.clj
index be1e03d2..7cd442eb 100644
--- a/test/clj/auto_ap/import/yodlee_test.clj
+++ b/test/clj/auto_ap/import/yodlee_test.clj
@@ -2,7 +2,6 @@
(:require [auto-ap.import.yodlee2 :as sut]
[clojure.test :as t]))
-
(def base-transaction {:postDate "2014-01-04"
:accountId 1234
:date "2014-01-02"
@@ -26,6 +25,6 @@
:baseType "DEBIT")
false))))
(t/is (= 12.0 (:transaction/amount (sut/yodlee->transaction (assoc base-transaction
- :amount {:amount 12.0}
- :baseType "CREDIT")
+ :amount {:amount 12.0}
+ :baseType "CREDIT")
false))))))
diff --git a/test/clj/auto_ap/integration/graphql.clj b/test/clj/auto_ap/integration/graphql.clj
index 1ecbb196..a6d0feb3 100644
--- a/test/clj/auto_ap/integration/graphql.clj
+++ b/test/clj/auto_ap/integration/graphql.clj
@@ -6,7 +6,6 @@
[auto-ap.integration.util :refer [wrap-setup admin-token user-token setup-test-data test-transaction]]
[auto-ap.datomic :refer [conn]]))
-
(defn new-client [args]
(merge {:client/name "Test client"
:client/code (.toString (java.util.UUID/randomUUID))
@@ -29,7 +28,7 @@
(deftest transaction-page
(testing "transaction page"
(let [{:strs [test-client-id]} (setup-test-data [(test-transaction :transaction/description-original "hi")])]
-
+
(testing "It should find all transactions"
(let [result (:transaction-page (:data (sut/query (admin-token) "{ transaction_page(filters: {}) { count, start, data { id } }}" {:clients [{:db/id test-client-id}]})))]
(is (= 1 (:count result)))
@@ -42,13 +41,12 @@
(is (= 0 (:start result)))
(is (= 0 (count (:data result)))))))))
-
(deftest invoice-page
(testing "invoice page"
@(dc/transact conn
- [(new-client {:db/id "client"})
- (new-invoice {:invoice/client "client"
- :invoice/status :invoice-status/paid})])
+ [(new-client {:db/id "client"})
+ (new-invoice {:invoice/client "client"
+ :invoice/status :invoice-status/paid})])
(testing "It should find all invoices"
(let [result (first (:invoice-page (:data (sut/query (admin-token) "{ invoice_page(filters: { status:paid}) { count, start, invoices { id } }}"))))]
(is (= 1 (:count result)))
@@ -69,7 +67,6 @@
(is (int? (:start result)))
(is (seqable? (:journal-entries result)))))))
-
(deftest vendors
(testing "vendors"
(testing "it should find vendors"
@@ -88,17 +85,17 @@
(is (seqable? (:transaction-rules result))))))
(deftest upsert-transaction-rule
- (let [{:strs [vendor-id account-id yodlee-merchant-id]} (->
- @(dc/transact
- conn
- [{:vendor/name "Bryce's Meat Co"
- :db/id "vendor-id"}
- {:account/name "hello"
- :db/id "account-id"}
- {:yodlee-merchant/name "yodlee"
- :db/id "yodlee-merchant-id"}])
-
- :tempids)]
+ (let [{:strs [vendor-id account-id yodlee-merchant-id]} (->
+ @(dc/transact
+ conn
+ [{:vendor/name "Bryce's Meat Co"
+ :db/id "vendor-id"}
+ {:account/name "hello"
+ :db/id "account-id"}
+ {:yodlee-merchant/name "yodlee"
+ :db/id "yodlee-merchant-id"}])
+
+ :tempids)]
(testing "it should reject rules that don't add up to 100%"
(let [q (v/graphql-query {:venia/operation {:operation/type :mutation
:operation/name "UpsertTransactionRule"}
@@ -106,10 +103,9 @@
{:transaction-rule {:accounts [{:account-id account-id
:percentage "0.25"
:location "Shared"}]}}
- [:id ]])}]})]
+ [:id]])}]})]
(is (thrown? clojure.lang.ExceptionInfo (sut/query (admin-token) q)))))
-
(testing "It should reject rules that are missing both description and merchant"
(let [q (v/graphql-query {:venia/operation {:operation/type :mutation
:operation/name "UpsertTransactionRule"}
@@ -117,7 +113,7 @@
{:transaction-rule {:accounts [{:account-id account-id
:percentage "1.0"
:location "Shared"}]}}
- [:id ]])}]})]
+ [:id]])}]})]
(is (thrown? clojure.lang.ExceptionInfo (sut/query (admin-token) q)))))
(testing "it should add rules"
(let [q (v/graphql-query {:venia/operation {:operation/type :mutation
@@ -141,12 +137,12 @@
result (-> (sut/query (admin-token) q)
:data
:upsert-transaction-rule)]
-
+
(is (= "123" (:description result)))
(is (= "Bryce's Meat Co" (-> result :vendor :name)))
(is (= "yodlee" (-> result :yodlee-merchant :name)))
(is (= :approved (:transaction-approval-status result)))
- (is (= "hello" (-> result :accounts (get 0) :account :name )))
+ (is (= "hello" (-> result :accounts (get 0) :account :name)))
(is (:id result))
(testing "it should unset removed fields"
@@ -185,40 +181,39 @@
(is (= 1 (count (:accounts result))))))))))
-
(deftest test-transaction-rule
(testing "it should match rules"
(let [matching-transaction @(dc/transact conn
- [{:transaction/description-original "matching-desc"
- :transaction/date #inst "2019-01-05T00:00:00.000-08:00"
- :transaction/client {:client/name "1"
- :db/id "client-1"}
- :transaction/bank-account {:db/id "bank-account-1"
- :bank-account/name "1"}
+ [{:transaction/description-original "matching-desc"
+ :transaction/date #inst "2019-01-05T00:00:00.000-08:00"
+ :transaction/client {:client/name "1"
+ :db/id "client-1"}
+ :transaction/bank-account {:db/id "bank-account-1"
+ :bank-account/name "1"}
- :transaction/amount 1.00
- :transaction/id "2019-01-05 matching-desc 1"
- :db/id "a"}
+ :transaction/amount 1.00
+ :transaction/id "2019-01-05 matching-desc 1"
+ :db/id "a"}
- {:transaction/description-original "nonmatching-desc"
- :transaction/client {:client/name "2"
- :db/id "client-2"}
- :transaction/bank-account {:db/id "bank-account-2"
- :bank-account/name "2"}
- :transaction/date #inst "2019-01-15T23:23:00.000-08:00"
- :transaction/amount 2.00
- :transaction/id "2019-01-15 nonmatching-desc 2"
- :db/id "b"}])
+ {:transaction/description-original "nonmatching-desc"
+ :transaction/client {:client/name "2"
+ :db/id "client-2"}
+ :transaction/bank-account {:db/id "bank-account-2"
+ :bank-account/name "2"}
+ :transaction/date #inst "2019-01-15T23:23:00.000-08:00"
+ :transaction/amount 2.00
+ :transaction/id "2019-01-15 nonmatching-desc 2"
+ :db/id "b"}])
{:strs [a b client-1 client-2 bank-account-1 bank-account-2]} (get-in matching-transaction [:tempids])
a (str a)
b (str b)
rule-test (fn [rule]
(-> (sut/query (admin-token) (v/graphql-query {:venia/operation {:operation/type :query
- :operation/name "TestTransactionRule"}
- :venia/queries [{:query/data (sut/->graphql [:test-transaction-rule
- {:transaction-rule rule}
- [:id]])}]}))
+ :operation/name "TestTransactionRule"}
+ :venia/queries [{:query/data (sut/->graphql [:test-transaction-rule
+ {:transaction-rule rule}
+ [:id]])}]}))
:data
:test-transaction-rule))]
(testing "based on date "
@@ -233,8 +228,8 @@
(testing "based on amount"
(is (= [{:id a}] (rule-test {:amount-gte 1.0 :amount-lte 1.0})))
- (is (= (set [{:id a} {:id b}]) (set (rule-test {:amount-gte 1.0 }))) )
- (is (= (set [{:id a} {:id b}]) (set (rule-test {:amount-lte 2.0 }))) ))
+ (is (= (set [{:id a} {:id b}]) (set (rule-test {:amount-gte 1.0}))))
+ (is (= (set [{:id a} {:id b}]) (set (rule-test {:amount-lte 2.0})))))
(testing "based on client"
(is (= [{:id a}] (rule-test {:client-id (str client-1)})))
@@ -247,40 +242,40 @@
(deftest test-match-transaction-rule
(testing "it should apply a rules"
(let [{:strs [transaction-id transaction-rule-id uneven-transaction-rule-id]} (-> @(dc/transact conn
- [{:transaction/description-original "matching-desc"
- :transaction/date #inst "2019-01-05T00:00:00.000-08:00"
- :transaction/client {:client/name "1"
- :db/id "client-1"}
- :transaction/bank-account {:db/id "bank-account-1"
- :bank-account/name "1"}
- :transaction/amount 1.00
- :db/id "transaction-id"}
+ [{:transaction/description-original "matching-desc"
+ :transaction/date #inst "2019-01-05T00:00:00.000-08:00"
+ :transaction/client {:client/name "1"
+ :db/id "client-1"}
+ :transaction/bank-account {:db/id "bank-account-1"
+ :bank-account/name "1"}
+ :transaction/amount 1.00
+ :db/id "transaction-id"}
- {:db/id "transaction-rule-id"
- :transaction-rule/note "transaction rule note"
- :transaction-rule/description "matching-desc"
- :transaction-rule/accounts [{:transaction-rule-account/location "A"
- :transaction-rule-account/account {:account/numeric-code 123 :db/id "123"}
- :transaction-rule-account/percentage 1.0}]}
- {:db/id "uneven-transaction-rule-id"
- :transaction-rule/note "transaction rule note"
- :transaction-rule/description "matching-desc"
- :transaction-rule/accounts [{:transaction-rule-account/location "A"
- :transaction-rule-account/account {:account/numeric-code 123 :db/id "123"}
- :transaction-rule-account/percentage 0.3333333}
- {:transaction-rule-account/location "B"
- :transaction-rule-account/account {:account/numeric-code 123 :db/id "123"}
- :transaction-rule-account/percentage 0.33333333}
- {:transaction-rule-account/location "c"
- :transaction-rule-account/account {:account/numeric-code 123 :db/id "123"}
- :transaction-rule-account/percentage 0.333333}]}])
- :tempids)
+ {:db/id "transaction-rule-id"
+ :transaction-rule/note "transaction rule note"
+ :transaction-rule/description "matching-desc"
+ :transaction-rule/accounts [{:transaction-rule-account/location "A"
+ :transaction-rule-account/account {:account/numeric-code 123 :db/id "123"}
+ :transaction-rule-account/percentage 1.0}]}
+ {:db/id "uneven-transaction-rule-id"
+ :transaction-rule/note "transaction rule note"
+ :transaction-rule/description "matching-desc"
+ :transaction-rule/accounts [{:transaction-rule-account/location "A"
+ :transaction-rule-account/account {:account/numeric-code 123 :db/id "123"}
+ :transaction-rule-account/percentage 0.3333333}
+ {:transaction-rule-account/location "B"
+ :transaction-rule-account/account {:account/numeric-code 123 :db/id "123"}
+ :transaction-rule-account/percentage 0.33333333}
+ {:transaction-rule-account/location "c"
+ :transaction-rule-account/account {:account/numeric-code 123 :db/id "123"}
+ :transaction-rule-account/percentage 0.333333}]}])
+ :tempids)
rule-test (-> (sut/query (admin-token) (v/graphql-query {:venia/operation {:operation/type :mutation
:operation/name "MatchTransactionRules"}
:venia/queries [{:query/data (sut/->graphql [:match-transaction-rules
{:transaction-rule-id transaction-rule-id
:transaction-ids [transaction-id]}
- [[:matched-rule [:id :note]] [:accounts [:id]] ]])}]}))
+ [[:matched-rule [:id :note]] [:accounts [:id]]]])}]}))
:data
:match-transaction-rules)]
@@ -293,7 +288,7 @@
:venia/queries [{:query/data (sut/->graphql [:match-transaction-rules
{:transaction-rule-id transaction-rule-id
:transaction-ids [transaction-id]}
- [[:matched-rule [:id :note]] [:accounts [:id]] ]])}]}))
+ [[:matched-rule [:id :note]] [:accounts [:id]]]])}]}))
:data
:match-transaction-rules)]
(is (= 1 (-> rule-test first :accounts count)))))
@@ -304,7 +299,7 @@
:venia/queries [{:query/data (sut/->graphql [:match-transaction-rules
{:transaction-rule-id uneven-transaction-rule-id
:transaction-ids [transaction-id]}
- [[:matched-rule [:id :note]] [:accounts [:id :amount]] ]])}]}))
+ [[:matched-rule [:id :note]] [:accounts [:id :amount]]]])}]}))
:data
:match-transaction-rules)]
(is (= 3 (-> rule-test first :accounts count)))
diff --git a/test/clj/auto_ap/integration/graphql/accounts.clj b/test/clj/auto_ap/integration/graphql/accounts.clj
index 0fedb0dd..6e2b4594 100644
--- a/test/clj/auto_ap/integration/graphql/accounts.clj
+++ b/test/clj/auto_ap/integration/graphql/accounts.clj
@@ -10,206 +10,206 @@
#_(deftest test-account-search
- (with-redefs [auto-ap.solr/impl (auto-ap.solr/->InMemSolrClient (atom {}))]
- (testing "It should find matching account names"
- @(dc/transact conn [{:account/name "Food Research"
- :db/ident :client-specific-account
- :account/numeric-code 51100
- :account/search-terms "Food Research"
- :account/applicability :account-applicability/global
- :account/default-allowance :allowance/allowed}])
- (sut/rebuild-search-index)
- (clojure.pprint/pprint auto-ap.solr/impl)
- (is (> (count (sut/search {:id (admin-token)}
- {:query "Food Research"}
- nil))
- 0)))
- (testing "It should find exact matches by numbers"
- (is (= (count (sut/search {:id (admin-token)}
- {:query "51100"}
- nil))
- 1)))
- (testing "It should filter out accounts that are not allowed for clients"
- @(dc/transact conn [{:account/name "CLIENT SPECIFIC"
- :db/ident :client-specific-account
- :account/numeric-code 99999
- :account/search-terms "CLIENTSPECIFIC"
- :account/applicability :account-applicability/customized
- :account/default-allowance :allowance/allowed}])
- (sut/rebuild-search-index)
- (is (= [] (sut/search {:id (admin-token)}
- {:query "CLIENTSPECIFIC"}
- nil)))
-
- (testing "It should show up for the client specific version"
- (let [client-id (-> @(dc/transact conn [{:client/name "CLIENT"
- :db/id "client"}
- {:db/ident :client-specific-account
- :account/client-overrides [{:account-client-override/client "client"
- :account-client-override/name "HI"
- :account-client-override/search-terms "HELLOWORLD"}]}])
- :tempids
- (get "client"))]
- (sut/rebuild-search-index)
- (is (= 1 (count (sut/search {:id (admin-token)}
- {:query "HELLOWORLD"
- :client_id client-id}
- nil))))))
-
- (testing "It should hide accounts that arent applicable"
- @(dc/transact conn [{:account/name "DENIED"
- :db/ident :denied-account
- :account/numeric-code 99998
- :account/search-terms "DENIED"
+ (with-redefs [auto-ap.solr/impl (auto-ap.solr/->InMemSolrClient (atom {}))]
+ (testing "It should find matching account names"
+ @(dc/transact conn [{:account/name "Food Research"
+ :db/ident :client-specific-account
+ :account/numeric-code 51100
+ :account/search-terms "Food Research"
:account/applicability :account-applicability/global
- :account/default-allowance :allowance/denied
- :account/vendor-allowance :allowance/denied
- :account/invoice-allowance :allowance/denied}])
- (is (= 0 (count (sut/search {:id (admin-token)}
- {:query "DENIED"}
- nil))))
- (is (= 0 (count (sut/search {:id (admin-token)}
- {:query "DENIED"
- :allowance :invoice}
- nil))))
- (is (= 0 (count (sut/search {:id (admin-token)}
- {:query "DENIED"
- :allowance :vendor}
- nil)))))
-
- (testing "It should warn when using a warn account"
- @(dc/transact conn [{:account/name "WARNING"
- :db/ident :warn-account
- :account/numeric-code 99997
- :account/search-terms "WARNING"
- :account/applicability :account-applicability/global
- :account/default-allowance :allowance/warn
- :account/vendor-allowance :allowance/warn
- :account/invoice-allowance :allowance/warn}])
+ :account/default-allowance :allowance/allowed}])
(sut/rebuild-search-index)
- (is (some? (:warning (first (sut/search {:id (admin-token)}
- {:query "WARNING"
- :allowance :global}
- nil)))))
- (is (some? (:warning (first (sut/search {:id (admin-token)}
- {:query "WARNING"
- :allowance :invoice}
- nil)))))
- (is (some? (:warning (first (sut/search {:id (admin-token)}
- {:query "WARNING"
- :allowance :vendor}
- nil))))))
- (testing "It should only include admin accounts for admins"
- @(dc/transact conn [{:account/name "ADMINONLY"
- :db/ident :warn-account
- :account/numeric-code 99997
- :account/search-terms "ADMINONLY"
- :account/applicability :account-applicability/global
- :account/default-allowance :allowance/admin-only
- :account/vendor-allowance :allowance/admin-only
- :account/invoice-allowance :allowance/admin-only}])
+ (clojure.pprint/pprint auto-ap.solr/impl)
+ (is (> (count (sut/search {:id (admin-token)}
+ {:query "Food Research"}
+ nil))
+ 0)))
+ (testing "It should find exact matches by numbers"
+ (is (= (count (sut/search {:id (admin-token)}
+ {:query "51100"}
+ nil))
+ 1)))
+ (testing "It should filter out accounts that are not allowed for clients"
+ @(dc/transact conn [{:account/name "CLIENT SPECIFIC"
+ :db/ident :client-specific-account
+ :account/numeric-code 99999
+ :account/search-terms "CLIENTSPECIFIC"
+ :account/applicability :account-applicability/customized
+ :account/default-allowance :allowance/allowed}])
(sut/rebuild-search-index)
- (is (= 1 (count (sut/search {:id (admin-token)}
- {:query "ADMINONLY"}
- nil))))
- (is (= 0 (count (sut/search {:id (user-token)}
- {:query "ADMINONLY"}
- nil)))))
+ (is (= [] (sut/search {:id (admin-token)}
+ {:query "CLIENTSPECIFIC"}
+ nil)))
- (testing "It should allow searching for vendor accounts for invoices"
- (let [vendor-id (-> @(dc/transact conn [{:account/name "VENDORONLY"
- :db/id "vendor-only"
- :db/ident :vendor-only
- :account/numeric-code 99996
- :account/search-terms "VENDORONLY"
- :account/applicability :account-applicability/global
- :account/default-allowance :allowance/allowed
- :account/vendor-allowance :allowance/allowed
- :account/invoice-allowance :allowance/denied}
- {:vendor/name "Allowed"
- :vendor/default-account "vendor-only"
- :db/id "vendor"}])
- :tempids
- (get "vendor"))]
- (sut/rebuild-search-index)
+ (testing "It should show up for the client specific version"
+ (let [client-id (-> @(dc/transact conn [{:client/name "CLIENT"
+ :db/id "client"}
+ {:db/ident :client-specific-account
+ :account/client-overrides [{:account-client-override/client "client"
+ :account-client-override/name "HI"
+ :account-client-override/search-terms "HELLOWORLD"}]}])
+ :tempids
+ (get "client"))]
+ (sut/rebuild-search-index)
+ (is (= 1 (count (sut/search {:id (admin-token)}
+ {:query "HELLOWORLD"
+ :client_id client-id}
+ nil))))))
+
+ (testing "It should hide accounts that arent applicable"
+ @(dc/transact conn [{:account/name "DENIED"
+ :db/ident :denied-account
+ :account/numeric-code 99998
+ :account/search-terms "DENIED"
+ :account/applicability :account-applicability/global
+ :account/default-allowance :allowance/denied
+ :account/vendor-allowance :allowance/denied
+ :account/invoice-allowance :allowance/denied}])
(is (= 0 (count (sut/search {:id (admin-token)}
- {:query "VENDORONLY"
+ {:query "DENIED"}
+ nil))))
+ (is (= 0 (count (sut/search {:id (admin-token)}
+ {:query "DENIED"
:allowance :invoice}
nil))))
+ (is (= 0 (count (sut/search {:id (admin-token)}
+ {:query "DENIED"
+ :allowance :vendor}
+ nil)))))
+ (testing "It should warn when using a warn account"
+ @(dc/transact conn [{:account/name "WARNING"
+ :db/ident :warn-account
+ :account/numeric-code 99997
+ :account/search-terms "WARNING"
+ :account/applicability :account-applicability/global
+ :account/default-allowance :allowance/warn
+ :account/vendor-allowance :allowance/warn
+ :account/invoice-allowance :allowance/warn}])
+ (sut/rebuild-search-index)
+ (is (some? (:warning (first (sut/search {:id (admin-token)}
+ {:query "WARNING"
+ :allowance :global}
+ nil)))))
+ (is (some? (:warning (first (sut/search {:id (admin-token)}
+ {:query "WARNING"
+ :allowance :invoice}
+ nil)))))
+ (is (some? (:warning (first (sut/search {:id (admin-token)}
+ {:query "WARNING"
+ :allowance :vendor}
+ nil))))))
+ (testing "It should only include admin accounts for admins"
+ @(dc/transact conn [{:account/name "ADMINONLY"
+ :db/ident :warn-account
+ :account/numeric-code 99997
+ :account/search-terms "ADMINONLY"
+ :account/applicability :account-applicability/global
+ :account/default-allowance :allowance/admin-only
+ :account/vendor-allowance :allowance/admin-only
+ :account/invoice-allowance :allowance/admin-only}])
+ (sut/rebuild-search-index)
(is (= 1 (count (sut/search {:id (admin-token)}
- {:query "VENDORONLY"
- :allowance :invoice
- :vendor_id vendor-id}
- nil)))))))
+ {:query "ADMINONLY"}
+ nil))))
+ (is (= 0 (count (sut/search {:id (user-token)}
+ {:query "ADMINONLY"}
+ nil)))))
- (deftest get-graphql
- (testing "should retrieve a single account"
- @(dc/transact conn [{:account/numeric-code 1
- :account/default-allowance :allowance/allowed
- :account/type :account-type/asset
- :account/location "A"
- :account/name "Test"}])
+ (testing "It should allow searching for vendor accounts for invoices"
+ (let [vendor-id (-> @(dc/transact conn [{:account/name "VENDORONLY"
+ :db/id "vendor-only"
+ :db/ident :vendor-only
+ :account/numeric-code 99996
+ :account/search-terms "VENDORONLY"
+ :account/applicability :account-applicability/global
+ :account/default-allowance :allowance/allowed
+ :account/vendor-allowance :allowance/allowed
+ :account/invoice-allowance :allowance/denied}
+ {:vendor/name "Allowed"
+ :vendor/default-account "vendor-only"
+ :db/id "vendor"}])
+ :tempids
+ (get "vendor"))]
+ (sut/rebuild-search-index)
+ (is (= 0 (count (sut/search {:id (admin-token)}
+ {:query "VENDORONLY"
+ :allowance :invoice}
+ nil))))
- (is (= {:name "Test",
- :invoice_allowance nil,
- :numeric_code 1,
- :vendor_allowance nil,
- :location "A",
- :applicability nil}
- (dissoc (first (:accounts (sut/get-graphql {:id (admin-token)} {} nil)))
- :id
- :type
- :default_allowance)))))))
+ (is (= 1 (count (sut/search {:id (admin-token)}
+ {:query "VENDORONLY"
+ :allowance :invoice
+ :vendor_id vendor-id}
+ nil)))))))
-#_(deftest upsert-account
- (testing "should create a new account"
- (let [result (sut/upsert-account {:id (admin-token)} {:account {:client_overrides []
- :numeric_code 123
- :location "A"
- :applicability :global
- :account-set "global"
- :name "Test"
- :invoice-allowance :allowed
- :vendor-allowance :allowed
- :type :asset}} nil)]
- (is (= {:search_terms "Test",
- :name "Test",
- :invoice_allowance :allowed,
- :numeric_code 123,
- :code "123",
- :account_set "global",
- :vendor_allowance :allowed,
- :location "A",
- :applicability :global}
- (dissoc result
- :id
- :type
- :default_allowance)))
- (testing "Should allow updating account"
- (let [edit-result (sut/upsert-account {:id (admin-token)} {:account {:client_overrides []
- :id (:id result)
- :numeric_code 890
- :location "B"
- :applicability :global
- :account-set "global"
- :name "Hello"
- :invoice-allowance :denied
- :vendor-allowance :denied
- :type :expense}} nil)]
- (is (= {:search_terms "Hello",
- :name "Hello",
- :invoice_allowance :denied,
- :code "123",
- :account_set "global",
- :vendor_allowance :denied,
- :location "B",
- :applicability :global}
- (dissoc edit-result
+ (deftest get-graphql
+ (testing "should retrieve a single account"
+ @(dc/transact conn [{:account/numeric-code 1
+ :account/default-allowance :allowance/allowed
+ :account/type :account-type/asset
+ :account/location "A"
+ :account/name "Test"}])
+
+ (is (= {:name "Test",
+ :invoice_allowance nil,
+ :numeric_code 1,
+ :vendor_allowance nil,
+ :location "A",
+ :applicability nil}
+ (dissoc (first (:accounts (sut/get-graphql {:id (admin-token)} {} nil)))
:id
:type
- :default_allowance
- :numeric_code)))
- (testing "Should not allow changing numeric code"
+ :default_allowance)))))))
- (is (= 123 (:numeric_code edit-result)))))))))
+#_(deftest upsert-account
+ (testing "should create a new account"
+ (let [result (sut/upsert-account {:id (admin-token)} {:account {:client_overrides []
+ :numeric_code 123
+ :location "A"
+ :applicability :global
+ :account-set "global"
+ :name "Test"
+ :invoice-allowance :allowed
+ :vendor-allowance :allowed
+ :type :asset}} nil)]
+ (is (= {:search_terms "Test",
+ :name "Test",
+ :invoice_allowance :allowed,
+ :numeric_code 123,
+ :code "123",
+ :account_set "global",
+ :vendor_allowance :allowed,
+ :location "A",
+ :applicability :global}
+ (dissoc result
+ :id
+ :type
+ :default_allowance)))
+ (testing "Should allow updating account"
+ (let [edit-result (sut/upsert-account {:id (admin-token)} {:account {:client_overrides []
+ :id (:id result)
+ :numeric_code 890
+ :location "B"
+ :applicability :global
+ :account-set "global"
+ :name "Hello"
+ :invoice-allowance :denied
+ :vendor-allowance :denied
+ :type :expense}} nil)]
+ (is (= {:search_terms "Hello",
+ :name "Hello",
+ :invoice_allowance :denied,
+ :code "123",
+ :account_set "global",
+ :vendor_allowance :denied,
+ :location "B",
+ :applicability :global}
+ (dissoc edit-result
+ :id
+ :type
+ :default_allowance
+ :numeric_code)))
+ (testing "Should not allow changing numeric code"
+
+ (is (= 123 (:numeric_code edit-result)))))))))
diff --git a/test/clj/auto_ap/integration/graphql/checks.clj b/test/clj/auto_ap/integration/graphql/checks.clj
index ddc4d34c..deef551c 100644
--- a/test/clj/auto_ap/integration/graphql/checks.clj
+++ b/test/clj/auto_ap/integration/graphql/checks.clj
@@ -16,7 +16,7 @@
(use-fixtures :each wrap-setup)
(defn sample-payment [& kwargs]
- (apply assoc
+ (apply assoc
{:db/id "check-id"
:payment/check-number 1000
:payment/bank-account "bank-id"
@@ -28,31 +28,30 @@
:payment/date #inst "2022-01-01"}
kwargs))
-
(deftest get-payment-page
(testing "Should list payments"
(let [{{:strs [bank-id check-id client-id]} :tempids} @(d/transact conn [{:bank-account/code "bank"
- :db/id "bank-id"}
- {:client/code "client"
- :db/id "client-id"}
- {:db/id "check-id"
- :payment/check-number 1000
- :payment/bank-account "bank-id"
- :payment/client "client-id"
- :payment/type :payment-type/check
- :payment/amount 123.50
- :payment/paid-to "Someone"
- :payment/status :payment-status/pending
- :payment/date #inst "2022-01-01"}])]
- (is (= [ {:amount 123.5,
- :type :check,
- :bank_account {:id bank-id, :code "bank"},
- :client {:id client-id, :code "client"},
- :status :pending,
- :id check-id,
- :paid_to "Someone",
- :_payment [],
- :check_number 1000}],
+ :db/id "bank-id"}
+ {:client/code "client"
+ :db/id "client-id"}
+ {:db/id "check-id"
+ :payment/check-number 1000
+ :payment/bank-account "bank-id"
+ :payment/client "client-id"
+ :payment/type :payment-type/check
+ :payment/amount 123.50
+ :payment/paid-to "Someone"
+ :payment/status :payment-status/pending
+ :payment/date #inst "2022-01-01"}])]
+ (is (= [{:amount 123.5,
+ :type :check,
+ :bank_account {:id bank-id, :code "bank"},
+ :client {:id client-id, :code "client"},
+ :status :pending,
+ :id check-id,
+ :paid_to "Someone",
+ :_payment [],
+ :check_number 1000}],
(map #(dissoc % :date) (:payments (first (sut/get-payment-page {:clients [{:db/id client-id}]} {} nil))))))
(testing "Should omit clients that can't be seen"
(is (not (seq (:payments (first (sut/get-payment-page {:clients nil} {} nil))))))
@@ -76,47 +75,43 @@
:payments
seq)))
(is (-> (sut/get-payment-page {:clients [{:db/id client-id}]} {:filters {:date_range {:end #inst "2022-01-02"}}} nil)
- first
- :payments
- seq))))
-
- )
-
- )
+ first
+ :payments
+ seq))))))
(deftest void-payment
(testing "Should void payments"
(let [{{:strs [bank-id check-id client-id]} :tempids} @(d/transact conn [{:bank-account/code "bank"
- :db/id "bank-id"}
- {:client/code "client"
- :db/id "client-id"}
- (sample-payment :db/id "check-id")])]
+ :db/id "bank-id"}
+ {:client/code "client"
+ :db/id "client-id"}
+ (sample-payment :db/id "check-id")])]
(sut/void-payment {:id (admin-token)} {:payment_id check-id} nil)
- (is (= :payment-status/voided (-> (d/pull (d/db conn) [{:payment/status [:db/ident ]}] check-id)
+ (is (= :payment-status/voided (-> (d/pull (d/db conn) [{:payment/status [:db/ident]}] check-id)
:payment/status
:db/ident)))))
(testing "Should not void payments if account is locked"
(let [{{:strs [check-id]} :tempids} @(d/transact conn [{:bank-account/code "bank"
- :db/id "bank-id"}
- {:client/code "client"
- :db/id "client-id"
- :client/locked-until #inst "2030-01-01"}
- (sample-payment :payment/client "client-id"
- :db/id "check-id"
- :payment/date #inst "2020-01-01")])]
+ :db/id "bank-id"}
+ {:client/code "client"
+ :db/id "client-id"
+ :client/locked-until #inst "2030-01-01"}
+ (sample-payment :payment/client "client-id"
+ :db/id "check-id"
+ :payment/date #inst "2020-01-01")])]
(is (thrown? Exception (sut/void-payment {:id (admin-token)} {:payment_id check-id} nil))))))
(deftest void-payments
(testing "bulk void"
(testing "Should bulk void payments if account is not locked"
(let [{{:strs [check-id]} :tempids} @(d/transact conn [{:bank-account/code "bank"
- :db/id "bank-id"}
- {:client/code "client-new"
- :db/id "client-id"}
- (sample-payment :payment/client "client-id"
- :db/id "check-id"
- :payment/date #inst "2020-01-01")])]
+ :db/id "bank-id"}
+ {:client/code "client-new"
+ :db/id "client-id"}
+ (sample-payment :payment/client "client-id"
+ :db/id "check-id"
+ :payment/date #inst "2020-01-01")])]
(sut/void-payments {:id (admin-token)} {:filters {:date_range {:start #inst "2000-01-01"}}} nil)
(is (= :payment-status/voided (-> (d/pull (d/db conn) '[{:payment/status [:db/ident]}] check-id)
:payment/status
@@ -124,12 +119,12 @@
(testing "Should only void a payment if it matches filter criteria"
(let [{{:strs [check-id]} :tempids} @(d/transact conn [{:bank-account/code "bank"
- :db/id "bank-id"}
- {:client/code "client-new"
- :db/id "client-id"}
- (sample-payment :payment/client "client-id"
- :db/id "check-id"
- :payment/date #inst "2020-01-01")])]
+ :db/id "bank-id"}
+ {:client/code "client-new"
+ :db/id "client-id"}
+ (sample-payment :payment/client "client-id"
+ :db/id "check-id"
+ :payment/date #inst "2020-01-01")])]
(sut/void-payments {:id (admin-token)} {:filters {:date_range {:start #inst "2022-01-01"}}} nil)
(is (= :payment-status/pending (-> (d/pull (d/db conn) '[{:payment/status [:db/ident]}] check-id)
:payment/status
@@ -137,13 +132,13 @@
(testing "Should not bulk void payments if account is locked"
(let [{{:strs [check-id]} :tempids} @(d/transact conn [{:bank-account/code "bank"
- :db/id "bank-id"}
- {:client/code "client"
- :db/id "client-id"
- :client/locked-until #inst "2030-01-01"}
- (sample-payment :payment/client "client-id"
- :db/id "check-id"
- :payment/date #inst "2020-01-01")])]
+ :db/id "bank-id"}
+ {:client/code "client"
+ :db/id "client-id"
+ :client/locked-until #inst "2030-01-01"}
+ (sample-payment :payment/client "client-id"
+ :db/id "check-id"
+ :payment/date #inst "2020-01-01")])]
(sut/void-payments {:id (admin-token)} {:filters {:date_range {:start #inst "2000-01-01"}}} nil)
(is (= :payment-status/pending (-> (d/pull (d/db conn) '[{:payment/status [:db/ident]}] check-id)
:payment/status
@@ -151,38 +146,37 @@
(testing "Only admins should be able to bulk void"
(let [{{:strs [check-id]} :tempids} @(d/transact conn [{:bank-account/code "bank"
- :db/id "bank-id"}
- {:client/code "client"
- :db/id "client-id"}
- (sample-payment :payment/client "client-id"
- :db/id "check-id"
- :payment/date #inst "2020-01-01")])]
+ :db/id "bank-id"}
+ {:client/code "client"
+ :db/id "client-id"}
+ (sample-payment :payment/client "client-id"
+ :db/id "check-id"
+ :payment/date #inst "2020-01-01")])]
(is (thrown? Exception (sut/void-payments {:id (user-token)} {:filters {:date_range {:start #inst "2000-01-01"}}} nil)))))))
-
(deftest print-checks
(testing "Print checks"
(testing "Should allow 'printing' cash checks"
(let [{{:strs [invoice-id client-id bank-id]} :tempids} @(d/transact conn [{:client/code "client"
- :db/id "client-id"
- :client/locked-until #inst "2030-01-01"
- :client/bank-accounts [{:bank-account/code "bank"
- :db/id "bank-id"}]}
- {:db/id "vendor-id"
- :vendor/name "V"
- :vendor/default-account "account-id"}
- {:db/id "account-id"
- :account/name "My account"
- :account/numeric-code 21000}
- {:db/id "invoice-id"
- :invoice/client "client-id"
- :invoice/date #inst "2022-01-01"
- :invoice/vendor "vendor-id"
- :invoice/total 30.0
- :invoice/outstanding-balance 30.0
- :invoice/expense-accounts [{:db/id "invoice-expense-account"
- :invoice-expense-account/account "account-id"
- :invoice-expense-account/amount 30.0}]}])]
+ :db/id "client-id"
+ :client/locked-until #inst "2030-01-01"
+ :client/bank-accounts [{:bank-account/code "bank"
+ :db/id "bank-id"}]}
+ {:db/id "vendor-id"
+ :vendor/name "V"
+ :vendor/default-account "account-id"}
+ {:db/id "account-id"
+ :account/name "My account"
+ :account/numeric-code 21000}
+ {:db/id "invoice-id"
+ :invoice/client "client-id"
+ :invoice/date #inst "2022-01-01"
+ :invoice/vendor "vendor-id"
+ :invoice/total 30.0
+ :invoice/outstanding-balance 30.0
+ :invoice/expense-accounts [{:db/id "invoice-expense-account"
+ :invoice-expense-account/account "account-id"
+ :invoice-expense-account/amount 30.0}]}])]
(let [paid-invoice (-> (sut/print-checks {:id (admin-token)} {:invoice_payments [{:invoice_id invoice-id
:amount 30.0}]
:client_id client-id
@@ -200,36 +194,36 @@
:amount))))
(testing "Should create a transaction for cash payments"
(is (seq (d/q '[:find (pull ?t [* {:transaction/payment [*]}])
- :in $ ?p
- :where [?t :transaction/payment]
- [?t :transaction/amount -30.0]]
- (d/db conn)
- (-> paid-invoice
- :payments
- first
- :payment
- :id))))))))
+ :in $ ?p
+ :where [?t :transaction/payment]
+ [?t :transaction/amount -30.0]]
+ (d/db conn)
+ (-> paid-invoice
+ :payments
+ first
+ :payment
+ :id))))))))
(testing "Should allow 'printing' debit checks"
(let [{{:strs [invoice-id client-id bank-id]} :tempids} @(d/transact conn [{:client/code "client"
- :db/id "client-id"
- :client/bank-accounts [{:bank-account/code "bank"
- :db/id "bank-id"}]}
- {:db/id "vendor-id"
- :vendor/name "V"
- :vendor/default-account "account-id"}
- {:db/id "account-id"
- :account/name "My account"
- :account/numeric-code 21000}
- {:db/id "invoice-id"
- :invoice/client "client-id"
- :invoice/date #inst "2022-01-01"
- :invoice/vendor "vendor-id"
- :invoice/total 50.0
- :invoice/outstanding-balance 50.0
- :invoice/expense-accounts [{:db/id "invoice-expense-account"
- :invoice-expense-account/account "account-id"
- :invoice-expense-account/amount 50.0}]}])]
+ :db/id "client-id"
+ :client/bank-accounts [{:bank-account/code "bank"
+ :db/id "bank-id"}]}
+ {:db/id "vendor-id"
+ :vendor/name "V"
+ :vendor/default-account "account-id"}
+ {:db/id "account-id"
+ :account/name "My account"
+ :account/numeric-code 21000}
+ {:db/id "invoice-id"
+ :invoice/client "client-id"
+ :invoice/date #inst "2022-01-01"
+ :invoice/vendor "vendor-id"
+ :invoice/total 50.0
+ :invoice/outstanding-balance 50.0
+ :invoice/expense-accounts [{:db/id "invoice-expense-account"
+ :invoice-expense-account/account "account-id"
+ :invoice-expense-account/amount 50.0}]}])]
(let [paid-invoice (-> (sut/print-checks {:id (admin-token)} {:invoice_payments [{:invoice_id invoice-id
:amount 50.0}]
:client_id client-id
@@ -259,43 +253,43 @@
(testing "Should allow printing checks"
(let [{{:strs [invoice-id client-id bank-id]} :tempids} @(d/transact conn [{:client/code "client"
- :db/id "client-id"
- :client/bank-accounts [{:bank-account/code "bank"
- :bank-account/type :bank-account-type/check
+ :db/id "client-id"
+ :client/bank-accounts [{:bank-account/code "bank"
+ :bank-account/type :bank-account-type/check
- :bank-account/check-number 10000
- :db/id "bank-id"}]}
- {:db/id "vendor-id"
- :vendor/name "V"
- :vendor/default-account "account-id"}
- {:db/id "account-id"
- :account/name "My account"
- :account/numeric-code 21000}
- {:db/id "invoice-id"
- :invoice/client "client-id"
- :invoice/date #inst "2022-01-01"
- :invoice/vendor "vendor-id"
- :invoice/total 150.0
- :invoice/outstanding-balance 150.0
- :invoice/expense-accounts [{:db/id "invoice-expense-account"
- :invoice-expense-account/account "account-id"
- :invoice-expense-account/amount 150.0}]}])]
+ :bank-account/check-number 10000
+ :db/id "bank-id"}]}
+ {:db/id "vendor-id"
+ :vendor/name "V"
+ :vendor/default-account "account-id"}
+ {:db/id "account-id"
+ :account/name "My account"
+ :account/numeric-code 21000}
+ {:db/id "invoice-id"
+ :invoice/client "client-id"
+ :invoice/date #inst "2022-01-01"
+ :invoice/vendor "vendor-id"
+ :invoice/total 150.0
+ :invoice/outstanding-balance 150.0
+ :invoice/expense-accounts [{:db/id "invoice-expense-account"
+ :invoice-expense-account/account "account-id"
+ :invoice-expense-account/amount 150.0}]}])]
(let [result (-> (sut/print-checks {:id (admin-token)} {:invoice_payments [{:invoice_id invoice-id
:amount 150.0}]
:client_id client-id
:bank_account_id bank-id
:type :check} nil)
- :invoices
- first)
+ :invoices
+ first)
paid-invoice result]
(testing "Paying full balance should complete invoice"
(is (= :paid (:status paid-invoice)))
(is (= 0.0 (:outstanding_balance paid-invoice))))
(testing "Payment should exist"
(is (= 150.0 (-> paid-invoice
- :payments
- first
- :amount))))
+ :payments
+ first
+ :amount))))
(testing "Should create pdf"
(is (-> paid-invoice
:payments
@@ -303,20 +297,19 @@
:payment
:s3_url))))))))
-
(deftest get-potential-payments
(testing "should match payments for a transaction"
(let [{:strs [transaction-id
payment-id
test-client-id]} (setup-test-data [(test-payment
- :db/id "payment-id"
- :payment/status :payment-status/pending
- :payment/amount 100.0
- :payment/date #inst "2021-05-25")
- (test-transaction
- :db/id "transaction-id"
- :transaction/amount -100.0
- :transaction/date #inst "2021-06-01")])]
+ :db/id "payment-id"
+ :payment/status :payment-status/pending
+ :payment/amount 100.0
+ :payment/date #inst "2021-05-25")
+ (test-transaction
+ :db/id "transaction-id"
+ :transaction/amount -100.0
+ :transaction/date #inst "2021-06-01")])]
(is (= [payment-id] (->> (sut/get-potential-payments {:id (admin-token) :clients [{:db/id test-client-id}]}
{:transaction_id transaction-id}
nil)
@@ -325,26 +318,26 @@
(let [{:strs [transaction-id
older-payment-id
newer-payment-id]} (setup-test-data [(test-payment
- :db/id "newer-payment-id"
- :payment/status :payment-status/pending
- :payment/amount 100.0
- :payment/date #inst "2021-05-25")
- (test-payment
- :db/id "older-payment-id"
- :payment/status :payment-status/pending
- :payment/amount 100.0
- :payment/date #inst "2021-05-20")
- (test-payment
- :db/id "payment-too-old-id"
- :payment/status :payment-status/pending
- :payment/amount 100.0
- :payment/date #inst "2021-01-01")
- (test-transaction
- :db/id "transaction-id"
- :transaction/amount -100.0
- :transaction/date #inst "2021-06-01")])]
+ :db/id "newer-payment-id"
+ :payment/status :payment-status/pending
+ :payment/amount 100.0
+ :payment/date #inst "2021-05-25")
+ (test-payment
+ :db/id "older-payment-id"
+ :payment/status :payment-status/pending
+ :payment/amount 100.0
+ :payment/date #inst "2021-05-20")
+ (test-payment
+ :db/id "payment-too-old-id"
+ :payment/status :payment-status/pending
+ :payment/amount 100.0
+ :payment/date #inst "2021-01-01")
+ (test-transaction
+ :db/id "transaction-id"
+ :transaction/amount -100.0
+ :transaction/date #inst "2021-06-01")])]
(is (= [newer-payment-id
older-payment-id] (->> (sut/get-potential-payments {:id (admin-token)}
- {:transaction_id transaction-id}
- nil)
- (map :id)))))))
+ {:transaction_id transaction-id}
+ nil)
+ (map :id)))))))
diff --git a/test/clj/auto_ap/integration/graphql/invoices.clj b/test/clj/auto_ap/integration/graphql/invoices.clj
index 85ded831..8ec1fa4f 100644
--- a/test/clj/auto_ap/integration/graphql/invoices.clj
+++ b/test/clj/auto_ap/integration/graphql/invoices.clj
@@ -77,7 +77,7 @@
:expense_accounts [{:amount 100.0
:location "DT"
:account_id new-account-id}]}}
- nil)))
+ nil)))
(is (= #:invoice{:invoice-number "890213"
:date #inst "2023-01-01T00:00:00.000-00:00"
:total 100.0
@@ -118,11 +118,11 @@
(setup-test-data [(test-invoice :db/id "invoice-id")
(test-account :db/id "new-account-id")])]
(is (some? (sut/edit-expense-accounts {:id (admin-token)}
- {:invoice_id invoice-id
- :expense_accounts [{:amount 100.0
- :account_id new-account-id
- :location "DT"}]}
- nil)))
+ {:invoice_id invoice-id
+ :expense_accounts [{:amount 100.0
+ :account_id new-account-id
+ :location "DT"}]}
+ nil)))
(is (= [#:invoice-expense-account{:amount 100.0
:location "DT"
:account {:db/id new-account-id}}]
@@ -145,7 +145,7 @@
:accounts [{:percentage 1.0
:account_id new-account-id
:location "Shared"}]}
- nil)))
+ nil)))
(is (= [#:invoice-expense-account{:amount 100.0
:location "DT"
:account {:db/id new-account-id}}]
@@ -163,35 +163,7 @@
(test-account :db/id "new-account-id")])]
(is (some? (sut/void-invoices {:id (admin-token) :clients [{:db/id test-client-id}]}
- {:filters {:client_id test-client-id}}
- nil)))
- (is (= :invoice-status/voided
- (-> (d/pull (d/db conn) [{:invoice/status [:db/ident]}]
- invoice-id)
- :invoice/status
- :db/ident)))
-
- (testing "Should unvoid invoice"
- (is (some? (sut/unvoid-invoice {:id (admin-token)}
- {:invoice_id invoice-id}
- nil)))
- (is (= :invoice-status/unpaid
- (-> (d/pull (d/db conn) [{:invoice/status [:db/ident]}]
- invoice-id)
- :invoice/status
- :db/ident)))))))
-
-
-(deftest void-invoice
- (testing "It should voide invoices in bulk"
- (let [{:strs [invoice-id]}
- (setup-test-data [(test-invoice :db/id "invoice-id"
- :invoice/status :invoice-status/unpaid)
- (test-account :db/id "new-account-id")])]
-
-
- (is (some? (sut/void-invoice {:id (admin-token)}
- {:invoice_id invoice-id}
+ {:filters {:client_id test-client-id}}
nil)))
(is (= :invoice-status/voided
(-> (d/pull (d/db conn) [{:invoice/status [:db/ident]}]
@@ -201,8 +173,34 @@
(testing "Should unvoid invoice"
(is (some? (sut/unvoid-invoice {:id (admin-token)}
- {:invoice_id invoice-id}
- nil)))
+ {:invoice_id invoice-id}
+ nil)))
+ (is (= :invoice-status/unpaid
+ (-> (d/pull (d/db conn) [{:invoice/status [:db/ident]}]
+ invoice-id)
+ :invoice/status
+ :db/ident)))))))
+
+(deftest void-invoice
+ (testing "It should voide invoices in bulk"
+ (let [{:strs [invoice-id]}
+ (setup-test-data [(test-invoice :db/id "invoice-id"
+ :invoice/status :invoice-status/unpaid)
+ (test-account :db/id "new-account-id")])]
+
+ (is (some? (sut/void-invoice {:id (admin-token)}
+ {:invoice_id invoice-id}
+ nil)))
+ (is (= :invoice-status/voided
+ (-> (d/pull (d/db conn) [{:invoice/status [:db/ident]}]
+ invoice-id)
+ :invoice/status
+ :db/ident)))
+
+ (testing "Should unvoid invoice"
+ (is (some? (sut/unvoid-invoice {:id (admin-token)}
+ {:invoice_id invoice-id}
+ nil)))
(is (= :invoice-status/unpaid
(-> (d/pull (d/db conn) [{:invoice/status [:db/ident]}]
invoice-id)
diff --git a/test/clj/auto_ap/integration/graphql/ledger/running_balance.clj b/test/clj/auto_ap/integration/graphql/ledger/running_balance.clj
index 950b13cc..03664c8b 100644
--- a/test/clj/auto_ap/integration/graphql/ledger/running_balance.clj
+++ b/test/clj/auto_ap/integration/graphql/ledger/running_balance.clj
@@ -23,11 +23,11 @@
line-2-2
line-3-1
line-3-2]} (:tempids @(d/transact conn [{:db/id "test-account-1"
- :account/type :account-type/asset}
- {:db/id "test-account-2"
- :account/type :account-type/equity}
- {:db/id "test-client"
- :client/code "TEST"}
+ :account/type :account-type/asset}
+ {:db/id "test-account-2"
+ :account/type :account-type/equity}
+ {:db/id "test-client"
+ :client/code "TEST"}
[:upsert-ledger {:db/id "journal-entry-1"
:journal-entry/external-id "1"
:journal-entry/date #inst "2022-01-01"
@@ -66,19 +66,18 @@
:journal-entry-line/credit 150.0}]}]]))]
(testing "should set running-balance on ledger entries missing them"
-
+
(sut/upsert-running-balance)
(println (d/pull (d/db conn) '[*] line-1-1))
(is (= [-10.0 -60.0 -210.0]
- (map #(pull-attr (d/db conn) :journal-entry-line/running-balance %) [line-1-1 line-2-1 line-3-1
- ])))
+ (map #(pull-attr (d/db conn) :journal-entry-line/running-balance %) [line-1-1 line-2-1 line-3-1])))
(is (= [10.0 60.0 210.0]
(map #(pull-attr (d/db conn) :journal-entry-line/running-balance %) [line-1-2 line-2-2 line-3-2]))))
(testing "should recompute if the data is out of date"
- (d/transact conn
+ (d/transact conn
[{:db/id line-1-1
:journal-entry-line/dirty true
:journal-entry-line/running-balance 123810.23}])
@@ -89,7 +88,7 @@
(testing "should recompute every entry after the out of date one"
- (d/transact conn
+ (d/transact conn
[{:db/id line-1-1
:journal-entry-line/dirty true
:journal-entry-line/debit 70.0}])
@@ -98,40 +97,39 @@
(map #(pull-attr (d/db conn) :journal-entry-line/running-balance %) [line-1-1 line-2-1 line-3-1]))))
(testing "should not recompute entries that aren't dirty"
- (d/transact conn
+ (d/transact conn
[{:db/id line-1-1
:journal-entry-line/dirty false
:journal-entry-line/debit 90.0}])
(sut/upsert-running-balance)
(is (= [-70.0 -120.0 -270.0]
- (map #(pull-attr (d/db conn) :journal-entry-line/running-balance %) [line-1-1 line-2-1 line-3-1])))
+ (map #(pull-attr (d/db conn) :journal-entry-line/running-balance %) [line-1-1 line-2-1 line-3-1]))))
- )
(testing "changing a ledger entry should mark the line items as dirty"
(println "AFTER HERE")
- @(d/transact conn
- [[:upsert-ledger {:db/id journal-entry-2
- :journal-entry/date #inst "2022-01-02"
- :journal-entry/client test-client
- :journal-entry/external-id "2"
- :journal-entry/line-items [{:db/id "line-2-1"
- :journal-entry-line/account test-account-1
- :journal-entry-line/location "A"
- :journal-entry-line/debit 50.0}
- {:db/id "line-2-2"
- :journal-entry-line/account test-account-2
- :journal-entry-line/location "A"
- :journal-entry-line/credit 50.0}]}]])
+ @(d/transact conn
+ [[:upsert-ledger {:db/id journal-entry-2
+ :journal-entry/date #inst "2022-01-02"
+ :journal-entry/client test-client
+ :journal-entry/external-id "2"
+ :journal-entry/line-items [{:db/id "line-2-1"
+ :journal-entry-line/account test-account-1
+ :journal-entry-line/location "A"
+ :journal-entry-line/debit 50.0}
+ {:db/id "line-2-2"
+ :journal-entry-line/account test-account-2
+ :journal-entry-line/location "A"
+ :journal-entry-line/credit 50.0}]}]])
+ (is (= [true true]
+ (->> (d/pull (d/db conn) '[{:journal-entry/line-items [:journal-entry-line/dirty]}] journal-entry-2)
+ (:journal-entry/line-items)
+ (map :journal-entry-line/dirty))))
+ (testing "should also mark the next entry as dirty, so that if a ledger entry is changed, the old accounts get updated"
+ (is (= [false false]
+ (->> (d/pull (d/db conn) '[{:journal-entry/line-items [:journal-entry-line/dirty]}] journal-entry-1)
+ (:journal-entry/line-items)
+ (map :journal-entry-line/dirty))))
(is (= [true true]
(->> (d/pull (d/db conn) '[{:journal-entry/line-items [:journal-entry-line/dirty]}] journal-entry-2)
(:journal-entry/line-items)
- (map :journal-entry-line/dirty))))
- (testing "should also mark the next entry as dirty, so that if a ledger entry is changed, the old accounts get updated"
- (is (= [false false]
- (->> (d/pull (d/db conn) '[{:journal-entry/line-items [:journal-entry-line/dirty]}] journal-entry-1)
- (:journal-entry/line-items)
- (map :journal-entry-line/dirty))))
- (is (= [true true]
- (->> (d/pull (d/db conn) '[{:journal-entry/line-items [:journal-entry-line/dirty]}] journal-entry-2)
- (:journal-entry/line-items)
- (map :journal-entry-line/dirty))))))))
+ (map :journal-entry-line/dirty))))))))
diff --git a/test/clj/auto_ap/integration/graphql/transaction_rules.clj b/test/clj/auto_ap/integration/graphql/transaction_rules.clj
index 02a66eaf..5685823c 100644
--- a/test/clj/auto_ap/integration/graphql/transaction_rules.clj
+++ b/test/clj/auto_ap/integration/graphql/transaction_rules.clj
@@ -11,11 +11,11 @@
(testing "Should find a single rule that matches a transaction"
(let [{:strs [transaction-id
transaction-rule-id]} (setup-test-data [(test-transaction
- :db/id "transaction-id"
- :transaction/description-original "Disneyland")
+ :db/id "transaction-id"
+ :transaction/description-original "Disneyland")
(test-transaction-rule
- :db/id "transaction-rule-id"
- :transaction-rule/description ".*")])]
+ :db/id "transaction-rule-id"
+ :transaction-rule/description ".*")])]
(is (= [transaction-rule-id] (->> (sut2/get-transaction-rule-matches {:id (admin-token)}
{:transaction_id transaction-id}
nil)
diff --git a/test/clj/auto_ap/integration/graphql/transactions.clj b/test/clj/auto_ap/integration/graphql/transactions.clj
index 64325bbd..9782e3ec 100644
--- a/test/clj/auto_ap/integration/graphql/transactions.clj
+++ b/test/clj/auto_ap/integration/graphql/transactions.clj
@@ -22,8 +22,8 @@
(testing "Should list transactions"
(let [{:strs [transaction-id
test-client-id]} (setup-test-data [(test-transaction :db/id "transaction-id"
- :transaction/client "test-client-id"
- :transaction/bank-account "test-bank-account-id")])]
+ :transaction/client "test-client-id"
+ :transaction/bank-account "test-bank-account-id")])]
(is (= 1 (:total (sut/get-transaction-page {:id (admin-token)} {} nil))))
(is (= transaction-id (:id (first (:data (sut/get-transaction-page {:id (admin-token)} {} nil))))))
(testing "Should only show transactions you have access to"
@@ -39,18 +39,17 @@
(testing "Should only show potential duplicates if filtered enough"
(is (thrown? Exception (:total (sut/get-transaction-page {:id (admin-token)} {:filters {:potential_duplicates true}} nil))))))))
-
(deftest bulk-change-status
(testing "Should change status of multiple transactions"
(let [{:strs [transaction-id
test-client-id]} (setup-test-data [(test-transaction :db/id "transaction-id"
- :transaction/client "test-client-id"
- :transaction/approval-status :transaction-approval-status/approved
- :transaction/bank-account "test-bank-account-id")])]
+ :transaction/client "test-client-id"
+ :transaction/approval-status :transaction-approval-status/approved
+ :transaction/bank-account "test-bank-account-id")])]
(is (= "Succesfully changed 1 transactions to be unapproved."
(:message (sut/bulk-change-status {:id (admin-token)
:clients [{:db/id test-client-id}]} {:filters {}
- :status :unapproved} nil))))
+ :status :unapproved} nil))))
(is (= :transaction-approval-status/unapproved
(:db/ident (:transaction/approval-status (dc/pull (dc/db conn) '[{:transaction/approval-status [:db/ident]}] transaction-id)))))
@@ -65,9 +64,9 @@
test-client-id
test-account-id
test-vendor-id]} (setup-test-data [(test-transaction :db/id "transaction-id"
- :transaction/client "test-client-id"
- :transaction/bank-account "test-bank-account-id"
- :transaction/amount 40.0)])]
+ :transaction/client "test-client-id"
+ :transaction/bank-account "test-bank-account-id"
+ :transaction/amount 40.0)])]
(is (= "Successfully coded 1 transactions."
(:message (sut/bulk-code-transactions {:id (admin-token)
:clients [{:db/id test-client-id}]}
@@ -94,18 +93,17 @@
(let [{:strs [transaction-id-1
transaction-id-2
test-client-id-2
- test-client-id]} (setup-test-data [
- (test-transaction :db/id "transaction-id-1"
- :transaction/client "test-client-id"
- :transaction/bank-account "test-bank-account-id"
- :transaction/amount 40.0)
- (test-transaction :db/id "transaction-id-2"
- :transaction/client "test-client-id-2"
- :transaction/bank-account "test-bank-account-id-2"
- :transaction/amount 40.0)
- (test-client :db/id "test-client-id-2"
- :client/locations ["GR"])
- (test-bank-account :db/id "test-bank-account-id-2")])]
+ test-client-id]} (setup-test-data [(test-transaction :db/id "transaction-id-1"
+ :transaction/client "test-client-id"
+ :transaction/bank-account "test-bank-account-id"
+ :transaction/amount 40.0)
+ (test-transaction :db/id "transaction-id-2"
+ :transaction/client "test-client-id-2"
+ :transaction/bank-account "test-bank-account-id-2"
+ :transaction/amount 40.0)
+ (test-client :db/id "test-client-id-2"
+ :client/locations ["GR"])
+ (test-bank-account :db/id "test-bank-account-id-2")])]
(is (= "Successfully coded 2 transactions."
(:message (sut/bulk-code-transactions {:id (admin-token)
:clients [{:db/id test-client-id}
@@ -116,7 +114,7 @@
:accounts [{:account_id test-account-id
:location "Shared"
:percentage 1.0}]} nil))))
-
+
(is (= #:transaction{:vendor {:db/id test-vendor-id}
:approval-status {:db/ident :transaction-approval-status/unapproved}
:accounts [#:transaction-account{:account {:db/id test-account-id}
@@ -143,8 +141,7 @@
(testing "should reject a location that doesnt exist"
(let [{:strs [test-client-id-1
- test-client-id-2]} (setup-test-data [
- (test-transaction :db/id "transaction-id-1"
+ test-client-id-2]} (setup-test-data [(test-transaction :db/id "transaction-id-1"
:transaction/client "test-client-id-1"
:transaction/bank-account "test-bank-account-id"
:transaction/amount 40.0)
@@ -158,14 +155,14 @@
:client/locations ["GR" "BOTH"])
(test-bank-account :db/id "test-bank-account-id-2")])]
(is (thrown? Exception (sut/bulk-code-transactions {:id (admin-token)
- :clients [{:db/id test-client-id}
- {:db/id test-client-id-2}]}
- {:filters {}
- :vendor test-vendor-id
- :approval_status :unapproved
- :accounts [{:account_id test-account-id
- :location "OG"
- :percentage 1.0}]} nil)))
+ :clients [{:db/id test-client-id}
+ {:db/id test-client-id-2}]}
+ {:filters {}
+ :vendor test-vendor-id
+ :approval_status :unapproved
+ :accounts [{:account_id test-account-id
+ :location "OG"
+ :percentage 1.0}]} nil)))
(is (thrown? Exception (sut/bulk-code-transactions {:id (admin-token)
:clients [{:db/id test-client-id}
{:db/id test-client-id-2}]}
@@ -223,7 +220,6 @@
:location "DT"
:amount 20.0}]}} nil)))))))
-
(deftest match-transaction
(testing "Should link a transaction to a payment, mark it as accounts payable"
(let [{:strs [transaction-id
@@ -275,36 +271,33 @@
:payment/bank-account "mismatched-bank-account-id"
:payment/amount 50.0)])]
(is (thrown? Exception (sut/match-transaction {:id (admin-token)} {:transaction_id transaction-id :payment_id mismatched-amount-payment-id} nil)))
- (is (thrown? Exception (sut/match-transaction {:id (admin-token)} {:transaction_id transaction-id :payment_id mismatched-bank-account-payment-id} nil)))
- )))
+ (is (thrown? Exception (sut/match-transaction {:id (admin-token)} {:transaction_id transaction-id :payment_id mismatched-bank-account-payment-id} nil))))))
(deftest match-transaction-autopay-invoices
(testing "Should link transaction to a set of autopaid invoices"
(let [{:strs [transaction-id
test-vendor-id
invoice-1
- invoice-2
- ]} (setup-test-data [(test-transaction :db/id "transaction-id"
- :transaction/amount -50.0)
- (test-invoice :db/id "invoice-1"
- :invoice/total 30.0)
- (test-invoice :db/id "invoice-2"
- :invoice/total 20.0)])]
+ invoice-2]} (setup-test-data [(test-transaction :db/id "transaction-id"
+ :transaction/amount -50.0)
+ (test-invoice :db/id "invoice-1"
+ :invoice/total 30.0)
+ (test-invoice :db/id "invoice-2"
+ :invoice/total 20.0)])]
(sut/match-transaction-autopay-invoices {:id (admin-token)} {:transaction_id transaction-id :autopay_invoice_ids [invoice-1 invoice-2]} nil)
(let [result (dc/pull (dc/db conn) '[:transaction/vendor
{:transaction/payment [:db/id {:payment/status [:db/ident]}]}
{:transaction/approval-status [:db/ident]
:transaction/accounts [:transaction-account/account
:transaction-account/location
- :transaction-account/amount]}
- ]
+ :transaction-account/amount]}]
transaction-id)]
(testing "should have created a payment"
(is (some? (:transaction/payment result)))
(is (= :payment-status/cleared (-> result
- :transaction/payment
- :payment/status
- :db/ident)))
+ :transaction/payment
+ :payment/status
+ :db/ident)))
(is (= :transaction-approval-status/approved (-> result :transaction/approval-status :db/ident))))
(testing "Should have completed the invoice"
(is (= :invoice-status/paid (->> invoice-1
@@ -320,11 +313,10 @@
(let [{:strs [transaction-id
test-vendor-id
invoice-1
- invoice-2
- ]} (setup-test-data [(test-transaction :db/id "transaction-id"
- :transaction/amount -50.0)
- (test-invoice :db/id "invoice-1"
- :invoice/total 30.0)])]
+ invoice-2]} (setup-test-data [(test-transaction :db/id "transaction-id"
+ :transaction/amount -50.0)
+ (test-invoice :db/id "invoice-1"
+ :invoice/total 30.0)])]
(is (thrown? Exception (sut/match-transaction-autopay-invoices {:id (admin-token)} {:transaction_id transaction-id :autopay_invoice_ids [invoice-1 invoice-2]} nil))))))
(deftest match-transaction-unpaid-invoices
@@ -332,30 +324,28 @@
(let [{:strs [transaction-id
test-vendor-id
invoice-1
- invoice-2
- ]} (setup-test-data [(test-transaction :db/id "transaction-id"
- :transaction/amount -50.0)
- (test-invoice :db/id "invoice-1"
- :invoice/outstanding-balance 30.0 ;; TODO this part is a little different
- :invoice/total 30.0)
- (test-invoice :db/id "invoice-2"
- :invoice/outstanding-balance 20.0 ;; TODO this part is a little different
- :invoice/total 20.0)])]
+ invoice-2]} (setup-test-data [(test-transaction :db/id "transaction-id"
+ :transaction/amount -50.0)
+ (test-invoice :db/id "invoice-1"
+ :invoice/outstanding-balance 30.0 ;; TODO this part is a little different
+ :invoice/total 30.0)
+ (test-invoice :db/id "invoice-2"
+ :invoice/outstanding-balance 20.0 ;; TODO this part is a little different
+ :invoice/total 20.0)])]
(sut/match-transaction-unpaid-invoices {:id (admin-token)} {:transaction_id transaction-id :unpaid_invoice_ids [invoice-1 invoice-2]} nil)
(let [result (dc/pull (dc/db conn) '[:transaction/vendor
{:transaction/payment [:db/id {:payment/status [:db/ident]}]}
{:transaction/approval-status [:db/ident]
:transaction/accounts [:transaction-account/account
:transaction-account/location
- :transaction-account/amount]}
- ]
+ :transaction-account/amount]}]
transaction-id)]
(testing "should have created a payment"
(is (some? (:transaction/payment result)))
(is (= :payment-status/cleared (-> result
- :transaction/payment
- :payment/status
- :db/ident)))
+ :transaction/payment
+ :payment/status
+ :db/ident)))
(is (= :transaction-approval-status/approved (-> result :transaction/approval-status :db/ident))))
(testing "Should have completed the invoice"
(is (= :invoice-status/paid (->> invoice-1
@@ -371,29 +361,25 @@
(let [{:strs [transaction-id
test-vendor-id
invoice-1
- invoice-2
- ]} (setup-test-data [(test-transaction :db/id "transaction-id"
- :transaction/amount -50.0)
- (test-invoice :db/id "invoice-1"
- :invoice/total 30.0)])]
+ invoice-2]} (setup-test-data [(test-transaction :db/id "transaction-id"
+ :transaction/amount -50.0)
+ (test-invoice :db/id "invoice-1"
+ :invoice/total 30.0)])]
(is (thrown? Exception (sut/match-transaction-autopay-invoices {:id (admin-token)} {:transaction_id transaction-id :autopay_invoice_ids [invoice-1 invoice-2]} nil))))))
-
(deftest match-transaction-rules
(testing "Should match transactions without linked payments"
(let [{:strs [transaction-id
- transaction-rule-id
- ]} (setup-test-data [(test-transaction :db/id "transaction-id"
- :transaction/amount -50.0)
- (test-transaction-rule :db/id "transaction-rule-id"
- :transaction-rule/client "test-client-id"
- :transaction-rule/transaction-approval-status :transaction-approval-status/excluded
- :transaction-rule/description ".*"
- )])]
+ transaction-rule-id]} (setup-test-data [(test-transaction :db/id "transaction-id"
+ :transaction/amount -50.0)
+ (test-transaction-rule :db/id "transaction-rule-id"
+ :transaction-rule/client "test-client-id"
+ :transaction-rule/transaction-approval-status :transaction-approval-status/excluded
+ :transaction-rule/description ".*")])]
(is (= transaction-rule-id (-> (sut/match-transaction-rules {:id (admin-token)} {:transaction_ids [transaction-id] :transaction_rule_id transaction-rule-id} nil)
- first
- :matched_rule
- :id)))
+ first
+ :matched_rule
+ :id)))
(testing "Should apply statuses"
(is (= :excluded
@@ -401,8 +387,7 @@
{:transaction_ids [transaction-id] :transaction_rule_id transaction-rule-id}
nil)
first
- :approval_status
- ))))))
+ :approval_status))))))
(testing "Should not apply to transactions if they don't match"
(let [{:strs [transaction-id
@@ -410,12 +395,11 @@
(setup-test-data [(test-transaction :db/id "transaction-id"
:transaction/amount -50.0)
(test-transaction-rule :db/id "transaction-rule-id"
- :transaction-rule/description "NOMATCH"
- )])]
+ :transaction-rule/description "NOMATCH")])]
(is (thrown? Exception (-> (sut/match-transaction-rules {:id (admin-token)} {:transaction_ids [transaction-id] :transaction_rule_id transaction-rule-id} nil)
- first
- :matched_rule
- :id)))))
+ first
+ :matched_rule
+ :id)))))
(testing "Should not apply to transactions if they are already matched"
(let [{:strs [transaction-id
transaction-rule-id]}
@@ -424,8 +408,7 @@
:transaction/payment {:db/id "extant-payment-id"}
:transaction/amount -50.0)
(test-transaction-rule :db/id "transaction-rule-id"
- :transaction-rule/description ".*"
- )])]
+ :transaction-rule/description ".*")])]
(is (thrown? Exception (-> (sut/match-transaction-rules {:id (admin-token)} {:transaction_ids [transaction-id] :transaction_rule_id transaction-rule-id} nil)
first
:matched_rule
@@ -438,8 +421,7 @@
:transaction/description-original "MATCH"
:transaction/amount -50.0)
(test-transaction-rule :db/id "transaction-rule-id"
- :transaction-rule/description ".*"
- )])]
+ :transaction-rule/description ".*")])]
(sut/match-transaction-rules {:id (admin-token)} {:all true
:transaction_rule_id transaction-rule-id} nil)
(= {:transaction/matched-rule {:db/id transaction-rule-id}}
diff --git a/test/clj/auto_ap/integration/graphql/users.clj b/test/clj/auto_ap/integration/graphql/users.clj
index b50df7bf..dc9aeea8 100644
--- a/test/clj/auto_ap/integration/graphql/users.clj
+++ b/test/clj/auto_ap/integration/graphql/users.clj
@@ -9,27 +9,25 @@
(use-fixtures :each wrap-setup)
-
#_(deftest edit-user
- (testing "should allow editing a user"
-
+ (testing "should allow editing a user"
- (let [{{:strs [user-id] } :tempids} @(d/transact conn [{:db/id "user-id" :user/name "Bryce"}])
- result (sut/edit-user {:id (admin-token)} {:edit_user {:role :power_user :id user-id}} nil)]
- (is (some? (:id result))
- (= :power_user (:role result)))
- (testing "Should allow adding clients"
- (let [{{:strs [client-id] } :tempids} @(d/transact conn [{:db/id "client-id" :client/name "Bryce"}])
- result (sut/edit-user {:id (admin-token)} {:edit_user {:role :power_user
- :id user-id
- :clients [(str client-id)]}} nil)]
- (is (= client-id (get-in result [:clients 0 :id])))))
- (testing "Should allow adding clients"
- (let [result (sut/edit-user {:id (admin-token)} {:edit_user {:role :power_user
- :id user-id
- :clients []}} nil)]
- (is (not (seq (:clients result))))))
- (testing "Should disallow normies"
- (is (thrown? Exception (sut/edit-user {:id (user-token)} {:edit_user {:role :power_user
- :id user-id
- :clients []}} nil)))))))
+ (let [{{:strs [user-id]} :tempids} @(d/transact conn [{:db/id "user-id" :user/name "Bryce"}])
+ result (sut/edit-user {:id (admin-token)} {:edit_user {:role :power_user :id user-id}} nil)]
+ (is (some? (:id result))
+ (= :power_user (:role result)))
+ (testing "Should allow adding clients"
+ (let [{{:strs [client-id]} :tempids} @(d/transact conn [{:db/id "client-id" :client/name "Bryce"}])
+ result (sut/edit-user {:id (admin-token)} {:edit_user {:role :power_user
+ :id user-id
+ :clients [(str client-id)]}} nil)]
+ (is (= client-id (get-in result [:clients 0 :id])))))
+ (testing "Should allow adding clients"
+ (let [result (sut/edit-user {:id (admin-token)} {:edit_user {:role :power_user
+ :id user-id
+ :clients []}} nil)]
+ (is (not (seq (:clients result))))))
+ (testing "Should disallow normies"
+ (is (thrown? Exception (sut/edit-user {:id (user-token)} {:edit_user {:role :power_user
+ :id user-id
+ :clients []}} nil)))))))
diff --git a/test/clj/auto_ap/integration/graphql/vendors.clj b/test/clj/auto_ap/integration/graphql/vendors.clj
index 1bf1d36e..f89e375b 100644
--- a/test/clj/auto_ap/integration/graphql/vendors.clj
+++ b/test/clj/auto_ap/integration/graphql/vendors.clj
@@ -3,16 +3,14 @@
[auto-ap.integration.util :refer [wrap-setup admin-token setup-test-data test-vendor test-account dissoc-id]]
[clojure.test :as t :refer [deftest is testing use-fixtures]]))
-
(use-fixtures :each wrap-setup)
-
(deftest vendors
(testing "vendors"
(let [{:strs [test-vendor-id]} (setup-test-data [])]
(testing "it should find vendors"
(let [result (sut2/get-graphql {:id (admin-token)} {} {})]
- (is ((into #{} (map :id (:vendors result))) test-vendor-id )))))))
+ (is ((into #{} (map :id (:vendors result))) test-vendor-id)))))))
(deftest upsert-vendor
(testing "Should allow upsert of an extant vendor"
@@ -38,9 +36,9 @@
:schedule_payment_dom [{:client_id test-client-id
:dom 12}]
:terms_overrides [{:client_id test-client-id
- :terms 100}]
+ :terms 100}]
:account_overrides [{:client_id test-client-id
- :account_id test-account-id-2}]
+ :account_id test-account-id-2}]
:automatically_paid_when_due [test-client-id]}}
nil)]
(is (= {:address {:street1 "1900 Penn ave",
@@ -52,7 +50,7 @@
:search_terms ["New Vendor Name!"],
:terms 30,
:name "New Vendor Name!",
- :secondary_contact { :name "Ben"},
+ :secondary_contact {:name "Ben"},
:usage nil,
:hidden true,
:id test-vendor-id,
@@ -72,7 +70,6 @@
(update :schedule_payment_dom #(map dissoc-id %))
(update :terms_overrides #(map dissoc-id %))
(update :account_overrides #(map dissoc-id %)))))
- (is (= 1 (count (:automatically_paid_when_due result))))
- ))))
+ (is (= 1 (count (:automatically_paid_when_due result))))))))
diff --git a/test/clj/auto_ap/integration/jobs/ntg.clj b/test/clj/auto_ap/integration/jobs/ntg.clj
index a8a2929f..e950a5ba 100644
--- a/test/clj/auto_ap/integration/jobs/ntg.clj
+++ b/test/clj/auto_ap/integration/jobs/ntg.clj
@@ -4,7 +4,6 @@
[clojure.test :as t :refer [deftest is testing use-fixtures]]
[clojure.java.io :as io]))
-
(use-fixtures :each wrap-setup)
(deftest extract-invoice-details-cintas
@@ -14,23 +13,22 @@
:client/locations ["OP"]
:client/matches ["2034 BROADWAY ST"]}]
(is (=
- [{:invoice/invoice-number "1500000592"
- :invoice/date #inst "2023-03-09T08:00:00-00:00"
- :invoice/due #inst "2023-04-08T07:00:00-00:00"
- :invoice/import-status :import-status/imported
- :invoice/client-identifier "2034 BROADWAY ST"
- :invoice/location "OP"
- :invoice/status :invoice-status/unpaid
- :invoice/vendor :vendor/cintas
- :invoice/scheduled-payment #inst "2023-04-08T07:00:00-00:00"
- :invoice/client 1
- :invoice/total 39.88
- :invoice/outstanding-balance 39.88
- }]
- (map #(dissoc % :invoice/expense-accounts :db/id)
- (sut/extract-invoice-details "ntg-invoices/Cintas/123.zcic"
- (io/input-stream (io/resource "test-cintas/o.zcic.230310093903"))
- [client]))))))
+ [{:invoice/invoice-number "1500000592"
+ :invoice/date #inst "2023-03-09T08:00:00-00:00"
+ :invoice/due #inst "2023-04-08T07:00:00-00:00"
+ :invoice/import-status :import-status/imported
+ :invoice/client-identifier "2034 BROADWAY ST"
+ :invoice/location "OP"
+ :invoice/status :invoice-status/unpaid
+ :invoice/vendor :vendor/cintas
+ :invoice/scheduled-payment #inst "2023-04-08T07:00:00-00:00"
+ :invoice/client 1
+ :invoice/total 39.88
+ :invoice/outstanding-balance 39.88}]
+ (map #(dissoc % :invoice/expense-accounts :db/id)
+ (sut/extract-invoice-details "ntg-invoices/Cintas/123.zcic"
+ (io/input-stream (io/resource "test-cintas/o.zcic.230310093903"))
+ [client]))))))
(testing "Should disable automatic payment based on feature flag"
(let [client {:db/id 1
@@ -50,8 +48,8 @@
:client/locations ["OP"]
:client/matches ["123 time square"]}]
(is (=
- []
- (sut/extract-invoice-details "ntg-invoices/Cintas/123"
- (io/input-stream (io/resource "test-cintas/o.zcic.230310093903"))
- [client]))))))
+ []
+ (sut/extract-invoice-details "ntg-invoices/Cintas/123"
+ (io/input-stream (io/resource "test-cintas/o.zcic.230310093903"))
+ [client]))))))
diff --git a/test/clj/auto_ap/integration/routes/invoice_test.clj b/test/clj/auto_ap/integration/routes/invoice_test.clj
index 7b9c29b7..338e2d93 100644
--- a/test/clj/auto_ap/integration/routes/invoice_test.clj
+++ b/test/clj/auto_ap/integration/routes/invoice_test.clj
@@ -24,12 +24,12 @@
:account/name "Food"})
(defn invoice-count-for-client [c]
- (or
- (first (first (dc/q '[:find (count ?i)
+ (or
+ (first (first (dc/q '[:find (count ?i)
:in $ ?c
:where [?i :invoice/client ?c]]
(dc/db conn) c)))
- 0))
+ 0))
(def invoice {:customer-identifier "ABC"
:date (coerce/to-date-time #inst "2021-01-01")
@@ -49,11 +49,10 @@
(t/testing "Should only import the same invoice once"
(t/is (thrown? Exception (sut/import-uploaded-invoice user [(assoc invoice :customer-identifier "ABC")])))
-
-
+
(t/is (thrown? Exception (sut/import-uploaded-invoice user [(assoc invoice
- :customer-identifier "ABC"
- :total "456.32")]))))
+ :customer-identifier "ABC"
+ :total "456.32")]))))
(t/testing "Should override location"
(sut/import-uploaded-invoice user [(assoc invoice
@@ -61,26 +60,26 @@
:customer-identifier "ABC"
:invoice-number "789")])
(t/is (= #{["DE"]} (dc/q '[:find ?l
- :where [?i :invoice/invoice-number "789"]
- [?i :invoice/expense-accounts ?ea]
- [?ea :invoice-expense-account/location ?l]]
- (dc/db conn)))))
+ :where [?i :invoice/invoice-number "789"]
+ [?i :invoice/expense-accounts ?ea]
+ [?ea :invoice-expense-account/location ?l]]
+ (dc/db conn)))))
(t/testing "Should code invoice"
(let [{{:strs [my-default-account coded-vendor]} :tempids} @(dc/transact conn
- [{:vendor/name "Coded"
- :db/id "coded-vendor"
- :vendor/terms 12
- :vendor/default-account "my-default-account"}
- {:db/id "my-default-account"
- :account/name "My default-account"}])]
+ [{:vendor/name "Coded"
+ :db/id "coded-vendor"
+ :vendor/terms 12
+ :vendor/default-account "my-default-account"}
+ {:db/id "my-default-account"
+ :account/name "My default-account"}])]
(sut/import-uploaded-invoice user [(assoc invoice
- :invoice-number "456"
- :customer-identifier "ABC"
- :vendor-code "Coded")])
+ :invoice-number "456"
+ :customer-identifier "ABC"
+ :vendor-code "Coded")])
(let [[[result]] (dc/q '[:find (pull ?i [*])
- :where [?i :invoice/invoice-number "456"]]
- (dc/db conn))]
+ :where [?i :invoice/invoice-number "456"]]
+ (dc/db conn))]
(t/is (= coded-vendor (:db/id (:invoice/vendor result))))
(t/is (= [my-default-account]
(map (comp :db/id :invoice-expense-account/account) (:invoice/expense-accounts result))))
diff --git a/test/clj/auto_ap/integration/rule_matching.clj b/test/clj/auto_ap/integration/rule_matching.clj
index 32f7bfd7..3c0593ad 100644
--- a/test/clj/auto_ap/integration/rule_matching.clj
+++ b/test/clj/auto_ap/integration/rule_matching.clj
@@ -19,15 +19,13 @@
:approval-status :transaction-approval-status/unapproved
:description-simple "simple-description"})
-
-
(t/deftest rule-applying-fn
(t/testing "Should apply if description matches"
(t/is (sut/rule-applies?
base-transaction
{:transaction-rule/description #"original-description"
:transaction-rule/transaction-approval-status :transaction-approval-status/approved}))
-
+
(t/is (not (sut/rule-applies?
base-transaction
{:transaction-rule/description #"xxx"
@@ -42,7 +40,7 @@
(let [process (sut/rule-applying-fn [{:transaction-rule/description "simple-description"
:transaction-rule/transaction-approval-status :transaction-approval-status/approved}])
transaction (assoc base-transaction :transaction/description-original "simple-description")]
- (t/is (= :transaction-approval-status/approved
+ (t/is (= :transaction-approval-status/approved
(:transaction/approval-status (process transaction ["NG"]))))))
(t/testing "spread cents"
@@ -79,5 +77,4 @@
(t/is (= [0.01 0.01]
(map :transaction-account/amount (:transaction/accounts (process (assoc transaction :transaction/amount 0.02) ["NG" "BT" "DE"])))))
(t/is (= [0.02 0.01 0.01]
- (map :transaction-account/amount (:transaction/accounts (process (assoc transaction :transaction/amount 0.04) ["NG" "BT" "DE"])))))
- )))
+ (map :transaction-account/amount (:transaction/accounts (process (assoc transaction :transaction/amount 0.04) ["NG" "BT" "DE"]))))))))
diff --git a/test/clj/auto_ap/integration/util.clj b/test/clj/auto_ap/integration/util.clj
index 405e6bd0..df869296 100644
--- a/test/clj/auto_ap/integration/util.clj
+++ b/test/clj/auto_ap/integration/util.clj
@@ -22,15 +22,11 @@
(defn user-token
([] (user-token 1))
([client-id]
- {:user "TEST USER"
- :exp (time/plus (time/now) (time/days 1))
- :user/role "user"
- :user/name "TEST USER"
- :user/clients [{:db/id client-id}]}))
-
-
-
-
+ {:user "TEST USER"
+ :exp (time/plus (time/now) (time/days 1))
+ :user/role "user"
+ :user/name "TEST USER"
+ :user/clients [{:db/id client-id}]}))
(defn test-client [& kwargs]
(apply assoc {:db/id "client-id"
@@ -102,15 +98,15 @@
(defn setup-test-data [data]
(:tempids @(dc/transact conn (into data
- [(test-account :db/id "test-account-id")
- (test-client :db/id "test-client-id"
- :client/bank-accounts [(test-bank-account :db/id "test-bank-account-id")])
- (test-vendor :db/id "test-vendor-id")
- {:db/id "accounts-payable-id"
- :account/name "Accounts Payable"
- :db/ident :account/accounts-payable
- :account/numeric-code 21000
- :account/account-set "default"}]))))
+ [(test-account :db/id "test-account-id")
+ (test-client :db/id "test-client-id"
+ :client/bank-accounts [(test-bank-account :db/id "test-bank-account-id")])
+ (test-vendor :db/id "test-vendor-id")
+ {:db/id "accounts-payable-id"
+ :account/name "Accounts Payable"
+ :db/ident :account/accounts-payable
+ :account/numeric-code 21000
+ :account/account-set "default"}]))))
(defn apply-tx [data]
(:db-after @(dc/transact conn data)))
diff --git a/test/clj/auto_ap/ledger_test.clj b/test/clj/auto_ap/ledger_test.clj
index d1332b5c..12b0099a 100644
--- a/test/clj/auto_ap/ledger_test.clj
+++ b/test/clj/auto_ap/ledger_test.clj
@@ -5,40 +5,37 @@
(t/use-fixtures :each wrap-setup)
-
(t/deftest entity-change->ledger
#_(t/testing "Should code an expected deposit"
- (let [{:strs [ed ccp receipts-split client]}
- (:tempids @(d/transact conn [#:expected-deposit {:status :expected-deposit-status/pending
- :client {:db/id "client"
- :client/code "BRYCE"
- :client/locations ["M"]}
- :total 4.0
- :fee 1.0
- :date #inst "2021-01-01T00:00:00-08:00"
- :location "M"
- :db/id "ed"}]))
- result (sut/entity-change->ledger (d/db conn) [:expected-deposit ed])]
- (t/is (= #:journal-entry
- {:source "expected-deposit"
- :client {:db/id client}
- :date #inst "2021-01-01T00:00:00-08:00"
- :original-entity ed
- :vendor :vendor/ccp-square
- :amount 4.0
- }
- (dissoc result :journal-entry/line-items)))
+ (let [{:strs [ed ccp receipts-split client]}
+ (:tempids @(d/transact conn [#:expected-deposit {:status :expected-deposit-status/pending
+ :client {:db/id "client"
+ :client/code "BRYCE"
+ :client/locations ["M"]}
+ :total 4.0
+ :fee 1.0
+ :date #inst "2021-01-01T00:00:00-08:00"
+ :location "M"
+ :db/id "ed"}]))
+ result (sut/entity-change->ledger (d/db conn) [:expected-deposit ed])]
+ (t/is (= #:journal-entry
+ {:source "expected-deposit"
+ :client {:db/id client}
+ :date #inst "2021-01-01T00:00:00-08:00"
+ :original-entity ed
+ :vendor :vendor/ccp-square
+ :amount 4.0}
+ (dissoc result :journal-entry/line-items)))
- (t/testing "should debit ccp"
- (t/is (= [#:journal-entry-line
- {:debit 4.0
- :location "A"
- :account :account/ccp}]
- (filter :journal-entry-line/debit (:journal-entry/line-items result))))
- )
- (t/testing "should credit receipts split ccp"
- (t/is (= [#:journal-entry-line
- {:credit 4.0
- :location "A"
- :account :account/receipts-split}]
- (filter :journal-entry-line/credit (:journal-entry/line-items result))))))))
+ (t/testing "should debit ccp"
+ (t/is (= [#:journal-entry-line
+ {:debit 4.0
+ :location "A"
+ :account :account/ccp}]
+ (filter :journal-entry-line/debit (:journal-entry/line-items result)))))
+ (t/testing "should credit receipts split ccp"
+ (t/is (= [#:journal-entry-line
+ {:credit 4.0
+ :location "A"
+ :account :account/receipts-split}]
+ (filter :journal-entry-line/credit (:journal-entry/line-items result))))))))
diff --git a/test/clj/auto_ap/ssr/admin/accounts_test.clj b/test/clj/auto_ap/ssr/admin/accounts_test.clj
index 9b261458..690e330c 100644
--- a/test/clj/auto_ap/ssr/admin/accounts_test.clj
+++ b/test/clj/auto_ap/ssr/admin/accounts_test.clj
@@ -126,8 +126,8 @@
;; Test passes if sorting parameter is accepted and function returns successfully
(is (number? matching-count)))))
-(deftest account-sorting-by-numeric-code
- (testing "Account sorting by numeric code should work (default)"
+ (deftest account-sorting-by-numeric-code
+ (testing "Account sorting by numeric code should work (default)"
(with-redefs [auto-ap.solr/impl (auto-ap.solr/->InMemSolrClient (atom {}))]
;; Create test accounts before sorting
(sut/account-save {:form-params {:account/numeric-code 12372 :account/name "Numeric Account" :account/type :account-type/asset :account/location "A"} :request-method :post :identity (admin-token)})
@@ -138,8 +138,8 @@
;; Test passes if sorting parameter is accepted and function returns successfully
(is (number? matching-count))))))
-(deftest account-sorting-by-type
- (testing "Account sorting by type should work"
+ (deftest account-sorting-by-type
+ (testing "Account sorting by type should work"
(with-redefs [auto-ap.solr/impl (auto-ap.solr/->InMemSolrClient (atom {}))]
;; Create test accounts before sorting
(sut/account-save {:form-params {:account/numeric-code 12374 :account/name "Type Test" :account/type :account-type/asset :account/location "A"} :request-method :post :identity (admin-token)})
diff --git a/test/clj/auto_ap/ssr/ledger_test.clj b/test/clj/auto_ap/ssr/ledger_test.clj
index 81fc001c..96f181ae 100644
--- a/test/clj/auto_ap/ssr/ledger_test.clj
+++ b/test/clj/auto_ap/ssr/ledger_test.clj
@@ -411,7 +411,7 @@
:location "HQ"}]}
admin-identity (admin-token)]
(is (thrown? Exception (sut/import-ledger {:form-params form-params
- :identity admin-identity})))))
+ :identity admin-identity})))))
(testing "Should produce form-errors for invalid account entries"
(let [_ (setup-test-data [(test-client :db/id "err-client-1"
@@ -430,7 +430,7 @@
:location "HQ"}]}
admin-identity (admin-token)]
(is (thrown? Exception (sut/import-ledger {:form-params form-params
- :identity admin-identity}))))))
+ :identity admin-identity}))))))
(deftest import-ledger-with-warnings-test
(testing "Should ignore entries with only warnings"
diff --git a/test/clj/auto_ap/ssr/transaction/edit_test.clj b/test/clj/auto_ap/ssr/transaction/edit_test.clj
index 388b1f9e..e2f1cb3d 100644
--- a/test/clj/auto_ap/ssr/transaction/edit_test.clj
+++ b/test/clj/auto_ap/ssr/transaction/edit_test.clj
@@ -15,14 +15,14 @@
[datomic.api :as dc]))
#_(t/use-fixtures :each (fn [f]
- (dc/transact conn [{:db/id "vendor-id"
- :vendor/name "Test Vendor"
- :vendor/default-account {:db/id "account-id"}}
- {:db/id "client-id"
- :client/code "TEST"
- :client/locations ["Z" "E"]
- :client/bank-accounts ["bank-account-id"]}])
- (f)))
+ (dc/transact conn [{:db/id "vendor-id"
+ :vendor/name "Test Vendor"
+ :vendor/default-account {:db/id "account-id"}}
+ {:db/id "client-id"
+ :client/code "TEST"
+ :client/locations ["Z" "E"]
+ :client/bank-accounts ["bank-account-id"]}])
+ (f)))
(t/use-fixtures :each wrap-setup)
(t/deftest get-vendor-test
@@ -39,11 +39,11 @@
(t/deftest clientize-vendor-test
(t/testing "Should transform vendor data for a specific client"
(let [vendor {:db/id "vendor-id"
- :vendor/name "Test Vendor"
- :vendor/default-account {:db/id "account-id"}
- :vendor/terms-overrides [{:vendor-terms-override/client {:db/id "client-id"}
+ :vendor/name "Test Vendor"
+ :vendor/default-account {:db/id "account-id"}
+ :vendor/terms-overrides [{:vendor-terms-override/client {:db/id "client-id"}
:vendor-terms-override/terms "New Terms"}]
- :vendor/account-overrides [{:vendor-account-override/client {:db/id "client-id"}
+ :vendor/account-overrides [{:vendor-account-override/client {:db/id "client-id"}
:vendor-account-override/account {:db/id "client-specific-account-id"}}]}
clientized-vendor (clientize-vendor vendor "client-id")]
(t/is (= "New Terms" (:vendor/terms clientized-vendor)))
diff --git a/test/clj/auto_ap/test_server.clj b/test/clj/auto_ap/test_server.clj
index 41c09559..02615533 100644
--- a/test/clj/auto_ap/test_server.clj
+++ b/test/clj/auto_ap/test_server.clj
@@ -53,59 +53,59 @@
(defn seed-test-data [conn]
(let [tx-result @(dc/transact conn
- [(assoc (test-client :db/id "client-id"
- :client/code "TEST"
- :client/locations ["DT"])
- :client/bank-accounts [(test-bank-account :db/id "bank-account-id")])
- {:db/id "account-id"
- :account/name "Test Account"
- :account/type :account-type/expense
- :account/numeric-code 50000
- :account/applicability :account-applicability/global
- :account/default-allowance {:db/ident :allowance/allowed}}
- {:db/id "account-id-2"
- :account/name "Second Account"
- :account/type :account-type/expense
- :account/numeric-code 50001
- :account/applicability :account-applicability/global
- :account/default-allowance {:db/ident :allowance/allowed}}
- {:db/id "account-id-fixed-loc"
- :account/name "Fixed Location Account"
- :account/type :account-type/expense
- :account/numeric-code 50002
- :account/applicability :account-applicability/global
- :account/location "DT"
- :account/default-allowance {:db/ident :allowance/allowed}}
- {:db/id "ap-account-id"
- :account/name "Accounts Payable"
- :db/ident :account/accounts-payable
- :account/numeric-code 21000
- :account/account-set "default"
- :account/applicability :account-applicability/global
- :account/default-allowance {:db/ident :allowance/allowed}}
- {:db/id "vendor-id"
- :vendor/name "Test Vendor"
- :vendor/default-account "account-id"}
- (test-transaction :db/id "transaction-id"
- :transaction/client "client-id"
- :transaction/bank-account "bank-account-id"
- :transaction/amount 100.0
- :transaction/description-original "Test transaction"
- :transaction/approval-status :transaction-approval-status/unapproved)
- (test-transaction :db/id "transaction-id-2"
- :transaction/client "client-id"
- :transaction/bank-account "bank-account-id"
- :transaction/amount 200.0
- :transaction/description-original "Second transaction"
- :transaction/approval-status :transaction-approval-status/unapproved)
- (test-transaction :db/id "transaction-id-3"
- :transaction/client "client-id"
- :transaction/bank-account "bank-account-id"
- :transaction/amount 300.0
- :transaction/description-original "Third transaction"
- :transaction/approval-status :transaction-approval-status/unapproved)])
- tempids (:tempids tx-result)
- tx-entity-id (get tempids "transaction-id")]
+ [(assoc (test-client :db/id "client-id"
+ :client/code "TEST"
+ :client/locations ["DT"])
+ :client/bank-accounts [(test-bank-account :db/id "bank-account-id")])
+ {:db/id "account-id"
+ :account/name "Test Account"
+ :account/type :account-type/expense
+ :account/numeric-code 50000
+ :account/applicability :account-applicability/global
+ :account/default-allowance {:db/ident :allowance/allowed}}
+ {:db/id "account-id-2"
+ :account/name "Second Account"
+ :account/type :account-type/expense
+ :account/numeric-code 50001
+ :account/applicability :account-applicability/global
+ :account/default-allowance {:db/ident :allowance/allowed}}
+ {:db/id "account-id-fixed-loc"
+ :account/name "Fixed Location Account"
+ :account/type :account-type/expense
+ :account/numeric-code 50002
+ :account/applicability :account-applicability/global
+ :account/location "DT"
+ :account/default-allowance {:db/ident :allowance/allowed}}
+ {:db/id "ap-account-id"
+ :account/name "Accounts Payable"
+ :db/ident :account/accounts-payable
+ :account/numeric-code 21000
+ :account/account-set "default"
+ :account/applicability :account-applicability/global
+ :account/default-allowance {:db/ident :allowance/allowed}}
+ {:db/id "vendor-id"
+ :vendor/name "Test Vendor"
+ :vendor/default-account "account-id"}
+ (test-transaction :db/id "transaction-id"
+ :transaction/client "client-id"
+ :transaction/bank-account "bank-account-id"
+ :transaction/amount 100.0
+ :transaction/description-original "Test transaction"
+ :transaction/approval-status :transaction-approval-status/unapproved)
+ (test-transaction :db/id "transaction-id-2"
+ :transaction/client "client-id"
+ :transaction/bank-account "bank-account-id"
+ :transaction/amount 200.0
+ :transaction/description-original "Second transaction"
+ :transaction/approval-status :transaction-approval-status/unapproved)
+ (test-transaction :db/id "transaction-id-3"
+ :transaction/client "client-id"
+ :transaction/bank-account "bank-account-id"
+ :transaction/amount 300.0
+ :transaction/description-original "Third transaction"
+ :transaction/approval-status :transaction-approval-status/unapproved)])
+ tempids (:tempids tx-result)
+ tx-entity-id (get tempids "transaction-id")]
(println "Test transaction entity ID:" tx-entity-id)
(reset! test-account-ids
{:test-account (get tempids "account-id")
diff --git a/test/clj/iol_ion/integration/tx.clj b/test/clj/iol_ion/integration/tx.clj
index d127e424..529bb966 100644
--- a/test/clj/iol_ion/integration/tx.clj
+++ b/test/clj/iol_ion/integration/tx.clj
@@ -23,64 +23,61 @@
:journal-entry-line/dirty
:journal-entry-line/debit]}])
-
(deftest upsert-invoice
(testing "Importing should create a journal entry"
(let [{:strs [invoice-id
test-client-id
- test-vendor-id
- ]} (setup-test-data
- [(test-invoice :db/id "invoice-id"
- :invoice/import-status :import-status/pending
- :invoice/total 200.0
- )])]
-
+ test-vendor-id]} (setup-test-data
+ [(test-invoice :db/id "invoice-id"
+ :invoice/import-status :import-status/pending
+ :invoice/total 200.0)])]
+
(is (nil? (:db/id (dc/pull (dc/db conn) journal-pull
[:journal-entry/original-entity invoice-id]))))
(let [db-after (apply-tx (sut-i/upsert-invoice
- (dc/db conn)
- {:db/id invoice-id
- :invoice/import-status :import-status/imported}))]
-
+ (dc/db conn)
+ {:db/id invoice-id
+ :invoice/import-status :import-status/imported}))]
+
(is (= #:journal-entry{:date #inst "2022-01-01T00:00:00.000-00:00",
- :original-entity #:db{:id invoice-id},
- :client #:db{:id test-client-id},
- :line-items
- [#:journal-entry-line{:account
- #:account{:name
- "Accounts Payable"},
- :credit 200.0,
- :location "A",
- :dirty true}
- #:journal-entry-line{:account
- #:account{:name "Account"},
- :location "DT",
- :dirty true,
- :debit 100.0}],
- :source "invoice",
- :cleared false,
- :amount 200.0,
- :vendor #:db{:id test-vendor-id}}
+ :original-entity #:db{:id invoice-id},
+ :client #:db{:id test-client-id},
+ :line-items
+ [#:journal-entry-line{:account
+ #:account{:name
+ "Accounts Payable"},
+ :credit 200.0,
+ :location "A",
+ :dirty true}
+ #:journal-entry-line{:account
+ #:account{:name "Account"},
+ :location "DT",
+ :dirty true,
+ :debit 100.0}],
+ :source "invoice",
+ :cleared false,
+ :amount 200.0,
+ :vendor #:db{:id test-vendor-id}}
(dc/pull db-after journal-pull
[:journal-entry/original-entity invoice-id])))
(testing "voiding an invoice should remove the journal entry"
(let [db-after (apply-tx (sut-i/upsert-invoice
- (dc/db conn)
- {:db/id invoice-id
- :invoice/status :invoice-status/voided}))]
-
- (is (= nil
+ (dc/db conn)
+ {:db/id invoice-id
+ :invoice/status :invoice-status/voided}))]
+
+ (is (= nil
(dc/pull db-after journal-pull
[:journal-entry/original-entity invoice-id])))))
(testing "invoice should remove the journal entry"
(let [db-after (apply-tx (sut-i/upsert-invoice
- (dc/db conn)
- {:db/id invoice-id
- :invoice/status :invoice-status/unpaid
- :invoice/import-status :import-status/pending}))]
-
- (is (= nil
+ (dc/db conn)
+ {:db/id invoice-id
+ :invoice/status :invoice-status/unpaid
+ :invoice/import-status :import-status/pending}))]
+
+ (is (= nil
(dc/pull db-after journal-pull
[:journal-entry/original-entity invoice-id])))))))))
@@ -91,31 +88,28 @@
test-account-id
test-vendor-id
test-transaction-id
- test-import-batch-id
- ]} (setup-test-data
- [(test-transaction :db/id "test-transaction-id"
- )
- {:db/id "test-import-batch-id"
- :import-batch/date #inst "2022-01-01"}])
+ test-import-batch-id]} (setup-test-data
+ [(test-transaction :db/id "test-transaction-id")
+ {:db/id "test-import-batch-id"
+ :import-batch/date #inst "2022-01-01"}])
update (sut-t/upsert-transaction (dc/db conn) {:db/id test-transaction-id
- :transaction/id "hello"
- :transaction/bank-account test-bank-account-id
- :transaction/amount 500.00
- :transaction/client test-client-id
- :transaction/date #inst "2022-01-01"
- :transaction/vendor test-vendor-id
- :transaction/approval-status :transaction-approval-status/approved
- :transaction/accounts [
- {:db/id "account"
- :transaction-account/account test-account-id
- :transaction-account/location "A"
- :transaction-account/amount 500.00}]})]
-
+ :transaction/id "hello"
+ :transaction/bank-account test-bank-account-id
+ :transaction/amount 500.00
+ :transaction/client test-client-id
+ :transaction/date #inst "2022-01-01"
+ :transaction/vendor test-vendor-id
+ :transaction/approval-status :transaction-approval-status/approved
+ :transaction/accounts [{:db/id "account"
+ :transaction-account/account test-account-id
+ :transaction-account/location "A"
+ :transaction-account/amount 500.00}]})]
+
(is (nil? (:db/id (dc/pull (dc/db conn) journal-pull
[:journal-entry/original-entity test-transaction-id]))))
(let [db-after (apply-tx update)]
(testing "should create journal entry"
- (is (= #:journal-entry{:date #inst "2022-01-01T00:00:00.000-00:00",
+ (is (= #:journal-entry{:date #inst "2022-01-01T00:00:00.000-00:00",
:original-entity #:db{:id test-transaction-id},
:client #:db{:id test-client-id},
:source "transaction",
@@ -130,8 +124,6 @@
#:account{:name "Account"},
:location "A",
:credit 500.0,
- :dirty true}]}
+ :dirty true}]}
(dc/pull db-after journal-pull
- [:journal-entry/original-entity test-transaction-id])))))
-
- )))
+ [:journal-entry/original-entity test-transaction-id]))))))))