Files
integreat/src/clj/auto_ap/ledger.clj
2020-04-30 07:26:26 -07:00

159 lines
7.7 KiB
Clojure

(ns auto-ap.ledger
(:require [datomic.api :as d]
[auto-ap.datomic.accounts :as a]
[auto-ap.datomic :refer [uri remove-nils]]))
(defn datums->impacted-entity [db [e changes]]
(let [entity (d/pull db '[* {:invoice/_expense-accounts [:db/id] :transaction/_accounts [:db/id]}] e)
namespaces (->> changes
(map :a)
(map namespace)
set)]
(cond (namespaces "invoice" ) [[:invoice e]]
(namespaces "invoice-expense-account" ) [[:invoice (:db/id (:invoice/_expense-accounts entity))]]
(namespaces "transaction-account" ) [[:transaction (:db/id (:transaction/_accounts entity))]]
(namespaces "transaction" ) [[:transaction e]]
:else nil)))
(defn infer-entity [_ [_ changes]]
(let [namespaces (->> changes
(map :a)
(map namespace)
set)]
(cond (namespaces "invoice" ) :invoice
(namespaces "invoice-expense-account" ) :invoice-expense-account
(namespaces "transaction-account" ) :transaction-account
:else nil)))
(defmulti entity-change->ledger (fn [_ [type]]
type))
(defmethod entity-change->ledger :invoice
[db [type id]]
(let [entity (d/pull db ['* {:invoice/vendor '[*] :invoice/payment '[*]}] id)]
(when-not (= true (:invoice/exclude-from-ledger entity))
(remove-nils
{:journal-entry/source "invoice"
:journal-entry/client (:db/id (:invoice/client entity))
:journal-entry/date (:invoice/date entity)
:journal-entry/original-entity (:db/id entity)
:journal-entry/vendor (:db/id (:invoice/vendor entity))
:journal-entry/amount (:invoice/total entity)
:journal-entry/line-items (into [{:journal-entry-line/account (:db/id (a/get-account-by-numeric-code-and-sets 2110 ["default"]))
:journal-entry-line/location "A"
:journal-entry-line/credit (:invoice/total entity)}]
(map (fn [ea]
(remove-nils {:journal-entry-line/account (:db/id (:invoice-expense-account/account ea))
:journal-entry-line/location (or (:invoice-expense-account/location ea) "HQ") ;; TODO?
:journal-entry-line/debit (:invoice-expense-account/amount ea)}))
(:invoice/expense-accounts entity)))
:journal-entry/cleared (and (< (:invoice/outstanding-balance entity) 0.01)
(every? #(= :payment-status/cleared (:payment/status %)) (:invoice/payments entity))
)}))))
(defmethod entity-change->ledger :transaction
[db [type id]]
(let [entity (d/pull db ['* {:transaction/vendor '[*]
:transaction/client '[*]
:transaction/approval-status '[*]
:transaction/bank-account '[* {:bank-account/type [:db/ident]}]
:transaction/accounts '[*
{:transaction-account/account [*]}] }] id)
bank-account-type (-> entity :transaction/bank-account :bank-account/type :db/ident)
decreasing? (< (:transaction/amount entity) 0.0)
credit-from-bank? decreasing?
debit-from-bank? (not decreasing?)]
(when-not (= :transaction-approval-status/excluded (:db/ident (:transaction/approval-status entity)))
(remove-nils
{:journal-entry/source "transaction"
:journal-entry/client (:db/id (:transaction/client entity))
:journal-entry/date (:transaction/date entity)
:journal-entry/original-entity (:db/id entity)
:journal-entry/vendor (:db/id (:transaction/vendor entity))
:journal-entry/amount (Math/abs (:transaction/amount entity))
:journal-entry/line-items (into [(remove-nils {:journal-entry-line/account (:db/id (:transaction/bank-account entity))
:journal-entry-line/location "A"
:journal-entry-line/credit (when credit-from-bank?
(Math/abs (:transaction/amount entity)))
:journal-entry-line/debit (when debit-from-bank?
(Math/abs (:transaction/amount entity)))})
]
(map
(fn [a]
(remove-nils{:journal-entry-line/account (:db/id (:transaction-account/account a))
:journal-entry-line/location (:transaction-account/location a)
:journal-entry-line/debit (when credit-from-bank?
(Math/abs (:transaction-account/amount a)))
:journal-entry-line/credit (when debit-from-bank?
(Math/abs (:transaction-account/amount a)))}))
(if (seq (:transaction/accounts entity))
(:transaction/accounts entity)
[{:transaction-account/amount (:transaction/amount entity)}])))
:journal-entry/cleared true}))))
(defmethod entity-change->ledger :invoice-expense-account
[db [entity changes]]
nil
)
(defmethod entity-change->ledger nil
[db [entity changes]]
nil)
#_(defn entity-change->ledger [[entity-id changes]]
[entity-id (infer-entity changes)])
(defn ledger-entries->transaction [entries]
(into [[:replace-general-ledger (:journal-entry/original-entity (first entries))]]
entries))
(defn process-one [report-queue]
(let [transaction (.take report-queue)
_ (println "processing transaction")
db (:db-after transaction)
affected-entities (->> (:tx-data transaction)
(map (fn [^datomic.db.Datum x]
{:e (:e x)
:a (d/ident db (:a x))
:v (:v x)
:added (:added x)}))
(group-by :e)
(mapcat #(datums->impacted-entity db %))
(set))
_ (println "affected" (count affected-entities))
d-txs (->> affected-entities
(map #(entity-change->ledger db %))
(filter seq))
retractions (map (fn [[_ e]] [:db/retractEntity [:journal-entry/original-entity e]]) affected-entities)]
(when (seq retractions)
@(d/transact (d/connect uri) retractions))
(doseq [d-tx d-txs]
#_(println "updating general-ledger " d-tx)
@(d/transact (d/connect uri) [d-tx]))))
(def break (atom false))
(defn process-all []
(println "Starting worker")
(while (and (not @break)
(not (Thread/interrupted)))
(try
(process-one (d/tx-report-queue (d/connect uri) ))
(catch Exception e
(println (.toString e))))))
#_(process-one (d/tx-report-queue (d/connect uri) ))
#_(process-all)