From 70714a2de17fae3b32c10254abe916c1b264167e Mon Sep 17 00:00:00 2001 From: Bryce Covert Date: Wed, 15 May 2019 07:12:56 -0700 Subject: [PATCH] kind of applies rules. --- src/clj/auto_ap/yodlee/import.clj | 83 ++++++++++++++++++------------ test/clj/auto_ap/yodlee/import.clj | 68 ++++++++++++++++++++++-- 2 files changed, 114 insertions(+), 37 deletions(-) diff --git a/src/clj/auto_ap/yodlee/import.clj b/src/clj/auto_ap/yodlee/import.clj index d8b636c0..3e8d3a45 100644 --- a/src/clj/auto_ap/yodlee/import.clj +++ b/src/clj/auto_ap/yodlee/import.clj @@ -50,7 +50,7 @@ nil)) nil)) -(defn transactions->txs [transactions transaction->client transaction->bank-account-id] +(defn transactions->txs [transactions transaction->client transaction->bank-account-id apply-rules] (into [] (for [transaction transactions @@ -75,36 +75,40 @@ bank-account-id (transaction->bank-account-id transaction) check (transaction->payment transaction check-number client-id bank-account-id amount id)] :when client-id] - (->> (remove-nils #:transaction - {:post-date (coerce/to-date (time/parse post-date "YYYY-MM-dd")) - :id (sha-256 (str id)) - :account-id account-id - :date (coerce/to-date (time/parse date "YYYY-MM-dd")) - :yodlee-merchant (when (and merchant-id merchant-name) - {:yodlee-merchant/yodlee-id merchant-id - :yodlee-merchant/name merchant-name}) - :amount (double amount) - :description-original description-original - :description-simple description-simple - :exclude-from-ledger false - :type type - :status status - :client client-id - :check-number check-number - :bank-account (transaction->bank-account-id transaction) - :payment (when check - {:db/id (:db/id check) - :payment/status :payment-status/cleared} - ) + (-> + #:transaction + {:post-date (coerce/to-date (time/parse post-date "YYYY-MM-dd")) + :id (sha-256 (str id)) + :account-id account-id + :date (coerce/to-date (time/parse date "YYYY-MM-dd")) + :yodlee-merchant (when (and merchant-id merchant-name) + {:yodlee-merchant/yodlee-id merchant-id + :yodlee-merchant/name merchant-name}) + :amount (double amount) + :description-original description-original + :description-simple description-simple + :exclude-from-ledger false + :type type + :status status + :client client-id + :check-number check-number + :bank-account (transaction->bank-account-id transaction) + :payment (when check + {:db/id (:db/id check) + :payment/status :payment-status/cleared} + ) - :vendor (when check - (:db/id (:payment/vendor check))) - :location (when check - "A") - :accounts (when check - [#:transaction-account {:account (:db/id (a/get-account-by-numeric-code-and-sets 2110 ["default"])) - :location "A" - :amount (Math/abs (double amount))}])}))))) + :vendor (when check + (:db/id (:payment/vendor check))) + :location (when check + "A") + :accounts (when check + [#:transaction-account {:account (:db/id (a/get-account-by-numeric-code-and-sets 2110 ["default"])) + :location "A" + :amount (Math/abs (double amount))}])} + + remove-nils + apply-rules)))) (defn batch-transact [transactions] @@ -117,6 +121,21 @@ [] (partition-all 100 transactions))) +(defn rule-applies? [transaction rule] + (re-find (:transaction-rule/description rule) (:transaction/description-original transaction))) + +(defn rule-applying-fn [rules] + (let [rules (transduce (map (fn [r] (update r :transaction-rule/description #(some-> % re-pattern)))) + conj + [] + rules)] + + (fn [transaction] + (let [matching-rules (filter #(rule-applies? transaction %) rules)] + (if-let [top-match (first matching-rules)] + (assoc transaction :transaction/approval-status (:transaction-rule/transaction-approval-status (first matching-rules))) + transaction))))) + (defn manual-import [manual-transactions] (let [transformed-transactions (->> manual-transactions (filter #(= "posted" (:status %))) @@ -137,7 +156,7 @@ transaction-group))))] (println "importing manual transactions" transformed-transactions) (batch-transact - (transactions->txs transformed-transactions :client-id :bank-account-id)))) + (transactions->txs transformed-transactions :client-id :bank-account-id (rule-applying-fn []))))) @@ -155,5 +174,5 @@ (d-clients/get-all)) transaction->client (comp (by :yodlee-account-id :client-id all-bank-accounts) :accountId) transaction->bank-account-id (comp (by :yodlee-account-id :bank-account-id all-bank-accounts) :accountId)] - (batch-transact (transactions->txs transactions transaction->client transaction->bank-account-id))))) + (batch-transact (transactions->txs transactions transaction->client transaction->bank-account-id (rule-applying-fn [])))))) diff --git a/test/clj/auto_ap/yodlee/import.clj b/test/clj/auto_ap/yodlee/import.clj index d91c8afd..87d94dd2 100644 --- a/test/clj/auto_ap/yodlee/import.clj +++ b/test/clj/auto_ap/yodlee/import.clj @@ -34,7 +34,8 @@ (t/testing "Should import single transaction" (let [result (sut/transactions->txs [base-transaction] :client-id - :bank-account-id)] + :bank-account-id + identity)] (t/is (= [#:transaction {:amount -12.0 :date #inst "2014-01-02T08:00:00.000-00:00" :bank-account 456 @@ -53,7 +54,8 @@ (t/testing "Should skip transaction if no client is found" (let [result (sut/transactions->txs [(assoc base-transaction :client-id nil)] :client-id - :bank-account-id)] + :bank-account-id + identity)] (t/is (= [] result)))) (t/testing "Should match an uncleared check" @@ -81,7 +83,8 @@ :client-id client-id :bank-account-id bank-account-id)] :client-id - :bank-account-id)] + :bank-account-id + identity)] (t/is (= {:db/id payment-id :payment/status :payment-status/cleared} @@ -97,7 +100,62 @@ :client-id client-id :bank-account-id bank-account-id)] :client-id - :bank-account-id)] + :bank-account-id + identity)] (t/is (= nil - (:transaction/payment result))))))))) + (:transaction/payment result))))))) + + + (t/testing "Rules" + (t/testing "Should apply rules to imported transaction" + (let [{:strs [bank-account-id client-id payment-id]} (->> [#:payment {:status :payment-status/pending + :date #inst "2019-01-01" + :bank-account "bank-account-id" + :client "client-id" + :check-number 10001 + :amount 30.0 + :db/id "payment-id"} + #:bank-account {:name "Bank account" + :db/id "bank-account-id"} + #:client {:name "Client" + :db/id "client-id" + :bank-accounts ["bank-account-id"]}] + (d/transact (d/connect uri)) + deref + :tempids) + [result] (sut/transactions->txs [(assoc base-transaction + :description {:original "Hello XXX039" + :simple ""} + :amount {:amount 30.0} + :id 789 + :client-id client-id + :bank-account-id bank-account-id)] + :client-id + :bank-account-id + (sut/rule-applying-fn [{:transaction-rule/description "XXX039" + :transaction-rule/transaction-approval-status :transaction-approval-status/approved}]))] + + (t/is (= :transaction-approval-status/approved + (:transaction/approval-status result))))) + + (t/testing "Should match if description matches" + (let [apply-rules (sut/rule-applying-fn [{:transaction-rule/description "XXX039" + :transaction-rule/transaction-approval-status :transaction-approval-status/approved + :transaction-rule/vendor {:db/id 123}} + {:transaction-rule/description "OtherMatch" + :transaction-rule/transaction-approval-status :transaction-approval-status/requires-feedback + :transaction-rule/vendor {:db/id 456}}])] + (t/is (= {:transaction/description-original "Hello XXX039", + :transaction/approval-status :transaction-approval-status/approved} + + (-> {:transaction/description-original "Hello XXX039"} + apply-rules))) + (t/is (= {:transaction/description-original "OtherMatch", + :transaction/approval-status :transaction-approval-status/requires-feedback} + (-> {:transaction/description-original "OtherMatch"} + apply-rules))) + (t/is (= {:transaction/description-original "Hello Not match"} + (-> {:transaction/description-original "Hello Not match"} + apply-rules + ))))))))