386 lines
19 KiB
Clojure
386 lines
19 KiB
Clojure
(ns auto-ap.views.components.vendor-dialog
|
|
(:require
|
|
[auto-ap.forms :as forms]
|
|
[auto-ap.forms.builder :as form-builder]
|
|
[auto-ap.schema :as schema]
|
|
[auto-ap.status :as status]
|
|
[auto-ap.subs :as subs]
|
|
[auto-ap.views.components :as com]
|
|
[auto-ap.views.components.address :refer [address2-field]]
|
|
[auto-ap.views.components.level :refer [left-stack]]
|
|
[auto-ap.views.components.modal :as modal]
|
|
[auto-ap.views.components.multi :refer [multi-field-v2]]
|
|
[auto-ap.views.components.number :refer [number-input]]
|
|
[auto-ap.views.components.typeahead :refer [typeahead-v3]]
|
|
[auto-ap.views.components.typeahead.vendor
|
|
:refer [search-backed-typeahead]]
|
|
[auto-ap.views.pages.admin.vendors.common :as common]
|
|
[auto-ap.views.utils
|
|
:refer [dispatch-event str->int with-is-admin? with-user]]
|
|
[malli.core :as m]
|
|
[re-frame.core :as re-frame]))
|
|
|
|
;; Remaining cleanup todos:
|
|
;; test minification
|
|
|
|
(def terms-override-schema (m/schema [:map
|
|
[:client schema/reference]
|
|
[:terms :int]]))
|
|
|
|
(def automatically-paid-schema (m/schema [:map
|
|
[:client schema/reference]]))
|
|
|
|
(def schedule-payment-dom-schema (m/schema [:map
|
|
[:client schema/reference]
|
|
[:dom [:int {:max 30}]]]))
|
|
|
|
(def account-override-schema (m/schema [:map
|
|
[:client schema/reference]
|
|
[:account schema/reference]]))
|
|
|
|
(def schema (m/schema [:map [:name schema/not-empty-string]
|
|
[:print-as {:optional true}
|
|
[:maybe :string]]
|
|
[:hidden {:optional true}
|
|
[:maybe :boolean]]
|
|
[:terms {:optional true}
|
|
[:maybe :int]]
|
|
[:terms-overrides {:optional true}
|
|
[:maybe [:sequential terms-override-schema]]]
|
|
[:schedule-payment-dom {:optional true}
|
|
[:maybe [:sequential schedule-payment-dom-schema]]]
|
|
[:default-account schema/reference]
|
|
[:account-overrides {:optional true}
|
|
[:sequential account-override-schema]]
|
|
[:legal-entity-first-name {:optional true}
|
|
[:maybe :string]]
|
|
[:legal-entity-middle-name {:optional true}
|
|
[:maybe :string]]
|
|
[:legal-entity-last-name {:optional true}
|
|
[:maybe :string]]
|
|
[:legal-entity-tin {:optional true}
|
|
[:maybe :string]]
|
|
[:legal-entity-tin-type {:optional true}
|
|
[:or [:maybe :string]
|
|
[:maybe keyword?]]]
|
|
[:legal-entity-1099-type {:optional true}
|
|
[:or [:maybe :string]
|
|
[:maybe keyword?]]]]))
|
|
|
|
(re-frame/reg-event-fx
|
|
::save-complete
|
|
[(forms/triggers-stop ::vendor-form)]
|
|
(fn [_ [_ _ ]]
|
|
{:dispatch [::modal/modal-closed ]}))
|
|
|
|
(re-frame/reg-event-fx
|
|
::save
|
|
[with-user with-is-admin? (forms/triggers-loading ::vendor-form) (forms/in-form ::vendor-form)]
|
|
(fn [{:keys [user is-admin?] {{:keys [name hidden print-as terms invoice-reminder-schedule primary-contact automatically-paid-when-due schedule-payment-dom secondary-contact address default-account terms-overrides account-overrides id legal-entity-tin legal-entity-tin-type legal-entity-first-name legal-entity-last-name legal-entity-middle-name legal-entity-1099-type] :as data} :data} :db} _]
|
|
(if (m/validate schema data)
|
|
(let [query [:upsert-vendor
|
|
{:vendor (cond-> {:id id
|
|
:name name
|
|
:print-as print-as
|
|
:terms (or terms
|
|
nil)
|
|
:default-account-id (:id default-account)
|
|
:address address
|
|
:primary-contact primary-contact
|
|
:secondary-contact secondary-contact
|
|
:invoice-reminder-schedule invoice-reminder-schedule}
|
|
is-admin? (assoc :hidden hidden
|
|
:terms-overrides (mapv
|
|
(fn [{:keys [client terms id]}]
|
|
{:id id
|
|
:client-id (:id client)
|
|
:terms (or (str->int terms) 0)})
|
|
terms-overrides)
|
|
:account-overrides (mapv
|
|
(fn [{:keys [client account id]}]
|
|
{:id id
|
|
:client-id (:id client)
|
|
:account-id (:id account)})
|
|
account-overrides)
|
|
:schedule-payment-dom (mapv
|
|
(fn [{:keys [client dom id]}]
|
|
{:id id
|
|
:client-id (:id client)
|
|
:dom (or (str->int dom)
|
|
0)})
|
|
schedule-payment-dom)
|
|
:automatically-paid-when-due (mapv
|
|
(comp :id :client)
|
|
automatically-paid-when-due)
|
|
:legal-entity-first-name legal-entity-first-name
|
|
:legal-entity-middle-name legal-entity-middle-name
|
|
:legal-entity-last-name legal-entity-last-name
|
|
:legal-entity-tin legal-entity-tin
|
|
:legal-entity-tin-type (some-> legal-entity-tin-type clojure.core/name not-empty keyword)
|
|
:legal-entity-1099-type (some-> legal-entity-1099-type clojure.core/name not-empty keyword)))}
|
|
common/default-read]]
|
|
{ :graphql
|
|
{:token user
|
|
:owns-state {:single ::vendor-form}
|
|
:query-obj {:venia/operation
|
|
{:operation/type :mutation
|
|
:operation/name "UpsertVendor"} :venia/queries [{:query/data query}]}
|
|
:on-success [::save-complete]}})
|
|
|
|
{:dispatch-n [[::forms/attempted-submit ::vendor-form]
|
|
[::status/error ::vendor-form [{:message "Please fix the errors and try again."}]]]})))
|
|
|
|
(defn contact-field [{:keys [name field]}]
|
|
[form-builder/with-scope {:scope field}
|
|
[form-builder/vertical-control
|
|
name
|
|
[left-stack
|
|
[form-builder/vertical-control {:is-small? true}
|
|
"Name"
|
|
[:div.control.has-icons-left
|
|
[form-builder/raw-field-v2 {:field :name}
|
|
[:input.input.is-expanded {:type "text"}]]
|
|
[:span.icon.is-small.is-left
|
|
[:i.fa.fa-user]]]]
|
|
[form-builder/vertical-control {:is-small? true}
|
|
"Email"
|
|
|
|
[:div.control.has-icons-left
|
|
[:span.icon.is-small.is-left
|
|
[:i.fa.fa-envelope]]
|
|
[form-builder/raw-field-v2 {:field :email}
|
|
[:input.input {:type "email"}]]]]
|
|
[form-builder/vertical-control {:is-small? true}
|
|
"Phone"
|
|
[:div.control.has-icons-left
|
|
[form-builder/raw-field-v2 {:field :phone}
|
|
[:input.input {:type "phone"}]]
|
|
[:span.icon.is-small.is-left
|
|
[:i.fa.fa-phone]]]]]]])
|
|
|
|
|
|
|
|
(defn form-content []
|
|
(let [is-admin? @(re-frame/subscribe [::subs/is-admin?])
|
|
clients @(re-frame/subscribe [::subs/client-refs])]
|
|
[form-builder/builder {:submit-event [::save]
|
|
:id ::vendor-form
|
|
:schema schema}
|
|
[form-builder/field-v2 {:field :name
|
|
:required true}
|
|
"Name"
|
|
[:input.input {:auto-focus true}]]
|
|
|
|
[form-builder/field-v2 {:field :print-as}
|
|
"Print Checks As"
|
|
[:input.input]]
|
|
(when is-admin?
|
|
[form-builder/raw-field-v2 {:field :hidden}
|
|
[com/checkbox {:label "Hidden"}]])
|
|
|
|
[form-builder/section {:title "Terms"}
|
|
[form-builder/field-v2 {:field :terms}
|
|
"Terms"
|
|
[number-input ]]
|
|
(when is-admin?
|
|
[form-builder/field-v2 {:field [:terms-overrides]}
|
|
"Overrides"
|
|
[multi-field-v2 {:template [[form-builder/raw-field-v2 {:field [:client]}
|
|
[typeahead-v3 {:entities clients
|
|
:entity->text :name
|
|
:style {:width "13em"}
|
|
:type "typeahead-v3"
|
|
}]]
|
|
[form-builder/raw-field-v2 {:field :terms}
|
|
[number-input]]]
|
|
:schema [:sequential terms-override-schema]
|
|
:key-fn :id
|
|
:next-key (random-uuid)
|
|
:new-text "New Terms Override"}]])]
|
|
|
|
(when is-admin?
|
|
[form-builder/section {:title "Schedule payment when due"}
|
|
[form-builder/field-v2 {:field [:automatically-paid-when-due]}
|
|
"Client"
|
|
[multi-field-v2 {:template [[form-builder/raw-field-v2 {:field :client}
|
|
[typeahead-v3 {:entities clients
|
|
:entity->text :name
|
|
:style {:width "13em"}}]]]
|
|
:schema [:sequential automatically-paid-schema]
|
|
:key-fn :id
|
|
:next-key (random-uuid)
|
|
:new-text "Schedule another client"}]]])
|
|
|
|
(when is-admin?
|
|
[form-builder/section {:title "Schedule payment on day of month"}
|
|
[form-builder/field-v2 {:field [:schedule-payment-dom]}
|
|
"Overrides"
|
|
[multi-field-v2 {:template [[form-builder/raw-field-v2 {:field :client}
|
|
[typeahead-v3 {:entities clients
|
|
:entity->text :name
|
|
:style {:width "13em"}}]]
|
|
[form-builder/raw-field-v2 {:field :dom}
|
|
[number-input]]]
|
|
:schema [:sequential schedule-payment-dom-schema]
|
|
:key-fn :id
|
|
:next-key (random-uuid)
|
|
:new-text "Schedule another client"}]]])
|
|
[form-builder/section {:title "Expense Accounts"}
|
|
[form-builder/field-v2 {:field :default-account
|
|
:required? true}
|
|
"Default"
|
|
[search-backed-typeahead {:search-query (fn [i]
|
|
[:search_account
|
|
{:query i}
|
|
[:name :id]])
|
|
:style {:width "19em"}}]]
|
|
|
|
(when is-admin?
|
|
[form-builder/field-v2 {:field [:account-overrides]}
|
|
"Overrides"
|
|
[multi-field-v2 {:template (fn [entity]
|
|
[[form-builder/raw-field-v2 {:field :client}
|
|
[typeahead-v3 {:entities clients
|
|
:entity->text :name
|
|
:style {:width "19em"}
|
|
}]]
|
|
[form-builder/raw-field-v2 {:field :account}
|
|
[search-backed-typeahead {:search-query (fn [i]
|
|
[:search_account
|
|
{:query i
|
|
:client_id (:id (:client entity))}
|
|
[:name :id]])
|
|
:style {:width "15em"}}]]])
|
|
:schema [:sequential account-override-schema]
|
|
:key-fn :id
|
|
:next-key (random-uuid)
|
|
:new-text "Add override"}]])]
|
|
|
|
[form-builder/section {:title "Address"}
|
|
[:div {:style {:width "30em"}}
|
|
[form-builder/raw-field-v2 {:field :address}
|
|
[address2-field]]]]
|
|
|
|
[form-builder/section {:title "Contact"}
|
|
[contact-field {:name "Primary"
|
|
:field [:primary-contact]}]
|
|
[contact-field {:name "Secondary"
|
|
:field [:secondary-contact]}]]
|
|
|
|
(when is-admin?
|
|
[form-builder/section {:title "Legal Entity"}
|
|
[form-builder/vertical-control
|
|
"Name"
|
|
[left-stack
|
|
[:div.control
|
|
[form-builder/raw-field-v2 {:field :legal-entity-first-name}
|
|
[:input.input {:type "text"
|
|
:placeholder "First Name"}]]]
|
|
|
|
[:div.control
|
|
[form-builder/raw-field-v2 {:field :legal-entity-middle-name}
|
|
[:input.input {:type "text"
|
|
:placeholder "Middle Name"}]]]
|
|
|
|
[:div.control
|
|
[form-builder/raw-field-v2 {:field :legal-entity-last-name}
|
|
[:input.input {:type "text"
|
|
:placeholder "Last Name"}]]]]]
|
|
[form-builder/vertical-control
|
|
"TIN"
|
|
[left-stack
|
|
[form-builder/raw-field-v2 {:field :legal-entity-tin}
|
|
[:input.input {:type "text"
|
|
:placeholder "SSN or EIN"
|
|
:size "12"
|
|
}]]
|
|
|
|
[:div.control
|
|
[form-builder/raw-field-v2 {:field :legal-entity-tin-type}
|
|
[com/select-field {:options [["ein" "EIN"]
|
|
["ssn" "SSN"]]
|
|
:allow-nil? true}]]]]]
|
|
|
|
[form-builder/field-v2 {:field :legal-entity-1099-type}
|
|
"1099 Type"
|
|
[com/select-field {:options [["none" "Don't 1099"]
|
|
["misc" "Misc"]
|
|
["landlord" "Landlord"]]
|
|
:allow-nil? true}]]])
|
|
[form-builder/hidden-submit-button]]))
|
|
|
|
(defn vendor-dialog [ ]
|
|
(let [{:keys [data]} @(re-frame/subscribe [::forms/form ::vendor-form])]
|
|
[:div
|
|
[form-content {:data data}]]))
|
|
|
|
(re-frame/reg-event-fx
|
|
::vendor-selected
|
|
[with-user (forms/in-form ::select-vendor-form)]
|
|
(fn [{{:keys [data]} :db :keys [user]} _]
|
|
(if (:vendor data)
|
|
{:graphql {:token user
|
|
:query-obj {:venia/queries [[:vendor-by-id
|
|
{:id (:id (:vendor data))}
|
|
common/default-read]]}
|
|
:owns-state {:single ::select-vendor-form}
|
|
:on-success (fn [r]
|
|
[::started (:vendor-by-id r)])}}
|
|
{:dispatch-n [[::forms/attempted-submit ::select-vendor-form]
|
|
[::status/error ::select-vendor-form [{:message "Please select a vendor."}]]]})))
|
|
|
|
(defn select-vendor-form-content []
|
|
[form-builder/builder {:submit-event [::vendor-selected]
|
|
:id ::select-vendor-form
|
|
:validation-error-string "Please select a vendor."
|
|
:schema [:map
|
|
[:vendor schema/reference]]}
|
|
[form-builder/field-v2 {:field :vendor
|
|
:required? true}
|
|
"Vendor to edit"
|
|
[search-backed-typeahead {:search-query (fn [i]
|
|
[:search_vendor
|
|
{:query i}
|
|
[:name :id]])
|
|
:style {:width "20em"}
|
|
:auto-focus true}]]
|
|
[form-builder/hidden-submit-button]])
|
|
|
|
|
|
|
|
(re-frame/reg-event-fx
|
|
::started
|
|
(fn [{:keys [db]} [_ vendor]]
|
|
{:db (-> db (forms/start-form ::vendor-form (-> vendor
|
|
(update :automatically-paid-when-due #(mapv (fn [apwd]
|
|
apwd
|
|
{:id (:id apwd)
|
|
:client apwd})
|
|
%))
|
|
(update :hidden #(if (nil? %)
|
|
false
|
|
%)))))
|
|
:dispatch [::modal/modal-requested
|
|
{:title "Vendor"
|
|
:body [vendor-dialog]
|
|
:class "semi-wide"
|
|
:status-from [::status/single ::vendor-form]
|
|
:confirm {:value "Save Vendor"
|
|
:status-from [::status/single ::vendor-form]
|
|
:class "is-primary"
|
|
:on-click (dispatch-event [::save])
|
|
:close-event [::status/completed ::vendor-form]}}]}))
|
|
|
|
(re-frame/reg-event-fx
|
|
::edit
|
|
(fn [{:keys [db]} [_]]
|
|
{:db (-> db (forms/start-form ::select-vendor-form {}))
|
|
:dispatch [::modal/modal-requested
|
|
{:title "Select Vendor"
|
|
:body [select-vendor-form-content]
|
|
:confirm {:value "Choose a vendor"
|
|
:status-from [::status/single ::select-vendor-form]
|
|
:class "is-primary"
|
|
:on-click (dispatch-event [::vendor-selected])
|
|
:close-event [::status/completed ::select-vendor-form]}}]}))
|