user progress
This commit is contained in:
@@ -234,7 +234,7 @@
|
|||||||
:user
|
:user
|
||||||
{:fields {:id {:type :id}
|
{:fields {:id {:type :id}
|
||||||
:name {:type 'String}
|
:name {:type 'String}
|
||||||
:role {:type 'String}
|
:role {:type :role}
|
||||||
:clients {:type '(list :client)}}}
|
:clients {:type '(list :client)}}}
|
||||||
|
|
||||||
:account_client_override
|
:account_client_override
|
||||||
@@ -415,7 +415,7 @@
|
|||||||
:edit_user
|
:edit_user
|
||||||
{:fields {:id {:type :id}
|
{:fields {:id {:type :id}
|
||||||
:name {:type 'String}
|
:name {:type 'String}
|
||||||
:role {:type 'String}
|
:role {:type :role}
|
||||||
:clients {:type '(list String)}}}
|
:clients {:type '(list String)}}}
|
||||||
|
|
||||||
:add_contact
|
:add_contact
|
||||||
@@ -520,6 +520,11 @@
|
|||||||
:applicability {:values [{:enum-value :global}
|
:applicability {:values [{:enum-value :global}
|
||||||
{:enum-value :optional}
|
{:enum-value :optional}
|
||||||
{:enum-value :customized}]}
|
{:enum-value :customized}]}
|
||||||
|
:role {:values [{:enum-value :none}
|
||||||
|
{:enum-value :user}
|
||||||
|
{:enum-value :manager}
|
||||||
|
{:enum-value :power_user}
|
||||||
|
{:enum-value :admin}]}
|
||||||
:account_type {:values [{:enum-value :dividend}
|
:account_type {:values [{:enum-value :dividend}
|
||||||
{:enum-value :expense}
|
{:enum-value :expense}
|
||||||
{:enum-value :asset}
|
{:enum-value :asset}
|
||||||
|
|||||||
@@ -1,59 +1,49 @@
|
|||||||
(ns auto-ap.graphql.transaction-rules
|
(ns auto-ap.graphql.transaction-rules
|
||||||
(:require [auto-ap.datomic
|
(:require
|
||||||
:refer
|
[auto-ap.datomic
|
||||||
[audit-transact merge-query remove-nils replace-nils-with-retract uri conn]]
|
:refer [audit-transact
|
||||||
[auto-ap.datomic.transaction-rules :as tr]
|
conn
|
||||||
[auto-ap.datomic.transactions :as d-transactions]
|
merge-query
|
||||||
[auto-ap.graphql.utils
|
remove-nils
|
||||||
:refer
|
replace-nils-with-retract]]
|
||||||
[->graphql
|
[auto-ap.datomic.transaction-rules :as tr]
|
||||||
<-graphql
|
[auto-ap.datomic.transactions :as d-transactions]
|
||||||
assert-admin
|
[auto-ap.graphql.utils
|
||||||
ident->enum-f
|
:refer [->graphql
|
||||||
limited-clients
|
<-graphql
|
||||||
result->page
|
assert-admin
|
||||||
snake->kebab]]
|
ident->enum-f
|
||||||
[auto-ap.rule-matching :as rm]
|
limited-clients
|
||||||
[auto-ap.utils :refer [dollars=]]
|
result->page
|
||||||
[clj-time.coerce :as coerce]
|
snake->kebab]]
|
||||||
[clojure.set :as set]
|
[auto-ap.rule-matching :as rm]
|
||||||
[clojure.string :as str]
|
[auto-ap.utils :refer [dollars=]]
|
||||||
[clojure.tools.logging :as log]
|
[clj-time.coerce :as c]
|
||||||
[datomic.api :as d]
|
[clojure.set :as set]
|
||||||
[clj-time.coerce :as c])
|
[clojure.string :as str]
|
||||||
(:import java.time.temporal.ChronoField))
|
[datomic.api :as d]))
|
||||||
|
|
||||||
(defn get-transaction-rule-page [context args value]
|
(defn get-transaction-rule-page [context args _]
|
||||||
(let [args (assoc args :id (:id context))
|
(let [args (assoc args :id (:id context))
|
||||||
[journal-entries journal-entries-count] (tr/get-graphql (<-graphql args))]
|
[journal-entries journal-entries-count] (tr/get-graphql (<-graphql args))]
|
||||||
(result->page (->> journal-entries
|
(result->page (->> journal-entries
|
||||||
(map (ident->enum-f :transaction-rule/transaction-approval-status)))
|
(map (ident->enum-f :transaction-rule/transaction-approval-status)))
|
||||||
journal-entries-count :transaction_rules args)))
|
journal-entries-count :transaction_rules args)))
|
||||||
|
|
||||||
(defn get-transaction-rule-matches [context args value]
|
(defn get-transaction-rule-matches [context args _]
|
||||||
(if (= "admin" (:user/role (:id context)))
|
(if (= "admin" (:user/role (:id context)))
|
||||||
(let [all-rules (tr/get-all)
|
(let [all-rules (tr/get-all)
|
||||||
transaction (update (d-transactions/get-by-id (:transaction_id args)) :transaction/date coerce/to-date)]
|
transaction (update (d-transactions/get-by-id (:transaction_id args)) :transaction/date c/to-date)]
|
||||||
(map ->graphql (rm/get-matching-rules transaction all-rules)))
|
(map ->graphql (rm/get-matching-rules transaction all-rules)))
|
||||||
nil))
|
nil))
|
||||||
|
|
||||||
(defn deleted-accounts [transaction accounts]
|
|
||||||
(let [current-accounts (:transaction-rule/accounts transaction)
|
|
||||||
specified-ids (->> accounts
|
|
||||||
(map :id)
|
|
||||||
set)
|
|
||||||
existing-ids (->> current-accounts
|
|
||||||
(map :db/id)
|
|
||||||
set)]
|
|
||||||
(set/difference existing-ids specified-ids)))
|
|
||||||
|
|
||||||
(defn transaction-rule-account->entity [{:keys [id account_id percentage location]}]
|
(defn transaction-rule-account->entity [{:keys [id account_id percentage location]}]
|
||||||
(remove-nils #:transaction-rule-account {:percentage percentage
|
(remove-nils #:transaction-rule-account {:percentage percentage
|
||||||
:db/id id
|
:db/id id
|
||||||
:account account_id
|
:account account_id
|
||||||
:location location}))
|
:location location}))
|
||||||
|
|
||||||
(defn delete-transaction-rule [context {:keys [transaction_rule_id ]} value]
|
(defn delete-transaction-rule [context {:keys [transaction_rule_id ]} _]
|
||||||
(assert-admin (:id context))
|
(assert-admin (:id context))
|
||||||
(let [existing-transaction-rule (tr/get-by-id transaction_rule_id)]
|
(let [existing-transaction-rule (tr/get-by-id transaction_rule_id)]
|
||||||
(when-not (:transaction-rule/description existing-transaction-rule)
|
(when-not (:transaction-rule/description existing-transaction-rule)
|
||||||
@@ -62,10 +52,9 @@
|
|||||||
(audit-transact [[:db/retractEntity transaction_rule_id]] (:id context))
|
(audit-transact [[:db/retractEntity transaction_rule_id]] (:id context))
|
||||||
transaction_rule_id))
|
transaction_rule_id))
|
||||||
|
|
||||||
(defn upsert-transaction-rule [context {{:keys [id description yodlee_merchant_id note client_id bank_account_id amount_lte amount_gte vendor_id accounts transaction_approval_status dom_gte dom_lte]} :transaction_rule :as z} value]
|
(defn upsert-transaction-rule [context {{:keys [id description yodlee_merchant_id note client_id bank_account_id amount_lte amount_gte vendor_id accounts transaction_approval_status dom_gte dom_lte]} :transaction_rule} _]
|
||||||
(assert-admin (:id context))
|
(assert-admin (:id context))
|
||||||
(let [existing-transaction (tr/get-by-id id)
|
(let [existing-transaction (tr/get-by-id id)
|
||||||
deleted (deleted-accounts existing-transaction accounts)
|
|
||||||
account-total (reduce + 0 (map (fn [x] (:percentage x)) accounts))
|
account-total (reduce + 0 (map (fn [x] (:percentage x)) accounts))
|
||||||
_ (when-not (dollars= 1.0 account-total)
|
_ (when-not (dollars= 1.0 account-total)
|
||||||
(let [error (str "Account total (" account-total ") does not reach 100%")]
|
(let [error (str "Account total (" account-total ") does not reach 100%")]
|
||||||
@@ -75,7 +64,7 @@
|
|||||||
(let [error (str "You must provide a description or a yodlee merchant")]
|
(let [error (str "You must provide a description or a yodlee merchant")]
|
||||||
(throw (ex-info error {:validation-error error}))))
|
(throw (ex-info error {:validation-error error}))))
|
||||||
_ (doseq [a accounts
|
_ (doseq [a accounts
|
||||||
:let [{:keys [:account/location :account/name] :as account} (d/entity (d/db conn) (:account_id a))
|
:let [{:keys [:account/location :account/name]} (d/entity (d/db conn) (:account_id a))
|
||||||
client (d/entity (d/db conn) client_id)
|
client (d/entity (d/db conn) client_id)
|
||||||
]]
|
]]
|
||||||
(when (and location (not= location (:location a)))
|
(when (and location (not= location (:location a)))
|
||||||
@@ -120,7 +109,7 @@
|
|||||||
(defn tr [z x]
|
(defn tr [z x]
|
||||||
(re-find (re-pattern z) x))
|
(re-find (re-pattern z) x))
|
||||||
|
|
||||||
(defn -test-transaction-rule [id {:keys [:transaction-rule/description :transaction-rule/note :transaction-rule/client :transaction-rule/bank-account :transaction-rule/amount-lte :transaction-rule/amount-gte :transaction-rule/dom-lte :transaction-rule/dom-gte :transaction-rule/yodlee-merchant]} include-coded? count]
|
(defn -test-transaction-rule [id {:keys [:transaction-rule/description :transaction-rule/client :transaction-rule/bank-account :transaction-rule/amount-lte :transaction-rule/amount-gte :transaction-rule/dom-lte :transaction-rule/dom-gte :transaction-rule/yodlee-merchant]} include-coded? count]
|
||||||
(->>
|
(->>
|
||||||
(d/query
|
(d/query
|
||||||
(cond-> {:query {:find ['(pull ?e [* {:transaction/client [:client/name]
|
(cond-> {:query {:find ['(pull ?e [* {:transaction/client [:client/name]
|
||||||
@@ -204,7 +193,7 @@
|
|||||||
(map ->graphql))
|
(map ->graphql))
|
||||||
conj [])))
|
conj [])))
|
||||||
|
|
||||||
(defn test-transaction-rule [{:keys [id]} {{:keys [description note client_id bank_account_id amount_lte amount_gte dom_lte dom_gte yodlee_merchant_id]} :transaction_rule :as z} value]
|
(defn test-transaction-rule [{:keys [id]} {{:keys [description client_id bank_account_id amount_lte amount_gte dom_lte dom_gte yodlee_merchant_id]} :transaction_rule} _]
|
||||||
(assert-admin id)
|
(assert-admin id)
|
||||||
(-test-transaction-rule id #:transaction-rule {:description description
|
(-test-transaction-rule id #:transaction-rule {:description description
|
||||||
:client (when client_id {:db/id client_id})
|
:client (when client_id {:db/id client_id})
|
||||||
@@ -217,6 +206,6 @@
|
|||||||
true 15))
|
true 15))
|
||||||
|
|
||||||
|
|
||||||
(defn run-transaction-rule [{:keys [id]} {:keys [transaction_rule_id count]} value]
|
(defn run-transaction-rule [{:keys [id]} {:keys [transaction_rule_id count]} _]
|
||||||
(assert-admin id)
|
(assert-admin id)
|
||||||
(-test-transaction-rule id (tr/get-by-id transaction_rule_id) false count))
|
(-test-transaction-rule id (tr/get-by-id transaction_rule_id) false count))
|
||||||
|
|||||||
@@ -3,11 +3,11 @@
|
|||||||
[auto-ap.datomic.users :as d-users]
|
[auto-ap.datomic.users :as d-users]
|
||||||
[auto-ap.graphql.utils :refer [->graphql assert-admin]]))
|
[auto-ap.graphql.utils :refer [->graphql assert-admin]]))
|
||||||
|
|
||||||
(def role->datomic-role {":none" :user-role/none
|
(def role->datomic-role {:none :user-role/none
|
||||||
":admin" :user-role/admin
|
:admin :user-role/admin
|
||||||
":power_user" :user-role/power-user
|
:power_user :user-role/power-user
|
||||||
":manager" :user-role/manager
|
:manager :user-role/manager
|
||||||
":user" :user-role/user})
|
:user :user-role/user})
|
||||||
|
|
||||||
(defn edit-user [context {:keys [edit_user] :as args} value]
|
(defn edit-user [context {:keys [edit_user] :as args} value]
|
||||||
(assert-admin (:id context))
|
(assert-admin (:id context))
|
||||||
|
|||||||
@@ -41,6 +41,11 @@
|
|||||||
:complete-listener complete-listener})
|
:complete-listener complete-listener})
|
||||||
(assoc-in [::status/status form] nil))))
|
(assoc-in [::status/status form] nil))))
|
||||||
|
|
||||||
|
(re-frame/reg-event-db
|
||||||
|
::start-form
|
||||||
|
(fn [db [_ id data]]
|
||||||
|
(start-form db id data)))
|
||||||
|
|
||||||
(defn triggers-saved [form data-key]
|
(defn triggers-saved [form data-key]
|
||||||
(i/->interceptor
|
(i/->interceptor
|
||||||
:id :triggers-saved
|
:id :triggers-saved
|
||||||
@@ -81,6 +86,11 @@
|
|||||||
db
|
db
|
||||||
(partition 2 path-pairs))))
|
(partition 2 path-pairs))))
|
||||||
|
|
||||||
|
(re-frame/reg-event-db
|
||||||
|
::reset
|
||||||
|
(fn [db [_ form v]]
|
||||||
|
(assoc-in db [::forms form :data] v)))
|
||||||
|
|
||||||
(re-frame/reg-event-db
|
(re-frame/reg-event-db
|
||||||
::visited
|
::visited
|
||||||
(fn [db [_ form & paths]]
|
(fn [db [_ form & paths]]
|
||||||
@@ -90,7 +100,12 @@
|
|||||||
(re-frame/reg-event-db
|
(re-frame/reg-event-db
|
||||||
::check-problems
|
::check-problems
|
||||||
(fn [db [_ form schema]]
|
(fn [db [_ form schema]]
|
||||||
(println "Checking problems")
|
(println "Checking problems"
|
||||||
|
form
|
||||||
|
(get-in db [::forms form :data])
|
||||||
|
(keys (get-in db [::forms]))
|
||||||
|
schema
|
||||||
|
(m/explain schema (get-in db [::forms form :data])))
|
||||||
(assoc-in db [::forms form :problems]
|
(assoc-in db [::forms form :problems]
|
||||||
(when schema (m/explain schema (get-in db [::forms form :data]))))))
|
(when schema (m/explain schema (get-in db [::forms form :data]))))))
|
||||||
|
|
||||||
@@ -116,6 +131,7 @@
|
|||||||
(re-frame/reg-event-db
|
(re-frame/reg-event-db
|
||||||
::save-error
|
::save-error
|
||||||
(fn [db [_ form result]]
|
(fn [db [_ form result]]
|
||||||
|
(println result)
|
||||||
(-> db
|
(-> db
|
||||||
(assoc-in [::forms form :status] :error)
|
(assoc-in [::forms form :status] :error)
|
||||||
(assoc-in [::forms form :error] (or (:message (first result))
|
(assoc-in [::forms form :error] (or (:message (first result))
|
||||||
|
|||||||
@@ -53,7 +53,7 @@
|
|||||||
(let [data-sub (or data-sub [::forms/form id])
|
(let [data-sub (or data-sub [::forms/form id])
|
||||||
change-event (when-not on-change
|
change-event (when-not on-change
|
||||||
(or change-event [::forms/change id]))
|
(or change-event [::forms/change id]))
|
||||||
{:keys [data visited attempted-submit? problems] form-key :id} @(re-frame/subscribe data-sub)
|
{:keys [data visited attempted-submit? problems error] form-key :id} @(re-frame/subscribe data-sub)
|
||||||
data (or value data)
|
data (or value data)
|
||||||
status @(re-frame/subscribe [::status/single id])
|
status @(re-frame/subscribe [::status/single id])
|
||||||
can-submit (if can-submit @(re-frame/subscribe can-submit)
|
can-submit (if can-submit @(re-frame/subscribe can-submit)
|
||||||
@@ -68,7 +68,7 @@
|
|||||||
:submit-event submit-event
|
:submit-event submit-event
|
||||||
:problems problems
|
:problems problems
|
||||||
:attempted-submit? attempted-submit?
|
:attempted-submit? attempted-submit?
|
||||||
:error (-> status :error first :message)
|
:error (or error (-> status :error first :message))
|
||||||
:status status
|
:status status
|
||||||
:id id
|
:id id
|
||||||
:data data
|
:data data
|
||||||
@@ -91,7 +91,8 @@
|
|||||||
))))
|
))))
|
||||||
|
|
||||||
(defn virtual-builder []
|
(defn virtual-builder []
|
||||||
(let [key (r/atom (random-uuid))]
|
(let [starting-key (random-uuid)
|
||||||
|
key (r/atom starting-key)]
|
||||||
(fn [{:keys [value on-change can-submit error-messages fullwidth? schema]}]
|
(fn [{:keys [value on-change can-submit error-messages fullwidth? schema]}]
|
||||||
(let [data-sub [::forms/form @key]
|
(let [data-sub [::forms/form @key]
|
||||||
{:keys [data error problems visited]} @(re-frame/subscribe data-sub)
|
{:keys [data error problems visited]} @(re-frame/subscribe data-sub)
|
||||||
@@ -99,8 +100,12 @@
|
|||||||
(r/create-element Provider #js {:value #js {:can-submit can-submit
|
(r/create-element Provider #js {:value #js {:can-submit can-submit
|
||||||
:error-messages (or error-messages
|
:error-messages (or error-messages
|
||||||
nil)
|
nil)
|
||||||
:on-change on-change
|
;; wrap to make sure raw form updates too
|
||||||
:blur-event [::blurred schema @key]
|
:on-change (fn [v]
|
||||||
|
(re-frame/dispatch-sync [::forms/reset @key v])
|
||||||
|
(on-change v))
|
||||||
|
:blur-event [::blurred schema @key ]
|
||||||
|
:problems problems
|
||||||
:visited visited
|
:visited visited
|
||||||
:error error
|
:error error
|
||||||
:id @key
|
:id @key
|
||||||
@@ -201,6 +206,7 @@
|
|||||||
nil)
|
nil)
|
||||||
visited? (get visited full-field-path)
|
visited? (get visited full-field-path)
|
||||||
value (get-in data full-field-path)]
|
value (get-in data full-field-path)]
|
||||||
|
(println "VISITED " visited full-field-path problems)
|
||||||
(-> child-props
|
(-> child-props
|
||||||
(assoc :on-change
|
(assoc :on-change
|
||||||
(if on-change
|
(if on-change
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
[clojure.string :as str]
|
[clojure.string :as str]
|
||||||
[auto-ap.views.components.multi :as multi]
|
[auto-ap.views.components.multi :as multi]
|
||||||
[auto-ap.views.components.money-field :as money]
|
[auto-ap.views.components.money-field :as money]
|
||||||
[auto-ap.views.components.number :as number]))
|
[auto-ap.views.components.number :as number]
|
||||||
|
[auto-ap.views.components.typeahead.vendor :as typeahead]))
|
||||||
|
|
||||||
|
|
||||||
(defn checkbox [{:keys [on-change
|
(defn checkbox [{:keys [on-change
|
||||||
@@ -53,3 +54,8 @@
|
|||||||
(def number-input number/number-input)
|
(def number-input number/number-input)
|
||||||
|
|
||||||
(def money-input money/field)
|
(def money-input money/field)
|
||||||
|
|
||||||
|
(def search-backed-typeahead typeahead/search-backed-typeahead)
|
||||||
|
(def entity-typeahead typeahead/typeahead-v3)
|
||||||
|
|
||||||
|
(def button-radio-input auto-ap.views.components.button-radio/button-radio)
|
||||||
|
|||||||
@@ -302,20 +302,22 @@
|
|||||||
locations))
|
locations))
|
||||||
:disabled (boolean (:location account))
|
:disabled (boolean (:location account))
|
||||||
:allow-nil? true}]]]]]
|
:allow-nil? true}]]]]]
|
||||||
|
(if percentage-only?
|
||||||
[left-stack
|
[form-builder/raw-field-v2 {:field [index :amount-percentage]}
|
||||||
[:div.field.has-addons
|
[percentage-field {}]]
|
||||||
[form-builder/raw-field-v2 {:field [index :amount-mode]}
|
[left-stack
|
||||||
[button-radio/button-radio {:options [["$" "Amount"]
|
[:div.field.has-addons
|
||||||
["%" "Percent"]]}]]
|
[form-builder/raw-field-v2 {:field [index :amount-mode]}
|
||||||
(if (= "$" amount-mode)
|
[button-radio/button-radio {:options [["$" "Amount"]
|
||||||
[form-builder/raw-field-v2 {:field [index :amount]}
|
["%" "Percent"]]}]]
|
||||||
[money-field {}]
|
(if (= "$" amount-mode)
|
||||||
]
|
[form-builder/raw-field-v2 {:field [index :amount]}
|
||||||
[form-builder/raw-field-v2 {:field [index :amount-percentage]}
|
[money-field {}]
|
||||||
[percentage-field {}]])]
|
]
|
||||||
(when (= "%" amount-mode)
|
[form-builder/raw-field-v2 {:field [index :amount-percentage]}
|
||||||
[:div.tag.is-primary.is-light (gstring/format "$%.2f" (or amount 0) )])]]]))
|
[percentage-field {}]])]
|
||||||
|
(when (= "%" amount-mode)
|
||||||
|
[:div.tag.is-primary.is-light (gstring/format "$%.2f" (or amount 0) )])])]]))
|
||||||
(when-not disabled
|
(when-not disabled
|
||||||
[:p.buttons
|
[:p.buttons
|
||||||
[:a.button {:on-click (fn []
|
[:a.button {:on-click (fn []
|
||||||
|
|||||||
@@ -1,26 +1,22 @@
|
|||||||
(ns auto-ap.views.pages.admin.rules.form
|
(ns auto-ap.views.pages.admin.rules.form
|
||||||
(:require
|
(:require
|
||||||
[auto-ap.entities.transaction-rule :as entity]
|
|
||||||
[auto-ap.events :as events]
|
[auto-ap.events :as events]
|
||||||
[auto-ap.forms :as forms]
|
[auto-ap.forms :as forms]
|
||||||
[auto-ap.forms.builder :as form-builder]
|
[auto-ap.forms.builder :as form-builder]
|
||||||
|
[auto-ap.schema :as schema]
|
||||||
[auto-ap.status :as status]
|
[auto-ap.status :as status]
|
||||||
[auto-ap.subs :as subs]
|
[auto-ap.subs :as subs]
|
||||||
[auto-ap.views.components.level :refer [left-stack]]
|
[auto-ap.views.components :as com]
|
||||||
[auto-ap.views.components.button-radio :refer [button-radio]]
|
|
||||||
[auto-ap.views.components.expense-accounts-field
|
[auto-ap.views.components.expense-accounts-field
|
||||||
:as expense-accounts-field
|
:as expense-accounts-field
|
||||||
:refer [expense-accounts-field]]
|
:refer [expense-accounts-field-v2]]
|
||||||
[auto-ap.views.components.layouts :as layouts]
|
[auto-ap.views.components.layouts :as layouts]
|
||||||
[auto-ap.views.components.money-field :refer [money-field]]
|
[auto-ap.views.components.level :refer [left-stack]]
|
||||||
[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.common :refer [default-read]]
|
||||||
[auto-ap.views.pages.admin.rules.results-modal :as results-modal]
|
[auto-ap.views.pages.admin.rules.results-modal :as results-modal]
|
||||||
[auto-ap.views.utils :refer [dispatch-event with-user]]
|
[auto-ap.views.utils :refer [coerce-float dispatch-event with-user]]
|
||||||
[clojure.spec.alpha :as s]
|
|
||||||
[clojure.string :as str]
|
[clojure.string :as str]
|
||||||
|
[malli.core :as m]
|
||||||
[re-frame.core :as re-frame]
|
[re-frame.core :as re-frame]
|
||||||
[reagent.core :as r]
|
[reagent.core :as r]
|
||||||
[vimsical.re-frame.cofx.inject :as inject]
|
[vimsical.re-frame.cofx.inject :as inject]
|
||||||
@@ -49,12 +45,6 @@
|
|||||||
(when dom-lte
|
(when dom-lte
|
||||||
(str "<" dom-lte))))]))))
|
(str "<" dom-lte))))]))))
|
||||||
|
|
||||||
(re-frame/reg-sub
|
|
||||||
::can-submit
|
|
||||||
:<- [::forms/form ::form]
|
|
||||||
(fn [{:keys [data]} _]
|
|
||||||
(s/valid? ::entity/transaction-rule data)))
|
|
||||||
|
|
||||||
(re-frame/reg-sub
|
(re-frame/reg-sub
|
||||||
::query
|
::query
|
||||||
:<- [::forms/form ::form]
|
:<- [::forms/form ::form]
|
||||||
@@ -91,10 +81,6 @@
|
|||||||
(assoc :bank-account-id (:id (:bank-account data))))}
|
(assoc :bank-account-id (:id (:bank-account data))))}
|
||||||
default-read]}]}))
|
default-read]}]}))
|
||||||
|
|
||||||
(defn ungraphql-transaction-rule [x]
|
|
||||||
(-> x
|
|
||||||
(update :amount-lte #(some-> % js/parseFloat))
|
|
||||||
(update :amount-gte #(some-> % js/parseFloat))))
|
|
||||||
|
|
||||||
(re-frame/reg-sub
|
(re-frame/reg-sub
|
||||||
::test-query
|
::test-query
|
||||||
@@ -162,6 +148,9 @@
|
|||||||
:accounts
|
:accounts
|
||||||
:yodlee-merchant
|
:yodlee-merchant
|
||||||
:transaction-approval-status])
|
:transaction-approval-status])
|
||||||
|
|
||||||
|
(update :amount-lte coerce-float)
|
||||||
|
(update :amount-gte coerce-float)
|
||||||
(update :accounts (fn [xs]
|
(update :accounts (fn [xs]
|
||||||
(mapv #(-> %
|
(mapv #(-> %
|
||||||
(assoc :amount-percentage (* (:percentage %) 100.0)))
|
(assoc :amount-percentage (* (:percentage %) 100.0)))
|
||||||
@@ -210,9 +199,9 @@
|
|||||||
{:graphql
|
{:graphql
|
||||||
{:token user
|
{:token user
|
||||||
:query-obj query
|
:query-obj query
|
||||||
|
:owns-state {:single ::form}
|
||||||
:on-success (fn [result]
|
:on-success (fn [result]
|
||||||
[::updated (:upsert-transaction-rule result)])
|
[::updated (:upsert-transaction-rule result)])}}))
|
||||||
:on-error [::forms/save-error ::form]}}))
|
|
||||||
|
|
||||||
(re-frame/reg-event-fx
|
(re-frame/reg-event-fx
|
||||||
::test-clicked
|
::test-clicked
|
||||||
@@ -228,7 +217,7 @@
|
|||||||
(re-frame/reg-event-fx
|
(re-frame/reg-event-fx
|
||||||
::updated
|
::updated
|
||||||
[(forms/triggers-stop ::form)]
|
[(forms/triggers-stop ::form)]
|
||||||
(fn [{:keys [db]} [_ {:keys [rule-saved]} _]]
|
(fn [{:keys [db]} _]
|
||||||
{:db (forms/start-form db ::form {:client @(re-frame/subscribe [::subs/client])})}))
|
{:db (forms/start-form db ::form {:client @(re-frame/subscribe [::subs/client])})}))
|
||||||
|
|
||||||
(re-frame/reg-event-fx
|
(re-frame/reg-event-fx
|
||||||
@@ -261,117 +250,107 @@
|
|||||||
{::track/dispose [{:id ::client}
|
{::track/dispose [{:id ::client}
|
||||||
{:id ::vendor-change}]}))
|
{:id ::vendor-change}]}))
|
||||||
|
|
||||||
(defn form-contents [params]
|
(def rule-schema
|
||||||
|
(m/schema [:map
|
||||||
|
[:client {:optional true}
|
||||||
|
[:maybe schema/reference]]
|
||||||
|
[:bank-account {:optional true}
|
||||||
|
[:maybe schema/reference]]
|
||||||
|
[:description
|
||||||
|
schema/not-empty-string]
|
||||||
|
[:amount-gte {:optional true}
|
||||||
|
[:maybe schema/money]]
|
||||||
|
[:amount-lte {:optional true}
|
||||||
|
[:maybe schema/money]]
|
||||||
|
[:dom-gte {:optional true}
|
||||||
|
[:maybe [:int {:min 1 :max 31}]]]
|
||||||
|
[:dom-lte {:optional true}
|
||||||
|
[:maybe [:int {:min 1 :max 31}]]]
|
||||||
|
[:vendor {:optional true}
|
||||||
|
[:maybe schema/reference]]
|
||||||
|
[:transaction-approval-status {:optional true}
|
||||||
|
[:maybe [:enum :unapproved :requires-feedback :approved :excluded]]]
|
||||||
|
[:note {:optional true}
|
||||||
|
[:maybe :string]]]))
|
||||||
|
|
||||||
|
(defn form-contents []
|
||||||
[layouts/side-bar {:on-close (dispatch-event [::forms/form-closing ::form ])}
|
[layouts/side-bar {:on-close (dispatch-event [::forms/form-closing ::form ])}
|
||||||
(let [{:keys [data id]} @(re-frame/subscribe [::forms/form ::form])
|
(let [{:keys [data]} @(re-frame/subscribe [::forms/form ::form])
|
||||||
default-note @(re-frame/subscribe [::default-note])
|
default-note @(re-frame/subscribe [::default-note])
|
||||||
test-state @(re-frame/subscribe [::status/single ::test])]
|
test-state @(re-frame/subscribe [::status/single ::test])]
|
||||||
[form-builder/builder {:can-submit [::can-submit]
|
[form-builder/builder {:change-event [::changed]
|
||||||
:change-event [::changed]
|
|
||||||
:submit-event [::saving ]
|
:submit-event [::saving ]
|
||||||
:id ::form}
|
:id ::form
|
||||||
|
:schema rule-schema}
|
||||||
[form-builder/section {:title "Transaction Rule"}
|
[form-builder/section {:title "Transaction Rule"}
|
||||||
[form-builder/field {:required? true}
|
[form-builder/field-v2 {:required? true
|
||||||
|
:field :client}
|
||||||
"Client"
|
"Client"
|
||||||
[typeahead-v3 {:entities @(re-frame/subscribe [::subs/clients])
|
[com/entity-typeahead {:entities @(re-frame/subscribe [::subs/clients])
|
||||||
:auto-focus true
|
:auto-focus true
|
||||||
:entity->text :name
|
:entity->text :name}]]
|
||||||
:type "typeahead-v3"
|
|
||||||
:field [:client]
|
|
||||||
:spec ::entity/client}]]
|
|
||||||
|
|
||||||
|
|
||||||
[form-builder/field
|
[form-builder/field-v2 {:field [:bank-account]}
|
||||||
"Bank account"
|
"Bank account"
|
||||||
[typeahead-v3 {:entities @(re-frame/subscribe [::subs/real-bank-accounts-for-client (:client data)])
|
[com/entity-typeahead {:entities @(re-frame/subscribe [::subs/real-bank-accounts-for-client (:client data)])
|
||||||
:entity->text :name
|
:entity->text :name}]]
|
||||||
:type "typeahead-v3"
|
|
||||||
:field [:bank-account]
|
|
||||||
:spec ::entity/bank-account}]]
|
|
||||||
|
|
||||||
#_[form-builder/field
|
[form-builder/field-v2 {:field :description
|
||||||
"Yodlee Merchant"
|
:required? true}
|
||||||
[typeahead-v3 {:entities @(re-frame/subscribe [::subs/yodlee-merchants])
|
|
||||||
:entity->text #(str (:name %) " - " (:yodlee-id %))
|
|
||||||
:type "typeahead-v3"
|
|
||||||
:field [:yodlee-merchant]}]]
|
|
||||||
|
|
||||||
[form-builder/field
|
|
||||||
[:span "Description (" [:a {:href "https://regex101.com" :target "_new"} "regex tester"] ")" ]
|
[:span "Description (" [:a {:href "https://regex101.com" :target "_new"} "regex tester"] ")" ]
|
||||||
[:input.input {:type "text"
|
[:input.input {:type "text"}]]
|
||||||
:field [:description]
|
|
||||||
:spec ::entity/description}]]
|
|
||||||
|
|
||||||
[:div.field
|
[:div.field
|
||||||
[:p.help "Amount"]
|
[:p.help "Amount"]
|
||||||
[left-stack
|
[left-stack
|
||||||
[form-builder/raw-field
|
[form-builder/raw-field-v2 {:field :amount-gte}
|
||||||
[money-field {:type "money"
|
[com/money-input {:placeholder ">="}]]
|
||||||
:placeholder ">="
|
|
||||||
:field [:amount-gte]
|
|
||||||
:spec ::entity/amount-gte}]]
|
|
||||||
"-"
|
"-"
|
||||||
[form-builder/raw-field
|
[form-builder/raw-field-v2 {:field :amount-lte}
|
||||||
[money-field {:type "money"
|
[com/money-input {:placeholder "<="}]]]]
|
||||||
:placeholder "<="
|
|
||||||
:field [:amount-lte]
|
|
||||||
:spec ::entity/amount-lte}]]]]
|
|
||||||
|
|
||||||
[:div.field
|
[:div.field
|
||||||
[:p.help "Day of Month"]
|
[:p.help "Day of month"]
|
||||||
[:div.control
|
[left-stack
|
||||||
[:div.columns
|
[form-builder/raw-field-v2 {:field :dom-gte}
|
||||||
[:div.column
|
[com/number-input {:placeholder ">="
|
||||||
[form-builder/raw-field
|
:style {:width "7em"}}]]
|
||||||
[:input.input {:type "number"
|
"-"
|
||||||
:placeholder ">="
|
[form-builder/raw-field-v2 {:field :dom-lte}
|
||||||
:field [:dom-gte]
|
[com/number-input {:placeholder "<="
|
||||||
:spec ::entity/dom-gte
|
:style {:width "7em"}}]]]]
|
||||||
:precision 0
|
|
||||||
:step "1"}]]]
|
|
||||||
[:div.column
|
|
||||||
[form-builder/raw-field
|
|
||||||
[:input.input {:type "number"
|
|
||||||
:placeholder "<="
|
|
||||||
:field [:dom-lte]
|
|
||||||
:spec ::entity/dom-lte
|
|
||||||
:precision 0
|
|
||||||
:step "1"}]]]]]]
|
|
||||||
|
|
||||||
[:h2.title.is-4 "Outcomes"]
|
[:h2.title.is-4 "Outcomes"]
|
||||||
|
|
||||||
[form-builder/field "Assign Vendor"
|
[form-builder/field-v2 {:field :vendor}
|
||||||
[search-backed-typeahead {:search-query (fn [i]
|
"Assign Vendor"
|
||||||
|
[com/search-backed-typeahead {:search-query (fn [i]
|
||||||
[:search_vendor
|
[:search_vendor
|
||||||
{:query i}
|
{:query i}
|
||||||
[:name :id]])
|
[:name :id]])}]]
|
||||||
:type "typeahead-v3"
|
|
||||||
:field [:vendor]}]]
|
|
||||||
|
|
||||||
[form-builder/raw-field
|
[form-builder/field-v2 {:field :accounts}
|
||||||
[expense-accounts-field {:type "expense-accounts"
|
"Accounts"
|
||||||
:descriptor "account asssignment"
|
[expense-accounts-field-v2 {:descriptor "account asssignment"
|
||||||
:percentage-only? true
|
:percentage-only? true
|
||||||
:client (:client data)
|
:client (:client data)
|
||||||
:locations (into ["Shared"] @(re-frame/subscribe [::subs/locations-for-client-or-bank-account (:id (:client data)) (:id (:bank-account data))]))
|
:locations (into ["Shared"] @(re-frame/subscribe [::subs/locations-for-client-or-bank-account (:id (:client data)) (:id (:bank-account data))]))
|
||||||
:max 100
|
:max 100}]]
|
||||||
:field [:accounts]}]]
|
|
||||||
|
|
||||||
[form-builder/field
|
[form-builder/field-v2 {:field :transaction-approval-status}
|
||||||
"Approval Status"
|
"Approval Status"
|
||||||
[button-radio
|
[com/button-radio-input
|
||||||
{:type "button-radio"
|
{:options [[:unapproved "Unapproved"]
|
||||||
:field [:transaction-approval-status]
|
|
||||||
:options [[:unapproved "Unapproved"]
|
|
||||||
[:requires-feedback "Client Review"]
|
[:requires-feedback "Client Review"]
|
||||||
[:approved "Approved"]
|
[:approved "Approved"]
|
||||||
[:excluded "Excluded from Ledger"]]}]]
|
[:excluded "Excluded from Ledger"]]}]]
|
||||||
|
|
||||||
[form-builder/field "Note"
|
[form-builder/field-v2 {:field :note}
|
||||||
|
"Note"
|
||||||
[:input.input {:type "text"
|
[:input.input {:type "text"
|
||||||
:field [:note]
|
:placeholder default-note}]]
|
||||||
:placeholder default-note
|
|
||||||
:spec (s/nilable ::entity/note)}]]
|
|
||||||
|
|
||||||
[:div.is-divider]
|
[:div.is-divider]
|
||||||
[form-builder/error-notification]
|
[form-builder/error-notification]
|
||||||
|
|||||||
@@ -1,18 +1,15 @@
|
|||||||
(ns auto-ap.views.pages.admin.users.form
|
(ns auto-ap.views.pages.admin.users.form
|
||||||
(:require
|
(:require
|
||||||
[auto-ap.entities.clients :as entity]
|
|
||||||
[auto-ap.forms :as forms]
|
[auto-ap.forms :as forms]
|
||||||
[auto-ap.forms.builder :as form-builder]
|
[auto-ap.forms.builder :as form-builder]
|
||||||
|
[auto-ap.schema :as schema]
|
||||||
[auto-ap.status :as status]
|
[auto-ap.status :as status]
|
||||||
[auto-ap.subs :as subs]
|
[auto-ap.subs :as subs]
|
||||||
|
[auto-ap.views.components :as com]
|
||||||
[auto-ap.views.components.modal :as modal]
|
[auto-ap.views.components.modal :as modal]
|
||||||
[auto-ap.views.components.typeahead :refer [typeahead-v3]]
|
[auto-ap.views.utils :refer [dispatch-event with-user]]
|
||||||
[auto-ap.views.utils :refer [dispatch-event multi-field with-user]]
|
[malli.core :as m]
|
||||||
[react-popper :refer [usePopper] :as react-popper]
|
[re-frame.core :as re-frame]))
|
||||||
|
|
||||||
[re-frame.core :as re-frame]
|
|
||||||
[react :as react]
|
|
||||||
[reagent.core :as r]))
|
|
||||||
|
|
||||||
(re-frame/reg-event-fx
|
(re-frame/reg-event-fx
|
||||||
::saving
|
::saving
|
||||||
@@ -37,41 +34,52 @@
|
|||||||
{:dispatch [::modal/modal-closed]}))
|
{:dispatch [::modal/modal-closed]}))
|
||||||
|
|
||||||
|
|
||||||
|
(def client-schema
|
||||||
|
(m/schema [:map [:client schema/reference]]))
|
||||||
|
|
||||||
|
(def user-schema
|
||||||
|
(m/schema
|
||||||
|
[:map
|
||||||
|
[:name schema/not-empty-string]
|
||||||
|
[:role [:enum :none :user :manager :power_user :admin]]
|
||||||
|
[:clients {:optional true}
|
||||||
|
[:maybe
|
||||||
|
[:sequential client-schema]]]]))
|
||||||
|
|
||||||
|
|
||||||
(defn form []
|
(defn form []
|
||||||
(let [{:keys [data]} @(re-frame/subscribe [::forms/form ::form])
|
(let [{:keys [data]} @(re-frame/subscribe [::forms/form ::form])
|
||||||
clients @(re-frame/subscribe [::subs/clients])]
|
clients @(re-frame/subscribe [::subs/clients])]
|
||||||
|
(println data)
|
||||||
[:<>
|
[:<>
|
||||||
[form-builder/builder {:submit-event [::saving]
|
[form-builder/builder {:submit-event [::saving]
|
||||||
:id ::form}
|
:id ::form
|
||||||
[form-builder/field
|
:schema user-schema}
|
||||||
|
[form-builder/field-v2 {:required? true
|
||||||
|
:field :name}
|
||||||
"Name"
|
"Name"
|
||||||
[:input.input {:type "text"
|
[:input.input {:type "text"}]]
|
||||||
:field [:name]
|
[form-builder/field-v2 {:required? true
|
||||||
:spec ::entity/name}]]
|
:field :role}
|
||||||
[:div.field
|
"Role"
|
||||||
[:p.help "Role"]
|
[com/select-field {:options [[:none "None"]
|
||||||
[:div.control
|
[:user "User"]
|
||||||
[:div.select
|
[:manager "Manager"]
|
||||||
[form-builder/raw-field
|
[:power_user "Power User"]
|
||||||
[:select {:type "select"
|
[:admin "Admin"]]
|
||||||
:field [:role]}
|
:allow-nil? false
|
||||||
[:option {:value ":none"} "None"]
|
:keywordize? true}]]
|
||||||
[:option {:value ":user"} "User"]
|
(when (#{:user :manager :power_user} (:role data))
|
||||||
[:option {:value ":manager"} "Manager"]
|
[form-builder/field-v2 {:field :clients}
|
||||||
[:option {:value ":power_user"} "Power User"]
|
|
||||||
[:option {:value ":admin"} "Admin"]]]]]]
|
|
||||||
(when (#{":user" ":manager" ":power_user"} (:role data))
|
|
||||||
[form-builder/field
|
|
||||||
"Client"
|
"Client"
|
||||||
[multi-field {:type "multi-field"
|
[com/multi-field-v2 {:template [[form-builder/raw-field-v2 {:field :client}
|
||||||
:field [:clients]
|
[com/entity-typeahead
|
||||||
:template [[typeahead-v3 {:entities clients
|
{:entities clients
|
||||||
:entity->text :name
|
:entity->text :name
|
||||||
:style {:width "13em"}
|
:style {:width "13em"}}]]]
|
||||||
:type "typeahead-v3"
|
:key-fn :id
|
||||||
:field [:client]}]]}]])
|
:schema [:sequential client-schema]
|
||||||
|
:new-text "Grant access to client"}]])
|
||||||
|
|
||||||
[form-builder/hidden-submit-button]]]))
|
[form-builder/hidden-submit-button]]]))
|
||||||
|
|
||||||
@@ -79,7 +87,7 @@
|
|||||||
::editing
|
::editing
|
||||||
(fn [{:keys [db]} [_ d]]
|
(fn [{:keys [db]} [_ d]]
|
||||||
{:db (-> db
|
{:db (-> db
|
||||||
(forms/start-form ::form (update d :clients #(map (fn [x] {:client x}) %))))
|
(forms/start-form ::form (update d :clients #(mapv (fn [x] {:client x :id (random-uuid)}) %))))
|
||||||
:dispatch [::modal/modal-requested {:title (str "Edit user " (:name d))
|
:dispatch [::modal/modal-requested {:title (str "Edit user " (:name d))
|
||||||
:body [form]
|
:body [form]
|
||||||
:cancel? false
|
:cancel? false
|
||||||
|
|||||||
@@ -622,3 +622,17 @@
|
|||||||
second
|
second
|
||||||
base64/decodeString)]
|
base64/decodeString)]
|
||||||
(js->clj (.parse js/JSON json) :keywordize-keys true)))
|
(js->clj (.parse js/JSON json) :keywordize-keys true)))
|
||||||
|
|
||||||
|
(defn coerce-float [f]
|
||||||
|
(cond (str/blank? f)
|
||||||
|
nil
|
||||||
|
|
||||||
|
(float? f)
|
||||||
|
f
|
||||||
|
|
||||||
|
(and (string? f)
|
||||||
|
(not (js/Number.isNaN (js/parseFloat f))))
|
||||||
|
(js/parseFloat f)
|
||||||
|
|
||||||
|
:else
|
||||||
|
nil))
|
||||||
|
|||||||
Reference in New Issue
Block a user