From e897443f7dd95de6fc9fe229c2909e06dfb09815 Mon Sep 17 00:00:00 2001 From: Bryce Covert Date: Sun, 10 Apr 2022 20:28:02 -0700 Subject: [PATCH 01/12] vendor querying step 1. --- dev.cljs.edn | 3 +- src/clj/auto_ap/datomic/migrate/vendors.clj | 22 ++- src/clj/auto_ap/datomic/vendors.clj | 12 ++ src/clj/auto_ap/graphql.clj | 17 +- src/clj/auto_ap/graphql/vendors.clj | 18 ++ src/cljs/auto_ap/forms.cljs | 7 + .../components/expense_accounts_field.cljs | 3 +- .../views/components/typeahead/vendor.cljs | 134 +++++++++++++++ .../auto_ap/views/pages/invoices/form.cljs | 159 +++++++++++------- 9 files changed, 308 insertions(+), 67 deletions(-) create mode 100644 src/cljs/auto_ap/views/components/typeahead/vendor.cljs diff --git a/dev.cljs.edn b/dev.cljs.edn index 1ab2394f..839d1ef3 100644 --- a/dev.cljs.edn +++ b/dev.cljs.edn @@ -15,5 +15,4 @@ "--output-filename" :final-output-filename] :default ["npx" "webpack" "--mode=production" :output-to "--output-path" :final-output-dir - "--output-filename" :final-output-filename]} - } + "--output-filename" :final-output-filename]}} diff --git a/src/clj/auto_ap/datomic/migrate/vendors.clj b/src/clj/auto_ap/datomic/migrate/vendors.clj index 946b7dc6..49a0e45e 100644 --- a/src/clj/auto_ap/datomic/migrate/vendors.clj +++ b/src/clj/auto_ap/datomic/migrate/vendors.clj @@ -1,6 +1,14 @@ (ns auto-ap.datomic.migrate.vendors - (:require [datomic.api :as d] - [auto-ap.datomic :refer [uri]])) + (:require [datomic.api :as d])) + +(defn add-vendor-search-terms [conn] + [(->> (d/q '[:find ?i ?n + :in $ + :where [?i :vendor/name ?n]] + (d/db conn)) + (map (fn [[i n]] + {:db/id i + :vendor/search-terms n})))]) (def norms-map {:add-1099-stuff {:txes [[{:db/ident :vendor/legal-entity-first-name :db/doc "The first name for the legal entity" @@ -56,7 +64,15 @@ :db/valueType :db.type/long :db/cardinality :db.cardinality/one :db/noHistory true}] - ]}}) + ]} + ::make-fulltext-search {:txes [[{:db/ident :vendor/search-terms + :db/valueType :db.type/string + :db/cardinality :db.cardinality/many + :db/doc "a name search for vendors" + :db/fulltext true}]] + :requires [:auto-ap/base-schema]} + ::add-vendor-search-terms {:txes-fn `add-vendor-search-terms + :requires [::make-fulltext-search]}}) diff --git a/src/clj/auto_ap/datomic/vendors.clj b/src/clj/auto_ap/datomic/vendors.clj index 7c094fba..09899c38 100644 --- a/src/clj/auto_ap/datomic/vendors.clj +++ b/src/clj/auto_ap/datomic/vendors.clj @@ -85,6 +85,18 @@ (map #(trim-usage % (limited-clients (:id args)))) #_(map #(assoc % :usage (get usages (:db/id %)))))) +(defn get-graphql-by-id [args id] + (->> (cond-> {:query {:find [(list 'pull '?e default-read)] + :in ['$ '?e] + :where ['[?e :vendor/name]]} + :args [(d/db (d/connect uri)) id]}) + (d/query) + (map first) + (map #(cleanse (:id args) %)) + (map <-datomic) + (map #(trim-usage % (limited-clients (:id args)))) + first)) + (defn get-by-id [id] (->> (d/q '[:find (pull ?e [* diff --git a/src/clj/auto_ap/graphql.clj b/src/clj/auto_ap/graphql.clj index c96a40af..d1d35290 100644 --- a/src/clj/auto_ap/graphql.clj +++ b/src/clj/auto_ap/graphql.clj @@ -98,6 +98,10 @@ :message {:fields {:message {:type 'String}}} + :search_result + {:fields {:name {:type 'String} + :id {:type :id}}} + :yodlee_provider_account {:fields {:id {:type 'Int} :client {:type :client} @@ -347,6 +351,10 @@ :args {:account_set {:type 'String}} :resolve :get-accounts} + :search_vendor {:type '(list :search_result) + :args {:query {:type 'String}} + :resolve :search-vendor} + :all_sales_orders {:type '(list :sales_order) @@ -393,7 +401,10 @@ :vendor {:type '(list :vendor) :resolve :get-vendor} :user {:type '(list :user) - :resolve :get-user}} + :resolve :get-user} + :vendor_by_id {:type :vendor + :args {:id {:type :id}} + :resolve :vendor-by-id}} :input-objects { @@ -777,6 +788,7 @@ :get-cash-flow get-cash-flow :get-yodlee-merchants ym/get-yodlee-merchants :get-intuit-bank-accounts gq-intuit-bank-accounts/get-intuit-bank-accounts + :vendor-by-id gq-vendors/get-by-id :get-user get-user :mutation/delete-transaction-rule gq-transaction-rules/delete-transaction-rule :mutation/edit-user gq-users/edit-user @@ -787,7 +799,8 @@ :mutation/upsert-account gq-accounts/upsert-account :mutation/merge-vendors gq-vendors/merge-vendors :mutation/request-import gq-requests/request-import - :get-vendor gq-vendors/get-graphql}) + :get-vendor gq-vendors/get-graphql + :search-vendor gq-vendors/search}) gq-checks/attach gq-ledger/attach gq-reports/attach diff --git a/src/clj/auto_ap/graphql/vendors.clj b/src/clj/auto_ap/graphql/vendors.clj index c92fb166..037d3e09 100644 --- a/src/clj/auto_ap/graphql/vendors.clj +++ b/src/clj/auto_ap/graphql/vendors.clj @@ -117,3 +117,21 @@ (defn get-graphql [context args value] (->graphql (d-vendors/get-graphql (assoc args :id (:id context))))) + +(defn get-by-id [context args value] + (->graphql + (d-vendors/get-graphql-by-id (assoc args :id (:id context)) + (:id args)))) + +(defn search [context args value] + (->> (d/q '[:find ?n ?i ?s + :in $ ?q + :where [(fulltext $ :vendor/search-terms ?q) [[?i ?n _ ?s]]] + (not [?i :vendor/hidden true])] + (d/db conn) + (:query args)) + (sort-by last) + (map (fn [[n i]] + {:name n + :id i} + )))) diff --git a/src/cljs/auto_ap/forms.cljs b/src/cljs/auto_ap/forms.cljs index 86b58377..257cc6c1 100644 --- a/src/cljs/auto_ap/forms.cljs +++ b/src/cljs/auto_ap/forms.cljs @@ -10,6 +10,13 @@ (fn [db [_ x]] (get (-> db ::forms) x))) +(re-frame/reg-sub + ::field + (fn [db [_ x f]] + (-> (get (-> db ::forms) x) + :data + (get-in f)))) + (re-frame/reg-sub ::is-loading? diff --git a/src/cljs/auto_ap/views/components/expense_accounts_field.cljs b/src/cljs/auto_ap/views/components/expense_accounts_field.cljs index f02ecfd7..bbe45003 100644 --- a/src/cljs/auto_ap/views/components/expense_accounts_field.cljs +++ b/src/cljs/auto_ap/views/components/expense_accounts_field.cljs @@ -14,8 +14,7 @@ (not (get-in accounts [0 :account :id])))) (defn default-account [accounts default-account amount locations] - [{:id (get-in accounts [0 :id] - (str "new-" (random-uuid))) + [{:id (str "new-" (random-uuid)) :amount (Math/abs amount) :amount-percentage 100 :amount-mode "%" diff --git a/src/cljs/auto_ap/views/components/typeahead/vendor.cljs b/src/cljs/auto_ap/views/components/typeahead/vendor.cljs new file mode 100644 index 00000000..57b1ab37 --- /dev/null +++ b/src/cljs/auto_ap/views/components/typeahead/vendor.cljs @@ -0,0 +1,134 @@ +(ns auto-ap.views.components.typeahead.vendor + (:require + [downshift :as ds :refer [useCombobox]] + [re-frame.core :as re-frame] + [auto-ap.views.utils :refer [with-user]] + [react])) + +(set! *warn-on-infer* true) + +;; TODO: This avoids the use of inferred externs by using aget. You could just use the ^js tag though +(defn state-reducer [^js/FakeStateObject state ^js/FakeActionsAndChanges actions-and-changes] + (let [useCombobox ^js/Downshift useCombobox] + (cond + (= (.-type actions-and-changes) (.-InputChange (.-stateChangeTypes ^js/Downshift useCombobox))) + (set! (.-selectedItem (.-changes actions-and-changes)) nil) + + (and (= (.-type actions-and-changes) (.-InputBlur (.-stateChangeTypes ^js/Downshift useCombobox))) + (not (.-selectedItem state))) + (set! (.-inputValue (.-changes actions-and-changes )) + nil) + + :else + nil)) + (.-changes actions-and-changes)) + +(re-frame/reg-event-fx + ::search-completed + (fn [_ [_ set-items set-loading-status result]] + (set-loading-status nil) + (set-items (:search-results result)) + {})) +(re-frame/reg-event-fx + ::search-failed + (fn [_ _] + {})) + +(re-frame/reg-event-fx + ::input-value-settled + [with-user] + (fn [{:keys [user]} [_ input-value search-query set-items set-loading-status]] + (when (> (count input-value) 2) + (set-loading-status :loading) + + + {:graphql {:token user + :query-obj {:venia/queries [{:query/data (search-query input-value ) + :query/alias :search-results}]} + :on-success [::search-completed set-items set-loading-status] + :on-error [::search-failed set-loading-status]}}))) + +(re-frame/reg-event-fx + ::input-value-changed + (fn [_ [_ input-value search-query set-items set-loading-status]] + (set-items []) + (when (> (count input-value) 2) + (set-loading-status :loading) + {:dispatch-debounce {:event [::input-value-settled input-value search-query set-items set-loading-status] + :time 250 + :key ::input-value-settled}}))) + +(defn typeahead-v3-internal [{:keys [class style ^js entity->text on-change disabled value name search-query auto-focus] :or {disabled false} :as i}] + (let [[items set-items] (react/useState []) + [loading-status set-loading-status] (react/useState false) + [getLabelProps getMenuProps getComboboxProps getToggleButtonProps getInputProps getItemProps isOpen highlightedIndex selectItem selectedItem setInputValue] + (as-> (useCombobox (clj->js {:items items + :defaultHighlightedIndex 0 + :defaultSelectedItem value + :onInputValueChange (fn [input] + (re-frame/dispatch [::input-value-changed (aget input "inputValue") search-query set-items set-loading-status]) + true) + :stateReducer state-reducer + :onSelectedItemChange (fn [z] + (when on-change + (on-change (js->clj (aget z "selectedItem") :keywordize-keys true))))})) $ + (map #(aget $ %) ["getLabelProps" "getMenuProps" "getComboboxProps" "getToggleButtonProps" "getInputProps" "getItemProps" "isOpen" "highlightedIndex" "selectItem" "selectedItem" "setInputValue"]))] + #_(println (getInputProps)) + + [:<> + [:div.typeahead (assoc (js->clj (getComboboxProps)) + :style style) + (cond + selectedItem + ^{:key "typeahead"} + [:div.input (assoc (js->clj (getInputProps #js {:disabled (if disabled + "disabled" + "")})) + :on-key-up (fn [e] + (when (= 8 (aget e "keyCode" )) + (selectItem nil) + (setInputValue nil) + (when on-change + (on-change nil)))) + :class (if (= :loading loading-status) + "is-loading" + class) + :tab-index "0") + [:div.control + [:div.tags.has-addons + [:span.tag (entity->text (js->clj selectedItem :keywordize-keys true))] + (when name + [:input {:type "hidden" :name name :value (:id (js->clj selectedItem :keywordize-keys true))}]) + (when-not disabled + [:a.tag.is-delete {:on-click (fn [] + (setInputValue nil) + (selectItem nil) + (when on-change + (on-change nil)))}])]]] + + :else + ^{:key "typeahead"} [:div.control {:class (when (= :loading loading-status) + "is-loading")} + [:input.input (js->clj + (getInputProps #js {:disabled (if disabled + "disabled" + "") + :autoFocus (if auto-focus + "autoFocus" + "")}))]]) + [:div {:class (when (and isOpen (seq items)) + "typeahead-menu")} + [:ul (js->clj (getMenuProps)) + (if (and isOpen (seq items)) + (for [[index item] (map vector (range) (js->clj items :keywordize-keys true))] + ^{:key item} + [:li.typeahead-suggestion (assoc (js->clj (getItemProps #js {:item item :index index})) + :class (if (= index highlightedIndex) + "typeahead-highlighted")) + (entity->text item)]))]]]])) + +(defn search-backed-typeahead [props] + [:div + [:f> typeahead-v3-internal (assoc props + :entity->text :name + )]]) diff --git a/src/cljs/auto_ap/views/pages/invoices/form.cljs b/src/cljs/auto_ap/views/pages/invoices/form.cljs index 5fef263d..dc5e7441 100644 --- a/src/cljs/auto_ap/views/pages/invoices/form.cljs +++ b/src/cljs/auto_ap/views/pages/invoices/form.cljs @@ -17,6 +17,7 @@ [auto-ap.views.components.money-field :refer [money-field]] [auto-ap.views.components.switch-field :refer [switch-field]] [auto-ap.views.components.typeahead :refer [typeahead-v3]] + [auto-ap.views.components.typeahead.vendor :refer [search-backed-typeahead]] [auto-ap.views.pages.invoices.common :refer [invoice-read]] [auto-ap.views.utils :refer @@ -26,13 +27,14 @@ [clojure.string :as str] [re-frame.core :as re-frame] [reagent.core :as r] - [vimsical.re-frame.fx.track :as track])) + [vimsical.re-frame.fx.track :as track] + [vimsical.re-frame.cofx.inject :as inject])) ;; SUBS (re-frame/reg-sub ::can-submit :<- [::forms/form ::form] - (fn [{:keys [data status]} _] + (fn [{:keys [data]} _] (let [min-total (if (= (:total (:original data)) (:outstanding-balance (:original data))) nil (- (:total (:original data)) (:outstanding-balance (:original data)))) @@ -45,7 +47,7 @@ (re-frame/reg-sub ::create-query :<- [::forms/form ::form] - (fn [{:keys [data] {:keys [id invoice-number date due scheduled-payment location total expense-accounts vendor client]} :data}] + (fn [{{:keys [invoice-number date due scheduled-payment location total expense-accounts vendor client]} :data}] {:venia/operation {:operation/type :mutation :operation/name "AddInvoice"} :venia/queries [{:query/data [:add-invoice @@ -69,7 +71,7 @@ (re-frame/reg-sub ::edit-query :<- [::forms/form ::form] - (fn [{:keys [data] {:keys [id invoice-number date due scheduled-payment location total expense-accounts vendor client]} :data}] + (fn [{{:keys [id invoice-number date due scheduled-payment total expense-accounts]} :data}] {:venia/operation {:operation/type :mutation :operation/name "EditInvoice"} :venia/queries [{:query/data [:edit-invoice @@ -90,8 +92,8 @@ (re-frame/reg-sub ::add-and-print-query - (fn [db [_ bank-account-id type]] - (let [{:keys [data] {:keys [id invoice-number date location total expense-accounts scheduled-payment vendor client]} :data} @(re-frame/subscribe [::forms/form ::form])] + (fn [_ [_ bank-account-id type]] + (let [{{:keys [invoice-number date location total expense-accounts scheduled-payment vendor client]} :data} @(re-frame/subscribe [::forms/form ::form])] {:venia/operation {:operation/type :mutation :operation/name "AddAndPrintInvoice"} :venia/queries [{:query/data [:add-and-print-invoice @@ -119,7 +121,7 @@ (re-frame/reg-event-db ::updated - (fn [db [_ invoice command]] + (fn [db [_ _ command]] (if (= :create command) (-> db (forms/stop-form ::form ) @@ -178,34 +180,7 @@ (forms/change-handler ::form (fn [data field value] (let [locations @(re-frame/subscribe [::subs/locations-for-client (:id (:client data))])] - - (cond (and (= [:vendor] field) - value) - (let [schedule-payment-dom (get (by (comp :id :client ) :dom (:schedule-payment-dom value)) - (:id (:client data)))] - (cond-> [] - - (expense-accounts-field/can-replace-with-default? (:expense-accounts data)) - (into [[:expense-accounts] (expense-accounts-field/default-account (:expense-accounts data) - @(re-frame/subscribe [::subs/vendor-default-account (:id value) (:client data)]) - (:total data) - locations)]) - - - (boolean ((set (map :id (:automatically-paid-when-due value))) (:id (:client data)))) - (into [[:scheduled-payment] (:due data) - [:schedule-when-due] true - [:vendor-autopay? ] true]) - - - schedule-payment-dom - (into [[:scheduled-payment] (date->str (next-dom (str->date (:date data) standard) schedule-payment-dom) standard) - [:vendor-autopay?] true]) - - true - (into [[:schedule-payment-dom] schedule-payment-dom]))) - - (= [:total] field) + (cond (= [:total] field) [[:expense-accounts] (recalculate-amounts (:expense-accounts data) value)] (and (= [:date] field) @@ -231,7 +206,7 @@ (re-frame/reg-event-fx ::add-and-print [with-user (forms/in-form ::form)] - (fn [{:keys [user] {:keys [data]} :db} [_ bank-account-id type]] + (fn [{:keys [user]} [_ bank-account-id type]] {:graphql {:token user :owns-state {:single ::form} @@ -263,7 +238,7 @@ (re-frame/reg-event-fx ::save-requested [with-user (forms/in-form ::form)] - (fn [{:keys [user db]} [_ fwd-event]] + (fn [{:keys [db]} [_ fwd-event]] (if (and (:scheduled-payment (:data db)) (not (:vendor-autopay? (:data db)))) {:dispatch @@ -282,16 +257,80 @@ (re-frame/reg-event-fx ::added-and-printed - (fn [{:keys [db]} [_ result]] + (fn [_ [_ result]] (let [invoice (first (:invoices (:add-and-print-invoice result)))] {:dispatch-n [[::updated (assoc invoice :class "live-added") :create] [::checks-printed [invoice] (:pdf-url (:add-and-print-invoice result))]]}))) (re-frame/reg-event-db ::checks-printed - (fn [db [_ invoices pdf-url]] + (fn [db _] db)) +(re-frame/reg-sub + ::client-accounts + :<- [::forms/field ::form [:client]] + :<- [::subs/all-accounts] + (fn [[client all-accounts]] + (subs/accounts-by-id all-accounts client))) + +(re-frame/reg-event-fx + ::changed-vendor + [with-user (forms/in-form ::form) (re-frame/inject-cofx ::inject/sub [::client-accounts])] + (fn [{::keys [client-accounts] :keys [user] {{:keys [client date due expense-accounts total]} :data} :db} [_ vendor]] + (when (:id vendor) + {:graphql {:token user + :query-obj {:venia/queries [[:vendor-by-id + {:id (:id vendor)} + [[:automatically-paid-when-due [:id]] + [:schedule-payment-dom [[:client [:id]] :dom]] + [:default-account [:id]]]]]} + :on-success (fn [r] + (let [schedule-payment-dom (->> r + :vendor-by-id + :schedule-payment-dom + (filter (fn [spd] + (= (-> spd :client :id) + (:id client)))) + first + :dom) + + default-account (let [client-override (->> r + :vendor-by-id + :account-overrides + (filter #(= (:id (:client %)) (:id client))) + first + :account + :id) + default-id (->> r :vendor-by-id :default-account :id) + i (or client-override default-id)] + (client-accounts i)) + changes (cond-> [] + (expense-accounts-field/can-replace-with-default? expense-accounts) + (into [[:expense-accounts] (expense-accounts-field/default-account expense-accounts + default-account + total + (:locations client) + )]) + + (boolean ((->> r :vendor-by-id :automatically-paid-when-due (map :id) set) (:id client))) + (into [[:scheduled-payment] due + [:schedule-when-due] true + [:vendor-autopay? ] true]) + + schedule-payment-dom + (into [[:scheduled-payment] (date->str (next-dom (str->date date standard) schedule-payment-dom) standard) + [:vendor-autopay?] true]) + + true + (into [[:schedule-payment-dom] schedule-payment-dom]))] + (if (seq changes) + (into [::changed ] changes) + [:ignore])) + ) + :on-failure [:bad] + }}))) + ;; VIEWS @@ -308,16 +347,21 @@ :subscription [::subs/client] :event-fn (fn [c] - [::maybe-change-client c])}]})) + [::maybe-change-client c])} + {:id ::vendor-change + :subscription [::forms/field ::form [:vendor]] + :event-fn (fn [v] + [::changed-vendor v])}]})) (re-frame/reg-event-fx ::unmounted (fn [] - {::track/dispose [{:id ::client}]})) + {::track/dispose [{:id ::client} + {:id ::vendor-change}]})) -(defn form-content [{:keys [can-change-amount?] :as params}] +(defn form-content [params] [layouts/side-bar {:on-close (dispatch-event [::forms/form-closing ::form ])} - (let [{:keys [data active? error id]} @(re-frame/subscribe [::forms/form ::form]) + (let [{:keys [data id]} @(re-frame/subscribe [::forms/form ::form]) {:keys [form-inline field raw-field error-notification submit-button ]} invoice-form can-submit? (boolean @(re-frame/subscribe [::can-submit])) status @(re-frame/subscribe [::status/single ::form]) @@ -339,7 +383,7 @@ (not (seq (:payments data)))) [:div.tag.is-info.is-light "Automatically paid"] - (and (#{:paid ":paid"} (:status data))) + (#{:paid ":paid"} (:status data)) (if-let [check-number (:check-number (:payment (first (:payments data))))] [:div.tag.is-info.is-light "Paid by check #" check-number ] [:div.tag.is-info.is-light "Paid"]) @@ -359,13 +403,14 @@ :spec ::invoice/client}])) (field [:span "Vendor" [:span.has-text-danger " *"]] - [typeahead-v3 {:entities-by-id @(re-frame/subscribe [::subs/vendors-by-id]) - :entity-index @(re-frame/subscribe [::subs/searchable-vendors-index]) - :entity->text :name - :type "typeahead-v3" - :disabled exists? - :auto-focus (if @(re-frame/subscribe [::subs/client]) true false) - :field [:vendor]}]) + [search-backed-typeahead {:disabled exists? + :search-query (fn [i] + [:search_vendor + {:query i} + [:name :id]]) + :type "typeahead-v3" + :auto-focus (if @(re-frame/subscribe [::subs/client]) true false) + :field [:vendor]}]) (field [:span "Date" [:span.has-text-danger " *"]] @@ -374,6 +419,7 @@ :format-week-number (fn [] "") :previous-month-button-label "" :placeholder "mm/dd/yyyy" + :disable-keyboard-navigation true :next-month-button-label "" :next-month-label "" :type "date" @@ -386,6 +432,7 @@ :format-week-number (fn [] "") :previous-month-button-label "" :placeholder "mm/dd/yyyy" + :disable-keyboard-navigation true :next-month-button-label "" :next-month-label "" :type "date" @@ -440,11 +487,7 @@ :max (:total data) :client (or (:client data) @(re-frame/subscribe [::subs/client])) :field [:expense-accounts]}]) - {:key (str (:id (:vendor data)))}) - - - - + {:key (str (:id (:vendor data) "none") "-" (:id (:client data) "none") )}) (error-notification) @@ -464,7 +507,7 @@ :id ::add-and-print-invoice} [:div (list - (for [{:keys [id number name type]} (->> (:bank-accounts (:client data)) (filter :visible) (sort-by :sort-order))] + (for [{:keys [id name type]} (->> (:bank-accounts (:client data)) (filter :visible) (sort-by :sort-order))] (if (= :cash type) ^{:key id} [:a.dropdown-item {:on-click (dispatch-event [::save-requested [::add-and-print id :cash]])} "With cash"] (list @@ -476,7 +519,7 @@ {:key id}))]) -(defn form [p] +(defn form [_] (r/create-class {:display-name "invoice-form" :component-did-mount #(re-frame/dispatch [::mounted]) From 53de49d7588ca28e1e7ab0a289aa107f5afec0f3 Mon Sep 17 00:00:00 2001 From: Bryce Covert Date: Sun, 10 Apr 2022 20:42:21 -0700 Subject: [PATCH 02/12] sidebar filtering --- .../views/components/invoices/side_bar.cljs | 36 +++++++++------- src/cljs/auto_ap/views/pages/home.cljs | 3 -- .../auto_ap/views/pages/invoices/form.cljs | 25 ++++++----- .../auto_ap/views/pages/ledger/side_bar.cljs | 39 +++++++++-------- .../views/pages/payments/side_bar.cljs | 30 ++++++------- .../views/pages/transactions/side_bar.cljs | 42 ++++++++++--------- 6 files changed, 92 insertions(+), 83 deletions(-) diff --git a/src/cljs/auto_ap/views/components/invoices/side_bar.cljs b/src/cljs/auto_ap/views/components/invoices/side_bar.cljs index 4424a3b8..c3923802 100644 --- a/src/cljs/auto_ap/views/components/invoices/side_bar.cljs +++ b/src/cljs/auto_ap/views/components/invoices/side_bar.cljs @@ -1,14 +1,17 @@ (ns auto-ap.views.components.invoices.side-bar - (:require [auto-ap.routes :as routes] - [auto-ap.subs :as subs] - [auto-ap.views.components.date-range-filter :refer [date-range-filter]] - [auto-ap.views.components.number-filter :refer [number-filter]] - [auto-ap.views.components.switch-field :refer [switch-field]] - [auto-ap.views.components.typeahead :refer [typeahead-v3]] - [auto-ap.views.pages.data-page :as data-page] - [auto-ap.views.utils :refer [active-when dispatch-event dispatch-value-change]] - [bidi.bidi :as bidi] - [re-frame.core :as re-frame])) + (:require + [auto-ap.routes :as routes] + [auto-ap.subs :as subs] + [auto-ap.views.components.date-range-filter :refer [date-range-filter]] + [auto-ap.views.components.number-filter :refer [number-filter]] + [auto-ap.views.components.switch-field :refer [switch-field]] + [auto-ap.views.components.typeahead.vendor + :refer [search-backed-typeahead]] + [auto-ap.views.pages.data-page :as data-page] + [auto-ap.views.utils + :refer [active-when dispatch-event dispatch-value-change]] + [bidi.bidi :as bidi] + [re-frame.core :as re-frame])) (defn invoice-number-filter [{:keys [data-page]}] [:div.field @@ -59,12 +62,13 @@ [:div [:p.menu-label "Vendor"] [:div - [typeahead-v3 {:entities-by-id @(re-frame/subscribe [::subs/vendors-by-id]) - :entity-index @(re-frame/subscribe [::subs/searchable-vendors-index]) - :on-change #(re-frame/dispatch [::data-page/filter-changed data-page :vendor (some-> % (select-keys [:name :id]))]) - :entity->text :name - :type "typeahead-v3" - :value @(re-frame/subscribe [::data-page/filter data-page :vendor])}]] + [search-backed-typeahead {:search-query (fn [i] + [:search_vendor + {:query i} + [:name :id]]) + :type "typeahead-v3" + :on-change #(re-frame/dispatch [::data-page/filter-changed data-page :vendor %]) + :value @(re-frame/subscribe [::data-page/filter data-page :vendor])}]] [:p.menu-label "Date Range"] [:div [date-range-filter diff --git a/src/cljs/auto_ap/views/pages/home.cljs b/src/cljs/auto_ap/views/pages/home.cljs index 16c65d47..3daefedc 100644 --- a/src/cljs/auto_ap/views/pages/home.cljs +++ b/src/cljs/auto_ap/views/pages/home.cljs @@ -300,9 +300,6 @@ (defn home-content [] (let [client-id (-> @(re-frame/subscribe [::subs/client]) :id) - vendors-by-id @(re-frame/subscribe [::subs/vendors-by-id]) - vendors-index @(re-frame/subscribe [::subs/searchable-vendors-index]) - accounts-by-id @(re-frame/subscribe [::subs/accounts-by-id]) accounts-index @(re-frame/subscribe [::subs/accounts-index]) chart-options @(re-frame/subscribe [::chart-options])] diff --git a/src/cljs/auto_ap/views/pages/invoices/form.cljs b/src/cljs/auto_ap/views/pages/invoices/form.cljs index dc5e7441..7eff34a7 100644 --- a/src/cljs/auto_ap/views/pages/invoices/form.cljs +++ b/src/cljs/auto_ap/views/pages/invoices/form.cljs @@ -5,7 +5,7 @@ [auto-ap.status :as status] [auto-ap.subs :as subs] [auto-ap.time-utils :refer [next-dom]] - [auto-ap.utils :refer [by dollars=]] + [auto-ap.utils :refer [dollars=]] [auto-ap.views.components.dropdown :refer [drop-down]] [auto-ap.views.components.expense-accounts-field :as @@ -179,22 +179,21 @@ ::changed (forms/change-handler ::form (fn [data field value] - (let [locations @(re-frame/subscribe [::subs/locations-for-client (:id (:client data))])] - (cond (= [:total] field) - [[:expense-accounts] (recalculate-amounts (:expense-accounts data) value)] + (cond (= [:total] field) + [[:expense-accounts] (recalculate-amounts (:expense-accounts data) value)] - (and (= [:date] field) - (:schedule-payment-dom data)) - [[:scheduled-payment] (date->str (next-dom (str->date value standard) (:schedule-payment-dom data)) standard) ] + (and (= [:date] field) + (:schedule-payment-dom data)) + [[:scheduled-payment] (date->str (next-dom (str->date value standard) (:schedule-payment-dom data)) standard) ] - (and (= [:schedule-when-due] field) value) - [[:scheduled-payment] (:due data)] + (and (= [:schedule-when-due] field) value) + [[:scheduled-payment] (:due data)] - (and (= [:due] field) (:schedule-when-due data)) - [[:scheduled-payment] value] + (and (= [:due] field) (:schedule-when-due data)) + [[:scheduled-payment] value] - :else - []))))) + :else + [])))) (re-frame/reg-event-db ::maybe-change-client [ (forms/in-form ::form)] diff --git a/src/cljs/auto_ap/views/pages/ledger/side_bar.cljs b/src/cljs/auto_ap/views/pages/ledger/side_bar.cljs index a1d93c85..724895b7 100644 --- a/src/cljs/auto_ap/views/pages/ledger/side_bar.cljs +++ b/src/cljs/auto_ap/views/pages/ledger/side_bar.cljs @@ -1,14 +1,18 @@ (ns auto-ap.views.pages.ledger.side-bar - (:require [auto-ap.routes :as routes] - [auto-ap.subs :as subs] - [auto-ap.views.components.date-range-filter :refer [date-range-filter]] - [auto-ap.views.components.number-filter :refer [number-filter]] - [auto-ap.views.components.bank-account-filter :refer [bank-account-filter]] - [auto-ap.views.components.typeahead :refer [typeahead-v3]] - [auto-ap.views.pages.data-page :as data-page] - [auto-ap.views.utils :refer [active-when dispatch-value-change]] - [bidi.bidi :as bidi] - [re-frame.core :as re-frame])) + (:require + [auto-ap.routes :as routes] + [auto-ap.subs :as subs] + [auto-ap.views.components.bank-account-filter + :refer [bank-account-filter]] + [auto-ap.views.components.date-range-filter :refer [date-range-filter]] + [auto-ap.views.components.number-filter :refer [number-filter]] + [auto-ap.views.components.typeahead :refer [typeahead-v3]] + [auto-ap.views.components.typeahead.vendor + :refer [search-backed-typeahead]] + [auto-ap.views.pages.data-page :as data-page] + [auto-ap.views.utils :refer [active-when dispatch-value-change]] + [bidi.bidi :as bidi] + [re-frame.core :as re-frame])) (defn ledger-side-bar [{:keys [data-page]}] (let [ap @(re-frame/subscribe [::subs/active-page]) @@ -66,14 +70,13 @@ [:p.menu-label "Vendor"] [:div - [typeahead-v3 {:entities-by-id @(re-frame/subscribe [::subs/vendors-by-id]) - :entity-index @(re-frame/subscribe [::subs/searchable-vendors-index]) - :on-change #(re-frame/dispatch [::data-page/filter-changed data-page :vendor (some-> % (select-keys [:name :id]))]) - :entity->text :name - :type "typeahead-v3" - :value @(re-frame/subscribe [::data-page/filter data-page :vendor])}]] - - + [search-backed-typeahead {:search-query (fn [i] + [:search_vendor + {:query i} + [:name :id]]) + :type "typeahead-v3" + :on-change #(re-frame/dispatch [::data-page/filter-changed data-page :vendor %]) + :value @(re-frame/subscribe [::data-page/filter data-page :vendor])}]] [:p.menu-label "Date Range"] [:div diff --git a/src/cljs/auto_ap/views/pages/payments/side_bar.cljs b/src/cljs/auto_ap/views/pages/payments/side_bar.cljs index d40ebd4b..2133c2fe 100644 --- a/src/cljs/auto_ap/views/pages/payments/side_bar.cljs +++ b/src/cljs/auto_ap/views/pages/payments/side_bar.cljs @@ -1,11 +1,13 @@ (ns auto-ap.views.pages.payments.side-bar - (:require [auto-ap.subs :as subs] - [auto-ap.views.components.date-range-filter :refer [date-range-filter]] - [auto-ap.views.components.number-filter :refer [number-filter]] - [auto-ap.views.components.typeahead :refer [typeahead-v3]] - [auto-ap.views.pages.data-page :as data-page] - [auto-ap.views.utils :refer [dispatch-event dispatch-value-change]] - [re-frame.core :as re-frame])) + (:require + [auto-ap.subs :as subs] + [auto-ap.views.components.date-range-filter :refer [date-range-filter]] + [auto-ap.views.components.number-filter :refer [number-filter]] + [auto-ap.views.components.typeahead.vendor + :refer [search-backed-typeahead]] + [auto-ap.views.pages.data-page :as data-page] + [auto-ap.views.utils :refer [dispatch-event dispatch-value-change]] + [re-frame.core :as re-frame])) (defn side-bar [{:keys [data-page]}] (let [ap @(re-frame/subscribe [::subs/active-page]) @@ -14,13 +16,13 @@ [:div [:p.menu-label "Vendor"] [:div - [typeahead-v3 {:entities-by-id @(re-frame/subscribe [::subs/vendors-by-id]) - :entity-index @(re-frame/subscribe [::subs/searchable-vendors-index]) - :on-change #(re-frame/dispatch [::data-page/filter-changed data-page :vendor (some-> % (select-keys [:name :id]))]) - :include-keys [:name :id] - :entity->text :name - :type "typeahead-v3" - :value @(re-frame/subscribe [::data-page/filter data-page :vendor])}]] + [search-backed-typeahead {:search-query (fn [i] + [:search_vendor + {:query i} + [:name :id]]) + :type "typeahead-v3" + :on-change #(re-frame/dispatch [::data-page/filter-changed data-page :vendor %]) + :value @(re-frame/subscribe [::data-page/filter data-page :vendor])}]] [:p.menu-label "Date Range"] [:div diff --git a/src/cljs/auto_ap/views/pages/transactions/side_bar.cljs b/src/cljs/auto_ap/views/pages/transactions/side_bar.cljs index 03678058..9f0d1bd5 100644 --- a/src/cljs/auto_ap/views/pages/transactions/side_bar.cljs +++ b/src/cljs/auto_ap/views/pages/transactions/side_bar.cljs @@ -1,17 +1,20 @@ (ns auto-ap.views.pages.transactions.side-bar - (:require [auto-ap.routes :as routes] - [auto-ap.subs :as subs] - [auto-ap.views.components.bank-account-filter - :refer - [bank-account-filter]] - [auto-ap.views.components.date-range-filter :refer [date-range-filter]] - [auto-ap.views.components.number-filter :refer [number-filter]] - [auto-ap.views.components.switch-field :refer [switch-field]] - [auto-ap.views.components.typeahead :refer [typeahead-v3]] - [auto-ap.views.pages.data-page :as data-page] - [auto-ap.views.utils :refer [active-when dispatch-event dispatch-value-change ->$ account->match-text]] - [bidi.bidi :as bidi] - [re-frame.core :as re-frame])) + (:require + [auto-ap.routes :as routes] + [auto-ap.subs :as subs] + [auto-ap.views.components.bank-account-filter + :refer [bank-account-filter]] + [auto-ap.views.components.date-range-filter :refer [date-range-filter]] + [auto-ap.views.components.number-filter :refer [number-filter]] + [auto-ap.views.components.switch-field :refer [switch-field]] + [auto-ap.views.components.typeahead :refer [typeahead-v3]] + [auto-ap.views.components.typeahead.vendor + :refer [search-backed-typeahead]] + [auto-ap.views.pages.data-page :as data-page] + [auto-ap.views.utils + :refer [account->match-text active-when dispatch-event dispatch-value-change]] + [bidi.bidi :as bidi] + [re-frame.core :as re-frame])) (defn side-bar [{:keys [data-page]}] (let [ap @(re-frame/subscribe [::subs/active-page]) @@ -74,12 +77,13 @@ [:p.menu-label "Vendor"] [:div - [typeahead-v3 {:entities-by-id @(re-frame/subscribe [::subs/vendors-by-id]) - :entity-index @(re-frame/subscribe [::subs/searchable-vendors-index]) - :on-change #(re-frame/dispatch [::data-page/filter-changed data-page :vendor (some-> % (select-keys [:name :id]))]) - :entity->text :name - :type "typeahead-v3" - :value @(re-frame/subscribe [::data-page/filter data-page :vendor])}]] + [search-backed-typeahead {:search-query (fn [i] + [:search_vendor + {:query i} + [:name :id]]) + :type "typeahead-v3" + :on-change #(re-frame/dispatch [::data-page/filter-changed data-page :vendor %]) + :value @(re-frame/subscribe [::data-page/filter data-page :vendor])}]] From b4d5b7fde6c85422b083cbb8f32285ca6aec59f3 Mon Sep 17 00:00:00 2001 From: Bryce Covert Date: Sun, 10 Apr 2022 21:46:58 -0700 Subject: [PATCH 03/12] very easy to lookup customized accounts. --- src/clj/auto_ap/datomic/accounts.clj | 48 ++++++++++++++----- src/clj/auto_ap/graphql.clj | 7 ++- src/clj/auto_ap/graphql/accounts.clj | 26 +++++++--- .../auto_ap/views/pages/admin/rules/form.cljs | 36 ++++++-------- .../auto_ap/views/pages/invoices/form.cljs | 27 ++++------- 5 files changed, 87 insertions(+), 57 deletions(-) diff --git a/src/clj/auto_ap/datomic/accounts.clj b/src/clj/auto_ap/datomic/accounts.clj index db3aa711..5f7b8d44 100644 --- a/src/clj/auto_ap/datomic/accounts.clj +++ b/src/clj/auto_ap/datomic/accounts.clj @@ -1,19 +1,21 @@ (ns auto-ap.datomic.accounts (:require [datomic.api :as d] [auto-ap.graphql.utils :refer [->graphql]] - [auto-ap.datomic :refer [uri merge-query]])) + [auto-ap.datomic :refer [uri merge-query conn]])) (defn <-datomic [a] (update a :account/applicability :db/ident)) +(def default-read ['* {:account/type [:db/ident :db/id] + :account/applicability [:db/ident :db/id] + :account/client-overrides [:db/id + :account-client-override/name + {:account-client-override/client [:db/id :client/name]}]}]) + (defn get-accounts ([] (get-accounts {})) ([args] - (let [query (cond-> {:query {:find ['(pull ?e [* {:account/type [:db/ident :db/id] - :account/applicability [:db/ident :db/id] - :account/client-overrides [:db/id - :account-client-override/name - {:account-client-override/client [:db/id :client/name]}]}])] + (let [query (cond-> {:query {:find [(list 'pull '?e default-read)] :in ['$] :where [['?e :account/name]]} :args [(d/db (d/connect uri))]} @@ -26,11 +28,7 @@ (map <-datomic))))) (defn get-by-id [id] - (let [query {:query {:find ['(pull ?e [* {:account/type [:db/ident :db/id] - :account/applicability [:db/ident :db/id] - :account/client-overrides [:db/id - :account-client-override/name - {:account-client-override/client [:db/id :client/name]}]}])] + (let [query {:query {:find [(list 'pull '?e default-read)] :in ['$ '?e]} :args [(d/db (d/connect uri) ) id]}] (->> @@ -39,6 +37,34 @@ (map <-datomic) first))) +(defn get-for-vendor [vendor-id client-id] + (if client-id + (->> + (d/q [:find (list 'pull '?e default-read) + :in '$ '?v '?c + :where '(or-join [?v ?c ?e] + (and [?v :vendor/account-overrides ?ao] + [?ao :vendor-account-override/client ?c] + [?ao :vendor-account-override/account ?e]) + (and [?v :vendor/account-overrides ?ao] + (not [?ao vendor-account-override/client ?c]) + [?v :vendor/default-account ?e]) + (and (not [?v :vendor/account-overrides]) + [?v :vendor/default-account ?e]))] + + (d/db conn ) + vendor-id + client-id) + (map first) + (map <-datomic) + first) + (d/q [:find (list 'pull '?e default-read) + :in '$ '?v + :where '[?v :vendor/default-account ?e]] + + (d/db conn ) + vendor-id))) + (defn get-account-by-numeric-code-and-sets [numeric-code sets] (let [query (cond-> {:query {:find ['(pull ?e [* {:account/type [:db/ident :db/id]}])] :in ['$ '?numeric-code] diff --git a/src/clj/auto_ap/graphql.clj b/src/clj/auto_ap/graphql.clj index d1d35290..20192cdb 100644 --- a/src/clj/auto_ap/graphql.clj +++ b/src/clj/auto_ap/graphql.clj @@ -404,7 +404,11 @@ :resolve :get-user} :vendor_by_id {:type :vendor :args {:id {:type :id}} - :resolve :vendor-by-id}} + :resolve :vendor-by-id} + :account_for_vendor {:type :account + :args {:client_id {:type :id} + :vendor_id {:type :id}} + :resolve :account-for-vendor}} :input-objects { @@ -789,6 +793,7 @@ :get-yodlee-merchants ym/get-yodlee-merchants :get-intuit-bank-accounts gq-intuit-bank-accounts/get-intuit-bank-accounts :vendor-by-id gq-vendors/get-by-id + :account-for-vendor gq-accounts/default-for-vendor :get-user get-user :mutation/delete-transaction-rule gq-transaction-rules/delete-transaction-rule :mutation/edit-user gq-users/edit-user diff --git a/src/clj/auto_ap/graphql/accounts.clj b/src/clj/auto_ap/graphql/accounts.clj index 9b4d80b4..9ed1ca1d 100644 --- a/src/clj/auto_ap/graphql/accounts.clj +++ b/src/clj/auto_ap/graphql/accounts.clj @@ -1,15 +1,27 @@ (ns auto-ap.graphql.accounts - (:require [datomic.api :as d] - [auto-ap.datomic.accounts :as d-accounts] - [auto-ap.graphql.utils :refer [->graphql <-graphql enum->keyword] ] - [auto-ap.datomic :refer [uri merge-query remove-nils audit-transact]] - [clojure.tools.logging :as log])) - - + (:require + [auto-ap.datomic :refer [audit-transact remove-nils uri]] + [auto-ap.datomic.accounts :as d-accounts] + [auto-ap.graphql.utils + :refer [->graphql <-graphql assert-can-see-client enum->keyword]] + [datomic.api :as d])) (defn get-accounts [context args value] (->graphql (d-accounts/get-accounts (<-graphql args)))) +(defn default-for-vendor [context args value] + (assert-can-see-client (:id context) (:client_id args)) + (let [result (d-accounts/get-for-vendor (:vendor_id args) (:client_id args))] + (->graphql + (if-let [override-name (->> result + :account/client-overrides + (filter #(= (:client_id args) + (:db/id (:account-client-override/client %)))) + first + :account-client-override/name)] + (assoc result :account/name override-name) + result)))) + (defn upsert-account [context args value] (let [{{:keys [id client-overrides numeric-code location applicability account-set name type]} :account} (<-graphql args)] (when-not id diff --git a/src/cljs/auto_ap/views/pages/admin/rules/form.cljs b/src/cljs/auto_ap/views/pages/admin/rules/form.cljs index 74145e6e..02fec28e 100644 --- a/src/cljs/auto_ap/views/pages/admin/rules/form.cljs +++ b/src/cljs/auto_ap/views/pages/admin/rules/form.cljs @@ -1,22 +1,19 @@ (ns auto-ap.views.pages.admin.rules.form (:require [auto-ap.entities.transaction-rule :as entity] - [auto-ap.events :as events] [auto-ap.forms :as forms] [auto-ap.subs :as subs] [auto-ap.views.components.button-radio :refer [button-radio]] - [auto-ap.utils :refer [dollars=]] - [auto-ap.views.components.dropdown :refer [drop-down]] - [auto-ap.views.components.expense-accounts-field :as expense-accounts-field :refer [expense-accounts-field recalculate-amounts]] + [auto-ap.views.components.expense-accounts-field :as expense-accounts-field :refer [expense-accounts-field]] [auto-ap.views.components.layouts :as layouts] [auto-ap.views.components.typeahead :refer [typeahead-v3]] [auto-ap.views.pages.admin.rules.common :refer [default-read]] [auto-ap.views.pages.admin.rules.results-modal :as results-modal] - [auto-ap.views.utils :refer [date->str date-picker dispatch-event standard with-user]] - [cljs-time.core :as c] + [auto-ap.views.utils :refer [dispatch-event with-user]] [clojure.spec.alpha :as s] [clojure.string :as str] [re-frame.core :as re-frame] - [auto-ap.status :as status])) + [auto-ap.status :as status] + [vimsical.re-frame.cofx.inject :as inject])) ;; SUBS @@ -45,7 +42,7 @@ (re-frame/reg-sub ::can-submit :<- [::forms/form ::form] - (fn [{:keys [data status]} _] + (fn [{:keys [data]} _] (s/valid? ::entity/transaction-rule data))) (re-frame/reg-sub @@ -188,23 +185,23 @@ (re-frame/reg-event-fx ::saving - [with-user (forms/triggers-loading ::form) (forms/in-form ::form)] - (fn [{:keys [user] {:keys [data]} :db} [_ params]] + [with-user (forms/triggers-loading ::form) (forms/in-form ::form) (re-frame/inject-cofx ::inject/sub [::query])] + (fn [{:keys [user] ::keys [query]} _] {:graphql {:token user - :query-obj @(re-frame/subscribe [::query]) + :query-obj query :on-success (fn [result] [::updated (:upsert-transaction-rule result)]) :on-error [::forms/save-error ::form]}})) (re-frame/reg-event-fx ::test-clicked - [with-user (forms/triggers-loading ::form) (forms/in-form ::form)] - (fn [{:keys [user] {:keys [data]} :db} [_ params]] + [with-user (forms/triggers-loading ::form) (forms/in-form ::form) (re-frame/inject-cofx ::inject/sub [::test-query])] + (fn [{:keys [user] ::keys [test-query]} _] {:graphql {:token user :owns-state {:single ::test} - :query-obj @(re-frame/subscribe [::test-query]) + :query-obj test-query :on-success [::succeeded-test] :on-error [::forms/save-error ::form]}})) @@ -218,7 +215,7 @@ (re-frame/reg-event-fx ::succeeded-test [(forms/triggers-stop-loading ::form)] - (fn [{:keys [db]} [_ result]] + (fn [_ [_ result]] {:dispatch [::results-modal/opening (:test-transaction-rule result) nil false]})) @@ -233,13 +230,12 @@ -(defn form [{:keys [can-change-amount?] :as params}] +(defn form [params] [layouts/side-bar {:on-close (dispatch-event [::forms/form-closing ::form ])} - (let [{:keys [data active? error id]} @(re-frame/subscribe [::forms/form ::form]) + (let [{:keys [data id]} @(re-frame/subscribe [::forms/form ::form]) {:keys [form-inline field raw-field error-notification submit-button ]} rule-form default-note @(re-frame/subscribe [::default-note]) - test-state @(re-frame/subscribe [::status/single ::test]) - exists? (:id data)] + test-state @(re-frame/subscribe [::status/single ::test])] ^{:key id} (form-inline (assoc params :title "New Transaction Rule") [:<> @@ -315,8 +311,6 @@ :precision 0 :step "1"}])]]]] - - [:h2.title.is-4 "Outcomes"] (field "Assign Vendor" diff --git a/src/cljs/auto_ap/views/pages/invoices/form.cljs b/src/cljs/auto_ap/views/pages/invoices/form.cljs index 7eff34a7..612d4557 100644 --- a/src/cljs/auto_ap/views/pages/invoices/form.cljs +++ b/src/cljs/auto_ap/views/pages/invoices/form.cljs @@ -266,7 +266,7 @@ (fn [db _] db)) -(re-frame/reg-sub +#_(re-frame/reg-sub ::client-accounts :<- [::forms/field ::form [:client]] :<- [::subs/all-accounts] @@ -275,15 +275,19 @@ (re-frame/reg-event-fx ::changed-vendor - [with-user (forms/in-form ::form) (re-frame/inject-cofx ::inject/sub [::client-accounts])] - (fn [{::keys [client-accounts] :keys [user] {{:keys [client date due expense-accounts total]} :data} :db} [_ vendor]] + [with-user (forms/in-form ::form)] + (fn [{:keys [user] {{:keys [client date due expense-accounts total]} :data} :db} [_ vendor]] (when (:id vendor) {:graphql {:token user :query-obj {:venia/queries [[:vendor-by-id {:id (:id vendor)} [[:automatically-paid-when-due [:id]] [:schedule-payment-dom [[:client [:id]] :dom]] - [:default-account [:id]]]]]} + [:default-account [:id]]]] + [:account-for-vendor + {:vendor-id (:id vendor) + :client-id (:id client)} + [:name :id :numeric-code :location]]]} :on-success (fn [r] (let [schedule-payment-dom (->> r :vendor-by-id @@ -294,23 +298,12 @@ first :dom) - default-account (let [client-override (->> r - :vendor-by-id - :account-overrides - (filter #(= (:id (:client %)) (:id client))) - first - :account - :id) - default-id (->> r :vendor-by-id :default-account :id) - i (or client-override default-id)] - (client-accounts i)) changes (cond-> [] (expense-accounts-field/can-replace-with-default? expense-accounts) (into [[:expense-accounts] (expense-accounts-field/default-account expense-accounts - default-account + (:account-for-vendor r) total - (:locations client) - )]) + (:locations client))]) (boolean ((->> r :vendor-by-id :automatically-paid-when-due (map :id) set) (:id client))) (into [[:scheduled-payment] due From 919f8a773d5779911a9926b03c2057eaa6c2c394 Mon Sep 17 00:00:00 2001 From: Bryce Covert Date: Mon, 11 Apr 2022 07:52:38 -0700 Subject: [PATCH 04/12] Vendor sub not needed. --- src/cljs/auto_ap/events.cljs | 53 +++- .../views/components/invoice_table.cljs | 10 +- .../auto_ap/views/pages/invoices/form.cljs | 291 ++++++++---------- 3 files changed, 178 insertions(+), 176 deletions(-) diff --git a/src/cljs/auto_ap/events.cljs b/src/cljs/auto_ap/events.cljs index 1cf591f4..837dcb62 100644 --- a/src/cljs/auto_ap/events.cljs +++ b/src/cljs/auto_ap/events.cljs @@ -1,16 +1,14 @@ (ns auto-ap.events - (:require [re-frame.core :as re-frame] - [auto-ap.db :as db] - [auto-ap.subs :as subs] - [auto-ap.routes :as routes] - [auto-ap.effects :as effects] - - [auto-ap.utils :refer [by]] - [venia.core :as v] - [bidi.bidi :as bidi] - [cemerick.url :refer [url]] - [goog.crypt.base64 :as b64] - [clojure.string :as str])) + (:require + [auto-ap.db :as db] + [auto-ap.routes :as routes] + [auto-ap.subs :as subs] + [auto-ap.utils :refer [by]] + [auto-ap.views.utils :refer [with-user]] + [bidi.bidi :as bidi] + [clojure.string :as str] + [goog.crypt.base64 :as b64] + [re-frame.core :as re-frame])) (defn jwt->data [token] (js->clj (.parse js/JSON (b64/decodeString (second (str/split token #"\." )))))) @@ -275,3 +273,34 @@ :query-obj {:venia/queries [[:yodlee-merchants [:name :yodlee-id :id]]]} :on-success [::yodlee-merchants-received]}})) + +(re-frame/reg-event-fx + ::vendor-preferences-requested + [with-user] + (fn [{:keys [user]} [_ {:keys [ client-id vendor-id on-success on-failure owns-state]}]] + {:graphql {:token user + :query-obj {:venia/queries [[:vendor-by-id + {:id vendor-id} + [[:automatically-paid-when-due [:id]] + [:schedule-payment-dom [[:client [:id]] :dom]] + [:default-account [:id]]]] + [:account-for-vendor + {:vendor-id vendor-id + :client-id client-id} + [:name :id :numeric-code :location]]]} + :owns-state owns-state + :on-success (fn [r] + (let [schedule-payment-dom (->> r + :vendor-by-id + :schedule-payment-dom + (filter (fn [spd] + (= (-> spd :client :id) + client-id))) + first + :dom) + automatically-paid-when-due (boolean ((->> r :vendor-by-id :automatically-paid-when-due (map :id) set) client-id))] + (conj on-success {:default-account (:account-for-vendor r) + :schedule-payment-dom schedule-payment-dom + :automatically-paid-when-due automatically-paid-when-due + :vendor-autopay? (or automatically-paid-when-due (boolean schedule-payment-dom))}))) + :on-failure on-failure}})) diff --git a/src/cljs/auto_ap/views/components/invoice_table.cljs b/src/cljs/auto_ap/views/components/invoice_table.cljs index 6b73ccd8..bb0e77f4 100644 --- a/src/cljs/auto_ap/views/components/invoice_table.cljs +++ b/src/cljs/auto_ap/views/components/invoice_table.cljs @@ -119,6 +119,7 @@ (let [{:keys [client status payments expense-accounts invoice-number date due total outstanding-balance id vendor source-url] :as i} invoice accounts-by-id @(re-frame/subscribe [::subs/accounts-by-id client]) unautopay-states @(re-frame/subscribe [::status/multi ::unautopay]) + editing-states @(re-frame/subscribe [::status/multi ::edits]) account->name #(:name (accounts-by-id (:id %)))] [grid/row {:class (:class i) :id id :checkable? checkable? :entity invoice} (when-not selected-client @@ -213,7 +214,14 @@ (when (and (get actions :edit) (not= ":voided" (:status i))) [buttons/fa-icon {:icon "fa-pencil" - :event [::form/editing i]}]) + :class (status/class-for (get editing-states id)) + :event + [::events/vendor-preferences-requested {:client-id (:id client) + :vendor-id (:id vendor) + :on-success [::form/editing i] + :on-failure [] + :owns-state {:multi ::edits + :which (:id i)}}]}]) (when (and (get actions :void) (= (:outstanding-balance i) (:total i)) (not= ":voided" (:status i))) [buttons/sl-icon {:icon "icon-bin-2" diff --git a/src/cljs/auto_ap/views/pages/invoices/form.cljs b/src/cljs/auto_ap/views/pages/invoices/form.cljs index 612d4557..8eaf4d20 100644 --- a/src/cljs/auto_ap/views/pages/invoices/form.cljs +++ b/src/cljs/auto_ap/views/pages/invoices/form.cljs @@ -90,87 +90,63 @@ expense-accounts)}} invoice-read]}]})) -(re-frame/reg-sub - ::add-and-print-query - (fn [_ [_ bank-account-id type]] - (let [{{:keys [invoice-number date location total expense-accounts scheduled-payment vendor client]} :data} @(re-frame/subscribe [::forms/form ::form])] - {:venia/operation {:operation/type :mutation - :operation/name "AddAndPrintInvoice"} - :venia/queries [{:query/data [:add-and-print-invoice - {:invoice {:date date - :vendor-id (:id vendor) - :client-id (:id client) - :scheduled-payment scheduled-payment - :invoice-number invoice-number - :location location - :total total - :expense-accounts (map (fn [ea] - {:id (when-not (str/starts-with? (:id ea) "new-") - (:id ea)) - :account_id (:id (:account ea)) - :location (:location ea) - :amount (:amount ea)}) - expense-accounts)} - :bank-account-id bank-account-id - :type type} - [:pdf-url [:invoices invoice-read]]]}]}))) - - ;; EVENTS -(re-frame/reg-event-db +(re-frame/reg-event-fx ::updated - (fn [db [_ _ command]] - (if (= :create command) - (-> db - (forms/stop-form ::form ) - (forms/start-form ::form {:client @(re-frame/subscribe [::subs/client]) - :status :unpaid - :date (date->str (c/now) standard)})) - db))) + [(re-frame/inject-cofx ::inject/sub [::subs/client])] + (fn [{:keys [db] ::subs/keys [client]} [_ _ command]] + (when (= :create command) + {:db + (-> db + (forms/stop-form ::form ) + (forms/start-form ::form {:client client + :status :unpaid + :date (date->str (c/now) standard)}))}))) -(re-frame/reg-event-db +(re-frame/reg-event-fx ::adding - (fn [db [_ new]] - (let [locations @(re-frame/subscribe [::subs/locations-for-client (:client new)]) - accounts-by-id @(re-frame/subscribe [::subs/accounts-by-id (:client new)])] - (-> db (forms/start-form ::form (assoc new :expense-accounts - (expense-accounts-field/from-graphql (:expense-accounts new) - accounts-by-id - 0.0 - locations))))))) -(re-frame/reg-event-db + [(re-frame/inject-cofx ::inject/sub (fn [[_ which _]] + [::subs/accounts-by-id (:id (:client which))])) + (re-frame/inject-cofx ::inject/sub (fn [[_ which _]] + [::subs/locations-for-client (:id (:client which))]))] + (fn [{:keys [db] ::subs/keys [locations-for-client accounts-by-id]} [_ new]] + {:db + (-> db (forms/start-form ::form (assoc new :expense-accounts + (expense-accounts-field/from-graphql (:expense-accounts new) + accounts-by-id + 0.0 + locations-for-client))))})) +(re-frame/reg-event-fx ::editing - (fn [db [_ which]] - (let [accounts-by-id @(re-frame/subscribe [::subs/accounts-by-id (:client which)]) - vendor (get @(re-frame/subscribe [::subs/vendors-by-id]) (:id (:vendor which))) - edit-invoice (update which :date #(date->str % standard)) + [(re-frame/inject-cofx ::inject/sub (fn [[_ which _]] + [::subs/accounts-by-id (:id (:client which))])) + (re-frame/inject-cofx ::inject/sub (fn [[_ which _]] + [::subs/locations-for-client (:id (:client which))]))] + (fn [{:keys [db] ::subs/keys [locations-for-client accounts-by-id]} [_ which vendor-preferences]] + (let [edit-invoice (update which :date #(date->str % standard)) edit-invoice (update edit-invoice :due #(date->str % standard)) edit-invoice (update edit-invoice :scheduled-payment #(date->str % standard)) - edit-invoice (assoc edit-invoice :original edit-invoice) - edit-invoice (assoc edit-invoice :vendor-autopay? (boolean ((set (map :id (:automatically-paid-when-due vendor))) - (:id (:client which))))) - locations @(re-frame/subscribe [::subs/locations-for-client (:id (:client which))]) - ] - (-> db - (forms/start-form ::form {:id (:id edit-invoice) - :payments (:payments edit-invoice) - :status (:status edit-invoice) - :date (:date edit-invoice) - :due (:due edit-invoice) - :vendor-autopay? (boolean ((set (map :id (:automatically-paid-when-due vendor))) - (:id (:client which)))) - :scheduled-payment (:scheduled-payment edit-invoice) - :invoice-number (:invoice-number edit-invoice) - :total (:total edit-invoice) - :original edit-invoice - :vendor (:vendor edit-invoice) - :client (:client edit-invoice) - :expense-accounts (expense-accounts-field/from-graphql (:expense-accounts which) - accounts-by-id - (:total which) - locations)}))))) + edit-invoice (assoc edit-invoice :original edit-invoice)] + {:db + (-> db + (forms/start-form ::form {:id (:id edit-invoice) + :payments (:payments edit-invoice) + :status (:status edit-invoice) + :date (:date edit-invoice) + :due (:due edit-invoice) + :vendor-preferences vendor-preferences + :scheduled-payment (:scheduled-payment edit-invoice) + :invoice-number (:invoice-number edit-invoice) + :total (:total edit-invoice) + :original edit-invoice + :vendor (:vendor edit-invoice) + :client (:client edit-invoice) + :expense-accounts (expense-accounts-field/from-graphql (:expense-accounts which) + accounts-by-id + (:total which) + locations-for-client)}))}))) @@ -179,50 +155,87 @@ ::changed (forms/change-handler ::form (fn [data field value] - (cond (= [:total] field) - [[:expense-accounts] (recalculate-amounts (:expense-accounts data) value)] + (cond + (= [:vendor-preferences] field) + (cond-> [] + (expense-accounts-field/can-replace-with-default? (:expense-accounts data)) + (into [[:expense-accounts] (expense-accounts-field/default-account (:expense-accounts data) + (:default-account value) + (:total data) + (:locations (:client data)))]) - (and (= [:date] field) - (:schedule-payment-dom data)) - [[:scheduled-payment] (date->str (next-dom (str->date value standard) (:schedule-payment-dom data)) standard) ] + (:automatically-paid-when-due value) + (into [[:scheduled-payment] (:due data) + [:schedule-when-due] true]) - (and (= [:schedule-when-due] field) value) - [[:scheduled-payment] (:due data)] + (:schedule-payment-dom value) + (into [[:scheduled-payment] (date->str (next-dom (str->date (:date data) standard) (:schedule-payment-dom value)) standard)])) - (and (= [:due] field) (:schedule-when-due data)) - [[:scheduled-payment] value] + (= [:total] field) + [[:expense-accounts] (recalculate-amounts (:expense-accounts data) value)] - :else - [])))) + (and (= [:date] field) + (:schedule-payment-dom (:vendor-preferences data))) + [[:scheduled-payment] (date->str (next-dom (str->date value standard) (:schedule-payment-dom (:vendor-preferences data))) standard) ] + + (and (= [:schedule-when-due] field) value) + [[:scheduled-payment] (:due data)] + + (and (= [:due] field) (:schedule-when-due data)) + [[:scheduled-payment] value] + + :else + [])))) (re-frame/reg-event-db ::maybe-change-client - [ (forms/in-form ::form)] + [ (forms/in-form ::form) ] (fn [{:keys [data] :as f} [_ c]] - (if (:id data) - f - (assoc-in f [:data :client] c)))) + (if (and (not (:id data)) + (:id c)) + (assoc-in f [:data :client] c) + f))) (re-frame/reg-event-fx - ::add-and-print - [with-user (forms/in-form ::form)] - (fn [{:keys [user]} [_ bank-account-id type]] - {:graphql - {:token user - :owns-state {:single ::form} - :query-obj @(re-frame/subscribe [::add-and-print-query bank-account-id type]) - :on-success [::added-and-printed] - :on-error [::forms/save-error ::form]}})) + ::add-and-print + [with-user (forms/in-form ::form)] + (fn [{:keys [user] + {{:keys [invoice-number date location total expense-accounts scheduled-payment vendor client]} :data} :db} [_ bank-account-id type]] + {:graphql + {:token user + :owns-state {:single ::form} + :query-obj {:venia/operation {:operation/type :mutation + :operation/name "AddAndPrintInvoice"} + :venia/queries [{:query/data [:add-and-print-invoice + {:invoice {:date date + :vendor-id (:id vendor) + :client-id (:id client) + :scheduled-payment scheduled-payment + :invoice-number invoice-number + :location location + :total total + :expense-accounts (map (fn [ea] + {:id (when-not (str/starts-with? (:id ea) "new-") + (:id ea)) + :account_id (:id (:account ea)) + :location (:location ea) + :amount (:amount ea)}) + expense-accounts)} + :bank-account-id bank-account-id + :type type} + [:pdf-url [:invoices invoice-read]]]}]} + :on-success [::added-and-printed] + :on-error [::forms/save-error ::form]}})) (re-frame/reg-event-fx ::saving - [with-user (forms/in-form ::form)] - (fn [{:keys [user] {:keys [data]} :db} _] + [with-user (forms/in-form ::form) (re-frame/inject-cofx ::inject/sub [::edit-query]) (re-frame/inject-cofx ::inject/sub [::create-query])] + (fn [{:keys [user] {:keys [data]} :db ::keys [edit-query create-query]} _] {:graphql {:token user :owns-state {:single ::form} :query-obj (if (:id data) - @(re-frame/subscribe [::edit-query]) - @(re-frame/subscribe [::create-query])) + edit-query + create-query) :on-success (fn [result] [::updated (assoc (if (:id data) @@ -239,12 +252,11 @@ [with-user (forms/in-form ::form)] (fn [{:keys [db]} [_ fwd-event]] (if (and (:scheduled-payment (:data db)) - (not (:vendor-autopay? (:data db)))) + (not (:vendor-autopay? (:vendor-preferences (:data db))))) {:dispatch [::modal/modal-requested {:title "Scheduled payment date" :body [:div "This vendor isn't set up to be automatically paid. On " (:scheduled-payment (:data db)) - " the invoice will be marked as paid, but no payment will be made to the vendor. " "Are you sure you want to continue?"] :confirm {:value "Save" @@ -263,66 +275,18 @@ (re-frame/reg-event-db ::checks-printed - (fn [db _] + (fn [db [_]] db)) -#_(re-frame/reg-sub - ::client-accounts - :<- [::forms/field ::form [:client]] - :<- [::subs/all-accounts] - (fn [[client all-accounts]] - (subs/accounts-by-id all-accounts client))) - (re-frame/reg-event-fx ::changed-vendor - [with-user (forms/in-form ::form)] - (fn [{:keys [user] {{:keys [client date due expense-accounts total]} :data} :db} [_ vendor]] - (when (:id vendor) - {:graphql {:token user - :query-obj {:venia/queries [[:vendor-by-id - {:id (:id vendor)} - [[:automatically-paid-when-due [:id]] - [:schedule-payment-dom [[:client [:id]] :dom]] - [:default-account [:id]]]] - [:account-for-vendor - {:vendor-id (:id vendor) - :client-id (:id client)} - [:name :id :numeric-code :location]]]} - :on-success (fn [r] - (let [schedule-payment-dom (->> r - :vendor-by-id - :schedule-payment-dom - (filter (fn [spd] - (= (-> spd :client :id) - (:id client)))) - first - :dom) - - changes (cond-> [] - (expense-accounts-field/can-replace-with-default? expense-accounts) - (into [[:expense-accounts] (expense-accounts-field/default-account expense-accounts - (:account-for-vendor r) - total - (:locations client))]) - - (boolean ((->> r :vendor-by-id :automatically-paid-when-due (map :id) set) (:id client))) - (into [[:scheduled-payment] due - [:schedule-when-due] true - [:vendor-autopay? ] true]) - - schedule-payment-dom - (into [[:scheduled-payment] (date->str (next-dom (str->date date standard) schedule-payment-dom) standard) - [:vendor-autopay?] true]) - - true - (into [[:schedule-payment-dom] schedule-payment-dom]))] - (if (seq changes) - (into [::changed ] changes) - [:ignore])) - ) - :on-failure [:bad] - }}))) - + [(forms/in-form ::form)] + (fn [{{{:keys [client]} :data} :db} [_ vendor]] + (when (and (:id client) (:id vendor)) + {:dispatch [::events/vendor-preferences-requested {:client-id (:id client) + :vendor-id (:id vendor) + :on-success [::changed [:vendor-preferences]] + :on-failure [:hello]}]}))) ;; VIEWS @@ -338,7 +302,7 @@ {::track/register [{:id ::client :subscription [::subs/client] :event-fn (fn [c] - + (println "changing client?") [::maybe-change-client c])} {:id ::vendor-change :subscription [::forms/field ::form [:vendor]] @@ -357,6 +321,7 @@ {:keys [form-inline field raw-field error-notification submit-button ]} invoice-form can-submit? (boolean @(re-frame/subscribe [::can-submit])) status @(re-frame/subscribe [::status/single ::form]) + active-client @(re-frame/subscribe [::subs/client]) exists? (:id data) can-change-amount? (#{:unpaid ":unpaid"} (:status data)) min-total (if (= (:total (:original data)) (:outstanding-balance (:original data))) @@ -383,13 +348,13 @@ :else nil)]) [:<> - (when-not @(re-frame/subscribe [::subs/client]) + (when-not active-client (field [:span "Client" [:span.has-text-danger " *"]] [typeahead-v3 {:entities @(re-frame/subscribe [::subs/clients]) :entity->text :name :type "typeahead-v3" - :auto-focus (if @(re-frame/subscribe [::subs/client]) false true) + :auto-focus (if active-client false true) :field [:client] :disabled exists? :spec ::invoice/client}])) @@ -401,7 +366,7 @@ {:query i} [:name :id]]) :type "typeahead-v3" - :auto-focus (if @(re-frame/subscribe [::subs/client]) true false) + :auto-focus (if active-client true false) :field [:vendor]}]) (field [:span "Date" @@ -477,7 +442,7 @@ :descriptor "expense account" :locations (:locations (:client data)) :max (:total data) - :client (or (:client data) @(re-frame/subscribe [::subs/client])) + :client (or (:client data) active-client) :field [:expense-accounts]}]) {:key (str (:id (:vendor data) "none") "-" (:id (:client data) "none") )}) From 8bba073e07b2e7ed45ca66e50bef6b0a659db3ca Mon Sep 17 00:00:00 2001 From: Bryce Covert Date: Mon, 11 Apr 2022 10:02:08 -0700 Subject: [PATCH 05/12] trimming more vendor usage. --- .../components/expense_accounts_field.cljs | 4 +- .../auto_ap/views/pages/admin/rules/form.cljs | 116 ++++++++++++------ .../pages/admin/vendors/merge_dialog.cljs | 48 ++++---- .../auto_ap/views/pages/invoices/form.cljs | 1 - 4 files changed, 105 insertions(+), 64 deletions(-) diff --git a/src/cljs/auto_ap/views/components/expense_accounts_field.cljs b/src/cljs/auto_ap/views/components/expense_accounts_field.cljs index bbe45003..5a2ba6ef 100644 --- a/src/cljs/auto_ap/views/components/expense_accounts_field.cljs +++ b/src/cljs/auto_ap/views/components/expense_accounts_field.cljs @@ -1,8 +1,6 @@ (ns auto-ap.views.components.expense-accounts-field - (:require [auto-ap.forms :as forms] - [auto-ap.subs :as subs] + (:require [auto-ap.subs :as subs] [auto-ap.views.components.typeahead :refer [typeahead-v3]] - [auto-ap.views.components.money-field :refer [money-field]] [auto-ap.views.utils :refer [bind-field dispatch-event ->$]] [goog.string :as gstring] [re-frame.core :as re-frame] diff --git a/src/cljs/auto_ap/views/pages/admin/rules/form.cljs b/src/cljs/auto_ap/views/pages/admin/rules/form.cljs index 02fec28e..e80e75c3 100644 --- a/src/cljs/auto_ap/views/pages/admin/rules/form.cljs +++ b/src/cljs/auto_ap/views/pages/admin/rules/form.cljs @@ -1,19 +1,27 @@ (ns auto-ap.views.pages.admin.rules.form - (:require [auto-ap.entities.transaction-rule :as entity] - [auto-ap.forms :as forms] - [auto-ap.subs :as subs] - [auto-ap.views.components.button-radio :refer [button-radio]] - [auto-ap.views.components.expense-accounts-field :as expense-accounts-field :refer [expense-accounts-field]] - [auto-ap.views.components.layouts :as layouts] - [auto-ap.views.components.typeahead :refer [typeahead-v3]] - [auto-ap.views.pages.admin.rules.common :refer [default-read]] - [auto-ap.views.pages.admin.rules.results-modal :as results-modal] - [auto-ap.views.utils :refer [dispatch-event with-user]] - [clojure.spec.alpha :as s] - [clojure.string :as str] - [re-frame.core :as re-frame] - [auto-ap.status :as status] - [vimsical.re-frame.cofx.inject :as inject])) + (:require + [auto-ap.entities.transaction-rule :as entity] + [auto-ap.events :as events] + [auto-ap.forms :as forms] + [auto-ap.status :as status] + [auto-ap.subs :as subs] + [auto-ap.views.components.button-radio :refer [button-radio]] + [auto-ap.views.components.expense-accounts-field + :as expense-accounts-field + :refer [expense-accounts-field]] + [auto-ap.views.components.layouts :as layouts] + [auto-ap.views.components.typeahead :refer [typeahead-v3]] + [auto-ap.views.components.typeahead.vendor + :refer [search-backed-typeahead]] + [auto-ap.views.pages.admin.rules.common :refer [default-read]] + [auto-ap.views.pages.admin.rules.results-modal :as results-modal] + [auto-ap.views.utils :refer [dispatch-event with-user]] + [clojure.spec.alpha :as s] + [clojure.string :as str] + [re-frame.core :as re-frame] + [reagent.core :as r] + [vimsical.re-frame.cofx.inject :as inject] + [vimsical.re-frame.fx.track :as track])) ;; SUBS @@ -161,25 +169,35 @@ xs))))))))) +(re-frame/reg-event-fx + ::changed-vendor + [(forms/in-form ::form)] + (fn [{{{:keys [client]} :data} :db} [_ vendor]] + (when (and (:id client) (:id vendor)) + {:dispatch [::events/vendor-preferences-requested {:client-id (:id client) + :vendor-id (:id vendor) + :on-success [::changed [:vendor-preferences]]}]}))) + +(re-frame/reg-event-fx + ::changed-client + [(forms/in-form ::form)] + (fn [{{{:keys [vendor]} :data} :db} [_ client]] + (when (and (:id client) (:id vendor)) + {:dispatch [::events/vendor-preferences-requested {:client-id (:id client) + :vendor-id (:id vendor) + :on-success [::changed [:vendor-preferences]]}]}))) + (re-frame/reg-event-db ::changed (forms/change-handler ::form (fn [data field value] - (cond (and (= [:vendor] field) + (cond (and (= [:vendor-preferences] field) value (expense-accounts-field/can-replace-with-default? (:accounts data))) [[:accounts] (expense-accounts-field/default-account (:accounts data) - @(re-frame/subscribe [::subs/vendor-default-account (:id value) (:client data)]) + (:default-account value) (:total data) [])] - - (= [:client] field) - [[:accounts] (expense-accounts-field/default-account (:accounts data) - @(re-frame/subscribe [::subs/vendor-default-account (:id (:vendor data)) value]) - (:total data) - []) - [:bank-account] nil] - :else [])))) @@ -228,14 +246,30 @@ :submit-event [::saving ] :id ::form})) +(re-frame/reg-event-fx + ::mounted + (fn [_ _] + {::track/register [{:id ::client + :subscription [::forms/field ::form [:client]] + :event-fn (fn [c] + [::changed-client c])} + {:id ::vendor-change + :subscription [::forms/field ::form [:vendor]] + :event-fn (fn [v] + [::changed-vendor v])}]})) +(re-frame/reg-event-fx + ::unmounted + (fn [] + {::track/dispose [{:id ::client} + {:id ::vendor-change}]})) -(defn form [params] - [layouts/side-bar {:on-close (dispatch-event [::forms/form-closing ::form ])} - (let [{:keys [data id]} @(re-frame/subscribe [::forms/form ::form]) +(defn form-contents [params] + [layouts/side-bar {:on-close (dispatch-event [::forms/form-closing ::form ])} + (let [{:keys [data id]} @(re-frame/subscribe [::forms/form ::form]) {:keys [form-inline field raw-field error-notification submit-button ]} rule-form - default-note @(re-frame/subscribe [::default-note]) - test-state @(re-frame/subscribe [::status/single ::test])] + default-note @(re-frame/subscribe [::default-note]) + test-state @(re-frame/subscribe [::status/single ::test])] ^{:key id} (form-inline (assoc params :title "New Transaction Rule") [:<> @@ -314,12 +348,12 @@ [:h2.title.is-4 "Outcomes"] (field "Assign Vendor" - [typeahead-v3 {:entities-by-id @(re-frame/subscribe [::subs/vendors-by-id]) - :entity-index @(re-frame/subscribe [::subs/all-vendors-index]) - :entity->text :name - :type "typeahead-v3" - :field [:vendor] - :spec ::entity/vendor}]) + [search-backed-typeahead {:search-query (fn [i] + [:search_vendor + {:query i} + [:name :id]]) + :type "typeahead-v3" + :field [:vendor]}]) (with-meta (field nil @@ -330,7 +364,7 @@ :locations (into ["Shared"] @(re-frame/subscribe [::subs/locations-for-client-or-bank-account (:id (:client data)) (:id (:bank-account data))])) :max 100 :field [:accounts]}]) - {:key (some-> data :vendor :id str)}) + {:key (str (some-> data :vendor :id str) "-" (some-> data :client :id str))}) (field "Approval Status" [button-radio @@ -357,3 +391,11 @@ "Test Rule"]] [:div.column (submit-button "Save")]]]))]) + +(defn form [_] + (r/create-class + {:display-name "rule-form" + :component-did-mount #(re-frame/dispatch [::mounted]) + :component-will-unmount #(re-frame/dispatch [::unmounted]) + :reagent-render (fn [p] + [form-contents p])})) diff --git a/src/cljs/auto_ap/views/pages/admin/vendors/merge_dialog.cljs b/src/cljs/auto_ap/views/pages/admin/vendors/merge_dialog.cljs index 33e387e5..65182f73 100644 --- a/src/cljs/auto_ap/views/pages/admin/vendors/merge_dialog.cljs +++ b/src/cljs/auto_ap/views/pages/admin/vendors/merge_dialog.cljs @@ -1,11 +1,13 @@ (ns auto-ap.views.pages.admin.vendors.merge-dialog - (:require [auto-ap.forms :as forms] - [auto-ap.status :as status] - [auto-ap.subs :as subs] - [auto-ap.views.components.modal :as modal] - [auto-ap.views.components.typeahead :refer [typeahead-v3]] - [auto-ap.views.utils :refer [dispatch-event]] - [re-frame.core :as re-frame])) + (:require + [auto-ap.forms :as forms] + [auto-ap.status :as status] + [auto-ap.subs :as subs] + [auto-ap.views.components.modal :as modal] + [auto-ap.views.components.typeahead.vendor + :refer [search-backed-typeahead]] + [auto-ap.views.utils :refer [dispatch-event]] + [re-frame.core :as re-frame])) (re-frame/reg-sub ::can-submit @@ -21,29 +23,29 @@ :id ::form})) (defn form [] - (let [{:keys [data active? error id]} @(re-frame/subscribe [::forms/form ::form]) - {:keys [form-inline horizontal-field field raw-field error-notification submit-button]} merge-form] + (let [_ @(re-frame/subscribe [::forms/form ::form]) + {:keys [form-inline field]} merge-form] (form-inline {} [:<> (field "Form Vendor (will be deleted)" - [typeahead-v3 {:entities-by-id @(re-frame/subscribe [::subs/vendors-by-id]) - :entity-index @(re-frame/subscribe [::subs/all-vendors-index]) - :type "typeahead-v3" - :auto-focus true - :entity->text (fn [x] - (str (:name x) " (" (reduce + 0 (map :count (:usage x))) " usages)") ) - :field [:from]}]) + [search-backed-typeahead {:search-query (fn [i] + [:search_vendor + {:query i} + [:name :id]]) + :type "typeahead-v3" + :auto-focus true + :field [:from]}]) (field "To Vendor" - [typeahead-v3 {:entities-by-id @(re-frame/subscribe [::subs/vendors-by-id]) - :entity-index @(re-frame/subscribe [::subs/searchable-vendors-index]) - :type "typeahead-v3" - :entity->text (fn [x] - (str (:name x) " (" (reduce + 0 (map :count (:usage x))) " usages)") ) - :field [:to]}])]))) + [search-backed-typeahead {:search-query (fn [i] + [:search_vendor + {:query i} + [:name :id]]) + :type "typeahead-v3" + :field [:to]}])]))) (re-frame/reg-event-fx ::show @@ -68,7 +70,7 @@ (re-frame/reg-event-fx ::save [(forms/in-form ::form)] - (fn [{{{:keys [from to]} :data :as merge-vendors-form} :db :as g} _] + (fn [{{{:keys [from to]} :data} :db} _] (let [user @(re-frame/subscribe [::subs/token])] {:graphql {:token user diff --git a/src/cljs/auto_ap/views/pages/invoices/form.cljs b/src/cljs/auto_ap/views/pages/invoices/form.cljs index 8eaf4d20..69479561 100644 --- a/src/cljs/auto_ap/views/pages/invoices/form.cljs +++ b/src/cljs/auto_ap/views/pages/invoices/form.cljs @@ -302,7 +302,6 @@ {::track/register [{:id ::client :subscription [::subs/client] :event-fn (fn [c] - (println "changing client?") [::maybe-change-client c])} {:id ::vendor-change :subscription [::forms/field ::form [:vendor]] From e4da1376f84c286518fe5cf638157accd30d9501 Mon Sep 17 00:00:00 2001 From: Bryce Covert Date: Mon, 11 Apr 2022 10:16:49 -0700 Subject: [PATCH 06/12] more vendor replacement. --- src/clj/auto_ap/datomic/invoices.clj | 1 + .../views/pages/admin/rules/side_bar.cljs | 22 +++++++++---------- .../auto_ap/views/pages/import_invoices.cljs | 22 +++++++++---------- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/clj/auto_ap/datomic/invoices.clj b/src/clj/auto_ap/datomic/invoices.clj index 66acaa95..1c53e157 100644 --- a/src/clj/auto_ap/datomic/invoices.clj +++ b/src/clj/auto_ap/datomic/invoices.clj @@ -303,6 +303,7 @@ schedule-payment-dom (-> date + coerce/to-date-time (next-dom schedule-payment-dom) coerce/to-date) :else nil) diff --git a/src/cljs/auto_ap/views/pages/admin/rules/side_bar.cljs b/src/cljs/auto_ap/views/pages/admin/rules/side_bar.cljs index c16556ac..2c2d7187 100644 --- a/src/cljs/auto_ap/views/pages/admin/rules/side_bar.cljs +++ b/src/cljs/auto_ap/views/pages/admin/rules/side_bar.cljs @@ -1,22 +1,22 @@ (ns auto-ap.views.pages.admin.rules.side-bar (:require - [re-frame.core :as re-frame] - [auto-ap.subs :as subs] + [auto-ap.views.components.typeahead.vendor + :refer [search-backed-typeahead]] + [auto-ap.views.pages.data-page :as data-page] [auto-ap.views.utils :refer [dispatch-value-change]] - [auto-ap.views.components.typeahead :refer [typeahead-v3]] - [auto-ap.views.pages.data-page :as data-page])) - + [re-frame.core :as re-frame])) (defn rule-side-bar [{:keys [data-page]}] [:div [:p.menu-label "Vendor"] [:div - [typeahead-v3 {:entities-by-id @(re-frame/subscribe [::subs/vendors-by-id]) - :entity-index @(re-frame/subscribe [::subs/searchable-vendors-index]) - :on-change #(re-frame/dispatch [::data-page/filter-changed data-page :vendor (some-> % (select-keys [:name :id]))]) - :entity->text :name - :type "typeahead-v3" - :value @(re-frame/subscribe [::data-page/filter data-page :vendor])}]] + [search-backed-typeahead {:search-query (fn [i] + [:search_vendor + {:query i} + [:name :id]]) + :type "typeahead-v3" + :on-change #(re-frame/dispatch [::data-page/filter-changed data-page :vendor %]) + :value @(re-frame/subscribe [::data-page/filter data-page :vendor])}]] [:p.menu-label "Note"] [:div diff --git a/src/cljs/auto_ap/views/pages/import_invoices.cljs b/src/cljs/auto_ap/views/pages/import_invoices.cljs index c67fb76e..68a7de8e 100644 --- a/src/cljs/auto_ap/views/pages/import_invoices.cljs +++ b/src/cljs/auto_ap/views/pages/import_invoices.cljs @@ -7,7 +7,6 @@ [auto-ap.views.components.invoices.side-bar :refer [invoices-side-bar]] [auto-ap.views.utils :refer [dispatch-event with-user ->%]] [auto-ap.utils :refer [by]] - [auto-ap.views.components.typeahead :refer [typeahead-v3]] [auto-ap.views.components.invoice-table :refer [invoice-table] :as invoice-table] [cljs.reader :as edn] [auto-ap.status :as status] @@ -18,7 +17,7 @@ [clojure.set :as set] [auto-ap.effects.forward :as forward] [goog.string :as gstring] - )) + [auto-ap.views.components.typeahead.vendor :refer [search-backed-typeahead]])) (defn dropzone [] @@ -57,15 +56,16 @@ [:p.control [:a.button.is-static "Force vendor"]] [:div.control {:style {:width "400px"}} - [typeahead-v3 {:entities-by-id @(re-frame/subscribe [::subs/vendors-by-id]) - :entity-index @(re-frame/subscribe [::subs/searchable-vendors-index]) - :entity->text :name - :name "vendor" - :type "typeahead-v3" - :on-change (fn [v] - (reset! vendor v)) - :value @vendor}]] - ] + + [search-backed-typeahead {:search-query (fn [i] + [:search_vendor + {:query i} + [:name :id]]) + :type "typeahead-v3" + :name "vendor" + :on-change (fn [v] + (reset! vendor v)) + :value @vendor}]]] [:div.tile.notification [:div.has-text-centered {:style {:padding "80px 0px" :width "100%"}} [:span From 2e4e693bc644f5acc5d5c156797d077b70ca9ad7 Mon Sep 17 00:00:00 2001 From: Bryce Covert Date: Mon, 11 Apr 2022 17:26:03 -0700 Subject: [PATCH 07/12] Supports vendors not loading --- src/clj/auto_ap/datomic/vendors.clj | 58 +++++-- src/clj/auto_ap/graphql.clj | 31 ++-- src/clj/auto_ap/graphql/transactions.clj | 13 +- src/clj/auto_ap/graphql/vendors.clj | 24 +-- src/cljs/auto_ap/events.cljs | 97 +---------- src/cljs/auto_ap/subs.cljs | 96 ----------- .../views/components/vendor_dialog.cljs | 65 +++---- .../views/pages/admin/accounts/side_bar.cljs | 3 +- src/cljs/auto_ap/views/pages/admin/rules.cljs | 51 +++--- .../views/pages/admin/rules/table.cljs | 29 ++-- .../auto_ap/views/pages/admin/vendors.cljs | 163 ++++++++---------- .../views/pages/admin/vendors/common.cljs | 15 ++ .../views/pages/admin/vendors/side_bar.cljs | 68 +------- .../views/pages/admin/vendors/table.cljs | 37 ++-- src/cljs/auto_ap/views/pages/home.cljs | 64 ++++--- src/cljs/auto_ap/views/pages/new_invoice.cljs | 75 -------- .../pages/transactions/bulk_updates.cljs | 72 ++++++-- .../views/pages/transactions/form.cljs | 102 ++++++----- .../views/pages/transactions/table.cljs | 6 +- 19 files changed, 412 insertions(+), 657 deletions(-) create mode 100644 src/cljs/auto_ap/views/pages/admin/vendors/common.cljs delete mode 100644 src/cljs/auto_ap/views/pages/new_invoice.cljs diff --git a/src/clj/auto_ap/datomic/vendors.clj b/src/clj/auto_ap/datomic/vendors.clj index 09899c38..0e3c7bed 100644 --- a/src/clj/auto_ap/datomic/vendors.clj +++ b/src/clj/auto_ap/datomic/vendors.clj @@ -1,8 +1,10 @@ (ns auto-ap.datomic.vendors - (:require [datomic.api :as d] - [auto-ap.graphql.utils :refer [limited-clients ]] + (:require + [auto-ap.datomic :refer [conn merge-query uri add-sorter-fields apply-pagination merge-query apply-sort-3]] + [auto-ap.graphql.utils :refer [limited-clients]] + [clojure.string :as str] + [datomic.api :as d])) - [auto-ap.datomic :refer [uri conn merge-query]])) (defn <-datomic [a] (cond-> a (:vendor/legal-entity-tin-type a) (update :vendor/legal-entity-tin-type :db/ident) @@ -58,6 +60,30 @@ (update usages v (fnil conj []) {:client-id c :count cnt})) {}))) +(defn raw-graphql-ids [db args] + (let [query (cond-> {:query {:find [] + :in ['$] + :where []} + :args [db]} + (:sort args) (add-sorter-fields {"name" ['[?e :vendor/name ?sort-name]]} + args) + + (not (str/blank? (:name-like args))) + (merge-query {:query {:in ['?name-like] + :where ['[?e :vendor/name ?n] + '[(re-find ?name-like ?n)]]} + :args [(re-pattern (str "(?i)" (:name-like args)))]}) + + true + (merge-query {:query {:find ['?e] + :where ['[?e :vendor/name]]}}))] + + + (cond->> query + true (d/query) + true (apply-sort-3 args) + true (apply-pagination args)))) + (defn trim-usage [v limited-clients] (->> (if limited-clients (update v :usage (fn [usages] @@ -73,17 +99,23 @@ )) +(defn graphql-results [ids db args] + (let [results (->> (d/pull-many db default-read ids) + (group-by :db/id)) + vendors (->> ids + (map results) + (map first) + (map #(cleanse (:id args) %)) + (map <-datomic))] + vendors)) + (defn get-graphql [args] - (->> (cond-> {:query {:find [(list 'pull '?e default-read)] - :in ['$] - :where ['[?e :vendor/name]]} - :args [(d/db (d/connect uri))]}) - (d/query) - (map first) - (map #(cleanse (:id args) %)) - (map <-datomic) - (map #(trim-usage % (limited-clients (:id args)))) - #_(map #(assoc % :usage (get usages (:db/id %)))))) + (let [db (d/db conn) + {ids-to-retrieve :ids matching-count :count} (raw-graphql-ids db args)] + [(->> (graphql-results ids-to-retrieve db args)) + matching-count]) + + ) (defn get-graphql-by-id [args id] (->> (cond-> {:query {:find [(list 'pull '?e default-read)] diff --git a/src/clj/auto_ap/graphql.clj b/src/clj/auto_ap/graphql.clj index 20192cdb..60f9b147 100644 --- a/src/clj/auto_ap/graphql.clj +++ b/src/clj/auto_ap/graphql.clj @@ -294,11 +294,17 @@ :sales_order_total {:type :money} :sales_order_tax {:type :money}}} + :vendor_page {:fields {:vendors {:type '(list :vendor)} + :count {:type 'Int} + :total {:type 'Int} + :start {:type 'Int} + :end {:type 'Int}}} + :reminder_page {:fields {:reminders {:type '(list :reminder)} - :count {:type 'Int} - :total {:type 'Int} - :start {:type 'Int} - :end {:type 'Int}}} + :count {:type 'Int} + :total {:type 'Int} + :start {:type 'Int} + :end {:type 'Int}}} :check_result {:fields {:invoices {:type '(list :invoice)} :pdf_url {:type 'String}}} @@ -398,9 +404,13 @@ - :vendor {:type '(list :vendor) + :vendor {:type :vendor_page + :args {:name_like {:type 'String} + :start {:type 'Int} + :per_page {:type 'Int} + :sort {:type '(list :sort_item)}} :resolve :get-vendor} - :user {:type '(list :user) + :user {:type '(list :user) :resolve :get-user} :vendor_by_id {:type :vendor :args {:id {:type :id}} @@ -708,7 +718,7 @@ '[(get-else $ ?je :journal-entry-line/debit 0.0) ?debit] '[(get-else $ ?je :journal-entry-line/credit 0.0) ?credit]]} :args [(d/db (d/connect uri)) client_id]})) - bills-due-soon (d/query {:query {:find '[?due ?outstanding ?invoice-number ?vendor-id] + bills-due-soon (d/query {:query {:find '[?due ?outstanding ?invoice-number ?vendor-id ?vendor-name] :in '[$ ?client ?due-before] :where ['[?i :invoice/client ?client] '[?i :invoice/status :invoice-status/unpaid] @@ -716,7 +726,8 @@ '[(<= ?due ?due-before)] '[?i :invoice/outstanding-balance ?outstanding] '[?i :invoice/invoice-number ?invoice-number] - '[?i :invoice/vendor ?vendor-id]]} + '[?i :invoice/vendor ?vendor-id] + '[?vendor-id :vendor/name ?vendor-name]]} :args [(d/db (d/connect uri)) client_id (coerce/to-date (t/plus (time/local-now) (t/days 180)))]}) outstanding-checks (reduce + @@ -754,10 +765,10 @@ {:beginning_balance total-cash :outstanding_payments outstanding-checks - :invoices_due_soon (mapv (fn [[due outstanding invoice-number vendor-id]] + :invoices_due_soon (mapv (fn [[due outstanding invoice-number vendor-id vendor-name]] {:due (coerce/to-date-time due) :invoice_number invoice-number - :vendor {:id vendor-id} + :vendor {:id vendor-id :name vendor-name} :outstanding_balance outstanding}) bills-due-soon) :upcoming_credits (into (mapv diff --git a/src/clj/auto_ap/graphql/transactions.clj b/src/clj/auto_ap/graphql/transactions.clj index ed234116..91e8c19b 100644 --- a/src/clj/auto_ap/graphql/transactions.clj +++ b/src/clj/auto_ap/graphql/transactions.clj @@ -134,13 +134,18 @@ (defn bulk-code-transactions [context args _] (assert-admin (:id context)) + (when-not (:client_id args) + (throw (ex-info "Client is required" + {:validation-error "client is required"}))) (let [args (assoc args :id (:id context)) locations (:client/locations (d/pull (d/db conn) [:client/locations] - (:client_id (:filters args)))) + (:client_id args))) all-ids (all-ids-not-locked (get-ids-matching-filters args)) transactions (d/pull-many (d/db conn) '[:db/id :transaction/amount] (vec all-ids)) account-total (reduce + 0 (map (fn [x] (:percentage x)) (:accounts args)))] + (log/info "client is" locations) + (when (and (seq (:accounts args)) @@ -149,14 +154,13 @@ (throw (ex-info error {:validation-error error})))) (doseq [a (:accounts args) - :let [{:keys [:account/location :account/name]} (d/entity (d/db conn) (:account_id a)) - client (d/entity (d/db conn) (:client_id (:filters args)))]] + :let [{:keys [:account/location :account/name]} (d/entity (d/db conn) (:account_id a))]] (when (and location (not= location (:location a))) (let [err (str "Account " name " uses location " (:location a) ", but is supposed to be " location)] (throw (ex-info err {:validation-error err}) ))) (when (and (not location) - (not (get (into #{"Shared"} (:client/locations client)) + (not (get (into #{"Shared"} locations) (:location a)))) (let [err (str "Account " name " uses location " (:location a) ", but doesn't belong to the client.")] (throw (ex-info err {:validation-error err}) )))) @@ -599,6 +603,7 @@ :resolve :mutation/bulk-change-transaction-status} :bulk_code_transactions {:type :message :args {:filters {:type :transaction_filters} + :client_id {:type :id} :vendor {:type :id} :approval_status {:type :transaction_approval_status} :accounts {:type '(list :edit_percentage_account)} diff --git a/src/clj/auto_ap/graphql/vendors.clj b/src/clj/auto_ap/graphql/vendors.clj index 037d3e09..62820f8f 100644 --- a/src/clj/auto_ap/graphql/vendors.clj +++ b/src/clj/auto_ap/graphql/vendors.clj @@ -1,13 +1,11 @@ (ns auto-ap.graphql.vendors - (:require [auto-ap.graphql.utils :refer [->graphql assert-can-see-client assert-admin is-admin? enum->keyword]] - [auto-ap.datomic.vendors :as d-vendors] - [auto-ap.time :refer [parse iso-date]] - [datomic.api :as d] - [auto-ap.datomic :refer [uri remove-nils audit-transact conn]] - [clj-time.coerce :as coerce] - [clojure.set :as set] - [clojure.tools.logging :as log])) - + (:require + [auto-ap.datomic :refer [audit-transact conn remove-nils]] + [auto-ap.datomic.vendors :as d-vendors] + [auto-ap.graphql.utils + :refer [->graphql <-graphql assert-admin enum->keyword is-admin? result->page]] + [clojure.tools.logging :as log] + [datomic.api :as d])) (defn upsert-vendor [context {{:keys [id name hidden terms code print_as primary_contact secondary_contact address default_account_id invoice_reminder_schedule schedule_payment_dom terms_overrides account_overrides] :as in} :vendor} value] (when id @@ -114,10 +112,16 @@ (audit-transact transaction (:id context)) to)) -(defn get-graphql [context args value] +#_(defn get-graphql [context args value] (->graphql (d-vendors/get-graphql (assoc args :id (:id context))))) +(defn get-graphql [context args _] + (assert-admin (:id context)) + (let [args (assoc args :id (:id context)) + [vendors vendors-count ] (d-vendors/get-graphql (<-graphql args))] + (result->page vendors vendors-count :vendors args))) + (defn get-by-id [context args value] (->graphql (d-vendors/get-graphql-by-id (assoc args :id (:id context)) diff --git a/src/cljs/auto_ap/events.cljs b/src/cljs/auto_ap/events.cljs index 837dcb62..8ec5dd6d 100644 --- a/src/cljs/auto_ap/events.cljs +++ b/src/cljs/auto_ap/events.cljs @@ -13,20 +13,6 @@ (defn jwt->data [token] (js->clj (.parse js/JSON (b64/decodeString (second (str/split token #"\." )))))) -(def vendor-query - [:id :name :hidden :terms [:default-account [:name :id :location]] - [:account-overrides [[:client [:id :name]] :id [:account [:id :numeric-code :name]]]] - [:automatically-paid-when-due [:id :name]] - [:terms-overrides [[:client [:id :name]] :id :terms]] - [:schedule-payment-dom [[:client [:id :name]] :id :dom]] - [:usage [:client-id :count]] - [:primary-contact [:name :phone :email :id]] - [:secondary-contact [:id :name :phone :email]] - :print-as :invoice-reminder-schedule :code - :legal-entity-first-name :legal-entity-middle-name :legal-entity-last-name - :legal-entity-tin :legal-entity-tin-type - :legal-entity-1099-type - [:address [:street1 :street2 :city :state :zip]]]) (defn client-query [token] (cond-> [:id :name :signature-file :code :email :matches :week-a-debits :week-a-credits :week-b-debits :week-b-credits :locations :locked-until :square-auth-token @@ -45,7 +31,7 @@ (re-frame/reg-event-fx ::initialize-db - (fn [{:keys [db]} [_ token]] + (fn [{:keys [_]} [_ token]] (let [handler (:handler (bidi/match-route routes/routes (.. js/window -location -pathname)))] (cond (= :login handler) @@ -79,8 +65,7 @@ :query-obj {:venia/queries [[:client (client-query token)] - [:vendor - vendor-query] + [:accounts [:numeric-code :location :name :type :account_set :applicability :id [:client-overrides [:name :id [:client [:name :id]]]]]]]} :on-success [::received-initial] :on-error [::failed-initial]}})))) @@ -97,8 +82,7 @@ {:graphql {:token token :query-obj {:venia/queries [[:client (client-query token)] - [:vendor - vendor-query] + [:accounts [:numeric-code :name :location :type :account_set :applicability :id [:client-overrides [:name [:client [:name :id]]]]]]]} :on-success [::received-initial] @@ -109,11 +93,10 @@ (re-frame/reg-event-fx ::received-initial - (fn [{:keys [db]} [_ {accounts :accounts clients :client vendors :vendor :as x}]] + (fn [{:keys [db]} [_ {accounts :accounts clients :client}]] {:db (-> db (assoc :clients (by :id clients) ) - (assoc :vendors (by :id vendors) ) (assoc :is-initial-loading? false) (assoc :accounts accounts ) (assoc :client (or (when (= 1 (count clients)) (->> clients first :id )) @@ -178,36 +161,6 @@ :auto-ap.forms/forms nil) (auto-ap.views.pages.data-page/dispose-all))}))) -(re-frame/reg-event-db - ::imported-invoices - (fn [db [_ new-invoices]] - (assoc-in db [:invoices :pending] new-invoices))) - -(re-frame/reg-event-fx - ::view-pending-invoices - (fn [cofx []] - {:db (assoc-in (:db cofx) [:status :loading] true) - :graphql {:token (-> cofx :db :user) - :query-obj {:venia/queries [[:invoice - {:imported false :client_id (:id @(re-frame/subscribe [::subs/client]))} - [:id :total :invoice-number :date [:vendor [:name :id]] [:client [:name :id]]]]]} - - :on-success [::received-invoices :pending]}})) - -(re-frame/reg-event-fx - ::view-unpaid-invoices - (fn [cofx []] - {:db (assoc-in (:db cofx) [:status :loading] true) - :graphql {:token (-> cofx :db :user) - :query-obj {:venia/queries [[:invoice - {:imported true :client_id (:id @(re-frame/subscribe [::subs/client]))} - [:id :total :invoice-number :date [:vendor [:name :id]] [:client [:name :id]]]]]} - :on-success [::received-invoices :unpaid]}})) - -(re-frame/reg-event-db - ::submitted-new-invoice - (fn [db [_ invoice]] - (assoc db :new-invoice {}))) (re-frame/reg-event-fx ::logout @@ -218,48 +171,6 @@ :redirect (bidi/path-for routes/routes :login) :set-local-storage ["jwt" nil]})) -(re-frame/reg-event-fx - ::submit-new-invoice - (fn [{:keys [db]} [_ invoice]] - {:http {:method :post - :token (-> db :user) - :uri "/api/invoices" - :body (pr-str {:rows [(assoc invoice :imported true)]}) - :headers {"Content-Type" "application/edn"} - :on-success [::submitted-new-invoice]} - :db (assoc-in db [:new-invoice :loading?] true)})) - -(re-frame/reg-event-db - ::received-invoices - (fn [db [_ type result]] - (let [new-invoices (if (:invoice result) - (:invoice result) - result)] - (-> db - (assoc-in [:invoices type] new-invoices) - (assoc-in [:status :loading] false))))) - -(re-frame/reg-event-db - ::change-form-state - (fn [db [_ target value]] - (assoc-in db target value))) - -(re-frame/reg-event-db - ::change-nested-form-state - (fn [db [_ form more value]] - (update-in db form - (fn [x] - (assoc-in x more value))))) - - - -(re-frame/reg-event-db - ::page-failed - (fn [db [_ result]] - (println "Page failure" result) - (assoc db :page-failure result - :status nil))) - (re-frame/reg-event-db ::yodlee-merchants-received diff --git a/src/cljs/auto_ap/subs.cljs b/src/cljs/auto_ap/subs.cljs index f3ddba56..912590ae 100644 --- a/src/cljs/auto_ap/subs.cljs +++ b/src/cljs/auto_ap/subs.cljs @@ -195,102 +195,6 @@ (fn [db] (:user db))) -(re-frame/reg-sub - ::vendors - :<- [::is-admin?] - :<- [::all-vendors] - (fn [[is-admin all-vendors]] - (filter #(or (not (:hidden %)) - is-admin) all-vendors))) - -(re-frame/reg-sub - ::searchable-vendors - :<- [::is-admin?] - :<- [::client] - :<- [::all-vendors] - (fn [[is-admin client all-vendors]] - (cond is-admin - all-vendors - - client - (filter (fn [{:keys [hidden usage]}] - (or (not hidden) - (-> (first (filter #(= (:client-id %) - (:id client)) - usage)) - (:count 0) - (> 0)))) - all-vendors) - - - :else - - - (filter #(not (:hidden %)) all-vendors)))) - -(re-frame/reg-sub - ::searchable-vendors-index - :<- [::searchable-vendors] - (fn [searchable-vendors] - (doto (MiniSearch. #js {:fields #js ["name" "content"] - :storeFields #js ["id" "name" "content" "usage"]}) - (.addAll (clj->js (map (fn [v] {:id (:id v) - :name (:name v) - :content (:name v) - :usage (:usage v)}) - searchable-vendors)))))) - -(re-frame/reg-sub - ::all-vendors-index - :<- [::all-vendors] - (fn [all-vendors] - (doto (MiniSearch. #js {:fields #js ["name" "content"] - :storeFields #js ["id" "name" "content" "usage"]}) - (.addAll (clj->js (map (fn [v] {:id (:id v) - :name (:name v) - :content (:name v) - :usage (:usage v)}) - all-vendors)))))) - -(re-frame/reg-sub - ::all-vendors - (fn [db] - (vals (:vendors db)))) - -(re-frame/reg-sub - ::vendors-by-id - (fn [db] - (:vendors db))) - -(re-frame/reg-sub - ::vendor-default-account - (fn [db [_ v client]] - (let [accounts (accounts-by-id (:accounts db) client) - vendor (if (map? v) - v - (-> (:vendors db) (get v))) - client-override (->> (:account-overrides vendor) - (filter #(= (:id (:client %)) (:id client)) - ) - first - :account - :id) - default-id (:id (:default-account vendor)) - i (or client-override default-id)] - (accounts i)))) - -(re-frame/reg-sub - ::sorted-vendors - :<- [::vendors] - (fn [vendors] - (sort-by :name vendors))) - -(re-frame/reg-sub - ::sorted-all-vendors - :<- [::all-vendors] - (fn [all-vendors] - (sort-by :name all-vendors))) - (re-frame/reg-sub ::admin (fn [db] diff --git a/src/cljs/auto_ap/views/components/vendor_dialog.cljs b/src/cljs/auto_ap/views/components/vendor_dialog.cljs index f8d0e4e4..acbd7ad3 100644 --- a/src/cljs/auto_ap/views/components/vendor_dialog.cljs +++ b/src/cljs/auto_ap/views/components/vendor_dialog.cljs @@ -1,19 +1,24 @@ (ns auto-ap.views.components.vendor-dialog - (:require [re-frame.core :as re-frame] - [auto-ap.views.utils :refer [dispatch-event horizontal-field bind-field with-user with-is-admin? active-when account->match-text]] - [auto-ap.views.components.address :refer [address-field]] - [auto-ap.views.components.typeahead :refer [typeahead-v3]] - [auto-ap.views.components.dropdown :refer [drop-down drop-down-contents]] - [auto-ap.events :as events] - [clj-fuzzy.metrics :refer [jaccard jaro-winkler]] - [clojure.spec.alpha :as s] - [clojure.string :as str] - [auto-ap.entities.vendors :as entity] - [auto-ap.entities.contact :as contact] - [auto-ap.subs :as subs] - [auto-ap.forms :as forms] - [auto-ap.views.components.modal :as modal] - [auto-ap.status :as status])) + (: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.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 [account->match-text + bind-field + dispatch-event + horizontal-field + with-is-admin? + with-user]] + [clojure.spec.alpha :as s] + [clojure.string :as str] + [re-frame.core :as re-frame])) (re-frame/reg-sub ::can-submit @@ -28,25 +33,8 @@ (re-frame/reg-event-db ::settled [(forms/in-form ::vendor-form)] - (fn [{:keys [data] :as form} _] - form - #_(let [vendors @(re-frame/subscribe [::subs/vendors]) - text (.toLowerCase (:name data "")) - matching-vendor (when (> (count text) 5) - (->> vendors - (filter #(not= (:id %) (:id data))) - (map :name) - (mapcat - (fn [v] (mapv - (fn [n] [v (jaro-winkler text n ) n text]) - (ngrams v (count text))))) - (filter #(> (second %) 0.9)) - - (map first) - first))] - (if matching-vendor - (assoc form :warning (str "Are you sure you don't mean " matching-vendor "?")) - (dissoc form :warning))))) + (fn [form _] + form)) (re-frame/reg-event-db ::removed-override @@ -83,15 +71,13 @@ (re-frame/reg-event-fx ::save-complete [(forms/triggers-stop ::vendor-form)] - (fn [{:keys [db]} [_ {vendor :upsert-vendor} ]] - {:dispatch [::modal/modal-closed ] - :db (-> db (assoc-in [:vendors (:id vendor)] vendor))})) + (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} _] - (println data) (when (s/valid? ::entity/vendor data) { :graphql {:token user @@ -137,7 +123,7 @@ :legal-entity-1099-type (some-> legal-entity-1099-type clojure.core/name not-empty keyword) )) println)} - events/vendor-query]}]} + common/default-read]}]} :on-success [::save-complete]}}))) (defn client-list [{:keys [override-key override-value-key change-event default-key data]} template] @@ -472,7 +458,6 @@ (defn vendor-dialog [{:keys [save-event] }] (let [clients @(re-frame/subscribe [::subs/clients]) - all-vendors @(re-frame/subscribe [::subs/vendors]) {:keys [data error warning] :as f} @(re-frame/subscribe [::forms/form ::vendor-form]) change-event [::changed]] [:div diff --git a/src/cljs/auto_ap/views/pages/admin/accounts/side_bar.cljs b/src/cljs/auto_ap/views/pages/admin/accounts/side_bar.cljs index 592713ec..6e8fee87 100644 --- a/src/cljs/auto_ap/views/pages/admin/accounts/side_bar.cljs +++ b/src/cljs/auto_ap/views/pages/admin/accounts/side_bar.cljs @@ -13,9 +13,8 @@ (re-frame/reg-sub ::filters :<- [::specific-filters] - :<- [::subs/vendors-by-id] :<- [::subs/query-params] - (fn [[specific-filters vendors-by-id query-params] ] + (fn [[specific-filters query-params] ] (let [url-filters (-> query-params (select-keys #{:name-like :code-like}) (update :name-like #(some-> % str)) diff --git a/src/cljs/auto_ap/views/pages/admin/rules.cljs b/src/cljs/auto_ap/views/pages/admin/rules.cljs index e2081d54..fbf5e642 100644 --- a/src/cljs/auto_ap/views/pages/admin/rules.cljs +++ b/src/cljs/auto_ap/views/pages/admin/rules.cljs @@ -1,26 +1,22 @@ (ns auto-ap.views.pages.admin.rules - (:require [auto-ap.forms :as forms] - [auto-ap.subs :as subs] - [auto-ap.views.components.admin.side-bar :refer [admin-side-bar]] - [auto-ap.views.pages.admin.rules.side-bar :as side-bar] - [auto-ap.views.components.layouts :refer [appearing-side-bar side-bar-layout]] - [auto-ap.views.pages.admin.rules.table :as table] - [auto-ap.views.pages.admin.rules.results-modal :as results-modal] - [auto-ap.views.pages.admin.rules.form :as form] - [auto-ap.views.pages.admin.rules.common :refer [default-read]] - [auto-ap.views.utils :refer [dispatch-event with-user]] - [vimsical.re-frame.cofx.inject :as inject] - [vimsical.re-frame.fx.track :as track] - [auto-ap.events :as events] - [auto-ap.utils :refer [replace-by merge-by]] - [re-frame.core :as re-frame] - [auto-ap.status :as status] - [auto-ap.effects.forward :as forward] - [auto-ap.views.pages.data-page :as data-page] - [clojure.set :as set] - [auto-ap.views.components.buttons :as buttons])) - - + (:require + [auto-ap.effects.forward :as forward] + [auto-ap.events :as events] + [auto-ap.forms :as forms] + [auto-ap.subs :as subs] + [auto-ap.views.components.admin.side-bar :refer [admin-side-bar]] + [auto-ap.views.components.buttons :as buttons] + [auto-ap.views.components.layouts + :refer [appearing-side-bar side-bar-layout]] + [auto-ap.views.pages.admin.rules.common :refer [default-read]] + [auto-ap.views.pages.admin.rules.form :as form] + [auto-ap.views.pages.admin.rules.side-bar :as side-bar] + [auto-ap.views.pages.admin.rules.table :as table] + [auto-ap.views.pages.data-page :as data-page] + [auto-ap.views.utils :refer [dispatch-event with-user]] + [clojure.set :as set] + [re-frame.core :as re-frame] + [vimsical.re-frame.fx.track :as track])) ;; EVENTS @@ -29,7 +25,7 @@ (re-frame/reg-event-fx ::params-change [with-user ] - (fn [{:keys [db user] :as cofx} [_ params]] + (fn [{:keys [user]} [_ params]] {:graphql {:token user :owns-state {:single [::data-page/page ::page]} :query-obj {:venia/queries [{:query/data [:transaction_rule_page @@ -52,13 +48,13 @@ (re-frame/reg-event-fx ::new-rule-clicked - (fn [{:keys [db]} _] + (fn [_ _] {:dispatch [::form/adding {:client @(re-frame/subscribe [::subs/client])}]})) (re-frame/reg-event-fx ::mounted - (fn [{:keys [db]}] + (fn [_] {:dispatch-n [[::events/yodlee-merchants-needed]] ::forward/register [{:id ::page :events #{::form/updated} @@ -75,7 +71,7 @@ (re-frame/reg-event-fx ::unmounted - (fn [{:keys [db]}] + (fn [_] {:dispatch [::data-page/dispose ::page] ::forward/dispose [{:id ::page} {:id ::deleted-transaction-rule}] @@ -85,8 +81,7 @@ (def rules-content (with-meta (fn [] - (let [current-client @(re-frame/subscribe [::subs/client]) - user @(re-frame/subscribe [::subs/user])] + (let [user @(re-frame/subscribe [::subs/user])] [:div [:h1.title "Transaction Rules"] (when (= "admin" (:user/role user)) diff --git a/src/cljs/auto_ap/views/pages/admin/rules/table.cljs b/src/cljs/auto_ap/views/pages/admin/rules/table.cljs index f3265b44..509522ca 100644 --- a/src/cljs/auto_ap/views/pages/admin/rules/table.cljs +++ b/src/cljs/auto_ap/views/pages/admin/rules/table.cljs @@ -1,20 +1,17 @@ (ns auto-ap.views.pages.admin.rules.table - (:require [auto-ap.subs :as subs] - [auto-ap.views.utils :refer [dispatch-event ->$ with-user action-cell-width]] - [auto-ap.views.pages.admin.rules.form :as form] - [auto-ap.views.components.paginator :refer [paginator]] - [auto-ap.views.components.sort-by-list :refer [sort-by-list]] - [auto-ap.views.pages.admin.rules.results-modal :as results-modal] - [auto-ap.views.components.sorter :refer [sorted-column toggle-sort-by sort-icon]] - [auto-ap.views.components.buttons :as buttons] - [auto-ap.views.components.grid :as grid] - [auto-ap.events :as events] - [auto-ap.status :as status] - [re-frame.core :as re-frame] - [reagent.core :as reagent] - [reagent.core :as r] - [auto-ap.views.components.modal :as modal] - [auto-ap.views.pages.data-page :as data-page])) + (:require + [auto-ap.status :as status] + [auto-ap.subs :as subs] + [auto-ap.views.components.buttons :as buttons] + [auto-ap.views.components.grid :as grid] + [auto-ap.views.components.modal :as modal] + [auto-ap.views.pages.admin.rules.form :as form] + [auto-ap.views.pages.admin.rules.results-modal :as results-modal] + [auto-ap.views.pages.data-page :as data-page] + [auto-ap.views.utils + :refer [->$ action-cell-width dispatch-event with-user]] + [re-frame.core :as re-frame] + [reagent.core :as r])) (re-frame/reg-event-fx ::run-clicked diff --git a/src/cljs/auto_ap/views/pages/admin/vendors.cljs b/src/cljs/auto_ap/views/pages/admin/vendors.cljs index 6e77e8e7..6413d4d4 100644 --- a/src/cljs/auto_ap/views/pages/admin/vendors.cljs +++ b/src/cljs/auto_ap/views/pages/admin/vendors.cljs @@ -1,93 +1,76 @@ (ns auto-ap.views.pages.admin.vendors - (:require [re-frame.core :as re-frame] - [reagent.core :as reagent] - [clojure.string :as str] - [auto-ap.views.components.buttons :as buttons] - [auto-ap.views.pages.admin.vendors.side-bar :as side-bar] - [auto-ap.subs :as subs] - [auto-ap.events :as events] - [auto-ap.forms :as forms] - [auto-ap.utils :refer [by]] - [auto-ap.entities.vendors :as entity] - [auto-ap.views.components.address :refer [address-field]] - [auto-ap.views.components.grid :as grid] - [auto-ap.views.components.admin.side-bar :refer [admin-side-bar]] - [auto-ap.views.components.layouts :refer [side-bar-layout]] - [clojure.spec.alpha :as s] - [auto-ap.views.utils :refer [login-url dispatch-value-change dispatch-event bind-field horizontal-field action-cell-width]] - [auto-ap.views.pages.admin.vendors.table :as table] - [auto-ap.views.pages.admin.vendors.merge-dialog :as merge-dialog] - [cljs.reader :as edn] - [auto-ap.routes :as routes] - [bidi.bidi :as bidi] - [auto-ap.status :as status] - [vimsical.re-frame.fx.track :as track] - [auto-ap.effects.forward :as forward]) - (:require-macros [cljs.core.async.macros :refer [go]] - [auto-ap.entities.vendors :as vendor])) + (:require + [auto-ap.effects.forward :as forward] + [auto-ap.subs :as subs] + [auto-ap.views.components.admin.side-bar :refer [admin-side-bar]] + [auto-ap.views.components.layouts :refer [side-bar-layout]] + [auto-ap.views.pages.admin.vendors.merge-dialog :as merge-dialog] + [auto-ap.views.pages.admin.vendors.side-bar :as side-bar] + [auto-ap.views.pages.admin.vendors.table :as table] + [auto-ap.views.pages.data-page :as data-page] + [auto-ap.views.utils :refer [dispatch-event with-user]] + [clojure.set :as set] + [re-frame.core :as re-frame] + [vimsical.re-frame.fx.track :as track] + [auto-ap.views.components.vendor-dialog :as vendor-dialog])) - -(re-frame/reg-event-fx - ::invalidated - (fn [{:keys [db]}] - {:graphql {:token (:user db) - :owns-state {:single ::vendor-page} - :query-obj {:venia/queries [[:vendor events/vendor-query]]} - :on-success [::received-vendors]}})) - -(re-frame/reg-event-fx - ::mounted - (fn [{:keys [db]} _] - {:dispatch [::invalidated] - ::track/register {:id ::params - :subscription [::params] - :event-fn (fn [params] [::params-change params])} - ::forward/register {:id ::merge-complete - :events #{::merge-dialog/complete} - :event-fn (fn [_] - [::invalidated])}})) - -(re-frame/reg-event-db - ::received-vendors - (fn [db [_ vendors]] - (assoc db :vendors (by :id (:vendor vendors ))))) - - -(re-frame/reg-event-fx - ::unmounted - (fn [{:keys [db]} _] - {:db (dissoc db ::table/params ::side-bar/filter-params) - ::forward/dispose {:id ::merge-complete} - ::track/dispose {:id ::params}})) - -(re-frame/reg-sub - ::params - :<- [::side-bar/filter-params] - :<- [::table/params] - (fn [[filter-params table-params]] - (cond-> {} - (seq filter-params) (merge filter-params) - (seq table-params) (merge table-params)))) +(def default-read [:id :name :hidden :terms [:default-account [:name :id :location]] + [:account-overrides [[:client [:id :name]] :id [:account [:id :numeric-code :name]]]] + [:automatically-paid-when-due [:id :name]] + [:terms-overrides [[:client [:id :name]] :id :terms]] + [:schedule-payment-dom [[:client [:id :name]] :id :dom]] + [:usage [:client-id :count]] + [:primary-contact [:name :phone :email :id]] + [:secondary-contact [:id :name :phone :email]] + :print-as :invoice-reminder-schedule :code + :legal-entity-first-name :legal-entity-middle-name :legal-entity-last-name + :legal-entity-tin :legal-entity-tin-type + :legal-entity-1099-type + [:address [:street1 :street2 :city :state :zip]]]) (re-frame/reg-event-fx ::params-change - (fn [_ [_ params]] - {:set-uri-params params})) - -(re-frame/reg-sub - ::vendor-page - :<- [::params] - :<- [::subs/sorted-all-vendors] - (fn [[params all-vendors]] - (let [matching-vendors (cond->> all-vendors - (:name-like params) (filter #(str/includes? (str/lower-case (or (:name %) "")) - (str/lower-case (:name-like params)))))] - (assoc (grid/virtual-paginate-controls (:start params ) (:per-page params) matching-vendors) - :data (grid/virtual-paginate (:start params) (:per-page params) matching-vendors))))) - - + [with-user] + (fn [{:keys [user]} [_ params]] + {:graphql {:token user + :owns-state {:single [::data-page/page ::page]} + :query-obj {:venia/queries [{:query/data [:vendor + {:sort (:sort params) + :start (:start params 0) + :per-page (:per-page params) + :name-like (:name-like params)} + [[:vendors default-read] + :total + :start + :end]] + :query/alias :result}]} + :on-success (fn [result] + [::data-page/received ::page + (set/rename-keys (:result result) + {:vendors :data})])}})) +(re-frame/reg-event-fx + ::mounted + (fn [_ _] + {::forward/register [{:id ::merge-complete + :events #{::merge-dialog/complete} + :event-fn (fn [_] + [::params-change {}])} + {:id ::save-complete + :events #{::vendor-dialog/save-complete} + :event-fn (fn [_] + [::params-change {}])}] + ::track/register {:id ::params + :subscription [::data-page/params ::page] + :event-fn (fn [params] + [::params-change params])}})) +(re-frame/reg-event-fx + ::unmounted + (fn [_ _] + {:dispatch [::data-page/dispose ::page] + ::forward/dispose [{:id ::merge-complete} {:id ::save-complete}] + ::track/dispose {:id ::params}})) (defn admin-vendors-content [] [(with-meta @@ -95,17 +78,15 @@ [:div.inbox-messages (when-let [banner (:banner @(re-frame/subscribe [::subs/admin]))] [:div.notification banner]) - (let [vendors (re-frame/subscribe [::subs/vendors]) - editing-vendor (:vendor @(re-frame/subscribe [::subs/admin]))] - [:div - [:h1.title "Vendors"] - [:div.is-pulled-right [:a.button.is-primary.is-outlined {:on-click (dispatch-event [::merge-dialog/show])} "Merge vendors"]] - [table/vendors-table {:page @(re-frame/subscribe [::vendor-page]) - :status @(re-frame/subscribe [::status/single ::vendor-page])}]])]) + [:div + [:h1.title "Vendors"] + [:div.is-pulled-right [:a.button.is-primary.is-outlined {:on-click (dispatch-event [::merge-dialog/show])} "Merge vendors"]] + [table/vendors-table {:id :vendors + :data-page ::page}]]]) {:component-did-mount #(re-frame/dispatch [::mounted]) :component-will-unmount #(re-frame/dispatch-sync [::unmounted])})]) (defn admin-vendors-page [] [side-bar-layout {:side-bar [admin-side-bar {} - [side-bar/vendor-side-bar]] + [side-bar/vendor-side-bar {:data-page ::page}]] :main [admin-vendors-content]}]) diff --git a/src/cljs/auto_ap/views/pages/admin/vendors/common.cljs b/src/cljs/auto_ap/views/pages/admin/vendors/common.cljs new file mode 100644 index 00000000..3aebce41 --- /dev/null +++ b/src/cljs/auto_ap/views/pages/admin/vendors/common.cljs @@ -0,0 +1,15 @@ +(ns auto-ap.views.pages.admin.vendors.common) + +(def default-read [:id :name :hidden :terms [:default-account [:name :id :location]] + [:account-overrides [[:client [:id :name]] :id [:account [:id :numeric-code :name]]]] + [:automatically-paid-when-due [:id :name]] + [:terms-overrides [[:client [:id :name]] :id :terms]] + [:schedule-payment-dom [[:client [:id :name]] :id :dom]] + [:usage [:client-id :count]] + [:primary-contact [:name :phone :email :id]] + [:secondary-contact [:id :name :phone :email]] + :print-as :invoice-reminder-schedule :code + :legal-entity-first-name :legal-entity-middle-name :legal-entity-last-name + :legal-entity-tin :legal-entity-tin-type + :legal-entity-1099-type + [:address [:street1 :street2 :city :state :zip]]]) diff --git a/src/cljs/auto_ap/views/pages/admin/vendors/side_bar.cljs b/src/cljs/auto_ap/views/pages/admin/vendors/side_bar.cljs index dda46698..25a2d5a9 100644 --- a/src/cljs/auto_ap/views/pages/admin/vendors/side_bar.cljs +++ b/src/cljs/auto_ap/views/pages/admin/vendors/side_bar.cljs @@ -2,69 +2,17 @@ (:require [re-frame.core :as re-frame] [auto-ap.subs :as subs] - [auto-ap.views.utils :refer [active-when dispatch-value-change]])) - -(re-frame/reg-sub - ::specific-filters - (fn [db ] - (::filters db nil))) - -(re-frame/reg-sub - ::filters - :<- [::specific-filters] - :<- [::subs/vendors-by-id] - :<- [::subs/query-params] - (fn [[specific-filters vendors-by-id query-params] ] - (let [url-filters (-> query-params - (select-keys #{:name-like}) - (update :name-like #(some-> % str)))] - (merge url-filters specific-filters )))) - -(re-frame/reg-sub - ::filter - :<- [::filters] - (fn [filters [_ which]] - (get filters which))) - -(re-frame/reg-sub - ::settled-filters - (fn [db ] - (::settled-filters db))) - -(re-frame/reg-sub - ::filter-params - :<- [::settled-filters] - :<- [::filters] - :<- [::subs/active-page] - (fn [[settled-filters filters ap ]] - (let [filters (or settled-filters filters)] - {:name-like (:name-like filters)}))) - -(re-frame/reg-event-fx - ::filters-settled - (fn [{:keys [db]} [_ & params]] - {:db (assoc db ::settled-filters @(re-frame/subscribe [::filters]))})) - -(re-frame/reg-event-fx - ::filter-changed - (fn [{:keys [db]} [_ & params]] - (let [[a b c] params - [which val] (if (= 3 (count params)) - [(into [a] b) c] - [[a] b])] - {:db (assoc-in db (into [::filters] which) val) - :dispatch-debounce {:event [::filters-settled] - :time 800 - :key ::filters}}))) + [auto-ap.views.utils :refer [dispatch-value-change]] + [auto-ap.views.pages.data-page :as data-page])) -(defn vendor-side-bar [] +(defn vendor-side-bar [{:keys [data-page]}] [:div [:p.menu-label "Name"] - - [:div.field - [:div.control [:input.input {:placeholder "Harry's Food Products" - :value @(re-frame/subscribe [::filter :name-like]) - :on-change (dispatch-value-change [::filter-changed :name-like])} ]]]]) + [:div + [:div.field + [:div.control [:input.input {:placeholder "HOME DEPOT" + :value @(re-frame/subscribe [::data-page/filter data-page :name-like]) + :on-change (dispatch-value-change [::data-page/filter-changed data-page :name-like])} ]]]]]) diff --git a/src/cljs/auto_ap/views/pages/admin/vendors/table.cljs b/src/cljs/auto_ap/views/pages/admin/vendors/table.cljs index 827e680b..994482f0 100644 --- a/src/cljs/auto_ap/views/pages/admin/vendors/table.cljs +++ b/src/cljs/auto_ap/views/pages/admin/vendors/table.cljs @@ -1,34 +1,21 @@ (ns auto-ap.views.pages.admin.vendors.table - (:require - [re-frame.core :as re-frame] - [reagent.core :as reagent] + (:require [auto-ap.subs :as subs] - [auto-ap.status :as status] - [auto-ap.views.components.vendor-dialog :refer [vendor-dialog] :as vendor-dialog] + [auto-ap.views.components.buttons :as buttons] [auto-ap.views.components.grid :as grid] - [auto-ap.views.utils :refer [login-url dispatch-value-change dispatch-event bind-field horizontal-field action-cell-width]] - [auto-ap.views.components.buttons :as buttons])) + [auto-ap.views.components.vendor-dialog :as vendor-dialog] + [auto-ap.views.pages.data-page :as data-page] + [auto-ap.views.utils :refer [action-cell-width]] + [re-frame.core :as re-frame])) -(re-frame/reg-event-fx - ::params-changed - (fn [{:keys [db]} [_ p]] - {:db (assoc db ::params p)})) - -(re-frame/reg-sub - ::params - (fn [db] - (-> db ::params))) - -(defn vendors-table [{:keys [status page]}] - (let [params @(re-frame/subscribe [::params]) +(defn vendors-table [{:keys [data-page]}] + (let [{:keys [data]} @(re-frame/subscribe [::data-page/page data-page]) accounts @(re-frame/subscribe [::subs/all-accounts-by-id])] + (println "DATA COUNT" (keys data)) - [grid/grid {:status status - :on-params-change (fn [p] - (re-frame/dispatch [::params-changed p])) - :params params + [grid/grid {:data-page data-page :column-count 4} - [grid/controls page] + [grid/controls data] [grid/table {:fullwidth true} [grid/header [grid/row {} @@ -37,7 +24,7 @@ [grid/header-cell {} "Default Account"] [grid/header-cell {:style {:width (action-cell-width 1)}}]]] [grid/body - (for [v (:data page)] + (for [v (:data data)] ^{:key (str (:id v))} [grid/row {:class (:class v) :id (:id v)} [grid/cell {} (:name v) diff --git a/src/cljs/auto_ap/views/pages/home.cljs b/src/cljs/auto_ap/views/pages/home.cljs index 3daefedc..d8cfa285 100644 --- a/src/cljs/auto_ap/views/pages/home.cljs +++ b/src/cljs/auto_ap/views/pages/home.cljs @@ -3,18 +3,17 @@ [auto-ap.subs :as subs] [auto-ap.views.components.grid :as grid] [auto-ap.views.components.layouts :refer [side-bar-layout]] - [auto-ap.views.components.typeahead :refer [typeahead-v3]] + [auto-ap.history :refer [history]] + [cemerick.url :as url] [auto-ap.views.utils :refer [->$ date->str days-until dispatch-event local-now standard]] [bidi.bidi :as bidi] [cljs-time.coerce :as coerce] [cljs-time.core :as t] - [clojure.string :as str] [pushy.core :as pushy] [re-frame.core :as re-frame] [recharts] - [downshift :as ds :refer [useCombobox]] [reagent.core :as r] [react])) @@ -39,7 +38,7 @@ :data data :dataKey "value" :inner-radius 20} - (map (fn [x y] + (map (fn [_ y] ^{:key y} [cell {:key y :fill (colors y)}]) data (range)) ] @@ -57,7 +56,7 @@ (defn make-cash-flow-chart [{:keys [width height data] }] (let [redirect-fn (fn [x] - (pushy/set-token! auto-ap.history/history (str (bidi/path-for routes/routes :unpaid-invoices) "?" (get (js->clj x) "query-params"))) + (pushy/set-token! history (str (bidi/path-for routes/routes :unpaid-invoices) "?" (get (js->clj x) "query-params"))) )] [bar-chart {:width width :height height :data data :fill "#FFFFFF" :stackOffset "sign"} [tool-tip] @@ -161,29 +160,29 @@ (fn [[{:keys [effective-balance credits-yesterday] } :as acc] day] (let [invoices-due-today (invoices-due-soon (date->str (t/plus start-date (t/days day))) 0.0) credits-due-today (upcoming-credits (date->str (t/plus start-date (t/days day))) 0.0) - debits-due-today (upcoming-debits (date->str (t/plus start-date (t/days day))) 0.0)] - (let [today (t/plus start-date (t/days day))] - (conj acc - {:name (date->str today) - :date today - :effective-balance (+ (- effective-balance invoices-due-today ) - debits-due-today - credits-yesterday) - :credits-yesterday credits-due-today - :credits credits-due-today - :debits debits-due-today - :invoices (- invoices-due-today) - :query-params (cemerick.url/map->query {:due-range {:start (date->str today standard) - :end (date->str today standard)}})})))) - (list {:name (date->str start-date) - :date start-date - :effective-balance effective-balance - :invoices (- (invoices-due-soon (date->str start-date) 0.0)) - :credits (upcoming-credits (date->str start-date) 0.0) - :credits-yesterday (upcoming-credits (date->str start-date) 0.0) - :debits (upcoming-debits (date->str start-date) 0.0) + debits-due-today (upcoming-debits (date->str (t/plus start-date (t/days day))) 0.0) + today (t/plus start-date (t/days day))] + (conj acc + {:name (date->str today) + :date today + :effective-balance (+ (- effective-balance invoices-due-today ) + debits-due-today + credits-yesterday) + :credits-yesterday credits-due-today + :credits credits-due-today + :debits debits-due-today + :invoices (- invoices-due-today) + :query-params (url/map->query {:due-range {:start (date->str today standard) + :end (date->str today standard)}})}))) + (list {:name (date->str start-date) + :date start-date + :effective-balance effective-balance + :invoices (- (invoices-due-soon (date->str start-date) 0.0)) + :credits (upcoming-credits (date->str start-date) 0.0) + :credits-yesterday (upcoming-credits (date->str start-date) 0.0) + :debits (upcoming-debits (date->str start-date) 0.0) :outstanding-payments (- outstanding-payments) - :query-params (cemerick.url/map->query {:due-range {:end (date->str start-date standard)}})}) + :query-params (url/map->query {:due-range {:end (date->str start-date standard)}})}) (condp = (:cash-flow-range chart-options) :seven-days (range 1 7) @@ -210,9 +209,8 @@ ::cash-flow-page :<- [::cash-flow-table-params] :<- [::cash-flow-data] - :<- [::subs/vendors-by-id] - (fn [[params cash-flow-data vendors-by-id]] - (let [ {:keys [outstanding-payments invoices-due-soon upcoming-credits upcoming-debits]} cash-flow-data + (fn [[params cash-flow-data]] + (let [ {:keys [invoices-due-soon upcoming-credits upcoming-debits]} cash-flow-data rows (concat (map (fn [c] {:date (:date c) :days-until (days-until (:date c)) @@ -230,7 +228,7 @@ {:date (:due c) :days-until (days-until (:due c)) :amount (:outstanding-balance c) - :name (str (:name (get vendors-by-id (:id (:vendor c)))) " (" (:invoice-number c) ")") + :name (str (:name (:vendor c)) " (" (:invoice-number c) ")") :type "Invoice"}) invoices-due-soon))] (assoc (grid/virtual-paginate-controls (:start params ) (:per-page params) rows) @@ -253,7 +251,7 @@ {:client-id (:id @(re-frame/subscribe [::subs/client]))} [:beginning-balance :outstanding-payments - [:invoices-due-soon [:due :outstanding-balance [:vendor [:id]] :invoice-number]] + [:invoices-due-soon [:due :outstanding-balance [:vendor [:id :name]] :invoice-number]] [:upcoming-credits [:date :amount :identifier]] [:upcoming-debits [:date :amount :identifier]]]]]} :on-success [::received]}})) @@ -300,8 +298,6 @@ (defn home-content [] (let [client-id (-> @(re-frame/subscribe [::subs/client]) :id) - accounts-by-id @(re-frame/subscribe [::subs/accounts-by-id]) - accounts-index @(re-frame/subscribe [::subs/accounts-index]) chart-options @(re-frame/subscribe [::chart-options])] ^{:key client-id} [side-bar-layout {:side-bar [:div diff --git a/src/cljs/auto_ap/views/pages/new_invoice.cljs b/src/cljs/auto_ap/views/pages/new_invoice.cljs deleted file mode 100644 index 3ac28955..00000000 --- a/src/cljs/auto_ap/views/pages/new_invoice.cljs +++ /dev/null @@ -1,75 +0,0 @@ -(ns auto-ap.views.pages.new-invoice - (:require [re-frame.core :as re-frame] - [auto-ap.subs :as subs] - [auto-ap.events :as events])) - -(defn new-invoice-page [] - (let [form-data (re-frame/subscribe [::subs/new-invoice-form])] - [:div - [:form - [:h1.title "New InvoiceR"] - [:div.field - [:label.label "Vendor"] - [:div.control - [:input.input {:type "text" - :auto-focus true - :placeholder "CINTAS" - :value (:vendor @form-data) - :on-change (fn [e] - (re-frame/dispatch [::events/change-form-state - [:new-invoice :vendor] - (.. e -target -value)]))}]]] - [:div.field - [:label.label "Customer"] - [:div.control - [:input.input {:type "text" - :placeholder "Brown Chicken Brown Cow" - :value (:client @form-data) - :on-change (fn [e] - (re-frame/dispatch [::events/change-form-state - [:new-invoice :client] - (.. e -target -value)]))}]]] - [:div.field - [:label.label "Invoice #"] - [:div.control - [:input.input {:type "text" - :placeholder "12345" - :value (:invoice-number @form-data) - :on-change (fn [e] - (re-frame/dispatch [::events/change-form-state - [:new-invoice :invoice-number] - (.. e -target -value)]))}]]] - [:div.field - [:label.label "Date"] - [:div.control - [:input.input {:type "text" - :placeholder "11/11/2011" - :value (:date @form-data) - :on-change (fn [e] - (re-frame/dispatch [::events/change-form-state - [:new-invoice :date] - (.. e -target -value)]))}]]] - [:div.field - [:label.label "Total"] - [:div.control - [:input.input {:type "text" - :placeholder "$14.50" - :value (:total @form-data) - :on-change (fn [e] - (re-frame/dispatch [::events/change-form-state - [:new-invoice :total] - (.. e -target -value)]))}]]] - [:div.control - [:submit.button.is-large.is-primary { - :disabled (if (and (:total @form-data) (:date @form-data) (:client @form-data) (:invoice-number @form-data) - (:vendor @form-data)) - "" - "disabled") - :on-click - (fn [x] - (.preventDefault x) - (re-frame/dispatch [::events/submit-new-invoice @form-data]))} - [:span - (when (:loading? @form-data) - [:i.fa.fa-spin.fa-spinner]) - "Save"]]]]])) diff --git a/src/cljs/auto_ap/views/pages/transactions/bulk_updates.cljs b/src/cljs/auto_ap/views/pages/transactions/bulk_updates.cljs index eefc3955..a8480993 100644 --- a/src/cljs/auto_ap/views/pages/transactions/bulk_updates.cljs +++ b/src/cljs/auto_ap/views/pages/transactions/bulk_updates.cljs @@ -8,13 +8,18 @@ :as expense-accounts-field :refer [expense-accounts-field]] [auto-ap.views.components.modal :as modal] - [auto-ap.views.components.typeahead :refer [typeahead-v3]] + [auto-ap.views.components.typeahead.vendor + :refer [search-backed-typeahead]] + [auto-ap.views.pages.data-page :as data-page] [auto-ap.views.pages.transactions.common :refer [data-params->query-params]] [auto-ap.views.utils :refer [dispatch-event with-user]] [clojure.string :as str] [re-frame.core :as re-frame] - [auto-ap.views.pages.data-page :as data-page])) + [reagent.core :as r] + [vimsical.re-frame.fx.track :as track] + [auto-ap.events :as events] + [vimsical.re-frame.cofx.inject :as inject])) (re-frame/reg-sub ::can-submit @@ -31,8 +36,8 @@ (re-frame/reg-event-fx ::code-selected - [with-user (forms/in-form ::form) ] - (fn [{:keys [user db]} [_ checked]] + [with-user (forms/in-form ::form) (re-frame/inject-cofx ::inject/sub [::subs/client])] + (fn [{:keys [user db] ::subs/keys [client]} [_ checked]] (let [checked-params (get checked "header") specific-transactions (map :id (vals (dissoc checked "header"))) data (:data db)] @@ -43,6 +48,7 @@ :operation/name "BulkCodeTransactions"} :venia/queries [[:bulk-code-transactions {:filters (some-> checked-params data-params->query-params) + :client_id (:id client) :ids specific-transactions :vendor (:id (:vendor data)) :approval-status (:transaction-approval-status data) @@ -67,33 +73,56 @@ ::changed (forms/change-handler ::form (fn [data field value] - (cond (and (= [:vendor] field) + (cond (and (= [:vendor-preferences] field) value) [[:accounts] (expense-accounts-field/default-account (:accounts data) - @(re-frame/subscribe [::subs/vendor-default-account (:id value) (:client data)]) + (:default-account value) (:total data) [])] :else [])))) +(re-frame/reg-event-fx + ::changed-vendor + [(forms/in-form ::form)] + (fn [{{{:keys [client]} :data} :db} [_ vendor]] + (when (and (:id client) (:id vendor)) + {:dispatch [::events/vendor-preferences-requested {:client-id (:id client) + :vendor-id (:id vendor) + :on-success [::changed [:vendor-preferences]] + :on-failure [:hello]}]}))) + +(re-frame/reg-event-fx + ::mounted + (fn [] + {::track/register {:id ::vendor-change + :subscription [::forms/field ::form [:vendor]] + :event-fn (fn [v] + [::changed-vendor v])}})) + +(re-frame/reg-event-fx + ::unmounted + (fn [] + {::track/dispose {:id ::vendor-change}})) + (def code-form (forms/vertical-form {:submit-event [::code-selected] - :change-event [::changed] - :can-submit [::can-submit] - :id ::form})) -(defn form [] + :change-event [::changed] + :can-submit [::can-submit] + :id ::form})) +(defn form-content [_] (let [{:keys [data]} @(re-frame/subscribe [::forms/form ::form]) - {:keys [form-inline field]} code-form - ] + {:keys [form-inline field]} code-form] (form-inline {} [:<> (field "Vendor" - [typeahead-v3 {:entities-by-id @(re-frame/subscribe [::subs/vendors-by-id]) - :entity-index @(re-frame/subscribe [::subs/searchable-vendors-index]) - :entity->text :name - :type "typeahead-v3" - :auto-focus true - :field [:vendor]}]) + [search-backed-typeahead {:search-query (fn [i] + [:search_vendor + {:query i} + [:name :id]]) + :type "typeahead-v3" + :auto-focus true + :field [:vendor]}]) (field "Approval Status" [button-radio @@ -115,6 +144,13 @@ :field [:accounts]}]) {:key (some-> data :vendor :id str)}) ]))) +(defn form [_] + (r/create-class + {:display-name "transaction-bulk-update-form" + :component-did-mount #(re-frame/dispatch [::mounted]) + :component-will-unmount #(re-frame/dispatch [::unmounted]) + :reagent-render (fn [p] + [form-content p])})) (re-frame/reg-event-fx ::code-requested diff --git a/src/cljs/auto_ap/views/pages/transactions/form.cljs b/src/cljs/auto_ap/views/pages/transactions/form.cljs index 2ed439ab..1d31071c 100644 --- a/src/cljs/auto_ap/views/pages/transactions/form.cljs +++ b/src/cljs/auto_ap/views/pages/transactions/form.cljs @@ -1,23 +1,25 @@ (ns auto-ap.views.pages.transactions.form - (:require [auto-ap.forms :as forms] - [auto-ap.status :as status] - [auto-ap.subs :as subs] - [auto-ap.views.components.button-radio :refer [button-radio]] - [auto-ap.views.components.expense-accounts-field - :as - expense-accounts-field - :refer - [expense-accounts-field]] - [auto-ap.views.components.layouts :as layouts] - [auto-ap.views.components.typeahead :refer [typeahead-v3]] - [auto-ap.views.pages.transactions.common :refer [transaction-read]] - [auto-ap.views.utils - :refer - [->$ date->str dispatch-event pretty with-user]] - [clojure.string :as str] - [re-frame.core :as re-frame] - [react :as react] - [reagent.core :as r])) + (:require + [auto-ap.forms :as forms] + [auto-ap.status :as status] + [auto-ap.subs :as subs] + [auto-ap.views.components.button-radio :refer [button-radio]] + [auto-ap.views.components.expense-accounts-field + :as expense-accounts-field + :refer [expense-accounts-field]] + [auto-ap.views.components.layouts :as layouts] + [auto-ap.views.components.typeahead :refer [typeahead-v3]] + [auto-ap.views.components.typeahead.vendor + :refer [search-backed-typeahead]] + [auto-ap.views.pages.transactions.common :refer [transaction-read]] + [auto-ap.views.utils + :refer [->$ date->str dispatch-event pretty with-user]] + [clojure.string :as str] + [re-frame.core :as re-frame] + [react :as react] + [reagent.core :as r] + [vimsical.re-frame.fx.track :as track] + [auto-ap.events :as events])) ;; SUBS (re-frame/reg-sub @@ -76,11 +78,11 @@ (forms/change-handler ::form (fn [data field value] (let [locations @(re-frame/subscribe [::subs/locations-for-client (:id (:client data))])] - (if (and (= [:vendor] field) + (if (and (= [:vendor-preferences] field) value (expense-accounts-field/can-replace-with-default? (:accounts data))) [[:accounts] (expense-accounts-field/default-account (:accounts data) - @(re-frame/subscribe [::subs/vendor-default-account (:id value) (:client data)]) + (:default-account (:vendor-preferences data)) (:amount data) locations)] []))))) @@ -185,17 +187,28 @@ {})) -(re-frame/reg-event-db - ::manual-match - [(forms/in-form ::form)] - (fn [edit-transaction] - (update-in edit-transaction [:data] dissoc :potential-payment-matches))) +(re-frame/reg-event-fx + ::changed-vendor + [(forms/in-form ::form)] + (fn [{{{:keys [client]} :data} :db} [_ vendor]] + (when (and (:id client) (:id vendor)) + {:dispatch [::events/vendor-preferences-requested {:client-id (:id client) + :vendor-id (:id vendor) + :on-success [::changed [:vendor-preferences]] + :on-failure [:hello]}]}))) -(re-frame/reg-event-db - ::transaction-rule-closed - [(forms/in-form ::form)] - (fn [edit-transaction] - (update-in edit-transaction [:data] dissoc :potential-transaction-rule-matches))) +(re-frame/reg-event-fx + ::mounted + (fn [] + {::track/register {:id ::vendor-change + :subscription [::forms/field ::form [:vendor]] + :event-fn (fn [v] + [::changed-vendor v])}})) + +(re-frame/reg-event-fx + ::unmounted + (fn [] + {::track/dispose {:id ::vendor-change}})) ;; VIEWS @@ -324,7 +337,7 @@ [:a {:on-click (fn [] (on-tab-clicked (:key props)))} (:title props)]])))]) -(defn form [_] +(defn form-content [_] [layouts/side-bar {:on-close (dispatch-event [::forms/form-closing ::form])} (let [{:keys [data] } @(re-frame/subscribe [::forms/form ::form]) locations @(re-frame/subscribe [::subs/locations-for-client (:id (:client data))]) @@ -404,14 +417,15 @@ [tab {:title "Details" :key :details} [:div (field "Vendor" - [typeahead-v3 {:entities-by-id @(re-frame/subscribe [::subs/vendors-by-id]) - :entity-index @(re-frame/subscribe [::subs/searchable-vendors-index]) - :entity->text :name - :type "typeahead-v3" - :auto-focus true - :field [:vendor] - :disabled (or (boolean (:payment data)) - should-disable-for-client?)}]) + [search-backed-typeahead {:search-query (fn [i] + [:search_vendor + {:query i} + [:name :id]]) + :type "typeahead-v3" + :auto-focus true + :field [:vendor] + :disabled (or (boolean (:payment data)) + should-disable-for-client?)}]) (with-meta (field nil [expense-accounts-field @@ -442,3 +456,11 @@ (when-not should-disable-for-client? (submit-button "Save"))]]]]) {:key (:id data)}))]) + +(defn form [_] + (r/create-class + {:display-name "transaction-form" + :component-did-mount #(re-frame/dispatch [::mounted]) + :component-will-unmount #(re-frame/dispatch [::unmounted]) + :reagent-render (fn [p] + [form-content p])})) diff --git a/src/cljs/auto_ap/views/pages/transactions/table.cljs b/src/cljs/auto_ap/views/pages/transactions/table.cljs index e1edf714..201a716b 100644 --- a/src/cljs/auto_ap/views/pages/transactions/table.cljs +++ b/src/cljs/auto_ap/views/pages/transactions/table.cljs @@ -40,6 +40,9 @@ :which (:id which)} :query-obj {:venia/queries (cond-> [{:query/data [:potential-payment-matches + {:transaction_id (:id which)} + [:id :memo :check-number [:vendor [:name]]]]} + {:query/data [:potential-payment-matches {:transaction_id (:id which)} [:id :memo :check-number [:vendor [:name]]]]}] (or (= "admin" role) @@ -55,8 +58,7 @@ (into [{:query/data [:potential-transaction-rule-matches {:transaction_id (:id which)} [:id :note]]}]))} - :on-success [::editing-matches-found which] - :on-error [::editing-matches-failed which]}})) + :on-success [::editing-matches-found which]}})) (re-frame/reg-sub ::table-params From 6ad554112447edec7ae908d6340b37a9125500b9 Mon Sep 17 00:00:00 2001 From: Bryce Covert Date: Tue, 12 Apr 2022 09:31:00 -0700 Subject: [PATCH 08/12] tweak. --- src/clj/auto_ap/graphql/vendors.clj | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/clj/auto_ap/graphql/vendors.clj b/src/clj/auto_ap/graphql/vendors.clj index 62820f8f..717b73e2 100644 --- a/src/clj/auto_ap/graphql/vendors.clj +++ b/src/clj/auto_ap/graphql/vendors.clj @@ -82,6 +82,7 @@ :vendor/legal-entity-tin-type (enum->keyword (:legal_entity_tin_type in) "legal-entity-tin-type") :vendor/legal-entity-1099-type (enum->keyword (:legal_entity_1099_type in) "legal-entity-1099-type"))))] (is-admin? (:id context)) (conj [:reset (if id id "vendor") :vendor/account-overrides account-overrides]) + (is-admin? (:id context)) (conj [:reset (if id id "vendor") :vendor/search-terms [name]]) (is-admin? (:id context)) (conj [:reset (if id id "vendor") :vendor/terms-overrides terms-overrides]) (is-admin? (:id context)) (conj [:reset (if id id "vendor") :vendor/schedule-payment-dom schedule-payment-dom]) (is-admin? (:id context)) (conj [:reset (if id id "vendor") :vendor/automatically-paid-when-due @@ -112,9 +113,6 @@ (audit-transact transaction (:id context)) to)) -#_(defn get-graphql [context args value] - (->graphql - (d-vendors/get-graphql (assoc args :id (:id context))))) (defn get-graphql [context args _] (assert-admin (:id context)) @@ -122,12 +120,12 @@ [vendors vendors-count ] (d-vendors/get-graphql (<-graphql args))] (result->page vendors vendors-count :vendors args))) -(defn get-by-id [context args value] +(defn get-by-id [context args _] (->graphql (d-vendors/get-graphql-by-id (assoc args :id (:id context)) (:id args)))) -(defn search [context args value] +(defn search [_ args _] (->> (d/q '[:find ?n ?i ?s :in $ ?q :where [(fulltext $ :vendor/search-terms ?q) [[?i ?n _ ?s]]] @@ -137,5 +135,4 @@ (sort-by last) (map (fn [[n i]] {:name n - :id i} - )))) + :id i})))) From f73b406abd7c5b50200861ed4a916016a5bc5bfd Mon Sep 17 00:00:00 2001 From: Bryce Covert Date: Tue, 12 Apr 2022 15:49:45 -0700 Subject: [PATCH 09/12] users can now edit vendors. --- src/clj/auto_ap/graphql/vendors.clj | 40 +++- .../auto_ap/views/components/layouts.cljs | 35 ++-- .../views/components/vendor_dialog.cljs | 171 ++++++++++++------ 3 files changed, 162 insertions(+), 84 deletions(-) diff --git a/src/clj/auto_ap/graphql/vendors.clj b/src/clj/auto_ap/graphql/vendors.clj index 717b73e2..82552575 100644 --- a/src/clj/auto_ap/graphql/vendors.clj +++ b/src/clj/auto_ap/graphql/vendors.clj @@ -3,19 +3,41 @@ [auto-ap.datomic :refer [audit-transact conn remove-nils]] [auto-ap.datomic.vendors :as d-vendors] [auto-ap.graphql.utils - :refer [->graphql <-graphql assert-admin enum->keyword is-admin? result->page]] + :refer [->graphql + <-graphql + assert-admin + assert-failure + enum->keyword + is-admin? + result->page]] + [clojure.set :as set] [clojure.tools.logging :as log] [datomic.api :as d])) +(defn can-user-edit-vendor? [vendor-id id] + (if (is-admin? id) + true + (empty? + (set/difference (set (d/q '[:find [?c ...] + :in $ ?v + :where [?vu :vendor-usage/vendor ?v] + [?vu :vendor-usage/client ?c] + [?vu :vendor-usage/count ?d] + [(>= ?d 0)]] + (d/db conn) + vendor-id)) + (set (map :db/id (:user/clients id))))))) + (defn upsert-vendor [context {{:keys [id name hidden terms code print_as primary_contact secondary_contact address default_account_id invoice_reminder_schedule schedule_payment_dom terms_overrides account_overrides] :as in} :vendor} value] - (when id - (assert-admin (:id context))) - #_(Thread/sleep 3000) - #_(throw (ex-info "" {:validation-error "can't do that."})) - (let [hidden (if (is-admin? (:id context)) + + (when (and id (not (can-user-edit-vendor? id (:id context)))) + (assert-failure "This vendor is managed by Integreat. Please reach out to ben@integreatconsult.com for your changes.")) + (let [ + hidden (if (is-admin? (:id context)) hidden false) - + existing (when id + (d/pull (d/db conn) '[:vendor/name] id)) terms-overrides (mapv (fn [to] (cond-> @@ -82,14 +104,14 @@ :vendor/legal-entity-tin-type (enum->keyword (:legal_entity_tin_type in) "legal-entity-tin-type") :vendor/legal-entity-1099-type (enum->keyword (:legal_entity_1099_type in) "legal-entity-1099-type"))))] (is-admin? (:id context)) (conj [:reset (if id id "vendor") :vendor/account-overrides account-overrides]) - (is-admin? (:id context)) (conj [:reset (if id id "vendor") :vendor/search-terms [name]]) (is-admin? (:id context)) (conj [:reset (if id id "vendor") :vendor/terms-overrides terms-overrides]) (is-admin? (:id context)) (conj [:reset (if id id "vendor") :vendor/schedule-payment-dom schedule-payment-dom]) (is-admin? (:id context)) (conj [:reset (if id id "vendor") :vendor/automatically-paid-when-due (mapv (fn [apwd] {:db/id apwd}) - (:automatically_paid_when_due in))])) + (:automatically_paid_when_due in))]) + (not= (:vendor/name existing) name) (conj [:reset (if id id "vendor") :vendor/search-terms [name]])) _ (log/info "Upserting vendor" transaction) transaction-result (audit-transact transaction (:id context))] diff --git a/src/cljs/auto_ap/views/components/layouts.cljs b/src/cljs/auto_ap/views/components/layouts.cljs index ed9d2000..1f35c153 100644 --- a/src/cljs/auto_ap/views/components/layouts.cljs +++ b/src/cljs/auto_ap/views/components/layouts.cljs @@ -1,23 +1,21 @@ (ns auto-ap.views.components.layouts (:require - [reagent.core :as reagent] - [re-frame.core :as re-frame] - [bidi.bidi :as bidi] + [auto-ap.events :as events] [auto-ap.routes :as routes] [auto-ap.subs :as subs] - [auto-ap.events :as events] - [auto-ap.views.utils :refer [active-when active-when= login-url dispatch-event appearing css-transition-group bind-field]] + [auto-ap.views.components.dropdown :refer [drop-down drop-down-contents]] [auto-ap.views.components.modal :as modal] - [auto-ap.entities.vendors :as vendor] - [clojure.string :as str] - [reagent.core :as r] [auto-ap.views.components.vendor-dialog :as vendor-dialog] - [auto-ap.views.components.buttons :as buttons])) - + [auto-ap.views.utils + :refer [active-when appearing bind-field dispatch-event dispatch-event-with-propagation login-url]] + [bidi.bidi :as bidi] + [clojure.string :as str] + [re-frame.core :as re-frame] + [reagent.core :as r])) (defn navbar-drop-down-contents [{:keys [id]} children ] (let [toggle-fn (fn [] (re-frame/dispatch [::events/toggle-menu id]))] - (reagent/create-class {:component-did-mount (fn [] (.addEventListener js/document "click" toggle-fn)) + (r/create-class {:component-did-mount (fn [] (.addEventListener js/document "click" toggle-fn)) :component-will-unmount (fn [] (.removeEventListener js/document "click" toggle-fn)) :reagent-render (fn [children] @@ -25,7 +23,7 @@ (defn navbar-drop-down [{:keys [ header id class]} child] (let [menu-active? (re-frame/subscribe [::subs/menu-active? id])] - (reagent/create-class + (r/create-class {:reagent-render (fn [{:keys [header id]} child] (let [menu-active? @(re-frame/subscribe [::subs/menu-active? id])] [:div { :class (str "navbar-item has-dropdown " (when menu-active? "is-active " ) " " class)} @@ -134,9 +132,16 @@ (when-not is-initial-loading [:div.navbar-end [:div.navbar-item - [buttons/new-button {:event [::vendor-dialog/started {}] - :name "Vendor" - :class "is-primary"}]] + [drop-down {:header [:a.button.is-outlined {:aria-haspopup true + :type "button" + :on-click (dispatch-event-with-propagation [::events/toggle-menu ::vendor ])} + "Vendors " + [:span " "] + [:span.icon [:i.fa.fa-angle-down {:aria-hidden "true"}]]] + :id ::vendor} + [:<> + [:a.dropdown-item {:on-click (dispatch-event-with-propagation [::vendor-dialog/started {}])} "New"] + [:a.dropdown-item {:on-click (dispatch-event-with-propagation [::vendor-dialog/edit {}])} "Edit"]]]] (when (> (count @clients) 1) diff --git a/src/cljs/auto_ap/views/components/vendor_dialog.cljs b/src/cljs/auto_ap/views/components/vendor_dialog.cljs index acbd7ad3..31fce5a4 100644 --- a/src/cljs/auto_ap/views/components/vendor_dialog.cljs +++ b/src/cljs/auto_ap/views/components/vendor_dialog.cljs @@ -6,6 +6,8 @@ [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] @@ -17,7 +19,6 @@ with-is-admin? with-user]] [clojure.spec.alpha :as s] - [clojure.string :as str] [re-frame.core :as re-frame])) (re-frame/reg-sub @@ -26,9 +27,6 @@ (fn [form] (s/valid? ::entity/vendor (:data form)))) -(defn ngrams [text len] - (mapv #(.toLowerCase (str/join "" %)) - (partition len 1 text))) (re-frame/reg-event-db ::settled @@ -61,7 +59,7 @@ :event [::settled]})] (forms/change-handler ::vendor-form - (fn [data field value] + (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]))) @@ -84,49 +82,48 @@ :owns-state {:single ::vendor-form} :query-obj {:venia/operation {:operation/type :mutation :operation/name "UpsertVendor"} :venia/queries [{:query/data [:upsert-vendor - {:vendor (doto (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) - )) - println)} + {: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 override-value-key change-event default-key data]} template] +(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? @@ -150,7 +147,7 @@ [: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 override-value-key change-event default-key data mandatory?]} template] +(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 @@ -187,7 +184,7 @@ [: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 override-value-key change-event data mandatory?]} template] +(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? @@ -217,9 +214,7 @@ [:a.button {:on-click (dispatch-event [::removed-override override-key i])} [:span.icon [:span.icon-remove]]]]]))]))) (defn form-content [{:keys [data change-event]}] - (let [accounts @(re-frame/subscribe [::subs/accounts]) - clients @(re-frame/subscribe [::subs/clients]) - is-admin? @(re-frame/subscribe [::subs/is-admin?])] + (let [is-admin? @(re-frame/subscribe [::subs/is-admin?])] [:div [horizontal-field [:label.label [:span "Name " @@ -257,7 +252,7 @@ [default-with-overrides {:data data :change-event change-event :default-key :terms :override-key :terms-overrides} - (fn [field client] + (fn [field _] [:input.input {:type "number" :step "1" :style {:width "4em"} @@ -267,15 +262,17 @@ :event change-event :subscription data}])] - [:h2.subtitle "Schedule payment when due"] + (when is-admin? + [:h2.subtitle "Schedule payment when due"]) [client-list {:data data :change-event change-event :override-key :automatically-paid-when-due}] - [:h2.subtitle "Schedule payment on day of month"] + (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 client] + (fn [field _] [:input.input {:type "number" :step "1" :style {:width "5em"} @@ -291,7 +288,6 @@ :default-key :default-account :override-key :account-overrides} (fn [field client] - [typeahead-v3 {:entities @(re-frame/subscribe [::subs/accounts client]) :entity->text account->match-text :field field @@ -453,16 +449,56 @@ [:option {:value nil} ""] [:option {:value "none"} "Don't 1099"] [:option {:value "misc"} "Misc"] - [:option {:value "landlord"} "Landlord"]]]]]] - ])) + [:option {:value "landlord"} "Landlord"]]]]]]])) -(defn vendor-dialog [{:keys [save-event] }] - (let [clients @(re-frame/subscribe [::subs/clients]) - {:keys [data error warning] :as f} @(re-frame/subscribe [::forms/form ::vendor-form]) +(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]] @@ -503,3 +539,18 @@ :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]}}]})) From f51f3450d6331b95a6cecc0832507c22178a83a3 Mon Sep 17 00:00:00 2001 From: Bryce Covert Date: Tue, 19 Apr 2022 13:27:47 -0700 Subject: [PATCH 10/12] Accounts don't need to be loaded --- src/clj/auto_ap/datomic/accounts.clj | 10 + src/clj/auto_ap/datomic/invoices.clj | 13 +- src/clj/auto_ap/datomic/ledger.clj | 33 ++- src/clj/auto_ap/datomic/migrate.clj | 31 ++- src/clj/auto_ap/datomic/transactions.clj | 19 +- src/clj/auto_ap/datomic/vendors.clj | 30 ++- src/clj/auto_ap/graphql.clj | 12 +- src/clj/auto_ap/graphql/accounts.clj | 80 ++++-- src/cljs/auto_ap/subs.cljs | 97 -------- .../components/expense_accounts_dialog.cljs | 67 +++-- .../components/expense_accounts_field.cljs | 229 +++++++++--------- .../views/components/invoice_table.cljs | 25 +- .../views/components/typeahead/vendor.cljs | 12 +- .../views/components/vendor_dialog.cljs | 26 +- .../auto_ap/views/pages/invoices/form.cljs | 10 +- src/cljs/auto_ap/views/pages/ledger.cljs | 52 ++-- .../views/pages/ledger/external_ledger.cljs | 2 +- .../pages/ledger/external_ledger_table.cljs | 8 +- .../auto_ap/views/pages/ledger/side_bar.cljs | 18 +- .../auto_ap/views/pages/ledger/table.cljs | 16 +- .../views/pages/transactions/form.cljs | 5 +- .../views/pages/transactions/side_bar.cljs | 23 +- 22 files changed, 398 insertions(+), 420 deletions(-) diff --git a/src/clj/auto_ap/datomic/accounts.clj b/src/clj/auto_ap/datomic/accounts.clj index 5f7b8d44..a7372bad 100644 --- a/src/clj/auto_ap/datomic/accounts.clj +++ b/src/clj/auto_ap/datomic/accounts.clj @@ -11,6 +11,16 @@ :account-client-override/name {:account-client-override/client [:db/id :client/name]}]}]) +(defn clientize [a client] + (if-let [override-name (->> a + :account/client-overrides + (filter #(= client + (:db/id (:account-client-override/client %)))) + first + :account-client-override/name)] + (assoc a :account/name override-name) + a)) + (defn get-accounts ([] (get-accounts {})) diff --git a/src/clj/auto_ap/datomic/invoices.clj b/src/clj/auto_ap/datomic/invoices.clj index 1c53e157..d1acb46a 100644 --- a/src/clj/auto_ap/datomic/invoices.clj +++ b/src/clj/auto_ap/datomic/invoices.clj @@ -6,13 +6,18 @@ [clj-time.coerce :as coerce] [auto-ap.time-utils :refer [next-dom]] [clj-time.core :as time] - [auto-ap.datomic.vendors :as d-vendors])) + [auto-ap.datomic.vendors :as d-vendors] + [clojure.tools.logging :as log] + [auto-ap.datomic.accounts :as d-accounts])) (def default-read '[* {:invoice/client [:client/name :db/id :client/locations :client/code]} {:invoice/vendor [* {:vendor/address [*]}]} {:invoice/status [:db/ident]} - {:invoice/expense-accounts [* {:invoice-expense-account/account [*]}]} + {:invoice/expense-accounts [* {:invoice-expense-account/account [:account/name :db/id + :account/location + {:account/client-overrides [:account-client-override/name + {:account-client-override/client [:db/id]}]}]}]} {:invoice-payment/_invoice [* {:invoice-payment/payment [* {:payment/status [*]} {:payment/bank-account [*]} {:transaction/_payment [*]}]}]}]) @@ -23,6 +28,10 @@ (update :invoice/due coerce/from-date) (update :invoice/scheduled-payment coerce/from-date) (update :invoice/status :db/ident) + (update :invoice/expense-accounts (fn [eas] + (map + #(update % :invoice-expense-account/account d-accounts/clientize (:db/id (:invoice/client x))) + eas))) (rename-keys {:invoice-payment/_invoice :invoice/payments}))) (defn raw-graphql-ids diff --git a/src/clj/auto_ap/datomic/ledger.clj b/src/clj/auto_ap/datomic/ledger.clj index 77f614cd..fb0f2da9 100644 --- a/src/clj/auto_ap/datomic/ledger.clj +++ b/src/clj/auto_ap/datomic/ledger.clj @@ -1,13 +1,9 @@ (ns auto-ap.datomic.ledger (:require [datomic.api :as d] - [auto-ap.graphql.utils :refer [->graphql limited-clients]] - [auto-ap.utils :refer [dollars-0?]] + [auto-ap.graphql.utils :refer [limited-clients]] [auto-ap.datomic :refer [merge-query apply-sort-3 apply-pagination add-sorter-fields conn]] - [auto-ap.datomic :refer [uri]] - - [clojure.tools.logging :as log] [clj-time.coerce :as c] - [clj-time.core :as time])) + [auto-ap.datomic.accounts :as d-accounts])) (def export-read [:journal-entry/external-id @@ -34,12 +30,12 @@ :account/name :account/numeric-code :account/location + {:account/client-overrides [:account-client-override/name + {:account-client-override/client [:db/id]}]} {:account/type [:db/ident :db/id]} {:bank-account/type [:db/ident :db/id]}]}]}]) (defn raw-graphql-ids [db args] - (log/info "ARGS" args) - (let [query (cond-> {:query {:find [] :in ['$ ] :where []} @@ -161,20 +157,27 @@ true (merge-query {:query {:find ['?sort-default '?e] :where ['[?e :journal-entry/date ?sort-default]]}}))] - (->> (doto query log/info) - + (->> query (d/query) (apply-sort-3 (update args :sort conj {:sort-key "default-2" :asc true})) (apply-pagination args)))) -(defn graphql-results [ids db args] +(defn graphql-results [ids db _] (let [results (->> (d/pull-many db '[* {:journal-entry/client [:client/name :client/code :db/id] :journal-entry/vendor [:vendor/name :db/id] :journal-entry/line-items [* {:journal-entry-line/account [* {:account/type [*]} + {:account/client-overrides [:account-client-override/name + {:account-client-override/client [:db/id]}]} {:bank-account/type [*]}]}]}] ids) (map #(update % :journal-entry/date c/from-date)) + (map (fn [je] + (update je :journal-entry/line-items + (fn [jels] + (map + #(update % :journal-entry-line/account d-accounts/clientize (:db/id (:journal-entry/client je))) + jels))))) (filter (fn [je] (every? (fn [jel] @@ -191,12 +194,9 @@ (defn get-graphql [args] (let [db (d/db conn) {ids-to-retrieve :ids matching-count :count} (raw-graphql-ids db args)] - [(->> (graphql-results ids-to-retrieve db args)) matching-count])) - - (defn filter-ids [ids] (if ids (->> {:query {:find ['?e] @@ -207,8 +207,3 @@ (map first) vec) [])) - - - - - diff --git a/src/clj/auto_ap/datomic/migrate.clj b/src/clj/auto_ap/datomic/migrate.clj index e09c3eba..fb2ff577 100644 --- a/src/clj/auto_ap/datomic/migrate.clj +++ b/src/clj/auto_ap/datomic/migrate.clj @@ -142,6 +142,22 @@ {:db/id accounts-payable :db/ident :account/accounts-payable}]])) +(defn add-account-search-terms [conn] + [(->> (d/q '[:find ?i ?n + :in $ + :where [?i :account/name ?n]] + (d/db conn)) + (map (fn [[i n]] + {:db/id i + :account/search-terms n}))) + (->> (d/q '[:find ?i ?n + :in $ + :where [?i :account-client-override/name ?n]] + (d/db conn)) + (map (fn [[i n]] + {:db/id i + :account-client-override/search-terms n})))]) + (defn migrate [conn] (let [ norms-map (merge {:auto-ap/base-schema {:txes auto-ap.datomic/base-schema} @@ -491,7 +507,20 @@ :db/valueType :db.type/boolean :db/cardinality :db.cardinality/one}]]} :auto-ap/add-payment-type-credit {:txes [[{:db/ident :payment-type/credit - :db/doc "Credit for negative invoices"}]]}} + :db/doc "Credit for negative invoices"}]]} + :auto-ap/fulltext-accounts {:txes [[{:db/ident :account/search-terms + :db/valueType :db.type/string + :db/cardinality :db.cardinality/one + :db/doc "a name search for accounts" + :db/fulltext true} + {:db/ident :account-client-override/search-terms + :db/valueType :db.type/string + :db/cardinality :db.cardinality/one + :db/doc "a name search for accounts" + :db/fulltext true}]] + :requires [:auto-ap/add-account-overrides]} + :auto-ap/add-search-terms-accounts {:txes-fn `add-account-search-terms + :requires [:auto-ap/fulltext-accounts]}} diff --git a/src/clj/auto_ap/datomic/transactions.clj b/src/clj/auto_ap/datomic/transactions.clj index 9b13fcae..eccf55a1 100644 --- a/src/clj/auto_ap/datomic/transactions.clj +++ b/src/clj/auto_ap/datomic/transactions.clj @@ -5,7 +5,8 @@ [clj-time.coerce :as c] [clj-time.coerce :as coerce] [clojure.tools.logging :as log] - [clojure.string :as str])) + [clojure.string :as str] + [auto-ap.datomic.accounts :as d-accounts])) (defn sort-fn [sort-by] (cond @@ -194,11 +195,20 @@ :transaction/accounts [:transaction-account/amount :db/id :transaction-account/location - {:transaction-account/account [:db/id :account/name :account/numeric-code]}] + {:transaction-account/account [:account/name :db/id + :account/location + {:account/client-overrides [:account-client-override/name + {:account-client-override/client [:db/id]}]}]}] :transaction/yodlee-merchant [:db/id :yodlee-merchant/yodlee-id :yodlee-merchant/name]}] ids) (map #(update % :transaction/date c/from-date)) (map #(update % :transaction/post-date c/from-date)) + (map #(update % :transaction/accounts + (fn [tas] + (map + (fn [ta] + (update ta :transaction-account/account d-accounts/clientize (:db/id (:transaction/client %)))) + tas)))) (map (fn [transaction] (cond-> transaction (:transaction/payment transaction) (update-in [:transaction/payment :payment/date] c/from-date) @@ -240,7 +250,10 @@ :transaction/accounts [:transaction-account/amount :db/id :transaction-account/location - { :transaction-account/account [:db/id :account/name :account/numeric-code]}] + { :transaction-account/account [:account/name :db/id + :account/location + {:account/client-overrides [:account-client-override/name + {:account-client-override/client [:db/id]}]}]}] :transaction/yodlee-merchant [:db/id :yodlee-merchant/yodlee-id :yodlee-merchant/name]}] id) (update :transaction/date c/from-date) diff --git a/src/clj/auto_ap/datomic/vendors.clj b/src/clj/auto_ap/datomic/vendors.clj index 0e3c7bed..4a8becd0 100644 --- a/src/clj/auto_ap/datomic/vendors.clj +++ b/src/clj/auto_ap/datomic/vendors.clj @@ -3,7 +3,8 @@ [auto-ap.datomic :refer [conn merge-query uri add-sorter-fields apply-pagination merge-query apply-sort-3]] [auto-ap.graphql.utils :refer [limited-clients]] [clojure.string :as str] - [datomic.api :as d])) + [datomic.api :as d] + [auto-ap.datomic.accounts :as d-accounts])) (defn <-datomic [a] (cond-> a @@ -18,21 +19,28 @@ nil)] (if clients (-> vendor - (update :vendor/account-overrides (fn [ao] (filter #(clients (:db/id (:vendor-account-override/client %))) ao))) + (update :vendor/account-overrides (fn [aos] + (->> aos + (filter #(clients (:db/id (:vendor-account-override/client %)))) + (map #(update % :vendor-account-override/account d-accounts/clientize (:db/id (:vendor-account-override/client %))))))) (update :vendor/terms-overrides (fn [to] (filter #(clients (:db/id (:vendor-terms-override/client %))) to))) (update :vendor/schedule-payment-dom (fn [to] (filter #(clients (:db/id (:vendor-schedule-payment-dom/client %))) to)))) - vendor))) + (-> vendor + (update :vendor/account-overrides (fn [aos] + (->> aos + (map #(update % :vendor-account-override/account d-accounts/clientize (:db/id (:vendor-account-override/client %))))))))))) (def default-read - '[* {:vendor/account-overrides [* {:vendor-account-override/client [:client/name :db/id] - :vendor-account-override/account [:account/name :account/numeric-code :db/id]}] - :vendor/terms-overrides [* {:vendor-terms-override/client [:client/name :client/code :db/id]}] - :vendor/schedule-payment-dom [* {:vendor-schedule-payment-dom/client [:client/name :client/code :db/id]}] + '[* {:vendor/account-overrides [* {:vendor-account-override/client [:client/name :db/id] + :vendor-account-override/account [:account/name :account/numeric-code :db/id + {:account/client-overrides [:account-client-override/client :account-client-override/name]}]}] + :vendor/terms-overrides [* {:vendor-terms-override/client [:client/name :client/code :db/id]}] + :vendor/schedule-payment-dom [* {:vendor-schedule-payment-dom/client [:client/name :client/code :db/id]}] :vendor/automatically-paid-when-due [:db/id :client/name] - :vendor/legal-entity-tin-type [:db/ident :db/id] - :vendor/legal-entity-1099-type [:db/ident :db/id] - :vendor/default-account [:db/id :account/numeric-code :account/name] - :vendor-usage/_vendor [:vendor-usage/client :vendor-usage/count]}]) + :vendor/legal-entity-tin-type [:db/ident :db/id] + :vendor/legal-entity-1099-type [:db/ident :db/id] + :vendor/default-account [:db/id :account/numeric-code :account/name] + :vendor-usage/_vendor [:vendor-usage/client :vendor-usage/count]}]) (defn get-usages [args] (->> (cond-> {:query {:find ['?v '?c '(count ?e)] diff --git a/src/clj/auto_ap/graphql.clj b/src/clj/auto_ap/graphql.clj index 60f9b147..d07135c6 100644 --- a/src/clj/auto_ap/graphql.clj +++ b/src/clj/auto_ap/graphql.clj @@ -102,6 +102,11 @@ {:fields {:name {:type 'String} :id {:type :id}}} + :account_search_result + {:fields {:name {:type 'String} + :id {:type :id} + :location {:type 'String}}} + :yodlee_provider_account {:fields {:id {:type 'Int} :client {:type :client} @@ -360,6 +365,10 @@ :search_vendor {:type '(list :search_result) :args {:query {:type 'String}} :resolve :search-vendor} + :search_account {:type '(list :account_search_result) + :args {:query {:type 'String} + :client_id {:type :id}} + :resolve :search-account} @@ -816,7 +825,8 @@ :mutation/merge-vendors gq-vendors/merge-vendors :mutation/request-import gq-requests/request-import :get-vendor gq-vendors/get-graphql - :search-vendor gq-vendors/search}) + :search-vendor gq-vendors/search + :search-account gq-accounts/search}) gq-checks/attach gq-ledger/attach gq-reports/attach diff --git a/src/clj/auto_ap/graphql/accounts.clj b/src/clj/auto_ap/graphql/accounts.clj index 9ed1ca1d..591598e8 100644 --- a/src/clj/auto_ap/graphql/accounts.clj +++ b/src/clj/auto_ap/graphql/accounts.clj @@ -1,51 +1,48 @@ (ns auto-ap.graphql.accounts (:require - [auto-ap.datomic :refer [audit-transact remove-nils uri]] + [auto-ap.datomic :refer [audit-transact remove-nils conn]] [auto-ap.datomic.accounts :as d-accounts] [auto-ap.graphql.utils :refer [->graphql <-graphql assert-can-see-client enum->keyword]] - [datomic.api :as d])) + [datomic.api :as d] + [clojure.tools.logging :as log])) -(defn get-accounts [context args value] +(defn get-accounts [_ args _] (->graphql (d-accounts/get-accounts (<-graphql args)))) -(defn default-for-vendor [context args value] + + +(defn default-for-vendor [context args _] (assert-can-see-client (:id context) (:client_id args)) (let [result (d-accounts/get-for-vendor (:vendor_id args) (:client_id args))] - (->graphql - (if-let [override-name (->> result - :account/client-overrides - (filter #(= (:client_id args) - (:db/id (:account-client-override/client %)))) - first - :account-client-override/name)] - (assoc result :account/name override-name) - result)))) + (->graphql (d-accounts/clientize result (:client_id args))))) -(defn upsert-account [context args value] +(defn upsert-account [context args _] (let [{{:keys [id client-overrides numeric-code location applicability account-set name type]} :account} (<-graphql args)] (when-not id (when (seq (d/query {:query {:find ['?e] :in '[$ ?account-set ?numeric-code] :where ['[?e :account/account-set ?account-set] '[?e :account/numeric-code ?numeric-code]]} - :args [(d/db (d/connect uri)) account-set numeric-code]})) + :args [(d/db conn) account-set numeric-code]})) (throw (ex-info (str "Account set " account-set " already has an account for code " numeric-code) {} )))) - (let [original (if id (d/entity (d/db (d/connect uri)) id)) + (let [original (when id + (d/entity (d/db conn) id)) result (audit-transact (cond-> [(remove-nils {:db/id (or id "new-account") :account/name name + :account/search-terms name :account/type (keyword "account-type" (clojure.core/name type)) :account/applicability (enum->keyword applicability "account-applicability") :account/account-set account-set :account/location location - :account/numeric-code (if-not id + :account/numeric-code (when-not id numeric-code) - :account/code (if-not id + :account/code (when-not id (str numeric-code))}) [:reset (or id "new-account") :account/client-overrides (mapv @@ -53,10 +50,55 @@ (remove-nils {:db/id (:id client-override) :account-client-override/client (:client-id client-override) - :account-client-override/name (:name client-override)})) + :account-client-override/name (:name client-override) + :account-client-override/search-terms (:name client-override)})) client-overrides)]] (and (not location) (:account/location original)) (conj [:db/retract (or id "new-account") :account/location (:account/location original)])) (:id context))] (->graphql (d-accounts/get-by-id (or id (get-in result [:tempids "new-account"]))))))) +(defn search [context {query :query client :client_id} _] + (when client + (assert-can-see-client (:id context) client)) + (let [num (some-> (re-find #"([0-9]+)" query) + second + (not-empty ) + Integer/parseInt)] + (if num + (->> (d/q '[:find ?n (pull ?i [:db/id :account/numeric-code :account/location]) + :in $ ?numeric-code + :where [?i :account/numeric-code ?numeric-code] + [?i :account/name ?n]] + (d/db conn) + num) + (map (fn [[n a]] + {:name (str (:account/numeric-code a) " - " n) + :id (:db/id a) + :location (:account/location a)}))) + + (->> (d/q '[:find ?n (pull ?i [:db/id :account/numeric-code :account/location]) ?s + :in $ ?q + :where [(fulltext $ :account/search-terms ?q) [[?i ?n _ ?s]]] + [?i :account/numeric-code ?numeric-code] + (or [?i :account/applicability :account-applicability/global] + [?i :account/applicability :account-applicability/optional])] + (d/db conn) + query) + (concat (when client + (d/q '[:find ?n (pull ?a [:db/id :account/numeric-code :account/location]) ?s + :in $ ?c ?q + :where + [?i :account-client-override/client ?c] + [(fulltext $ :account-client-override/search-terms ?q) [[?i ?n _ ?s]]] + [?a :account/client-overrides ?i] + [?a :account/numeric-code ?numeric-code]] + (d/db conn) + client + query))) + (sort-by (comp - last)) + (map (fn [[n a]] + {:name (str (:account/numeric-code a) " - " n) + :id (:db/id a) + :location (:account/location a)})))))) + diff --git a/src/cljs/auto_ap/subs.cljs b/src/cljs/auto_ap/subs.cljs index 912590ae..00b3766d 100644 --- a/src/cljs/auto_ap/subs.cljs +++ b/src/cljs/auto_ap/subs.cljs @@ -32,78 +32,6 @@ (fn [db] (by :id (:accounts db)))) -(defn clientize-account [account client] - (let [override (->> - (:client-overrides account) - (filter (fn [co] - (= (:id (:client co)) (:id client)))) - first)] - (condp = (:applicability account) - nil - (assoc account :name (or (:name override) (:name account))) - - :global - (assoc account :name (or (:name override) (:name account))) - - :optional - (when override - (assoc account :name (or (:name override) (:name account)))) - - :customized - (when override - (assoc account :name (or (:name override) (:name account))))))) - -(re-frame/reg-sub - ::accounts - :<- [::all-accounts] - :<- [::client] - (fn [[accounts client] [_ client-override]] - (transduce - (comp - (map - #(clientize-account % (or client-override client))) - (filter identity)) - conj - [] - accounts))) - -(re-frame/reg-sub - ::accounts-index - :<- [::accounts] - (fn [accounts] - (doto (MiniSearch. #js {:fields #js ["numeric-code" "name" "content"] - :storeFields #js ["id" "numeric-code" "name" "content"]}) - (.addAll (clj->js (map (fn [v] {:id (:id v) - :name (:name v) - :numeric-code (:numeric-code v) - :content (str (:numeric-code v) " " (:name v))}) - accounts)))))) - - - - -(re-frame/reg-sub - ::account - (fn [[_ client]] - [(re-frame/subscribe [::accounts-by-id client])]) - (fn [[as] [_ _ i]] - (as i))) - -(defn accounts-by-id [accounts client] - (by :id - (map - #(clientize-account % client) - - accounts))) - -(re-frame/reg-sub - ::accounts-by-id - (fn [[_ client-override]] - [(re-frame/subscribe [::accounts client-override]) - (re-frame/subscribe [::client])]) - (fn [[accounts client] ] - (accounts-by-id accounts client))) - (re-frame/reg-sub ::bank-accounts :<- [::clients] @@ -181,15 +109,6 @@ (fn [db] (::intuit-bank-accounts db))) -(re-frame/reg-sub - ::modal-state - (fn [db [_ id status-from]] - (if status-from - (assoc (get (:modal-state db) id) - :error-message (get-in db [:auto-ap.forms/forms status-from :error]) - :saving? (= (get-in db [:auto-ap.forms/forms status-from :status]) :loading)) - (get (:modal-state db) id)))) - (re-frame/reg-sub ::token (fn [db] @@ -224,22 +143,6 @@ (fn [db] (:active-page db))) -(re-frame/reg-sub - ::pending-invoices - (fn [db] - (:pending (:invoices db)))) - - -(re-frame/reg-sub - ::status - (fn [db] - (:status db))) - -(re-frame/reg-sub - ::new-invoice-form - (fn [db] - (:new-invoice db))) - (re-frame/reg-sub ::query-params (fn [db] diff --git a/src/cljs/auto_ap/views/components/expense_accounts_dialog.cljs b/src/cljs/auto_ap/views/components/expense_accounts_dialog.cljs index 5ceea8c1..88d1b3ed 100644 --- a/src/cljs/auto_ap/views/components/expense_accounts_dialog.cljs +++ b/src/cljs/auto_ap/views/components/expense_accounts_dialog.cljs @@ -1,15 +1,16 @@ (ns auto-ap.views.components.expense-accounts-dialog - (:require [auto-ap.forms :as forms] - [auto-ap.status :as status] - [auto-ap.subs :as subs] - [auto-ap.utils :refer [by]] - [auto-ap.views.components.modal :as modal] - [auto-ap.views.components.typeahead :refer [typeahead-v3]] - [auto-ap.views.pages.invoices.common :refer [invoice-read]] - [auto-ap.views.utils :refer [dispatch-event with-user]] - [clojure.string :as str] - [goog.string :as gstring] - [re-frame.core :as re-frame])) + (:require + [auto-ap.forms :as forms] + [auto-ap.status :as status] + [auto-ap.subs :as subs] + [auto-ap.utils :refer [by]] + [auto-ap.views.components.modal :as modal] + [auto-ap.views.components.typeahead.vendor :refer [search-backed-typeahead]] + [auto-ap.views.pages.invoices.common :refer [invoice-read]] + [auto-ap.views.utils :refer [dispatch-event with-user]] + [clojure.string :as str] + [goog.string :as gstring] + [re-frame.core :as re-frame])) (re-frame/reg-sub ::can-submit @@ -95,7 +96,6 @@ {:keys [total] :or {total 0} {:keys [locations] :as client} :client} (:invoice data) {:keys [form-inline horizontal-field field raw-field error-notification submit-button]} change-expense-accounts-form multi-location? (> (count locations) 1) - chooseable-expense-accounts @(re-frame/subscribe [::subs/accounts client]) expense-accounts-total (->> expense-accounts vals (map :new-amount) @@ -124,14 +124,17 @@ [:tr [:td.expandable [:div.control (raw-field - [typeahead-v3 {:entities chooseable-expense-accounts - :type "typeahead-v3" - :entity->text (fn [x] (str (:numeric-code x) " - " (:name x))) - :field [:expense-accounts id :account]}])]] + [search-backed-typeahead {:search-query (fn [i] + [:search_account + {:query i + :client-id (:id client)} + [:name :id :location]]) + :type "typeahead-v3" + :field [:expense-accounts id :account]}])]] (when multi-location? [:td - (if-let [forced-location (:location @(re-frame/subscribe [::subs/account client (get-in expense-accounts [id :account :id])]))] + (if-let [forced-location (get-in expense-accounts [id :account :location])] [:div.select [:select {:disabled "disabled" :value forced-location} [:option {:value forced-location} forced-location]]] [:div.select @@ -175,21 +178,17 @@ (re-frame/reg-event-fx ::show (fn [{:keys [db]} [_ i]] - (let [accounts-by-id @(re-frame/subscribe [::subs/accounts-by-id])] - {:dispatch [::modal/modal-requested {:title "Change expense accounts" - :body [form] - :confirm {:value "Save" - :status-from [::status/single ::form] - :class "is-primary" - :on-click (dispatch-event [::try-save]) - :can-submit [::can-submit] - :close-event [::status/completed ::form]}}] - :db (-> db - (forms/start-form ::form + {:dispatch [::modal/modal-requested {:title "Change expense accounts" + :body [form] + :confirm {:value "Save" + :status-from [::status/single ::form] + :class "is-primary" + :on-click (dispatch-event [::try-save]) + :can-submit [::can-submit] + :close-event [::status/completed ::form]}}] + :db (-> db + (forms/start-form ::form - {:expense-accounts (by :id - (mapv - (fn [ea] - (assoc ea :account (accounts-by-id (:id (:account ea))))) - (:expense-accounts i))) - :invoice i}))}))) + {:expense-accounts (by :id + (:expense-accounts i)) + :invoice i}))})) diff --git a/src/cljs/auto_ap/views/components/expense_accounts_field.cljs b/src/cljs/auto_ap/views/components/expense_accounts_field.cljs index 5a2ba6ef..5ce35fb5 100644 --- a/src/cljs/auto_ap/views/components/expense_accounts_field.cljs +++ b/src/cljs/auto_ap/views/components/expense_accounts_field.cljs @@ -1,10 +1,10 @@ (ns auto-ap.views.components.expense-accounts-field - (:require [auto-ap.subs :as subs] - [auto-ap.views.components.typeahead :refer [typeahead-v3]] - [auto-ap.views.utils :refer [bind-field dispatch-event ->$]] - [goog.string :as gstring] - [re-frame.core :as re-frame] - [clojure.string :as str])) + (:require + [auto-ap.views.utils :refer [->$ bind-field dispatch-event]] + [auto-ap.views.components.typeahead.vendor :refer [search-backed-typeahead]] + [clojure.string :as str] + [goog.string :as gstring] + [re-frame.core :as re-frame])) (defn can-replace-with-default? [accounts] (and (or (not (seq accounts)) @@ -25,14 +25,11 @@ :account default-account}]) -(defn from-graphql [accounts accounts-by-id total locations] +(defn from-graphql [accounts total locations] (if (seq accounts) (vec (map (fn [a] (-> a - (update :account (fn [a] - (accounts-by-id (:id a)))) - (update :amount js/parseFloat) (assoc :amount-percentage (* 100 (/ (js/parseFloat (:amount a)) (Math/abs total)))) @@ -92,14 +89,10 @@ (re-frame/reg-event-fx ::expense-account-changed (fn [_ [_ event expense-accounts max-value field value]] - (println field value) (let [updated-accounts (cond-> expense-accounts true (assoc-in field value) (= (list :account) (drop 1 field)) (assoc-in [(first field) :location] nil) - (= (list :account :id) (drop 1 field)) (-> (assoc-in [(first field) :account] @(re-frame/subscribe [::subs/account nil value])) - (assoc-in [(first field) :location] nil)) - (= (list :amount-percentage) (drop 1 field)) (assoc-in [(first field) :amount] (js/parseFloat (goog.string/format "%.2f" @@ -116,111 +109,111 @@ ;; VIEWS (defn expense-accounts-field [{expense-accounts :value client :client max-value :max locations :locations event :event descriptor :descriptor disabled :disabled percentage-only? :percentage-only? :or {percentage-only? false}}] - (let [chooseable-expense-accounts @(re-frame/subscribe [::subs/accounts client]) - accounts-by-id @(re-frame/subscribe [::subs/accounts-by-id client])] - [:div - [:div.columns - [:div.column - [:h1.subtitle.is-4.is-inline (str/capitalize descriptor) "s"] - (when-not percentage-only? - [:p.help "Remaining " (->$ (- max-value (reduce + 0 (map (comp js/parseFloat :amount) expense-accounts))))])] - [:div.column.is-narrow - (when-not disabled - [:p.buttons - [:a.button {:on-click (dispatch-event [::spread-evenly event expense-accounts max-value])} "Spread evenly"] - [:a.button {:on-click (dispatch-event [::add-expense-account event expense-accounts locations])} "Add"]])]] + [:div + [:div.columns + [:div.column + [:h1.subtitle.is-4.is-inline (str/capitalize descriptor) "s"] + (when-not percentage-only? + [:p.help "Remaining " (->$ (- max-value (reduce + 0 (map (comp js/parseFloat :amount) expense-accounts))))])] + [:div.column.is-narrow + (when-not disabled + [:p.buttons + [:a.button {:on-click (dispatch-event [::spread-evenly event expense-accounts max-value])} "Spread evenly"] + [:a.button {:on-click (dispatch-event [::add-expense-account event expense-accounts locations])} "Add"]])]] - (for [[index {:keys [account id location amount amount-percentage amount-mode] :as expense-account}] (map vector (range) expense-accounts) - :let [account (accounts-by-id (:id account))]] - ^{:key id} - [:div.box - [:div.columns - [:div.column - [:h1.subtitle.is-6 (cond (and account (not percentage-only?)) - (str (:name account) " - " - location ": " - (gstring/format "$%.2f" (or amount 0) )) + (for [[index {:keys [account id location amount amount-percentage amount-mode] :as expense-account}] (map vector (range) expense-accounts)] + ^{:key id} + [:div.box + [:div.columns + [:div.column + [:h1.subtitle.is-6 (cond (and account (not percentage-only?)) + (str (:name account) " - " + location ": " + (gstring/format "$%.2f" (or amount 0) )) - account - (str (:name account) " - " - location ": %" - amount-percentage) + account + (str (:name account) " - " + location ": %" + amount-percentage) - :else - [:i "New " descriptor])]] - [:div.column.is-narrow - (when-not disabled - [:a.delete {:on-click (dispatch-event [::remove-expense-account event expense-accounts id])}])]] - - [:div.field - [:div.columns - [:div.column - [:p.help "Account"] - [:div.control.is-fullwidth - [bind-field - ^{:key (:id client)} - [typeahead-v3 {:entities chooseable-expense-accounts - :entity->text (fn [x ] - (str (:numeric-code x) " - " (:name x))) - :disabled disabled - :type "typeahead-v3" - :field [index :account] - :event [::expense-account-changed event expense-accounts max-value] - :subscription expense-accounts}]]]] - [:div.column.is-narrow - [:p.help "Location"] - [:div.control - (if-let [forced-location (:location account)] - [:div.select - [:select {:disabled "disabled" :style {:width "5em"} :value forced-location} [:option {:value forced-location} forced-location]]] - [:div.select - [bind-field - [:select {:type "select" - :disabled (boolean (or (:location account) - disabled)) - :style {:width "5em"} - :field [index :location] - :allow-nil? true - :spec (set locations) - :event [::expense-account-changed event expense-accounts max-value] - :subscription expense-accounts} - (map (fn [l] ^{:key l} [:option {:value l} l]) locations)]]])]]]] - - [:div.field - [:p.help "Amount"] + :else + [:i "New " descriptor])]] + [:div.column.is-narrow + (when-not disabled + [:a.delete {:on-click (dispatch-event [::remove-expense-account event expense-accounts id])}])]] + + [:div.field + [:div.columns + [:div.column + [:p.help "Account"] + [:div.control.is-fullwidth + [bind-field + ^{:key (:id client)} + [search-backed-typeahead {:search-query (fn [i] + [:search_account + {:query i + :client-id (:id client)} + [:name :id :location]]) + :type "typeahead-v3" + :field [index :account] + + :disabled disabled + :event [::expense-account-changed event expense-accounts max-value] + :subscription expense-accounts}]]]] + [:div.column.is-narrow + [:p.help "Location"] [:div.control - [:div.field.has-addons.is-extended - [:p.control [:span.select - [bind-field - [:select {:type "select" - :disabled (or disabled percentage-only?) - :field [index :amount-mode] - :allow-nil? false - :event [::expense-account-changed event expense-accounts max-value] - :subscription expense-accounts} - [:option "$"] - [:option "%"]]]]] - [:p.control - (if (= "$" amount-mode) - [bind-field - [:input.input {:type "number" - :field [index :amount] - :style {:text-align "right" :width "7em"} - :event [::expense-account-changed event expense-accounts max-value] - :disabled disabled - :subscription expense-accounts - :precision 2 - :value (get-in expense-account [:amount]) - :max max-value - :step "0.01"}]] - [bind-field - [:input.input {:type "number" - :field [index :amount-percentage] - :style {:text-align "right" :width "7em"} - :disabled disabled - :event [::expense-account-changed event expense-accounts max-value] - :precision 2 - :subscription expense-accounts - :value (get-in expense-account [:amount-percentage]) - :max "100" - :step "0.01"}]])]]]]])])) + (if-let [forced-location (:location account)] + [:div.select + [:select {:disabled "disabled" :style {:width "5em"} :value forced-location} [:option {:value forced-location} forced-location]]] + [:div.select + [bind-field + [:select {:type "select" + :disabled (boolean (or (:location account) + disabled)) + :style {:width "5em"} + :field [index :location] + :allow-nil? true + :spec (set locations) + :event [::expense-account-changed event expense-accounts max-value] + :subscription expense-accounts} + (map (fn [l] ^{:key l} [:option {:value l} l]) locations)]]])]]]] + + [:div.field + [:p.help "Amount"] + [:div.control + [:div.field.has-addons.is-extended + [:p.control [:span.select + [bind-field + [:select {:type "select" + :disabled (or disabled percentage-only?) + :field [index :amount-mode] + :allow-nil? false + :event [::expense-account-changed event expense-accounts max-value] + :subscription expense-accounts} + [:option "$"] + [:option "%"]]]]] + [:p.control + (if (= "$" amount-mode) + [bind-field + [:input.input {:type "number" + :field [index :amount] + :style {:text-align "right" :width "7em"} + :event [::expense-account-changed event expense-accounts max-value] + :disabled disabled + :subscription expense-accounts + :precision 2 + :value (get-in expense-account [:amount]) + :max max-value + :step "0.01"}]] + [bind-field + [:input.input {:type "number" + :field [index :amount-percentage] + :style {:text-align "right" :width "7em"} + :disabled disabled + :event [::expense-account-changed event expense-accounts max-value] + :precision 2 + :subscription expense-accounts + :value (get-in expense-account [:amount-percentage]) + :max "100" + :step "0.01"}]])]]]]])]) diff --git a/src/cljs/auto_ap/views/components/invoice_table.cljs b/src/cljs/auto_ap/views/components/invoice_table.cljs index bb0e77f4..af11e4ee 100644 --- a/src/cljs/auto_ap/views/components/invoice_table.cljs +++ b/src/cljs/auto_ap/views/components/invoice_table.cljs @@ -13,7 +13,6 @@ [auto-ap.views.utils :refer [date->str dispatch-event dispatch-event-with-propagation nf days-until]] [bidi.bidi :as bidi] [cemerick.url :as url] - [cljs-time.core :as t] [clojure.string :as str] [goog.string :as gstring] [re-frame.core :as re-frame] @@ -51,7 +50,7 @@ [[:invoices [:id :total :outstanding-balance :invoice-number :date :due :status :client-identifier :scheduled-payment :source-url :similarity [:vendor [:name :id]] [:expense_accounts [:amount :id :location - [:account [:id ]]]] + [:account [:id :name :location]]]] [:client [:name :id :locations]] [:payments [:amount :id [:payment [:id :status :amount :s3_url :check_number [:transaction [:post_date]]]]]]]] @@ -112,15 +111,13 @@ (re-frame/reg-event-fx ::invoice-updated - (fn [{:keys [db]} [_ invoice]] + (fn [{:keys [db]} [_ _]] {:db db})) -(defn row [{:keys [invoice check-boxes selected-client overrides checkable? expense-event actions]}] +(defn row [{:keys [invoice selected-client overrides checkable? actions]}] (let [{:keys [client status payments expense-accounts invoice-number date due total outstanding-balance id vendor source-url] :as i} invoice - accounts-by-id @(re-frame/subscribe [::subs/accounts-by-id client]) unautopay-states @(re-frame/subscribe [::status/multi ::unautopay]) - editing-states @(re-frame/subscribe [::status/multi ::edits]) - account->name #(:name (accounts-by-id (:id %)))] + editing-states @(re-frame/subscribe [::status/multi ::edits])] [grid/row {:class (:class i) :id id :checkable? checkable? :entity invoice} (when-not selected-client [grid/cell {} @@ -143,9 +140,8 @@ [grid/cell {:class "has-text-right"} (nf total )] [grid/cell {:class "has-text-right"} - (if (:scheduled-payment i) - [:<> [:div.tag.is-info.is-light "Autopay"] " "] - ) + (when (:scheduled-payment i) + [:<> [:div.tag.is-info.is-light "Autopay"] " "]) (nf outstanding-balance )] [grid/button-cell {} [:div.buttons @@ -160,7 +156,7 @@ [:div (for [e expense-accounts] ^{:key (:id e)} - [:span.dropdown-item (account->name (:account e)) " " (gstring/format "$%.2f" (:amount e) ) ]) + [:span.dropdown-item (:name (:account e)) " " (gstring/format "$%.2f" (:amount e) ) ]) (when (get actions :expense-accounts) [:<> @@ -237,17 +233,14 @@ :class (status/class-for (get unautopay-states (:id i))) :event [::unautopay i]}])]]])) -(defn invoice-table [{:keys [id check-boxes overrides actions data-page checkable-fn]}] - (let [selected-client @(re-frame/subscribe [::subs/client]) - {:keys [data status params table-params]} @(re-frame/subscribe [::data-page/page data-page]) - +(defn invoice-table [{:keys [check-boxes overrides actions data-page checkable-fn]}] + (let [{:keys [data status params table-params]} @(re-frame/subscribe [::data-page/page data-page]) selected-client @(re-frame/subscribe [::subs/client]) is-loading? (= :loading (:state status)) is-sorted-by-vendor? (and (= "vendor" (:sort-key (first (:sort table-params)))) (not is-loading?) (or (apply <= (map (comp :name :vendor) (:data data))) (apply >= (map (comp :name :vendor) (:data data))))) - [invoice-groups] (if is-sorted-by-vendor? (reduce (fn [[acc last-vendor] invoice] diff --git a/src/cljs/auto_ap/views/components/typeahead/vendor.cljs b/src/cljs/auto_ap/views/components/typeahead/vendor.cljs index 57b1ab37..87a07b5f 100644 --- a/src/cljs/auto_ap/views/components/typeahead/vendor.cljs +++ b/src/cljs/auto_ap/views/components/typeahead/vendor.cljs @@ -58,7 +58,7 @@ :time 250 :key ::input-value-settled}}))) -(defn typeahead-v3-internal [{:keys [class style ^js entity->text on-change disabled value name search-query auto-focus] :or {disabled false} :as i}] +(defn typeahead-v3-internal [{:keys [class style ^js on-change disabled value name search-query auto-focus] :or {disabled false} :as i}] (let [[items set-items] (react/useState []) [loading-status set-loading-status] (react/useState false) [getLabelProps getMenuProps getComboboxProps getToggleButtonProps getInputProps getItemProps isOpen highlightedIndex selectItem selectedItem setInputValue] @@ -73,8 +73,6 @@ (when on-change (on-change (js->clj (aget z "selectedItem") :keywordize-keys true))))})) $ (map #(aget $ %) ["getLabelProps" "getMenuProps" "getComboboxProps" "getToggleButtonProps" "getInputProps" "getItemProps" "isOpen" "highlightedIndex" "selectItem" "selectedItem" "setInputValue"]))] - #_(println (getInputProps)) - [:<> [:div.typeahead (assoc (js->clj (getComboboxProps)) :style style) @@ -96,7 +94,7 @@ :tab-index "0") [:div.control [:div.tags.has-addons - [:span.tag (entity->text (js->clj selectedItem :keywordize-keys true))] + [:span.tag (:name (js->clj selectedItem :keywordize-keys true))] (when name [:input {:type "hidden" :name name :value (:id (js->clj selectedItem :keywordize-keys true))}]) (when-not disabled @@ -125,10 +123,8 @@ [:li.typeahead-suggestion (assoc (js->clj (getItemProps #js {:item item :index index})) :class (if (= index highlightedIndex) "typeahead-highlighted")) - (entity->text item)]))]]]])) + (:name item)]))]]]])) (defn search-backed-typeahead [props] [:div - [:f> typeahead-v3-internal (assoc props - :entity->text :name - )]]) + [:f> typeahead-v3-internal props]]) diff --git a/src/cljs/auto_ap/views/components/vendor_dialog.cljs b/src/cljs/auto_ap/views/components/vendor_dialog.cljs index 31fce5a4..4d099d4d 100644 --- a/src/cljs/auto_ap/views/components/vendor_dialog.cljs +++ b/src/cljs/auto_ap/views/components/vendor_dialog.cljs @@ -12,8 +12,7 @@ [auto-ap.views.components.typeahead :refer [typeahead-v3]] [auto-ap.views.pages.admin.vendors.common :as common] [auto-ap.views.utils - :refer [account->match-text - bind-field + :refer [bind-field dispatch-event horizontal-field with-is-admin? @@ -87,8 +86,7 @@ :print-as print-as :terms terms :default-account-id (:id default-account) - :address address - :primary-contact primary-contact + :address address :primary-contact primary-contact :secondary-contact secondary-contact :invoice-reminder-schedule invoice-reminder-schedule} is-admin? (assoc :hidden hidden @@ -288,12 +286,15 @@ :default-key :default-account :override-key :account-overrides} (fn [field client] - [typeahead-v3 {:entities @(re-frame/subscribe [::subs/accounts client]) - :entity->text account->match-text - :field field - :type "typeahead-v3" - :event change-event - :subscription data}])] + [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] @@ -525,10 +526,7 @@ (update :automatically-paid-when-due #(mapv identity %)) (update :hidden #(if (nil? %) false - %)) - (update :default-account - (fn [da] - @(re-frame/subscribe [::subs/account nil (:id da)])))))) + %))))) :dispatch [::modal/modal-requested {:title "Vendor" :class "is-wide" diff --git a/src/cljs/auto_ap/views/pages/invoices/form.cljs b/src/cljs/auto_ap/views/pages/invoices/form.cljs index 69479561..def5a8fb 100644 --- a/src/cljs/auto_ap/views/pages/invoices/form.cljs +++ b/src/cljs/auto_ap/views/pages/invoices/form.cljs @@ -108,23 +108,18 @@ (re-frame/reg-event-fx ::adding [(re-frame/inject-cofx ::inject/sub (fn [[_ which _]] - [::subs/accounts-by-id (:id (:client which))])) - (re-frame/inject-cofx ::inject/sub (fn [[_ which _]] [::subs/locations-for-client (:id (:client which))]))] - (fn [{:keys [db] ::subs/keys [locations-for-client accounts-by-id]} [_ new]] + (fn [{:keys [db] ::subs/keys [locations-for-client]} [_ new]] {:db (-> db (forms/start-form ::form (assoc new :expense-accounts (expense-accounts-field/from-graphql (:expense-accounts new) - accounts-by-id 0.0 locations-for-client))))})) (re-frame/reg-event-fx ::editing [(re-frame/inject-cofx ::inject/sub (fn [[_ which _]] - [::subs/accounts-by-id (:id (:client which))])) - (re-frame/inject-cofx ::inject/sub (fn [[_ which _]] [::subs/locations-for-client (:id (:client which))]))] - (fn [{:keys [db] ::subs/keys [locations-for-client accounts-by-id]} [_ which vendor-preferences]] + (fn [{:keys [db] ::subs/keys [locations-for-client]} [_ which vendor-preferences]] (let [edit-invoice (update which :date #(date->str % standard)) edit-invoice (update edit-invoice :due #(date->str % standard)) edit-invoice (update edit-invoice :scheduled-payment #(date->str % standard)) @@ -144,7 +139,6 @@ :vendor (:vendor edit-invoice) :client (:client edit-invoice) :expense-accounts (expense-accounts-field/from-graphql (:expense-accounts which) - accounts-by-id (:total which) locations-for-client)}))}))) diff --git a/src/cljs/auto_ap/views/pages/ledger.cljs b/src/cljs/auto_ap/views/pages/ledger.cljs index 5b149c26..655c1fcc 100644 --- a/src/cljs/auto_ap/views/pages/ledger.cljs +++ b/src/cljs/auto_ap/views/pages/ledger.cljs @@ -1,27 +1,18 @@ (ns auto-ap.views.pages.ledger - (:require [auto-ap.events :as events] - [auto-ap.forms :as forms] - [auto-ap.subs :as subs] - [auto-ap.views.components.bank-account-filter :refer [bank-account-filter]] - [auto-ap.views.components.layouts :refer [appearing-side-bar side-bar-layout]] - [auto-ap.routes :as routes] - [bidi.bidi :as bidi] - [auto-ap.views.components.paginator :refer [paginator]] - [auto-ap.views.components.sorter :refer [sorted-column]] - [auto-ap.views.pages.ledger.table :as table] - [auto-ap.views.pages.ledger.side-bar :refer [ledger-side-bar] :as side-bar] - [auto-ap.views.pages.transactions.common :refer [transaction-read]] + (:require + [auto-ap.subs :as subs] + [auto-ap.views.components.layouts :refer [side-bar-layout]] + [auto-ap.views.pages.data-page :as data-page] + [auto-ap.views.pages.ledger.side-bar + :as side-bar + :refer [ledger-side-bar]] + [auto-ap.views.pages.ledger.table :as table] + [auto-ap.views.utils :refer [with-user]] + [clojure.set :as set] + [re-frame.core :as re-frame] + [reagent.core :as reagent] + [vimsical.re-frame.fx.track :as track])) - [auto-ap.utils :refer [replace-by]] - [auto-ap.views.pages.transactions.manual :as manual] - [auto-ap.views.utils :refer [bind-field date->str dispatch-event nf active-when with-user]] - [goog.string :as gstring] - [re-frame.core :as re-frame] - [reagent.core :as reagent] - [auto-ap.status :as status] - [vimsical.re-frame.fx.track :as track] - [auto-ap.views.pages.data-page :as data-page] - [clojure.set :as set])) (defn data-params->query-params [params] {:start (:start params 0) :sort (:sort params) @@ -39,7 +30,7 @@ (re-frame/reg-event-fx ::params-change [with-user] - (fn [{:keys [user db]} [_ params]] + (fn [{:keys [user]} [_ params]] {:graphql {:token user :owns-state {:single [::data-page/page ::page]} :query-obj {:venia/queries [[:ledger-page @@ -57,7 +48,7 @@ [:name :id]] [:line-items [:id :debit :credit :location :running-balance - [:account [:id]]]] + [:account [:id :name]]]] :date]] :total :start @@ -68,13 +59,13 @@ (re-frame/reg-event-fx ::unmounted - (fn [{:keys [db]} _] + (fn [_ _] {:dispatch [::data-page/dispose ::page] ::track/dispose {:id ::params}})) (re-frame/reg-event-fx ::mounted - (fn [{:keys [db]} _] + (fn [_ _] {::track/register {:id ::params :subscription [::data-page/params ::page] :event-fn (fn [params] [::params-change params])}})) @@ -82,11 +73,10 @@ (defn ledger-content [] - (let [current-client @(re-frame/subscribe [::subs/client])] - [:div - [:h1.title "Ledger"] - [table/table {:id :ledger - :data-page ::page}]])) + [:div + [:h1.title "Ledger"] + [table/table {:id :ledger + :data-page ::page}]]) (defn ledger-page [] diff --git a/src/cljs/auto_ap/views/pages/ledger/external_ledger.cljs b/src/cljs/auto_ap/views/pages/ledger/external_ledger.cljs index 84fd1b7e..7551c603 100644 --- a/src/cljs/auto_ap/views/pages/ledger/external_ledger.cljs +++ b/src/cljs/auto_ap/views/pages/ledger/external_ledger.cljs @@ -52,7 +52,7 @@ [:name :id]] [:line-items [:id :debit :credit :location :running-balance - [:account [:id]]]] + [:account [:id :name]]]] :date]] :total :start diff --git a/src/cljs/auto_ap/views/pages/ledger/external_ledger_table.cljs b/src/cljs/auto_ap/views/pages/ledger/external_ledger_table.cljs index d72931f0..093617fd 100644 --- a/src/cljs/auto_ap/views/pages/ledger/external_ledger_table.cljs +++ b/src/cljs/auto_ap/views/pages/ledger/external_ledger_table.cljs @@ -11,7 +11,7 @@ (defn external-ledger-row [{{:keys [client vendor alternate-description status date amount id source external-id line-items] :as i} :row - :keys [selected-client accounts-by-id bank-accounts-by-id]}] + :keys [selected-client bank-accounts-by-id]}] [:<> [grid/row {:class (:class i) :id id :checkable? true :entity i} (when-not selected-client @@ -28,8 +28,8 @@ [grid/cell {:class "has-text-right"} (nf amount )]] [:<> (for [{:keys [debit credit location account id running-balance]} line-items - :let [account (or (accounts-by-id (:id account)) - (bank-accounts-by-id (:id account)))]] + :let [account (or (bank-accounts-by-id (:id account)) + account)]] ^{:key id} [grid/row {:checkable? false} (when-not selected-client @@ -49,7 +49,6 @@ (defn table [{:keys [id data-page status vendors on-check-changed expense-event]}] (let [{:keys [data status params]} @(re-frame/subscribe [::data-page/page data-page]) selected-client @(re-frame/subscribe [::subs/client]) - accounts-by-id @(re-frame/subscribe [::subs/accounts-by-id selected-client]) bank-accounts-by-id @(re-frame/subscribe [::subs/bank-accounts-by-id])] [grid/grid {:data-page data-page :check-boxes? true @@ -73,6 +72,5 @@ ^{:key id} [external-ledger-row {:row i :selected-client selected-client - :accounts-by-id accounts-by-id :bank-accounts-by-id bank-accounts-by-id}])]]])) diff --git a/src/cljs/auto_ap/views/pages/ledger/side_bar.cljs b/src/cljs/auto_ap/views/pages/ledger/side_bar.cljs index 724895b7..d15c3174 100644 --- a/src/cljs/auto_ap/views/pages/ledger/side_bar.cljs +++ b/src/cljs/auto_ap/views/pages/ledger/side_bar.cljs @@ -17,8 +17,7 @@ (defn ledger-side-bar [{:keys [data-page]}] (let [ap @(re-frame/subscribe [::subs/active-page]) user @(re-frame/subscribe [::subs/user]) - accounts-by-id @(re-frame/subscribe [::subs/accounts-by-id]) - accounts-index @(re-frame/subscribe [::subs/accounts-index])] + client @(re-frame/subscribe [::subs/client])] [:div [:ul.menu-list [:li.menu-item @@ -61,12 +60,15 @@ [:p.menu-label "Financial Account"] [:div - [typeahead-v3 {:entities-by-id accounts-by-id - :entity-index accounts-index - :entity->text (fn [x ] (str (:numeric-code x) " - " (:name x))) - :type "typeahead-v3" - :on-change #(re-frame/dispatch [::data-page/filter-changed data-page :account (some-> % (select-keys [:name :id :numeric-code]))]) - :value @(re-frame/subscribe [::data-page/filter data-page :account])}]] + [search-backed-typeahead {:search-query (fn [i] + [:search_account + {:query i + :client-id (:id client)} + [:name :id]]) + :entity->text (fn [x ] (str (:numeric-code x) " - " (:name x))) + :type "typeahead-v3" + :on-change #(re-frame/dispatch [::data-page/filter-changed data-page :account (some-> % (select-keys [:name :id :numeric-code]))]) + :value @(re-frame/subscribe [::data-page/filter data-page :account])}]] [:p.menu-label "Vendor"] [:div diff --git a/src/cljs/auto_ap/views/pages/ledger/table.cljs b/src/cljs/auto_ap/views/pages/ledger/table.cljs index b0a4cc6c..f5da3366 100644 --- a/src/cljs/auto_ap/views/pages/ledger/table.cljs +++ b/src/cljs/auto_ap/views/pages/ledger/table.cljs @@ -16,8 +16,8 @@ [cemerick.url :as url] [re-frame.core :as re-frame])) -(defn ledger-row [{{:keys [client vendor alternate-description note status date amount id line-items source original-entity] :as i} :row - :keys [selected-client accounts-by-id bank-accounts-by-id]}] +(defn ledger-row [{{:keys [client vendor alternate-description note date amount id line-items source original-entity] :as i} :row + :keys [selected-client bank-accounts-by-id]}] [:<> [grid/row {:class (:class i) :id id} (when-not selected-client @@ -73,8 +73,8 @@ )]]]]])]] [:<> (for [{:keys [debit credit location account id running-balance]} line-items - :let [account (or (accounts-by-id (:id account)) - (bank-accounts-by-id (:id account)))]] + :let [account (or (bank-accounts-by-id (:id account)) + account)]] ^{:key id} [grid/row {} (when-not selected-client @@ -92,10 +92,9 @@ #_[grid/cell {:class "has-text-right"} (when running-balance (nf running-balance ))]])]]) -(defn table [{:keys [id data-page status vendors on-check-changed expense-event]}] - (let [{:keys [data status]} @(re-frame/subscribe [::data-page/page data-page]) +(defn table [{:keys [data-page]}] + (let [{:keys [data]} @(re-frame/subscribe [::data-page/page data-page]) selected-client @(re-frame/subscribe [::subs/client]) - accounts-by-id @(re-frame/subscribe [::subs/accounts-by-id selected-client]) bank-accounts-by-id @(re-frame/subscribe [::subs/bank-accounts-by-id])] [grid/grid {:data-page data-page :column-count (if selected-client 5 6)} @@ -113,10 +112,9 @@ [grid/header-cell {:style {:width (action-cell-width 1)}}] #_[grid/header-cell {:class "has-text-right" :style {:width "10em"}} "Running Balance"]]] [grid/body - (for [{:keys [client vendor status date amount id line-items] :as i} (:data data)] + (for [{:keys [id] :as i} (:data data)] ^{:key id} [ledger-row {:row i :selected-client selected-client - :accounts-by-id accounts-by-id :bank-accounts-by-id bank-accounts-by-id}])]]])) diff --git a/src/cljs/auto_ap/views/pages/transactions/form.cljs b/src/cljs/auto_ap/views/pages/transactions/form.cljs index 1d31071c..a6be06a2 100644 --- a/src/cljs/auto_ap/views/pages/transactions/form.cljs +++ b/src/cljs/auto_ap/views/pages/transactions/form.cljs @@ -56,8 +56,7 @@ (re-frame/reg-event-db ::editing (fn [db [_ which potential-payment-matches potential-autopay-invoices-matches potential-unpaid-invoices-matches potential-transaction-rule-matches]] - (let [locations @(re-frame/subscribe [::subs/locations-for-client (:id (:client which))]) - accounts-by-id @(re-frame/subscribe [::subs/accounts-by-id (:client which)])] + (let [locations @(re-frame/subscribe [::subs/locations-for-client (:id (:client which))])] (forms/start-form db ::form (-> which (select-keys [:vendor :amount :payment :client :description-original @@ -71,7 +70,7 @@ (assoc :potential-transaction-rule-matches potential-transaction-rule-matches) (assoc :potential-autopay-invoices-matches potential-autopay-invoices-matches) (assoc :potential-unpaid-invoices-matches potential-unpaid-invoices-matches) - (update :accounts expense-accounts-field/from-graphql accounts-by-id (:amount which) locations)))))) + (update :accounts expense-accounts-field/from-graphql (:amount which) locations)))))) (re-frame/reg-event-db ::changed diff --git a/src/cljs/auto_ap/views/pages/transactions/side_bar.cljs b/src/cljs/auto_ap/views/pages/transactions/side_bar.cljs index 9f0d1bd5..6698c649 100644 --- a/src/cljs/auto_ap/views/pages/transactions/side_bar.cljs +++ b/src/cljs/auto_ap/views/pages/transactions/side_bar.cljs @@ -7,22 +7,18 @@ [auto-ap.views.components.date-range-filter :refer [date-range-filter]] [auto-ap.views.components.number-filter :refer [number-filter]] [auto-ap.views.components.switch-field :refer [switch-field]] - [auto-ap.views.components.typeahead :refer [typeahead-v3]] [auto-ap.views.components.typeahead.vendor :refer [search-backed-typeahead]] [auto-ap.views.pages.data-page :as data-page] [auto-ap.views.utils - :refer [account->match-text active-when dispatch-event dispatch-value-change]] + :refer [active-when dispatch-event dispatch-value-change]] [bidi.bidi :as bidi] [re-frame.core :as re-frame])) (defn side-bar [{:keys [data-page]}] (let [ap @(re-frame/subscribe [::subs/active-page]) user @(re-frame/subscribe [::subs/user]) - - accounts @(re-frame/subscribe [::subs/accounts-by-id]) - account-index @(re-frame/subscribe [::subs/accounts-index]) - ] + client @(re-frame/subscribe [::subs/client])] [:div [:div [:p.menu-label "Type"] [:ul.menu-list @@ -68,12 +64,15 @@ [:p.menu-label "Financial Account"] [:div - [typeahead-v3 {:entities-by-id accounts - :entity-index account-index - :entity->text account->match-text - :type "typeahead-v3" - :on-change #(re-frame/dispatch [::data-page/filter-changed data-page :account (some-> % (select-keys [:name :id :numeric-code]))]) - :value @(re-frame/subscribe [::data-page/filter data-page :account])}]] + [search-backed-typeahead {:search-query (fn [i] + [:search_account + {:query i + :client-id (:id client)} + [:name :id]]) + :entity->text (fn [x ] (str (:numeric-code x) " - " (:name x))) + :type "typeahead-v3" + :on-change #(re-frame/dispatch [::data-page/filter-changed data-page :account (some-> % (select-keys [:name :id :numeric-code]))]) + :value @(re-frame/subscribe [::data-page/filter data-page :account])}]] [:p.menu-label "Vendor"] [:div From cae28589d2cb4aaf1bd6399207308cce36400f0d Mon Sep 17 00:00:00 2001 From: Bryce Covert Date: Tue, 19 Apr 2022 15:05:05 -0700 Subject: [PATCH 11/12] Makes accounts not loaded. --- src/clj/auto_ap/datomic/accounts.clj | 58 +++++++- src/clj/auto_ap/graphql.clj | 22 +-- src/clj/auto_ap/graphql/accounts.clj | 21 +-- src/cljs/auto_ap/events.cljs | 14 +- .../auto_ap/views/pages/admin/accounts.cljs | 125 ++++++------------ .../views/pages/admin/accounts/side_bar.cljs | 68 +--------- .../views/pages/admin/accounts/table.cljs | 36 +++++ 7 files changed, 169 insertions(+), 175 deletions(-) create mode 100644 src/cljs/auto_ap/views/pages/admin/accounts/table.cljs diff --git a/src/clj/auto_ap/datomic/accounts.clj b/src/clj/auto_ap/datomic/accounts.clj index a7372bad..4c47a8be 100644 --- a/src/clj/auto_ap/datomic/accounts.clj +++ b/src/clj/auto_ap/datomic/accounts.clj @@ -1,7 +1,11 @@ (ns auto-ap.datomic.accounts - (:require [datomic.api :as d] - [auto-ap.graphql.utils :refer [->graphql]] - [auto-ap.datomic :refer [uri merge-query conn]])) + (:require + [auto-ap.datomic + :refer [add-sorter-fields apply-pagination apply-sort-3 conn merge-query uri]] + [clojure.string :as str] + [datomic.api :as d] + [clojure.tools.logging :as log])) + (defn <-datomic [a] (update a :account/applicability :db/ident)) @@ -86,4 +90,50 @@ (map <-datomic) (first)))) -#_(get-account-by-numeric-code-and-sets 5110 nil) +(defn raw-graphql-ids [db args] + (println args) + (let [query (cond-> {:query {:find [] + :in ['$] + :where []} + :args [db]} + (:sort args) (add-sorter-fields {"name" ['[?e :account/name ?sort-name]]} + args) + + (:numeric-code args) + (merge-query {:query {:in ['?numeric-code] + :where ['[?e :account/numeric-code ?numeric-code]]} + :args [(:numeric-code args)]}) + + (not (str/blank? (:name-like args))) + (merge-query {:query {:in ['?name-like] + :where ['[?e :account/name ?n] + '[(re-find ?name-like ?n)]]} + :args [(re-pattern (str "(?i)" (:name-like args)))]}) + + true + (merge-query {:query {:find ['?sort-default '?e ] + :where ['[?e :account/name] + '[?e :account/numeric-code ?sort-default]]}}))] + + + (cond->> query + true (d/query) + true (apply-sort-3 args) + true (apply-pagination args)))) + + +(defn graphql-results [ids db args] + (let [results (->> (d/pull-many db default-read ids) + (group-by :db/id)) + accounts (->> ids + (map results) + (map first) + (map <-datomic))] + accounts)) + +(defn get-graphql [args] + (log/info "ARGS" args) + (let [db (d/db conn) + {ids-to-retrieve :ids matching-count :count} (raw-graphql-ids db args)] + [(->> (graphql-results ids-to-retrieve db args)) + matching-count])) diff --git a/src/clj/auto_ap/graphql.clj b/src/clj/auto_ap/graphql.clj index d07135c6..b11533ff 100644 --- a/src/clj/auto_ap/graphql.clj +++ b/src/clj/auto_ap/graphql.clj @@ -305,6 +305,12 @@ :start {:type 'Int} :end {:type 'Int}}} + :account_page {:fields {:accounts {:type '(list :account)} + :count {:type 'Int} + :total {:type 'Int} + :start {:type 'Int} + :end {:type 'Int}}} + :reminder_page {:fields {:reminders {:type '(list :reminder)} :count {:type 'Int} :total {:type 'Int} @@ -358,9 +364,13 @@ :args {:client_id {:type :id}} :resolve :get-yodlee-provider-account-page} - :accounts {:type '(list :account) - :args {:account_set {:type 'String}} - :resolve :get-accounts} + :account_page {:type :account_page + :args {:name_like {:type 'String} + :numeric_code {:type 'Int} + :start {:type 'Int} + :per_page {:type 'Int} + :sort {:type '(list :sort_item)}} + :resolve :get-accounts} :search_vendor {:type '(list :search_result) :args {:query {:type 'String}} @@ -409,10 +419,6 @@ :per_page {:type 'Int} :sort {:type '(list :sort_item)}} :resolve :get-sales-order-page} - - - - :vendor {:type :vendor_page :args {:name_like {:type 'String} :start {:type 'Int} @@ -803,7 +809,7 @@ (attach-resolvers { :get-yodlee-provider-account-page gq-yodlee2/get-yodlee-provider-account-page :get-all-sales-orders get-all-sales-orders - :get-accounts gq-accounts/get-accounts + :get-accounts gq-accounts/get-graphql :get-sales-order-page gq-sales-orders/get-sales-orders-page :get-transaction-rule-page gq-transaction-rules/get-transaction-rule-page :get-transaction-rule-matches gq-transaction-rules/get-transaction-rule-matches diff --git a/src/clj/auto_ap/graphql/accounts.clj b/src/clj/auto_ap/graphql/accounts.clj index 591598e8..d0d364d0 100644 --- a/src/clj/auto_ap/graphql/accounts.clj +++ b/src/clj/auto_ap/graphql/accounts.clj @@ -1,16 +1,21 @@ (ns auto-ap.graphql.accounts (:require - [auto-ap.datomic :refer [audit-transact remove-nils conn]] + [auto-ap.datomic :refer [audit-transact conn remove-nils]] [auto-ap.datomic.accounts :as d-accounts] [auto-ap.graphql.utils - :refer [->graphql <-graphql assert-can-see-client enum->keyword]] - [datomic.api :as d] - [clojure.tools.logging :as log])) - -(defn get-accounts [_ args _] - (->graphql (d-accounts/get-accounts (<-graphql args)))) - + :refer [->graphql + <-graphql + assert-admin + assert-can-see-client + enum->keyword + result->page]] + [datomic.api :as d])) +(defn get-graphql [context args _] + (assert-admin (:id context)) + (let [args (assoc args :id (:id context)) + [accounts accounts-count ] (d-accounts/get-graphql (<-graphql args))] + (result->page accounts accounts-count :accounts args))) (defn default-for-vendor [context args _] (assert-can-see-client (:id context) (:client_id args)) diff --git a/src/cljs/auto_ap/events.cljs b/src/cljs/auto_ap/events.cljs index 8ec5dd6d..f2b64ef0 100644 --- a/src/cljs/auto_ap/events.cljs +++ b/src/cljs/auto_ap/events.cljs @@ -62,11 +62,7 @@ :query-params (auto-ap.views.utils/query-params) :user token) :graphql {:token token - :query-obj {:venia/queries [[:client - - (client-query token)] - - [:accounts [:numeric-code :location :name :type :account_set :applicability :id [:client-overrides [:name :id [:client [:name :id]]]]]]]} + :query-obj {:venia/queries [[:client (client-query token)]]} :on-success [::received-initial] :on-error [::failed-initial]}})))) @@ -80,10 +76,7 @@ ::logged-in (fn [{:keys [db]} [_ token user]] {:graphql {:token token - :query-obj {:venia/queries [[:client - (client-query token)] - - [:accounts [:numeric-code :name :location :type :account_set :applicability :id [:client-overrides [:name [:client [:name :id]]]]]]]} + :query-obj {:venia/queries [[:client (client-query token)]]} :on-success [::received-initial] :on-error [::failed-initial]} @@ -93,12 +86,11 @@ (re-frame/reg-event-fx ::received-initial - (fn [{:keys [db]} [_ {accounts :accounts clients :client}]] + (fn [{:keys [db]} [_ {clients :client}]] {:db (-> db (assoc :clients (by :id clients) ) (assoc :is-initial-loading? false) - (assoc :accounts accounts ) (assoc :client (or (when (= 1 (count clients)) (->> clients first :id )) (->> clients (map :id) diff --git a/src/cljs/auto_ap/views/pages/admin/accounts.cljs b/src/cljs/auto_ap/views/pages/admin/accounts.cljs index 715869f4..4bc7af25 100644 --- a/src/cljs/auto_ap/views/pages/admin/accounts.cljs +++ b/src/cljs/auto_ap/views/pages/admin/accounts.cljs @@ -4,7 +4,8 @@ [auto-ap.utils :refer [replace-by]] [auto-ap.views.components.admin.side-bar :refer [admin-side-bar]] [auto-ap.views.pages.admin.accounts.side-bar :as side-bar] - [auto-ap.views.utils :refer [dispatch-event action-cell-width]] + [auto-ap.views.pages.admin.accounts.table :as table] + [auto-ap.views.utils :refer [dispatch-event action-cell-width with-user]] [auto-ap.views.components.layouts :refer [appearing-side-bar side-bar-layout]] @@ -15,96 +16,55 @@ [auto-ap.views.components.buttons :as buttons] [reagent.core :as reagent] [clojure.string :as str] - [vimsical.re-frame.fx.track :as track])) + [vimsical.re-frame.fx.track :as track] + [auto-ap.views.pages.data-page :as data-page] + [clojure.set :as set] + [auto-ap.effects.forward :as forward])) + +(def default-read [:numeric-code :name :location :type :account_set :applicability :id [:client-overrides [:name [:client [:name :id]]]]]) + -(re-frame/reg-event-db - ::edit-completed - (fn [db [_ edit-account]] - (-> db - (update :accounts replace-by :id (assoc edit-account :class "live-added"))))) -(re-frame/reg-event-db - ::table-params-changed - (fn [db [_ p]] - (assoc db ::table-params p))) (re-frame/reg-event-fx ::params-change - (fn [db [_ p]] - {:set-uri-params p})) - -(re-frame/reg-sub - ::table-params - (fn [db] - (-> db ::table-params))) - -(re-frame/reg-sub - ::params - :<- [::table-params] - :<- [::side-bar/filter-params] - (fn [[table-params filter-params]] - (cond-> {} - (seq filter-params) (merge filter-params) - (seq table-params) (merge table-params)))) + [with-user] + (fn [{:keys [user]} [_ params]] + {:set-uri-params params + :graphql {:token user + :owns-state {:single [::data-page/page ::page]} + :query-obj {:venia/queries [{:query/data [:account_page + {:sort (:sort params) + :start (:start params 0) + :per-page (:per-page params) + :name-like (:name-like params) + :numeric-code (some-> params :numeric-code not-empty js/parseInt)} + [[:accounts default-read] + :total + :start + :end]] + :query/alias :result}]} + :on-success (fn [result] + [::data-page/received ::page + (set/rename-keys (:result result) + {:accounts :data})])}})) (re-frame/reg-event-fx ::unmounted - (fn [{:keys [db]} _] - {:db (dissoc db ::table-params ::side-bar/filter-params) - ::track/dispose {:id ::params}})) + (fn [_ _] + {::track/dispose {:id ::params} + ::forward/dispose [{:id ::updated}]})) (re-frame/reg-event-fx ::mounted - (fn [{:keys [db]} _] + (fn [_ _] {::track/register {:id ::params - :subscription [::params] - :event-fn (fn [params] [::params-change params])}})) -(re-frame/reg-sub - ::account-page - :<- [::params] - :<- [::subs/all-accounts] - (fn [[params all-accounts]] - (let [matching-accounts (cond->> all-accounts - (:name-like params) (filter #(str/includes? (str/lower-case (or (:name %) "")) - (str/lower-case (:name-like params)))) - (not-empty (:code-like params)) (filter #(str/starts-with? (str (or (:numeric-code %) "")) - (str/lower-case (:code-like params)))))] - (assoc (grid/virtual-paginate-controls (:start params ) (:per-page params) matching-accounts) - :data (grid/virtual-paginate (:start params) - (:per-page params) - (sort-by :numeric-code matching-accounts)))))) - -(defn accounts-table [{:keys [accounts]}] - (let [status @(re-frame/subscribe [::status/single ::page]) - opc (fn [p] - (re-frame/dispatch [::table-params-changed p])) - params @(re-frame/subscribe [::params])] - - [:div - [grid/grid {:status status - :on-params-change opc - :params params - :column-count 5} - [grid/controls accounts] - [grid/table {:fullwidth true} - [grid/header - [grid/row {} - [grid/header-cell {} "Code"] - [grid/header-cell {} "Name"] - [grid/header-cell {} "Type"] - [grid/header-cell {} "Location"] - [grid/header-cell {:style {:width (action-cell-width 1)}} ]]] - [grid/body - (for [{:keys [id numeric-code name type location class] :as account} (:data accounts)] - ^{:key id} - [grid/row {:class (:class account) :id id} - [grid/cell {} numeric-code] - [grid/cell {} name] - [grid/cell {} type] - [grid/cell {} location] - [grid/cell {} - [buttons/fa-icon {:event [::account-form/editing account [::edit-completed]] - :icon "fa-pencil"}]]])]]]])) + :subscription [::data-page/params ::page] + :event-fn (fn [params] [::params-change params])} + ::forward/register [{:id ::updated + :events #{::account-form/edited} + :event-fn (fn [[_ edited-account]] + [::data-page/updated-entity ::page (:upsert-account edited-account)])}]})) (defn admin-accounts-content [] [:div @@ -114,9 +74,8 @@ :class "is-primary" :event [::account-form/editing {:type :asset - :account-set "default"} - [::edit-completed]]}]] - [accounts-table {:accounts @(re-frame/subscribe [::account-page])}]]) + :account-set "default"}]}]] + [table/accounts-table {:data-page ::page}]]) (defn admin-accounts-page [] (reagent/create-class @@ -127,6 +86,6 @@ (fn [] (let [{:keys [active?]} @(re-frame/subscribe [::forms/form ::account-form/form])] [side-bar-layout {:side-bar [admin-side-bar {} - [side-bar/accounts-side-bar]] + [side-bar/accounts-side-bar {:data-page ::page}]] :main [admin-accounts-content] :right-side-bar [appearing-side-bar {:visible? active?} [account-form/form ]]}]))})) diff --git a/src/cljs/auto_ap/views/pages/admin/accounts/side_bar.cljs b/src/cljs/auto_ap/views/pages/admin/accounts/side_bar.cljs index 6e8fee87..283c83a4 100644 --- a/src/cljs/auto_ap/views/pages/admin/accounts/side_bar.cljs +++ b/src/cljs/auto_ap/views/pages/admin/accounts/side_bar.cljs @@ -2,78 +2,24 @@ (:require [re-frame.core :as re-frame] [auto-ap.subs :as subs] - [auto-ap.views.utils :refer [active-when dispatch-value-change]])) + [auto-ap.views.utils :refer [dispatch-value-change]] + [auto-ap.views.pages.data-page :as data-page])) -(re-frame/reg-sub - ::specific-filters - (fn [db ] - (::filters db nil))) - -(re-frame/reg-sub - ::filters - :<- [::specific-filters] - :<- [::subs/query-params] - (fn [[specific-filters query-params] ] - (let [url-filters (-> query-params - (select-keys #{:name-like :code-like}) - (update :name-like #(some-> % str)) - (update :code-like #(some-> % str)))] - (merge url-filters specific-filters )))) - -(re-frame/reg-sub - ::filter - :<- [::filters] - (fn [filters [_ which]] - (get filters which))) - -(re-frame/reg-sub - ::settled-filters - (fn [db ] - (::settled-filters db))) - -(re-frame/reg-sub - ::filter-params - :<- [::settled-filters] - :<- [::filters] - :<- [::subs/active-page] - (fn [[settled-filters filters ap ]] - (let [filters (or settled-filters filters)] - {:name-like (:name-like filters) - :code-like (:code-like filters)}))) - -(re-frame/reg-event-fx - ::filters-settled - (fn [{:keys [db]} [_ & params]] - {:db (assoc db ::settled-filters @(re-frame/subscribe [::filters]))})) - -(re-frame/reg-event-fx - ::filter-changed - (fn [{:keys [db]} [_ & params]] - (let [[a b c] params - [which val] (if (= 3 (count params)) - [(into [a] b) c] - [[a] b])] - {:db (assoc-in db (into [::filters] which) val) - :dispatch-debounce {:event [::filters-settled] - :time 800 - :key ::filters}}))) - - -(defn accounts-side-bar [] +(defn accounts-side-bar [{:keys [data-page]}] [:div [:p.menu-label "Name"] [:div.field [:div.control [:input.input {:placeholder "Food Cost" - :value @(re-frame/subscribe [::filter :name-like]) - :on-change (dispatch-value-change [::filter-changed :name-like])} ]]] + :value @(re-frame/subscribe [::data-page/filter data-page :name-like]) + :on-change (dispatch-value-change [::data-page/filter-changed data-page :name-like])} ]]] [:p.menu-label "Code"] [:div.field [:div.control [:input.input {:placeholder "11000" - :value @(re-frame/subscribe [::filter :code-like]) - :on-change (dispatch-value-change [::filter-changed :code-like])}]]]]) + :value @(re-frame/subscribe [::data-page/filter data-page :numeric-code]) + :on-change (dispatch-value-change [::data-page/filter-changed data-page :numeric-code])}]]]]) diff --git a/src/cljs/auto_ap/views/pages/admin/accounts/table.cljs b/src/cljs/auto_ap/views/pages/admin/accounts/table.cljs new file mode 100644 index 00000000..c6a110a8 --- /dev/null +++ b/src/cljs/auto_ap/views/pages/admin/accounts/table.cljs @@ -0,0 +1,36 @@ +(ns auto-ap.views.pages.admin.accounts.table + (:require + [auto-ap.status :as status] + [auto-ap.views.components.buttons :as buttons] + [auto-ap.views.components.grid :as grid] + [auto-ap.views.pages.admin.accounts.form :as account-form] + [auto-ap.views.utils :refer [action-cell-width]] + [re-frame.core :as re-frame] + [auto-ap.views.pages.data-page :as data-page])) + +(defn accounts-table [{:keys [data-page]}] + (let [{:keys [data]} @(re-frame/subscribe [::data-page/page data-page])] + + [:div + [grid/grid {:data-page data-page + :column-count 5} + [grid/controls data] + [grid/table {:fullwidth true} + [grid/header + [grid/row {} + [grid/header-cell {} "Code"] + [grid/header-cell {} "Name"] + [grid/header-cell {} "Type"] + [grid/header-cell {} "Location"] + [grid/header-cell {:style {:width (action-cell-width 1)}} ]]] + [grid/body + (for [{:keys [id numeric-code name type location class] :as account} (:data data)] + ^{:key id} + [grid/row {:class (:class account) :id id} + [grid/cell {} numeric-code] + [grid/cell {} name] + [grid/cell {} type] + [grid/cell {} location] + [grid/cell {} + [buttons/fa-icon {:event [::account-form/editing account [::edit-completed]] + :icon "fa-pencil"}]]])]]]])) From 96a796522c905878964856003557e581c3febd00 Mon Sep 17 00:00:00 2001 From: Bryce Covert Date: Tue, 26 Apr 2022 17:05:58 -0700 Subject: [PATCH 12/12] fixes. --- src/clj/auto_ap/datomic/vendors.clj | 2 +- src/clj/auto_ap/graphql/checks.clj | 7 ++- src/clj/auto_ap/graphql/invoices.clj | 1 + src/clj/auto_ap/graphql/vendors.clj | 30 +++++++----- .../views/components/invoice_table.cljs | 3 +- .../auto_ap/views/components/layouts.cljs | 47 +++++++------------ .../auto_ap/integration/graphql/vendors.clj | 29 +----------- 7 files changed, 48 insertions(+), 71 deletions(-) diff --git a/src/clj/auto_ap/datomic/vendors.clj b/src/clj/auto_ap/datomic/vendors.clj index 4a8becd0..4c5a3462 100644 --- a/src/clj/auto_ap/datomic/vendors.clj +++ b/src/clj/auto_ap/datomic/vendors.clj @@ -140,7 +140,7 @@ (defn get-by-id [id] (->> (d/q '[:find (pull ?e [* - {:default-account [:account/name :db/id :account/location] + {:vendor/default-account [:account/name :db/id :account/location] :vendor/legal-entity-tin-type [:db/ident :db/id] :vendor/legal-entity-1099-type [:db/ident :db/id] :vendor/account-overrides [* {:vendor-account-override/client [:client/name :db/id] diff --git a/src/clj/auto_ap/graphql/checks.clj b/src/clj/auto_ap/graphql/checks.clj index 722c4373..b01e092d 100644 --- a/src/clj/auto_ap/graphql/checks.clj +++ b/src/clj/auto_ap/graphql/checks.clj @@ -366,7 +366,12 @@ (let [type (keyword "payment-type" (name type)) invoices (d-invoices/get-multi (map :invoice-id invoice-payments)) client (d-clients/get-by-id client-id) - vendors (by :db/id (d-vendors/get-graphql {})) + vendors (->> (d/q '[:find [?e ...] + :in $ + :where [?e :vendor/name]] + (d/db conn)) + (d/pull-many (d/db conn) d-vendors/default-read) + (by :db/id)) invoice-amounts (by :invoice-id :amount invoice-payments) invoices-grouped-by-vendor (group-by (comp :db/id :invoice/vendor) invoices) bank-account (d-bank-accounts/get-by-id bank-account-id) diff --git a/src/clj/auto_ap/graphql/invoices.clj b/src/clj/auto_ap/graphql/invoices.clj index 9bb01aa9..623107ec 100644 --- a/src/clj/auto_ap/graphql/invoices.clj +++ b/src/clj/auto_ap/graphql/invoices.clj @@ -307,6 +307,7 @@ (assert (:invoice/client invoice)) (assert-can-see-client (:id context) (:db/id (:invoice/client invoice))) (assert-not-locked (:db/id (:invoice/client invoice)) (:invoice/date invoice)) + (assert (not (seq (:invoice-payment/_invoice invoice)))) (audit-transact [[:db/add id :invoice/status :invoice-status/unpaid] [:db/add id :invoice/outstanding-balance (:invoice/total invoice)] [:db/retract id :invoice/scheduled-payment (:invoice/scheduled-payment invoice)]] diff --git a/src/clj/auto_ap/graphql/vendors.clj b/src/clj/auto_ap/graphql/vendors.clj index 82552575..8beaf6dd 100644 --- a/src/clj/auto_ap/graphql/vendors.clj +++ b/src/clj/auto_ap/graphql/vendors.clj @@ -147,14 +147,22 @@ (d-vendors/get-graphql-by-id (assoc args :id (:id context)) (:id args)))) -(defn search [_ args _] - (->> (d/q '[:find ?n ?i ?s - :in $ ?q - :where [(fulltext $ :vendor/search-terms ?q) [[?i ?n _ ?s]]] - (not [?i :vendor/hidden true])] - (d/db conn) - (:query args)) - (sort-by last) - (map (fn [[n i]] - {:name n - :id i})))) +(defn search [context args _] + (let [data (if (is-admin? (:id context)) + (d/q '[:find ?n ?i ?s + :in $ ?q + :where [(fulltext $ :vendor/search-terms ?q) [[?i ?n _ ?s]]]] + (d/db conn) + (:query args)) + (d/q '[:find ?n ?i ?s + :in $ ?q + :where [(fulltext $ :vendor/search-terms ?q) [[?i ?n _ ?s]]] + (not [?i :vendor/hidden true])] + (d/db conn) + (:query args)))] + + (->> data + (sort-by last) + (map (fn [[n i]] + {:name n + :id i}))))) diff --git a/src/cljs/auto_ap/views/components/invoice_table.cljs b/src/cljs/auto_ap/views/components/invoice_table.cljs index af11e4ee..67b57ad8 100644 --- a/src/cljs/auto_ap/views/components/invoice_table.cljs +++ b/src/cljs/auto_ap/views/components/invoice_table.cljs @@ -228,7 +228,8 @@ :event [::unvoid-invoice i]}]) (when (and (get actions :void) (= ":paid" (:status i)) - (:scheduled-payment i)) + (:scheduled-payment i) + (not (seq (:payments i)))) [buttons/fa-icon {:icon "fa-undo" :class (status/class-for (get unautopay-states (:id i))) :event [::unautopay i]}])]]])) diff --git a/src/cljs/auto_ap/views/components/layouts.cljs b/src/cljs/auto_ap/views/components/layouts.cljs index 1f35c153..367307de 100644 --- a/src/cljs/auto_ap/views/components/layouts.cljs +++ b/src/cljs/auto_ap/views/components/layouts.cljs @@ -22,30 +22,30 @@ children)}))) (defn navbar-drop-down [{:keys [ header id class]} child] - (let [menu-active? (re-frame/subscribe [::subs/menu-active? id])] - (r/create-class - {:reagent-render (fn [{:keys [header id]} child] - (let [menu-active? @(re-frame/subscribe [::subs/menu-active? id])] - [:div { :class (str "navbar-item has-dropdown " (when menu-active? "is-active " ) " " class)} - [:a {:class "navbar-link login" :on-click (fn [e] - (.preventDefault e) - (.stopPropagation e) - (re-frame/dispatch [::events/toggle-menu id]) - true)} header] - [appearing {:visible? menu-active? :enter-class "appear" :exit-class "disappear" :timeout 200} - [:div {:class "navbar-dropdown"} - [navbar-drop-down-contents {:id id} - [:div child]]]]]))}))) + (r/create-class + {:reagent-render (fn [{:keys [header id]} child] + (let [menu-active? @(re-frame/subscribe [::subs/menu-active? id])] + [:div { :class (str "navbar-item has-dropdown " (when menu-active? "is-active " ) " " class)} + [:a {:class "navbar-link login" :on-click (fn [e] + (.preventDefault e) + (.stopPropagation e) + (re-frame/dispatch [::events/toggle-menu id]) + true)} header] + [appearing {:visible? menu-active? :enter-class "appear" :exit-class "disappear" :timeout 200} + [:div {:class "navbar-dropdown"} + [navbar-drop-down-contents {:id id} + [:div child]]]]]))})) (defn login-dropdown [] - (let [user (re-frame/subscribe [::subs/user]) - menu (re-frame/subscribe [::subs/menu])] + (let [user (re-frame/subscribe [::subs/user])] (if @user [navbar-drop-down {:header [:span [:span.icon [:i.fa.fa-user] ] [:span (:user/name @user)]] :id ::account} [:div [:a {:class "navbar-item" :href (bidi/path-for routes/routes :reports)} "My company"] + [:a.dropdown-item {:on-click (dispatch-event-with-propagation [::vendor-dialog/started {}])} "New Vendor"] + [:a.dropdown-item {:on-click (dispatch-event-with-propagation [::vendor-dialog/edit {}])} "Edit Vendor"] (when (= "admin" (:user/role @user)) [:a {:class "navbar-item" :href (bidi/path-for routes/routes :admin)} "Administration"]) [:hr {:class "navbar-divider"}] @@ -131,18 +131,6 @@ (when-not is-initial-loading [:div.navbar-end - [:div.navbar-item - [drop-down {:header [:a.button.is-outlined {:aria-haspopup true - :type "button" - :on-click (dispatch-event-with-propagation [::events/toggle-menu ::vendor ])} - "Vendors " - [:span " "] - [:span.icon [:i.fa.fa-angle-down {:aria-hidden "true"}]]] - :id ::vendor} - [:<> - [:a.dropdown-item {:on-click (dispatch-event-with-propagation [::vendor-dialog/started {}])} "New"] - [:a.dropdown-item {:on-click (dispatch-event-with-propagation [::vendor-dialog/edit {}])} "Edit"]]]] - (when (> (count @clients) 1) [navbar-drop-down {:header (str "Company: " (if @client (:name @client) @@ -193,8 +181,7 @@ (defn side-bar-layout [{:keys [side-bar main ap bottom right-side-bar]}] (let [ap @(re-frame/subscribe [::subs/active-page]) - client @(re-frame/subscribe [::subs/client]) - is-initial-loading @(re-frame/subscribe [::subs/is-initial-loading?])] + client @(re-frame/subscribe [::subs/client])] [:div [modal/global-modal] [navbar ap] diff --git a/test/clj/auto_ap/integration/graphql/vendors.clj b/test/clj/auto_ap/integration/graphql/vendors.clj index 204da51f..5dfb48db 100644 --- a/test/clj/auto_ap/integration/graphql/vendors.clj +++ b/test/clj/auto_ap/integration/graphql/vendors.clj @@ -48,34 +48,9 @@ {:db/id "client" :client/code "DEF"}]))] (testing "it should find vendors" - (let [result (sut2/get-graphql {} {} {})] - (is (= 1 (count result))))) - - (testing "It should count invoice usages for each client" - @(d/transact (d/connect uri) - [{:invoice/client client - :invoice/invoice-number "123" - :invoice/vendor vendor}]) - - (let [result (sut2/get-graphql {:id (admin-token)} {} {})] - (is (= [{:client_id client - :count 1}] (-> result first :usage))))) + (is ((into #{} (map :id (:vendors result))) vendor )))) - (testing "It should count transaction usages for each client" - @(d/transact (d/connect uri) - [{:transaction/client client - :transaction/vendor vendor}]) - (let [result (sut2/get-graphql {:id (admin-token)} {} {})] - (is (= [{:client_id client - :count 2}] (-> result first :usage))))) - - (testing "It should limit usages to visible clients" - (let [result (sut2/get-graphql {:id (user-token client)} {} {})] - (is (= [{:client_id client - :count 2}] (-> result first :usage)))) - - (let [result (sut2/get-graphql {:id (user-token 0)} {} {})] - (is (= nil (-> result first :usage)))))))) + )))