Supports ledger ignore
This commit is contained in:
@@ -420,6 +420,7 @@
|
|||||||
|
|
||||||
:import_ledger_result {:fields {:successful {:type '(list :import_ledger_entry_result)}
|
:import_ledger_result {:fields {:successful {:type '(list :import_ledger_entry_result)}
|
||||||
:existing {:type '(list :import_ledger_entry_result)}
|
:existing {:type '(list :import_ledger_entry_result)}
|
||||||
|
:ignored {:type '(list :import_ledger_entry_result)}
|
||||||
:errors {:type '(list :import_ledger_entry_result)}
|
:errors {:type '(list :import_ledger_entry_result)}
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
|||||||
@@ -201,7 +201,9 @@
|
|||||||
(try
|
(try
|
||||||
(f entry)
|
(f entry)
|
||||||
(catch Exception e
|
(catch Exception e
|
||||||
(assoc entry :error (.getMessage e))))))
|
(assoc entry :error (.getMessage e)
|
||||||
|
:status (or (:status (ex-data e))
|
||||||
|
:error))))))
|
||||||
|
|
||||||
(defn delete-external-ledger [context args value]
|
(defn delete-external-ledger [context args value]
|
||||||
(let [_ (assert-admin (:id context))
|
(let [_ (assert-admin (:id context))
|
||||||
@@ -274,91 +276,104 @@
|
|||||||
lis))))]
|
lis))))]
|
||||||
(let [vendor (all-vendors (:vendor_name entry))]
|
(let [vendor (all-vendors (:vendor_name entry))]
|
||||||
(when-not (all-clients (:client_code entry))
|
(when-not (all-clients (:client_code entry))
|
||||||
(throw (Exception. (str "Client '" (:client_code entry )"' not found.")) ))
|
(throw (ex-info (str "Client '" (:client_code entry )"' not found.") {:status :error}) ))
|
||||||
(when-not vendor
|
(when-not vendor
|
||||||
(throw (Exception. (str "Vendor '" (:vendor_name entry) "' not found."))))
|
(throw (ex-info (str "Vendor '" (:vendor_name entry) "' not found.") {:status :error})))
|
||||||
(when-not (re-find #"\d{1,2}/\d{1,2}/\d{4}" (:date entry))
|
(when-not (re-find #"\d{1,2}/\d{1,2}/\d{4}" (:date entry))
|
||||||
(throw (Exception. (str "Date must be MM/dd/yyyy"))))
|
(throw (ex-info (str "Date must be MM/dd/yyyy") {:status :error})))
|
||||||
(when-not (dollars= (reduce + 0.0 (map :debit (:line_items entry)))
|
(when-not (dollars= (reduce + 0.0 (map :debit (:line_items entry)))
|
||||||
(reduce + 0.0 (map :credit (:line_items entry))))
|
(reduce + 0.0 (map :credit (:line_items entry))))
|
||||||
(throw (Exception. (str "Debits '"
|
(throw (ex-info (str "Debits '"
|
||||||
(reduce + 0 (map :debit (:line_items entry)))
|
(reduce + 0 (map :debit (:line_items entry)))
|
||||||
"' and credits '"
|
"' and credits '"
|
||||||
(reduce + 0 (map :credit (:line_items entry)))
|
(reduce + 0 (map :credit (:line_items entry)))
|
||||||
"' do not add up."))))
|
"' do not add up.")
|
||||||
|
{:status :error})))
|
||||||
(when (dollars= (reduce + 0.0 (map :debit (:line_items entry)))
|
(when (dollars= (reduce + 0.0 (map :debit (:line_items entry)))
|
||||||
0.0)
|
0.0)
|
||||||
(throw (Exception. (str "Cannot have ledger entries that total $0.00"))))
|
(throw (ex-info (str "Cannot have ledger entries that total $0.00")
|
||||||
(remove-nils
|
{:status :ignored})))
|
||||||
{:journal-entry/source (:source entry)
|
(assoc entry
|
||||||
:journal-entry/client [:client/code (:client_code entry)]
|
:status :success
|
||||||
:journal-entry/date (coerce/to-date (parse/parse-value :clj-time "MM/dd/yyyy" (:date entry)))
|
:tx
|
||||||
:journal-entry/external-id (:external_id entry)
|
(remove-nils
|
||||||
:journal-entry/vendor (:db/id (doto (all-vendors (:vendor_name entry))
|
{:journal-entry/source (:source entry)
|
||||||
println))
|
:journal-entry/client [:client/code (:client_code entry)]
|
||||||
:journal-entry/amount (:amount entry)
|
:journal-entry/date (coerce/to-date (parse/parse-value :clj-time "MM/dd/yyyy" (:date entry)))
|
||||||
:journal-entry/note (:note entry)
|
:journal-entry/external-id (:external_id entry)
|
||||||
:journal-entry/cleared-against (:cleared_against entry)
|
:journal-entry/vendor (:db/id (all-vendors (:vendor_name entry)))
|
||||||
|
:journal-entry/amount (:amount entry)
|
||||||
|
:journal-entry/note (:note entry)
|
||||||
|
:journal-entry/cleared-against (:cleared_against entry)
|
||||||
|
|
||||||
:journal-entry/line-items
|
:journal-entry/line-items
|
||||||
(mapv (fn [ea]
|
(mapv (fn [ea]
|
||||||
(when-not (get
|
(when-not (get
|
||||||
(get all-client-locations (:client_code entry))
|
(get all-client-locations (:client_code entry))
|
||||||
(:location ea))
|
(:location ea))
|
||||||
(throw (Exception. (str "Location '" (:location ea) "' not found."))))
|
(throw (ex-info (str "Location '" (:location ea) "' not found.")
|
||||||
(when (and (<= (:debit ea 0.0) 0.0)
|
{:status :error})))
|
||||||
(<= (:credit ea 0.0) 0.0))
|
(when (and (<= (:debit ea 0.0) 0.0)
|
||||||
(throw (Exception. (str "Line item amount " (or (:debit ea) (:credit ea)) " must be greater than 0."))))
|
(<= (:credit ea 0.0) 0.0))
|
||||||
(when (and (not (all-accounts (:account_identifier ea)))
|
(throw (ex-info (str "Line item amount " (or (:debit ea) (:credit ea)) " must be greater than 0.")
|
||||||
(not (get
|
{:status :error})))
|
||||||
(get all-client-bank-accounts (:client_code entry))
|
(when (and (not (all-accounts (:account_identifier ea)))
|
||||||
(:account_identifier ea))))
|
(not (get
|
||||||
(throw (Exception. (str "Account '" (:account_identifier ea) "' not found."))))
|
(get all-client-bank-accounts (:client_code entry))
|
||||||
(let [matching-account (when (re-matches #"^[0-9]+$" (:account_identifier ea))
|
(:account_identifier ea))))
|
||||||
(a/get-account-by-numeric-code-and-sets (Integer/parseInt (:account_identifier ea)) ["default"]))]
|
(throw (ex-info (str "Account '" (:account_identifier ea) "' not found.")
|
||||||
(when (and matching-account
|
{:status :error})))
|
||||||
(:account/location matching-account)
|
(let [matching-account (when (re-matches #"^[0-9]+$" (:account_identifier ea))
|
||||||
(not= (:account/location matching-account)
|
(a/get-account-by-numeric-code-and-sets (Integer/parseInt (:account_identifier ea)) ["default"]))]
|
||||||
(:location ea)))
|
(when (and matching-account
|
||||||
(throw (Exception. (str "Account '"
|
(:account/location matching-account)
|
||||||
(:account/numeric-code matching-account)
|
(not= (:account/location matching-account)
|
||||||
"' requires location '"
|
(:location ea)))
|
||||||
(:account/location matching-account)
|
(throw (ex-info (str "Account '"
|
||||||
"' but got '"
|
(:account/numeric-code matching-account)
|
||||||
(:location ea)
|
"' requires location '"
|
||||||
"'"
|
(:account/location matching-account)
|
||||||
))))
|
"' but got '"
|
||||||
|
(:location ea)
|
||||||
|
"'")
|
||||||
|
{:status :error})))
|
||||||
|
|
||||||
(when (and matching-account
|
(when (and matching-account
|
||||||
(not (:account/location matching-account))
|
(not (:account/location matching-account))
|
||||||
(= "A" (:location ea)))
|
(= "A" (:location ea)))
|
||||||
(throw (Exception. (str "Account '"
|
(throw (ex-info (str "Account '"
|
||||||
(:account/numeric-code matching-account)
|
(:account/numeric-code matching-account)
|
||||||
"' cannot use location '"
|
"' cannot use location '"
|
||||||
(:location ea)
|
(:location ea)
|
||||||
"'"))))
|
"'")
|
||||||
(remove-nils (cond-> {:journal-entry-line/location (:location ea)
|
{:status :error})))
|
||||||
:journal-entry-line/debit (when (> (:debit ea) 0)
|
(remove-nils (cond-> {:journal-entry-line/location (:location ea)
|
||||||
(:debit ea))
|
:journal-entry-line/debit (when (> (:debit ea) 0)
|
||||||
:journal-entry-line/credit (when (> (:credit ea) 0)
|
(:debit ea))
|
||||||
(:credit ea))}
|
:journal-entry-line/credit (when (> (:credit ea) 0)
|
||||||
matching-account (assoc :journal-entry-line/account (:db/id matching-account))
|
(:credit ea))}
|
||||||
(not matching-account) (assoc :journal-entry-line/account [:bank-account/code (:account_identifier ea)])))))
|
matching-account (assoc :journal-entry-line/account (:db/id matching-account))
|
||||||
(:line_items entry))
|
(not matching-account) (assoc :journal-entry-line/account [:bank-account/code (:account_identifier ea)])))))
|
||||||
|
(:line_items entry))
|
||||||
:journal-entry/cleared true})))))
|
|
||||||
|
:journal-entry/cleared true}))))))
|
||||||
(:entries args)))
|
(:entries args)))
|
||||||
errors (filter :error transaction)
|
errors (filter #(= (:status %) :error) transaction)
|
||||||
success (filter (comp not :error) transaction)
|
ignored (filter #(= (:status %) :ignored) transaction)
|
||||||
retraction (mapv (fn [x] [:db/retractEntity [:journal-entry/external-id (:journal-entry/external-id x)]])
|
success (filter #(= (:status %) :success) transaction)
|
||||||
|
retraction (mapv (fn [x] [:db/retractEntity [:journal-entry/external-id (:external_id x)]])
|
||||||
success)]
|
success)]
|
||||||
(log/info "manual ledger import has " (count success) " new rows")
|
(log/info "manual ledger import has " (count success) " new rows")
|
||||||
|
|
||||||
|
|
||||||
(audit-transact-batch retraction (:id context))
|
(audit-transact-batch retraction (:id context))
|
||||||
(audit-transact-batch success (:id context))
|
(log/info (map :tx success))
|
||||||
|
(audit-transact-batch (map :tx success) (:id context))
|
||||||
|
|
||||||
{:successful (map (fn [x] {:external_id (:journal-entry/external-id x)}) success)
|
{:successful (map (fn [x] {:external_id (:external_id x)}) success)
|
||||||
|
:ignored (map (fn [x]
|
||||||
|
{:external_id (:external_id x)})
|
||||||
|
ignored)
|
||||||
:existing []
|
:existing []
|
||||||
:errors (map (fn [x] {:external_id (:external_id x)
|
:errors (map (fn [x] {:external_id (:external_id x)
|
||||||
:error (:error x)}) errors)})))
|
:error (:error x)}) errors)})))
|
||||||
|
|||||||
@@ -82,9 +82,10 @@
|
|||||||
(let [err (str "Account " name " uses location " (:location a) ", but is supposed to be " location)]
|
(let [err (str "Account " name " uses location " (:location a) ", but is supposed to be " location)]
|
||||||
(throw (ex-info err {:validation-error err}) )))
|
(throw (ex-info err {:validation-error err}) )))
|
||||||
|
|
||||||
(when (not (get (into #{"Shared"} (:client/locations client))
|
(when (and (not location)
|
||||||
(:location a)))
|
(not (get (into #{"Shared"} (:client/locations client))
|
||||||
(let [err (str "Account " name " uses location " (:location a) ", but doesn't belong to the client " location)]
|
(:location a))))
|
||||||
|
(let [err (str "Account " name " uses location " (:location a) ", but doesn't belong to the client.")]
|
||||||
(throw (ex-info err {:validation-error err}) ))))
|
(throw (ex-info err {:validation-error err}) ))))
|
||||||
rule-id (if id
|
rule-id (if id
|
||||||
id
|
id
|
||||||
|
|||||||
@@ -62,10 +62,11 @@
|
|||||||
(fn [{:keys [db]} [_ result]]
|
(fn [{:keys [db]} [_ result]]
|
||||||
(let [successful-set (set (map :external-id (:successful (:import-ledger result))))
|
(let [successful-set (set (map :external-id (:successful (:import-ledger result))))
|
||||||
error-set (into {} (map (juxt :external-id :error) (:errors (:import-ledger result))))
|
error-set (into {} (map (juxt :external-id :error) (:errors (:import-ledger result))))
|
||||||
existing-set (set (map :external-id (:existing (:import-ledger result))))]
|
existing-set (set (map :external-id (:existing (:import-ledger result))))
|
||||||
|
ignored-set (set (map :external-id (:ignored (:import-ledger result))))]
|
||||||
|
|
||||||
{:db (-> (forms/save-succeeded db ::form )
|
{:db (-> (forms/save-succeeded db ::form )
|
||||||
(assoc-in [::forms/forms ::form :result] {:errors error-set :success successful-set :existing existing-set})
|
(assoc-in [::forms/forms ::form :result] {:errors error-set :success successful-set :existing existing-set :ignored ignored-set})
|
||||||
(update-in [::forms/forms ::form :data :line-items]
|
(update-in [::forms/forms ::form :data :line-items]
|
||||||
(fn [lis]
|
(fn [lis]
|
||||||
(mapv
|
(mapv
|
||||||
@@ -73,6 +74,9 @@
|
|||||||
(cond (successful-set (line->id %))
|
(cond (successful-set (line->id %))
|
||||||
[:span.icon [:i.fa.fa-check]]
|
[:span.icon [:i.fa.fa-check]]
|
||||||
|
|
||||||
|
(ignored-set (line->id %))
|
||||||
|
[:span.icon [:i.fa.fa-minus-circle]]
|
||||||
|
|
||||||
(existing-set (line->id %))
|
(existing-set (line->id %))
|
||||||
""
|
""
|
||||||
|
|
||||||
@@ -91,6 +95,10 @@
|
|||||||
(cond (successful-set (line->id %))
|
(cond (successful-set (line->id %))
|
||||||
:success
|
:success
|
||||||
|
|
||||||
|
|
||||||
|
(ignored-set (line->id %))
|
||||||
|
:ignored
|
||||||
|
|
||||||
(existing-set (line->id %))
|
(existing-set (line->id %))
|
||||||
:existing
|
:existing
|
||||||
|
|
||||||
@@ -111,6 +119,7 @@
|
|||||||
{:entries @(re-frame/subscribe [::request])}
|
{:entries @(re-frame/subscribe [::request])}
|
||||||
[[:successful [:external-id]]
|
[[:successful [:external-id]]
|
||||||
[:existing [:external-id]]
|
[:existing [:external-id]]
|
||||||
|
[:ignored [:external-id]]
|
||||||
[:errors [:external-id :error]]]]}]}
|
[:errors [:external-id :error]]]]}]}
|
||||||
:on-success [::imported]
|
:on-success [::imported]
|
||||||
:on-error [::forms/save-error ::form]}})))
|
:on-error [::forms/save-error ::form]}})))
|
||||||
@@ -191,7 +200,10 @@
|
|||||||
:on-click (dispatch-event [::importing])} "Import"]]]
|
:on-click (dispatch-event [::importing])} "Import"]]]
|
||||||
(when result
|
(when result
|
||||||
[:div.notification
|
[:div.notification
|
||||||
"Imported with " (count (:errors result)) " errors, " (count (:success result)) " successful."])
|
"Imported with "
|
||||||
|
(count (:errors result)) " errors, "
|
||||||
|
(count (:ignored result)) " ignored, "
|
||||||
|
(count (:success result)) " successful."])
|
||||||
(if @(re-frame/subscribe [::forms/is-loading? ::form])
|
(if @(re-frame/subscribe [::forms/is-loading? ::form])
|
||||||
[:div [:i.icon.fa.fa-spin.fa-spinner]]
|
[:div [:i.icon.fa.fa-spin.fa-spinner]]
|
||||||
[:div
|
[:div
|
||||||
|
|||||||
@@ -38,7 +38,8 @@
|
|||||||
[grid/cell {} ]
|
[grid/cell {} ]
|
||||||
[grid/cell {} ]
|
[grid/cell {} ]
|
||||||
[grid/cell {} ]
|
[grid/cell {} ]
|
||||||
[grid/cell {} (if (:name account)
|
[grid/cell {}
|
||||||
|
(if (:name account)
|
||||||
[:span.has-tooltip-arrow.has-tooltip-right {:data-tooltip (str "Balance as of this entry: " (nf running-balance ))}
|
[:span.has-tooltip-arrow.has-tooltip-right {:data-tooltip (str "Balance as of this entry: " (nf running-balance ))}
|
||||||
(str location ": " (:name account)) ]
|
(str location ": " (:name account)) ]
|
||||||
[:i "unknown"])]
|
[:i "unknown"])]
|
||||||
|
|||||||
@@ -5,17 +5,18 @@
|
|||||||
[clojure.test :as t :refer [deftest is testing use-fixtures]]
|
[clojure.test :as t :refer [deftest is testing use-fixtures]]
|
||||||
[clj-time.core :as time]
|
[clj-time.core :as time]
|
||||||
[datomic.api :as d]
|
[datomic.api :as d]
|
||||||
[auto-ap.datomic :refer [uri]]
|
[auto-ap.datomic :refer [uri conn]]
|
||||||
[buddy.sign.jwt :as jwt]
|
[buddy.sign.jwt :as jwt]
|
||||||
[config.core :refer [env]]))
|
[config.core :refer [env]]))
|
||||||
(defn wrap-setup
|
(defn wrap-setup
|
||||||
[f]
|
[f]
|
||||||
(with-redefs [auto-ap.datomic/uri "datomic:mem://datomic-transactor:4334/invoice"]
|
(with-redefs [auto-ap.datomic/uri "datomic:mem://datomic-transactor:4334/invoice"]
|
||||||
(d/create-database uri)
|
(d/create-database uri)
|
||||||
(m/-main false)
|
(with-redefs [auto-ap.datomic/conn (d/connect uri)]
|
||||||
(f)
|
(m/migrate conn)
|
||||||
(d/release (d/connect uri))
|
(f)
|
||||||
(d/delete-database uri)))
|
(d/release conn)
|
||||||
|
(d/delete-database uri))))
|
||||||
|
|
||||||
(defn admin-token []
|
(defn admin-token []
|
||||||
{:user "TEST ADMIN"
|
{:user "TEST ADMIN"
|
||||||
|
|||||||
Reference in New Issue
Block a user