Compare commits
3 Commits
ec4f88b7fc
...
f9438ba983
| Author | SHA1 | Date | |
|---|---|---|---|
| f9438ba983 | |||
| 7d34b8a5f6 | |||
| c09d85ede6 |
@@ -28,7 +28,7 @@
|
|||||||
(com/data-grid-header {} "Synced count")
|
(com/data-grid-header {} "Synced count")
|
||||||
(com/data-grid-header {} "Approved transactions")
|
(com/data-grid-header {} "Approved transactions")
|
||||||
(com/data-grid-header {} "Unapproved transactions")
|
(com/data-grid-header {} "Unapproved transactions")
|
||||||
(com/data-grid-header {} "Requires feedback transactions")
|
(com/data-grid-header {} "Client Review transactions")
|
||||||
(com/data-grid-header {} "Missing transactions")])
|
(com/data-grid-header {} "Missing transactions")])
|
||||||
#_#_:thead-params {:class "sticky top-0 z-50"}}
|
#_#_:thead-params {:class "sticky top-0 z-50"}}
|
||||||
(for [row report]
|
(for [row report]
|
||||||
@@ -84,18 +84,18 @@
|
|||||||
(com/validated-field {:label "Start"
|
(com/validated-field {:label "Start"
|
||||||
:errors (fc/field-errors)}
|
:errors (fc/field-errors)}
|
||||||
[:div {:class "w-64"}
|
[:div {:class "w-64"}
|
||||||
(com/date-input {:name (fc/field-name)
|
(com/date-input {:name (fc/field-name)
|
||||||
:class "w-64"
|
:class "w-64"
|
||||||
:value (some-> (fc/field-value)
|
:value (some-> (fc/field-value)
|
||||||
(atime/unparse-local atime/normal-date))})]))
|
(atime/unparse-local atime/normal-date))})]))
|
||||||
(fc/with-field :end-date
|
(fc/with-field :end-date
|
||||||
(com/validated-field {:label "End"
|
(com/validated-field {:label "End"
|
||||||
:errors (fc/field-errors)}
|
:errors (fc/field-errors)}
|
||||||
[:div {:class "w-64"}
|
[:div {:class "w-64"}
|
||||||
(com/date-input {:name (fc/field-name)
|
(com/date-input {:name (fc/field-name)
|
||||||
:class "w-64"
|
:class "w-64"
|
||||||
:value (some-> (fc/field-value)
|
:value (some-> (fc/field-value)
|
||||||
(atime/unparse-local atime/normal-date))})]))
|
(atime/unparse-local atime/normal-date))})]))
|
||||||
(com/button {:color :primary :class "self-center w-24"} "Run")])]
|
(com/button {:color :primary :class "self-center w-24"} "Run")])]
|
||||||
(if report
|
(if report
|
||||||
(report* {:request request :report report})
|
(report* {:request request :report report})
|
||||||
@@ -104,15 +104,15 @@
|
|||||||
(defn page [request]
|
(defn page [request]
|
||||||
(base-page
|
(base-page
|
||||||
request
|
request
|
||||||
(com/page {:nav com/company-aside-nav
|
(com/page {:nav com/company-aside-nav
|
||||||
:client-selection (:client-selection request)
|
:client-selection (:client-selection request)
|
||||||
:client (:client request)
|
:client (:client request)
|
||||||
:clients (:clients request)
|
:clients (:clients request)
|
||||||
:identity (:identity request)
|
:identity (:identity request)
|
||||||
:app-params {:hx-get (bidi/path-for ssr-routes/only-routes :company-reconciliation-report)
|
:app-params {:hx-get (bidi/path-for ssr-routes/only-routes :company-reconciliation-report)
|
||||||
:hx-trigger "clientSelected from:body"
|
:hx-trigger "clientSelected from:body"
|
||||||
:hx-select "#app-contents"
|
:hx-select "#app-contents"
|
||||||
:hx-swap "outerHTML swap:300ms"}}
|
:hx-swap "outerHTML swap:300ms"}}
|
||||||
(com/breadcrumbs {}
|
(com/breadcrumbs {}
|
||||||
[:a {:href (bidi/path-for ssr-routes/only-routes :company)}
|
[:a {:href (bidi/path-for ssr-routes/only-routes :company)}
|
||||||
"My Company"]
|
"My Company"]
|
||||||
@@ -133,7 +133,7 @@
|
|||||||
|
|
||||||
(defn get-report-data [start-date end-date client-ids]
|
(defn get-report-data [start-date end-date client-ids]
|
||||||
(let [client-codes (map first (dc/q '[:find ?cc :in $ [?c ...] :where [?c :client/code ?cc]] (dc/db conn) client-ids))]
|
(let [client-codes (map first (dc/q '[:find ?cc :in $ [?c ...] :where [?c :client/code ?cc]] (dc/db conn) client-ids))]
|
||||||
(for [[ib ba c] (seq (apply get-intuit-bank-accounts (dc/db conn) client-codes))
|
(for [[ib ba c] (seq (apply get-intuit-bank-accounts (dc/db conn) client-codes))
|
||||||
:let [raw-transactions (get-transactions (atime/unparse-local start-date atime/iso-date)
|
:let [raw-transactions (get-transactions (atime/unparse-local start-date atime/iso-date)
|
||||||
(atime/unparse-local end-date atime/iso-date)
|
(atime/unparse-local end-date atime/iso-date)
|
||||||
ib)
|
ib)
|
||||||
|
|||||||
@@ -138,6 +138,33 @@
|
|||||||
[:div.space-y-1 {}
|
[:div.space-y-1 {}
|
||||||
children])
|
children])
|
||||||
|
|
||||||
|
(defn flatten-form-errors
|
||||||
|
"Walks a malli-humanized error structure and returns a flat sequence of
|
||||||
|
human-readable strings, prefixing each leaf message with the nearest
|
||||||
|
field name for context. Lets the footer's error bar surface every
|
||||||
|
validation error for the whole form, even ones whose field lives on a
|
||||||
|
hidden step/tab and so would otherwise be invisible."
|
||||||
|
([errors] (flatten-form-errors nil errors))
|
||||||
|
([field errors]
|
||||||
|
(let [label (cond (keyword? field) (name field)
|
||||||
|
(string? field) field
|
||||||
|
:else nil)
|
||||||
|
decorate (fn [msg] (if label (str label ": " msg) msg))]
|
||||||
|
(cond
|
||||||
|
(map? errors)
|
||||||
|
(mapcat (fn [[k v]] (flatten-form-errors k v)) errors)
|
||||||
|
|
||||||
|
(and (sequential? errors) (every? string? errors))
|
||||||
|
(map decorate errors)
|
||||||
|
|
||||||
|
(sequential? errors)
|
||||||
|
(mapcat #(flatten-form-errors field %) errors)
|
||||||
|
|
||||||
|
(string? errors)
|
||||||
|
[(decorate errors)]
|
||||||
|
|
||||||
|
:else nil))))
|
||||||
|
|
||||||
(defn default-step-footer [linear-wizard step & {:keys [validation-route
|
(defn default-step-footer [linear-wizard step & {:keys [validation-route
|
||||||
discard-button
|
discard-button
|
||||||
next-button
|
next-button
|
||||||
@@ -146,7 +173,8 @@
|
|||||||
[:div.flex.items-baseline.gap-x-4
|
[:div.flex.items-baseline.gap-x-4
|
||||||
(let [step-errors (:step-params fc/*form-errors*)]
|
(let [step-errors (:step-params fc/*form-errors*)]
|
||||||
(com/form-errors {:errors (or (:errors step-errors)
|
(com/form-errors {:errors (or (:errors step-errors)
|
||||||
(when (sequential? step-errors) step-errors))}))
|
(when (sequential? step-errors) step-errors)
|
||||||
|
(seq (distinct (flatten-form-errors step-errors))))}))
|
||||||
(when (not= (first (steps linear-wizard))
|
(when (not= (first (steps linear-wizard))
|
||||||
(step-key step))
|
(step-key step))
|
||||||
(when validation-route
|
(when validation-route
|
||||||
|
|||||||
@@ -185,28 +185,28 @@
|
|||||||
:hx-target "#account-entries"
|
:hx-target "#account-entries"
|
||||||
:hx-swap "innerHTML"
|
:hx-swap "innerHTML"
|
||||||
:hx-include "closest form"}
|
:hx-include "closest form"}
|
||||||
(fc/with-field :vendor
|
(fc/with-field :vendor
|
||||||
(com/validated-field {:label "Vendor"
|
(com/validated-field {:label "Vendor"
|
||||||
:errors (fc/field-errors)}
|
:errors (fc/field-errors)}
|
||||||
(com/typeahead {:name (fc/field-name)
|
(com/typeahead {:name (fc/field-name)
|
||||||
:placeholder "Search for vendor..."
|
:placeholder "Search for vendor..."
|
||||||
:url (bidi/path-for ssr-routes/only-routes :vendor-search)
|
:url (bidi/path-for ssr-routes/only-routes :vendor-search)
|
||||||
:value (fc/field-value)
|
:value (fc/field-value)
|
||||||
:content-fn (fn [c] (pull-attr (dc/db conn) :vendor/name c))})))]
|
:content-fn (fn [c] (pull-attr (dc/db conn) :vendor/name c))})))]
|
||||||
|
|
||||||
;; Status field
|
;; Status field
|
||||||
[:div
|
[:div
|
||||||
(fc/with-field :approval-status
|
(fc/with-field :approval-status
|
||||||
(com/validated-field {:label "Status"
|
(com/validated-field {:label "Status"
|
||||||
:errors (fc/field-errors)}
|
:errors (fc/field-errors)}
|
||||||
(com/select {:name (fc/field-name)
|
(com/select {:name (fc/field-name)
|
||||||
:value (some-> (fc/field-value)
|
:value (some-> (fc/field-value)
|
||||||
name)
|
name)
|
||||||
:options [["" "No Change"]
|
:options [["" "No Change"]
|
||||||
["approved" "Approved"]
|
["approved" "Approved"]
|
||||||
["unapproved" "Unapproved"]
|
["unapproved" "Unapproved"]
|
||||||
["suppressed" "Suppressed"]
|
["suppressed" "Suppressed"]
|
||||||
["requires_feedback" "Requires Feedback"]]})))]
|
["requires-feedback" "Client Review"]]})))]
|
||||||
|
|
||||||
;; Accounts section
|
;; Accounts section
|
||||||
[:div.col-span-2.pt-4
|
[:div.col-span-2.pt-4
|
||||||
@@ -219,10 +219,10 @@
|
|||||||
(com/data-grid {:headers [(com/data-grid-header {} "Account")
|
(com/data-grid {:headers [(com/data-grid-header {} "Account")
|
||||||
(com/data-grid-header {:class "w-32"} "Location")
|
(com/data-grid-header {:class "w-32"} "Location")
|
||||||
(com/data-grid-header {:class "w-16"} "%")
|
(com/data-grid-header {:class "w-16"} "%")
|
||||||
(com/data-grid-header {:class "w-16"})]}
|
(com/data-grid-header {:class "w-16"})]}
|
||||||
(fc/cursor-map #(transaction-account-row* {:value %}))
|
(fc/cursor-map #(transaction-account-row* {:value %}))
|
||||||
|
|
||||||
(com/data-grid-new-row {:colspan 4
|
(com/data-grid-new-row {:colspan 4
|
||||||
:hx-get (bidi/path-for ssr-routes/only-routes
|
:hx-get (bidi/path-for ssr-routes/only-routes
|
||||||
::route/bulk-code-new-account)
|
::route/bulk-code-new-account)
|
||||||
:row-offset 0
|
:row-offset 0
|
||||||
@@ -357,10 +357,10 @@
|
|||||||
{:errors (fc/field-errors)}
|
{:errors (fc/field-errors)}
|
||||||
(com/data-grid {:headers [(com/data-grid-header {} "Account")
|
(com/data-grid {:headers [(com/data-grid-header {} "Account")
|
||||||
(com/data-grid-header {:class "w-32"} "Location")
|
(com/data-grid-header {:class "w-32"} "Location")
|
||||||
(com/data-grid-header {:class "w-16"} "%")
|
(com/data-grid-header {:class "w-16"} "%")
|
||||||
(com/data-grid-header {:class "w-16"})]}
|
(com/data-grid-header {:class "w-16"})]}
|
||||||
(fc/cursor-map #(transaction-account-row* {:value %}))
|
(fc/cursor-map #(transaction-account-row* {:value %}))
|
||||||
(com/data-grid-new-row {:colspan 4
|
(com/data-grid-new-row {:colspan 4
|
||||||
:hx-get (bidi/path-for ssr-routes/only-routes
|
:hx-get (bidi/path-for ssr-routes/only-routes
|
||||||
::route/bulk-code-new-account)
|
::route/bulk-code-new-account)
|
||||||
:row-offset 0
|
:row-offset 0
|
||||||
|
|||||||
@@ -72,6 +72,27 @@
|
|||||||
(or (not= approval-status :transaction-approval-status/approved)
|
(or (not= approval-status :transaction-approval-status/approved)
|
||||||
(seq accounts)))]])
|
(seq accounts)))]])
|
||||||
|
|
||||||
|
(def account-coding-schema
|
||||||
|
"Validation for manually-coded transaction account rows. Applied only for
|
||||||
|
the :manual action: link / apply-rule actions build their own accounts
|
||||||
|
server-side, so the (often blank) account row carried along by the manual
|
||||||
|
tab must not be required when one of those actions is submitted."
|
||||||
|
[:maybe
|
||||||
|
[:vector {:coerce? true}
|
||||||
|
[:and
|
||||||
|
[:map
|
||||||
|
[:db/id {:optional true} [:maybe [:or temp-id entity-id]]]
|
||||||
|
[:transaction-account/account [:and entity-id
|
||||||
|
[:fn {:error/message "Not an allowed account."}
|
||||||
|
#(check-allowance % :account/default-allowance)]]]
|
||||||
|
[:transaction-account/location :string]
|
||||||
|
[:transaction-account/amount :double]]
|
||||||
|
[:fn {:error/fn (fn [r x] (:type r))
|
||||||
|
:error/path [:transaction-account/location]}
|
||||||
|
(fn [iea]
|
||||||
|
(check-location-belongs (:transaction-account/location iea)
|
||||||
|
(:transaction-account/account iea)))]]]])
|
||||||
|
|
||||||
(def edit-form-schema
|
(def edit-form-schema
|
||||||
(mc/schema
|
(mc/schema
|
||||||
[:and
|
[:and
|
||||||
@@ -81,23 +102,7 @@
|
|||||||
[:transaction/memo {:optional true} [:maybe [:string {:decode/string strip}]]]
|
[:transaction/memo {:optional true} [:maybe [:string {:decode/string strip}]]]
|
||||||
[:transaction/vendor {:optional true} [:maybe entity-id]]
|
[:transaction/vendor {:optional true} [:maybe entity-id]]
|
||||||
[:transaction/approval-status {:optional true} [:maybe (ref->enum-schema "transaction-approval-status")]]
|
[:transaction/approval-status {:optional true} [:maybe (ref->enum-schema "transaction-approval-status")]]
|
||||||
[:amount-mode {:optional true} [:maybe [:enum "$" "%"]]]
|
[:amount-mode {:optional true} [:maybe [:enum "$" "%"]]]]
|
||||||
[:transaction/accounts {:optional true}
|
|
||||||
[:maybe
|
|
||||||
[:vector {:coerce? true}
|
|
||||||
[:and
|
|
||||||
[:map
|
|
||||||
[:db/id {:optional true} [:maybe [:or temp-id entity-id]]]
|
|
||||||
[:transaction-account/account [:and entity-id
|
|
||||||
[:fn {:error/message "Not an allowed account."}
|
|
||||||
#(check-allowance % :account/default-allowance)]]]
|
|
||||||
[:transaction-account/location :string]
|
|
||||||
[:transaction-account/amount :double]]
|
|
||||||
[:fn {:error/fn (fn [r x] (:type r))
|
|
||||||
:error/path [:transaction-account/location]}
|
|
||||||
(fn [iea]
|
|
||||||
(check-location-belongs (:transaction-account/location iea)
|
|
||||||
(:transaction-account/account iea)))]]]]]]
|
|
||||||
[:multi {:dispatch :action}
|
[:multi {:dispatch :action}
|
||||||
[:apply-rule [:map
|
[:apply-rule [:map
|
||||||
[:rule-id {:optional true} [:maybe entity-id]]]]
|
[:rule-id {:optional true} [:maybe entity-id]]]]
|
||||||
@@ -110,7 +115,8 @@
|
|||||||
[:autopay-invoice-ids {:decode/string (fn [x] (edn/read-string x))} [:vector {:coerce? true} entity-id]]]]
|
[:autopay-invoice-ids {:decode/string (fn [x] (edn/read-string x))} [:vector {:coerce? true} entity-id]]]]
|
||||||
[:link-payment [:map
|
[:link-payment [:map
|
||||||
[:payment-id entity-id]]]
|
[:payment-id entity-id]]]
|
||||||
[:manual (require-approval [:map])]]]))
|
[:manual (require-approval [:map
|
||||||
|
[:transaction/accounts {:optional true} account-coding-schema]])]]]))
|
||||||
|
|
||||||
(defn clientize-vendor [{:vendor/keys [terms-overrides automatically-paid-when-due default-account account-overrides] :as vendor} client-id]
|
(defn clientize-vendor [{:vendor/keys [terms-overrides automatically-paid-when-due default-account account-overrides] :as vendor} client-id]
|
||||||
(if (nil? vendor)
|
(if (nil? vendor)
|
||||||
|
|||||||
@@ -211,7 +211,7 @@
|
|||||||
(com/data-grid-cell {} (fc/with-field :description-original
|
(com/data-grid-cell {} (fc/with-field :description-original
|
||||||
(com/text-input {:value (fc/field-value) :name (fc/field-name)})))
|
(com/text-input {:value (fc/field-value) :name (fc/field-name)})))
|
||||||
(com/data-grid-cell {} (fc/with-field :amount
|
(com/data-grid-cell {} (fc/with-field :amount
|
||||||
(com/money-input {:value (fc/field-value) :name (fc/field-name) :class "w-28"})))
|
(com/text-input {:value (fc/field-value) :name (fc/field-name) :class "w-28 text-right" :inputmode "decimal"})))
|
||||||
(com/data-grid-cell {} (fc/with-field :bank-account-code
|
(com/data-grid-cell {} (fc/with-field :bank-account-code
|
||||||
(com/text-input {:value (fc/field-value) :name (fc/field-name) :class "w-28"})))
|
(com/text-input {:value (fc/field-value) :name (fc/field-name) :class "w-28"})))
|
||||||
(com/data-grid-cell {} (fc/with-field :client-code
|
(com/data-grid-cell {} (fc/with-field :client-code
|
||||||
|
|||||||
Reference in New Issue
Block a user