diff --git a/src/clj/auto_ap/graphql/transaction_rules.clj b/src/clj/auto_ap/graphql/transaction_rules.clj index 7831ee50..2cf7758e 100644 --- a/src/clj/auto_ap/graphql/transaction_rules.clj +++ b/src/clj/auto_ap/graphql/transaction_rules.clj @@ -1,7 +1,7 @@ (ns auto-ap.graphql.transaction-rules (:require [auto-ap.datomic :refer - [audit-transact merge-query remove-nils replace-nils-with-retract uri]] + [audit-transact merge-query remove-nils replace-nils-with-retract uri conn]] [auto-ap.datomic.transaction-rules :as tr] [auto-ap.datomic.transactions :as d-transactions] [auto-ap.graphql.utils @@ -74,6 +74,19 @@ (nil? yodlee_merchant_id)) (let [error (str "You must provide a description or a yodlee merchant")] (throw (ex-info error {:validation-error error})))) + _ (doseq [a accounts + :let [{:keys [:account/location :account/name] :as account} (d/entity (d/db conn) (:account_id a)) + client (d/entity (d/db conn) client_id) + ]] + (when (and location (not= location (:location a))) + (let [err (str "Account " name " uses location " (:location a) ", but is supposed to be " location)] + (throw (ex-info err {:validation-error err}) ))) + + (when (not (get (into #{"Shared"} (:client/locations client)) + (:location a))) + (let [err (str "Account " name " uses location " (:location a) ", but doesn't belong to the client " location)] + (throw (ex-info err {:validation-error err}) ))) + ) rule-id (if id id "transaction-rule") @@ -116,7 +129,7 @@ ])] :in ['$ ] :where []} - :args [(d/db (d/connect uri))]} + :args [(d/db conn)]} (limited-clients id) (merge-query {:query {:in ['[?xx ...]] diff --git a/src/clj/auto_ap/ledger.clj b/src/clj/auto_ap/ledger.clj index 23ca419a..95f31efc 100644 --- a/src/clj/auto_ap/ledger.clj +++ b/src/clj/auto_ap/ledger.clj @@ -201,8 +201,108 @@ (catch Exception e (log/error e))))) -(mount/defstate reconciliation-frequency :start 60000) +(mount/defstate reconciliation-frequency :start (* 1000 60 60)) (mount/defstate ledger-reconciliation-worker :start (scheduler/every reconciliation-frequency reconcile-ledger) :stop (scheduler/stop ledger-reconciliation-worker)) + + +(defn touch-transaction [e] + @(d/transact conn [[:db/retractEntity [:journal-entry/original-entity e]]]) + @(d/transact conn [{:db/id "datomic.tx" + :db/doc "touching transaction to update ledger"} + (entity-change->ledger (d/db conn) + [:transaction e])])) + +(defn touch-invoice [e] + @(d/transact conn [[:db/retractEntity [:journal-entry/original-entity e]]]) + @(d/transact conn [{:db/id "datomic.tx" + :db/doc "touching invoice to update ledger"} + (entity-change->ledger (d/db conn) + [:invoice e])])) +(defn mismatched-transactions [] + (let [jel-accounts (reduce + (fn [acc [e lia]] + (update acc e (fnil conj #{} ) lia)) + {} + (d/query {:query {:find ['?e '?lia] + :in ['$] + :where ['[?je :journal-entry/line-items ?li] + '[?je :journal-entry/original-entity ?e] + '[?li :journal-entry-line/account ?lia] + '[?lia :account/name]]} + :args [(d/db auto-ap.datomic/conn)]})) + transaction-accounts (reduce + (fn [acc [e lia]] + (update acc e (fnil conj #{} ) lia)) + {} + (d/query {:query {:find ['?e '?lia] + :in ['$] + :where ['[?e :transaction/accounts ?li] + '(not [?e :transaction/approval-status :transaction-approval-status/excluded]) + '[?li :transaction-account/account ?lia] + + '[?lia :account/name]]} + :args [(d/db auto-ap.datomic/conn)]})) + ] + (filter + (fn [[e accounts]] (not= accounts (get jel-accounts e))) + transaction-accounts))) + +(defn mismatched-invoices [] + (let [jel-accounts (reduce + (fn [acc [e lia]] + (update acc e (fnil conj #{} ) lia)) + {} + (d/query {:query {:find ['?e '?lia] + :in ['$] + :where ['[?je :journal-entry/line-items ?li] + '[?je :journal-entry/original-entity ?e] + '[?li :journal-entry-line/account ?lia] + '(not [?lia :account/numeric-code 21000]) + '[?lia :account/name]]} + :args [(d/db auto-ap.datomic/conn)]})) + invoice-accounts (reduce + (fn [acc [e lia]] + (update acc e (fnil conj #{} ) lia)) + {} + (d/query {:query {:find ['?e '?lia] + :in ['$] + :where ['[?e :invoice/expense-accounts ?li] + '[?li :invoice-expense-account/account ?lia] + '[?lia :account/name] + '(not [?lia :account/numeric-code 21000]) + '(not [?e :invoice/status :invoice-status/voided]) + '(not [?e :invoice/exclude-from-ledger true]) + '[?e :invoice/import-status :import-status/imported]]} + :args [(d/db auto-ap.datomic/conn)]})) + ] + (filter + (fn [[e accounts]] (not= accounts (get jel-accounts e))) + invoice-accounts))) + +(defn touch-broken-ledger [] + (lc/with-context {:source "touch-broken-ledger"} + (try + (log/info "Attempting to fix transactions that are in the ledger but are wrong") + (let [mismatched-ts (mismatched-transactions)] + (if (seq mismatched-ts) + (do + (log/warn (count mismatched-ts) " transactions exist but don't match ledger ") + (doseq [[m] mismatched-ts] + (touch-transaction m))))) + (log/info "Finished fixing transactions that are in the ledger but are wrong") + (let [mismatched-is (mismatched-invoices)] + (if (seq mismatched-is) + (do + (log/warn (count mismatched-is) " invoice exist but don't match ledger ") + (doseq [[m] mismatched-is] + (touch-invoice m))))) + (log/info "Finished fixing invoices that are in the ledger but are wrong") + (catch Exception e + (log/error e))))) + +(mount/defstate touch-broken-ledger-worker + :start (scheduler/every reconciliation-frequency touch-broken-ledger) + :stop (scheduler/stop touch-broken-ledger-worker)) diff --git a/src/clj/user.clj b/src/clj/user.clj index ca9dbfab..f0cb058d 100644 --- a/src/clj/user.clj +++ b/src/clj/user.clj @@ -393,3 +393,37 @@ (defn start-db [] (mount.core/start (mount.core/only #{#'auto-ap.datomic/conn}))) + +(defn touch-transaction-ledger [e] + @(d/transact auto-ap.datomic/conn [[:db/retractEntity [:journal-entry/original-entity e]]]) + @(d/transact auto-ap.datomic/conn [(auto-ap.ledger/entity-change->ledger (d/db auto-ap.datomic/conn) + [:transaction e])])) + +(defn mismatched-transactions [] + (let [jel-accounts (reduce + (fn [acc [e lia]] + (update acc e (fnil conj #{} ) lia)) + {} + (d/query {:query {:find ['?e '?lia] + :in ['$] + :where ['[?je :journal-entry/line-items ?li] + '[?je :journal-entry/original-entity ?e] + '[?li :journal-entry-line/account ?lia] + '[?lia :account/name]]} + :args [(d/db auto-ap.datomic/conn)]})) + transaction-accounts (reduce + (fn [acc [e lia]] + (update acc e (fnil conj #{} ) lia)) + {} + (d/query {:query {:find ['?e '?lia] + :in ['$] + :where ['[?e :transaction/accounts ?li] + '(not [?e :transaction/approval-status :transaction-approval-status/excluded]) + '[?li :transaction-account/account ?lia] + + '[?lia :account/name]]} + :args [(d/db auto-ap.datomic/conn)]})) + ] + (filter + (fn [[e accounts]] (not= accounts (get jel-accounts e))) + transaction-accounts))) diff --git a/taskdef.json b/taskdef.json index 1bfb4abd..1bccbf75 100644 --- a/taskdef.json +++ b/taskdef.json @@ -1,6 +1,5 @@ { - "taskDefinition": { - "taskDefinitionArn": "arn:aws:ecs:us-east-1:679918342773:task-definition/integreat-app:4", + "taskDefinitionArn": "arn:aws:ecs:us-east-1:679918342773:task-definition/integreat-app:7", "containerDefinitions": [ { "name": "integreat-app", @@ -35,13 +34,27 @@ "awslogs-stream-prefix": "ecs" } } + }, + { + "environment": [ + { + "name": "DD_API_KEY", + "value": "pub253ebc8332538a122a32893c57488a0a" + }, + { + "name": "ECS_FARGATE", + "value": "true" + } + ], + "image": "datadog/agent:latest", + "name": "datadog-agent" } ], "family": "integreat-app", "taskRoleArn": "arn:aws:iam::679918342773:role/datomic-ddb", "executionRoleArn": "arn:aws:iam::679918342773:role/ecsTaskExecutionRole", "networkMode": "awsvpc", - "revision": 4, + "revision": 7, "volumes": [], "status": "ACTIVE", "requiresAttributes": [ @@ -80,5 +93,4 @@ ], "cpu": "2048", "memory": "4096" - } }