19 Commits

Author SHA1 Message Date
4221d6a0d6 more fixes 2026-05-21 11:46:45 -07:00
918ddd14ff Fix format error in toggle-amount-mode: ensure double values for %f format
The format specifier $%,.2f requires floating-point values but
(reduce + 0 ...) can return a Long when all amounts sum to an integer.
Added explicit (double ...) casts and changed initial value to 0.0
to ensure the format call always receives a double.
2026-05-21 09:14:20 -07:00
acd4184ef0 Fix toggle-amount-mode: avoid cursor context entirely for HTMX re-render
The previous attempts to set up form cursor context in toggle-amount-mode
were failing because the cursor library's dynamic binding model is complex
and requires specific initialization through fc/start-form.

Instead of trying to recreate the cursor context, this fix:
1. Creates transaction-account-row-no-cursor* that renders rows with explicit
   field names and values (no cursor functions)
2. Rewrites toggle-amount-mode to directly construct the data-grid HTML
   using map-indexed over the accounts vector
3. Removes the broken manual cursor binding attempts
4. Removes unused auto-ap.cursor import

This ensures the toggle handler works independently of the wizard's cursor
context while still producing identical HTML output.
2026-05-21 07:44:43 -07:00
857a1536ef Fix toggle-amount-mode: set up form cursor context for grid re-render
The toggle-amount-mode handler was failing because account-grid-body*
uses fc/cursor-map which requires the form cursor context to be set up.
Added manual cursor binding in toggle-amount-mode to create a cursor
pointing to the transaction/accounts vector and bind it to fc/*current*
before rendering the grid.
2026-05-21 07:33:15 -07:00
535ef4d113 Fix radio-card to pass through HTMX attributes for $/% toggle
The radio-card component was ignoring HTMX attributes (:hx-post, :hx-target,
etc.) passed to it. Modified the component to extract these attributes and
merge them into each radio input element, so the $/% toggle now properly
triggers HTMX requests when changed.
2026-05-20 23:23:19 -07:00
351659f8eb Fix toggle-amount-mode: properly update request state with converted accounts and new mode
The assoc-in call had too many arguments, so the request state wasn't being
updated with the new mode or converted accounts. Using -> threading with
separate assoc-in calls ensures both the accounts and mode are properly set
before re-rendering the grid.
2026-05-20 23:18:35 -07:00
4739769297 Fix $/% toggle: handle nil amounts and make toggle horizontal
- Fix Math/abs nil error when adding new accounts by using (or value 0.0)
- Fix Math/abs nil in account-grid-body* and save-handler for safety
- Make $/% radio toggle display side-by-side using :orientation :horizontal
- Apply fixes to edit-wizard-new-account render, account-grid-body*, and save-handler
2026-05-20 23:15:05 -07:00
567db50a66 Add $/% toggle for transaction account amounts in manual edit
When editing a transaction manually, users can now toggle between viewing
account amounts as dollar values or percentages. The toggle appears in the
table header as a radio button group ($ / %).

Key features:
- Global toggle switches all accounts simultaneously
- $→%: amounts are converted to percentages of the transaction total
- %→$: uses percentages->dollars with spread-cents for accurate cent distribution
- Form state preserves vendor, memo, approval status when toggling
- Save handler converts % back to $ before persisting to Datomic
- Uses HTMX to re-render only the account grid body on toggle

New route: /toggle-amount-mode
New functions: ->percentage, percentages->dollars, convert-accounts-mode,
              account-grid-body*, toggle-amount-mode
2026-05-20 23:07:11 -07:00
dbfa04c766 Add design spec for transaction account $/% toggle 2026-05-20 22:34:01 -07:00
0692089e39 Flash transaction row after editing through modal
When a transaction is saved via the edit modal, return the updated row
HTML with {:flash? true} instead of an empty div. This makes the row
flash with the live-added animation, matching the behavior of other
pages like invoices and accounts.

Uses hx-retarget to swap the specific row in the table while also
triggering modalclose to close the modal.
2026-05-20 21:39:30 -07:00
8189a7648b Fix Datomic conflict when spreading shared location across accounts
When a transaction account had 'Shared' location, spread-account was
creating multiple accounts with the same :db/id but different locations,
causing a datoms-conflict error in Datomic.

Now generates unique tempids for all spread accounts beyond the first,
preventing entity ID collisions while preserving the original account's
tempid for the first location.

Fixes: Two datoms in the same transaction conflict on :transaction-account/location
2026-05-20 21:33:44 -07:00
dd4d1a6d4f Add error message to require-approval validator
Ensure the validation error message shows up properly when users try
to approve a manual transaction without assigning financial accounts.
2026-05-20 21:29:03 -07:00
4f32527732 Fix form error display for root-level validation errors
When validation errors occur at the root level (e.g., from :fn validators
in multi schemas), they are returned as a vector directly rather than a
map with :errors key. Update default-step-footer to handle both cases.
2026-05-20 21:27:30 -07:00
0811771ae6 Fix manual transaction validation: require accounts when approving
- Add require-approval schema validation for :manual action
- Fix keyword comparison to use :transaction-approval-status/approved
- Move require-approval function before schema definition
- Also fix save handler validation to use correct keyword
2026-05-20 21:23:20 -07:00
c6b55ce567 Show potential-duplicates filter only when a bank account is selected 2026-05-20 21:14:21 -07:00
1f9a7080e1 Fix linked-to filter to handle empty string as nil 2026-05-20 21:02:23 -07:00
6f7f1c7815 Add linking, location, import-batch, and potential-duplicates filters to SSR transactions 2026-05-20 20:59:43 -07:00
065d1182d7 Add unresolved-only and financial account filters to SSR transaction listing 2026-05-20 20:51:22 -07:00
b42e2a6a44 adds unresolved only 2026-05-20 20:45:39 -07:00
7 changed files with 960 additions and 485 deletions

View File

@@ -0,0 +1,152 @@
# Transaction Account Amount Mode Toggle - Design Spec
**Date:** 2026-05-20
**Feature:** Global $/% Toggle for Transaction Accounts (Manual Action)
## Overview
In the transaction edit modal's "manual" action view, replace the static "$" column header with a sliding toggle that allows users to switch between viewing amounts as dollar values or percentages. When toggled, the entire account grid re-renders via HTMX with converted values. Percentages are multiplied by 100 (e.g., $200 on a $200 transaction → 100%). When switching back to dollars, use `spread-cents` to ensure accurate cent distribution.
## Motivation
The cljs master version supports per-row $/% toggles. Users want this capability in the SSR version, but with a single global toggle in the table header for simplicity and consistency with the bulk coding interface.
## Schema Changes
### Form State
Add `amount-mode` to the edit form's step params:
```clojure
[:amount-mode [:enum "$" "%"] {:default "$"}]
```
Stored in `multi-form-state` alongside existing transaction data. Not persisted to Datomic—purely a UI preference.
## UI Design
### Table Header
Replace the static `"$"` header cell (line ~739 in `edit.clj`) with a radio toggle:
```clojure
(com/radio-card {:options [{:value "$" :content "$"}
{:value "%" :content "%"}]
:value (or amount-mode "$")
:name "step-params[amount-mode]"
:hx-post (bidi/path-for ssr-routes/only-routes ::route/toggle-amount-mode)
:hx-target "#account-grid-body"
:hx-swap "outerHTML"
:hx-include "closest form"})
```
### Grid Body
Wrap the account grid rows in a container with id `account-grid-body`:
```clojure
[:div#account-grid-body
(fc/cursor-map #(transaction-account-row* ...))
...total/balance rows...]
```
When toggled, only this container re-renders. All other form fields (vendor, memo, approval status) are preserved.
## Data Flow
### Toggle Request (HTMX)
1. User clicks toggle
2. HTMX serializes entire form via `hx-include "closest form"`
3. POST to `::route/toggle-amount-mode`
4. Server:
- Merges form params into existing `multi-form-state`
- Extracts old mode and new mode
- Converts all `:transaction-account/amount` values:
- If old="$" new="%": multiply by 100/total
- If old="%" new="$": use `percentages->dollars` (see Conversion Logic)
- Updates `amount-mode` in state
- Re-renders `#account-grid-body`
5. Client swaps grid body. Tab order preserved.
### Conversion Logic
**$ → %:**
```clojure
(defn ->percentage [amount total]
(when (and amount total (not= total 0))
(* 100.0 (/ amount total))))
```
**% → $ (using spread-cents):**
```clojure
(defn percentages->dollars [percentages total]
(let [total-cents (int (* 100 (Math/abs total)))
pct-sum (reduce + 0 percentages)
;; Normalize percentages to sum to 100
normalized-pcts (if (zero? pct-sum)
(repeat (count percentages) 0)
(map #(* (/ % pct-sum) 100) percentages))
;; Convert each pct to its share of cents
individual-cents (map #(int (* total-cents (/ % 100))) normalized-pcts)
short-by (- total-cents (reduce + 0 individual-cents))
;; Distribute remainder using spread-cents pattern
adjustments (concat (take short-by (repeat 1)) (repeat 0))
final-cents (map + individual-cents adjustments)]
(map #(* 0.01 %) final-cents)))
```
Example: One account at 100% of $200.00 → `total-cents=20000`, `individual-cents=[20000]`, result: `[200.00]`
### Save Handling
Before validation in `save-handler :manual`:
```clojure
(let [snapshot (:snapshot multi-form-state)
accounts (:transaction/accounts snapshot)
total (Math/abs (:transaction/amount existing-tx))
mode (:amount-mode snapshot "$")
;; If in % mode, convert back to $ before saving
accounts' (if (= "%" mode)
(let [percentages (map :transaction-account/amount accounts)
dollar-amounts (percentages->dollars percentages total)]
(map #(assoc %1 :transaction-account/amount %2) accounts dollar-amounts))
accounts)]
...)
```
## Form Preservation
The HTMX toggle is designed to preserve:
- **Tab order:** All inputs remain in DOM with same `tabindex` attributes
- **Other form fields:** Vendor, memo, approval status are outside `#account-grid-body`
- **Alpine.js state:** `x-data` on rows uses `data-key="show"` for animation—this is re-established on re-render
- **Field names:** Account/location/amount field names follow `step-params[transaction/accounts][N][...]` pattern
## Error Handling
- **Zero transaction amount:** If total is $0, percentages are all 0%. Toggle is disabled or shows error.
- **Percentage sum ≠ 100:** After editing in % mode, if percentages don't sum to 100, normalize proportionally before converting back to $.
- **Invalid input:** If user types non-numeric in % mode, existing form validation catches it on submit.
## Testing Strategy
1. **Toggle $→%:** 200/200 transaction shows 100.0
2. **Toggle %→$:** 100% on 200 transaction shows 200.00
3. **Multiple accounts:** 50/50 split on 200 → 100.00/100.00 after conversion
4. **Cent distribution:** 33.33/33.33/33.34% on $100 → uses spread-cents for accurate distribution
5. **Form preservation:** Toggle doesn't lose vendor/memo data
6. **Save in % mode:** Correctly converts back to $ before Datomic transaction
## Files to Modify
- `src/clj/auto_ap/ssr/transaction/edit.clj` — main implementation
- `src/clj/auto_ap/routes/transactions.clj` — add `::route/toggle-amount-mode`
- `src/clj/auto_ap/ssr/transaction/edit.clj` routes map — register handler
## Future Considerations
- This pattern could be extracted for reuse in invoice expense accounts
- Consider persisting user's last-used mode preference in localStorage
- Could add visual indicator when percentages don't sum to 100%

File diff suppressed because one or more lines are too long

View File

@@ -148,7 +148,9 @@
next-button-content]}]
[:div.flex.justify-end
[:div.flex.items-baseline.gap-x-4
(com/form-errors {:errors (:errors (:step-params fc/*form-errors*))})
(let [step-errors (:step-params fc/*form-errors*)]
(com/form-errors {:errors (or (:errors step-errors)
(when (sequential? step-errors) step-errors))}))
(when (not= (first (steps linear-wizard))
(step-key step))
(when validation-route

View File

@@ -2,42 +2,44 @@
(:require [auto-ap.ssr.hiccup-helper :as hh]
[auto-ap.ssr.hx :as hx]))
(defn radio-card- [{:keys [options name title size orientation width] :or {size :medium width "w-48"} selected-value :value}]
[:h3 {:class "mb-4 font-semibold text-gray-900 dark:text-white"} title]
[:ul {:class (cond-> " text-sm font-medium text-gray-900 bg-white border border-gray-200 rounded-lg dark:bg-gray-700 dark:border-gray-600 dark:text-white"
(= orientation :horizontal) (-> (hh/add-class "flex gap-2 flex-wrap")
(hh/remove-wildcard ["w-" "rounded-lg" "border" "bg-"]))
true (str " " width " "))}
(for [{:keys [value content]} options]
[:li {:class (cond-> "w-full border-b border-gray-200 rounded-t-lg dark:border-gray-600"
(= orientation :horizontal) (-> (hh/remove-wildcard ["w-full" "rounded-"])
(hh/add-class "w-auto shrink-0 block rounded-lg border border-gray-200 dark:border-gray-600 px-3")))}
[:div {:class (cond-> "flex items-center"
(not= orientation :horizontal) (hh/add-class "pl-3"))}
[:input (cond-> {:id (str "list-" name "-" value)
:type "radio",
:value value
:name name
:class
(cond-> "w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-700 dark:focus:ring-offset-gray-700 focus:ring-2 dark:bg-gray-600 dark:border-gray-500"
(= size :small)
(str " " "text-xs")
(defn radio-card- [{:keys [options name title size orientation width] :or {size :medium width "w-48"} selected-value :value :as attrs}]
(let [htmx-attrs (select-keys attrs [:hx-post :hx-target :hx-swap :hx-include :hx-trigger])]
[:h3 {:class "mb-4 font-semibold text-gray-900 dark:text-white"} title]
[:ul {:class (cond-> " text-sm font-medium text-gray-900 bg-white border border-gray-200 rounded-lg dark:bg-gray-700 dark:border-gray-600 dark:text-white"
(= orientation :horizontal) (-> (hh/add-class "flex gap-2 flex-wrap")
(hh/remove-wildcard ["w-" "rounded-lg" "border" "bg-"]))
true (str " " width " "))}
(for [{:keys [value content]} options]
[:li {:class (cond-> "w-full border-b border-gray-200 rounded-t-lg dark:border-gray-600"
(= orientation :horizontal) (-> (hh/remove-wildcard ["w-full" "rounded-"])
(hh/add-class "w-auto shrink-0 block rounded-lg border border-gray-200 dark:border-gray-600 px-3")))}
[:div {:class (cond-> "flex items-center"
(not= orientation :horizontal) (hh/add-class "pl-3"))}
[:input (cond-> (merge {:id (str "list-" name "-" value)
:type "radio",
:value value
:name name
:class
(cond-> "w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-700 dark:focus:ring-offset-gray-700 focus:ring-2 dark:bg-gray-600 dark:border-gray-500"
(= size :small)
(str " " "text-xs")
(= size :medium)
(str " " "text-sm"))}
(= (cond-> selected-value (keyword? selected-value) clojure.core/name) value) (assoc :checked true))]
[:label {:for (str "list-" name "-" value)
:class
(cond-> "w-full ml-2 font-medium text-gray-900 dark:text-gray-300"
(= size :small)
(str " " "text-xs py-2")
(= size :medium)
(str " " "text-sm"))}
htmx-attrs)
(= (cond-> selected-value (keyword? selected-value) clojure.core/name) value) (assoc :checked true))]
[:label {:for (str "list-" name "-" value)
:class
(cond-> "w-full ml-2 font-medium text-gray-900 dark:text-gray-300"
(= size :small)
(str " " "text-xs py-2")
(= size :medium)
(str " " "text-sm py-3")
(= size :medium)
(str " " "text-sm py-3")
(= orientation :horizontal)
(hh/remove-class "w-full"))} content]]])]))
(= orientation :horizontal)
(hh/remove-class "w-full"))} content]]])])
(defn radio-list- [{:keys [options name x-model title size orientation] :or {size :medium} selected-value :value}]
[:h3 {:class "mb-4 font-semibold text-gray-900 dark:text-white"} title]
[:ul {:class (cond-> "w-48 text-sm font-medium text-gray-900"
@@ -55,7 +57,7 @@
:value value
:name name
:class
(cond-> "w-4 h-4 text-blue-600"
(cond-> "w-4 h-4 text-blue-600"
(= size :small)
(str " " "text-xs")
@@ -65,11 +67,11 @@
[:label {:for (str "list-" name "-" value)
:class
(cond-> "w-full ml-2 font-medium text-gray-900 dark:text-gray-300"
(= size :small)
(str " " "text-xs py-2")
(= size :small)
(str " " "text-xs py-2")
(= size :medium)
(str " " "text-sm py-3")
(= size :medium)
(str " " "text-sm py-3")
(= orientation :horizontal)
(hh/remove-class "w-full"))} content]]])])
(= orientation :horizontal)
(hh/remove-class "w-full"))} content]]])])

View File

@@ -1,8 +1,9 @@
(ns auto-ap.ssr.transaction.common
(:require
[auto-ap.datomic :refer [add-sorter-fields apply-pagination apply-sort-4
conn merge-query observable-query pull-many]]
[auto-ap.graphql.utils :refer [extract-client-ids]]
[auto-ap.datomic :refer [add-sorter-fields apply-pagination apply-sort-4
conn merge-query observable-query pull-many]]
[auto-ap.datomic.accounts :as d-accounts]
[auto-ap.graphql.utils :refer [extract-client-ids is-admin?]]
[auto-ap.routes.invoice :as invoice-routes]
[auto-ap.routes.ledger :as ledger-routes]
[auto-ap.routes.payments :as payment-routes]
@@ -32,11 +33,26 @@
[:amount-gte {:optional true} [:maybe :double]]
[:amount-lte {:optional true} [:maybe :double]]
[:client-id {:optional true} [:maybe entity-id]]
[:import-batch-id {:optional true} [:maybe entity-id]]
[:description {:optional true} [:maybe [:string {:decode/string strip}]]]
[:vendor {:optional true :default nil} [:maybe [:entity-map {:pull [:db/id :vendor/name]}]]]
[:bank-account {:optional true :default nil} [:maybe [:entity-map {:pull [:db/id :bank-account/numeric-code]}]]]
#_[:status {:optional true} [:maybe (ref->enum-schema "transaction-status")]]
[:import-batch-id {:optional true} [:maybe entity-id]]
[:unresolved {:optional true}
[:maybe [:boolean {:decode/string {:enter #(cond (= % "on") true
(= % "") false
:else
(boolean %))}}]]]
[:description {:optional true} [:maybe [:string {:decode/string strip}]]]
[:vendor {:optional true :default nil} [:maybe [:entity-map {:pull [:db/id :vendor/name]}]]]
[:bank-account {:optional true :default nil} [:maybe [:entity-map {:pull [:db/id :bank-account/numeric-code]}]]]
[:account {:optional true :default nil} [:maybe [:entity-map {:pull [:db/id :account/name]}]]]
[:linked-to {:optional true}
[:maybe [:enum {:decode/string {:enter #(if (seq %) % nil)}}
"payment" "expected-deposit" "invoice" "none"]]]
[:location {:optional true} [:maybe [:string {:decode/string strip}]]]
[:potential-duplicates {:optional true}
[:maybe [:boolean {:decode/string {:enter #(cond (= % "on") true
(= % "") false
:else
(boolean %))}}]]]
#_[:status {:optional true} [:maybe (ref->enum-schema "transaction-status")]]
[:exact-match-id {:optional true} [:maybe entity-id]]
[:all-selected {:optional true :default nil} [:maybe :boolean]]
[:selected {:optional true :default nil} [:maybe [:vector {:coerce? true}
@@ -145,16 +161,75 @@
:where ['[?e :transaction/bank-account ?ba]]}
:args [(:db/id (:bank-account args))]})
(:vendor args)
(merge-query {:query {:in ['?vendor-id]
:where ['[?e :transaction/vendor ?vendor-id]]}
:args [(:db/id (:vendor args))]})
(:import-batch-id args)
(merge-query {:query {:in ['?import-batch-id]
:where ['[?import-batch-id :import-batch/entry ?e]]}
:args [(:import-batch-id args)]})
(:vendor args)
(merge-query {:query {:in ['?vendor-id]
:where ['[?e :transaction/vendor ?vendor-id]]}
:args [(:db/id (:vendor args))]})
(:status route-params)
(:db/id (:account args))
(merge-query {:query {:in ['?account-id]
:where ['[?e :transaction/accounts ?tas]
'[?tas :transaction-account/account ?account-id]]}
:args [(:db/id (:account args))]})
(:import-batch-id args)
(merge-query {:query {:in ['?import-batch-id]
:where ['[?import-batch-id :import-batch/entry ?e]]}
:args [(:import-batch-id args)]})
(:unresolved args)
(merge-query {:query {:where ['[?e :transaction/date]
'(or-join [?e]
(not [?e :transaction/accounts])
(and [?e :transaction/accounts ?tas]
(not [?tas :transaction-account/account]))) ]}})
(seq (:location args))
(merge-query {:query {:in ['?location]
:where ['[?e :transaction/accounts ?tas]
'[?tas :transaction-account/location ?location]]}
:args [(:location args)]})
(= (:linked-to args) "payment")
(merge-query {:query {:where ['[?e :transaction/payment]]}})
(= (:linked-to args) "expected-deposit")
(merge-query {:query {:where ['[?e :transaction/expected-deposit]]}})
(= (:linked-to args) "invoice")
(merge-query {:query {:where ['[?e :transaction/payment ?p]
'[_ :invoice-payment/payment ?p]]}})
(= (:linked-to args) "none")
(merge-query {:query {:where ['(not [?e :transaction/payment])
'(not [?e :transaction/expected-deposit])]}})
(:potential-duplicates args)
(merge-query (let [bank-account-id (:db/id (:bank-account args))
_ (when-not bank-account-id
(throw (ex-info "In order to select potential duplicates, you must choose a bank account."
{:validation-error "In order to select potential duplicates, you must choose a bank account."})))
duplicate-ids (->> (dc/q '[:find ?tx ?amount ?date
:in $ ?ba
:where
[?tx :transaction/bank-account ?ba]
[?tx :transaction/amount ?amount]
[?tx :transaction/date ?date]
(not [?tx :transaction/approval-status :transaction-approval-status/suppressed])]
db
bank-account-id)
(group-by (fn [[_ amount date]]
[amount date]))
(filter (fn [[_ txes]]
(> (count txes) 1)))
(vals)
(mapcat identity)
(map first)
set)]
{:query {:in '[[?e ...]]
:where []}
:args [duplicate-ids]}))
(:status route-params)
(merge-query {:query {:in ['?status]
:where ['[?e :transaction/approval-status ?status]]}
:args [(:status route-params)]})
@@ -203,6 +278,18 @@
svg/x)]])]
[:div {:id "exact-match-id-tag"}]))
(defn import-batch-id* [request]
(when-let [import-batch-id (:import-batch-id (:query-params request))]
[:div {:x-data (hx/json {:import_batch_id import-batch-id}) :id "import-batch-id-tag"}
(com/hidden {:name "import-batch-id"
"x-model" "import_batch_id"})
(com/pill {:color :primary}
[:span.inline-flex.space-x-2.items-center
[:div (str "Batch " import-batch-id)]
[:div.w-3.h-3
(com/link {"@click" "import_batch_id=null; $nextTick(() => $dispatch('change'))"}
svg/x)]])]))
(defn bank-account-filter* [request]
@@ -237,43 +324,93 @@
(com/hidden {:name "status"
:value (some-> (:status (:query-params request)) name)})
[:fieldset.space-y-6
(com/field {:label "Vendor"}
(com/typeahead {:name "vendor"
:id "vendor"
:url (bidi/path-for ssr-routes/only-routes :vendor-search)
:value (:vendor (:query-params request))
:value-fn :db/id
:content-fn :vendor/name}))
(bank-account-filter* request)
(com/field {:label "Vendor"}
(com/typeahead {:name "vendor"
:id "vendor"
:url (bidi/path-for ssr-routes/only-routes :vendor-search)
:value (:vendor (:query-params request))
:value-fn :db/id
:content-fn :vendor/name}))
(com/field {:label "Financial Account"}
(com/typeahead {:name "account"
:id "account"
:url (bidi/path-for ssr-routes/only-routes :account-search)
:value (:account (:query-params request))
:value-fn :db/id
:content-fn #(:account/name (d-accounts/clientize (dc/pull (dc/db conn) d-accounts/default-read (:db/id %))
(:db/id (:client request))))}))
(bank-account-filter* request)
(date-range-field* request)
(com/field {:label "Description"}
(com/text-input {:name "description"
:id "description"
:class "hot-filter"
:value (:description (:query-params request))
:placeholder "e.g., Groceries"
:size :small}))
(com/field {:label "Description"}
(com/text-input {:name "description"
:id "description"
:class "hot-filter"
:value (:description (:query-params request))
:placeholder "e.g., Groceries"
:size :small}))
(com/field {:label "Amount"}
[:div.flex.space-x-4.items-baseline
(com/money-input {:name "amount-gte"
:id "amount-gte"
:hx-preserve "true"
:class "hot-filter w-20"
:value (:amount-gte (:query-params request))
:placeholder "0.01"
:size :small})
[:div.align-baseline
"to"]
(com/money-input {:name "amount-lte"
:hx-preserve "true"
:id "amount-lte"
:class "hot-filter w-20"
:value (:amount-lte (:query-params request))
:placeholder "9999.34"
:size :small})])
(exact-match-id* request)]])
(com/field {:label "Location"}
(com/text-input {:name "location"
:id "location"
:class "hot-filter"
:value (:location (:query-params request))
:placeholder "SC"
:size :small}))
(com/field {:label "Amount"}
[:div.flex.space-x-4.items-baseline
(com/money-input {:name "amount-gte"
:id "amount-gte"
:hx-preserve "true"
:class "hot-filter w-20"
:value (:amount-gte (:query-params request))
:placeholder "0.01"
:size :small})
[:div.align-baseline
"to"]
(com/money-input {:name "amount-lte"
:hx-preserve "true"
:id "amount-lte"
:class "hot-filter w-20"
:value (:amount-lte (:query-params request))
:placeholder "9999.34"
:size :small})])
(com/field {:label "Linking"}
(com/radio-card {:size :small
:name "linked-to"
:value (or (:linked-to (:query-params request)) "")
:options [{:value ""
:content "All"}
{:value "none"
:content "None"}
{:value "invoice"
:content "Invoice"}
{:value "expected-deposit"
:content "Expected Deposit"}
{:value "payment"
:content "Payment"}]}))
(when (is-admin? (:identity request))
[:div.mt-4 {:x-data (hx/json {:unresolvedOnly (:unresolved (:query-params request))})}
(com/hidden {:name "unresolved"
":value" "unresolvedOnly ? 'on' : ''"})
(com/checkbox {:value (:unresolved (:query-params request))
:x-model "unresolvedOnly"}
"Unresolved only")])
(when (and (is-admin? (:identity request))
(:db/id (:bank-account (:query-params request))))
[:div.mt-4 {:x-data (hx/json {:potentialDuplicates (:potential-duplicates (:query-params request))})}
(com/hidden {:name "potential-duplicates"
":value" "potentialDuplicates ? 'on' : ''"})
(com/checkbox {:value (:potential-duplicates (:query-params request))
:x-model "potentialDuplicates"}
"Same Amount + Date")])
(import-batch-id* request)
(exact-match-id* request)]])
(def grid-page
@@ -283,10 +420,11 @@
:page-specific-nav filters
:fetch-page fetch-page
:query-schema query-schema
:oob-render
(fn [request]
[(assoc-in (date-range-field* request) [1 :hx-swap-oob] true)
(assoc-in (exact-match-id* request) [1 :hx-swap-oob] true)])
:oob-render
(fn [request]
[(assoc-in (date-range-field* request) [1 :hx-swap-oob] true)
(assoc-in (exact-match-id* request) [1 :hx-swap-oob] true)
(some-> (import-batch-id* request) (assoc-in [1 :hx-swap-oob] true))])
:action-buttons (fn [request]
[
(com/button {:color :primary

File diff suppressed because it is too large Load Diff

View File

@@ -29,9 +29,10 @@
} }
"/edit-submit" ::edit-submit
"/location-select" ::location-select
"/account-total" ::account-total
"/account-balance" ::account-balance
"/edit-wizard-new-account" ::edit-wizard-new-account
"/account-total" ::account-total
"/account-balance" ::account-balance
"/toggle-amount-mode" ::toggle-amount-mode
"/edit-wizard-new-account" ::edit-wizard-new-account
"/match-payment" ::link-payment
"/match-autopay-invoices" ::link-autopay-invoices
"/match-unpaid-invoices" ::link-unpaid-invoices