diff --git a/src/cljs/auto_ap/forms.cljs b/src/cljs/auto_ap/forms.cljs index 3021d984..68037c56 100644 --- a/src/cljs/auto_ap/forms.cljs +++ b/src/cljs/auto_ap/forms.cljs @@ -50,6 +50,7 @@ (re-frame/reg-event-db ::change (fn [db [_ form & path-pairs]] + (println "CHANGING" form path-pairs) (reduce (fn [db [path value]] (assoc-in db (into [::forms form :data] path) value)) diff --git a/src/cljs/auto_ap/views/components/expense_account_field.cljs b/src/cljs/auto_ap/views/components/expense_account_field.cljs new file mode 100644 index 00000000..8bc730c4 --- /dev/null +++ b/src/cljs/auto_ap/views/components/expense_account_field.cljs @@ -0,0 +1,108 @@ +(ns auto-ap.views.components.expense-account-field + (:require [auto-ap.forms :as forms] + [auto-ap.subs :as subs] + [auto-ap.views.components.typeahead :refer [typeahead]] + [auto-ap.views.utils :refer [bind-field dispatch-event]] + [goog.string :as gstring] + [re-frame.core :as re-frame])) + + +;; EVENTS + +(re-frame/reg-event-fx + ::add-expense-account + (fn [_ [_ event expense-accounts]] + {:dispatch (conj event (conj expense-accounts + {:amount 0 :id (str "new-" (random-uuid))}))})) + +(re-frame/reg-event-fx + ::remove-expense-account + (fn [_ [_ event expense-accounts id]] + {:dispatch (conj event (transduce (filter + (fn [ea] + (not= (:id ea) id)) ) + conj + [] + expense-accounts))})) + +(re-frame/reg-event-fx + ::expense-account-changed + (fn [_ [_ event expense-accounts field value]] + {:dispatch (into event [(assoc-in expense-accounts field value) + (if (= (list :account :id) (drop 1 field)) + (if-let [location (:location @(re-frame/subscribe [::subs/account value]))] + [[(first field) :location] location]))])})) + + +;; VIEWS +(defn expense-accounts-field [{expense-accounts :value max-value :max locations :locations event :event}] + (let [chooseable-expense-accounts @(re-frame/subscribe [::subs/chooseable-expense-accounts]) + accounts-by-id @(re-frame/subscribe [::subs/accounts-for-client-by-id])] + [:div + [:div.columns + [:div.column + [:h1.subtitle.is-4.is-inline "Expense Accounts"]] + [:div.column.is-narrow + [:p.buttons + [:a.button {:on-click (dispatch-event [::add-expense-account event expense-accounts])} "Add"]]]] + + (for [[index {:keys [account id location amount] :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 (if account + (str (:name account) " - " + location ": " + (gstring/format "$%.2f" (or amount 0) )) + [:i "New expense account"])]] + [:div.column.is-narrow + [:a.button {:on-click (dispatch-event [::remove-expense-account event expense-accounts id])} [:span.icon [:i.fa.fa-times]]]]] + [:div.field + [:div.columns + [:div.column + [:p.help "Expense Account"] + [:div.control.is-fullwidth + [bind-field + [typeahead {:matches (map (fn [x] [(:id x) (str (:numeric-code x) " - " (:name x))]) chooseable-expense-accounts) + :type "typeahead" + :field [index :account :id] + :event [::expense-account-changed event expense-accounts] + :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 (if (:location account) + "disabled" + "") + :style {:width "5em"} + :field [index :location] + :allow-nil? true + :spec (set locations) + :event [::expense-account-changed event expense-accounts] + :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 [:a.button.is-static "$"]] + [:p.control + [bind-field + [:input.input {:type "number" + :field [index :amount] + :style {:text-align "right" :width "7em"} + :event [::expense-account-changed event expense-accounts] + :subscription expense-accounts + :value (get-in expense-account [:amount]) + :max max-value + :step "0.01"}]]]]]]])])) diff --git a/src/cljs/auto_ap/views/pages/invoices/form.cljs b/src/cljs/auto_ap/views/pages/invoices/form.cljs index 39909ce2..7c2e672b 100644 --- a/src/cljs/auto_ap/views/pages/invoices/form.cljs +++ b/src/cljs/auto_ap/views/pages/invoices/form.cljs @@ -1,11 +1,12 @@ (ns auto-ap.views.pages.invoices.form (:require [auto-ap.entities.invoice :as invoice] - [auto-ap.utils :refer [by]] + [auto-ap.utils :refer [by dollars=]] [auto-ap.events :as events] [auto-ap.forms :as forms] [auto-ap.subs :as subs] [auto-ap.views.components.dropdown :refer [drop-down]] [auto-ap.views.components.typeahead :refer [typeahead]] + [auto-ap.views.components.expense-account-field :refer [expense-accounts-field]] [auto-ap.views.pages.invoices.common :refer [invoice-read]] [auto-ap.views.utils :refer @@ -21,8 +22,9 @@ (re-frame/reg-sub ::can-submit-edit-invoice - :<- [::form] + :<- [::forms/form ::form] (fn [{:keys [data status]} _] + (println (s/explain-data ::invoice/invoice data)) (let [min-total (if (= (:total (:original data)) (:outstanding-balance (:original data))) nil (- (:total (:original data)) (:outstanding-balance (:original data))))] @@ -30,7 +32,7 @@ (s/valid? ::invoice/invoice data) (or (not min-total) (>= (:total data) min-total)) (or (not (:id data)) - (< (.abs js/Math (- (js/parseFloat (:total data)) (reduce + 0 (map (fn [ea] (js/parseFloat (:amount ea))) (:expense-accounts data))))) 0.001)))))) + (dollars= (js/parseFloat (:total data)) (reduce + 0 (map (fn [ea] (js/parseFloat (:amount ea))) (:expense-accounts data))))))))) (defmulti submit-query (fn [_ [_ command]] command)) @@ -134,33 +136,7 @@ [:client-id] value [:location] first-location]}))) -(re-frame/reg-event-fx - ::change-expense-account-account - (fn [{:keys [db]} [_ [_ which _] value]] - - (let [account @(re-frame/subscribe [::subs/account value]) - changes (cond-> [[:expense-accounts which :account :id] value] - (:location account) (into [[:expense-accounts which :location] (:location account)]))] - {:dispatch (into [::forms/change ::form] changes)}))) - -(re-frame/reg-event-db - ::add-expense-account - [(forms/in-form ::form) (re-frame/path [:data])] - (fn [form] - (update form :expense-accounts conj {:amount 0 :id (str "new-" (random-uuid))}))) - -(re-frame/reg-event-db - ::remove-expense-account - [(forms/in-form ::form) (re-frame/path [:data])] - (fn [form [_ x]] - (update form :expense-accounts (fn [eas] - (transduce (filter - (fn [ea] - (not= (:id ea) x)) ) - conj - [] - eas))))) (re-frame/reg-event-fx ::submitted @@ -199,6 +175,7 @@ ;; VIEWS + (defn form [{:keys [can-change-amount?] :as params}] [forms/side-bar-form {:form ::form } (let [{:keys [data active? error id]} @(re-frame/subscribe [::forms/form ::form]) @@ -308,74 +285,15 @@ :spec ::invoice/total :step "0.01"}]]]]]] - [:div - [:div.columns - [:div.column - [:h1.subtitle.is-4.is-inline "Expense Accounts"]] - [:div.column.is-narrow - [:p.buttons - [:a.button {:on-click (dispatch-event [::add-expense-account])} "Add"]]]] + [bind-field + [expense-accounts-field {:subscription data + :type "expense-accounts" + :event change-event + :locations locations + :max-value (:total data) + :field [:expense-accounts]}]] - (for [[index {:keys [account id location amount] :as expense-account}] (map vector (range) (:expense-accounts data)) - :let [account (accounts-by-id (:id account))]] - ^{:key id} - [:div.box - [:div.columns - [:div.column - [:h1.subtitle.is-6 (if account - (str (:name account) " - " - location ": " - (gstring/format "$%.2f" (or amount 0) )) - [:i "New expense account"])]] - [:div.column.is-narrow - [:a.button {:on-click (dispatch-event [::remove-expense-account id])} [:span.icon [:i.fa.fa-times]]]]] - [:div.field - [:div.columns - [:div.column - [:p.help "Expense Account"] - [:div.control.is-fullwidth - [bind-field - [typeahead {:matches (map (fn [x] [(:id x) (str (:numeric-code x) " - " (:name x))]) chooseable-expense-accounts) - :type "typeahead" - :field [:expense-accounts index :account :id] - :event [::change-expense-account-account] - :subscription data}]]]] - [: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 (if (:location account) - "disabled" - "") - :style {:width "5em"} - :field [:expense-accounts index :location] - :allow-nil? true - :spec (set locations) - :event [::forms/change ::form] - :subscription data} - (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 [:a.button.is-static "$"]] - [:p.control - [bind-field - [:input.input {:type "number" - :field [:expense-accounts index :amount] - :style {:text-align "right" :width "7em"} - :event [::forms/change ::form] - :subscription data - :value (get-in expense-account [:amount]) - :max (:total data) - :step "0.01"}]]]]]]])] + (when error ^{:key error} [:div.notification.is-warning.animated.fadeInUp diff --git a/src/cljs/auto_ap/views/utils.cljs b/src/cljs/auto_ap/views/utils.cljs index 05da273e..c17d94dd 100644 --- a/src/cljs/auto_ap/views/utils.cljs +++ b/src/cljs/auto_ap/views/utils.cljs @@ -140,6 +140,18 @@ keys (dissoc keys :field :subscription :event :spec)] (into [dom keys] (with-keys rest)))) +(defmethod do-bind "expense-accounts" [dom {:keys [field event subscription class spec] :as keys} & rest] + (let [field (if (keyword? field) [field] field) + event (if (keyword? event) [event] event) + keys (assoc keys + :value (get-in subscription field) + :event (conj event field) + :class (str class + (when (and spec (not (s/valid? spec (get-in subscription field)))) + " is-danger"))) + keys (dissoc keys :field :subscription :spec)] + (into [dom keys] (with-keys rest)))) + (defmethod do-bind :default [dom {:keys [field event subscription class spec] :as keys} & rest] (let [field (if (keyword? field) [field] field) event (if (keyword? event) [event] event)