From ede4d756af6be3e89122a74b0a111c81249908fe Mon Sep 17 00:00:00 2001 From: Bryce Date: Sun, 24 Mar 2024 14:26:30 -0700 Subject: [PATCH] automatically set scheduled payment --- src/clj/auto_ap/ssr/components/inputs.clj | 4 +- .../ssr/invoice/new_invoice_wizard.clj | 166 ++++++++++++++---- src/clj/auto_ap/ssr/utils.clj | 54 +++--- src/cljc/auto_ap/routes/invoice.cljc | 1 + 4 files changed, 170 insertions(+), 55 deletions(-) diff --git a/src/clj/auto_ap/ssr/components/inputs.clj b/src/clj/auto_ap/ssr/components/inputs.clj index b8e34c0d..d285e527 100644 --- a/src/clj/auto_ap/ssr/components/inputs.clj +++ b/src/clj/auto_ap/ssr/components/inputs.clj @@ -156,12 +156,14 @@ (dissoc :size))]) (defn date-input- [{:keys [size] :as params}] - [:div.shrink + [:div.shrink {:x-data (hx/json {:value (:value params)})} [:input (-> params (update :class (fnil hh/add-class "") default-input-classes) + (assoc :x-modelable "value") (assoc :type "text") (assoc "_" (hiccup/raw "init initDatepicker(me)")) + (assoc "@change" "value = $event.target.value; console.log(value)") (assoc "hx-on" (hiccup/raw "changeDate: htmx.trigger(this, \"change\") htmx:beforeCleanupElement: this.dp.destroy()")) (update :class #(str % (use-size size) " w-full")) diff --git a/src/clj/auto_ap/ssr/invoice/new_invoice_wizard.clj b/src/clj/auto_ap/ssr/invoice/new_invoice_wizard.clj index 246b839b..445c5544 100644 --- a/src/clj/auto_ap/ssr/invoice/new_invoice_wizard.clj +++ b/src/clj/auto_ap/ssr/invoice/new_invoice_wizard.clj @@ -15,9 +15,8 @@ [auto-ap.ssr.nested-form-params :refer [wrap-nested-form-params]] [auto-ap.ssr.svg :as svg] [auto-ap.ssr.utils - :refer [apply-middleware-to-all-handlers clj-date-schema - entity-id html-response money wrap-schema-decode - wrap-schema-enforce]] + :refer [->db-id apply-middleware-to-all-handlers clj-date-schema + entity-id html-response money wrap-schema-enforce]] [auto-ap.time :as atime] [bidi.bidi :as bidi] [clj-time.core :as time] @@ -30,6 +29,7 @@ [:invoice/client entity-id] [:invoice/date clj-date-schema] [:invoice/due {:optional true} [:maybe clj-date-schema]] + [:invoice/scheduled-payment {:optional true} [:maybe clj-date-schema]] [:invoice/vendor entity-id] [:invoice/expense-accounts [:vector {:coerce? true} @@ -49,7 +49,7 @@ []) (step-schema [_] - (mut/select-keys (mm/form-schema linear-wizard) #{:invoice/client :invoice/vendor :invoice/date :invoice/due})) + (mut/select-keys (mm/form-schema linear-wizard) #{:invoice/client :invoice/vendor :invoice/date :invoice/due :invoice/scheduled-payment})) (render-step [this request] (alog/peek ::check (:multi-form-state request)) @@ -62,7 +62,9 @@ (fc/field-value (:invoice/client fc/*current*))) :vendorId (fc/field-value (:invoice/vendor fc/*current*)) :date (-> (fc/field-value (:invoice/date fc/*current*)) - (atime/unparse-local atime/normal-date))})} + (atime/unparse-local atime/normal-date)) + :due (some-> (fc/field-value (:invoice/due fc/*current*)) + (atime/unparse-local atime/normal-date))})} (fc/with-field :invoice/client (if (:client request) (com/hidden {:name (fc/field-name) @@ -103,7 +105,8 @@ (atime/unparse-local atime/normal-date)) :name (fc/field-name) :error? (fc/field-errors) - "@change" "date=$event.target.value" ;; TODO x-model does not work + :x-model "date" + ;; "@change" "date=$event.target.value" ;; TODO x-model does not work :placeholder "1/1/2024"})])) (fc/with-field :invoice/due (com/validated-field @@ -115,13 +118,15 @@ :x-hx-val:vendor-id "vendorId" :x-hx-val:date "date" :x-dispatch:changed "[clientId, vendorId, date]" - :x-effect "console.log([clientId, vendorId, date])" :hx-trigger "changed" :hx-target "this" :hx-swap "innerHTML"} (com/date-input {:value (some-> (fc/field-value) (atime/unparse-local atime/normal-date)) :name (fc/field-name) + :x-model "due" + + :x-effect "console.log('hello due', [due])" :error? (fc/field-errors) :placeholder "1/1/2024"})])) (fc/with-field :invoice/scheduled-payment @@ -129,11 +134,19 @@ {:label "Scheduled payment (optional)" :errors (fc/field-errors)} [:div {:class "w-24"} - (com/date-input {:value (some-> (fc/field-value) - (atime/unparse-local atime/normal-date)) - :name (fc/field-name) - :error? (fc/field-errors) - :placeholder "1/1/2024"})]))] + [:div {:class "w-24" + + :hx-put (bidi.bidi/path-for ssr-routes/only-routes ::route/scheduled-payment-date) + :x-dispatch:changed "[clientId, vendorId, due]" + :x-effect "console.log('hello', [clientId, vendorId, due])" + :hx-trigger "changed" + :hx-target "this" + :hx-swap "innerHTML"} + (com/date-input {:value (some-> (fc/field-value) + (atime/unparse-local atime/normal-date)) + :name (fc/field-name) + :error? (fc/field-errors) + :placeholder "1/1/2024"})]]))] (fc/with-field :invoice/invoice-number (com/validated-field @@ -280,14 +293,14 @@ :validation-route ::route/new-wizard-navigate)) mm/Initializable (init-step-params - [_ request] - (alog/peek ::INIT {:invoice/expense-accounts [{:db/id "123" - :invoice-expense-account/amount 100}]}) - {:invoice/expense-accounts [{:db/id "123" - :invoice-expense-account/location "Shared" - :invoice-expense-account/account (ffirst (dc/q '[:find ?a :where [?a :account/name]] - (dc/db conn))) - :invoice-expense-account/amount 100}]})) + [_ request] + (alog/peek ::INIT {:invoice/expense-accounts [{:db/id "123" + :invoice-expense-account/amount 100}]}) + {:invoice/expense-accounts [{:db/id "123" + :invoice-expense-account/location "Shared" + :invoice-expense-account/account (ffirst (dc/q '[:find ?a :where [?a :account/name]] + (dc/db conn))) + :invoice-expense-account/amount 100}]})) @@ -340,24 +353,98 @@ (pull-attr (dc/db conn) :account/location)) :client-locations (some->> client-id (pull-attr (dc/db conn) :client/locations))}))) +(defn clientize-vendor [{:vendor/keys [terms-overrides automatically-paid-when-due default-account account-overrides] :as vendor} client-id] + (let [terms-override (->> terms-overrides + (filter (fn [to] + (= (->db-id (:vendor-terms-override/client to)) + client-id))) + (map :vendor-terms-override/terms) + first) + account (or (->> account-overrides + (filter (fn [to] + (= (->db-id (:vendor-account-override/client to)) + client-id))) + (map :vendor-account-override/account) + first) + default-account) + account (d-accounts/clientize account client-id) + + automatically-paid-when-due (->> automatically-paid-when-due + (filter (fn [to] + (= (->db-id to) + client-id))) + seq + boolean) + vendor (cond-> vendor + terms-override (assoc :vendor/terms terms-override) + true (assoc :vendor/automatically-paid-when-due automatically-paid-when-due + :vendor/default-account account) + true (dissoc :vendor/account-overrides :vendor/terms-overrides))] + vendor)) + + +(comment + (clojure.pprint/pprint + (clientize-vendor (dc/pull + (dc/db conn) + [:vendor/terms + :vendor/automatically-paid-when-due + {:vendor/default-account d-accounts/default-read + :vendor/account-overrides + [:vendor-account-override/client + {:vendor-account-override/account d-accounts/default-read}]} + {:vendor/terms-overrides + [:vendor-terms-override/client :vendor-terms-override/terms]}] + (ffirst (dc/q '[:find ?v :in $ :where [?v :vendor/name "Sysco"]] (dc/db conn)))) + (pull-attr (dc/db conn) :db/id [:client/code "DEMO"])))) + +(defn get-vendor [vendor-id] + (dc/pull + (dc/db conn) + [:vendor/terms + :vendor/automatically-paid-when-due + {:vendor/default-account d-accounts/default-read + :vendor/account-overrides + [:vendor-account-override/client + {:vendor-account-override/account d-accounts/default-read}]} + {:vendor/terms-overrides + [:vendor-terms-override/client :vendor-terms-override/terms]}] + vendor-id)) + (defn due-date [{:keys [multi-form-state]}] (alog/peek ::date multi-form-state) - (let [vendor (dc/pull (dc/db conn) - [:vendor/terms {:vendor/terms-overrides - [:vendor-terms-override/client :vendor-terms-override/terms]}] - (:invoice/vendor (:step-params multi-form-state))) + (let [vendor (clientize-vendor (get-vendor (:invoice/vendor (:step-params multi-form-state))) + (->db-id (:invoice/client (:step-params multi-form-state)))) good-date (or (when (and (:invoice/date (:step-params multi-form-state)) (-> vendor :vendor/terms)) ;; TODO get specific terms (time/plus (:invoice/date (:step-params multi-form-state)) (time/days (-> vendor :vendor/terms)))) (:invoice/due (:step-params multi-form-state)))] - (alog/peek ::good-date multi-form-state) (html-response (com/date-input {:value (some-> good-date (atime/unparse-local atime/normal-date)) :name "step-params[invoice/due]" + :x-init (format "due='%s'" (some-> good-date + (atime/unparse-local atime/normal-date))) + :x-model "due" + :error? false + :placeholder "1/1/2024"})))) + +(defn scheduled-payment-date [{:keys [multi-form-state]}] + (let [vendor (clientize-vendor (get-vendor (:invoice/vendor (:step-params multi-form-state))) + (->db-id (:invoice/client (:step-params multi-form-state)))) + good-date + (when (and (:invoice/due (:step-params multi-form-state)) + (:vendor/automatically-paid-when-due vendor)) ;; TODO get specific terms + + (:invoice/due (:step-params multi-form-state)))] + + (html-response + (com/date-input {:value (some-> good-date + (atime/unparse-local atime/normal-date)) + :name "step-params[invoice/scheduled-payment]" :error? false :placeholder "1/1/2024"})))) @@ -369,17 +456,30 @@ (mm/wrap-init-multi-form-state initial-new-wizard-state)) ::route/due-date (-> due-date #_(wrap-schema-decode :form-schema [:map - [:step-params {:optional true} - [:maybe - [:map - [:invoice/due {:optional true} [:maybe clj-date-schema]]]]] - [:date {:optional true} [:maybe clj-date-schema]] - [:due-date {:optional true} [:maybe clj-date-schema]] - [:client-id {:optional true} [:maybe entity-id]] - [:vendor-id {:optional true} [:maybe entity-id]]]) + [:step-params {:optional true} + [:maybe + [:map + [:invoice/due {:optional true} [:maybe clj-date-schema]]]]] + [:date {:optional true} [:maybe clj-date-schema]] + [:due-date {:optional true} [:maybe clj-date-schema]] + [:client-id {:optional true} [:maybe entity-id]] + [:vendor-id {:optional true} [:maybe entity-id]]]) (mm/wrap-wizard new-wizard) (mm/wrap-decode-multi-form-state) (wrap-nested-form-params)) + ::route/scheduled-payment-date (-> scheduled-payment-date + #_(wrap-schema-decode :form-schema [:map + [:step-params {:optional true} + [:maybe + [:map + [:invoice/due {:optional true} [:maybe clj-date-schema]]]]] + [:date {:optional true} [:maybe clj-date-schema]] + [:due-date {:optional true} [:maybe clj-date-schema]] + [:client-id {:optional true} [:maybe entity-id]] + [:vendor-id {:optional true} [:maybe entity-id]]]) + (mm/wrap-wizard new-wizard) + (mm/wrap-decode-multi-form-state) + (wrap-nested-form-params)) ::route/location-select (-> location-select (wrap-schema-enforce :query-schema [:map [:name :string] diff --git a/src/clj/auto_ap/ssr/utils.clj b/src/clj/auto_ap/ssr/utils.clj index 0e57f920..26644018 100644 --- a/src/clj/auto_ap/ssr/utils.clj +++ b/src/clj/auto_ap/ssr/utils.clj @@ -185,28 +185,28 @@ (mt2/transformer {:decoders {'inst? {:compile (fn [schema _] - (let [properties (mc/properties schema) - format (:format properties atime/normal-date)] - (fn [m] - (if (string? m) - (coerce/to-date-time (atime/parse m format)) + (let [properties (mc/properties schema) + format (:format properties atime/normal-date)] + (fn [m] + (if (string? m) + (coerce/to-date-time (atime/parse m format)) - m))))}} - :encoders - {'inst? + m))))}} + :encoders + {'inst? {:compile (fn [schema _] - (let [properties (mc/properties schema) - format (:format properties atime/normal-date)] - (fn [m] - (cond - (inst? m) - (atime/unparse-local (coerce/to-date-time m) format) + (let [properties (mc/properties schema) + format (:format properties atime/normal-date)] + (fn [m] + (cond + (inst? m) + (atime/unparse-local (coerce/to-date-time m) format) - (instance? org.joda.time.DateTime m) - (atime/unparse-local m format) + (instance? org.joda.time.DateTime m) + (atime/unparse-local m format) - :else - m))))}}})) + :else + m))))}}})) @@ -244,6 +244,18 @@ m)) m))))}}})) +(defn ->db-id [m] + (cond + (map? m) + (:db/id m) + (nat-int? m) + m + (and (string? m) (not-empty m)) + (Long/parseLong m) + + :else + m)) + (def pull-transformer (mt2/transformer {:decoders {:entity-map @@ -302,7 +314,7 @@ (get headers "hx-request"))] (alog/peek ::check {:enabled? is-htmx-that-should-inherit-url-parameters? :params (:query-params request)}) - + (if is-htmx-that-should-inherit-url-parameters? (handler (update request :query-params (fn [qp] (->> (concat (:hx-query-params request) qp) @@ -311,7 +323,7 @@ (def dissoc-nil-transformer - (let [e {:map {:compile (fn [schema _] + (let [e {:map {:compile (fn [schema _] (fn [data] (if (map? data) (filter-vals @@ -580,7 +592,7 @@ (with-precision 2 (double (.setScale (bigdec d) 2 java.math.RoundingMode/HALF_UP)))) - + (defn wrap-implied-route-param [handler & {:as route-params}] (fn [request] (handler (update-in request [:route-params] merge route-params)))) \ No newline at end of file diff --git a/src/cljc/auto_ap/routes/invoice.cljc b/src/cljc/auto_ap/routes/invoice.cljc index 55277d01..ee1b13e8 100644 --- a/src/cljc/auto_ap/routes/invoice.cljc +++ b/src/cljc/auto_ap/routes/invoice.cljc @@ -6,6 +6,7 @@ "/new" {:get ::new-wizard :post ::new-invoice-submit "/due-date" ::due-date + "/scheduled-payment-date" ::scheduled-payment-date "/navigate" ::new-wizard-navigate "/account/new" ::new-wizard-new-account "/account/location-select" ::location-select}