179 lines
8.5 KiB
Clojure
179 lines
8.5 KiB
Clojure
(ns auto-ap.views.components.expense-accounts-field
|
|
(:require
|
|
[auto-ap.forms.builder :as form-builder]
|
|
[auto-ap.schema :as schema]
|
|
[auto-ap.utils :refer [dollars-0?]]
|
|
[auto-ap.views.components :as com]
|
|
[auto-ap.views.components.button-radio :as button-radio]
|
|
[auto-ap.views.components.level :refer [left-stack]]
|
|
[auto-ap.views.components.money-field :refer [money-field]]
|
|
[auto-ap.views.components.percentage-field :refer [percentage-field]]
|
|
[auto-ap.views.components.typeahead.vendor
|
|
:refer [search-backed-typeahead]]
|
|
[auto-ap.views.utils :refer [->$ appearing-group]]
|
|
[goog.string :as gstring]
|
|
[malli.core :as m]))
|
|
|
|
(defn can-replace-with-default? [accounts]
|
|
(and (or (not (seq accounts))
|
|
(<= 1 (count accounts)))
|
|
(not (get-in accounts [0 :account :id]))))
|
|
|
|
(defn default-account [accounts default-account amount locations]
|
|
[{:id (str "new-" (random-uuid))
|
|
:amount (Math/abs amount)
|
|
:amount-percentage 100
|
|
:amount-mode "%"
|
|
:location (or
|
|
(:location default-account)
|
|
(get-in accounts [0 :account :location])
|
|
(if (= 1 (count locations))
|
|
(first locations)
|
|
nil))
|
|
:account default-account}])
|
|
|
|
|
|
(defn from-graphql [accounts total locations]
|
|
(if (seq accounts)
|
|
(vec (map
|
|
(fn [a]
|
|
(-> a
|
|
(update :amount js/parseFloat)
|
|
(assoc :amount-percentage (* 100 (/ (js/parseFloat (:amount a))
|
|
(Math/abs total))))
|
|
(assoc :amount-mode "$")))
|
|
accounts))
|
|
[{:id (str "new-" (random-uuid))
|
|
:amount-mode "$"
|
|
:amount (Math/abs total)
|
|
:amount-percentage 100
|
|
:location (if (= 1 (count locations))
|
|
(first locations)
|
|
nil)}]))
|
|
|
|
|
|
;; EVENTS
|
|
|
|
(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) total)))))
|
|
expense-accounts))
|
|
|
|
;; VIEWS
|
|
|
|
|
|
(def schema (m/schema [:sequential [:map
|
|
[:id :string]
|
|
[:account schema/reference]
|
|
[:location schema/not-empty-string]
|
|
[:amount schema/money]]]))
|
|
|
|
|
|
|
|
(defn expense-accounts-field-v2 [{value :value on-change :on-change expense-accounts :value client :client max-value :max locations :locations disabled :disabled percentage-only? :percentage-only? :or {percentage-only? false}}]
|
|
[form-builder/virtual-builder {:value value
|
|
:schema schema
|
|
:on-change (fn [expense-accounts original-expense-accounts]
|
|
(let [updated-expense-accounts
|
|
(for [[before-account after-account] (map vector (concat original-expense-accounts
|
|
(repeat nil)) expense-accounts)]
|
|
(cond-> after-account
|
|
(not= (:id (:account before-account))
|
|
(:id (:account after-account)))
|
|
(assoc :location nil)
|
|
|
|
(not= (:amount-percentage before-account)
|
|
(:amount-percentage after-account))
|
|
(assoc :amount (* (/ (:amount-percentage after-account) 100.0)
|
|
max-value))
|
|
|
|
(:location (:account after-account))
|
|
(assoc :location (:location (:account after-account)))))]
|
|
(on-change (into [] updated-expense-accounts))))}
|
|
[:div
|
|
[:div.tags
|
|
(when max-value
|
|
[:div.tag "To Allocate: " (->$ max-value)])
|
|
|
|
(when-not percentage-only?
|
|
(let [total (reduce + 0 (map (or :amount 0.0) expense-accounts))]
|
|
[:<>
|
|
[:div.tag "Total: " (->$ total) ]
|
|
[:div.tag {:class (if (dollars-0? (- max-value total))
|
|
["is-primary" "is-light"]
|
|
["is-danger" "is-light"])}
|
|
"Remaining: " (->$ (- max-value total))]]))]
|
|
|
|
(into [appearing-group]
|
|
(for [[index {:keys [account id amount amount-mode]}] (map vector (range) expense-accounts)]
|
|
^{:key id}
|
|
[:div.card {:style {:margin-bottom "2em"}}
|
|
[:div.card-header
|
|
[:p.card-header-title "Expense Account"]
|
|
(when-not disabled
|
|
[:div.card-header-icon {:on-click (fn []
|
|
(on-change (into [] (filter #(not= id (:id %)) expense-accounts))))}
|
|
[:a.delete ]])]
|
|
[:div.card-content
|
|
[:div.field
|
|
[:div.columns
|
|
[:div.column
|
|
[:div.control.is-fullwidth
|
|
[form-builder/field-v2 {:required? true
|
|
:field [index :account]}
|
|
"Account"
|
|
[search-backed-typeahead {:search-query (fn [i]
|
|
[:search_account
|
|
{:query i
|
|
:client-id (:id client)}
|
|
[:name :id :location]])
|
|
:disabled disabled}]]]]
|
|
[:div.column.is-narrow
|
|
[form-builder/field-v2 {:required? true
|
|
:field [index :location]}
|
|
"Location"
|
|
[com/select-field {:options (if (:location account)
|
|
[[(:location account) (:location account)]]
|
|
(map (fn [l] [l l])
|
|
locations))
|
|
:disabled (boolean (:location account))
|
|
:allow-nil? true}]]]]]
|
|
(if percentage-only?
|
|
[form-builder/raw-field-v2 {:field [index :amount-percentage]}
|
|
[percentage-field {}]]
|
|
[left-stack
|
|
[:div.field.has-addons
|
|
[form-builder/raw-field-v2 {:field [index :amount-mode]}
|
|
[button-radio/button-radio {:options [["$" "Amount"]
|
|
["%" "Percent"]]}]]
|
|
(if (= "$" amount-mode)
|
|
[form-builder/raw-field-v2 {:field [index :amount]}
|
|
[money-field {}]
|
|
]
|
|
[form-builder/raw-field-v2 {:field [index :amount-percentage]}
|
|
[percentage-field {}]])]
|
|
(when (= "%" amount-mode)
|
|
[:div.tag.is-primary.is-light (gstring/format "$%.2f" (or amount 0) )])])]]))
|
|
(when-not disabled
|
|
[:p.buttons
|
|
[:a.button {:on-click (fn []
|
|
(on-change
|
|
(recalculate-amounts (mapv
|
|
(fn [ea]
|
|
(assoc ea :amount-percentage (* 100.0 (/ 1 (count expense-accounts)))))
|
|
expense-accounts)
|
|
max-value))
|
|
)} "Spread evenly"]
|
|
[:a.button {:on-click
|
|
(fn []
|
|
(on-change (conj value {:id (str "new-" (random-uuid))
|
|
:amount-mode "%"
|
|
:location (if (= 1 (count locations))
|
|
(first locations)
|
|
nil)})))}
|
|
"Add"]])]])
|