new invoice progress
This commit is contained in:
@@ -12,10 +12,12 @@
|
||||
[auto-ap.ssr.components.multi-modal :as mm]
|
||||
[auto-ap.ssr.form-cursor :as fc]
|
||||
[auto-ap.ssr.hx :as hx]
|
||||
[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-enforce]]
|
||||
entity-id html-response money wrap-schema-decode
|
||||
wrap-schema-enforce]]
|
||||
[auto-ap.time :as atime]
|
||||
[bidi.bidi :as bidi]
|
||||
[clj-time.core :as time]
|
||||
@@ -27,6 +29,7 @@
|
||||
[:map
|
||||
[:invoice/client entity-id]
|
||||
[:invoice/date clj-date-schema]
|
||||
[:invoice/due {:optional true} [:maybe clj-date-schema]]
|
||||
[:invoice/vendor entity-id]
|
||||
[:invoice/expense-accounts
|
||||
[:vector {:coerce? true}
|
||||
@@ -46,99 +49,115 @@
|
||||
[])
|
||||
|
||||
(step-schema [_]
|
||||
(mut/select-keys (mm/form-schema linear-wizard) #{:invoice/client :invoice/vendor :invoice/date}))
|
||||
(mut/select-keys (mm/form-schema linear-wizard) #{:invoice/client :invoice/vendor :invoice/date :invoice/due}))
|
||||
|
||||
(render-step [this request]
|
||||
(alog/peek ::check (:multi-form-state request))
|
||||
(mm/default-render-step
|
||||
linear-wizard this
|
||||
:head [:div.p-2 "New invoice"]
|
||||
:body (mm/default-step-body
|
||||
{}
|
||||
[:div {}
|
||||
(fc/with-field :invoice/client
|
||||
(if (:client request)
|
||||
(com/hidden {:name (fc/field-name)
|
||||
:value (:db/id (:client request))})
|
||||
(com/validated-field
|
||||
{:label "Client"
|
||||
:errors (fc/field-errors)}
|
||||
[:div.w-96
|
||||
(com/typeahead {:name (fc/field-name)
|
||||
:error? (fc/error?)
|
||||
:class "w-96"
|
||||
:placeholder "Search..."
|
||||
:url (bidi/path-for ssr-routes/only-routes :company-search)
|
||||
:value (fc/field-value)
|
||||
:content-fn (fn [c] (pull-attr (dc/db conn) :client/name c))})])))
|
||||
(fc/with-field :invoice/vendor
|
||||
(com/validated-field
|
||||
{:label "Vendor"
|
||||
:errors (fc/field-errors)}
|
||||
[:div.w-96
|
||||
(com/typeahead {:name (fc/field-name)
|
||||
:error? (fc/error?)
|
||||
:class "w-96"
|
||||
:placeholder "Search..."
|
||||
:url (bidi/path-for ssr-routes/only-routes :vendor-search)
|
||||
:value (fc/field-value)
|
||||
:content-fn (fn [c] (pull-attr (dc/db conn) :vendor/name c))})]))
|
||||
(alog/peek ::check (:multi-form-state request))
|
||||
(mm/default-render-step
|
||||
linear-wizard this
|
||||
:head [:div.p-2 "New invoice"]
|
||||
:body (mm/default-step-body
|
||||
{}
|
||||
[:div {:x-data (hx/json {:clientId (or (:db/id (:client request))
|
||||
(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))})}
|
||||
(fc/with-field :invoice/client
|
||||
(if (:client request)
|
||||
(com/hidden {:name (fc/field-name)
|
||||
:value (:db/id (:client request))})
|
||||
(com/validated-field
|
||||
{:label "Client"
|
||||
:errors (fc/field-errors)}
|
||||
[:div.w-96
|
||||
(com/typeahead {:name (fc/field-name)
|
||||
:error? (fc/error?)
|
||||
:class "w-96"
|
||||
:placeholder "Search..."
|
||||
:url (bidi/path-for ssr-routes/only-routes :company-search)
|
||||
:value (fc/field-value)
|
||||
:content-fn (fn [c] (pull-attr (dc/db conn) :client/name c))
|
||||
:x-model "clientId"})])))
|
||||
(fc/with-field :invoice/vendor
|
||||
(com/validated-field
|
||||
{:label "Vendor"
|
||||
:errors (fc/field-errors)}
|
||||
[:div.w-96
|
||||
(com/typeahead {:name (fc/field-name)
|
||||
:error? (fc/error?)
|
||||
:class "w-96"
|
||||
:placeholder "Search..."
|
||||
:url (bidi/path-for ssr-routes/only-routes :vendor-search)
|
||||
:value (fc/field-value)
|
||||
:content-fn (fn [c] (pull-attr (dc/db conn) :vendor/name c))
|
||||
:x-model "vendorId"})]))
|
||||
|
||||
[:div.flex.space-x-8
|
||||
(fc/with-field :invoice/date
|
||||
(com/validated-field
|
||||
{:label "Date"
|
||||
: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"})]))
|
||||
(fc/with-field :invoice/due
|
||||
(com/validated-field
|
||||
{:label "Due (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"})]))
|
||||
(fc/with-field :invoice/scheduled-payment
|
||||
(com/validated-field
|
||||
{: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.flex.space-x-8
|
||||
(fc/with-field :invoice/date
|
||||
(com/validated-field
|
||||
{:label "Date"
|
||||
: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)
|
||||
"@change" "date=$event.target.value" ;; TODO x-model does not work
|
||||
:placeholder "1/1/2024"})]))
|
||||
(fc/with-field :invoice/due
|
||||
(com/validated-field
|
||||
{:label "Due (optional)"
|
||||
:errors (fc/field-errors)}
|
||||
[:div {:class "w-24"
|
||||
:hx-put (bidi.bidi/path-for ssr-routes/only-routes ::route/due-date)
|
||||
:x-hx-val:client-id "clientId" ;; TODO maybe just use the whole form
|
||||
: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)
|
||||
:error? (fc/field-errors)
|
||||
:placeholder "1/1/2024"})]))
|
||||
(fc/with-field :invoice/scheduled-payment
|
||||
(com/validated-field
|
||||
{: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"})]))]
|
||||
|
||||
(fc/with-field :invoice/invoice-number
|
||||
(com/validated-field
|
||||
{:label "Invoice Number"
|
||||
:errors (fc/field-errors)}
|
||||
[:div {:class "w-24"}
|
||||
(com/text-input {:value (-> (fc/field-value))
|
||||
:name (fc/field-name)
|
||||
:error? (fc/field-errors)
|
||||
:placeholder "HA-123"})]))
|
||||
(fc/with-field :invoice/total
|
||||
(com/validated-field
|
||||
{:label "Total"
|
||||
:errors (fc/field-errors)}
|
||||
[:div {:class "w-16"}
|
||||
(com/money-input {:value (-> (fc/field-value))
|
||||
:name (fc/field-name)
|
||||
:class "w-24"
|
||||
:error? (fc/field-errors)
|
||||
:placeholder "212.44"})]))])
|
||||
(fc/with-field :invoice/invoice-number
|
||||
(com/validated-field
|
||||
{:label "Invoice Number"
|
||||
:errors (fc/field-errors)}
|
||||
[:div {:class "w-24"}
|
||||
(com/text-input {:value (-> (fc/field-value))
|
||||
:name (fc/field-name)
|
||||
:error? (fc/field-errors)
|
||||
:placeholder "HA-123"})]))
|
||||
(fc/with-field :invoice/total
|
||||
(com/validated-field
|
||||
{:label "Total"
|
||||
:errors (fc/field-errors)}
|
||||
[:div {:class "w-16"}
|
||||
(com/money-input {:value (-> (fc/field-value))
|
||||
:name (fc/field-name)
|
||||
:class "w-24"
|
||||
:error? (fc/field-errors)
|
||||
:placeholder "212.44"})]))])
|
||||
|
||||
:footer
|
||||
(mm/default-step-footer linear-wizard this :validation-route ::route/new-wizard-navigate)
|
||||
:validation-route ::route/new-wizard-navigate)))
|
||||
:footer
|
||||
(mm/default-step-footer linear-wizard this :validation-route ::route/new-wizard-navigate)
|
||||
:validation-route ::route/new-wizard-navigate)))
|
||||
|
||||
(defn- location-select*
|
||||
[{:keys [name account-location client-locations value]}]
|
||||
@@ -173,7 +192,7 @@
|
||||
[{:keys [value client-id]}]
|
||||
(com/data-grid-row
|
||||
(-> {:x-data (hx/json {:show (boolean (not (fc/field-value (:new? value))))
|
||||
:accountId (:db/id (fc/field-value (:invoice-expense-account/account value)))})
|
||||
:accountId (fc/field-value (:invoice-expense-account/account value))})
|
||||
:data-key "show"
|
||||
:x-ref "p"}
|
||||
hx/alpine-mount-then-appear)
|
||||
@@ -205,10 +224,10 @@
|
||||
(location-select* {:name (fc/field-name)
|
||||
:account-location (:account/location (cond->> (:invoice-expense-account/account @value)
|
||||
(nat-int? (:invoice-expense-account/account @value)) (dc/pull (dc/db conn)
|
||||
'[:account/location])))
|
||||
'[:account/location])))
|
||||
:client-locations (pull-attr (dc/db conn) :client/locations client-id)
|
||||
:value (fc/field-value)}))))
|
||||
(fc/with-field :invoice-expense-account/amount
|
||||
(fc/with-field :invoice-expense-account/amount
|
||||
(com/data-grid-cell
|
||||
{}
|
||||
(com/validated-field
|
||||
@@ -233,31 +252,32 @@
|
||||
(mut/select-keys (mm/form-schema linear-wizard) #{:invoice/expense-accounts}))
|
||||
|
||||
(render-step [this {{:keys [snapshot]} :multi-form-state :as request}]
|
||||
(mm/default-render-step
|
||||
linear-wizard this
|
||||
:head [:div.p-2 "Invoice accounts "]
|
||||
:body (mm/default-step-body
|
||||
{}
|
||||
[:div {}
|
||||
(pull-attr (dc/db conn) :client/name (:invoice/client snapshot))
|
||||
(fc/with-field :invoice/expense-accounts
|
||||
(com/validated-field
|
||||
{:errors (fc/field-errors)}
|
||||
(com/data-grid {:headers [(com/data-grid-header {} "Account")
|
||||
(com/data-grid-header {:class "w-32"} "Location")
|
||||
(com/data-grid-header {:class "w-16"} "%")
|
||||
(com/data-grid-header {:class "w-16"})]}
|
||||
(fc/cursor-map #(invoice-expense-account-row* {:value %
|
||||
:client-id (:invoice/client snapshot)}))
|
||||
(com/data-grid-new-row {:colspan 4
|
||||
:hx-get (bidi/path-for ssr-routes/only-routes
|
||||
::route/new-wizard-new-account)
|
||||
:index (count (fc/field-value))
|
||||
:tr-params {:hx-vals (hx/json {:client-id (:invoice/client snapshot)})}}
|
||||
"New account"))))])
|
||||
:footer
|
||||
(mm/default-step-footer linear-wizard this :validation-route ::route/new-wizard-navigate)
|
||||
:validation-route ::route/new-wizard-navigate)))
|
||||
(alog/peek ::mfs (:step-params (:multi-form-state request)))
|
||||
(mm/default-render-step
|
||||
linear-wizard this
|
||||
:head [:div.p-2 "Invoice accounts "]
|
||||
:body (mm/default-step-body
|
||||
{}
|
||||
[:div {}
|
||||
(pull-attr (dc/db conn) :client/name (:invoice/client snapshot))
|
||||
(fc/with-field :invoice/expense-accounts
|
||||
(com/validated-field
|
||||
{:errors (fc/field-errors)}
|
||||
(com/data-grid {:headers [(com/data-grid-header {} "Account")
|
||||
(com/data-grid-header {:class "w-32"} "Location")
|
||||
(com/data-grid-header {:class "w-16"} "%")
|
||||
(com/data-grid-header {:class "w-16"})]}
|
||||
(fc/cursor-map #(invoice-expense-account-row* {:value %
|
||||
:client-id (:invoice/client snapshot)}))
|
||||
(com/data-grid-new-row {:colspan 4
|
||||
:hx-get (bidi/path-for ssr-routes/only-routes
|
||||
::route/new-wizard-new-account)
|
||||
:index (count (fc/field-value))
|
||||
:tr-params {:hx-vals (hx/json {:client-id (:invoice/client snapshot)})}}
|
||||
"New account"))))])
|
||||
:footer
|
||||
(mm/default-step-footer linear-wizard this :validation-route ::route/new-wizard-navigate)
|
||||
:validation-route ::route/new-wizard-navigate)))
|
||||
|
||||
|
||||
|
||||
@@ -291,8 +311,8 @@
|
||||
step-key)))
|
||||
(form-schema [_] new-form-schema)
|
||||
(submit [this {:keys [multi-form-state request-method identity] :as request}]
|
||||
(html-response [:div]
|
||||
:headers {"hx-trigger" "modalclose"})))
|
||||
(html-response [:div]
|
||||
:headers {"hx-trigger" "modalclose"})))
|
||||
|
||||
(def new-wizard (->NewWizard2 nil nil))
|
||||
|
||||
@@ -310,12 +330,46 @@
|
||||
(pull-attr (dc/db conn) :account/location))
|
||||
:client-locations (some->> client-id
|
||||
(pull-attr (dc/db conn) :client/locations))})))
|
||||
(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)))
|
||||
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]"
|
||||
:error? false
|
||||
:placeholder "1/1/2024"}))))
|
||||
|
||||
(def key->handler
|
||||
(apply-middleware-to-all-handlers
|
||||
{::route/new-wizard (-> mm/open-wizard-handler
|
||||
|
||||
(mm/wrap-wizard new-wizard)
|
||||
(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]]])
|
||||
(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]
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"/voided" ::voided-page}
|
||||
"/new" {:get ::new-wizard
|
||||
:post ::new-invoice-submit
|
||||
"/due-date" ::due-date
|
||||
"/navigate" ::new-wizard-navigate
|
||||
"/account/new" ::new-wizard-new-account
|
||||
"/account/location-select" ::location-select}
|
||||
|
||||
Reference in New Issue
Block a user