Makes testing of transaction rules work
This commit is contained in:
@@ -41,6 +41,7 @@
|
||||
wrap-entity
|
||||
wrap-form-4xx-2
|
||||
wrap-schema-decode]]
|
||||
[auto-ap.time :as atime]
|
||||
[auto-ap.utils :refer [dollars=]]
|
||||
[bidi.bidi :as bidi]
|
||||
[clojure.string :as str]
|
||||
@@ -258,33 +259,31 @@
|
||||
(set))
|
||||
bank-account-id))
|
||||
|
||||
(defn validate-transaction-rule [form-params]
|
||||
(doseq [[{:transaction-rule-account/keys [account location]} i] (map vector (:transaction-rule/accounts form-params) (range))
|
||||
:let [account-location (pull-attr (dc/db conn) :account/location account)]
|
||||
:when (and account-location (not= account-location location))]
|
||||
(field-validation-error (str "must be " account-location)
|
||||
[:transaction-rule/accounts i :transaction-rule-account/location]
|
||||
:form form-params))
|
||||
|
||||
(let [total (reduce + 0.0 (map :transaction-rule-account/percentage (:transaction-rule/accounts form-params)))]
|
||||
(when-not (dollars= 1.0 total)
|
||||
(form-validation-error (format "Expense accounts total (%d%%) must add to 100%%" (int (* 100.0 total)))
|
||||
:form form-params)))
|
||||
|
||||
(when (and (:transaction-rule/bank-account form-params)
|
||||
(not (bank-account-belongs-to-client? (:transaction-rule/bank-account form-params)
|
||||
(:transaction-rule/client form-params))))
|
||||
(field-validation-error "does not belong to client"
|
||||
[:transaction-rule/bank-account]
|
||||
:form form-params)))
|
||||
|
||||
(defn transaction-rule-save [{:keys [form-params request-method identity] :as request}]
|
||||
(validate-transaction-rule form-params)
|
||||
(let [entity (cond-> form-params
|
||||
(= :post request-method) (assoc :db/id "new")
|
||||
true (assoc :transaction-rule/note (entity->note form-params)))
|
||||
_ (doseq [[{:transaction-rule-account/keys [account location]} i] (map vector (:transaction-rule/accounts entity) (range))
|
||||
:let [account-location (pull-attr (dc/db conn) :account/location account)]
|
||||
:when (and account-location (not= account-location location))]
|
||||
(field-validation-error (str "must be " account-location)
|
||||
[:transaction-rule/accounts i :transaction-rule-account/location]
|
||||
:form form-params))
|
||||
|
||||
total (reduce +
|
||||
0.0
|
||||
(map :transaction-rule-account/percentage
|
||||
(:transaction-rule/accounts entity)))
|
||||
_ (when-not (dollars= 1.0 total)
|
||||
(form-validation-error (format "Expense accounts total (%d%%) must add to 100%%" (int (* 100.0 total)))
|
||||
:form form-params))
|
||||
|
||||
_ (when (and (:transaction-rule/bank-account entity)
|
||||
(not (bank-account-belongs-to-client? (:transaction-rule/bank-account entity)
|
||||
(:transaction-rule/client entity))))
|
||||
(field-validation-error "does not belong to client"
|
||||
[:transaction-rule/bank-account]
|
||||
:form form-params))
|
||||
|
||||
|
||||
{:keys [tempids]} (audit-transact [[:upsert-entity entity]]
|
||||
(:identity request))
|
||||
updated-rule (dc/pull (dc/db conn)
|
||||
@@ -295,7 +294,112 @@
|
||||
:headers (cond-> {"hx-trigger" "modalclose"}
|
||||
(= :post request-method) (assoc "hx-retarget" "#entity-table tbody"
|
||||
"hx-reswap" "afterbegin")
|
||||
(= :put request-method) (assoc "hx-retarget" (format "#entity-table tr[data-id=\"%d\"]" (:db/id updated-rule)))))))
|
||||
(= :put request-method) (assoc "hx-retarget" (format "#entity-table tr[data-id=\"%d\"]" (:db/id updated-rule))
|
||||
"hx-reswap" "outerHTML")))))
|
||||
|
||||
|
||||
|
||||
(def transaction-read '[{:transaction/client [:client/name]
|
||||
:transaction/bank-account [:bank-account/name]}
|
||||
:transaction/description-original
|
||||
[:transaction/date :xform clj-time.coerce/from-date]])
|
||||
|
||||
(defn transaction-rule-test [{:keys [form-params request-method identity] :as request
|
||||
{:transaction-rule/keys [description client bank-account amount-lte amount-gte dom-lte dom-gte yodlee-merchant]} :form-params}]
|
||||
(validate-transaction-rule form-params)
|
||||
(let [valid-clients (extract-client-ids (:clients request)
|
||||
client)
|
||||
query (cond-> {:query {:find ['(pull ?e read)]
|
||||
:in ['$ 'read]
|
||||
:where []}
|
||||
:args [(dc/db conn) transaction-read]}
|
||||
description
|
||||
(merge-query {:query {:in ['?descr]
|
||||
:where ['[(iol-ion.query/->pattern ?descr) ?description-regex]]}
|
||||
:args [description]})
|
||||
|
||||
valid-clients
|
||||
(merge-query {:query {:in ['[?xx ...]]
|
||||
:where ['[?e :transaction/client ?xx]]}
|
||||
:args [(set valid-clients)]})
|
||||
|
||||
bank-account
|
||||
(merge-query {:query {:in ['?bank-account-id]
|
||||
:where ['[?e :transaction/bank-account ?bank-account-id]]}
|
||||
:args [bank-account]})
|
||||
|
||||
description
|
||||
(merge-query {:query {:where ['[?e :transaction/description-original ?do]
|
||||
'[(re-find ?description-regex ?do)]]}})
|
||||
|
||||
amount-gte
|
||||
(merge-query {:query {:in ['?amount-gte]
|
||||
:where ['[?e :transaction/amount ?ta]
|
||||
'[(>= ?ta ?amount-gte)]]}
|
||||
:args [amount-gte]})
|
||||
|
||||
amount-lte
|
||||
(merge-query {:query {:in ['?amount-lte]
|
||||
:where ['[?e :transaction/amount ?ta]
|
||||
'[(<= ?ta ?amount-lte)]]}
|
||||
:args [amount-lte]})
|
||||
|
||||
dom-lte
|
||||
(merge-query {:query {:in ['?dom-lte]
|
||||
:where ['[?e :transaction/date ?transaction-date]
|
||||
'[(iol-ion.query/dom ?transaction-date) ?dom]
|
||||
'[(<= ?dom ?dom-lte)]]}
|
||||
:args [dom-lte]})
|
||||
|
||||
dom-gte
|
||||
(merge-query {:query {:in ['?dom-gte]
|
||||
:where ['[?e :transaction/date ?transaction-date]
|
||||
'[(iol-ion.query/dom ?transaction-date) ?dom]
|
||||
'[(>= ?dom ?dom-gte)]]}
|
||||
:args [dom-gte]})
|
||||
|
||||
client
|
||||
(merge-query {:query {:in ['?client-id]
|
||||
:where ['[?e :transaction/client ?client-id]]}
|
||||
:args [client]})
|
||||
|
||||
|
||||
true
|
||||
(merge-query {:query {:where ['[?e :transaction/id]]}}))
|
||||
results (->>
|
||||
(query2 query)
|
||||
(map first))]
|
||||
|
||||
(html-response
|
||||
(com/modal-card {:class "fade-in transition duration-300"}
|
||||
[:div.p-2.flex.space-x-4 [:div "Transaction Rule"] [:div ">"] [:div "Results"] [:div.ml-4.relative (com/badge {} (count results))]]
|
||||
(com/data-grid
|
||||
{:headers [(com/data-grid-header {} "Client")
|
||||
(com/data-grid-header {} "Bank")
|
||||
(com/data-grid-header {} "Date")
|
||||
(com/data-grid-header {} "Description")]}
|
||||
(for [r (take 15 results)]
|
||||
(com/data-grid-row
|
||||
{}
|
||||
(com/data-grid-cell {} (-> r :transaction/client :client/name))
|
||||
(com/data-grid-cell {} (-> r :transaction/bank-account :bank-account/name))
|
||||
(com/data-grid-cell {} (some-> r :transaction/date (atime/unparse-local atime/normal-date)))
|
||||
(com/data-grid-cell {} (some-> r :transaction/description-original )))))
|
||||
[:div
|
||||
(com/button (cond-> {:color :primary
|
||||
:hx-vals (hx/json (:raw-form-params request))
|
||||
}
|
||||
(:db/id form-params) (assoc :hx-put (bidi/path-for ssr-routes/only-routes :admin-transaction-rule-save))
|
||||
(not (:db/id form-params)) (assoc :hx-post (bidi/path-for ssr-routes/only-routes :admin-transaction-rule-save)))
|
||||
"Save")
|
||||
(com/button {:hx-vals (hx/json (:raw-form-params request))
|
||||
:hx-put (bidi/path-for ssr-routes/only-routes
|
||||
:admin-transaction-rule-filled-account
|
||||
)} "Back")
|
||||
])
|
||||
:headers (-> {}
|
||||
(assoc "hx-retarget" ".modal-card")
|
||||
(assoc "hx-reswap" "outerHTML")))))
|
||||
|
||||
|
||||
|
||||
@@ -366,6 +470,7 @@
|
||||
:x-data (hx/json {:location (fc/field-value)})}
|
||||
[:div {:hx-trigger "changed"
|
||||
:hx-target "next *"
|
||||
:hx-swap "outerHTML"
|
||||
:hx-vals (format "js:{name: '%s', 'client-id': event.detail.clientId || '', 'account-id': event.detail.accountId || '', value: event.detail.location}" (fc/field-name) )
|
||||
:hx-get (bidi/path-for ssr-routes/only-routes :admin-transaction-rule-location-select)
|
||||
:x-init "$watch('clientId', cid => $dispatch('changed', $data)); $watch('accountId', cid => $dispatch('changed', $data) )"}]
|
||||
@@ -554,7 +659,11 @@
|
||||
]]
|
||||
[:div
|
||||
(com/form-errors {:errors (:errors fc/*form-errors*)})
|
||||
(com/validated-save-button {:errors form-errors} "Save rule")])])))
|
||||
(com/validated-save-button {:errors form-errors} "Save rule")
|
||||
(com/validated-save-button {:errors form-errors :color :secondary
|
||||
:hx-post (bidi/path-for ssr-routes/only-routes :admin-transaction-rule-test)}
|
||||
|
||||
"Test rule")])])))
|
||||
|
||||
|
||||
(defn new-account [{{:keys [client-id index]} :query-params}]
|
||||
@@ -607,6 +716,7 @@
|
||||
[:transaction-rule-account/percentage percentage])]]))
|
||||
|
||||
(defn transaction-dialog [{:keys [entity form-params form-errors]}]
|
||||
(clojure.pprint/pprint form-params)
|
||||
(modal-response (dialog* {:entity entity
|
||||
:form-params (or (when (seq form-params)
|
||||
form-params)
|
||||
@@ -649,6 +759,19 @@
|
||||
(wrap-nested-form-params)
|
||||
(wrap-form-4xx-2 (-> transaction-dialog
|
||||
(wrap-entity [:form-params :db/id] default-read))))
|
||||
|
||||
:admin-transaction-rule-test (-> transaction-rule-test
|
||||
(wrap-entity [:form-params :db/id] default-read)
|
||||
(wrap-schema-decode :form-schema form-schema)
|
||||
(wrap-nested-form-params)
|
||||
(wrap-form-4xx-2 (-> transaction-dialog
|
||||
(wrap-entity [:form-params :db/id] default-read))))
|
||||
:admin-transaction-rule-filled-account (-> transaction-dialog
|
||||
(wrap-entity [:form-params :db/id] default-read)
|
||||
(wrap-schema-decode :form-schema form-schema)
|
||||
(wrap-nested-form-params)
|
||||
(wrap-form-4xx-2 (-> transaction-dialog
|
||||
(wrap-entity [:form-params :db/id] default-read))))
|
||||
:admin-transaction-rule-edit-dialog (-> transaction-dialog
|
||||
(wrap-entity [:route-params :db/id] default-read)
|
||||
(wrap-schema-decode :route-schema [:map [:db/id entity-id]]))
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
(def date-input inputs/date-input-)
|
||||
(def hidden inputs/hidden-)
|
||||
(def select inputs/select-)
|
||||
(def typeahead inputs/tytypeahead-
|
||||
(def typeahead inputs/typeahead-)
|
||||
(def field-errors inputs/field-errors-)
|
||||
(def field inputs/field-)
|
||||
(def validated-field inputs/validated-field-)
|
||||
|
||||
@@ -214,8 +214,7 @@
|
||||
|
||||
[:li
|
||||
(menu-button- {:icon svg/cog
|
||||
:href (bidi/path-for client-routes/routes
|
||||
:admin-rules)}
|
||||
:href (bidi/path-for ssr-routes/only-routes :admin-transaction-rules)}
|
||||
"Rules")]
|
||||
|
||||
[:li
|
||||
|
||||
@@ -176,7 +176,7 @@
|
||||
|
||||
|
||||
(defn validated-save-button- [{:keys [errors class] :as params} & children]
|
||||
(button- (-> {:color :primary
|
||||
(button- (-> {:color (or (:color params) :primary)
|
||||
:type "submit" :class (cond-> (or class "")
|
||||
true (hh/add-class "w-32")
|
||||
(seq errors) (hh/add-class "animate-shake"))}
|
||||
@@ -184,5 +184,4 @@
|
||||
(dissoc :errors))
|
||||
(if (seq children)
|
||||
children
|
||||
"Save"))
|
||||
)
|
||||
"Save")))
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
children])
|
||||
|
||||
(defn modal-card- [params header content footer]
|
||||
[:div#modal-card (update params
|
||||
[:div (update params
|
||||
:class (fn [c] (-> c
|
||||
(or "")
|
||||
(hh/add-class "w-full p-4 h-full")
|
||||
(hh/add-class "w-full p-4 h-full modal-card")
|
||||
)))
|
||||
[:div {:class "bg-white rounded-lg shadow dark:bg-gray-700 dark:text-white modal-content w-full flex flex-col h-full"}
|
||||
[:div {:class "flex items-start justify-between p-4 border-b rounded-t dark:border-gray-600 shrink-0"} header]
|
||||
|
||||
@@ -64,7 +64,9 @@
|
||||
(nested-params-request request {}))
|
||||
([request options]
|
||||
(let [parse (:key-parser options parse-nested-keys)]
|
||||
(update-in request [:form-params] nest-params parse))))
|
||||
(-> request
|
||||
(assoc :raw-form-params (:form-params request))
|
||||
(update-in [:form-params] nest-params parse)))))
|
||||
|
||||
(defn wrap-nested-form-params
|
||||
"Middleware to converts a flat map of parameters into a nested map.
|
||||
|
||||
@@ -3,9 +3,6 @@
|
||||
[auto-ap.time :as atime]
|
||||
[auto-ap.ssr.svg :as svg]))
|
||||
|
||||
;; TODO make date-input take clj date
|
||||
;; TODO make total fields take decimals
|
||||
|
||||
(defn date-range-field* [request]
|
||||
[:div#date-range {}
|
||||
(com/field {:label "Date Range"}
|
||||
|
||||
@@ -33,7 +33,9 @@
|
||||
:crossorigin= "anonymous"}]
|
||||
[:script {:src "https://unpkg.com/htmx.org@1.9.6/dist/htmx.min.js"
|
||||
:crossorigin= "anonymous"}])
|
||||
|
||||
|
||||
[:script {:src "https://unpkg.com/htmx.org@1.9.6/dist/ext/class-tools.js" :crossorigin= "anonymous"}]
|
||||
|
||||
[:script {:src "https://unpkg.com/htmx.org/dist/ext/debug.js"}]
|
||||
[:script {:src "/js/htmx-disable.js"}]
|
||||
[:script {:type "text/javascript", :src "https://cdn.yodlee.com/fastlink/v4/initialize.js", :async "async"}]]
|
||||
@@ -62,7 +64,7 @@ input[type=number] {
|
||||
} "]
|
||||
|
||||
|
||||
[:body {:hx-ext "disable-submit"}
|
||||
[:body {:hx-ext "disable-submit, class-tools"}
|
||||
contents
|
||||
[:script {:src "/js/flowbite.min.js"}]
|
||||
|
||||
|
||||
@@ -37,6 +37,15 @@
|
||||
(assoc-in [:headers "hx-retarget"] "#modal-content")
|
||||
(assoc-in [:headers "hx-reswap"] "innerHTML"))))))
|
||||
|
||||
(defn next-step-modal-response [hiccup & {:as opts}]
|
||||
(apply html-response
|
||||
(into
|
||||
[hiccup]
|
||||
(mapcat identity
|
||||
(-> opts
|
||||
(assoc-in [:headers "hx-retarget"] "#modal-content")
|
||||
(assoc-in [:headers "hx-reswap"] "innerHTML"))))))
|
||||
|
||||
(defn wrap-error-response [handler]
|
||||
(fn [request]
|
||||
(try
|
||||
|
||||
@@ -37,9 +37,11 @@
|
||||
:put :admin-transaction-rule-save
|
||||
:post :admin-transaction-rule-save}
|
||||
"/table" :admin-transaction-rule-table
|
||||
"/account/filled" :admin-transaction-rule-filled-account
|
||||
"/account/new" :admin-transaction-rule-new-account
|
||||
"/account/location-select" :admin-transaction-rule-location-select
|
||||
"/account/typeahead" :admin-transaction-rule-account-typeahead
|
||||
"/test" :admin-transaction-rule-test
|
||||
"/new" {:get :admin-transaction-rule-new-dialog}
|
||||
[[#"\d+" :db/id] "/edit"] :admin-transaction-rule-edit-dialog
|
||||
}}
|
||||
|
||||
Reference in New Issue
Block a user