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

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