Editing works.
This commit is contained in:
@@ -33,6 +33,9 @@
|
|||||||
(defprotocol Initializable
|
(defprotocol Initializable
|
||||||
(init-step-params [this multi-form-state request]))
|
(init-step-params [this multi-form-state request]))
|
||||||
|
|
||||||
|
(defprotocol CustomNext
|
||||||
|
(custom-next-handler [this request]))
|
||||||
|
|
||||||
(defprotocol Discardable
|
(defprotocol Discardable
|
||||||
(can-discard? [this step-params])
|
(can-discard? [this step-params])
|
||||||
(discard-changes [this request]))
|
(discard-changes [this request]))
|
||||||
@@ -70,6 +73,11 @@
|
|||||||
:edit-path []
|
:edit-path []
|
||||||
:step-params @cursor)))
|
:step-params @cursor)))
|
||||||
|
|
||||||
|
(defn get-mfs-field [mfs k]
|
||||||
|
(or (get (:step-params mfs) k)
|
||||||
|
(get-in (:snapshot mfs) (conj (or (:edit-path mfs) [])
|
||||||
|
k))))
|
||||||
|
|
||||||
(def step-key-schema (mc/schema [:orn {:decode/arbitrary clojure.edn/read-string
|
(def step-key-schema (mc/schema [:orn {:decode/arbitrary clojure.edn/read-string
|
||||||
:encode/arbitrary pr-str}
|
:encode/arbitrary pr-str}
|
||||||
[:sub-step [:cat :keyword [:or :int :string]]]
|
[:sub-step [:cat :keyword [:or :int :string]]]
|
||||||
@@ -228,7 +236,7 @@
|
|||||||
:else
|
:else
|
||||||
"forward")))
|
"forward")))
|
||||||
|
|
||||||
(defn render-navigate [{ {:keys [wizard] :as request } :request to-step :to-step oob :oob}]
|
(defn navigate-handler [{{:keys [wizard] :as request} :request to-step :to-step oob :oob}]
|
||||||
(let [current-step (get-current-step wizard)
|
(let [current-step (get-current-step wizard)
|
||||||
wizard (navigate wizard to-step)
|
wizard (navigate wizard to-step)
|
||||||
new-step (get-current-step wizard)
|
new-step (get-current-step wizard)
|
||||||
@@ -249,12 +257,15 @@
|
|||||||
|
|
||||||
(def next-handler
|
(def next-handler
|
||||||
(-> (fn [{:keys [wizard] :as request}]
|
(-> (fn [{:keys [wizard] :as request}]
|
||||||
(render-navigate {:request request
|
(let [current-step (get-current-step wizard)]
|
||||||
:to-step (:to (:query-params request))}))
|
(if (satisfies? CustomNext current-step)
|
||||||
|
(custom-next-handler current-step request)
|
||||||
|
(navigate-handler {:request request
|
||||||
|
:to-step (:to (:query-params request))}))))
|
||||||
(wrap-ensure-step)
|
(wrap-ensure-step)
|
||||||
(wrap-schema-enforce :query-schema
|
(wrap-schema-enforce :query-schema
|
||||||
[:map
|
[:map
|
||||||
[:to step-key-schema]])))
|
[:to {:optional true} [:maybe step-key-schema]]])))
|
||||||
|
|
||||||
(def discard-handler
|
(def discard-handler
|
||||||
(->
|
(->
|
||||||
@@ -326,7 +337,7 @@
|
|||||||
(assoc request :wizard (hydrate-from-request linear-wizard request))))))
|
(assoc request :wizard (hydrate-from-request linear-wizard request))))))
|
||||||
|
|
||||||
(defn open-wizard-handler [{:keys [wizard current-step query-params] :as request}]
|
(defn open-wizard-handler [{:keys [wizard current-step query-params] :as request}]
|
||||||
(cond->
|
(cond->
|
||||||
(modal-response
|
(modal-response
|
||||||
[:div#transitioner.flex-1 {:x-data (hx/json {"transitionType" "none"})
|
[:div#transitioner.flex-1 {:x-data (hx/json {"transitionType" "none"})
|
||||||
:x-ref "transitioner"
|
:x-ref "transitioner"
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
|
|
||||||
|
|
||||||
[:invoice/date :xform clj-time.coerce/from-date]
|
[:invoice/date :xform clj-time.coerce/from-date]
|
||||||
|
[:invoice/due :xform clj-time.coerce/from-date]
|
||||||
|
[:invoice/scheduled-payment :xform clj-time.coerce/from-date]
|
||||||
{:invoice/client [:client/code :db/id :client/name]
|
{:invoice/client [:client/code :db/id :client/name]
|
||||||
:invoice/expense-accounts [* {:invoice-expense-account/account [:account/name :db/id
|
:invoice/expense-accounts [* {:invoice-expense-account/account [:account/name :db/id
|
||||||
:account/location
|
:account/location
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
(:require [auto-ap.datomic
|
(:require [auto-ap.datomic
|
||||||
:refer [audit-transact conn pull-attr]]
|
:refer [audit-transact conn pull-attr]]
|
||||||
[auto-ap.datomic.accounts :as d-accounts]
|
[auto-ap.datomic.accounts :as d-accounts]
|
||||||
[auto-ap.ssr.invoice.common :refer [default-read]]
|
|
||||||
[auto-ap.datomic.invoices :as d-invoices]
|
[auto-ap.datomic.invoices :as d-invoices]
|
||||||
[auto-ap.graphql.utils :refer [assert-can-see-client
|
[auto-ap.graphql.utils :refer [assert-can-see-client
|
||||||
assert-not-locked exception->4xx]]
|
assert-not-locked exception->4xx]]
|
||||||
@@ -17,6 +16,7 @@
|
|||||||
[auto-ap.ssr.components.multi-modal :as mm]
|
[auto-ap.ssr.components.multi-modal :as mm]
|
||||||
[auto-ap.ssr.form-cursor :as fc]
|
[auto-ap.ssr.form-cursor :as fc]
|
||||||
[auto-ap.ssr.hx :as hx]
|
[auto-ap.ssr.hx :as hx]
|
||||||
|
[auto-ap.ssr.invoice.common :refer [default-read]]
|
||||||
[auto-ap.ssr.nested-form-params :refer [wrap-nested-form-params]]
|
[auto-ap.ssr.nested-form-params :refer [wrap-nested-form-params]]
|
||||||
[auto-ap.ssr.svg :as svg]
|
[auto-ap.ssr.svg :as svg]
|
||||||
[auto-ap.ssr.utils
|
[auto-ap.ssr.utils
|
||||||
@@ -70,10 +70,6 @@
|
|||||||
(some? (:vendor/default-account (get-vendor vendor-id))))
|
(some? (:vendor/default-account (get-vendor vendor-id))))
|
||||||
|
|
||||||
;; TODO negative expense accounts for negative invoices?
|
;; TODO negative expense accounts for negative invoices?
|
||||||
;; TODO make changing expense accounts work for editing invoices
|
|
||||||
;; TODO make a change of total automatically change the expense accounts when editing
|
|
||||||
;; TODO just close when editing, don't go to print screen
|
|
||||||
|
|
||||||
|
|
||||||
(def new-form-schema
|
(def new-form-schema
|
||||||
[:map
|
[:map
|
||||||
@@ -81,7 +77,9 @@
|
|||||||
[:customize-due-and-scheduled? {:optional true :default false :decode/arbitrary (fn [x] (if (= "" x)
|
[:customize-due-and-scheduled? {:optional true :default false :decode/arbitrary (fn [x] (if (= "" x)
|
||||||
false
|
false
|
||||||
x))} [:maybe :boolean]]
|
x))} [:maybe :boolean]]
|
||||||
[:invoice/client entity-id]
|
[:customize-accounts {:optional true :default :default} [:enum :default :customize]]
|
||||||
|
|
||||||
|
[:invoice/client {:optional true} [:maybe entity-id]]
|
||||||
[:invoice/date clj-date-schema]
|
[:invoice/date clj-date-schema]
|
||||||
[:invoice/due {:optional true} [:maybe clj-date-schema]]
|
[:invoice/due {:optional true} [:maybe clj-date-schema]]
|
||||||
[:invoice/scheduled-payment {:optional true} [:maybe clj-date-schema]]
|
[:invoice/scheduled-payment {:optional true} [:maybe clj-date-schema]]
|
||||||
@@ -105,41 +103,57 @@
|
|||||||
|
|
||||||
(defn wrap-schema [s]
|
(defn wrap-schema [s]
|
||||||
[:and s
|
[:and s
|
||||||
[:fn (fn [{:keys [:db/id :invoice/invoice-number :invoice/vendor] :as z}]
|
[:fn (fn [{:keys [:db/id :invoice/invoice-number :invoice/vendor :invoice/client] :as z}]
|
||||||
(println "HERE" id invoice-number vendor z)
|
|
||||||
(if id
|
(if id
|
||||||
true
|
true
|
||||||
(and invoice-number vendor)))]])
|
(and invoice-number vendor client)))]])
|
||||||
|
|
||||||
|
|
||||||
(defn clientize-vendor [{:vendor/keys [terms-overrides automatically-paid-when-due default-account account-overrides] :as vendor} client-id]
|
(defn clientize-vendor [{:vendor/keys [terms-overrides automatically-paid-when-due default-account account-overrides] :as vendor} client-id]
|
||||||
(let [terms-override (->> terms-overrides
|
(if (nil? vendor)
|
||||||
|
nil
|
||||||
|
(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]
|
(filter (fn [to]
|
||||||
(= (->db-id (:vendor-terms-override/client to))
|
(= (->db-id (:vendor-account-override/client to))
|
||||||
client-id)))
|
client-id)))
|
||||||
(map :vendor-terms-override/terms)
|
(map :vendor-account-override/account)
|
||||||
first)
|
first)
|
||||||
account (or (->> account-overrides
|
default-account)
|
||||||
(filter (fn [to]
|
account (d-accounts/clientize account client-id)
|
||||||
(= (->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
|
automatically-paid-when-due (->> automatically-paid-when-due
|
||||||
(filter (fn [to]
|
(filter (fn [to]
|
||||||
(= (->db-id to)
|
(= (->db-id to)
|
||||||
client-id)))
|
client-id)))
|
||||||
seq
|
seq
|
||||||
boolean)
|
boolean)
|
||||||
vendor (cond-> vendor
|
vendor (cond-> vendor
|
||||||
terms-override (assoc :vendor/terms terms-override)
|
terms-override (assoc :vendor/terms terms-override)
|
||||||
true (assoc :vendor/automatically-paid-when-due automatically-paid-when-due
|
true (assoc :vendor/automatically-paid-when-due automatically-paid-when-due
|
||||||
:vendor/default-account account)
|
:vendor/default-account account)
|
||||||
true (dissoc :vendor/account-overrides :vendor/terms-overrides))]
|
true (dissoc :vendor/account-overrides :vendor/terms-overrides))]
|
||||||
vendor))
|
vendor)))
|
||||||
|
|
||||||
|
(defn account-prediction* [{: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))))
|
||||||
|
account-name (:account/name (:vendor/default-account vendor))
|
||||||
|
value (mm/get-mfs-field multi-form-state :customize-accounts)]
|
||||||
|
(when vendor
|
||||||
|
(com/radio-list {:name "step-params[customize-accounts]"
|
||||||
|
|
||||||
|
:value (name value)
|
||||||
|
:options (filter identity
|
||||||
|
[(when account-name {:value (name :default)
|
||||||
|
:content (com/pill {:color :primary} account-name)})
|
||||||
|
{:value (name :customize)
|
||||||
|
:content [:div "Customize accounts"]}])}))))
|
||||||
|
|
||||||
|
|
||||||
(defrecord BasicDetailsStep [linear-wizard]
|
(defrecord BasicDetailsStep [linear-wizard]
|
||||||
@@ -153,155 +167,178 @@
|
|||||||
[])
|
[])
|
||||||
|
|
||||||
(step-schema [_]
|
(step-schema [_]
|
||||||
(wrap-schema (mut/select-keys (mm/form-schema linear-wizard) #{:invoice/client :invoice/vendor :invoice/date :invoice/due :invoice/scheduled-payment :invoice/total :invoice/invoice-number :db/id :customize-due-and-scheduled?})))
|
(wrap-schema (mut/select-keys (mm/form-schema linear-wizard) #{:invoice/client :invoice/vendor :invoice/date :invoice/due :invoice/scheduled-payment :invoice/total :invoice/invoice-number :db/id :customize-due-and-scheduled? :customize-accounts})))
|
||||||
|
|
||||||
(render-step [this request]
|
(render-step
|
||||||
(mm/default-render-step
|
[this {:keys [multi-form-state] :as request}]
|
||||||
linear-wizard this
|
(let [extant? (mm/get-mfs-field multi-form-state :db/id)]
|
||||||
:head [:div.p-2 "New invoice"]
|
(mm/default-render-step
|
||||||
:body (mm/default-step-body
|
linear-wizard this
|
||||||
{}
|
:head [:div.p-2 (if extant?
|
||||||
[:div {:x-data (hx/json {:clientId (or (:db/id (:client request))
|
"Edit invoice"
|
||||||
(fc/field-value (:invoice/client fc/*current*)))
|
"New invoice")]
|
||||||
:vendorId (fc/field-value (:invoice/vendor fc/*current*))
|
:body (mm/default-step-body
|
||||||
:date (-> (fc/field-value (:invoice/date fc/*current*))
|
{}
|
||||||
(atime/unparse-local atime/normal-date))
|
[:div {:x-data (hx/json {:clientId (or (fc/field-value (:invoice/client fc/*current*))
|
||||||
:due (some-> (fc/field-value (:invoice/due fc/*current*))
|
(:db/id (:client request)))
|
||||||
(atime/unparse-local atime/normal-date))
|
:vendorId (fc/field-value (:invoice/vendor fc/*current*))
|
||||||
:scheduledPayment (some-> (fc/field-value (:invoice/scheduled-payment fc/*current*))
|
:date (-> (fc/field-value (:invoice/date fc/*current*))
|
||||||
(atime/unparse-local atime/normal-date))
|
(atime/unparse-local atime/normal-date))
|
||||||
:customizeDueAndScheduled (fc/field-value (:customize-due-and-scheduled? fc/*current*))})}
|
:due (some-> (fc/field-value (:invoice/due fc/*current*))
|
||||||
(fc/with-field :db/id
|
(atime/unparse-local atime/normal-date))
|
||||||
(when (fc/field-value)
|
:scheduledPayment (some-> (fc/field-value (:invoice/scheduled-payment fc/*current*))
|
||||||
|
(atime/unparse-local atime/normal-date))
|
||||||
|
:customizeDueAndScheduled (fc/field-value (:customize-due-and-scheduled? fc/*current*))})}
|
||||||
|
(fc/with-field :db/id
|
||||||
|
(when extant?
|
||||||
|
(com/hidden {:name (fc/field-name)
|
||||||
|
:value (fc/field-value)})))
|
||||||
|
|
||||||
|
|
||||||
|
(fc/with-field :customize-due-and-scheduled?
|
||||||
(com/hidden {:name (fc/field-name)
|
(com/hidden {:name (fc/field-name)
|
||||||
:value (fc/field-value)})))
|
:value (fc/field-value)
|
||||||
|
:x-model "customizeDueAndScheduled"}))
|
||||||
|
(fc/with-field :invoice/client
|
||||||
(fc/with-field :customize-due-and-scheduled?
|
(if (or (:client request) extant?)
|
||||||
(com/hidden {:name (fc/field-name)
|
(com/hidden {:name (fc/field-name)
|
||||||
:value (fc/field-value)
|
:value (or (mm/get-mfs-field multi-form-state :invoice/client)
|
||||||
:x-model "customizeDueAndScheduled"}))
|
(:db/id (:client request)))})
|
||||||
(fc/with-field :invoice/client
|
(com/validated-field
|
||||||
(if (:client request)
|
{:label "Client"
|
||||||
(com/hidden {:name (fc/field-name)
|
:errors (fc/field-errors)}
|
||||||
:value (:db/id (:client request))})
|
[: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
|
(com/validated-field
|
||||||
{:label "Client"
|
{:label "Vendor"
|
||||||
:errors (fc/field-errors)}
|
:errors (fc/field-errors)}
|
||||||
[:div.w-96
|
[:div.w-96
|
||||||
(com/typeahead {:name (fc/field-name)
|
(com/typeahead {:name (fc/field-name)
|
||||||
:error? (fc/error?)
|
:error? (fc/error?)
|
||||||
|
:disabled (boolean (-> request :multi-form-state :snapshot :db/id))
|
||||||
:class "w-96"
|
:class "w-96"
|
||||||
:placeholder "Search..."
|
:placeholder "Search..."
|
||||||
:url (bidi/path-for ssr-routes/only-routes :company-search)
|
:url (bidi/path-for ssr-routes/only-routes :vendor-search)
|
||||||
:value (fc/field-value)
|
:value (fc/field-value)
|
||||||
:content-fn (fn [c] (pull-attr (dc/db conn) :client/name c))
|
:content-fn (fn [c] (pull-attr (dc/db conn) :vendor/name c))
|
||||||
:x-model "clientId"})])))
|
:x-model "vendorId"})]))
|
||||||
(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?)
|
|
||||||
:disabled (boolean (-> request :multi-form-state :snapshot :db/id))
|
|
||||||
: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.items-center.gap-2
|
[:div.flex.items-center.gap-2
|
||||||
(fc/with-field :invoice/date
|
(fc/with-field :invoice/date
|
||||||
(com/validated-field
|
(com/validated-field
|
||||||
{:label "Date"
|
{:label "Date"
|
||||||
:errors (fc/field-errors)}
|
:errors (fc/field-errors)}
|
||||||
[:div {:class "w-24"}
|
[:div {:class "w-24"}
|
||||||
(com/date-input {:value (some-> (fc/field-value)
|
(com/date-input {:value (some-> (fc/field-value)
|
||||||
(atime/unparse-local atime/normal-date))
|
(atime/unparse-local atime/normal-date))
|
||||||
:name (fc/field-name)
|
:name (fc/field-name)
|
||||||
:error? (fc/field-errors)
|
:error? (fc/field-errors)
|
||||||
:x-model "date"
|
:x-model "date"
|
||||||
:placeholder "1/1/2024"})]))
|
:placeholder "1/1/2024"})]))
|
||||||
[:div {:x-show "!customizeDueAndScheduled"}
|
[:div {:x-show "!customizeDueAndScheduled"}
|
||||||
(com/link {"@click" "customizeDueAndScheduled=true"
|
(com/link {"@click" "customizeDueAndScheduled=true"
|
||||||
:x-show "!due && !scheduledPayment"}
|
:x-show "!due && !scheduledPayment"}
|
||||||
"Add due / scheduled payment date")
|
"Add due / scheduled payment date")
|
||||||
(com/link {"@click" "customizeDueAndScheduled=true"
|
(com/link {"@click" "customizeDueAndScheduled=true"
|
||||||
:x-show "due || scheduledPayment"}
|
:x-show "due || scheduledPayment"}
|
||||||
"Change due / scheduled payment date")]]
|
"Change due / scheduled payment date")]]
|
||||||
(fc/with-field :invoice/due
|
(fc/with-field :invoice/due
|
||||||
(com/validated-field
|
(com/validated-field
|
||||||
(hx/alpine-appear {:label "Due (optional)"
|
(hx/alpine-appear {:label "Due (optional)"
|
||||||
:errors (fc/field-errors)
|
:errors (fc/field-errors)
|
||||||
:x-show "customizeDueAndScheduled"})
|
:x-show "customizeDueAndScheduled"})
|
||||||
[:div {:class "w-24"
|
[:div {:class "w-24"
|
||||||
:hx-put (bidi.bidi/path-for ssr-routes/only-routes ::route/due-date)
|
:hx-put (bidi.bidi/path-for ssr-routes/only-routes ::route/due-date)
|
||||||
:x-dispatch:changed "[clientId, vendorId, date]"
|
:x-dispatch:changed "[clientId, vendorId, date]"
|
||||||
:hx-trigger "changed"
|
:hx-trigger "changed"
|
||||||
:hx-target "this"
|
:hx-target "this"
|
||||||
:hx-swap "innerHTML"}
|
:hx-swap "innerHTML"}
|
||||||
(com/date-input {:value (some-> (fc/field-value)
|
(com/date-input {:value (some-> (fc/field-value)
|
||||||
(atime/unparse-local atime/normal-date))
|
(atime/unparse-local atime/normal-date))
|
||||||
:name (fc/field-name)
|
:name (fc/field-name)
|
||||||
:x-model "due"
|
:x-model "due"
|
||||||
|
|
||||||
:error? (fc/field-errors)
|
:error? (fc/field-errors)
|
||||||
:placeholder "1/1/2024"})]))
|
:placeholder "1/1/2024"})]))
|
||||||
(fc/with-field :invoice/scheduled-payment
|
(fc/with-field :invoice/scheduled-payment
|
||||||
(com/validated-field
|
(com/validated-field
|
||||||
(hx/alpine-appear {:label "Scheduled payment (optional)"
|
(hx/alpine-appear {:label "Scheduled payment (optional)"
|
||||||
:errors (fc/field-errors)
|
:errors (fc/field-errors)
|
||||||
:x-show "customizeDueAndScheduled"})
|
:x-show "customizeDueAndScheduled"})
|
||||||
[:div {:class "w-24"}
|
[:div {:class "w-24"}
|
||||||
[:div {:class "w-24"
|
[:div {:class "w-24"
|
||||||
|
|
||||||
:hx-put (bidi.bidi/path-for ssr-routes/only-routes ::route/scheduled-payment-date)
|
:hx-put (bidi.bidi/path-for ssr-routes/only-routes ::route/scheduled-payment-date)
|
||||||
:x-dispatch:changed "[clientId, vendorId, due]"
|
:x-dispatch:changed "[clientId, vendorId, due]"
|
||||||
:hx-trigger "changed"
|
:hx-trigger "changed"
|
||||||
:hx-target "this"
|
:hx-target "this"
|
||||||
:hx-swap "innerHTML"}
|
:hx-swap "innerHTML"}
|
||||||
(com/date-input {:value (some-> (fc/field-value)
|
(com/date-input {:value (some-> (fc/field-value)
|
||||||
(atime/unparse-local atime/normal-date))
|
(atime/unparse-local atime/normal-date))
|
||||||
:name (fc/field-name)
|
:name (fc/field-name)
|
||||||
:error? (fc/field-errors)
|
:error? (fc/field-errors)
|
||||||
:placeholder "1/1/2024"})]]))
|
:placeholder "1/1/2024"})]]))
|
||||||
|
|
||||||
(fc/with-field :invoice/invoice-number
|
(fc/with-field :invoice/invoice-number
|
||||||
(com/validated-field
|
(com/validated-field
|
||||||
{:label "Invoice Number"
|
{:label "Invoice Number"
|
||||||
:errors (fc/field-errors)}
|
:errors (fc/field-errors)}
|
||||||
[:div {:class "w-24"}
|
[:div {:class "w-24"}
|
||||||
(com/text-input {:value (-> (fc/field-value))
|
(com/text-input {:value (-> (fc/field-value))
|
||||||
:disabled (boolean (-> request :multi-form-state :snapshot :db/id))
|
:disabled (boolean (-> request :multi-form-state :snapshot :db/id))
|
||||||
:name (fc/field-name)
|
:name (fc/field-name)
|
||||||
:error? (fc/field-errors)
|
:error? (fc/field-errors)
|
||||||
:placeholder "HA-123"})]))
|
:placeholder "HA-123"})]))
|
||||||
(fc/with-field :invoice/total
|
(fc/with-field :invoice/total
|
||||||
(com/validated-field
|
(com/validated-field
|
||||||
{:label "Total"
|
{:label "Total"
|
||||||
:errors (fc/field-errors)}
|
:errors (fc/field-errors)}
|
||||||
[:div {:class "w-16"}
|
[:div {:class "w-16"}
|
||||||
(com/money-input {:value (-> (fc/field-value))
|
(com/money-input {:value (-> (fc/field-value))
|
||||||
:name (fc/field-name)
|
:name (fc/field-name)
|
||||||
:class "w-24"
|
:class "w-24"
|
||||||
:error? (fc/field-errors)
|
:error? (fc/field-errors)
|
||||||
:placeholder "212.44"})]))
|
:placeholder "212.44"})]))
|
||||||
|
|
||||||
[:div#expense-account-prediction
|
[:div#expense-account-prediction
|
||||||
(hx/alpine-appear
|
(hx/alpine-appear
|
||||||
{:x-dispatch:bryce "[vendorId]"
|
{:x-dispatch:bryce "[vendorId]"
|
||||||
:hx-trigger "load, bryce"
|
:hx-trigger "bryce"
|
||||||
:hx-put (bidi.bidi/path-for ssr-routes/only-routes ::route/account-prediction)
|
:hx-put (bidi.bidi/path-for ssr-routes/only-routes ::route/account-prediction)
|
||||||
:hx-target "this"
|
:hx-target "this"
|
||||||
:hx-swap "innerHTML"})]])
|
:hx-swap "innerHTML"})
|
||||||
|
(account-prediction* request)]])
|
||||||
|
|
||||||
|
:footer
|
||||||
|
(mm/default-step-footer linear-wizard this :validation-route ::route/new-wizard-navigate
|
||||||
|
:next-button (com/button {:color :primary :x-ref "next" :class "w-32"
|
||||||
|
:hx-put (bidi.bidi/path-for ssr-routes/only-routes
|
||||||
|
::route/new-wizard-navigate)} "Save"))
|
||||||
|
:validation-route ::route/new-wizard-navigate)))
|
||||||
|
|
||||||
|
mm/CustomNext
|
||||||
|
(custom-next-handler
|
||||||
|
[_ request]
|
||||||
|
(if (= (get-in request [:multi-form-state :step-params :customize-accounts])
|
||||||
|
:customize)
|
||||||
|
(mm/navigate-handler {:request request
|
||||||
|
:to-step :accounts})
|
||||||
|
|
||||||
|
(html-response [:div]
|
||||||
|
:headers {"location" (bidi.bidi/path-for ssr-routes/only-routes ::route/new-invoice-submit)}
|
||||||
|
:status 308)
|
||||||
|
#_(mm/navigate-handler {:request request
|
||||||
|
:to-step :next-steps}))))
|
||||||
|
|
||||||
:footer
|
|
||||||
(mm/default-step-footer linear-wizard this :validation-route ::route/new-wizard-navigate
|
|
||||||
:next-button (com/button {:color :primary :x-ref "next" :class "w-32"} "Save"))
|
|
||||||
:validation-route ::route/new-wizard-navigate)))
|
|
||||||
|
|
||||||
(defn- location-select*
|
(defn- location-select*
|
||||||
[{:keys [name account-location client-locations value]}]
|
[{:keys [name account-location client-locations value]}]
|
||||||
@@ -467,8 +504,6 @@
|
|||||||
:invoice-expense-account/amount (:invoice/total (:step-params current))}])
|
:invoice-expense-account/amount (:invoice/total (:step-params current))}])
|
||||||
(:step-params current))))
|
(:step-params current))))
|
||||||
|
|
||||||
;; TODO closing should render the final row
|
|
||||||
|
|
||||||
(defrecord NextSteps [linear-wizard]
|
(defrecord NextSteps [linear-wizard]
|
||||||
mm/ModalWizardStep
|
mm/ModalWizardStep
|
||||||
(step-name [_]
|
(step-name [_]
|
||||||
@@ -587,45 +622,61 @@
|
|||||||
new-form-schema)
|
new-form-schema)
|
||||||
(submit [this {:keys [multi-form-state request-method identity] :as request}]
|
(submit [this {:keys [multi-form-state request-method identity] :as request}]
|
||||||
(let [invoice (:snapshot multi-form-state)
|
(let [invoice (:snapshot multi-form-state)
|
||||||
|
extant? (:db/id invoice)
|
||||||
client-id (->db-id (:invoice/client invoice))
|
client-id (->db-id (:invoice/client invoice))
|
||||||
vendor-id (->db-id (:invoice/vendor invoice))
|
vendor-id (->db-id (:invoice/vendor invoice))
|
||||||
transaction [:upsert-invoice (-> multi-form-state
|
transaction [:upsert-invoice (-> multi-form-state
|
||||||
:snapshot
|
:snapshot
|
||||||
(assoc :db/id (or (:db/id invoice) "invoice"))
|
(assoc :db/id (or (:db/id invoice) "invoice"))
|
||||||
(dissoc :customize-due-and-scheduled? :invoice/journal-entry :invoice/payments)
|
(dissoc :customize-due-and-scheduled? :invoice/journal-entry :invoice/payments :customize-accounts)
|
||||||
(assoc :invoice/expense-accounts (if-let [ieas (seq (-> multi-form-state :snapshot :invoice/expense-accounts))]
|
(assoc :invoice/expense-accounts (if (= :customize (:customize-accounts invoice))
|
||||||
ieas
|
(-> multi-form-state :step-params :invoice/expense-accounts)
|
||||||
[{:db/id "123"
|
[{:db/id "123"
|
||||||
:invoice-expense-account/location "Shared"
|
:invoice-expense-account/location "Shared"
|
||||||
:invoice-expense-account/account (:db/id (:vendor/default-account (clientize-vendor (get-vendor vendor-id)
|
:invoice-expense-account/account (:db/id (:vendor/default-account (clientize-vendor (get-vendor vendor-id)
|
||||||
client-id)))
|
client-id)))
|
||||||
:invoice-expense-account/amount (:invoice/total (:snapshot multi-form-state))}]))
|
:invoice-expense-account/amount (or (:invoice/total (:step-params multi-form-state))
|
||||||
|
(:invoice/total (:snapshot multi-form-state)))}]))
|
||||||
(assoc
|
(assoc
|
||||||
:invoice/outstanding-balance (:invoice/total (:snapshot multi-form-state))
|
:invoice/outstanding-balance (or
|
||||||
|
(:invoice/total (:step-params multi-form-state))
|
||||||
|
(:invoice/total (:snapshot multi-form-state)))
|
||||||
:invoice/import-status :import-status/imported
|
:invoice/import-status :import-status/imported
|
||||||
:invoice/status :invoice-status/unpaid)
|
:invoice/status :invoice-status/unpaid)
|
||||||
(maybe-spread-locations)
|
(maybe-spread-locations)
|
||||||
(update :invoice/date coerce/to-date)
|
(update :invoice/date coerce/to-date)
|
||||||
(update :invoice/due coerce/to-date))]]
|
(update :invoice/due coerce/to-date)
|
||||||
|
(update :invoice/scheduled-payment coerce/to-date))]]
|
||||||
(assert-invoice-amounts-add-up (second transaction))
|
(assert-invoice-amounts-add-up (second transaction))
|
||||||
(when-not (:db/id invoice)
|
(when-not extant?
|
||||||
(assert-no-conflicting invoice))
|
(assert-no-conflicting invoice))
|
||||||
(exception->4xx #(assert-can-see-client (:identity request) client-id))
|
(exception->4xx #(assert-can-see-client (:identity request) client-id))
|
||||||
|
|
||||||
(exception->4xx #(assert-not-locked client-id (:invoice/date invoice)))
|
(exception->4xx #(assert-not-locked client-id (:invoice/date invoice)))
|
||||||
(let [transaction-result (audit-transact [transaction] (:identity request))]
|
(let [transaction-result (audit-transact [transaction] (:identity request))]
|
||||||
(solr/touch-with-ledger (get-in transaction-result [:tempids "invoice"]))
|
(solr/touch-with-ledger (get-in transaction-result [:tempids "invoice"]))
|
||||||
(assoc-in (mm/render-navigate {:request (assoc-in request [:multi-form-state :snapshot :db/id] (get-in transaction-result [:tempids "invoice"]))
|
(if extant?
|
||||||
:to-step :next-steps})
|
|
||||||
[:headers "hx-trigger"] "invalidated")))))
|
(html-response
|
||||||
|
(@(resolve 'auto-ap.ssr.invoices/row*) identity (dc/pull (dc/db conn) default-read (:db/id invoice)) {:flash? true
|
||||||
|
:request request})
|
||||||
|
:headers (cond-> {"hx-trigger" "modalclose"
|
||||||
|
"hx-retarget" (format "#entity-table tr[data-id=\"%d\"]" (:db/id invoice))
|
||||||
|
"hx-reswap" "outerHTML"}))
|
||||||
|
|
||||||
|
(assoc-in (mm/navigate-handler {:request (assoc-in request [:multi-form-state :snapshot :db/id] (get-in transaction-result [:tempids "invoice"]))
|
||||||
|
:to-step :next-steps})
|
||||||
|
[:headers "hx-trigger"] "invalidated"))))))
|
||||||
|
|
||||||
(def new-wizard (->NewWizard2 nil nil))
|
(def new-wizard (->NewWizard2 nil nil))
|
||||||
|
|
||||||
|
|
||||||
(defn initial-new-wizard-state [request]
|
(defn initial-new-wizard-state [request]
|
||||||
(mm/->MultiStepFormState {:invoice/date (time/now)}
|
(mm/->MultiStepFormState {:invoice/date (time/now)
|
||||||
|
:customize-accounts :default}
|
||||||
[]
|
[]
|
||||||
{:invoice/date (time/now)}))
|
{:invoice/date (time/now)
|
||||||
|
:customize-accounts :default}))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -634,9 +685,11 @@
|
|||||||
(let [entity (dc/pull (dc/db conn) default-read (:db/id (:route-params request)))
|
(let [entity (dc/pull (dc/db conn) default-read (:db/id (:route-params request)))
|
||||||
entity (select-keys entity (mut/keys new-form-schema))]
|
entity (select-keys entity (mut/keys new-form-schema))]
|
||||||
|
|
||||||
(mm/->MultiStepFormState entity
|
(mm/->MultiStepFormState (assoc entity
|
||||||
|
:customize-accounts :customize)
|
||||||
[]
|
[]
|
||||||
entity)))
|
(assoc entity
|
||||||
|
:customize-accounts :customize))))
|
||||||
|
|
||||||
(defn location-select [{{:keys [name account-id client-id value] :as qp} :query-params}]
|
(defn location-select [{{:keys [name account-id client-id value] :as qp} :query-params}]
|
||||||
(html-response (location-select* {:name name
|
(html-response (location-select* {:name name
|
||||||
@@ -683,27 +736,10 @@
|
|||||||
:error? false
|
:error? false
|
||||||
:placeholder "1/1/2024"}))))
|
:placeholder "1/1/2024"}))))
|
||||||
|
|
||||||
(defn account-prediction [{:keys [multi-form-state form-errors]}]
|
|
||||||
(let [vendor (clientize-vendor (get-vendor (:invoice/vendor (:step-params multi-form-state)))
|
|
||||||
(->db-id (:invoice/client (:step-params multi-form-state))))]
|
|
||||||
|
|
||||||
(html-response
|
(defn account-prediction [{:keys [multi-form-state form-errors] :as request}]
|
||||||
(if-let [account-name (:account/name (:vendor/default-account vendor))]
|
(html-response
|
||||||
[:div {:class "transition duration-300 ease-in-out htmx-added:opacity-0 opacity-100"}
|
(account-prediction* request)))
|
||||||
[:label {:class "block mb-2 text-sm font-medium text-gray-900 dark:text-white"} "Default expense account"]
|
|
||||||
[:div.flex.gap-2.items-center (com/pill {:color :primary} account-name)
|
|
||||||
(com/validated-save-button (cond-> {:errors form-errors
|
|
||||||
:color :secondary
|
|
||||||
;;:x-data (hx/json {})
|
|
||||||
:class "w-24"
|
|
||||||
:hx-put (hu/url (bidi/path-for ssr-routes/only-routes ::route/new-wizard-navigate)
|
|
||||||
{:from (mm/encode-step-key :basic-details)
|
|
||||||
:to (mm/encode-step-key :accounts)})
|
|
||||||
:hx-target "closest form"})
|
|
||||||
|
|
||||||
"Change"
|
|
||||||
[:div.w-5.h-5 svg/arrow-right])]]
|
|
||||||
[:div]))))
|
|
||||||
|
|
||||||
(def key->handler
|
(def key->handler
|
||||||
(apply-middleware-to-all-handlers
|
(apply-middleware-to-all-handlers
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
"/voided" ::voided-page}
|
"/voided" ::voided-page}
|
||||||
"/new" {:get ::new-wizard
|
"/new" {:get ::new-wizard
|
||||||
:post ::new-invoice-submit
|
:post ::new-invoice-submit
|
||||||
|
:put ::new-invoice-submit
|
||||||
"/due-date" ::due-date
|
"/due-date" ::due-date
|
||||||
"/scheduled-payment-date" ::scheduled-payment-date
|
"/scheduled-payment-date" ::scheduled-payment-date
|
||||||
"/navigate" ::new-wizard-navigate
|
"/navigate" ::new-wizard-navigate
|
||||||
|
|||||||
Reference in New Issue
Block a user