diff --git a/src/clj/auto_ap/graphql.clj b/src/clj/auto_ap/graphql.clj index ed6aacc3..1b2d12bb 100644 --- a/src/clj/auto_ap/graphql.clj +++ b/src/clj/auto_ap/graphql.clj @@ -40,7 +40,12 @@ (def integreat-schema { - :scalars {:id {:parse #(when % (Long/parseLong %)) + :scalars {:id {:parse #(cond (number? %) + % + + % + (Long/parseLong %)) + :serialize #(.toString %)} :ident {:parse (fn [x] {:db/ident x}) :serialize #(or (:ident %) (:db/ident %) %)} diff --git a/src/clj/auto_ap/import/transactions.clj b/src/clj/auto_ap/import/transactions.clj index 55449b19..69fcc5c4 100644 --- a/src/clj/auto_ap/import/transactions.clj +++ b/src/clj/auto_ap/import/transactions.clj @@ -196,46 +196,57 @@ :else :import))) +(defn maybe-assoc-check-number [transaction] + (if-let [check-number (or (:transaction/check-number transaction) + (extract-check-number transaction))] + (assoc transaction :transaction/check-number transaction) + transaction)) + + +(defn maybe-clear-payment [{:transaction/keys [check-number client bank-account amount id] :as transaction}] + (when-let [existing-payment (transaction->existing-payment transaction check-number client bank-account amount id)] + (assoc transaction + :transaction/approval-status :transaction-approval-status/approved + :transaction/payment {:db/id (:db/id existing-payment) + :payment/status :payment-status/cleared} + :transaction/vendor (:db/id (:payment/vendor existing-payment)) + :transaction/location "A" + :transaction/accounts [#:transaction-account + {:account (:db/id (a/get-account-by-numeric-code-and-sets 21000 ["default"])) + :location "A" + :amount (Math/abs (double amount))}]))) + +(defn maybe-autopay-invoices [{:transaction/keys [amount client bank-account] :as transaction}] + (when-let [autopay-invoices-matches (seq (match-transaction-to-unfulfilled-autopayments amount client))] + (add-new-payment autopay-invoices-matches bank-account client))) + +(defn maybe-clear-expected-deposit [{:transaction/keys [amount client date] :as transaction}] + (when (>= amount 0.0) + (when-let [expected-deposit (find-expected-deposit client amount (coerce/to-date-time date))] + (assoc transaction :transaction/expected-deposit {:db/id expected-deposit + :expected-deposit/status :expected-deposit-status/cleared})))) + +(defn maybe-code [{:transaction/keys [client amount] :as transaction} apply-rules valid-locations] + (if-let [unpaid-invoices (seq (match-transaction-to-unpaid-invoices amount client))] + nil + (apply-rules transaction valid-locations))) + (defn transaction->txs [transaction bank-account apply-rules] (let [bank-account-id (:db/id bank-account) client (:client/_bank-accounts bank-account) client-id (:db/id client) - valid-locations (or (:bank-account/locations bank-account) (:client/locations client))] - (into [] - (let [{:transaction/keys [amount id date]} transaction - check-number (extract-check-number transaction) - existing-check (transaction->existing-payment transaction check-number client-id bank-account-id amount id) - autopay-invoices-matches (when-not existing-check - (match-transaction-to-unfulfilled-autopayments amount client-id)) - unpaid-invoices-matches (when-not existing-check - (match-transaction-to-unpaid-invoices amount client-id )) - expected-deposit (when (and (> amount 0.0) - (not existing-check)) - (find-expected-deposit (:db/id client) amount (coerce/to-date-time date)))] - (cond-> - [(assoc transaction :transaction/approval-status :transaction-approval-status/unapproved)] - check-number (update 0 #(assoc % :transaction/check-number check-number)) - existing-check (update 0 #(assoc % :transaction/approval-status :transaction-approval-status/approved - :transaction/payment {:db/id (:db/id existing-check) - :payment/status :payment-status/cleared} - :transaction/vendor (:db/id (:payment/vendor existing-check)) - :transaction/location "A" - :transaction/accounts [#:transaction-account - {:account (:db/id (a/get-account-by-numeric-code-and-sets 21000 ["default"])) - :location "A" - :amount (Math/abs (double amount))}])) - - ;; temporarily removed to automatically match autopaid invoices - #_(and (not existing-check) - (seq autopay-invoices-matches)) #_(add-new-payment autopay-invoices-matches bank-account-id client-id) - expected-deposit (update 0 #(assoc % :transaction/expected-deposit {:db/id expected-deposit - :expected-deposit/status :expected-deposit-status/cleared})) - - - (and (not (seq autopay-invoices-matches)) - (not (seq unpaid-invoices-matches)) - (not expected-deposit)) (update 0 #(apply-rules % valid-locations)) - true (update 0 remove-nils)))))) + valid-locations (or (:bank-account/locations bank-account) (:client/locations client)) + code-fn (some-fn maybe-clear-payment + maybe-clear-expected-deposit + #_maybe-autopay-invoices + #(maybe-code % apply-rules valid-locations) + identity)] + [(-> transaction + (assoc :transaction/client client-id) + (assoc :transaction/bank-account bank-account-id) + (assoc :transaction/approval-status :transaction-approval-status/unapproved) + maybe-assoc-check-number + code-fn)])) diff --git a/test/clj/auto_ap/functional/test.clj b/test/clj/auto_ap/functional/test.clj deleted file mode 100644 index 21ce7b91..00000000 --- a/test/clj/auto_ap/functional/test.clj +++ /dev/null @@ -1,52 +0,0 @@ -(ns auto-ap.functional.test - (:require [clojure.test :as t :refer :all] - [etaoin.api :as e] - [etaoin.keys :as k] - [config.core :refer [env]] - [clj-time.core :as time] - [buddy.sign.jwt :as jwt])) - -(def base-url "https://staging3.app.integreatconsult.com") -(defn login-admin [driver] - (let [jwt-token (jwt/sign {:user "Automated Tests" - :exp (time/plus (time/now) (time/days 30)) - :user/role "admin" - :user/name "Automated Tests"} - (:jwt-secret env) - {:alg :hs512})] - (e/go driver (str base-url "/?jwt=" jwt-token)) - (e/wait-visible driver {:tag :h1 - :class "title"}))) - -(deftest create-invoice - (testing "Creating a new invoice" - (e/with-wait-timeout 10 - (e/with-firefox {} driver - (login-admin driver) - (e/click driver {:tag :a :fn/text "Invoices"}) - (e/wait-visible driver {:tag :h1 - :class "title" - :fn/text "Unpaid Invoices"}))))) - - -(deftest edit-client - (testing "Editing a client" - (e/with-wait-timeout 10 - (e/with-firefox {} driver - (login-admin driver) - - (e/click driver {:tag :a :class "navbar-link login"}) - (e/click driver {:tag :a :fn/text "Administration"}) - - (e/wait-visible driver {:tag :h1 - :class "title" - :fn/text "Admin"}) - (e/click driver {:tag :span :fn/text "Clients"}) - (e/wait-visible driver {:tag :h1 - :class "title" - :fn/text "Clients"}) - (e/click driver {:tag :i :class "fa fa-pencil"}) - (e/wait-visible driver {:tag :button - :fn/text "Save"}) - (e/click driver {:tag :button :fn/text "Save"}) - (e/wait-invisible driver {:tag :form}))))) diff --git a/test/clj/auto_ap/import/transactions_test.clj b/test/clj/auto_ap/import/transactions_test.clj index 20e7cb7a..247abafe 100644 --- a/test/clj/auto_ap/import/transactions_test.clj +++ b/test/clj/auto_ap/import/transactions_test.clj @@ -83,7 +83,10 @@ result (sut/transaction->txs base-transaction (d/entity (d/db conn) bank-account-id) noop-rule)] - (t/is (= [(assoc base-transaction :transaction/approval-status :transaction-approval-status/unapproved)] + (t/is (= [(assoc base-transaction + :transaction/approval-status :transaction-approval-status/unapproved + :transaction/bank-account bank-account-id + :transaction/client client-id)] result)))) (t/testing "Should match an uncleared check" @@ -113,6 +116,18 @@ :payment/status :payment-status/cleared} (:transaction/payment transaction-result)))) + + (t/testing "Should match a check that matches on amount if check number does not match" + (let [[transaction-result] (sut/transaction->txs (assoc base-transaction + :transaction/description-original "CHECK 12301" + :transaction/amount -30.0) + (d/entity (d/db conn ) bank-account-id) + noop-rule)] + + (t/is (= {:db/id payment-id + :payment/status :payment-status/cleared} + (:transaction/payment transaction-result))))) + (t/testing "Should not match an already matched check" @(d/transact (d/connect uri) [{:db/id payment-id :payment/status :payment-status/cleared}]) (let [[result] (sut/transaction->txs (assoc base-transaction diff --git a/test/clj/auto_ap/integration/graphql.clj b/test/clj/auto_ap/integration/graphql.clj index 2c388d9b..5a330f20 100644 --- a/test/clj/auto_ap/integration/graphql.clj +++ b/test/clj/auto_ap/integration/graphql.clj @@ -39,11 +39,13 @@ (defn new-transaction [args] (merge {:transaction/amount 100.0 + :transaction/date #inst "2021-01-01" :transaction/id (.toString (java.util.UUID/randomUUID))} args)) (defn new-invoice [args] (merge {:invoice/total 100.0 + :invoice/date #inst "2021-01-01" :invoice/invoice-number (.toString (java.util.UUID/randomUUID))} args)) @@ -88,7 +90,7 @@ (deftest ledger-page (testing "ledger" (testing "it should find ledger entries" - (let [result (:ledger-page (:data (sut/query (admin-token) "{ ledger_page(client_id: null) { count, start, journal_entries { id } }}")))] + (let [result (:ledger-page (:data (sut/query (admin-token) "{ ledger_page(filters: {client_id: null}) { count, start, journal_entries { id } }}")))] (is (int? (:count result))) (is (int? (:start result))) (is (seqable? (:journal-entries result))))))) @@ -97,7 +99,7 @@ (deftest vendors (testing "vendors" (testing "it should find vendors" - (let [result (:ledger-page (:data (sut/query (admin-token) "{ ledger_page(client_id: null) { count, start, journal_entries { id } }}")))] + (let [result (:ledger-page (:data (sut/query (admin-token) "{ ledger_page(filters: {client_id: null}) { count, start, journal_entries { id } }}")))] (is (int? (:count result))) (is (int? (:start result))) (is (seqable? (:journal-entries result))))))) @@ -128,7 +130,7 @@ :venia/queries [{:query/data (sut/->graphql [:upsert-transaction-rule {:transaction-rule {:accounts [{:account-id account-id :percentage "0.25" - :location "B"}]}} + :location "Shared"}]}} [:id ]])}]})] (is (thrown? clojure.lang.ExceptionInfo (sut/query (admin-token) q))))) @@ -139,7 +141,7 @@ :venia/queries [{:query/data (sut/->graphql [:upsert-transaction-rule {:transaction-rule {:accounts [{:account-id account-id :percentage "1.0" - :location "B"}]}} + :location "Shared"}]}} [:id ]])}]})] (is (thrown? clojure.lang.ExceptionInfo (sut/query (admin-token) q))))) (testing "it should add rules" @@ -152,10 +154,10 @@ :transaction-approval-status :approved :accounts [{:account-id account-id :percentage "0.5" - :location "B"} + :location "Shared"} {:account-id account-id :percentage "0.5" - :location "A"}]}} + :location "Shared"}]}} [:id :description :transaction-approval-status [:vendor [:name]] @@ -182,7 +184,7 @@ :accounts [{:id (-> result :accounts (get 0) :id) :account-id account-id :percentage "1.0" - :location "B"}]}} + :location "Shared"}]}} [[:vendor [:name]]]])}]}) result (-> (sut/query (admin-token) q) :data @@ -200,7 +202,7 @@ :accounts [{:id (-> result :accounts (get 0) :id) :account-id account-id :percentage "1.0" - :location "B"}]}} + :location "Shared"}]}} [[:accounts [:id :percentage [:account [:name]]]]]])}]}) result (-> (sut/query (admin-token) q) :data @@ -208,22 +210,6 @@ (is (= 1 (count (:accounts result)))))))))) -(deftest test-get-yodlee-merchants - (testing "it should find yodlee merchants" - @(d/transact (d/connect uri) - [{:yodlee-merchant/name "Merchant 1" - :yodlee-merchant/yodlee-id "123"} - {:yodlee-merchant/name "Merchant 2" - :yodlee-merchant/yodlee-id "456"}]) - - (is (= [{:name "Merchant 1" :yodlee-id "123"} {:name "Merchant 2" :yodlee-id "456"}] - (-> (sut/query (admin-token) (v/graphql-query {:venia/operation {:operation/type :query - :operation/name "GetYodleeMerchants"} - :venia/queries [{:query/data (sut/->graphql [:yodlee-merchants - [:yodlee-id :name]])}]})) - :data - :yodlee-merchants))))) - (deftest test-transaction-rule (testing "it should match rules" @@ -236,7 +222,8 @@ :bank-account/name "1"} :transaction/amount 1.00 - :transaction/id "2019-01-05 matching-desc 1"} + :transaction/id "2019-01-05 matching-desc 1" + :db/id "a"} {:transaction/description-original "nonmatching-desc" :transaction/client {:client/name "2" @@ -245,8 +232,11 @@ :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]) + :transaction/id "2019-01-15 nonmatching-desc 2" + :db/id "b"}]) + {:strs [a b client-1 client-2 bank-account-1 bank-account-2]} (get-in matching-transaction [:tempids]) + a (str a) + b (str b) rule-test (fn [rule] (-> (sut/query (admin-token) (v/graphql-query {:venia/operation {:operation/type :query @@ -257,27 +247,27 @@ :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 (= (set [{:id "2019-01-05 matching-desc 1"} {:id "2019-01-15 nonmatching-desc 2"}]) (set (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})))) + (is (= [{:id b}] (rule-test {:dom-gte 14 :dom-lte 16}))) + (is (= [{:id b}] (rule-test {:dom-gte 14}))) + (is (= (set [{:id a} {:id b}]) (set (rule-test {:dom-lte 15})))) + (is (= [{:id b}] (rule-test {:dom-gte 15}))) + (is (= [{:id b}] (rule-test {:dom-gte 15 :dom-lte 15})))) (testing "based on description" - (is (= [{:id "2019-01-05 matching-desc 1"}] (rule-test {:description "^match"})))) + (is (= [{:id a}] (rule-test {:description "^match"})))) (testing "based on amount" - (is (= [{:id "2019-01-05 matching-desc 1"}] (rule-test {:amount-gte 1.0 :amount-lte 1.0}))) - (is (= (set [{:id "2019-01-05 matching-desc 1"} {:id "2019-01-15 nonmatching-desc 2"}]) (set (rule-test {:amount-gte 1.0 }))) ) - (is (= (set [{:id "2019-01-05 matching-desc 1"} {:id "2019-01-15 nonmatching-desc 2"}]) (set (rule-test {:amount-lte 2.0 }))) )) + (is (= [{:id a}] (rule-test {:amount-gte 1.0 :amount-lte 1.0}))) + (is (= (set [{:id a} {:id b}]) (set (rule-test {:amount-gte 1.0 }))) ) + (is (= (set [{:id a} {:id b}]) (set (rule-test {:amount-lte 2.0 }))) )) (testing "based on client" - (is (= [{:id "2019-01-05 matching-desc 1"}] (rule-test {:client-id client-1}))) - (is (= [{:id "2019-01-15 nonmatching-desc 2"}] (rule-test {:client-id client-2})))) + (is (= [{:id a}] (rule-test {:client-id (str client-1)}))) + (is (= [{:id b}] (rule-test {:client-id (str 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}))))))) + (is (= [{:id a}] (rule-test {:bank-account-id (str bank-account-1)}))) + (is (= [{:id b}] (rule-test {:bank-account-id (str bank-account-2)}))))))) (deftest test-match-transaction-rule (testing "it should apply a rules"