feat(ledger): remove a whole ledger entry from the external import grid
The external ledger import review grid is one row per line item, so a journal entry spans two or more rows. When one entry fails validation the whole paste is rejected, and the only way forward was to re-paste without it. Each row now carries its entry id (client-source-externalId, the same key table->entries groups on) and the last column gets a trash button that drops every row sharing that id. The rows are the form inputs, so removing them from the DOM removes them from the next import post -- which also works for rows that fail schema validation. Index gaps left behind are compacted by coerce-vector on the way back in. Also drops two leftover pprint calls that dumped form-errors to stdout on every render. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
[clojure.data.csv :as csv]
|
||||
[clojure.string :as str]
|
||||
[datomic.api :as dc]
|
||||
[hiccup2.core :as hiccup]
|
||||
[malli.core :as mc]))
|
||||
|
||||
(use-fixtures :each wrap-setup)
|
||||
@@ -619,3 +620,93 @@
|
||||
(testing "Non-admin cannot bulk-delete"
|
||||
(is (thrown? Exception (sut/bulk-delete {:identity (user-token)
|
||||
:form-params {:selected [1]}})))))
|
||||
|
||||
;; =============================================================================
|
||||
;; External Import - removing an entry from the review grid
|
||||
;; =============================================================================
|
||||
|
||||
(defn- import-row [external-id account-code debit credit]
|
||||
{:external-id external-id
|
||||
:client-code "REMOVE-TEST"
|
||||
:source "manual"
|
||||
:vendor-name "Remove Vendor"
|
||||
:date (coerce/to-date-time #inst "2021-01-01")
|
||||
:account-code account-code
|
||||
:location "HQ"
|
||||
:debit debit
|
||||
:credit credit})
|
||||
|
||||
(defn- entry-rows
|
||||
"The two rows a balanced ledger entry is typically pasted as."
|
||||
[external-id debit-account amount]
|
||||
[(import-row external-id debit-account amount 0.0)
|
||||
(import-row external-id 2000 0.0 amount)])
|
||||
|
||||
(deftest external-import-remove-button-test
|
||||
(testing "Every row is tagged with the entry id the importer groups on"
|
||||
(let [table (vec (concat (entry-rows "ext-a" 1100 100.0)
|
||||
(entry-rows "ext-b" 1100 50.0)))
|
||||
html (str (hiccup/html (sut/external-import-table-form*
|
||||
{:form-params {:table table}
|
||||
:form-errors {}})))]
|
||||
(is (= 2 (count (re-seq #"data-entry-id=\"REMOVE-TEST-manual-ext-a\"" html))))
|
||||
(is (= 2 (count (re-seq #"data-entry-id=\"REMOVE-TEST-manual-ext-b\"" html))))
|
||||
(testing "and offers a remove button that drops the whole entry"
|
||||
(is (= 4 (count (re-seq #"removeImportEntryRows\(\$el\)" html))))
|
||||
(is (= 2 (count (re-seq #"data-remove-entry-id=\"REMOVE-TEST-manual-ext-a\"" html))))
|
||||
(is (= 2 (count (re-seq #"aria-label=\"Remove ledger entry REMOVE-TEST-manual-ext-a\"" html))))))))
|
||||
|
||||
(deftest external-import-remove-entry-then-import-test
|
||||
(testing "Removing the failing entry lets the remaining entries import"
|
||||
(let [_ (setup-test-data [(test-client :db/id "remove-client"
|
||||
:client/code "REMOVE-TEST"
|
||||
:client/locations ["HQ"])
|
||||
(test-vendor :db/id "remove-vendor"
|
||||
:vendor/name "Remove Vendor")
|
||||
{:db/id "remove-account-1100"
|
||||
:account/numeric-code 1100
|
||||
:account/account-set "default"
|
||||
:account/name "Cash"}
|
||||
{:db/id "remove-account-2000"
|
||||
:account/numeric-code 2000
|
||||
:account/account-set "default"
|
||||
:account/name "Accounts Payable"}])
|
||||
;; Three entries, six rows. The middle one posts to an account that
|
||||
;; does not exist, which is the kind of error a user has to resolve.
|
||||
table (vec (concat (entry-rows "ext-good-1" 1100 100.0)
|
||||
(entry-rows "ext-bad" 99999 75.0)
|
||||
(entry-rows "ext-good-2" 1100 25.0)))
|
||||
admin (admin-token)
|
||||
external-id (fn [id] (str "REMOVE-TEST-manual-" id))
|
||||
imported? (fn [db id]
|
||||
(boolean (dc/q '[:find ?je .
|
||||
:in $ ?ext
|
||||
:where [?je :journal-entry/external-id ?ext]]
|
||||
db (external-id id))))]
|
||||
|
||||
(testing "the bad entry blocks the whole paste"
|
||||
(is (thrown? Exception (sut/import-ledger {:form-params {:table table}
|
||||
:identity admin})))
|
||||
(let [db (dc/db conn)]
|
||||
(is (not (imported? db "ext-good-1")))
|
||||
(is (not (imported? db "ext-good-2")))))
|
||||
|
||||
(testing "removing it drops both of its rows"
|
||||
(let [remaining (vec (remove #(= (external-id "ext-bad") (sut/line->id %)) table))]
|
||||
(is (= 4 (count remaining)))
|
||||
|
||||
(testing "and the other two entries import"
|
||||
(let [result (sut/import-ledger {:form-params {:table remaining}
|
||||
:identity admin})
|
||||
db (dc/db conn)]
|
||||
(is (= 2 (:successful result)))
|
||||
(is (imported? db "ext-good-1"))
|
||||
(is (imported? db "ext-good-2"))
|
||||
(is (not (imported? db "ext-bad")))
|
||||
(let [entry (dc/pull db
|
||||
[:journal-entry/amount
|
||||
{:journal-entry/line-items [:journal-entry-line/debit
|
||||
:journal-entry-line/credit]}]
|
||||
[:journal-entry/external-id (external-id "ext-good-1")])]
|
||||
(is (= 100.0 (:journal-entry/amount entry)))
|
||||
(is (= 2 (count (:journal-entry/line-items entry))))))))))))
|
||||
|
||||
Reference in New Issue
Block a user