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:
@@ -24,32 +24,32 @@
|
||||
|
||||
(def query-schema (mc/schema
|
||||
[:maybe
|
||||
(into [:map {} ]
|
||||
(into [:map {}]
|
||||
default-grid-fields-schema)]))
|
||||
|
||||
(def vendor-read '[:db/id
|
||||
:vendor/name
|
||||
{:vendor/legal-entity-1099-type [:db/ident]}
|
||||
{:vendor/legal-entity-tin-type [:db/ident]}
|
||||
{:vendor/address [:address/street1
|
||||
:address/city
|
||||
:address/state
|
||||
:address/zip]}
|
||||
:vendor/name
|
||||
{:vendor/legal-entity-1099-type [:db/ident]}
|
||||
{:vendor/legal-entity-tin-type [:db/ident]}
|
||||
{:vendor/address [:address/street1
|
||||
:address/city
|
||||
:address/state
|
||||
:address/zip]}
|
||||
{:vendor/default-account [:account/name]}
|
||||
:vendor/legal-entity-tin
|
||||
:vendor/legal-entity-name
|
||||
:vendor/legal-entity-first-name
|
||||
:vendor/legal-entity-middle-name
|
||||
:vendor/legal-entity-last-name])
|
||||
:vendor/legal-entity-tin
|
||||
:vendor/legal-entity-name
|
||||
:vendor/legal-entity-first-name
|
||||
:vendor/legal-entity-middle-name
|
||||
:vendor/legal-entity-last-name])
|
||||
|
||||
(defn sum-for-client-vendor [client-id vendor-id]
|
||||
(ffirst (dc/q '[:find
|
||||
(sum ?a)
|
||||
:with ?d
|
||||
:with ?d
|
||||
:in $ ?c ?v
|
||||
:where
|
||||
[?p :payment/client ?c]
|
||||
[?p :payment/date ?d ]
|
||||
[?p :payment/date ?d]
|
||||
[(>= ?d #inst "2025-01-01T08:00")]
|
||||
[(< ?d #inst "2026-01-01T08:00")]
|
||||
[?p :payment/type :payment-type/check]
|
||||
@@ -64,11 +64,11 @@
|
||||
(pull ?c [:client/code :db/id])
|
||||
(pull ?v vendor-read)
|
||||
(sum ?a)
|
||||
:with ?d
|
||||
:with ?d
|
||||
:in $ [?c ...] vendor-read
|
||||
:where
|
||||
[?p :payment/client ?c]
|
||||
[?p :payment/date ?d ]
|
||||
[?p :payment/date ?d]
|
||||
[(>= ?d #inst "2025-01-01T08:00")]
|
||||
[(< ?d #inst "2026-01-01T08:00")]
|
||||
[?p :payment/type :payment-type/check]
|
||||
@@ -78,105 +78,101 @@
|
||||
trimmed-clients
|
||||
vendor-read)
|
||||
all (->> results
|
||||
(filter (fn [[_ _ a]]
|
||||
(>= (or a 0.0) 600.0)))
|
||||
(sort-by (fn [[client _ amount]]
|
||||
[(:client/code client ) amount]))
|
||||
(into []))
|
||||
(filter (fn [[_ _ a]]
|
||||
(>= (or a 0.0) 600.0)))
|
||||
(sort-by (fn [[client _ amount]]
|
||||
[(:client/code client) amount]))
|
||||
(into []))
|
||||
paginated (apply-pagination-raw {:start (:start query-params)
|
||||
:per-page (:per-page query-params)} all)]
|
||||
[(:entries paginated) (:count paginated)]))
|
||||
|
||||
(def grid-page
|
||||
(helper/build
|
||||
{:id "entity-table"
|
||||
:nav com/company-aside-nav
|
||||
:id-fn (comp :db/id second)
|
||||
:fetch-page fetch-page
|
||||
:breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes
|
||||
:company)}
|
||||
"My Company"]
|
||||
(helper/build
|
||||
{:id "entity-table"
|
||||
:nav com/company-aside-nav
|
||||
:id-fn (comp :db/id second)
|
||||
:fetch-page fetch-page
|
||||
:breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes
|
||||
:company)}
|
||||
"My Company"]
|
||||
|
||||
[:a {:href (bidi/path-for ssr-routes/only-routes
|
||||
:company-1099)}
|
||||
"1099 Vendor Info"]]
|
||||
:title "1099 Vendors"
|
||||
:entity-name "Vendors"
|
||||
:query-schema query-schema
|
||||
:route :company-1099-vendor-table
|
||||
:row-buttons (fn [request e]
|
||||
[(com/icon-button {:hx-get (url (bidi/path-for ssr-routes/only-routes
|
||||
:company-1099-vendor-dialog
|
||||
:vendor-id (:db/id (second e)))
|
||||
{:client-id (:db/id (first e))})}
|
||||
svg/pencil)])
|
||||
:headers [{:key "Client"
|
||||
:name "Client"
|
||||
:sort-key "client"
|
||||
:render (comp :client/code first)}
|
||||
{:key "vendor-name"
|
||||
:name "Vendor Name"
|
||||
:sort-key "vendor"
|
||||
:render (fn [[_ vendor]]
|
||||
[:div.flex.whitespace-nowrap.items-center.gap-4
|
||||
[:div [:div (:vendor/name vendor)]
|
||||
[:div.text-sm.text-gray-400
|
||||
(or (-> vendor :vendor/legal-entity-name not-empty)
|
||||
(str (-> vendor :vendor/legal-entity-first-name) " "
|
||||
(-> vendor :vendor/legal-entity-middle-name) " "
|
||||
(-> vendor :vendor/legal-entity-last-name)))]]
|
||||
(when-let [t99-type (some-> vendor :vendor/legal-entity-1099-type :db/ident name)]
|
||||
(com/pill
|
||||
{:class "text-xs font-medium"
|
||||
:color :primary}
|
||||
(str/capitalize t99-type))
|
||||
)])}
|
||||
{:key "tin"
|
||||
:name "TIN"
|
||||
:sort-key "tin"
|
||||
:show-starting "md"
|
||||
:render (fn [[_ vendor]]
|
||||
[:div.flex.gap-4
|
||||
(when-let [tin (-> vendor :vendor/legal-entity-tin)]
|
||||
[:span {:class "text-xs font-medium py-0.5 "}
|
||||
tin])
|
||||
(when-let [tin-type (some-> vendor :vendor/legal-entity-tin-type :db/ident name)]
|
||||
(com/pill {:class "text-xs font-medium"
|
||||
:color :yellow}
|
||||
(name tin-type)))]
|
||||
)}
|
||||
{:key "expense-account"
|
||||
:name "Expense Account"
|
||||
:show-starting "md"
|
||||
:render (fn [[_ vendor]]
|
||||
[:div.flex.gap-4
|
||||
(when-let [tin (-> vendor :vendor/default-account :account/name)]
|
||||
[:span {:class "text-xs font-medium py-0.5 "}
|
||||
tin]) ])}
|
||||
{:key "address"
|
||||
:name "Address"
|
||||
:sort-key "address"
|
||||
:show-starting "lg"
|
||||
:render (fn [[_ vendor]]
|
||||
(if (-> vendor :vendor/address :address/street1)
|
||||
[:a {:href (bidi/path-for ssr-routes/only-routes
|
||||
:company-1099)}
|
||||
"1099 Vendor Info"]]
|
||||
:title "1099 Vendors"
|
||||
:entity-name "Vendors"
|
||||
:query-schema query-schema
|
||||
:route :company-1099-vendor-table
|
||||
:row-buttons (fn [request e]
|
||||
[(com/icon-button {:hx-get (url (bidi/path-for ssr-routes/only-routes
|
||||
:company-1099-vendor-dialog
|
||||
:vendor-id (:db/id (second e)))
|
||||
{:client-id (:db/id (first e))})}
|
||||
svg/pencil)])
|
||||
:headers [{:key "Client"
|
||||
:name "Client"
|
||||
:sort-key "client"
|
||||
:render (comp :client/code first)}
|
||||
{:key "vendor-name"
|
||||
:name "Vendor Name"
|
||||
:sort-key "vendor"
|
||||
:render (fn [[_ vendor]]
|
||||
[:div.flex.whitespace-nowrap.items-center.gap-4
|
||||
[:div [:div (:vendor/name vendor)]
|
||||
[:div.text-sm.text-gray-400
|
||||
(or (-> vendor :vendor/legal-entity-name not-empty)
|
||||
(str (-> vendor :vendor/legal-entity-first-name) " "
|
||||
(-> vendor :vendor/legal-entity-middle-name) " "
|
||||
(-> vendor :vendor/legal-entity-last-name)))]]
|
||||
(when-let [t99-type (some-> vendor :vendor/legal-entity-1099-type :db/ident name)]
|
||||
(com/pill
|
||||
{:class "text-xs font-medium"
|
||||
:color :primary}
|
||||
(str/capitalize t99-type)))])}
|
||||
{:key "tin"
|
||||
:name "TIN"
|
||||
:sort-key "tin"
|
||||
:show-starting "md"
|
||||
:render (fn [[_ vendor]]
|
||||
[:div.flex.gap-4
|
||||
(when-let [tin (-> vendor :vendor/legal-entity-tin)]
|
||||
[:span {:class "text-xs font-medium py-0.5 "}
|
||||
tin])
|
||||
(when-let [tin-type (some-> vendor :vendor/legal-entity-tin-type :db/ident name)]
|
||||
(com/pill {:class "text-xs font-medium"
|
||||
:color :yellow}
|
||||
(name tin-type)))])}
|
||||
{:key "expense-account"
|
||||
:name "Expense Account"
|
||||
:show-starting "md"
|
||||
:render (fn [[_ vendor]]
|
||||
[:div.flex.gap-4
|
||||
(when-let [tin (-> vendor :vendor/default-account :account/name)]
|
||||
[:span {:class "text-xs font-medium py-0.5 "}
|
||||
tin])])}
|
||||
{:key "address"
|
||||
:name "Address"
|
||||
:sort-key "address"
|
||||
:show-starting "lg"
|
||||
:render (fn [[_ vendor]]
|
||||
(if (-> vendor :vendor/address :address/street1)
|
||||
[:div
|
||||
[:div (-> vendor :vendor/address :address/street1)] " "
|
||||
[:div
|
||||
[:div (-> vendor :vendor/address :address/street1)] " "
|
||||
[:div
|
||||
(-> vendor :vendor/address :address/street2)] " "
|
||||
[:div
|
||||
(-> vendor :vendor/address :address/city) " "
|
||||
(-> vendor :vendor/address :address/state) ","
|
||||
(-> vendor :vendor/address :address/zip)]]
|
||||
[:p.text-sm.italic.text-gray-400 "No address"]))}
|
||||
{:key "paid"
|
||||
:name "Paid"
|
||||
:sort-key "paid"
|
||||
:render (fn [[_ _ paid]]
|
||||
(com/pill {:class "text-xs font-medium"
|
||||
:color :primary}
|
||||
"Paid $" (Math/round paid)))}]}))
|
||||
|
||||
|
||||
(-> vendor :vendor/address :address/street2)] " "
|
||||
[:div
|
||||
(-> vendor :vendor/address :address/city) " "
|
||||
(-> vendor :vendor/address :address/state) ","
|
||||
(-> vendor :vendor/address :address/zip)]]
|
||||
[:p.text-sm.italic.text-gray-400 "No address"]))}
|
||||
{:key "paid"
|
||||
:name "Paid"
|
||||
:sort-key "paid"
|
||||
:render (fn [[_ _ paid]]
|
||||
(com/pill {:class "text-xs font-medium"
|
||||
:color :primary}
|
||||
"Paid $" (Math/round paid)))}]}))
|
||||
|
||||
(def table* (partial helper/table* grid-page))
|
||||
(def row* (partial helper/row* grid-page))
|
||||
@@ -185,7 +181,6 @@
|
||||
{:keys [vendor-id]} :route-params
|
||||
{:keys [client-id]} :query-params}]
|
||||
|
||||
|
||||
(assert-can-see-client identity client-id)
|
||||
|
||||
@(dc/transact conn [[:upsert-entity (-> form-params
|
||||
@@ -198,30 +193,28 @@
|
||||
(:address/zip a)
|
||||
(:db/id a))
|
||||
a
|
||||
nil)) ))]])
|
||||
(html-response
|
||||
nil))))]])
|
||||
(html-response
|
||||
|
||||
(row* identity [(dc/pull (dc/db conn) [:db/id :client/code] client-id)
|
||||
(dc/pull (dc/db conn) vendor-read vendor-id)
|
||||
(sum-for-client-vendor client-id vendor-id)
|
||||
] {:flash? true})
|
||||
:headers {"hx-trigger" "modalclose"
|
||||
"hx-retarget" (format "#entity-table tr[data-id=\"%d\"]" vendor-id)}))
|
||||
(row* identity [(dc/pull (dc/db conn) [:db/id :client/code] client-id)
|
||||
(dc/pull (dc/db conn) vendor-read vendor-id)
|
||||
(sum-for-client-vendor client-id vendor-id)] {:flash? true})
|
||||
:headers {"hx-trigger" "modalclose"
|
||||
"hx-retarget" (format "#entity-table tr[data-id=\"%d\"]" vendor-id)}))
|
||||
|
||||
(def default-vendor-read '[* {[:vendor/legal-entity-1099-type :xform iol-ion.query/ident] [:db/ident]
|
||||
[:vendor/legal-entity-tin-type :xform iol-ion.query/ident] [:db/ident]}])
|
||||
|
||||
|
||||
(def form-schema (mc/schema [:map
|
||||
[:vendor/address {:default {}}
|
||||
[:maybe
|
||||
[:map
|
||||
[:map
|
||||
[:db/id {:optional true} [:maybe entity-id]]
|
||||
[:address/street1 {:optional true} [:maybe [:string {:decode/string strip}]]]
|
||||
[:address/street2 {:optional true} [:maybe [:string {:decode/string strip}]]]
|
||||
[:address/city {:optional true} [:maybe [:string {:decode/string strip}]]]
|
||||
[:address/state {:optional true} [:maybe [:string {:decode/string strip}]]]
|
||||
[:address/zip {:optional true} [:maybe [:re { :error/message "invalid zip"
|
||||
[:address/zip {:optional true} [:maybe [:re {:error/message "invalid zip"
|
||||
:decode/string strip} #"^(\d{5}|)$"]]]]]]
|
||||
[:vendor/legal-entity-name {:optional true} [:maybe [:string {:decode/string strip}]]]
|
||||
[:vendor/legal-entity-first-name {:optional true} [:maybe [:string {:decode/string strip}]]]
|
||||
@@ -237,131 +230,131 @@
|
||||
(when entity
|
||||
(mc/decode form-schema entity main-transformer))
|
||||
{})
|
||||
form-errors
|
||||
(modal-response
|
||||
(com/modal
|
||||
{}
|
||||
[:form {:hx-post (url (bidi/path-for ssr-routes/only-routes
|
||||
:company-1099-vendor-save
|
||||
:request-method :post
|
||||
:vendor-id vendor-id)
|
||||
{:client-id client-id})
|
||||
:class "w-full h-full max-w-2xl"
|
||||
:hx-swap "outerHTML swap:300ms"}
|
||||
form-errors
|
||||
(modal-response
|
||||
(com/modal
|
||||
{}
|
||||
[:form {:hx-post (url (bidi/path-for ssr-routes/only-routes
|
||||
:company-1099-vendor-save
|
||||
:request-method :post
|
||||
:vendor-id vendor-id)
|
||||
{:client-id client-id})
|
||||
:class "w-full h-full max-w-2xl"
|
||||
:hx-swap "outerHTML swap:300ms"}
|
||||
|
||||
[:fieldset {:class "hx-disable w-full h-full"}
|
||||
(com/modal-card
|
||||
{}
|
||||
[:div.flex [:div.p-2 "Vendor 1099 Info"] [:p.ml-2.rounded.bg-gray-200.p-2.dark:bg-gray-600 (:vendor/name entity)]]
|
||||
[:div.grid.grid-cols-6.gap-x-4.gap-y-2
|
||||
|
||||
(fc/with-field-default :vendor/address {}
|
||||
(println "ADDRESS" fc/*current*)
|
||||
(list [:h4.text-xl.border-b.col-span-6 "Address"]
|
||||
[:div.col-span-6
|
||||
(fc/with-field :db/id
|
||||
(com/hidden {:name (fc/field-name)
|
||||
:value (fc/field-value)}))
|
||||
[:fieldset {:class "hx-disable w-full h-full"}
|
||||
(com/modal-card
|
||||
{}
|
||||
[:div.flex [:div.p-2 "Vendor 1099 Info"] [:p.ml-2.rounded.bg-gray-200.p-2.dark:bg-gray-600 (:vendor/name entity)]]
|
||||
[:div.grid.grid-cols-6.gap-x-4.gap-y-2
|
||||
|
||||
(fc/with-field :address/street1
|
||||
(com/validated-field {:label "Street 1"
|
||||
:errors (fc/field-errors)}
|
||||
(com/text-input {:name (fc/field-name)
|
||||
:class "w-full"
|
||||
:value (fc/field-value)
|
||||
:placeholder "1700 Pennsylvania Ave"
|
||||
:autofocus true})))]
|
||||
[:div.col-span-6
|
||||
(fc/with-field :address/street2
|
||||
(com/validated-field {:label "Street 2"
|
||||
:errors (fc/field-errors)}
|
||||
(com/text-input {:name (fc/field-name)
|
||||
:class "w-full"
|
||||
:value (fc/field-value)
|
||||
:placeholder "Suite 200"})))]
|
||||
[:div.col-span-3
|
||||
(fc/with-field :address/city
|
||||
(com/validated-field {:label "City"
|
||||
:errors (fc/field-errors)}
|
||||
(com/text-input {:name (fc/field-name)
|
||||
:class "w-full"
|
||||
:value (fc/field-value)
|
||||
:placeholder "Cupertino"})))]
|
||||
[:div.col-span-1
|
||||
(fc/with-field :address/state
|
||||
(com/validated-field {:label "State"
|
||||
:errors (fc/field-errors)}
|
||||
(com/text-input {:name (fc/field-name)
|
||||
:class "w-full"
|
||||
:value (fc/field-value)
|
||||
:placeholder "CA"})))]
|
||||
[:div.col-span-2
|
||||
(fc/with-field :address/zip
|
||||
(com/validated-field {:label "Zip"
|
||||
:errors (fc/field-errors)}
|
||||
(com/text-input {:name (fc/field-name)
|
||||
:class "w-full"
|
||||
:value (fc/field-value)
|
||||
:placeholder "98102"})))]))
|
||||
|
||||
[:h4.text-xl.border-b.col-span-6 "Legal Entity"]
|
||||
[:div.col-span-6
|
||||
(fc/with-field :vendor/legal-entity-name
|
||||
(com/validated-field {:label "Legal Entity Name"
|
||||
:errors (fc/field-errors)}
|
||||
(com/text-input {:name (fc/field-name)
|
||||
:class "w-full"
|
||||
:value (fc/field-value)
|
||||
:placeholder "Good Restaurant LLC"})))]
|
||||
[:div.col-span-6.text-center " - OR -"]
|
||||
[:div.col-span-2
|
||||
(fc/with-field :vendor/legal-entity-first-name
|
||||
(com/validated-field {:label "First Name"
|
||||
:errors (fc/field-errors)}
|
||||
(com/text-input {:name (fc/field-name)
|
||||
:value (fc/field-value)
|
||||
:placeholder "John"})))]
|
||||
[:div.col-span-2
|
||||
(fc/with-field :vendor/legal-entity-middle-name
|
||||
(com/validated-field {:label "Middle Name"
|
||||
:errors (fc/field-errors)}
|
||||
(com/text-input {:name (fc/field-name)
|
||||
:value (fc/field-value)
|
||||
:placeholder "C."})))]
|
||||
[:div.col-span-2
|
||||
(fc/with-field :vendor/legal-entity-last-name
|
||||
(com/validated-field {:label "Last Name"
|
||||
:errors (fc/field-errors)}
|
||||
(com/text-input {:name (fc/field-name)
|
||||
:value (fc/field-value)
|
||||
:placeholder "Riley"})))]
|
||||
[:div.col-span-2
|
||||
(fc/with-field :vendor/legal-entity-tin
|
||||
(com/validated-field {:label "TIN"
|
||||
:errors (fc/field-errors)}
|
||||
(com/text-input {:name (fc/field-name)
|
||||
:value (fc/field-value)
|
||||
:placeholder "John"})))]
|
||||
[:div.col-span-2
|
||||
(fc/with-field :vendor/legal-entity-tin-type
|
||||
(com/validated-field {:label "TIN Type"
|
||||
:errors (fc/field-errors)}
|
||||
(com/select {:name (fc/field-name)
|
||||
:allow-blank? true
|
||||
:value (some-> (fc/field-value) name)
|
||||
:options [["ein" "EIN"]
|
||||
["ssn" "SSN"]]})))]
|
||||
[:div.col-span-2
|
||||
(fc/with-field :vendor/legal-entity-1099-type
|
||||
(com/validated-field {:label "1099 Type"
|
||||
:errors (fc/field-errors)}
|
||||
(com/select {:name (fc/field-name)
|
||||
:allow-blank? true
|
||||
:value (some-> (fc/field-value) name)
|
||||
:options (ref->select-options "legal-entity-1099-type")})))]]
|
||||
[:div
|
||||
(com/form-errors {:errors (:errors fc/*form-errors*)})
|
||||
(com/validated-save-button {:errors form-errors} "Save vendor")])]]))))
|
||||
(fc/with-field-default :vendor/address {}
|
||||
(println "ADDRESS" fc/*current*)
|
||||
(list [:h4.text-xl.border-b.col-span-6 "Address"]
|
||||
[:div.col-span-6
|
||||
(fc/with-field :db/id
|
||||
(com/hidden {:name (fc/field-name)
|
||||
:value (fc/field-value)}))
|
||||
|
||||
(fc/with-field :address/street1
|
||||
(com/validated-field {:label "Street 1"
|
||||
:errors (fc/field-errors)}
|
||||
(com/text-input {:name (fc/field-name)
|
||||
:class "w-full"
|
||||
:value (fc/field-value)
|
||||
:placeholder "1700 Pennsylvania Ave"
|
||||
:autofocus true})))]
|
||||
[:div.col-span-6
|
||||
(fc/with-field :address/street2
|
||||
(com/validated-field {:label "Street 2"
|
||||
:errors (fc/field-errors)}
|
||||
(com/text-input {:name (fc/field-name)
|
||||
:class "w-full"
|
||||
:value (fc/field-value)
|
||||
:placeholder "Suite 200"})))]
|
||||
[:div.col-span-3
|
||||
(fc/with-field :address/city
|
||||
(com/validated-field {:label "City"
|
||||
:errors (fc/field-errors)}
|
||||
(com/text-input {:name (fc/field-name)
|
||||
:class "w-full"
|
||||
:value (fc/field-value)
|
||||
:placeholder "Cupertino"})))]
|
||||
[:div.col-span-1
|
||||
(fc/with-field :address/state
|
||||
(com/validated-field {:label "State"
|
||||
:errors (fc/field-errors)}
|
||||
(com/text-input {:name (fc/field-name)
|
||||
:class "w-full"
|
||||
:value (fc/field-value)
|
||||
:placeholder "CA"})))]
|
||||
[:div.col-span-2
|
||||
(fc/with-field :address/zip
|
||||
(com/validated-field {:label "Zip"
|
||||
:errors (fc/field-errors)}
|
||||
(com/text-input {:name (fc/field-name)
|
||||
:class "w-full"
|
||||
:value (fc/field-value)
|
||||
:placeholder "98102"})))]))
|
||||
|
||||
[:h4.text-xl.border-b.col-span-6 "Legal Entity"]
|
||||
[:div.col-span-6
|
||||
(fc/with-field :vendor/legal-entity-name
|
||||
(com/validated-field {:label "Legal Entity Name"
|
||||
:errors (fc/field-errors)}
|
||||
(com/text-input {:name (fc/field-name)
|
||||
:class "w-full"
|
||||
:value (fc/field-value)
|
||||
:placeholder "Good Restaurant LLC"})))]
|
||||
[:div.col-span-6.text-center " - OR -"]
|
||||
[:div.col-span-2
|
||||
(fc/with-field :vendor/legal-entity-first-name
|
||||
(com/validated-field {:label "First Name"
|
||||
:errors (fc/field-errors)}
|
||||
(com/text-input {:name (fc/field-name)
|
||||
:value (fc/field-value)
|
||||
:placeholder "John"})))]
|
||||
[:div.col-span-2
|
||||
(fc/with-field :vendor/legal-entity-middle-name
|
||||
(com/validated-field {:label "Middle Name"
|
||||
:errors (fc/field-errors)}
|
||||
(com/text-input {:name (fc/field-name)
|
||||
:value (fc/field-value)
|
||||
:placeholder "C."})))]
|
||||
[:div.col-span-2
|
||||
(fc/with-field :vendor/legal-entity-last-name
|
||||
(com/validated-field {:label "Last Name"
|
||||
:errors (fc/field-errors)}
|
||||
(com/text-input {:name (fc/field-name)
|
||||
:value (fc/field-value)
|
||||
:placeholder "Riley"})))]
|
||||
[:div.col-span-2
|
||||
(fc/with-field :vendor/legal-entity-tin
|
||||
(com/validated-field {:label "TIN"
|
||||
:errors (fc/field-errors)}
|
||||
(com/text-input {:name (fc/field-name)
|
||||
:value (fc/field-value)
|
||||
:placeholder "John"})))]
|
||||
[:div.col-span-2
|
||||
(fc/with-field :vendor/legal-entity-tin-type
|
||||
(com/validated-field {:label "TIN Type"
|
||||
:errors (fc/field-errors)}
|
||||
(com/select {:name (fc/field-name)
|
||||
:allow-blank? true
|
||||
:value (some-> (fc/field-value) name)
|
||||
:options [["ein" "EIN"]
|
||||
["ssn" "SSN"]]})))]
|
||||
[:div.col-span-2
|
||||
(fc/with-field :vendor/legal-entity-1099-type
|
||||
(com/validated-field {:label "1099 Type"
|
||||
:errors (fc/field-errors)}
|
||||
(com/select {:name (fc/field-name)
|
||||
:allow-blank? true
|
||||
:value (some-> (fc/field-value) name)
|
||||
:options (ref->select-options "legal-entity-1099-type")})))]]
|
||||
[:div
|
||||
(com/form-errors {:errors (:errors fc/*form-errors*)})
|
||||
(com/validated-save-button {:errors form-errors} "Save vendor")])]]))))
|
||||
|
||||
(def vendor-table (helper/table-route grid-page))
|
||||
(def page (helper/page-route grid-page))
|
||||
|
||||
@@ -25,9 +25,9 @@
|
||||
[hiccup2.core :as hiccup]
|
||||
[malli.core :as mc]))
|
||||
(def query-schema (mc/schema
|
||||
[:maybe
|
||||
(into [:map {} ]
|
||||
default-grid-fields-schema)]))
|
||||
[:maybe
|
||||
(into [:map {}]
|
||||
default-grid-fields-schema)]))
|
||||
|
||||
(def default-read '[:db/id
|
||||
:plaid-item/external-id
|
||||
@@ -37,7 +37,7 @@
|
||||
|
||||
{:plaid-item/accounts [:db/id
|
||||
{:bank-account/_plaid-account [{:bank-account/integration-status
|
||||
[{ [ :integration-status/state :xform iol-ion.query/ident] [:db/ident]}
|
||||
[{[:integration-status/state :xform iol-ion.query/ident] [:db/ident]}
|
||||
:integration-status/message
|
||||
:integration-status/last-attempt
|
||||
:integration-status/last-updated]}]}
|
||||
@@ -66,7 +66,6 @@
|
||||
true (apply-sort-3 query-params)
|
||||
true (apply-pagination query-params))))
|
||||
|
||||
|
||||
(defn hydrate-results [ids db _]
|
||||
(let [results (pull-many-by-id db default-read ids)]
|
||||
(->> ids
|
||||
@@ -78,15 +77,12 @@
|
||||
[(hydrate-results ids-to-retrieve db request)
|
||||
matching-count]))
|
||||
|
||||
|
||||
|
||||
(defn plaid-link-script [token]
|
||||
(format "window.plaid = Plaid.create(
|
||||
{ token: \"%s\",
|
||||
onSuccess: function (x) { htmx.trigger(\"#link-account\", \"linked\", {\"public_token\": x})}
|
||||
})", token))
|
||||
|
||||
|
||||
(defn link [{{client-code "client_code" public-token "public_token"} :form-params
|
||||
:keys [identity]
|
||||
:as request}]
|
||||
@@ -99,24 +95,24 @@
|
||||
(alog/info ::linking-plaid :id identity :client-code client-code)
|
||||
(assert-can-see-client identity (pull-attr (dc/db conn) :db/id [:client/code client-code]))
|
||||
(let [access-token (:access_token (p/exchange-public-token public-token client-code))
|
||||
account-result (p/get-accounts access-token )
|
||||
account-result (p/get-accounts access-token)
|
||||
item {:plaid-item/client [:client/code client-code]
|
||||
:plaid-item/external-id (-> account-result :item :item_id )
|
||||
:plaid-item/external-id (-> account-result :item :item_id)
|
||||
:plaid-item/access-token access-token
|
||||
:plaid-item/status (or (some-> account-result :item :error)
|
||||
"SUCCESS")
|
||||
"SUCCESS")
|
||||
:plaid-item/last-updated (coerce/to-date (time/now))
|
||||
:db/id "plaid-item"}]
|
||||
|
||||
@(dc/transact conn (->> (:accounts account-result)
|
||||
(map (fn [a]
|
||||
(let [balance (some-> a :balances :current (* 0.01))]
|
||||
(cond-> {:plaid-account/external-id (:account_id a)
|
||||
:plaid-account/number (:mask a)
|
||||
:plaid-account/name (str (:name a) " " (:mask a))
|
||||
:plaid-item/_accounts "plaid-item"}
|
||||
balance (assoc :plaid-account/balance balance)))))
|
||||
(into [item])))
|
||||
(map (fn [a]
|
||||
(let [balance (some-> a :balances :current (* 0.01))]
|
||||
(cond-> {:plaid-account/external-id (:account_id a)
|
||||
:plaid-account/number (:mask a)
|
||||
:plaid-account/name (str (:name a) " " (:mask a))
|
||||
:plaid-item/_accounts "plaid-item"}
|
||||
balance (assoc :plaid-account/balance balance)))))
|
||||
(into [item])))
|
||||
(alog/info ::access-token-was :token access-token)
|
||||
{:headers {"Hx-redirect" (bidi/path-for ssr-routes/only-routes
|
||||
:company-plaid)}}))
|
||||
@@ -141,115 +137,110 @@
|
||||
(com/button-icon {} svg/refresh)
|
||||
"Start relink")])))
|
||||
|
||||
(def grid-page
|
||||
(helper/build
|
||||
{:id "plaid-table"
|
||||
:nav com/company-aside-nav
|
||||
:fetch-page fetch-page
|
||||
:breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes
|
||||
:company)}
|
||||
"My Company"]
|
||||
|
||||
(def grid-page
|
||||
(helper/build
|
||||
{:id "plaid-table"
|
||||
:nav com/company-aside-nav
|
||||
:fetch-page fetch-page
|
||||
:breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes
|
||||
:company)}
|
||||
"My Company"]
|
||||
[:a {:href (bidi/path-for ssr-routes/only-routes
|
||||
:company-plaid)}
|
||||
"Plaid"]]
|
||||
:title "Plaid Accounts"
|
||||
:entity-name "Plaid accounts"
|
||||
:query-schema query-schema
|
||||
:route :company-plaid-table
|
||||
:action-buttons (fn [request]
|
||||
(when-let [client-code (:client/code (:client request))]
|
||||
[[:div {:hx-post (str (bidi/path-for ssr-routes/only-routes
|
||||
:company-plaid-link
|
||||
:request-method :post))
|
||||
:hx-vals (hiccup/raw (format "js:{client_code: \"%s\", public_token: event.detail.public_token}", client-code))
|
||||
:hx-trigger "linked"}
|
||||
[:script (hiccup/raw (plaid-link-script (p/get-link-token client-code)))]
|
||||
(com/button {:color :primary
|
||||
:id "link-account"
|
||||
:onClick "window.plaid.open()"}
|
||||
(com/button-icon {} svg/refresh)
|
||||
(format "Link %s account" client-code))]]))
|
||||
:row-buttons (fn [request e]
|
||||
[[:div (com/button {:hx-put (str (bidi/path-for ssr-routes/only-routes
|
||||
:company-plaid-relink)
|
||||
"?plaid-item-id=" (:db/id e))
|
||||
:color :primary
|
||||
:hx-target "closest div"}
|
||||
"Reauthenticate")]])
|
||||
:headers [{:key "plaid-item"
|
||||
:name "Plaid Item"
|
||||
:sort-key "external-id"
|
||||
:render :plaid-item/external-id}
|
||||
{:key "integreat-plaid-status"
|
||||
:name "Integreat ↔ Plaid status"
|
||||
:render (fn [e]
|
||||
|
||||
[:a {:href (bidi/path-for ssr-routes/only-routes
|
||||
:company-plaid)}
|
||||
"Plaid"]]
|
||||
:title "Plaid Accounts"
|
||||
:entity-name "Plaid accounts"
|
||||
:query-schema query-schema
|
||||
:route :company-plaid-table
|
||||
:action-buttons (fn [request]
|
||||
(when-let [client-code (:client/code (:client request))]
|
||||
[[:div {:hx-post (str (bidi/path-for ssr-routes/only-routes
|
||||
:company-plaid-link
|
||||
:request-method :post))
|
||||
:hx-vals (hiccup/raw (format "js:{client_code: \"%s\", public_token: event.detail.public_token}", client-code))
|
||||
:hx-trigger "linked"}
|
||||
[:script (hiccup/raw (plaid-link-script (p/get-link-token client-code)))]
|
||||
(com/button {:color :primary
|
||||
:id "link-account"
|
||||
:onClick "window.plaid.open()"}
|
||||
(com/button-icon {} svg/refresh)
|
||||
(format "Link %s account" client-code))]]))
|
||||
:row-buttons (fn [request e]
|
||||
[[:div (com/button {:hx-put (str (bidi/path-for ssr-routes/only-routes
|
||||
:company-plaid-relink)
|
||||
"?plaid-item-id=" (:db/id e))
|
||||
:color :primary
|
||||
:hx-target "closest div"}
|
||||
"Reauthenticate")]])
|
||||
:headers [{:key "plaid-item"
|
||||
:name "Plaid Item"
|
||||
:sort-key "external-id"
|
||||
:render :plaid-item/external-id}
|
||||
{:key "integreat-plaid-status"
|
||||
:name "Integreat ↔ Plaid status"
|
||||
:render (fn [e]
|
||||
|
||||
(let [bad-integration (->> (:plaid-item/accounts e)
|
||||
(map (comp
|
||||
first
|
||||
:bank-account/_plaid-account))
|
||||
(filter (comp #{:integration-state/failed :integration-state/unauthorized}
|
||||
:integration-status/state
|
||||
:bank-account/integration-status))
|
||||
first
|
||||
:bank-account/integration-status)]
|
||||
[:div
|
||||
|
||||
[:div.cursor-pointer (com/pill (cond-> {:color :primary}
|
||||
bad-integration (assoc :color :red
|
||||
:x-tooltip "{content: ()=>$refs.tooltip.innerHTML, theme: 'light', allowHTML: true}"))
|
||||
(let [bad-integration (->> (:plaid-item/accounts e)
|
||||
(map (comp
|
||||
first
|
||||
:bank-account/_plaid-account))
|
||||
(filter (comp #{:integration-state/failed :integration-state/unauthorized}
|
||||
:integration-status/state
|
||||
:bank-account/integration-status))
|
||||
first
|
||||
:bank-account/integration-status)]
|
||||
[:div
|
||||
|
||||
[:div.inline-flex.gap-2
|
||||
(or
|
||||
(some-> bad-integration
|
||||
:integration-status/state
|
||||
name
|
||||
str/capitalize)
|
||||
"Success")
|
||||
(when bad-integration
|
||||
" (detail)")
|
||||
|
||||
|
||||
(when bad-integration
|
||||
[:template {:x-ref "tooltip"}
|
||||
[:div.text-red-700
|
||||
(:integration-status/message bad-integration)]])])]
|
||||
[:div.grid.grid-cols-2.gap-1.auto-cols-min.grid-flow-row.shrink
|
||||
[:div "Attempted: "] [:div (atime/unparse-local (coerce/to-date-time (:integration-status/last-attempt e)) atime/normal-date)]
|
||||
[:div "Last Updated: "] [:div (atime/unparse-local (coerce/to-date-time (:integration-status/last-updated e)) atime/normal-date)]]]))}
|
||||
{:key "plaid-bank-status"
|
||||
:name "Plaid ↔ Bank Status"
|
||||
:sort-key "plaid-bank-status"
|
||||
:render (fn [e]
|
||||
(when-let [status (:plaid-item/status e)]
|
||||
[:div [:div (com/pill {:color :primary}
|
||||
status)]
|
||||
[:div (atime/unparse-local (coerce/to-date-time (:plaid-item/last-updated e)) atime/normal-date)]]))}
|
||||
|
||||
{:key "accounts"
|
||||
:name "Accounts"
|
||||
:show-starting "md"
|
||||
:render (fn [e]
|
||||
[:ul
|
||||
(for [a (:plaid-item/accounts e)]
|
||||
[:li [:svg.inline {:data-jdenticon-value (:db/id a) :width "24" :height "24"}] (:plaid-account/name a) " - " (:plaid-account/number a) " - updated "
|
||||
(atime/unparse-local (:plaid-account/last-synced a) atime/normal-date)])])}]}))
|
||||
[:div.cursor-pointer (com/pill (cond-> {:color :primary}
|
||||
bad-integration (assoc :color :red
|
||||
:x-tooltip "{content: ()=>$refs.tooltip.innerHTML, theme: 'light', allowHTML: true}"))
|
||||
|
||||
[:div.inline-flex.gap-2
|
||||
(or
|
||||
(some-> bad-integration
|
||||
:integration-status/state
|
||||
name
|
||||
str/capitalize)
|
||||
"Success")
|
||||
(when bad-integration
|
||||
" (detail)")
|
||||
|
||||
(when bad-integration
|
||||
[:template {:x-ref "tooltip"}
|
||||
[:div.text-red-700
|
||||
(:integration-status/message bad-integration)]])])]
|
||||
[:div.grid.grid-cols-2.gap-1.auto-cols-min.grid-flow-row.shrink
|
||||
[:div "Attempted: "] [:div (atime/unparse-local (coerce/to-date-time (:integration-status/last-attempt e)) atime/normal-date)]
|
||||
[:div "Last Updated: "] [:div (atime/unparse-local (coerce/to-date-time (:integration-status/last-updated e)) atime/normal-date)]]]))}
|
||||
{:key "plaid-bank-status"
|
||||
:name "Plaid ↔ Bank Status"
|
||||
:sort-key "plaid-bank-status"
|
||||
:render (fn [e]
|
||||
(when-let [status (:plaid-item/status e)]
|
||||
[:div [:div (com/pill {:color :primary}
|
||||
status)]
|
||||
[:div (atime/unparse-local (coerce/to-date-time (:plaid-item/last-updated e)) atime/normal-date)]]))}
|
||||
|
||||
{:key "accounts"
|
||||
:name "Accounts"
|
||||
:show-starting "md"
|
||||
:render (fn [e]
|
||||
[:ul
|
||||
(for [a (:plaid-item/accounts e)]
|
||||
[:li [:svg.inline {:data-jdenticon-value (:db/id a) :width "24" :height "24"}] (:plaid-account/name a) " - " (:plaid-account/number a) " - updated "
|
||||
(atime/unparse-local (:plaid-account/last-synced a) atime/normal-date)])])}]}))
|
||||
|
||||
(def page (helper/page-route grid-page))
|
||||
(def table (helper/table-route grid-page))
|
||||
|
||||
(def key->handler
|
||||
(apply-middleware-to-all-handlers
|
||||
{
|
||||
:company-plaid page
|
||||
(def key->handler
|
||||
(apply-middleware-to-all-handlers
|
||||
{:company-plaid page
|
||||
:company-plaid-table table
|
||||
:company-plaid-link link
|
||||
:company-plaid-relink relink
|
||||
|
||||
}
|
||||
:company-plaid-relink relink}
|
||||
|
||||
(fn [h]
|
||||
(-> h
|
||||
(wrap-copy-qp-pqp)
|
||||
|
||||
@@ -27,13 +27,12 @@
|
||||
(def query-schema (mc/schema
|
||||
[:maybe
|
||||
(into [:map {:date-range [:date-range :start-date :end-date]}
|
||||
|
||||
|
||||
[:start-date {:optional true}
|
||||
[:maybe clj-date-schema]]
|
||||
[:end-date {:optional true}
|
||||
[:maybe clj-date-schema]]
|
||||
[:client {:optional true :default nil} [:maybe [:entity-map {:pull [:db/id :client/name]}]]]
|
||||
]
|
||||
[:client {:optional true :default nil} [:maybe [:entity-map {:pull [:db/id :client/name]}]]]]
|
||||
default-grid-fields-schema)]))
|
||||
(def default-read '[:db/id :report/client [:report/created :xform clj-time.coerce/from-date] :report/url :report/name :report/creator])
|
||||
|
||||
@@ -43,22 +42,20 @@
|
||||
query (cond-> {:query {:find []
|
||||
:in '[$ [?c ...]]
|
||||
:where '[[?e :report/client ?c]]}
|
||||
:args [db (:trimmed-clients request)]}
|
||||
:args [db (:trimmed-clients request)]}
|
||||
|
||||
|
||||
(:sort query-params) (add-sorter-fields {"client" ['[?e :report/client ?c]
|
||||
'[?c :client/name ?sort-client]]
|
||||
"created" ['[?e :report/created ?sort-created]]
|
||||
"creator" ['[?e :report/creator ?sort-creator]]
|
||||
"name" ['[?e :report/name ?sort-name]
|
||||
]}
|
||||
query-params)
|
||||
'[?c :client/name ?sort-client]]
|
||||
"created" ['[?e :report/created ?sort-created]]
|
||||
"creator" ['[?e :report/creator ?sort-creator]]
|
||||
"name" ['[?e :report/name ?sort-name]]}
|
||||
query-params)
|
||||
|
||||
true
|
||||
(merge-query {:query {:find ['?sort-default '?e] :where ['[?e :report/created ?sort-default]]}}))]
|
||||
(->> (query2 query)
|
||||
(apply-sort-3 (update query-params :sort conj {:sort-key "default-2" :asc true}))
|
||||
(apply-pagination query-params))))
|
||||
(apply-sort-3 (update query-params :sort conj {:sort-key "default-2" :asc true}))
|
||||
(apply-pagination query-params))))
|
||||
|
||||
(defn hydrate-results [ids db request]
|
||||
(let [results (->> (pull-many db default-read ids)
|
||||
@@ -67,7 +64,7 @@
|
||||
(->> ids
|
||||
(map results)
|
||||
(filter identity)
|
||||
|
||||
|
||||
(map first)
|
||||
(filter (fn [r]
|
||||
(let [used-clients (set (map :db/id (:report/client r)))]
|
||||
@@ -78,7 +75,7 @@
|
||||
(defn fetch-page [args]
|
||||
(let [db (dc/db conn)
|
||||
{ids-to-retrieve :ids matching-count :count} (fetch-ids db args)]
|
||||
|
||||
|
||||
[(->> (hydrate-results ids-to-retrieve db args))
|
||||
matching-count]))
|
||||
|
||||
@@ -115,7 +112,7 @@
|
||||
:sort-key "creator"
|
||||
:render (fn [report]
|
||||
(when (:report/creator report)
|
||||
(com/pill {:color :primary }
|
||||
(com/pill {:color :primary}
|
||||
(:report/creator report))))}
|
||||
{:key "created"
|
||||
:name "Created"
|
||||
@@ -129,7 +126,7 @@
|
||||
(def page (helper/page-route grid-page))
|
||||
|
||||
(defn delete-report [{:keys [form-params identity]}]
|
||||
|
||||
|
||||
(let [[id-to-delete key] (first (dc/q '[:find ?i ?k
|
||||
:in $ ?i
|
||||
:where [?i :report/key ?k]]
|
||||
@@ -137,29 +134,28 @@
|
||||
(some-> (get form-params "id") not-empty Long/parseLong)))
|
||||
report (dc/pull (dc/db conn) default-read id-to-delete)]
|
||||
(assert-can-see-client identity (:report/client report))
|
||||
(when id-to-delete
|
||||
(when id-to-delete
|
||||
(s3/delete-object :bucket-name (:data-bucket env)
|
||||
:key key)
|
||||
@(dc/transact conn [[:db/retractEntity id-to-delete]]))
|
||||
(html-response
|
||||
(row* identity
|
||||
report
|
||||
{:flash? true
|
||||
:delete-after-settle? true}))))
|
||||
|
||||
(html-response
|
||||
(row* identity
|
||||
report
|
||||
{:flash? true
|
||||
:delete-after-settle? true}))))
|
||||
|
||||
(def key->handler
|
||||
(apply-middleware-to-all-handlers
|
||||
(->>
|
||||
(into
|
||||
{:company-reports page
|
||||
:company-reports-table table
|
||||
:company-reports-delete delete-report}
|
||||
company-expense-report/key->handler)
|
||||
(into
|
||||
{:company-reports page
|
||||
:company-reports-table table
|
||||
:company-reports-delete delete-report}
|
||||
company-expense-report/key->handler)
|
||||
(into company-reconciliation-report/key->handler))
|
||||
(fn [h]
|
||||
(-> h
|
||||
(wrap-copy-qp-pqp)
|
||||
(wrap-copy-qp-pqp)
|
||||
(wrap-apply-sort grid-page)
|
||||
(wrap-merge-prior-hx)
|
||||
(wrap-schema-enforce :query-schema query-schema)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
(ns auto-ap.ssr.company.reports.expense
|
||||
(ns auto-ap.ssr.company.reports.expense
|
||||
(:require [auto-ap.datomic :refer [conn merge-query]]
|
||||
[auto-ap.graphql.utils :refer [extract-client-ids]]
|
||||
[auto-ap.logging :as alog]
|
||||
@@ -20,73 +20,71 @@
|
||||
[hiccup2.core :as hiccup]))
|
||||
|
||||
(defn lookup-breakdown-data [request]
|
||||
(let [query (cond-> {:query '{:find [?cn ?user-date (sum ?amt)]
|
||||
:with [?e]
|
||||
:in [$ [?clients ?start ?end]]
|
||||
:where
|
||||
[[(iol-ion.query/scan-invoices $ ?clients ?start ?end) [[?e _ ?sort-default] ...]]
|
||||
(not [?e :invoice/status :invoice-status/voided])
|
||||
[?e :invoice/date ?d]
|
||||
[?e :invoice/client ?c]
|
||||
[?e :invoice/expense-accounts ?iea]
|
||||
[?iea :invoice-expense-account/amount ?amt]
|
||||
[?c :client/name ?cn]
|
||||
[(clj-time.coerce/to-date-time ?d) ?user-date]]}
|
||||
:args
|
||||
[(dc/db conn)
|
||||
[(extract-client-ids (:clients request)
|
||||
(:client-id request)
|
||||
(when (:client-code request)
|
||||
[:client/code (:client-code request)]))
|
||||
(some-> (time/plus (time/now) (time/days -65)) coerce/to-date)
|
||||
(some-> (time/now) coerce/to-date)]]}
|
||||
(let [query (cond-> {:query '{:find [?cn ?user-date (sum ?amt)]
|
||||
:with [?e]
|
||||
:in [$ [?clients ?start ?end]]
|
||||
:where
|
||||
[[(iol-ion.query/scan-invoices $ ?clients ?start ?end) [[?e _ ?sort-default] ...]]
|
||||
(not [?e :invoice/status :invoice-status/voided])
|
||||
[?e :invoice/date ?d]
|
||||
[?e :invoice/client ?c]
|
||||
[?e :invoice/expense-accounts ?iea]
|
||||
[?iea :invoice-expense-account/amount ?amt]
|
||||
[?c :client/name ?cn]
|
||||
[(clj-time.coerce/to-date-time ?d) ?user-date]]}
|
||||
:args
|
||||
[(dc/db conn)
|
||||
[(extract-client-ids (:clients request)
|
||||
(:client-id request)
|
||||
(when (:client-code request)
|
||||
[:client/code (:client-code request)]))
|
||||
(some-> (time/plus (time/now) (time/days -65)) coerce/to-date)
|
||||
(some-> (time/now) coerce/to-date)]]}
|
||||
|
||||
(:vendor-id (:query-params request))
|
||||
(merge-query {:query '{:in [?v]
|
||||
:where [ [?e :invoice/vendor ?v]]}
|
||||
:args [ (:db/id (:vendor-id (:query-params request)))]})
|
||||
|
||||
(:account-id (:query-params request))
|
||||
(merge-query {:query '{:in [?a]
|
||||
:where [ [?iea :invoice-expense-account/account ?a]]}
|
||||
:args [ (:db/id (:account-id (:query-params request)))]}))]
|
||||
|
||||
(dc/query query)))
|
||||
(:vendor-id (:query-params request))
|
||||
(merge-query {:query '{:in [?v]
|
||||
:where [[?e :invoice/vendor ?v]]}
|
||||
:args [(:db/id (:vendor-id (:query-params request)))]})
|
||||
|
||||
(:account-id (:query-params request))
|
||||
(merge-query {:query '{:in [?a]
|
||||
:where [[?iea :invoice-expense-account/account ?a]]}
|
||||
:args [(:db/id (:account-id (:query-params request)))]}))]
|
||||
|
||||
(dc/query query)))
|
||||
|
||||
(defn lookup-invoice-total-data [request]
|
||||
(let [start (:start-date (:query-params request) (time/plus (time/now) (time/days -30)))
|
||||
end (:end-date (:query-params request) (time/now))
|
||||
query (cond-> {:query '{:find [?cn ?vn (sum ?t)]
|
||||
:with [ ?e]
|
||||
:in [$ [?clients ?start ?end]]
|
||||
:where
|
||||
[[(iol-ion.query/scan-invoices $ ?clients ?start ?end) [[?e _ ?sort-default] ...]]
|
||||
(not [?e :invoice/status :invoice-status/voided])
|
||||
[?e :invoice/client ?c]
|
||||
[?e :invoice/total ?t]
|
||||
[?e :invoice/vendor ?v]
|
||||
[?v :vendor/name ?vn]
|
||||
[?c :client/name ?cn]
|
||||
]}
|
||||
:args
|
||||
[(dc/db conn)
|
||||
[(extract-client-ids (:clients request)
|
||||
(:client-id request)
|
||||
(when (:client-code request)
|
||||
[:client/code (:client-code request)]))
|
||||
(some-> start coerce/to-date)
|
||||
(some-> end coerce/to-date)]]})]
|
||||
|
||||
(dc/query query)))
|
||||
(let [start (:start-date (:query-params request) (time/plus (time/now) (time/days -30)))
|
||||
end (:end-date (:query-params request) (time/now))
|
||||
query (cond-> {:query '{:find [?cn ?vn (sum ?t)]
|
||||
:with [?e]
|
||||
:in [$ [?clients ?start ?end]]
|
||||
:where
|
||||
[[(iol-ion.query/scan-invoices $ ?clients ?start ?end) [[?e _ ?sort-default] ...]]
|
||||
(not [?e :invoice/status :invoice-status/voided])
|
||||
[?e :invoice/client ?c]
|
||||
[?e :invoice/total ?t]
|
||||
[?e :invoice/vendor ?v]
|
||||
[?v :vendor/name ?vn]
|
||||
[?c :client/name ?cn]]}
|
||||
:args
|
||||
[(dc/db conn)
|
||||
[(extract-client-ids (:clients request)
|
||||
(:client-id request)
|
||||
(when (:client-code request)
|
||||
[:client/code (:client-code request)]))
|
||||
(some-> start coerce/to-date)
|
||||
(some-> end coerce/to-date)]]})]
|
||||
|
||||
(defn week-seq
|
||||
(dc/query query)))
|
||||
|
||||
(defn week-seq
|
||||
([c] (week-seq c (atime/last-monday)))
|
||||
([c starting] (reverse (for [n (range c)
|
||||
:let [start (time/minus starting (time/weeks n))
|
||||
end (time/minus starting (time/weeks (dec n)))]]
|
||||
[(atime/as-local-time (coerce/to-date-time start)) (atime/as-local-time (coerce/to-date-time end))]))))
|
||||
|
||||
|
||||
(defn- best-week [d weeks]
|
||||
(reduce
|
||||
(fn [acc [start end]]
|
||||
@@ -97,11 +95,10 @@
|
||||
nil
|
||||
weeks))
|
||||
|
||||
|
||||
(defn expense-breakdown-card* [request]
|
||||
(com/card {:class "w-full h-full" :id "expense-breakdown-report"}
|
||||
[:div {:class "flex flex-col px-8 py-8 space-y-3 w-full h-full"}
|
||||
|
||||
|
||||
[:form {:hx-get (bidi.bidi/path-for ssr-routes/only-routes :company-expense-report-breakdown-card)
|
||||
:hx-trigger "change"
|
||||
:hx-target "#expense-breakdown-report"
|
||||
@@ -157,14 +154,14 @@
|
||||
(for [d weeks]
|
||||
(get-in lookup [ea d] 0)))]
|
||||
[:canvas {:x-data (hx/json {:chart nil
|
||||
:labels x-axis
|
||||
:datasets (map (fn [s a] {:label a
|
||||
:data s
|
||||
:borderWidth 1})
|
||||
series
|
||||
distinct-accounts)})
|
||||
:x-init
|
||||
"new Chart($el, {
|
||||
:labels x-axis
|
||||
:datasets (map (fn [s a] {:label a
|
||||
:data s
|
||||
:borderWidth 1})
|
||||
series
|
||||
distinct-accounts)})
|
||||
:x-init
|
||||
"new Chart($el, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: labels,
|
||||
@@ -186,7 +183,7 @@
|
||||
[:div {:class "flex flex-col px-8 py-8 space-y-3"}
|
||||
[:div
|
||||
[:h1.text-2xl.mb-3.font-bold "Invoice totals by vendor"]
|
||||
[:form {:hx-get (bidi.bidi/path-for ssr-routes/only-routes :company-expense-report-invoice-total-card )
|
||||
[:form {:hx-get (bidi.bidi/path-for ssr-routes/only-routes :company-expense-report-invoice-total-card)
|
||||
:hx-trigger "change"
|
||||
:hx-target "#invoice-totals-report"
|
||||
:hx-swap "outerHTML"}
|
||||
@@ -201,7 +198,7 @@
|
||||
(com/date-input {:name (fc/field-name)
|
||||
:class "w-64"
|
||||
:value (some-> (fc/field-value)
|
||||
(atime/unparse-local atime/normal-date)) })]))
|
||||
(atime/unparse-local atime/normal-date))})]))
|
||||
(fc/with-field :end-date
|
||||
(com/validated-field {:label "End"
|
||||
:errors (fc/field-errors)}
|
||||
@@ -209,13 +206,12 @@
|
||||
(com/date-input {:name (fc/field-name)
|
||||
:class "w-64"
|
||||
:value (some-> (fc/field-value)
|
||||
(atime/unparse-local atime/normal-date)) })]))])]
|
||||
(atime/unparse-local atime/normal-date))})]))])]
|
||||
[:div {:class "overflow-scroll min-w-full max-h-[700px]"}
|
||||
(let [data (lookup-invoice-total-data request)
|
||||
companies (sort (set (map first data)))
|
||||
vendors (sort (set (map second data)))
|
||||
result (by (juxt first second) last data)
|
||||
]
|
||||
result (by (juxt first second) last data)]
|
||||
(com/data-grid
|
||||
{:headers (into
|
||||
[(com/data-grid-header {:class "sticky left-0 z-60 bg-gray-100"} "Vendor")]
|
||||
@@ -231,7 +227,7 @@
|
||||
(com/data-grid-cell
|
||||
{}
|
||||
(or (some->> (get result [company vendor])
|
||||
(format "$%,.2f" ))
|
||||
(format "$%,.2f"))
|
||||
[:span.text-gray-200 "-"])))))))]]]))
|
||||
|
||||
(defn page [request]
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
(com/data-grid-cell {:class class}
|
||||
(when (> (count (:missing-transactions row)) 0)
|
||||
[:div
|
||||
(com/button { :x-tooltip.on.click "{content: ()=>$refs.tooltip.innerHTML, theme: 'light', allowHTML: true}" }
|
||||
(com/button {:x-tooltip.on.click "{content: ()=>$refs.tooltip.innerHTML, theme: 'light', allowHTML: true}"}
|
||||
[:div.flex.gap-2.items-center
|
||||
(count (:missing-transactions row))
|
||||
[:div.w-4.h-4 svg/question]])
|
||||
@@ -67,13 +67,12 @@
|
||||
(com/data-grid-cell {}
|
||||
(format "$%,.2f" (:transaction/amount r))))))]]))))))])
|
||||
|
||||
|
||||
(defn reconciliation-card* [{:keys [request report]}]
|
||||
(com/content-card {:class "w-full" :id "reconciliation-report"}
|
||||
[:div {:class "flex flex-col px-8 py-8 space-y-3"}
|
||||
[:div
|
||||
[:h1.text-2xl.mb-3.font-bold "Bank Reconciliation Report"]
|
||||
|
||||
|
||||
[:form {:hx-get (bidi.bidi/path-for ssr-routes/only-routes :company-reconciliation-report-card)
|
||||
:hx-target "#reconciliation-report"
|
||||
:hx-swap "outerHTML"}
|
||||
@@ -88,7 +87,7 @@
|
||||
(com/date-input {:name (fc/field-name)
|
||||
:class "w-64"
|
||||
:value (some-> (fc/field-value)
|
||||
(atime/unparse-local atime/normal-date)) })]))
|
||||
(atime/unparse-local atime/normal-date))})]))
|
||||
(fc/with-field :end-date
|
||||
(com/validated-field {:label "End"
|
||||
:errors (fc/field-errors)}
|
||||
@@ -96,12 +95,11 @@
|
||||
(com/date-input {:name (fc/field-name)
|
||||
:class "w-64"
|
||||
:value (some-> (fc/field-value)
|
||||
(atime/unparse-local atime/normal-date)) })]))
|
||||
(atime/unparse-local atime/normal-date))})]))
|
||||
(com/button {:color :primary :class "self-center w-24"} "Run")])]
|
||||
(if report
|
||||
(if report
|
||||
(report* {:request request :report report})
|
||||
[:div "Please choose a time range to run the report"])
|
||||
]]))
|
||||
[:div "Please choose a time range to run the report"])]]))
|
||||
|
||||
(defn page [request]
|
||||
(base-page
|
||||
@@ -134,7 +132,7 @@
|
||||
url/map->query))
|
||||
|
||||
(defn get-report-data [start-date end-date client-ids]
|
||||
(let [client-codes (map first (dc/q '[:find ?cc :in $ [?c ...] :where [?c :client/code ?cc]] (dc/db conn ) client-ids))]
|
||||
(let [client-codes (map first (dc/q '[:find ?cc :in $ [?c ...] :where [?c :client/code ?cc]] (dc/db conn) client-ids))]
|
||||
(for [[ib ba c] (seq (apply get-intuit-bank-accounts (dc/db conn) client-codes))
|
||||
:let [raw-transactions (get-transactions (atime/unparse-local start-date atime/iso-date)
|
||||
(atime/unparse-local end-date atime/iso-date)
|
||||
@@ -169,11 +167,11 @@
|
||||
:requires-feedback-count (:transaction-approval-status/requires-feedback found-transactions 0)
|
||||
:missing-transactions missing-transactions})))
|
||||
|
||||
(defn card [{ {:keys [start-date end-date]} :query-params :as request}]
|
||||
(defn card [{{:keys [start-date end-date]} :query-params :as request}]
|
||||
(let [client-ids (extract-client-ids (:clients request)
|
||||
(:client-id request)
|
||||
(when (:client-code request)
|
||||
[:client/code (:client-code request)]))
|
||||
(:client-id request)
|
||||
(when (:client-code request)
|
||||
[:client/code (:client-code request)]))
|
||||
report (get-report-data start-date end-date client-ids)]
|
||||
(html-response
|
||||
(reconciliation-card* {:request request
|
||||
@@ -182,7 +180,7 @@
|
||||
|
||||
(def key->handler
|
||||
(apply-middleware-to-all-handlers
|
||||
{:company-reconciliation-report page
|
||||
{:company-reconciliation-report page
|
||||
:company-reconciliation-report-card card}
|
||||
(fn [h]
|
||||
(-> h
|
||||
@@ -191,4 +189,4 @@
|
||||
[:start-date {:optional true}
|
||||
[:maybe clj-date-schema]]
|
||||
[:end-date {:optional true}
|
||||
[:maybe clj-date-schema]] ])))))
|
||||
[:maybe clj-date-schema]]])))))
|
||||
@@ -33,36 +33,34 @@
|
||||
:yodlee-provider-account/client [:client/code]}])
|
||||
|
||||
(def query-schema (mc/schema
|
||||
[:maybe
|
||||
(into [:map {}
|
||||
[:client-id {:optional true} [:maybe entity-id]] ]
|
||||
default-grid-fields-schema)]))
|
||||
[:maybe
|
||||
(into [:map {}
|
||||
[:client-id {:optional true} [:maybe entity-id]]]
|
||||
default-grid-fields-schema)]))
|
||||
|
||||
(defn fetch-ids [db request]
|
||||
(let [query-params (:query-params request)
|
||||
query (cond-> {:query {:find []
|
||||
:in ['$ '[?xx ...]]
|
||||
:where ['[?e :yodlee-provider-account/id]
|
||||
'[?e :yodlee-provider-account/client ?xx]]}
|
||||
:args [db (:trimmed-clients request)]}
|
||||
:in ['$ '[?xx ...]]
|
||||
:where ['[?e :yodlee-provider-account/id]
|
||||
'[?e :yodlee-provider-account/client ?xx]]}
|
||||
:args [db (:trimmed-clients request)]}
|
||||
|
||||
|
||||
(:sort query-params) (add-sorter-fields {"status" ['[?e :yodlee-provider-account/status ?sort-status]]
|
||||
"client" ['[?e :yodlee-provider-account/client ?c]
|
||||
'[?c :client/code ?sort-client]]
|
||||
"provider-account" ['[?e :yodlee-provider-account/id ?sort-provider-account]]
|
||||
"last-updated" ['[?e :yodlee-provider-account/last-updated ?sort-last-updated]]}
|
||||
query-params)
|
||||
true
|
||||
(merge-query {:query {:find ['?e ]
|
||||
:where ['[?e :yodlee-provider-account/id]]}}))]
|
||||
(:sort query-params) (add-sorter-fields {"status" ['[?e :yodlee-provider-account/status ?sort-status]]
|
||||
"client" ['[?e :yodlee-provider-account/client ?c]
|
||||
'[?c :client/code ?sort-client]]
|
||||
"provider-account" ['[?e :yodlee-provider-account/id ?sort-provider-account]]
|
||||
"last-updated" ['[?e :yodlee-provider-account/last-updated ?sort-last-updated]]}
|
||||
query-params)
|
||||
true
|
||||
(merge-query {:query {:find ['?e]
|
||||
:where ['[?e :yodlee-provider-account/id]]}}))]
|
||||
(->> query
|
||||
|
||||
(query2)
|
||||
(apply-sort-3 query-params)
|
||||
(apply-pagination query-params))))
|
||||
|
||||
|
||||
(defn hydrate-results [ids db _]
|
||||
(let [results (->> (pull-many db default-read ids)
|
||||
(group-by :db/id))]
|
||||
@@ -70,26 +68,24 @@
|
||||
(map results)
|
||||
(map first))))
|
||||
|
||||
|
||||
(defn fetch-page [request]
|
||||
(let [db (dc/db conn)
|
||||
{ids-to-retrieve :ids matching-count :count} (fetch-ids db request)]
|
||||
[(->> (hydrate-results ids-to-retrieve db request))
|
||||
matching-count]))
|
||||
|
||||
|
||||
(defn fastlink-dialog [{:keys [client]}]
|
||||
(modal-response
|
||||
(com/modal
|
||||
{}
|
||||
(com/modal-card
|
||||
{}
|
||||
[:div.flex [:div.p-2 "Yodlee Fastlink"] ]
|
||||
[:div
|
||||
[:div#fa-spot]
|
||||
[:script {:lang "text/javascript"}
|
||||
(hiccup/raw
|
||||
(format "
|
||||
(com/modal
|
||||
{}
|
||||
(com/modal-card
|
||||
{}
|
||||
[:div.flex [:div.p-2 "Yodlee Fastlink"]]
|
||||
[:div
|
||||
[:div#fa-spot]
|
||||
[:script {:lang "text/javascript"}
|
||||
(hiccup/raw
|
||||
(format "
|
||||
fastlink.open({fastLinkURL: '%s',
|
||||
accessToken: '%s',
|
||||
params: {'configName': 'Aggregation'},
|
||||
@@ -100,25 +96,24 @@ fastlink.open({fastLinkURL: '%s',
|
||||
}},
|
||||
'fa-spot');
|
||||
|
||||
" (:yodlee2-fastlink env) (yodlee/get-access-token (:client/code client))))]
|
||||
]
|
||||
[:div]))))
|
||||
" (:yodlee2-fastlink env) (yodlee/get-access-token (:client/code client))))]]
|
||||
[:div]))))
|
||||
|
||||
(defn reauthenticate [{:keys [form-params identity]}]
|
||||
(assert-can-see-client identity (-> (dc/pull (dc/db conn) '[{:yodlee-provider-account/client [:db/id]}] (Long/parseLong (get form-params "id")))
|
||||
:yodlee-provider-account/client
|
||||
:db/id))
|
||||
(html-response
|
||||
(com/modal
|
||||
{}
|
||||
(com/modal-card
|
||||
{}
|
||||
[:div.flex [:div.p-2 "Yodlee Fastlink"] ]
|
||||
[:div
|
||||
[:div#fa-spot]
|
||||
[:script {:lang "text/javascript"}
|
||||
(hiccup/raw
|
||||
(format "
|
||||
(com/modal
|
||||
{}
|
||||
(com/modal-card
|
||||
{}
|
||||
[:div.flex [:div.p-2 "Yodlee Fastlink"]]
|
||||
[:div
|
||||
[:div#fa-spot]
|
||||
[:script {:lang "text/javascript"}
|
||||
(hiccup/raw
|
||||
(format "
|
||||
fastlink.open({fastLinkURL: '%s',
|
||||
accessToken: '%s',
|
||||
params: {'configName': 'Aggregation',
|
||||
@@ -127,94 +122,93 @@ fastlink.open({fastLinkURL: '%s',
|
||||
'fa-spot');
|
||||
|
||||
"
|
||||
(:yodlee2-fastlink env)
|
||||
(yodlee/get-access-token (-> (dc/pull (dc/db conn)
|
||||
[{:yodlee-provider-account/client [:client/code]}]
|
||||
(Long/parseLong (get form-params "id")))
|
||||
:yodlee-provider-account/client
|
||||
:client/code))
|
||||
(pull-attr (dc/db conn) :yodlee-provider-account/id (Long/parseLong (get form-params "id")))))]]
|
||||
[:div]))))
|
||||
(:yodlee2-fastlink env)
|
||||
(yodlee/get-access-token (-> (dc/pull (dc/db conn)
|
||||
[{:yodlee-provider-account/client [:client/code]}]
|
||||
(Long/parseLong (get form-params "id")))
|
||||
:yodlee-provider-account/client
|
||||
:client/code))
|
||||
(pull-attr (dc/db conn) :yodlee-provider-account/id (Long/parseLong (get form-params "id")))))]]
|
||||
[:div]))))
|
||||
|
||||
(def grid-page
|
||||
(helper/build
|
||||
{:id "yodlee-table"
|
||||
:nav com/company-aside-nav
|
||||
:id-fn :db/id
|
||||
:fetch-page fetch-page
|
||||
:breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes
|
||||
:company)}
|
||||
"My Company"]
|
||||
[:a {:href (bidi/path-for ssr-routes/only-routes
|
||||
:company-yodlee)}
|
||||
"Yodlee"]]
|
||||
:title "Yodlee Accounts"
|
||||
:entity-name "Yodlee accounts"
|
||||
:query-schema query-schema
|
||||
:route :company-yodlee-table
|
||||
:action-buttons (fn [request]
|
||||
[[:div.flex.flex-col.flex-shrink
|
||||
[:div.flex-shrink
|
||||
(com/button {:color :primary
|
||||
:on-click "openFastlink()"
|
||||
:disabled (if (:client request)
|
||||
false
|
||||
true)
|
||||
:hx-get (bidi/path-for ssr-routes/only-routes
|
||||
:company-yodlee-fastlink-dialog)
|
||||
:hx-target "#modal-holder"}
|
||||
(com/button-icon {} svg/refresh)
|
||||
"Link new account")]
|
||||
(when-not (:client request)
|
||||
[:div.text-xs "Note: please select a specific customer to link a new account."])]])
|
||||
:row-buttons (fn [request _]
|
||||
[
|
||||
(com/button {:hx-put (bidi/path-for ssr-routes/only-routes
|
||||
:company-yodlee-provider-account-reauthenticate)
|
||||
:color :primary
|
||||
:hx-target "#modal-holder"}
|
||||
"Reauthenticate")
|
||||
(when (is-admin? (:identity request))
|
||||
(com/icon-button {:hx-put (bidi/path-for ssr-routes/only-routes
|
||||
:company-yodlee-provider-account-refresh)
|
||||
:hx-target "closest tr"}
|
||||
svg/refresh))])
|
||||
:headers [{:key "client"
|
||||
:name "Client"
|
||||
:sort-key "client"
|
||||
:hide? (fn [args]
|
||||
(= (count (:clients args)) 1))
|
||||
:render #(-> % :yodlee-provider-account/client :client/code)}
|
||||
{:key "provider-account"
|
||||
:name "Provider Account"
|
||||
:sort-key "provider-account"
|
||||
:render :yodlee-provider-account/id}
|
||||
{:key "status"
|
||||
:name "Status"
|
||||
:sort-key "status"
|
||||
:render #(when-let [status (:yodlee-provider-account/status %)]
|
||||
(com/pill {:color (if (not= status "SUCCESS")
|
||||
:yellow
|
||||
:primary) }
|
||||
status))}
|
||||
{:key "detailed-status"
|
||||
:name "Detailed Status"
|
||||
:sort-key "detailed-status"
|
||||
:render #(when-let [status (:yodlee-provider-account/detailed-status %)]
|
||||
status)}
|
||||
(helper/build
|
||||
{:id "yodlee-table"
|
||||
:nav com/company-aside-nav
|
||||
:id-fn :db/id
|
||||
:fetch-page fetch-page
|
||||
:breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes
|
||||
:company)}
|
||||
"My Company"]
|
||||
[:a {:href (bidi/path-for ssr-routes/only-routes
|
||||
:company-yodlee)}
|
||||
"Yodlee"]]
|
||||
:title "Yodlee Accounts"
|
||||
:entity-name "Yodlee accounts"
|
||||
:query-schema query-schema
|
||||
:route :company-yodlee-table
|
||||
:action-buttons (fn [request]
|
||||
[[:div.flex.flex-col.flex-shrink
|
||||
[:div.flex-shrink
|
||||
(com/button {:color :primary
|
||||
:on-click "openFastlink()"
|
||||
:disabled (if (:client request)
|
||||
false
|
||||
true)
|
||||
:hx-get (bidi/path-for ssr-routes/only-routes
|
||||
:company-yodlee-fastlink-dialog)
|
||||
:hx-target "#modal-holder"}
|
||||
(com/button-icon {} svg/refresh)
|
||||
"Link new account")]
|
||||
(when-not (:client request)
|
||||
[:div.text-xs "Note: please select a specific customer to link a new account."])]])
|
||||
:row-buttons (fn [request _]
|
||||
[(com/button {:hx-put (bidi/path-for ssr-routes/only-routes
|
||||
:company-yodlee-provider-account-reauthenticate)
|
||||
:color :primary
|
||||
:hx-target "#modal-holder"}
|
||||
"Reauthenticate")
|
||||
(when (is-admin? (:identity request))
|
||||
(com/icon-button {:hx-put (bidi/path-for ssr-routes/only-routes
|
||||
:company-yodlee-provider-account-refresh)
|
||||
:hx-target "closest tr"}
|
||||
svg/refresh))])
|
||||
:headers [{:key "client"
|
||||
:name "Client"
|
||||
:sort-key "client"
|
||||
:hide? (fn [args]
|
||||
(= (count (:clients args)) 1))
|
||||
:render #(-> % :yodlee-provider-account/client :client/code)}
|
||||
{:key "provider-account"
|
||||
:name "Provider Account"
|
||||
:sort-key "provider-account"
|
||||
:render :yodlee-provider-account/id}
|
||||
{:key "status"
|
||||
:name "Status"
|
||||
:sort-key "status"
|
||||
:render #(when-let [status (:yodlee-provider-account/status %)]
|
||||
(com/pill {:color (if (not= status "SUCCESS")
|
||||
:yellow
|
||||
:primary)}
|
||||
status))}
|
||||
{:key "detailed-status"
|
||||
:name "Detailed Status"
|
||||
:sort-key "detailed-status"
|
||||
:render #(when-let [status (:yodlee-provider-account/detailed-status %)]
|
||||
status)}
|
||||
|
||||
{:key "last-updated"
|
||||
:name "Last Updated"
|
||||
:sort-key "last-updated"
|
||||
:render #(atime/unparse-local (:yodlee-provider-account/last-updated %)
|
||||
atime/normal-date)}
|
||||
{:key "accounts"
|
||||
:name "Accounts"
|
||||
:show-starting "md"
|
||||
:render (fn [e]
|
||||
[:ul
|
||||
(for [a (:yodlee-provider-account/accounts e)]
|
||||
[:li (:yodlee-account/name a) " - " (:yodlee-account/number a)])])}]}))
|
||||
{:key "last-updated"
|
||||
:name "Last Updated"
|
||||
:sort-key "last-updated"
|
||||
:render #(atime/unparse-local (:yodlee-provider-account/last-updated %)
|
||||
atime/normal-date)}
|
||||
{:key "accounts"
|
||||
:name "Accounts"
|
||||
:show-starting "md"
|
||||
:render (fn [e]
|
||||
[:ul
|
||||
(for [a (:yodlee-provider-account/accounts e)]
|
||||
[:li (:yodlee-account/name a) " - " (:yodlee-account/number a)])])}]}))
|
||||
|
||||
(def page (helper/page-route grid-page))
|
||||
(def table (helper/table-route grid-page))
|
||||
@@ -224,26 +218,23 @@ fastlink.open({fastLinkURL: '%s',
|
||||
(yodlee/refresh-provider-account (:client/code (:yodlee-provider-account/client provider-account))
|
||||
(:yodlee-provider-account/id provider-account))
|
||||
(html-response
|
||||
(helper/row*
|
||||
grid-page
|
||||
identity
|
||||
provider-account
|
||||
{:flash? true}))))
|
||||
(helper/row*
|
||||
grid-page
|
||||
identity
|
||||
provider-account
|
||||
{:flash? true}))))
|
||||
|
||||
|
||||
(def key->handler
|
||||
(apply-middleware-to-all-handlers
|
||||
{
|
||||
:company-yodlee page
|
||||
(def key->handler
|
||||
(apply-middleware-to-all-handlers
|
||||
{:company-yodlee page
|
||||
:company-yodlee-table table
|
||||
:company-yodlee-fastlink-dialog fastlink-dialog
|
||||
}
|
||||
:company-yodlee-fastlink-dialog fastlink-dialog}
|
||||
(fn [h]
|
||||
(-> h
|
||||
(wrap-copy-qp-pqp)
|
||||
(wrap-apply-sort grid-page)
|
||||
(wrap-merge-prior-hx)
|
||||
(wrap-schema-enforce :query-schema query-schema)
|
||||
(wrap-schema-enforce :hx-schema query-schema)
|
||||
(wrap-client-redirect-unauthenticated)
|
||||
(wrap-secure)))))
|
||||
(-> h
|
||||
(wrap-copy-qp-pqp)
|
||||
(wrap-apply-sort grid-page)
|
||||
(wrap-merge-prior-hx)
|
||||
(wrap-schema-enforce :query-schema query-schema)
|
||||
(wrap-schema-enforce :hx-schema query-schema)
|
||||
(wrap-client-redirect-unauthenticated)
|
||||
(wrap-secure)))))
|
||||
Reference in New Issue
Block a user