percentage-based splitting.
This commit is contained in:
@@ -128,8 +128,8 @@
|
|||||||
:requires [:auto-ap/convert-invoices]}
|
:requires [:auto-ap/convert-invoices]}
|
||||||
:auto-ap/add-yodlee-merchant2 {:txes add-general-ledger/add-yodlee-merchant :requires [:auto-ap/convert-transactions]}
|
:auto-ap/add-yodlee-merchant2 {:txes add-general-ledger/add-yodlee-merchant :requires [:auto-ap/convert-transactions]}
|
||||||
|
|
||||||
:auto-ap/bulk-load-invoice-ledger3 {:txes-fn `add-general-ledger/bulk-load-invoice-ledger :requires [:auto-ap/convert-transactions]}
|
#_#_:auto-ap/bulk-load-invoice-ledger3 {:txes-fn `add-general-ledger/bulk-load-invoice-ledger :requires [:auto-ap/convert-transactions]}
|
||||||
:auto-ap/bulk-load-transaction-ledger3 {:txes-fn `add-general-ledger/bulk-load-transaction-ledger :requires [:auto-ap/convert-transactions]}
|
#_#_:auto-ap/bulk-load-transaction-ledger3 {:txes-fn `add-general-ledger/bulk-load-transaction-ledger :requires [:auto-ap/convert-transactions]}
|
||||||
|
|
||||||
}]
|
}]
|
||||||
(println "Conforming database...")
|
(println "Conforming database...")
|
||||||
|
|||||||
@@ -12,5 +12,6 @@
|
|||||||
(s/def ::required-identifier (s/and string?
|
(s/def ::required-identifier (s/and string?
|
||||||
#(not (str/blank? %))))
|
#(not (str/blank? %))))
|
||||||
|
|
||||||
(s/def ::money (s/and string?
|
(s/def ::money (s/or :string (s/and string?
|
||||||
#(re-matches money-regex %)))
|
#(re-matches money-regex %))
|
||||||
|
:float float?))
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
(:require [auto-ap.forms :as forms]
|
(:require [auto-ap.forms :as forms]
|
||||||
[auto-ap.subs :as subs]
|
[auto-ap.subs :as subs]
|
||||||
[auto-ap.views.components.typeahead :refer [typeahead]]
|
[auto-ap.views.components.typeahead :refer [typeahead]]
|
||||||
[auto-ap.views.utils :refer [bind-field dispatch-event]]
|
[auto-ap.views.utils :refer [bind-field dispatch-event ->$]]
|
||||||
[goog.string :as gstring]
|
[goog.string :as gstring]
|
||||||
[re-frame.core :as re-frame]
|
[re-frame.core :as re-frame]
|
||||||
[clojure.string :as str]))
|
[clojure.string :as str]))
|
||||||
@@ -14,7 +14,9 @@
|
|||||||
::add-expense-account
|
::add-expense-account
|
||||||
(fn [_ [_ event expense-accounts]]
|
(fn [_ [_ event expense-accounts]]
|
||||||
{:dispatch (conj event (conj expense-accounts
|
{:dispatch (conj event (conj expense-accounts
|
||||||
{:amount 0 :id (str "new-" (random-uuid))}))}))
|
{:amount 0 :id (str "new-" (random-uuid))
|
||||||
|
:amount-mode "%"
|
||||||
|
:amount-percentage 0}))}))
|
||||||
|
|
||||||
(re-frame/reg-event-fx
|
(re-frame/reg-event-fx
|
||||||
::remove-expense-account
|
::remove-expense-account
|
||||||
@@ -26,13 +28,30 @@
|
|||||||
[]
|
[]
|
||||||
expense-accounts))}))
|
expense-accounts))}))
|
||||||
|
|
||||||
|
(defn recalculate-amounts [expense-accounts total]
|
||||||
|
(mapv
|
||||||
|
(fn [ea]
|
||||||
|
(assoc ea :amount
|
||||||
|
(js/parseFloat
|
||||||
|
(goog.string/format "%.2f"
|
||||||
|
(* (/ (js/parseFloat (:amount-percentage ea)) 100.0) (js/parseFloat total))))))
|
||||||
|
expense-accounts))
|
||||||
|
|
||||||
(re-frame/reg-event-fx
|
(re-frame/reg-event-fx
|
||||||
::expense-account-changed
|
::expense-account-changed
|
||||||
(fn [_ [_ event expense-accounts field value]]
|
(fn [_ [_ event expense-accounts max-value field value]]
|
||||||
(let [updated-accounts (cond-> expense-accounts
|
(let [updated-accounts (cond-> expense-accounts
|
||||||
true (assoc-in field value)
|
true (assoc-in field value)
|
||||||
(= (list :account :id) (drop 1 field)) (assoc-in [(first field) :account] @(re-frame/subscribe [::subs/account value]))
|
(= (list :account :id) (drop 1 field)) (assoc-in [(first field) :account] @(re-frame/subscribe [::subs/account value]))
|
||||||
)
|
(= (list :amount-percentage) (drop 1 field)) (assoc-in [(first field) :amount]
|
||||||
|
(js/parseFloat
|
||||||
|
(goog.string/format "%.2f"
|
||||||
|
(do
|
||||||
|
(println value max-value)
|
||||||
|
(* (/ (cond-> value
|
||||||
|
(not (float? value)) (js/parseFloat )) 100.0)
|
||||||
|
(cond-> max-value
|
||||||
|
(not (float? max-value)) (js/parseFloat))))))))
|
||||||
updated-accounts (if-let [location (get-in updated-accounts [(first field) :account :location])]
|
updated-accounts (if-let [location (get-in updated-accounts [(first field) :account :location])]
|
||||||
(assoc-in updated-accounts [(first field) :location] location)
|
(assoc-in updated-accounts [(first field) :location] location)
|
||||||
updated-accounts)]
|
updated-accounts)]
|
||||||
@@ -43,16 +62,18 @@
|
|||||||
(defn expense-accounts-field [{expense-accounts :value max-value :max locations :locations event :event descriptor :descriptor}]
|
(defn expense-accounts-field [{expense-accounts :value max-value :max locations :locations event :event descriptor :descriptor}]
|
||||||
(let [chooseable-expense-accounts @(re-frame/subscribe [::subs/chooseable-expense-accounts])
|
(let [chooseable-expense-accounts @(re-frame/subscribe [::subs/chooseable-expense-accounts])
|
||||||
accounts-by-id @(re-frame/subscribe [::subs/accounts-for-client-by-id])]
|
accounts-by-id @(re-frame/subscribe [::subs/accounts-for-client-by-id])]
|
||||||
|
(println expense-accounts)
|
||||||
|
|
||||||
[:div
|
[:div
|
||||||
[:div.columns
|
[:div.columns
|
||||||
[:div.column
|
[:div.column
|
||||||
[:h1.subtitle.is-4.is-inline (str/capitalize descriptor) "s"]]
|
[:h1.subtitle.is-4.is-inline (str/capitalize descriptor) "s"]
|
||||||
|
[:p.help "Remaining " (->$ (- max-value (reduce + 0 (map (comp js/parseFloat :amount) expense-accounts))))]]
|
||||||
[:div.column.is-narrow
|
[:div.column.is-narrow
|
||||||
[:p.buttons
|
[:p.buttons
|
||||||
[:a.button {:on-click (dispatch-event [::add-expense-account event expense-accounts])} "Add"]]]]
|
[: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)
|
(for [[index {:keys [account id location amount amount-mode] :as expense-account}] (map vector (range) expense-accounts)
|
||||||
:let [account (accounts-by-id (:id account))]]
|
:let [account (accounts-by-id (:id account))]]
|
||||||
^{:key id}
|
^{:key id}
|
||||||
[:div.box
|
[:div.box
|
||||||
@@ -74,7 +95,9 @@
|
|||||||
[typeahead {:matches (map (fn [x] [(:id x) (str (:numeric-code x) " - " (:name x))]) chooseable-expense-accounts)
|
[typeahead {:matches (map (fn [x] [(:id x) (str (:numeric-code x) " - " (:name x))]) chooseable-expense-accounts)
|
||||||
:type "typeahead"
|
:type "typeahead"
|
||||||
:field [index :account :id]
|
:field [index :account :id]
|
||||||
:event [::expense-account-changed event expense-accounts]
|
#_#_:text-field [index :account :name]
|
||||||
|
|
||||||
|
:event [::expense-account-changed event expense-accounts max-value]
|
||||||
:subscription expense-accounts}]]]]
|
:subscription expense-accounts}]]]]
|
||||||
[:div.column.is-narrow
|
[:div.column.is-narrow
|
||||||
[:p.help "Location"]
|
[:p.help "Location"]
|
||||||
@@ -92,7 +115,7 @@
|
|||||||
:field [index :location]
|
:field [index :location]
|
||||||
:allow-nil? true
|
:allow-nil? true
|
||||||
:spec (set locations)
|
:spec (set locations)
|
||||||
:event [::expense-account-changed event expense-accounts]
|
:event [::expense-account-changed event expense-accounts max-value]
|
||||||
:subscription expense-accounts}
|
:subscription expense-accounts}
|
||||||
(map (fn [l] ^{:key l} [:option {:value l} l]) locations)]]])]]]]
|
(map (fn [l] ^{:key l} [:option {:value l} l]) locations)]]])]]]]
|
||||||
|
|
||||||
@@ -101,14 +124,34 @@
|
|||||||
[:p.help "Amount"]
|
[:p.help "Amount"]
|
||||||
[:div.control
|
[:div.control
|
||||||
[:div.field.has-addons.is-extended
|
[:div.field.has-addons.is-extended
|
||||||
[:p.control [:a.button.is-static "$"]]
|
[:p.control [:span.select
|
||||||
|
[bind-field
|
||||||
|
[:select {:type "select"
|
||||||
|
:field [index :amount-mode]
|
||||||
|
:allow-nil? false
|
||||||
|
:event [::expense-account-changed event expense-accounts max-value]
|
||||||
|
:subscription expense-accounts}
|
||||||
|
[:option "$"]
|
||||||
|
[:option "%"]]]]]
|
||||||
[:p.control
|
[:p.control
|
||||||
|
(if (= "$" amount-mode)
|
||||||
[bind-field
|
[bind-field
|
||||||
[:input.input {:type "number"
|
[:input.input {:type "number"
|
||||||
:field [index :amount]
|
:field [index :amount]
|
||||||
:style {:text-align "right" :width "7em"}
|
:style {:text-align "right" :width "7em"}
|
||||||
:event [::expense-account-changed event expense-accounts]
|
:event [::expense-account-changed event expense-accounts max-value]
|
||||||
:subscription expense-accounts
|
:subscription expense-accounts
|
||||||
|
:precision 2
|
||||||
:value (get-in expense-account [:amount])
|
:value (get-in expense-account [:amount])
|
||||||
:max max-value
|
:max max-value
|
||||||
:step "0.01"}]]]]]]])]))
|
:step "0.01"}]]
|
||||||
|
[bind-field
|
||||||
|
[:input.input {:type "number"
|
||||||
|
:field [index :amount-percentage]
|
||||||
|
:style {:text-align "right" :width "7em"}
|
||||||
|
: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"}]])]]]]])]))
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
[auto-ap.subs :as subs]
|
[auto-ap.subs :as subs]
|
||||||
[auto-ap.views.components.dropdown :refer [drop-down]]
|
[auto-ap.views.components.dropdown :refer [drop-down]]
|
||||||
[auto-ap.views.components.typeahead :refer [typeahead]]
|
[auto-ap.views.components.typeahead :refer [typeahead]]
|
||||||
[auto-ap.views.components.expense-accounts-field :refer [expense-accounts-field]]
|
[auto-ap.views.components.expense-accounts-field :refer [expense-accounts-field recalculate-amounts]]
|
||||||
[auto-ap.views.pages.invoices.common :refer [invoice-read]]
|
[auto-ap.views.pages.invoices.common :refer [invoice-read]]
|
||||||
[auto-ap.views.utils
|
[auto-ap.views.utils
|
||||||
:refer
|
:refer
|
||||||
@@ -104,7 +104,10 @@
|
|||||||
(re-frame/reg-event-db
|
(re-frame/reg-event-db
|
||||||
::adding
|
::adding
|
||||||
(fn [db [_ new]]
|
(fn [db [_ new]]
|
||||||
(-> db (forms/start-form ::form (assoc new :expense-accounts [{:amount 0 :id (str "new-" (random-uuid))}])))))
|
(-> db (forms/start-form ::form (assoc new :expense-accounts [{:amount 0
|
||||||
|
:id (str "new-" (random-uuid))
|
||||||
|
:amount-percentage 100
|
||||||
|
:amount-mode "%"}])))))
|
||||||
|
|
||||||
(re-frame/reg-event-db
|
(re-frame/reg-event-db
|
||||||
::editing
|
::editing
|
||||||
@@ -121,7 +124,19 @@
|
|||||||
:vendor-id (:id (:vendor edit-invoice))
|
:vendor-id (:id (:vendor edit-invoice))
|
||||||
:vendor-name (:name (:vendor edit-invoice))
|
:vendor-name (:name (:vendor edit-invoice))
|
||||||
:client-id (:id (:client edit-invoice))
|
:client-id (:id (:client edit-invoice))
|
||||||
:expense-accounts (:expense-accounts edit-invoice)
|
:expense-accounts (if (seq (:expense-accounts which))
|
||||||
|
(vec (map
|
||||||
|
(fn [a]
|
||||||
|
(-> a
|
||||||
|
(update :amount #(js/parseFloat %))
|
||||||
|
(assoc :amount-percentage (* 100 (/ (js/parseFloat (:amount a))
|
||||||
|
(Math/abs (js/parseFloat (:total which))))))
|
||||||
|
(assoc :amount-mode "%")))
|
||||||
|
(:expense-accounts edit-invoice)))
|
||||||
|
[{:id (str "new-" (random-uuid))
|
||||||
|
:amount-mode "$"
|
||||||
|
:amount (Math/abs (:total edit-invoice))
|
||||||
|
:amount-percentage 100}])
|
||||||
:client-name (:name (:client edit-invoice))})))))
|
:client-name (:name (:client edit-invoice))})))))
|
||||||
|
|
||||||
|
|
||||||
@@ -135,6 +150,15 @@
|
|||||||
[:client-id] value
|
[:client-id] value
|
||||||
[:location] first-location]})))
|
[:location] first-location]})))
|
||||||
|
|
||||||
|
(re-frame/reg-event-fx
|
||||||
|
::change-amount
|
||||||
|
[(forms/in-form ::form)]
|
||||||
|
(fn [{{:keys [data]} :db} [_ field value]]
|
||||||
|
(print field value (:expense-accounts data))
|
||||||
|
{:dispatch [::forms/change ::form
|
||||||
|
field value
|
||||||
|
[:expense-accounts] (recalculate-amounts (:expense-accounts data) value)]}))
|
||||||
|
|
||||||
(re-frame/reg-event-fx
|
(re-frame/reg-event-fx
|
||||||
::change-vendor
|
::change-vendor
|
||||||
[(forms/in-form ::form)]
|
[(forms/in-form ::form)]
|
||||||
@@ -148,6 +172,8 @@
|
|||||||
field value
|
field value
|
||||||
[:expense-accounts] [{:id (str "new-" (random-uuid))
|
[:expense-accounts] [{:id (str "new-" (random-uuid))
|
||||||
:amount (:total data)
|
:amount (:total data)
|
||||||
|
:amount-percentage 100
|
||||||
|
:amount-mode "%"
|
||||||
:account @(re-frame/subscribe [::subs/vendor-default-account value])}]]}
|
:account @(re-frame/subscribe [::subs/vendor-default-account value])}]]}
|
||||||
{:dispatch [::forms/change ::form
|
{:dispatch [::forms/change ::form
|
||||||
field value]}))))
|
field value]}))))
|
||||||
@@ -284,7 +310,7 @@
|
|||||||
[:input.input {:type "number"
|
[:input.input {:type "number"
|
||||||
:field [:total]
|
:field [:total]
|
||||||
:disabled (if can-change-amount? "" "disabled")
|
:disabled (if can-change-amount? "" "disabled")
|
||||||
:event change-event
|
:event [::change-amount]
|
||||||
:min min-total
|
:min min-total
|
||||||
:subscription data
|
:subscription data
|
||||||
:spec ::invoice/total
|
:spec ::invoice/total
|
||||||
@@ -297,7 +323,7 @@
|
|||||||
:descriptor "expense account"
|
:descriptor "expense account"
|
||||||
:event change-event
|
:event change-event
|
||||||
:locations locations
|
:locations locations
|
||||||
:max-value (:total data)
|
:max (:total data)
|
||||||
:field [:expense-accounts]}]]]
|
:field [:expense-accounts]}]]]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,7 @@
|
|||||||
(forms/stop-form ::edit-transaction))
|
(forms/stop-form ::edit-transaction))
|
||||||
|
|
||||||
:dispatch (conj edit-completed edit-transaction)}))
|
:dispatch (conj edit-completed edit-transaction)}))
|
||||||
|
|
||||||
(re-frame/reg-event-db
|
(re-frame/reg-event-db
|
||||||
::editing
|
::editing
|
||||||
(fn [db [_ which]]
|
(fn [db [_ which]]
|
||||||
@@ -46,14 +47,25 @@
|
|||||||
(-> db
|
(-> db
|
||||||
(forms/start-form ::edit-transaction {:id (:id which)
|
(forms/start-form ::edit-transaction {:id (:id which)
|
||||||
:yodlee-merchant (:yodlee-merchant which)
|
:yodlee-merchant (:yodlee-merchant which)
|
||||||
|
:amount (:amount which)
|
||||||
:description-original (:description-original which)
|
:description-original (:description-original which)
|
||||||
:location (:location which)
|
:location (:location which)
|
||||||
:client-id (:id (:client which))
|
:client-id (:id (:client which))
|
||||||
:vendor-id (:id (:vendor which))
|
:vendor-id (:id (:vendor which))
|
||||||
:vendor-name (:name (:vendor which))
|
:vendor-name (:name (:vendor which))
|
||||||
:accounts (or (vec (:accounts which))
|
:accounts (if (seq (:accounts which))
|
||||||
|
(vec (map
|
||||||
|
(fn [a]
|
||||||
|
(-> a
|
||||||
|
(update :amount js/parseFloat)
|
||||||
|
(assoc :amount-percentage (* 100 (/ (js/parseFloat (:amount a))
|
||||||
|
(Math/abs (js/parseFloat (:amount which))))))
|
||||||
|
(assoc :amount-mode "$")))
|
||||||
|
(:accounts which)))
|
||||||
[{:id (str "new-" (random-uuid))
|
[{:id (str "new-" (random-uuid))
|
||||||
:amount (Math/abs (:amount which))}])}))))
|
:amount-mode "$"
|
||||||
|
:amount (Math/abs (:amount which))
|
||||||
|
:amount-percentage 100}])}))))
|
||||||
|
|
||||||
|
|
||||||
(re-frame/reg-event-fx
|
(re-frame/reg-event-fx
|
||||||
@@ -111,6 +123,15 @@
|
|||||||
:disabled "disabled"
|
:disabled "disabled"
|
||||||
:subscription data}]]]]
|
:subscription data}]]]]
|
||||||
|
|
||||||
|
[:div.field
|
||||||
|
[:p.help "Amount"]
|
||||||
|
[:div.control
|
||||||
|
[bind-field
|
||||||
|
[:input.input {:type "text"
|
||||||
|
:field [:amount]
|
||||||
|
:disabled "disabled"
|
||||||
|
:subscription data}]]]]
|
||||||
|
|
||||||
[:div.field
|
[:div.field
|
||||||
[:p.help "Description"]
|
[:p.help "Description"]
|
||||||
[:div.control
|
[:div.control
|
||||||
@@ -133,15 +154,16 @@
|
|||||||
:event change-event
|
:event change-event
|
||||||
:subscription data}]]]]
|
:subscription data}]]]]
|
||||||
|
|
||||||
[:div.field]
|
[:div.field
|
||||||
[bind-field
|
[bind-field
|
||||||
[expense-accounts-field
|
[expense-accounts-field
|
||||||
{:type "expense-accounts"
|
{:type "expense-accounts"
|
||||||
:field [:accounts]
|
:field [:accounts]
|
||||||
|
:max (Math/abs (js/parseFloat (:amount data)))
|
||||||
:descriptor "credit account"
|
:descriptor "credit account"
|
||||||
:locations locations
|
:locations locations
|
||||||
:event change-event
|
:event change-event
|
||||||
:subscription data}]]
|
:subscription data}]]]
|
||||||
|
|
||||||
(comment
|
(comment
|
||||||
[:div.field
|
[:div.field
|
||||||
@@ -151,18 +173,11 @@
|
|||||||
:field [:always-map]
|
:field [:always-map]
|
||||||
:subscription data}]
|
:subscription data}]
|
||||||
" Always match Merchant '" (:merchant-name data) "' to '" (:vendor-name data) "'" ]]])
|
" Always match Merchant '" (:merchant-name data) "' to '" (:vendor-name data) "'" ]]])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(when error
|
(when error
|
||||||
^{:key error} [:div.notification.is-warning.animated.fadeInUp
|
^{:key error} [:div.notification.is-warning.animated.fadeInUp
|
||||||
error])
|
error])
|
||||||
|
|
||||||
[:button.button.is-medium.is-primary.is-fullwidth {:disabled (if @(re-frame/subscribe [::can-submit])
|
[:button.button.is-medium.is-primary.is-fullwidth {:disabled (if @(re-frame/subscribe [::can-submit])
|
||||||
""
|
""
|
||||||
"disabled")
|
"disabled")
|
||||||
:class (str @(re-frame/subscribe [::forms/loading-class ::edit-transaction])
|
:class (str @(re-frame/subscribe [::forms/loading-class ::edit-transaction])
|
||||||
(when error " animated shake"))} "Save"]
|
(when error " animated shake"))} "Save"]])])
|
||||||
])])
|
|
||||||
|
|||||||
@@ -156,6 +156,23 @@
|
|||||||
|
|
||||||
(into [dom keys] (with-keys rest))))
|
(into [dom keys] (with-keys rest))))
|
||||||
|
|
||||||
|
(defmethod do-bind "number" [dom {:keys [field precision event subscription class spec] :as keys :or {precision 2}} & rest]
|
||||||
|
(let [field (if (keyword? field) [field] field)
|
||||||
|
event (if (keyword? event) [event] event)
|
||||||
|
keys (assoc keys
|
||||||
|
:on-change (fn [e]
|
||||||
|
(.preventDefault e)
|
||||||
|
(re-frame/dispatch (-> event
|
||||||
|
(conj field)
|
||||||
|
(conj (js/parseFloat (.. e -target -value))))))
|
||||||
|
:value (get-in subscription field)
|
||||||
|
|
||||||
|
:class (str class
|
||||||
|
(when (and spec (not (s/valid? spec (get-in subscription field))))
|
||||||
|
" is-danger")))
|
||||||
|
keys (dissoc keys :field :subscription :event :spec)]
|
||||||
|
(into [dom keys] (with-keys rest))))
|
||||||
|
|
||||||
(defmethod do-bind :default [dom {:keys [field event subscription class spec] :as keys} & rest]
|
(defmethod do-bind :default [dom {:keys [field event subscription class spec] :as keys} & rest]
|
||||||
(let [field (if (keyword? field) [field] field)
|
(let [field (if (keyword? field) [field] field)
|
||||||
event (if (keyword? event) [event] event)
|
event (if (keyword? event) [event] event)
|
||||||
|
|||||||
Reference in New Issue
Block a user