Files
integreat/src/cljs/auto_ap/views/components/vendor_dialog.cljs

555 lines
27 KiB
Clojure

(ns auto-ap.views.components.vendor-dialog
(:require
[auto-ap.entities.contact :as contact]
[auto-ap.entities.vendors :as entity]
[auto-ap.forms :as forms]
[auto-ap.status :as status]
[auto-ap.subs :as subs]
[auto-ap.views.components.address :refer [address-field]]
[auto-ap.views.components.typeahead.vendor
:refer [search-backed-typeahead]]
[auto-ap.views.components.modal :as modal]
[auto-ap.views.components.typeahead :refer [typeahead-v3]]
[auto-ap.views.pages.admin.vendors.common :as common]
[auto-ap.views.utils
:refer [bind-field
dispatch-event
horizontal-field
with-is-admin?
with-user]]
[clojure.spec.alpha :as s]
[re-frame.core :as re-frame]))
(re-frame/reg-sub
::can-submit
:<- [::forms/form ::vendor-form]
(fn [form]
(s/valid? ::entity/vendor (:data form))))
(re-frame/reg-event-db
::settled
[(forms/in-form ::vendor-form)]
(fn [form _]
form))
(re-frame/reg-event-db
::removed-override
[(forms/in-form ::vendor-form)]
(fn [form [_ override-key index]]
(update-in form [:data override-key]
(fn [overrides]
(reduce
(fn [overrides [i override]]
(if (= i index)
overrides
(conj overrides override)))
[]
(map vector (range) overrides))))))
(re-frame/reg-event-db
::changed
[(forms/settles {:key ::vendor-form
:time 500
:event [::settled]})]
(forms/change-handler
::vendor-form
(fn [data field _]
(let [[override-key? i?] field]
(if (and (#{:account-overrides :terms-overrides :schedule-payment-dom} override-key?)
(nil? (get-in data [override-key? i? :key])))
[[override-key? i? :key] (random-uuid)]
[])))))
(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} _]
(when (s/valid? ::entity/vendor data)
{ :graphql
{:token user
:owns-state {:single ::vendor-form}
:query-obj {:venia/operation {:operation/type :mutation
:operation/name "UpsertVendor"} :venia/queries [{:query/data [:upsert-vendor
{:vendor (cond-> {:id id
:name name
:print-as print-as
:terms terms
: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 override id]}]
{:id id
:client-id (:id client)
:terms override})
terms-overrides)
:account-overrides (mapv
(fn [{:keys [client override id]}]
{:id id
:client-id (:id client)
:account-id (:id override)})
account-overrides)
:schedule-payment-dom (mapv
(fn [{:keys [client override id]}]
{:id id
:client-id (:id client)
:dom override})
schedule-payment-dom)
:automatically-paid-when-due (mapv
:id
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]}]}
:on-success [::save-complete]}})))
(defn client-list [{:keys [override-key change-event data]}]
(let [clients @(re-frame/subscribe [::subs/clients])
is-admin? @(re-frame/subscribe [::subs/is-admin?])]
(when is-admin?
[horizontal-field
[:label.label "Client"]
(doall
(for [[i override] (map vector (range) (conj (override-key data) {:key (random-uuid)}))]
^{:key (or
(:id override)
(:key override))}
[:div.columns
[:div.column
[bind-field
[typeahead-v3 {:entities clients
:entity->text :name
:type "typeahead-v3"
:field [override-key i]
:event change-event
:subscription data}]]]
[:div.column.is-1
[:a.button {:on-click (dispatch-event [::removed-override override-key i])} [:span.icon [:span.icon-remove]]]]]))])))
(defn default-with-overrides [{:keys [override-key change-event default-key data mandatory?]} template]
(let [clients @(re-frame/subscribe [::subs/clients])
is-admin? @(re-frame/subscribe [::subs/is-admin?])]
[:div
[horizontal-field
[:label.label [:span "Default"
(when mandatory?
[:span.has-text-danger " *"])]]
[bind-field
(template default-key nil)
#_(assoc-in template [1 :field ] default-key)]]
(when is-admin?
[horizontal-field
[:label.label "Overrides"]
(doall
(for [[i override] (map vector (range) (conj (override-key data) {:key (random-uuid)}))]
^{:key (or
(:id override)
(:key override))}
[:div.columns
[:div.column
[bind-field
[typeahead-v3 {:entities clients
:entity->text :name
:type "typeahead-v3"
:field [override-key i :client]
:event change-event
:subscription data}]]]
[:div.column
[bind-field
(template
[override-key i :override]
(get-in data [override-key i :client])
)]]
[:div.column.is-1
[:a.button {:on-click (dispatch-event [::removed-override override-key i])} [:span.icon [:span.icon-remove]]]]]))])]))
(defn client-overrides [{:keys [override-key change-event data]} template]
(let [clients @(re-frame/subscribe [::subs/clients])
is-admin? @(re-frame/subscribe [::subs/is-admin?])]
(when is-admin?
[horizontal-field
[:label.label "Overrides"]
(doall
(for [[i override] (map vector (range) (conj (override-key data) {:key (random-uuid)}))]
^{:key (or
(:id override)
(:key override))}
[:div.columns
[:div.column
[bind-field
[typeahead-v3 {:entities clients
:entity->text :name
:type "typeahead-v3"
:field [override-key i :client]
:event change-event
:subscription data}]]]
[:div.column
[bind-field
(template
[override-key i :override]
(get-in data [override-key i :client])
)]]
[:div.column.is-1
[:a.button {:on-click (dispatch-event [::removed-override override-key i])} [:span.icon [:span.icon-remove]]]]]))])))
(defn form-content [{:keys [data change-event]}]
(let [is-admin? @(re-frame/subscribe [::subs/is-admin?])]
[:div
[horizontal-field
[:label.label [:span "Name "
[:span.has-text-danger "*"]]]
[:div.control
[bind-field
[:input.input {:type "text"
:auto-focus true
:field :name
:spec ::entity/name
:event change-event
:subscription data}]]]]
[horizontal-field
[:label.label "Print Checks As"]
[:div.control
[bind-field
[:input.input {:type "text"
:field :print-as
:spec ::entity/print-as
:event change-event
:subscription data}]]]]
(when is-admin?
[horizontal-field
[:label.label "Hidden"]
[:div.control
[bind-field
[:input {:type "checkbox"
:field :hidden
:spec ::entity/hidden
:event change-event
:subscription data}]]]])
[:h2.subtitle "Terms"]
[default-with-overrides {:data data :change-event change-event
:default-key :terms
:override-key :terms-overrides}
(fn [field _]
[:input.input {:type "number"
:step "1"
:style {:width "4em"}
:field field
:size 3
:spec ::entity/terms
:event change-event
:subscription data}])]
(when is-admin?
[:h2.subtitle "Schedule payment when due"])
[client-list {:data data :change-event change-event
:override-key :automatically-paid-when-due}]
(when is-admin?
[:h2.subtitle "Schedule payment on day of month"])
[client-overrides {:data data :change-event change-event
:mandatory? true
:override-key :schedule-payment-dom}
(fn [field _]
[:input.input {:type "number"
:step "1"
:style {:width "5em"}
:field field
:size 3
:spec ::entity/dom
:event change-event
:subscription data}])]
[:h2.subtitle "Expense Accounts"]
[default-with-overrides {:data data :change-event change-event
:mandatory? true
:default-key :default-account
:override-key :account-overrides}
(fn [field client]
[search-backed-typeahead {:search-query (fn [i]
[:search_account
{:query i
:client-id (:id client)}
[:name :id]])
:type "typeahead-v3"
:field field
:event change-event
:subscription data}])]
[:h2.subtitle "Address"]
[address-field {:field [:address]
:event change-event
:subscription data}]
[:h2.subtitle "Contact"]
[horizontal-field
[:label.label "Primary"]
[:div.control.has-icons-left
[bind-field
[:input.input.is-expanded {:type "text"
:field [:primary-contact :name]
:spec ::contact/name
:event change-event
:subscription data}]]
[:span.icon.is-small.is-left
[:i.fa.fa-user]]]
[:div.control.has-icons-left
[:span.icon.is-small.is-left
[:i.fa.fa-envelope]]
[bind-field
[:input.input {:type "email"
:field [:primary-contact :email]
:spec ::contact/email
:event change-event
:subscription data}]]]
[:div.control.has-icons-left
[bind-field
[:input.input {:type "phone"
:field [:primary-contact :phone]
:spec ::contact/phone
:event change-event
:subscription data}]]
[:span.icon.is-small.is-left
[:i.fa.fa-phone]]]]
[horizontal-field
[:label.label "Secondary"]
[:div.control.has-icons-left
[bind-field
[:input.input.is-expanded {:type "text"
:field [:secondary-contact :name]
:spec ::contact/name
:event change-event
:subscription data}]]
[:span.icon.is-small.is-left
[:i.fa.fa-user]]]
[:div.control.has-icons-left
[:span.icon.is-small.is-left
[:i.fa.fa-envelope]]
[bind-field
[:input.input {:type "email"
:field [:secondary-contact :email]
:spec ::contact/email
:event change-event
:subscription data}]]]
[:div.control.has-icons-left
[bind-field
[:input.input {:type "phone"
:field [:secondary-contact :phone]
:spec ::contact/phone
:event change-event
:subscription data}]]
[:span.icon.is-small.is-left
[:i.fa.fa-phone]]]]
[horizontal-field
[:label.label "Invoice Reminders"]
[:div.control
[:label.radio
[bind-field
[:input {:type "radio"
:name "schedule"
:value "Weekly"
:field :invoice-reminder-schedule
:spec ::entity/invoice-reminder-schedule
:event change-event
:subscription data}]]
" Send weekly"]
[:label.radio
[bind-field
[:input {:type "radio"
:name "schedule"
:value "Never"
:field :invoice-reminder-schedule
:spec ::entity/invoice-reminder-schedule
:event change-event
:subscription data}]]
" Never"]]]
[:h2.subtitle "Legal Entity"]
[horizontal-field
[:label.label "Name"]
[:div.control
[bind-field
[:input.input {:type "text"
:placeholder "First Name"
:field [:legal-entity-first-name]
:spec ::contact/name
:event change-event
:subscription data}]]]
[:div.control
[bind-field
[:input.input {:type "text"
:placeholder "Middle Name"
:field [:legal-entity-middle-name]
:spec ::contact/name
:event change-event
:subscription data}]]]
[:div.control
[bind-field
[:input.input {:type "text"
:placeholder "Last Name"
:field [:legal-entity-last-name]
:spec ::contact/name
:event change-event
:subscription data}]]]]
[horizontal-field
[:label.label "TIN"]
[:div.control
[bind-field
[:input.input {:type "text"
:placeholder "SSN or EIN"
:field [:legal-entity-tin]
:size "12"
:spec ::contact/name
:event change-event
:subscription data}]]]
[:div.control
[:div.select
[bind-field
[:select {:type "select"
:field [:legal-entity-tin-type]
:event change-event
:subscription data}
[:option {:value nil} ""]
[:option {:value "ein"} "EIN"]
[:option {:value "ssn"} "SSN"]]]]]]
[horizontal-field
[:label.label "1099 Type"]
[:div.control
[:div.select
[bind-field
[:select {:type "select"
:field [:legal-entity-1099-type]
:event change-event
:subscription data}
[:option {:value nil} ""]
[:option {:value "none"} "Don't 1099"]
[:option {:value "misc"} "Misc"]
[:option {:value "landlord"} "Landlord"]]]]]]]))
(defn vendor-dialog [ ]
(let [{:keys [data]} @(re-frame/subscribe [::forms/form ::vendor-form])
change-event [::changed]]
[:div
[form-content {:data data :change-event change-event}]]))
(re-frame/reg-event-fx
::vendor-selected
[with-user (forms/in-form ::select-vendor-form)]
(fn [{{:keys [data]} :db :keys [user]} _]
{: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]
(println (:vendor-by-id r))
[::started (:vendor-by-id r)])}}))
(re-frame/reg-sub
::can-submit-select-vendor-form
:<- [::forms/field ::select-vendor-form [:vendor]]
(fn [vendor]
(if vendor
true
false)))
(def select-vendor-form (forms/vertical-form {:submit-event [::vendor-selected]
:change-event [::forms/change ::select-vendor-form]
:can-submit [::can-submit-select-vendor-form]
:id ::select-vendor-form}))
(defn select-vendor-form-content []
(let [_ @(re-frame/subscribe [::forms/form ::select-vendor-form])
{:keys [form-inline field]} select-vendor-form]
(form-inline {}
[:<>
(field "Vendor to edit"
[search-backed-typeahead {:search-query (fn [i]
[:search_vendor
{:query i}
[:name :id]])
:type "typeahead-v3"
:auto-focus true
:field [:vendor]}])])))
(re-frame/reg-event-fx
::started
(fn [{:keys [db]} [_ vendor]]
{:db (-> db (forms/start-form ::vendor-form (-> vendor
(update :account-overrides #(mapv
(fn [ao]
{:id (:id ao)
:client (:client ao)
:override (:account ao)})
%))
(update :schedule-payment-dom #(mapv
(fn [spdom]
{:id (:id spdom)
:client (:client spdom)
:override (:dom spdom)})
%))
(update :terms-overrides #(mapv
(fn [to]
{:id (:id to)
:client (:client to)
:override (:terms to)})
%))
(update :automatically-paid-when-due #(mapv identity %))
(update :hidden #(if (nil? %)
false
%)))))
:dispatch [::modal/modal-requested
{:title "Vendor"
:class "is-wide"
:body [vendor-dialog]
:confirm {:value "Save Vendor"
:status-from [::status/single ::vendor-form]
:class "is-primary"
:on-click (dispatch-event [::save])
:can-submit [::can-submit]
: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"
:class "is-wide"
: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])
:can-submit [::can-submit-select-vendor-form]
:close-event [::status/completed ::select-vendor-form]}}]}))