Add vendor pre-population for bulk code and individual edit forms

- Add vendor-changed HTMX handlers for both bulk code and individual edit
- Pre-populate default account at 100% when vendor is selected and no accounts exist
- Fix render-accounts-section to render from step-params correctly
- Change bulk code vendor-changed from hx-get to hx-post to include form data
- Add routes for vendor-changed endpoints
- Update e2e tests to cover vendor pre-population
- Run lein cljfmt fix across codebase
This commit is contained in:
2026-05-21 14:45:19 -07:00
parent 8bd0cee1b1
commit ba87805d4c
210 changed files with 8694 additions and 9627 deletions

View File

@@ -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');
});
});

View File

@@ -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)))))
#_(->> (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)))))

View File

@@ -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

View File

@@ -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

View File

@@ -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)))))

View File

@@ -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)))))

View File

@@ -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])

View File

@@ -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

View File

@@ -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)}])))

View File

@@ -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)}]))))))

View File

@@ -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)
)
(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))

View File

@@ -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)))

File diff suppressed because one or more lines are too long

View File

@@ -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*)

View File

@@ -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)

View File

@@ -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)))

View File

@@ -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)))))

View File

@@ -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]

View File

@@ -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)))

View File

@@ -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)))

View File

@@ -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)]

View File

@@ -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]

View File

@@ -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

View File

@@ -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]]}

View File

@@ -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}))

View File

@@ -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))))

View File

@@ -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]

View File

@@ -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.))

View File

@@ -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]

View File

@@ -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)))

View File

@@ -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

View File

@@ -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))))))))))

View File

@@ -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))

View File

@@ -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

View File

@@ -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

View File

@@ -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 _]

View File

@@ -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
{})

View File

@@ -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}

View File

@@ -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)))))

View File

@@ -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)

View File

@@ -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

View File

@@ -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})))

View File

@@ -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]
(->

View File

@@ -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))

View File

@@ -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

View File

@@ -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

View File

@@ -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"})]

View File

@@ -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]

View File

@@ -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)))

View File

@@ -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)

View File

@@ -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))))))

View File

@@ -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))

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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))

View File

@@ -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}))))

View File

@@ -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))})

View File

@@ -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))))

View File

@@ -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)

View File

@@ -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 "<div>You can download the original email <a href=\"" target-url "\">here</a>.<p><pre>" message "</pre></p></div>")
:text (str "<div>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)
)
(user/init-repl))

View File

@@ -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)
[])))

View File

@@ -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 [& _]

View File

@@ -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))

View File

@@ -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"}))))

View File

@@ -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

View File

@@ -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))

View File

@@ -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))

View File

@@ -12,7 +12,7 @@
:where
[?v :vendor/name]
(or-join [?v ?c ?e]
(and
(and
[?e :invoice/vendor ?v]
[?e :invoice/client ?c])
(and

View File

@@ -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]}

View File

@@ -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]

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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]

View File

@@ -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}))

View File

@@ -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"))
)
(clojure.pprint/pprint (get-accounts "access-production-c0e322fa-f33d-4806-bc42-5fc883fb1ba4")))

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)})

View File

@@ -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}]

View File

@@ -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)})

View File

@@ -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

View File

@@ -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)

View File

@@ -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))})

View File

@@ -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))))))

View File

@@ -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))

View File

@@ -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]

View File

@@ -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")))))

View File

@@ -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))

View File

@@ -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))})

View File

@@ -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

View File

@@ -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)))))

View File

@@ -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)

View File

@@ -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)))))

View File

@@ -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")))

View File

@@ -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)))))

View File

@@ -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)))))

View File

@@ -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)))))

View File

@@ -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"))

Some files were not shown because too many files have changed in this diff Show More