supports adding conclusions.

This commit is contained in:
Bryce Covert
2019-05-12 09:04:26 -07:00
parent 067fff8588
commit 2146073df8
5 changed files with 192 additions and 76 deletions

View File

@@ -350,6 +350,32 @@
:db/valueType :db.type/long :db/valueType :db.type/long
:db/cardinality :db.cardinality/one :db/cardinality :db.cardinality/one
:db/doc "Day of month has to be greater than or equal to this"} :db/doc "Day of month has to be greater than or equal to this"}
{:db/ident :transaction-rule/vendor
:db/valueType :db.type/ref
:db/cardinality :db.cardinality/one
:db/doc "The vendor to assign"}
{:db/ident :transaction-rule-account/percentage
:db/valueType :db.type/double
:db/cardinality :db.cardinality/one
:db/doc "How much should go to this account"}
{:db/ident :transaction-rule-account/location
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/doc "The location this split is for"}
{:db/ident :transaction-rule-account/account
:db/valueType :db.type/ref
:db/cardinality :db.cardinality/one
:db/doc "The account of this split"}
{:db/ident :transaction-rule/accounts
:db/valueType :db.type/ref
:db/cardinality :db.cardinality/many
:db/isComponent true
:db/doc "The outcome split"}
]]) ]])
(def add-credit-bank-account (def add-credit-bank-account

View File

@@ -11,7 +11,12 @@
(def default-read '[* (def default-read '[*
{:transaction-rule/client [:client/name :db/id :client/code]} {:transaction-rule/client [:client/name :db/id :client/code]}
{:transaction-rule/bank-account [*]} {:transaction-rule/bank-account [*]}
{:transaction-rule/yodlee-merchant [*]}]) {:transaction-rule/yodlee-merchant [*]}
{:transaction-rule/vendor [:vendor/name :db/id :vendor/default-account]}
{:transaction-rule/accounts [:transaction-rule-account/percentage
:transaction-rule-account/location
{:transaction-rule-account/account [*]}
:db/id]}])
(defn raw-graphql-ids [db args] (defn raw-graphql-ids [db args]
(let [query (cond-> {:query {:find [] (let [query (cond-> {:query {:find []

View File

@@ -47,7 +47,14 @@
:serialize (schema/as-conformer #(if (double? %) :serialize (schema/as-conformer #(if (double? %)
(str %) (str %)
%)) %))
}} }
:percentage {:parse (schema/as-conformer #(if (and (string? %)
(not (str/blank? %)))
(Double/parseDouble %)
%))
:serialize (schema/as-conformer #(if (double? %)
(str %)
%))}}
:objects :objects
{ {
:client :client
@@ -193,7 +200,8 @@
:amount_gte {:type 'String} :amount_gte {:type 'String}
:dom_lte {:type 'Int} :dom_lte {:type 'Int}
:dom_gte {:type 'Int} :dom_gte {:type 'Int}
:vendor {:type :vendor}}} :vendor {:type :vendor}
:accounts {:type '(list :percentage_account)}}}
:invoice_payment :invoice_payment
{:fields {:id {:type :id} {:fields {:id {:type :id}
@@ -226,6 +234,11 @@
:amount {:type 'String}}} :amount {:type 'String}}}
:percentage_account
{:fields {:id {:type :id}
:account {:type :account}
:location {:type 'String}
:percentage {:type :percentage}}}
:invoice :invoice
{:fields {:id {:type :id} {:fields {:id {:type :id}
:original_id {:type 'Int} :original_id {:type 'Int}
@@ -507,6 +520,12 @@
:vendor_id {:type :id} :vendor_id {:type :id}
:accounts {:type '(list :edit_expense_account)}}} :accounts {:type '(list :edit_expense_account)}}}
:edit_percentage_account
{:fields {:id {:type :id}
:account_id {:type :id}
:location {:type 'String}
:percentage {:type :percentage}}}
:edit_transaction_rule :edit_transaction_rule
{:fields {:id {:type :id} {:fields {:id {:type :id}
:description {:type 'String} :description {:type 'String}
@@ -516,7 +535,9 @@
:amount_lte {:type :money} :amount_lte {:type :money}
:amount_gte {:type :money} :amount_gte {:type :money}
:dom_lte {:type 'Int} :dom_lte {:type 'Int}
:dom_gte {:type 'Int}}} :dom_gte {:type 'Int}
:vendor_id {:type :id}
:accounts {:type '(list :edit_percentage_account)}}}
:edit_account :edit_account
{:fields {:id {:type :id} {:fields {:id {:type :id}

View File

@@ -4,7 +4,8 @@
[datomic.api :as d] [datomic.api :as d]
[auto-ap.datomic :refer [remove-nils uri merge-query]] [auto-ap.datomic :refer [remove-nils uri merge-query]]
[auto-ap.graphql.utils :refer [->graphql <-graphql limited-clients assert-admin result->page]] [auto-ap.graphql.utils :refer [->graphql <-graphql limited-clients assert-admin result->page]]
[clj-time.coerce :as c]) [clj-time.coerce :as c]
[clojure.set :as set])
(:import [java.time.temporal ChronoField])) (:import [java.time.temporal ChronoField]))
(defn get-transaction-rule-page [context args value] (defn get-transaction-rule-page [context args value]
@@ -12,9 +13,27 @@
[journal-entries journal-entries-count] (tr/get-graphql (<-graphql args))] [journal-entries journal-entries-count] (tr/get-graphql (<-graphql args))]
(result->page journal-entries journal-entries-count :transaction_rules args))) (result->page journal-entries journal-entries-count :transaction_rules args)))
(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]}]
(remove-nils #:transaction-rule-account {:percentage percentage
:db/id id
:account account_id
:location location}))
;; TODO ASSERT ADMIN ;; TODO ASSERT ADMIN
(defn upsert-transaction-rule [context {{:keys [id description note client_id bank_account_id amount_lte amount_gte ]} :transaction_rule :as z} value] (defn upsert-transaction-rule [context {{:keys [id description note client_id bank_account_id amount_lte amount_gte vendor_id accounts ]} :transaction_rule :as z} value]
(let [transaction [(remove-nils #:transaction-rule {:db/id (if id #_(assert-admin (:id context))
(let [existing-transaction (tr/get-by-id id)
deleted (deleted-accounts existing-transaction accounts)
transaction [(remove-nils #:transaction-rule {:db/id (if id
id id
"transaction-rule") "transaction-rule")
:description description :description description
@@ -22,8 +41,14 @@
:client client_id :client client_id
:bank-account bank_account_id :bank-account bank_account_id
:amount-lte amount_lte :amount-lte amount_lte
:amount-gte amount_gte})] :amount-gte amount_gte
_ (println transaction) :vendor vendor_id
:accounts (map transaction-rule-account->entity accounts)})]
transaction (into transaction
(map (fn [d]
[:db/retract id :transaction-rule/accounts d])
deleted))
transaction-result @(d/transact (d/connect uri) transaction)] transaction-result @(d/transact (d/connect uri) transaction)]
(-> (tr/get-by-id (or (-> transaction-result :tempids (get "transaction-rule")) (-> (tr/get-by-id (or (-> transaction-result :tempids (get "transaction-rule"))
id)) id))
@@ -33,7 +58,6 @@
(re-find (re-pattern z) x)) (re-find (re-pattern z) x))
(defn test-transaction-rule [{:keys [id]} {{:keys [description note client_id bank_account_id amount_lte amount_gte dom_lte dom_gte]} :transaction_rule :as z} value] (defn test-transaction-rule [{:keys [id]} {{:keys [description note client_id bank_account_id amount_lte amount_gte dom_lte dom_gte]} :transaction_rule :as z} value]
(prn z)
(->> (->>
(d/query (d/query
(cond-> {:query {:find ['(pull ?e [* {:transaction/client [:client/name] (cond-> {:query {:find ['(pull ?e [* {:transaction/client [:client/name]
@@ -95,6 +119,7 @@
:args [client_id]}) :args [client_id]})
true true
(merge-query {:query {:where ['[?e :transaction/id]]}}))) (merge-query {:query {:where ['[?e :transaction/id]]}})))
(transduce (comp (transduce (comp
(take 15) (take 15)
(map first) (map first)

View File

@@ -14,85 +14,124 @@
(d/delete-database uri)) (d/delete-database uri))
(use-fixtures :each wrap-setup) (use-fixtures :each wrap-setup)
(deftest query (deftest ledger-page
(testing "ledger" (testing "ledger"
(testing "it should find ledger entries" (testing "it should find ledger entries"
(let [result (:ledger-page (:data (sut/query nil "{ ledger_page(client_id: null) { count, start, journal_entries { id } }}")))] (let [result (:ledger-page (:data (sut/query nil "{ ledger_page(client_id: null) { count, start, journal_entries { id } }}")))]
(is (int? (:count result))) (is (int? (:count result)))
(is (int? (:start result))) (is (int? (:start result)))
(is (seqable? (:journal-entries result)))))) (is (seqable? (:journal-entries result)))))))
(testing "transaction-rules" (deftest transaction-rule-page
(testing "it should find rules" (testing "it should find rules"
(let [result (-> (sut/query nil "{ transaction_rule_page(client_id: null) { count, start, transaction_rules { id } }}") (let [result (-> (sut/query nil "{ transaction_rule_page(client_id: null) { count, start, transaction_rules { id } }}")
:data :data
:transaction-rule-page)] :transaction-rule-page)]
(is (int? (:count result))) (is (int? (:count result)))
(is (int? (:start result))) (is (int? (:start result)))
(is (seqable? (:transaction-rules result))))) (is (seqable? (:transaction-rules result))))))
(testing "it should add rules" (deftest upsert-transaction-rule
(let [q (v/graphql-query {:venia/operation {:operation/type :mutation (testing "it should add rules"
:operation/name "UpsertTransactionRule"} (let [{:strs [vendor-id account-id]} (-> (d/connect uri)
:venia/queries [{:query/data (sut/->graphql [:upsert-transaction-rule (d/transact
{:transaction-rule {:description "123"}} [{:vendor/name "Bryce's Meat Co"
[:id :description]])}]}) :db/id "vendor-id"}
result (-> (sut/query nil q) {:account/name "hello"
:data :db/id "account-id"}])
:upsert-transaction-rule)] deref
(is (= "123" (:description result))) :tempids)
(is (:id result)))) q (v/graphql-query {:venia/operation {:operation/type :mutation
:operation/name "UpsertTransactionRule"}
:venia/queries [{:query/data (sut/->graphql [:upsert-transaction-rule
{:transaction-rule {:description "123"
:vendor-id vendor-id
:accounts [{:account-id account-id
:percentage "0.5"
:location "B"}
{:account-id account-id
:percentage "0.5"
:location "A"}]}}
[:id :description
[:vendor [:name]]
[:accounts [:id :percentage [:account [:name]]]]]])}]})
result (-> (sut/query nil q)
:data
:upsert-transaction-rule)]
(is (= "123" (:description result)))
(is (= "Bryce's Meat Co" (-> result :vendor :name)))
(is (= "hello" (-> result :accounts (get 0) :account :name )))
(is (:id result))
(testing "it should delete removed rules"
(let [q (v/graphql-query {:venia/operation {:operation/type :mutation
:operation/name "UpsertTransactionRule"}
:venia/queries [{:query/data (sut/->graphql [:upsert-transaction-rule
{:transaction-rule {:id (:id result)
:description "123"
:vendor-id vendor-id
:accounts [{:id (-> result :accounts (get 0) :id)
:account-id account-id
:percentage "1.0"
:location "B"}]}}
[[:accounts [:id :percentage [:account [:name]]]]]])}]})
result (-> (sut/query nil q)
:data
:upsert-transaction-rule)]
(testing "it should match rules based on description regex" (is (= 1 (count (:accounts result)))))))))
(let [matching-transaction @(d/transact (d/connect uri)
[{:transaction/description-original "matching-desc"
:transaction/date #inst "2019-01-05T00:00:00.000-08:00"
:transaction/client {:client/name "1"
:db/id "client-1"}
:transaction/bank-account {:db/id "bank-account-1"
:bank-account/name "1"}
:transaction/amount 1.00 (deftest test-transaction-rule
:transaction/id "2019-01-05 matching-desc 1"} (testing "it should match rules"
(let [matching-transaction @(d/transact (d/connect uri)
[{:transaction/description-original "matching-desc"
:transaction/date #inst "2019-01-05T00:00:00.000-08:00"
:transaction/client {:client/name "1"
:db/id "client-1"}
:transaction/bank-account {:db/id "bank-account-1"
:bank-account/name "1"}
{:transaction/description-original "nonmatching-desc" :transaction/amount 1.00
:transaction/client {:client/name "2" :transaction/id "2019-01-05 matching-desc 1"}
:db/id "client-2"}
:transaction/bank-account {:db/id "bank-account-2"
:bank-account/name "2"}
:transaction/date #inst "2019-01-15T23:23:00.000-08:00"
:transaction/amount 2.00
:transaction/id "2019-01-15 nonmatching-desc 2"}])
{:strs [client-1 client-2 bank-account-1 bank-account-2]} (get-in matching-transaction [:tempids])
rule-test (fn [rule] {:transaction/description-original "nonmatching-desc"
(-> (sut/query nil (v/graphql-query {:venia/operation {:operation/type :query :transaction/client {:client/name "2"
:operation/name "TestTransactionRule"} :db/id "client-2"}
:venia/queries [{:query/data (sut/->graphql [:test-transaction-rule :transaction/bank-account {:db/id "bank-account-2"
{:transaction-rule rule} :bank-account/name "2"}
[:id]])}]})) :transaction/date #inst "2019-01-15T23:23:00.000-08:00"
:data :transaction/amount 2.00
:test-transaction-rule))] :transaction/id "2019-01-15 nonmatching-desc 2"}])
(testing "based on date " {:strs [client-1 client-2 bank-account-1 bank-account-2]} (get-in matching-transaction [:tempids])
(is (= [{:id "2019-01-15 nonmatching-desc 2"}] (rule-test {:dom-gte 14 :dom-lte 16})))
(is (= [{:id "2019-01-15 nonmatching-desc 2"}] (rule-test {:dom-gte 14})))
(is (= [{:id "2019-01-05 matching-desc 1"} {:id "2019-01-15 nonmatching-desc 2"}] (rule-test {:dom-lte 15})))
(is (= [{:id "2019-01-15 nonmatching-desc 2"}] (rule-test {:dom-gte 15})))
(is (= [{:id "2019-01-15 nonmatching-desc 2"}] (rule-test {:dom-gte 15 :dom-lte 15}))))
(testing "based on description" rule-test (fn [rule]
(is (= [{:id "2019-01-05 matching-desc 1"}] (rule-test {:description "^match"})))) (-> (sut/query nil (v/graphql-query {:venia/operation {:operation/type :query
:operation/name "TestTransactionRule"}
:venia/queries [{:query/data (sut/->graphql [:test-transaction-rule
{:transaction-rule rule}
[:id]])}]}))
:data
:test-transaction-rule))]
(testing "based on date "
(is (= [{:id "2019-01-15 nonmatching-desc 2"}] (rule-test {:dom-gte 14 :dom-lte 16})))
(is (= [{:id "2019-01-15 nonmatching-desc 2"}] (rule-test {:dom-gte 14})))
(is (= [{:id "2019-01-05 matching-desc 1"} {:id "2019-01-15 nonmatching-desc 2"}] (rule-test {:dom-lte 15})))
(is (= [{:id "2019-01-15 nonmatching-desc 2"}] (rule-test {:dom-gte 15})))
(is (= [{:id "2019-01-15 nonmatching-desc 2"}] (rule-test {:dom-gte 15 :dom-lte 15}))))
(testing "based on amount" (testing "based on description"
(is (= [{:id "2019-01-05 matching-desc 1"}] (rule-test {:amount-gte 1.0 :amount-lte 1.0}))) (is (= [{:id "2019-01-05 matching-desc 1"}] (rule-test {:description "^match"}))))
(is (= [{:id "2019-01-05 matching-desc 1"} {:id "2019-01-15 nonmatching-desc 2"}] (rule-test {:amount-gte 1.0 })) )
(is (= [{:id "2019-01-05 matching-desc 1"} {:id "2019-01-15 nonmatching-desc 2"}] (rule-test {:amount-lte 2.0 })) ))
(testing "based on client" (testing "based on amount"
(is (= [{:id "2019-01-05 matching-desc 1"}] (rule-test {:client-id client-1}))) (is (= [{:id "2019-01-05 matching-desc 1"}] (rule-test {:amount-gte 1.0 :amount-lte 1.0})))
(is (= [{:id "2019-01-15 nonmatching-desc 2"}] (rule-test {:client-id client-2})))) (is (= [{:id "2019-01-05 matching-desc 1"} {:id "2019-01-15 nonmatching-desc 2"}] (rule-test {:amount-gte 1.0 })) )
(is (= [{:id "2019-01-05 matching-desc 1"} {:id "2019-01-15 nonmatching-desc 2"}] (rule-test {:amount-lte 2.0 })) ))
(testing "based on bank account" (testing "based on client"
(is (= [{:id "2019-01-05 matching-desc 1"}] (rule-test {:bank-account-id bank-account-1}))) (is (= [{:id "2019-01-05 matching-desc 1"}] (rule-test {:client-id client-1})))
(is (= [{:id "2019-01-15 nonmatching-desc 2"}] (rule-test {:bank-account-id bank-account-2})))))))) (is (= [{:id "2019-01-15 nonmatching-desc 2"}] (rule-test {:client-id client-2}))))
(testing "based on bank account"
(is (= [{:id "2019-01-05 matching-desc 1"}] (rule-test {:bank-account-id bank-account-1})))
(is (= [{:id "2019-01-15 nonmatching-desc 2"}] (rule-test {:bank-account-id bank-account-2})))))))