5 Commits

Author SHA1 Message Date
Bryce
b02aec3546 Move sales summaries from admin to pos menu
- Move SSR handler from auto-ap.ssr.admin.sales-summaries to auto-ap.ssr.pos.sales-summaries
- Move route namespace from auto-ap.routes.admin.sales-summaries to auto-ap.routes.pos.sales-summaries
- Update nav to use main-aside-nav with POS breadcrumbs
- Use pos.common date-range-field* filter component
- Remove wrap-admin/wrap-client-redirect-unauthenticated from middleware
- Add Summaries to Sales sidebar menu
2026-05-16 00:13:42 -07:00
Bryce
5a39a0c762 fixes for sales summaries being automatic. 2026-05-15 23:22:38 -07:00
95f12a6072 refactor: remove dead calc-aggregate-totals and unused schema attributes
The 13 sales-summary/total-* attributes were computed and stored but never
read — the only consumer (get-debits) was commented out. Active display code
computes totals on-the-fly from the items list instead.
2026-05-15 23:22:38 -07:00
0e76506c22 consolidate sales summary ledger entry creation into upsert-sales-summary tx
Move journal entry calculation and creation from the reconcile-ledger
background job into the upsert-sales-summary tx function. Now any save
of a sales summary (job recalculation, admin edit wizard, or manual
touch) automatically creates the journal entry if balanced with all
accounts mapped, or retracts it if conditions no longer hold. Eliminates
the need for a separate upsert-sales-summary-ledger call and the
reconcile ledger pass for sales summaries.
2026-05-15 23:22:38 -07:00
baf8cfff97 feat: complete automatic sales summary calculations and ledger posting 2026-05-15 23:22:38 -07:00
7 changed files with 230 additions and 328 deletions

File diff suppressed because one or more lines are too long

View File

@@ -177,9 +177,8 @@
(conj [:paragraph {:color [128 0 0] :size 9} (:warning report)])
(conj
(table->pdf report
(cond-> (into [30 ] (repeat client-count 13))
(:include-comparison args) (into (repeat (* 2 client-count) 13))
(and (> client-count 1) (not (:include-comparison args))) (conj 13)))))
(cond-> (into [30 ] (repeat client-count 13))
(:include-comparison args) (into (repeat (* 2 client-count) 13))))))
output-stream)
(.toByteArray output-stream)))

View File

@@ -120,8 +120,7 @@
(list
[:div.text-2xl.font-bold.text-gray-600 (str "Balance Sheet - " (str/join ", " (map :client/name client))) ]
(rtable/table {:widths (cond-> (into [30 ] (repeat 13 client-count))
(> (count date) 1) (into (repeat 13 (* 2 client-count (dec (count date)))))
(and (> client-count 1) (= (count date) 1)) (conj 13))
(> (count date) 1) (into (repeat 13 (* 2 client-count (dec (count date))))))
:investigate-url (bidi.bidi/path-for ssr-routes/only-routes ::route/investigate)
:table report
:warning (not-empty (str/join "\n " (filter not-empty [warning (:warning report)])))} ))))])
@@ -202,9 +201,8 @@
(conj [:paragraph {:color [128 0 0] :size 9} (:warning report)])
(conj
(table->pdf report
(cond-> (into [30 ] (repeat client-count 13))
(> (count date) 1) (into (repeat (* 2 client-count (dec (count date))) 13 ))
(and (> client-count 1) (= (count date) 1)) (conj 13)))))
(cond-> (into [30 ] (repeat client-count 13))
(> (count date) 1) (into (repeat (* 2 client-count (dec (count date))) 13 ))))))
output-stream)
(.toByteArray output-stream)))

View File

@@ -1,17 +1,15 @@
(ns auto-ap.ssr.pos.sales-summaries
(:require
[auto-ap.datomic
:refer [apply-pagination apply-sort-3 conn merge-query pull-many
query2]]
:refer [apply-pagination apply-sort-3 conn merge-query pull-many
query2]]
[auto-ap.datomic.accounts :as d-accounts]
[auto-ap.graphql.utils :refer [extract-client-ids]]
[auto-ap.query-params :refer [wrap-copy-qp-pqp]]
[auto-ap.client-routes :as client-routes]
[auto-ap.routes.pos.sales-summaries :as route]
[auto-ap.ssr-routes :as ssr-routes]
[auto-ap.ssr.common-handlers :refer [add-new-entity-handler]]
[auto-ap.ssr.components :as com]
[auto-ap.ssr.components.link-dropdown :refer [link-dropdown]]
[auto-ap.ssr.components.multi-modal :as mm]
[auto-ap.ssr.form-cursor :as fc]
[auto-ap.ssr.grid-page-helper :as helper :refer [wrap-apply-sort]]
@@ -20,35 +18,35 @@
:refer [date-range-field*]]
[auto-ap.ssr.svg :as svg]
[auto-ap.ssr.utils
:refer [apply-middleware-to-all-handlers clj-date-schema
default-grid-fields-schema entity-id html-response money
strip temp-id wrap-merge-prior-hx wrap-schema-enforce]]
:refer [apply-middleware-to-all-handlers clj-date-schema
default-grid-fields-schema entity-id html-response money
strip temp-id wrap-merge-prior-hx wrap-schema-enforce]]
[auto-ap.time :as atime]
[bidi.bidi :as bidi]
[clj-time.coerce :as c]
[clojure.string :as str]
[datomic.api :as dc]
[hiccup.util :as hu]
[iol-ion.query :refer [dollars= dollars-0?]]
[iol-ion.query :refer [dollars=]]
[malli.core :as mc]
[malli.util :as mut]))
(def query-schema (mc/schema
[:maybe
(into [:map {:date-range [:date-range :start-date :end-date]}
[:start-date {:optional true}
[:maybe clj-date-schema]]
[:end-date {:optional true}
[:maybe clj-date-schema]]]
[:maybe clj-date-schema]] ]
default-grid-fields-schema)]))
(defn filters [request]
[:form {"hx-trigger" "change delay:500ms, keyup changed from:.hot-filter delay:1000ms"
"hx-get" (bidi/path-for ssr-routes/only-routes
::route/table)
"hx-target" "#entity-table"
"hx-indicator" "#entity-table"}
[:form {"hx-trigger" "change delay:500ms, keyup changed from:.hot-filter delay:1000ms"
"hx-get" (bidi/path-for ssr-routes/only-routes
::route/table)
"hx-target" "#entity-table"
"hx-indicator" "#entity-table"}
[:fieldset.space-y-6
(date-range-field* request)]])
@@ -57,14 +55,15 @@
*
[:sales-summary/date :xform clj-time.coerce/from-date]
{:sales-summary/client [:client/code :client/name :db/id]}
{:sales-summary/items [{[:ledger-mapped/ledger-side :xform iol-ion.query/ident] [:db/ident]}
:ledger-mapped/account
:ledger-mapped/amount
:sales-summary-item/category
:sales-summary-item/sort-order
:db/id
:sales-summary-item/manual?]}
{:journal-entry/original-entity [:db/id]}])
{:sales-summary/items [{[:ledger-mapped/ledger-side :xform iol-ion.query/ident] [:db/ident]
}
:ledger-mapped/account
:ledger-mapped/amount
:sales-summary-item/category
:sales-summary-item/sort-order
:db/id
:sales-summary-item/manual?]
} ])
(defn fetch-ids [db request]
(let [query-params (:query-params request)
@@ -73,26 +72,27 @@
(:client-id query-params)
(when (:client-code query-params)
[:client/code (:client-code query-params)]))
query (cond-> {:query {:find []
:in '[$ [?client ...]]
query (cond-> {:query {:find []
:in '[$ [?client ...]]
:where '[[?e :sales-summary/client ?client]]}
:args [db valid-clients]}
:args [db valid-clients]}
(or (:start-date query-params)
(:end-date query-params))
(merge-query {:query '{:where [[?e :sales-summary/date ?d]]}})
(:start-date query-params)
(merge-query {:query '{:in [?start-date]
(merge-query {:query '{:in [?start-date]
:where [[(>= ?d ?start-date)]]}
:args [(-> query-params :start-date c/to-date)]})
:args [(-> query-params :start-date c/to-date)]})
(:end-date query-params)
(merge-query {:query '{:in [?end-date]
(merge-query {:query '{:in [?end-date]
:where [[(< ?d ?end-date)]]}
:args [(-> query-params :end-date c/to-date)]})
:args [(-> query-params :end-date c/to-date)]})
true
(merge-query {:query {:find ['?sort-default '?e]
(merge-query {:query {:find ['?sort-default '?e]
:where ['[?e :sales-summary/date ?sort-default]]}}))]
(cond->> (query2 query)
true (apply-sort-3 query-params)
@@ -107,7 +107,7 @@
refunds))
(defn fetch-page [request]
(let [db (dc/db conn)
(let [db (dc/db conn)
{ids-to-retrieve :ids matching-count :count} (fetch-ids db request)]
[(->> (hydrate-results ids-to-retrieve db request))
@@ -116,29 +116,27 @@
(defn sort-items [ss]
(sort-by (juxt :ledger-mapped/ledger-side :sales-summary-item/sort-order :sales-summary-item/category) ss))
(defn total-debits [items]
(->> items
(->> items
(filter #(= :ledger-side/debit (:ledger-mapped/ledger-side %)))
(map #(:ledger-mapped/amount % 0.0))
(reduce + 0.0)))
(defn total-credits [items]
(->> items
(->> items
(filter #(= :ledger-side/credit (:ledger-mapped/ledger-side %)))
(map #(:ledger-mapped/amount % 0.0))
(reduce + 0.0)))
(defn truncate [s max-len]
(if (> (count s) max-len)
(str (subs s 0 (- max-len 3)) "...")
s))
(def grid-page
(helper/build {:id "entity-table"
(helper/build {:id "entity-table"
:id-fn :db/id
:nav com/main-aside-nav
:fetch-page fetch-page
:page-specific-nav filters
:nav com/main-aside-nav
:fetch-page fetch-page
:page-specific-nav filters
:query-schema query-schema
:row-buttons (fn [_ entity]
[(com/icon-button {:hx-get (bidi/path-for ssr-routes/only-routes
@@ -146,138 +144,84 @@
:db/id (:db/id entity))}
svg/pencil)])
:oob-render
(fn [request]
[(assoc-in (date-range-field* request) [1 :hx-swap-oob] true)])
:breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes
:company)}
"POS"]
(fn [request]
[(assoc-in (date-range-field* request) [1 :hx-swap-oob] true)])
:breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes
:company)}
"POS"]
[:a {:href (bidi/path-for ssr-routes/only-routes
::route/page)}
"Sales Summaries"]]
:title "Sales Summaries"
[:a {:href (bidi/path-for ssr-routes/only-routes
::route/page)}
"Sales Summaries"]]
:title "Sales Summaries"
:entity-name "Daily Summary"
:route ::route/table
:headers [{:key "client"
:name "Client"
:sort-key "client"
:hide? (fn [args]
(= (count (:clients args)) 1))
:render #(-> % :sales-summary/client :client/code)}
:route ::route/table
:headers [{:key "client"
:name "Client"
:sort-key "client"
:hide? (fn [args]
(= (count (:clients args)) 1))
:render #(-> % :sales-summary/client :client/code)}
{:key "date"
:name "Date"
:sort-key "date"
:render #(some-> % :sales-summary/date (atime/unparse-local atime/normal-date))}
{:key "debits"
:name "Debits"
:sort-key "debits"
:class "w-72 align-top"
:render (fn [ss]
(let [items (:sales-summary/items ss)
debit-items (filter #(= :ledger-side/debit (:ledger-mapped/ledger-side %)) (sort-items items))
credit-count (count (filter #(= :ledger-side/credit (:ledger-mapped/ledger-side %)) items))
total-debits (total-debits items)]
[:div.flex.flex-col.h-full
[:ul.flex-grow
(for [si debit-items]
[:li.flex.items-baseline.gap-2.py-0.5.text-sm.text-gray-700
[:span.flex-1.min-w-0.truncate.text-gray-600
(:sales-summary-item/category si)]
(when-not (:ledger-mapped/account si)
[:span.shrink-0 (com/pill {:color :red} "?")])
[:span.shrink-0.font-mono.tabular-nums.text-right.text-gray-900.whitespace-nowrap
(format "$%,.2f" (:ledger-mapped/amount si))]])
(for [_ (range (max 0 (- credit-count (count debit-items))))]
[:li.py-0.5.text-sm " "])]
[:div.border-t-2.border-gray-300.mt-1.pt-1.flex.justify-between.items-baseline
[:span.text-xs.uppercase.tracking-wider.font-semibold.text-gray-500 "Total"]
[:span.font-mono.tabular-nums.font-bold.text-gray-900
(format "$%,.2f" total-debits)]]]))}
{:key "date"
:name "Date"
:sort-key "date"
:render #(some-> % :sales-summary/date (atime/unparse-local atime/normal-date))}
{:key "credits"
:name "Credits"
:sort-key "credits"
:class "w-72 align-top"
:render (fn [ss]
(let [items (:sales-summary/items ss)
credit-items (filter #(= :ledger-side/credit (:ledger-mapped/ledger-side %)) (sort-items items))
debit-count (count (filter #(= :ledger-side/debit (:ledger-mapped/ledger-side %)) items))
total-credits (total-credits items)]
[:div.flex.flex-col.h-full
[:ul.flex-grow
(for [si credit-items]
[:li.flex.items-baseline.gap-2.py-0.5.text-sm.text-gray-700
[:span.flex-1.min-w-0.truncate.text-gray-600
(:sales-summary-item/category si)]
(when-not (:ledger-mapped/account si)
[:span.shrink-0 (com/pill {:color :red} "?")])
[:span.shrink-0.font-mono.tabular-nums.text-right.text-gray-900.whitespace-nowrap
(format "$%,.2f" (:ledger-mapped/amount si))]])
(for [_ (range (max 0 (- debit-count (count credit-items))))]
[:li.py-0.5.text-sm " "])]
[:div.border-t-2.border-gray-300.mt-1.pt-1.flex.justify-between.items-baseline
[:span.text-xs.uppercase.tracking-wider.font-semibold.text-gray-500 "Total"]
[:span.font-mono.tabular-nums.font-bold.text-gray-900
(format "$%,.2f" total-credits)]]]))}
{:key "balance"
:name "Status"
:sort-key "balance"
:class "w-28 align-top"
:render (fn [ss]
(let [items (:sales-summary/items ss)
total-debits (total-debits items)
total-credits (total-credits items)
delta (- total-debits total-credits)
balanced? (dollars= total-debits total-credits)
missing-account? (some #(not (:ledger-mapped/account %)) items)]
[:div.flex.flex-col.items-center.gap-1.pt-2
(when missing-account?
[:span.inline-block.text-xs.font-semibold.uppercase.tracking-wider.text-amber-800.bg-amber-100.border.border-amber-300.rounded-sm.px-1.5.py-0.5
"Missing acct"])
(if balanced?
(when-not missing-account?
[:span.inline-block.text-xs.font-semibold.uppercase.tracking-wider.text-emerald-800.bg-emerald-100.border.border-emerald-300.rounded-sm.px-1.5.py-0.5
"Balanced"])
[:div.flex.flex-col.items-center
[:span.font-mono.tabular-nums.text-red-700.font-bold.text-sm
(format "$%,.2f" (Math/abs delta))]
[:span.text-xs.uppercase.tracking-wider.text-red-600.font-medium.mt-0.5
(if (> total-debits total-credits) "Debit over" "Credit over")]])]))}
{:key "links"
:name "Links"
:show-starting "lg"
:class "w-8"
:render (fn [ss]
(let [ledger-entry (:journal-entry/original-entity ss)]
(when (seq ledger-entry)
(link-dropdown
[{:link (hu/url (bidi/path-for client-routes/routes :ledger)
{:exact-match-id (:db/id (first ledger-entry))})
:color :yellow
:content "Ledger entry"}]))))}]}))
{:key "debits"
:name "debits"
:sort-key "debits"
:render (fn [ss]
(let [total-debits (total-debits (:sales-summary/items ss))
total-credits (total-credits (:sales-summary/items ss))]
[:ul
(for [si (sort-items (:sales-summary/items ss))
:when (= :ledger-side/debit (:ledger-mapped/ledger-side si))]
[:li (:sales-summary-item/category si) ": " (format "$%,.2f" (:ledger-mapped/amount si))
(when-not (:ledger-mapped/account si)
[:span.pl-4 (com/pill {:color :red}
"missing account")])]
)
[:li (com/pill {:color (if (dollars= total-debits total-credits)
:primary
:red)} "Total: " (format "$%,.2f" total-debits))]]))}
{:key "credits"
:name "credits"
:sort-key "credits"
:render (fn [ss]
(let [total-debits (total-debits (:sales-summary/items ss))
total-credits (total-credits (:sales-summary/items ss))]
[:ul
(for [si (sort-items (:sales-summary/items ss))
:when (= :ledger-side/credit (:ledger-mapped/ledger-side si))]
[:li (:sales-summary-item/category si) ": " (format "$%,.2f" (:ledger-mapped/amount si))
(when-not (:ledger-mapped/account si)
[:span.pl-4 (com/pill {:color :red}
"missing account")])])
[:li (com/pill {:color (if (dollars= total-debits total-credits)
:primary
:red)} "Total: " (format "$%,.2f" total-credits))]]))}]}))
(def row* (partial helper/row* grid-page))
(def table* (partial helper/table* grid-page))
(def edit-schema
[:map
[:db/id entity-id]
[:db/id entity-id]
[:sales-summary/client [:map [:db/id entity-id]]]
[:sales-summary/items
[:sales-summary/items
[:vector {:coerce? true}
[:and
[:map
[:and
[:map
[:db/id [:or entity-id temp-id]]
[:sales-summary-item/category [:string {:decode/string strip}]]
[:sales-summary-item/manual? {:default false :decode/arbitrary (fn [x] (cond
[:sales-summary-item/manual? {:default false :decode/arbitrary (fn [x] (cond
(boolean? x)
x
(nil? x)
false
false
(str/blank? x)
false
:else
@@ -286,10 +230,11 @@
[:credit {:optional true} [:maybe money]]
[:debit {:optional true} [:maybe money]]]
[:fn {:error/message "Must choose one of credit/debit"
:error/path [:credit]}
:error/path [:credit]}
(fn [x]
(not (and (:credit x)
(:debit x))))]]]]])
(:debit x))))]]]] ])
(defn summary-total-row* [request]
(let [total-credits (-> request
@@ -304,22 +249,16 @@
(total-debits))]
(com/data-grid-row {:id "total-row"
:class "bg-slate-50 border-t-2 border-slate-300"
:hx-trigger "change from:closest form target:.amount-field"
:hx-put (bidi.bidi/path-for ssr-routes/only-routes ::route/expense-account-total)
:hx-target "this"
:hx-swap "innerHTML"}
(com/data-grid-cell {})
(com/data-grid-cell {:class "text-right"} [:span.font-bold.text-right "TOTAL"])
(com/data-grid-cell {:class "text-right"}
[:span.text-xs.uppercase.tracking-wider.font-semibold.text-slate-600
"Total"])
(format "$%,.2f" total-debits))
(com/data-grid-cell {:class "text-right"}
[:span.font-mono.tabular-nums.font-bold.text-slate-900
(format "$%,.2f" total-debits)])
(com/data-grid-cell {:class "text-right"}
[:span.font-mono.tabular-nums.font-bold.text-slate-900
(format "$%,.2f" total-credits)])
(com/data-grid-cell {}))))
(format "$%,.2f" total-credits)))))
(defn unbalanced-row* [request]
(let [total-credits (-> request
@@ -331,52 +270,44 @@
:multi-form-state
:step-params
:sales-summary/items
(total-debits))
unbalanced? (not (dollars= total-credits total-debits))
debit-over? (and unbalanced? (> total-debits total-credits))
credit-over? (and unbalanced? (> total-credits total-debits))]
(total-debits))]
(com/data-grid-row {:id "unbalanced-row"
:class (when unbalanced? "bg-red-50 border-t border-red-200")
(com/data-grid-row {:id "total-row"
:hx-trigger "change from:closest form target:.amount-field"
:hx-put (bidi.bidi/path-for ssr-routes/only-routes ::route/expense-account-total)
:hx-target "this"
:hx-swap "innerHTML"}
(com/data-grid-cell {})
(com/data-grid-cell {:class "text-right"} [:span.font-bold.text-right "UNBALANCED"])
(com/data-grid-cell {:class "text-right"}
(when unbalanced?
[:span.text-xs.uppercase.tracking-wider.font-semibold.text-red-700
"Out of balance"]))
(when (and
(not (dollars= total-credits total-debits))
(> total-debits total-credits))
(format "$%,.2f" (- total-debits total-credits))))
(com/data-grid-cell {:class "text-right"}
(when debit-over?
[:span.font-mono.tabular-nums.font-bold.text-red-700
(format "$%,.2f" (- total-debits total-credits))]))
(com/data-grid-cell {:class "text-right"}
(when credit-over?
[:span.font-mono.tabular-nums.font-bold.text-red-700
(format "$%,.2f" (- total-credits total-debits))]))
(com/data-grid-cell {}))))
(when
(and (not (dollars= total-credits total-debits))
(> total-credits total-debits))
(format "$%,.2f" (- total-credits total-debits)))))))
(defn- account-typeahead*
[{:keys [name value client-id]}]
[:div.flex.flex-col
(com/typeahead {:name name
(com/typeahead {:name name
:placeholder "Search..."
:url (hu/url (bidi/path-for ssr-routes/only-routes :account-search)
{:client-id client-id
:purpose "invoice"})
:value value
:content-fn (fn [value]
(:account/name (d-accounts/clientize (dc/pull (dc/db conn) d-accounts/default-read value)
client-id)))})])
:url (hu/url (bidi/path-for ssr-routes/only-routes :account-search)
{:client-id client-id
:purpose "invoice"})
:value value
:content-fn (fn [value]
(:account/name (d-accounts/clientize (dc/pull (dc/db conn) d-accounts/default-read value)
client-id)))})])
(defn sales-summary-item-row* [{:keys [value client-id]}]
(let [manual? (fc/field-value (:sales-summary-item/manual? value))]
(com/data-grid-row (cond-> {:x-ref "p"
:x-data (hx/json {})
:class (when manual?
"bg-indigo-50/40 border-l-2 border-indigo-300")}
(fc/field-value (:new? value)) (hx/htmx-transition-appear))
(com/data-grid-row (cond-> {:x-ref "p"
:x-data (hx/json {})}
(fc/field-value (:new? value)) (hx/htmx-transition-appear ))
(fc/with-field :db/id
(com/hidden {:name (fc/field-name)
:value (fc/field-value)}))
@@ -384,51 +315,48 @@
(fc/with-field :sales-summary-item/manual?
(com/hidden {:name (fc/field-name)
:value true})))
(com/data-grid-cell {:class "align-top"}
(com/data-grid-cell {}
(fc/with-field :sales-summary-item/category
(if manual?
(com/validated-field {:errors (fc/field-errors)}
(com/text-input {:placeholder "Category/Explanation"
:name (fc/field-name)
:value (fc/field-value)}))
(com/text-input {:placeholder "Category/Explanation"
:name (fc/field-name)
:value (fc/field-value)}))
(list
(com/hidden {:name (fc/field-name)
:value (fc/field-value)})
[:span.text-sm.text-gray-700
(fc/field-value (:sales-summary-item/category value))]))))
(com/data-grid-cell {:class "align-top"}
(fc/field-value (:sales-summary-item/category value))))))
(com/data-grid-cell {}
(fc/with-field :ledger-mapped/account
(com/validated-field {:errors (fc/field-errors)}
(account-typeahead* {:value (fc/field-value)
(account-typeahead* {:value (fc/field-value)
:client-id client-id
:name (fc/field-name)}))))
(com/data-grid-cell {:class "text-right align-top"}
:name (fc/field-name)}))))
(com/data-grid-cell {:class "text-right"}
(if manual?
(fc/with-field :debit
(com/validated-field {:errors (fc/field-errors)}
(com/money-input {:class "w-24 text-right font-mono tabular-nums"
(com/money-input {:class "w-24"
:name (fc/field-name)
:value (fc/field-value)})))
(when (= (fc/field-value (:ledger-mapped/ledger-side value))
:ledger-side/debit)
[:span.font-mono.tabular-nums.text-gray-900.text-sm.whitespace-nowrap
(format "$%,.2f" (fc/field-value (:ledger-mapped/amount value)))])))
(com/data-grid-cell {:class "text-right align-top"}
(format "$%,.2f" (fc/field-value (:ledger-mapped/amount value))))))
(com/data-grid-cell {:class "text-right"}
(if manual?
(fc/with-field :credit
(com/validated-field {:errors (fc/field-errors)}
(com/money-input {:class "w-24 text-right font-mono tabular-nums"
(com/money-input {:class "w-24"
:name (fc/field-name)
:value (fc/field-value)})))
(when (= (fc/field-value (:ledger-mapped/ledger-side value))
:ledger-side/credit)
[:span.font-mono.tabular-nums.text-gray-900.text-sm.whitespace-nowrap
(format "$%,.2f" (fc/field-value (:ledger-mapped/amount value)))])))
(format "$%,.2f" (fc/field-value (:ledger-mapped/amount value))))))
(com/data-grid-cell {:class "align-top"}
(when manual?
(when manual?
(com/a-icon-button {"@click.prevent.stop" "$refs.p.remove()"} svg/x))))))
(defrecord MainStep [linear-wizard]
@@ -442,45 +370,45 @@
[])
(step-schema [_]
(mut/select-keys (mm/form-schema linear-wizard) #{:db/id :sales-summary/items}))
(mut/select-keys (mm/form-schema linear-wizard) #{:db/id :sales-summary/items}))
(render-step
[this {:keys [multi-form-state] :as request}]
(mm/default-render-step
linear-wizard this
:head [:div.p-2 "New invoice"]
:body (mm/default-step-body
{}
[:div
(fc/with-field :db/id
(com/hidden {:name (fc/field-name)
:value (fc/field-value)}))
(com/data-grid {:headers
[(com/data-grid-header {} "Category")
(com/data-grid-header {} "Account")
(com/data-grid-header {} "Debits")
(com/data-grid-header {} "Credits")
(com/data-grid-header {} "")]}
(fc/with-field :sales-summary/items
(list
(fc/cursor-map #(sales-summary-item-row* {:value %
:client-id (:db/id (:sales-summary/client (:snapshot multi-form-state)))}))
(com/data-grid-new-row {:colspan 5
:hx-get (bidi/path-for ssr-routes/only-routes ::route/new-summary-item)
[this {:keys [multi-form-state] :as request}]
(mm/default-render-step
linear-wizard this
:head [:div.p-2 "New invoice"]
:body (mm/default-step-body
{}
[:div
(fc/with-field :db/id
(com/hidden {:name (fc/field-name)
:value (fc/field-value)}))
(com/data-grid {:headers
[(com/data-grid-header {} "Category")
(com/data-grid-header {} "Account")
(com/data-grid-header {} "Debits")
(com/data-grid-header {} "Credits")
(com/data-grid-header {} "")]}
(fc/with-field :sales-summary/items
(list
(fc/cursor-map #(sales-summary-item-row* {:value %
:client-id (:db/id (:sales-summary/client (:snapshot multi-form-state))) }))
(com/data-grid-new-row {:colspan 5
:hx-get (bidi/path-for ssr-routes/only-routes ::route/new-summary-item)
:row-offset 0
:index (count (fc/field-value))
:index (count (fc/field-value))
:tr-params {:hx-vals (hx/json {:client-id (:db/id (:sales-summary/client (:snapshot multi-form-state)))})}}
"New Summary Item")))
(summary-total-row* request)
(unbalanced-row* request))])
(summary-total-row* request)
(unbalanced-row* request)) ])
:footer
(mm/default-step-footer linear-wizard this :validation-route ::route/edit-wizard-navigate)
:validation-route ::route/edit-wizard-navigate
:width-height-class "lg:w-[920px] lg:h-[640px]")))
:footer
(mm/default-step-footer linear-wizard this :validation-route ::route/edit-wizard-navigate)
:validation-route ::route/edit-wizard-navigate
:width-height-class "lg:w-[850px] lg:h-[900px]")))
(defn attach-ledger [i]
(cond-> i
(cond-> i
(:credit i) (assoc :ledger-mapped/ledger-side :ledger-side/credit
:ledger-mapped/amount (:credit i))
(:debit i) (assoc :ledger-mapped/ledger-side :ledger-side/debit
@@ -496,8 +424,8 @@
(navigate [this step-key]
(assoc this :current-step step-key))
(get-current-step
[this]
(mm/get-step this :main))
[this]
(mm/get-step this :main))
(render-wizard [this {:keys [multi-form-state] :as request}]
(mm/default-render-wizard
this request
@@ -509,28 +437,29 @@
(steps [_]
[:main])
(get-step [this step-key]
(let [step-key-result (mc/parse mm/step-key-schema step-key)
(let [step-key-result (mc/parse mm/step-key-schema step-key)
[step-key-type step-key] step-key-result]
(->MainStep this)))
(form-schema [_]
edit-schema)
(submit [this {:keys [multi-form-state request-method identity] :as request}]
(let [result (:snapshot multi-form-state)
transaction [:upsert-sales-summary {:db/id (:db/id result)
:sales-summary/items (map
(fn [i]
(if (:sales-summary-item/manual? i)
(attach-ledger i)
{:db/id (:db/id i)
:ledger-mapped/account (:ledger-mapped/account i)}))
(:sales-summary/items result))}]]
(let [result (:snapshot multi-form-state )
transaction [:upsert-sales-summary {:db/id (:db/id result)
:sales-summary/items (map
(fn [i]
(if (:sales-summary-item/manual? i)
(attach-ledger i)
{:db/id (:db/id i)
:ledger-mapped/account (:ledger-mapped/account i)
}))
(:sales-summary/items result))}]]
(clojure.pprint/pprint (:sales-summary/items result))
@(dc/transact conn [transaction])
@(dc/transact conn [ transaction])
(html-response
(row* identity (dc/pull (dc/db conn) default-read (:db/id result))
(row* identity (dc/pull (dc/db conn) default-read (:db/id result))
{:flash? true
:request request})
:headers (cond-> {"hx-trigger" "modalclose"
:request request})
:headers (cond-> {"hx-trigger" "modalclose"
"hx-retarget" (format "#entity-table tr[data-id=\"%d\"]" (:db/id result))
"hx-reswap" "outerHTML"})))))
@@ -550,8 +479,8 @@
(def key->handler
(apply-middleware-to-all-handlers
(->>
{::route/page (helper/page-route grid-page)
::route/table (helper/table-route grid-page)
{::route/page (helper/page-route grid-page)
::route/table (helper/table-route grid-page)
::route/edit-wizard (-> mm/open-wizard-handler
(mm/wrap-wizard edit-wizard)
(mm/wrap-init-multi-form-state initial-edit-wizard-state)
@@ -569,9 +498,9 @@
(wrap-schema-enforce :query-schema [:map
[:client-id {:optional true}
[:maybe entity-id]]]))
::route/edit-wizard-submit (-> mm/submit-handler
(mm/wrap-wizard edit-wizard)
(mm/wrap-decode-multi-form-state))})
::route/edit-wizard-submit (-> mm/submit-handler
(mm/wrap-wizard edit-wizard)
(mm/wrap-decode-multi-form-state))})
(fn [h]
(-> h
(wrap-copy-qp-pqp)

View File

@@ -798,34 +798,30 @@
(defn balance-sheet-headers [pnl-data]
(let [period-count (count (:periods (:args pnl-data)))
client-ids (set (map :client-id (:data pnl-data)))
client-count (count client-ids)
show-total? (and (> client-count 1) (= 1 period-count))]
(let [period-count (count (:periods (:args pnl-data)))]
(cond-> []
(> client-count 1)
(conj (cond-> (into [{:value "Client"}]
(mapcat identity
(for [client client-ids]
(cond-> [{:value (str (-> pnl-data :client-codes (get client)))}]
(> period-count 1)
(into (apply concat (repeat (dec period-count) ["" ""])))))))
show-total? (conj {:value "Total" :bold true :border [:left]})))
(> (count (set (map :client-id (:data pnl-data)))) 1)
(conj (into [{:value "Client"}]
(mapcat identity
(for [client (set (map :client-id (:data pnl-data))) ]
(cond-> [{:value (str (-> pnl-data :client-codes (get client)))}]
(> period-count 1)
(into (apply concat (repeat (dec period-count) ["" ""]))))))))
true
(conj (cond-> (into [{:value "Period Ending"}]
(for [client client-ids
(conj (into [{:value "Period Ending"}]
(for [client (set (map :client-id (:data pnl-data)))
[index p] (map vector (range) (:periods (:args pnl-data)))
:let [is-first? (= 0 index)
period-date (date->str p)
period-headers (if (or is-first?
(not (:include-deltas (:args pnl-data))))
[{:value period-date}]
[{:value period-date}
{:value "+/-"}])]
[{:value period-date}]
[{:value period-date}
{:value "+/-"}])]
header period-headers]
header))
show-total? (conj {:value (date->str (first (:periods (:args pnl-data)))) :border [:left]}))))))
header))))))
(defn append-deltas [table]
(->> table
@@ -894,33 +890,12 @@
:rows table})))
)
(defn add-total-border [rows]
(map (fn [row]
(let [last-idx (dec (count row))]
(map-indexed
(fn [i cell]
(if (= i last-idx)
(let [borders (or (:border cell) [])]
(assoc cell :border (conj borders :left)))
cell))
row)))
rows))
(defn summarize-balance-sheet [pnl-data]
(let [client-ids (set (map :client-id (:data pnl-data)))
client-count (count client-ids)
period-count (count (:periods (:args pnl-data)))
show-total? (and (> client-count 1) (= 1 period-count))
pnl-datas (for [client-id client-ids
p (:periods (:args pnl-data))]
(-> pnl-data
(filter-client client-id)
(filter-period p)))
total-data (when show-total?
(-> pnl-data
(filter-period (first (:periods (:args pnl-data))))
(assoc :cell-args {:bold true})))
pnl-datas (concat pnl-datas (when total-data [total-data]))]
(let [pnl-datas (for [client-id (set (map :client-id (:data pnl-data)))
p (:periods (:args pnl-data))]
(-> pnl-data
(filter-client client-id)
(filter-period p)))]
(let [table (-> []
(into (detail-rows pnl-datas
:assets
@@ -937,11 +912,10 @@
(negate #{:cogs :payroll :controllable :fixed-overhead :ownership-controllable}))
pnl-datas)
"Retained Earnings")))
table (if (and (> period-count 1)
table (if (and (> (count (:periods (:args pnl-data))) 1)
(:include-deltas (:args pnl-data)))
(append-deltas table)
table)
table (if show-total? (add-total-border table) table)]
(append-deltas table)
table)]
{:warning (warning-message pnl-data)
:header (balance-sheet-headers pnl-data)
:rows table}))

View File

@@ -265,8 +265,7 @@ NOTE: Please review the transactions we may have question for you here: https://
[:div.notification.is-warning.is-light
(:warning report)])
[rtable/table {:widths (cond-> (into [30 ] (repeat 13 client-count))
(:include-comparison args) (into (repeat 13 (* 2 client-count)))
(and (> client-count 1) (not (:include-comparison args))) (conj 13))
(:include-comparison args) (into (repeat 13 (* 2 client-count))))
:click-event ::investigate-clicked
:table report}]]))

View File

@@ -1,2 +1,5 @@
#!/bin/bash
sudo docker run --rm -ti -v ~/dev/integreat/data/solr:/var/solr --network=bridge -p 8983:8983 679918342773.dkr.ecr.us-east-1.amazonaws.com/integreat-solr
sudo docker run --rm -ti -v ~/dev/integreat/data/solr:/var/solr --network=bridge -p 8983:8983 bryce-solr
#sudo podman container run --user 1000 --privileged --volume /home/notid/dev/integreat/data/solr:/var/solr -p 8983:8983 bryce-solr