Sales nearly ready
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -1879,32 +1879,37 @@
|
|||||||
:db/valueType :db.type/instant
|
:db/valueType :db.type/instant
|
||||||
:db/cardinality :db.cardinality/one
|
:db/cardinality :db.cardinality/one
|
||||||
:db/index true}
|
:db/index true}
|
||||||
{:db/ident :sales-summary/sales-items
|
{:db/ident :sales-summary/items
|
||||||
:db/valueType :db.type/ref
|
|
||||||
:db/isComponent true,
|
|
||||||
:db/cardinality :db.cardinality/many}
|
|
||||||
{:db/ident :sales-summary/payment-items
|
|
||||||
:db/valueType :db.type/ref
|
:db/valueType :db.type/ref
|
||||||
:db/isComponent true,
|
:db/isComponent true,
|
||||||
:db/cardinality :db.cardinality/many}
|
:db/cardinality :db.cardinality/many}
|
||||||
|
{:db/ident :sales-summary/dirty
|
||||||
|
:db/noHistory true,
|
||||||
|
:db/valueType :db.type/boolean
|
||||||
|
:db/cardinality :db.cardinality/one}
|
||||||
|
{:db/ident :sales-summary/client+date
|
||||||
|
:db/valueType :db.type/tuple
|
||||||
|
:db/tupleAttrs [:sales-summary/client :sales-summary/date]
|
||||||
|
:db/cardinality :db.cardinality/one
|
||||||
|
:db/unique :db.unique/identity
|
||||||
|
:db/index true}
|
||||||
|
{:db/ident :sales-summary/client+dirty
|
||||||
|
:db/valueType :db.type/tuple
|
||||||
|
:db/tupleAttrs [:sales-summary/client :sales-summary/dirty]
|
||||||
|
:db/cardinality :db.cardinality/one
|
||||||
|
:db/index true}
|
||||||
|
|
||||||
{:db/ident :sales-summary-item/category
|
{:db/ident :sales-summary-item/category
|
||||||
:db/valueType :db.type/string
|
:db/valueType :db.type/string
|
||||||
:db/cardinality :db.cardinality/one}
|
:db/cardinality :db.cardinality/one}
|
||||||
{:db/ident :ledger-mapped/ledger-side
|
{:db/ident :sales-summary-item/sort-order
|
||||||
:db/valueType :db.type/ref
|
:db/valueType :db.type/long
|
||||||
:db/cardinality :db.cardinality/one
|
:db/cardinality :db.cardinality/one
|
||||||
:db/noHistory true}
|
:db/noHistory true}
|
||||||
{:db/ident :ledger-mapped/account
|
{:db/ident :sales-summary-item/manual?
|
||||||
:db/valueType :db.type/ref
|
:db/valueType :db.type/boolean
|
||||||
:db/cardinality :db.cardinality/one
|
:db/cardinality :db.cardinality/one
|
||||||
:db/noHistory true}
|
:db/noHistory true}
|
||||||
{:db/ident :ledger-mapped/amount
|
|
||||||
:db/valueType :db.type/double
|
|
||||||
:db/cardinality :db.cardinality/one
|
|
||||||
:db/noHistory true}
|
|
||||||
{:db/ident :ledger-side/credit}
|
|
||||||
{:db/ident :ledger-side/debit}
|
|
||||||
{:db/ident :sales-summary-item/total
|
{:db/ident :sales-summary-item/total
|
||||||
:db/noHistory true,
|
:db/noHistory true,
|
||||||
:db/valueType :db.type/double
|
:db/valueType :db.type/double
|
||||||
@@ -1921,79 +1926,23 @@
|
|||||||
:db/noHistory true,
|
:db/noHistory true,
|
||||||
:db/valueType :db.type/double
|
:db/valueType :db.type/double
|
||||||
:db/cardinality :db.cardinality/one}
|
:db/cardinality :db.cardinality/one}
|
||||||
{:db/ident :sales-summary/dirty
|
|
||||||
:db/noHistory true,
|
|
||||||
:db/valueType :db.type/boolean
|
|
||||||
:db/cardinality :db.cardinality/one}
|
|
||||||
{:db/ident :sales-summary/client+date
|
|
||||||
:db/valueType :db.type/tuple
|
|
||||||
:db/tupleAttrs [:sales-summary/client :sales-summary/date]
|
|
||||||
:db/cardinality :db.cardinality/one
|
|
||||||
:db/unique :db.unique/identity
|
|
||||||
:db/index true}
|
|
||||||
{:db/ident :sales-summary/client+dirty
|
|
||||||
:db/valueType :db.type/tuple
|
|
||||||
:db/tupleAttrs [:sales-summary/client :sales-summary/dirty]
|
|
||||||
:db/cardinality :db.cardinality/one
|
|
||||||
:db/index true}
|
|
||||||
|
|
||||||
{:db/ident :sales-summary/discount
|
{:db/ident :ledger-mapped/ledger-side
|
||||||
:db/noHistory true,
|
|
||||||
:db/valueType :db.type/double
|
|
||||||
:db/cardinality :db.cardinality/one}
|
|
||||||
{:db/ident :sales-summary/total-card-payments
|
|
||||||
:db/noHistory true,
|
|
||||||
:db/valueType :db.type/double
|
|
||||||
:db/cardinality :db.cardinality/one}
|
|
||||||
{:db/ident :sales-summary/total-tax
|
|
||||||
:db/noHistory true,
|
|
||||||
:db/isComponent true
|
|
||||||
:db/valueType :db.type/ref
|
:db/valueType :db.type/ref
|
||||||
:db/cardinality :db.cardinality/one
|
:db/cardinality :db.cardinality/one
|
||||||
}
|
:db/noHistory true}
|
||||||
{:db/ident :sales-summary/total-tip
|
{:db/ident :ledger-mapped/account
|
||||||
:db/noHistory true,
|
|
||||||
:db/isComponent true
|
|
||||||
:db/valueType :db.type/ref
|
:db/valueType :db.type/ref
|
||||||
:db/cardinality :db.cardinality/one }
|
:db/cardinality :db.cardinality/one
|
||||||
{:db/ident :sales-summary/total-returns
|
:db/noHistory true}
|
||||||
:db/noHistory true,
|
{:db/ident :ledger-mapped/amount
|
||||||
:db/valueType :db.type/double
|
:db/valueType :db.type/double
|
||||||
:db/cardinality :db.cardinality/one}
|
:db/cardinality :db.cardinality/one
|
||||||
|
:db/noHistory true}
|
||||||
{:db/ident :sales-summary/total-card-fees
|
{:db/ident :ledger-side/credit}
|
||||||
:db/noHistory true,
|
{:db/ident :ledger-side/debit}
|
||||||
:db/valueType :db.type/double
|
|
||||||
:db/cardinality :db.cardinality/one}
|
|
||||||
{:db/ident :sales-summary/total-gift-card-payments
|
]
|
||||||
:db/noHistory true,
|
|
||||||
:db/valueType :db.type/double
|
|
||||||
:db/cardinality :db.cardinality/one}
|
|
||||||
|
|
||||||
{:db/ident :sales-summary/total-card-refunds
|
|
||||||
:db/noHistory true,
|
|
||||||
:db/valueType :db.type/double
|
|
||||||
:db/cardinality :db.cardinality/one}
|
|
||||||
{:db/ident :sales-summary/total-cash-payments
|
|
||||||
:db/noHistory true,
|
|
||||||
:db/valueType :db.type/double
|
|
||||||
:db/cardinality :db.cardinality/one}
|
|
||||||
{:db/ident :sales-summary/total-unknown-processor-payments
|
|
||||||
:db/noHistory true,
|
|
||||||
:db/valueType :db.type/double
|
|
||||||
:db/cardinality :db.cardinality/one}
|
|
||||||
|
|
||||||
{:db/ident :sales-summary/total-cash-refunds
|
|
||||||
:db/noHistory true,
|
|
||||||
:db/valueType :db.type/double
|
|
||||||
:db/cardinality :db.cardinality/one}
|
|
||||||
{:db/ident :sales-summary/total-food-app-payments
|
|
||||||
:db/noHistory true,
|
|
||||||
:db/valueType :db.type/double
|
|
||||||
:db/cardinality :db.cardinality/one}
|
|
||||||
{:db/ident :sales-summary/total-food-app-refunds
|
|
||||||
:db/noHistory true,
|
|
||||||
:db/valueType :db.type/double
|
|
||||||
:db/cardinality :db.cardinality/one}]
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
[clj-time.coerce :as c]
|
[clj-time.coerce :as c]
|
||||||
[clj-time.core :as time]
|
[clj-time.core :as time]
|
||||||
[clj-time.periodic :as per]
|
[clj-time.periodic :as per]
|
||||||
|
[clojure.string :as str]
|
||||||
[com.brunobonacci.mulog :as mu]
|
[com.brunobonacci.mulog :as mu]
|
||||||
[datomic.api :as dc]))
|
[datomic.api :as dc]))
|
||||||
|
|
||||||
@@ -31,6 +32,13 @@
|
|||||||
(dc/db conn))]
|
(dc/db conn))]
|
||||||
(apply mark-dirty c (last-n-days days))))
|
(apply mark-dirty c (last-n-days days))))
|
||||||
|
|
||||||
|
(defn lookup-account [number]
|
||||||
|
(ffirst (dc/q '[:find ?a
|
||||||
|
:in $ ?number
|
||||||
|
:where [?a :account/numeric-code ?number]]
|
||||||
|
(dc/db conn)
|
||||||
|
number)))
|
||||||
|
|
||||||
|
|
||||||
(defn delete-all []
|
(defn delete-all []
|
||||||
@(dc/transact-async conn
|
@(dc/transact-async conn
|
||||||
@@ -63,6 +71,30 @@
|
|||||||
c
|
c
|
||||||
date))
|
date))
|
||||||
0.0)))
|
0.0)))
|
||||||
|
|
||||||
|
(def name->number
|
||||||
|
{"gyros and pitas" 40111
|
||||||
|
"returns" 41000
|
||||||
|
"card payments" 75460
|
||||||
|
"cash payments" 75452
|
||||||
|
"food app payments" 72350
|
||||||
|
"unknown" 40000
|
||||||
|
"discounts" 41000
|
||||||
|
"fees" 75400
|
||||||
|
"alcohol" 46900
|
||||||
|
"beverages" 42000
|
||||||
|
"bowls" 40118
|
||||||
|
"catering" 43010
|
||||||
|
"desserts" 40116
|
||||||
|
"fries" 40117
|
||||||
|
"plates" 40113
|
||||||
|
"sides" 40115
|
||||||
|
"soup & salads" 40114
|
||||||
|
"uncategorized" 40000
|
||||||
|
"tax" 25700
|
||||||
|
"tip" 25500
|
||||||
|
"card refunds" 41000})
|
||||||
|
|
||||||
(defn get-payment-items [c date]
|
(defn get-payment-items [c date]
|
||||||
(->>
|
(->>
|
||||||
(dc/q '[:find ?processor ?type-name (sum ?total)
|
(dc/q '[:find ?processor ?type-name (sum ?total)
|
||||||
@@ -104,7 +136,9 @@
|
|||||||
{})
|
{})
|
||||||
(map (fn [[k v]]
|
(map (fn [[k v]]
|
||||||
{:db/id (str (java.util.UUID/randomUUID))
|
{:db/id (str (java.util.UUID/randomUUID))
|
||||||
|
:sales-summary-item/sort-order 0
|
||||||
:sales-summary-item/category k
|
:sales-summary-item/category k
|
||||||
|
|
||||||
:ledger-mapped/amount (if (= "Card Payments" k)
|
:ledger-mapped/amount (if (= "Card Payments" k)
|
||||||
(- v (get-fee c date))
|
(- v (get-fee c date))
|
||||||
v)
|
v)
|
||||||
@@ -119,9 +153,10 @@
|
|||||||
(dc/db conn)
|
(dc/db conn)
|
||||||
[[c] date date]))]
|
[[c] date date]))]
|
||||||
{:db/id (str (java.util.UUID/randomUUID))
|
{:db/id (str (java.util.UUID/randomUUID))
|
||||||
|
:sales-summary-item/sort-order 1
|
||||||
:sales-summary-item/category "Discounts"
|
:sales-summary-item/category "Discounts"
|
||||||
:ledger-mapped/amount discount
|
:ledger-mapped/amount discount
|
||||||
:ledger-mapped/ledger-side :ledger-side/debit }))
|
:ledger-mapped/ledger-side :ledger-side/debit}))
|
||||||
|
|
||||||
(defn get-refund-items [c date]
|
(defn get-refund-items [c date]
|
||||||
(->>
|
(->>
|
||||||
@@ -149,6 +184,7 @@
|
|||||||
{})
|
{})
|
||||||
(map (fn [[k v]]
|
(map (fn [[k v]]
|
||||||
{:db/id (str (java.util.UUID/randomUUID))
|
{:db/id (str (java.util.UUID/randomUUID))
|
||||||
|
:sales-summary-item/sort-order 3
|
||||||
:sales-summary-item/category k
|
:sales-summary-item/category k
|
||||||
:ledger-mapped/amount v
|
:ledger-mapped/amount v
|
||||||
:ledger-mapped/ledger-side :ledger-side/credit}))))
|
:ledger-mapped/ledger-side :ledger-side/credit}))))
|
||||||
@@ -158,12 +194,15 @@
|
|||||||
(defn get-fees [c date]
|
(defn get-fees [c date]
|
||||||
(when-let [fee (get-fee c date)]
|
(when-let [fee (get-fee c date)]
|
||||||
{:db/id (str (java.util.UUID/randomUUID))
|
{:db/id (str (java.util.UUID/randomUUID))
|
||||||
|
:sales-summary-item/sort-order 2
|
||||||
:sales-summary-item/category "Fees"
|
:sales-summary-item/category "Fees"
|
||||||
:ledger-mapped/amount fee
|
:ledger-mapped/amount fee
|
||||||
:ledger-mapped/ledger-side :ledger-side/debit}))
|
:ledger-mapped/ledger-side :ledger-side/debit}))
|
||||||
|
|
||||||
(defn- get-tax [c date]
|
(defn- get-tax [c date]
|
||||||
{:db/id (str (java.util.UUID/randomUUID))
|
{:db/id (str (java.util.UUID/randomUUID))
|
||||||
|
:sales-summary-item/category "Tax"
|
||||||
|
:sales-summary-item/sort-order 1
|
||||||
:ledger-mapped/ledger-side :ledger-side/credit
|
:ledger-mapped/ledger-side :ledger-side/credit
|
||||||
:ledger-mapped/amount
|
:ledger-mapped/amount
|
||||||
(or (ffirst (dc/q '[:find (sum ?tax)
|
(or (ffirst (dc/q '[:find (sum ?tax)
|
||||||
@@ -179,15 +218,19 @@
|
|||||||
|
|
||||||
(defn- get-tip [c date]
|
(defn- get-tip [c date]
|
||||||
{:ledger-mapped/ledger-side :ledger-side/credit
|
{:ledger-mapped/ledger-side :ledger-side/credit
|
||||||
:ledger-mapped/amount (or (ffirst (dc/q '[:find (sum ?tip)
|
:sales-summary-item/sort-order 2
|
||||||
:with ?c
|
:db/id (str (java.util.UUID/randomUUID))
|
||||||
:in $ [?clients ?start-date ?end-date]
|
|
||||||
:where [(iol-ion.query/scan-sales-orders $ ?clients ?start-date ?end-date) [[?e _ ?sort-default] ...]]
|
:sales-summary-item/category "Tip"
|
||||||
[?e :sales-order/charges ?c]
|
:ledger-mapped/amount (or (ffirst (dc/q '[:find (sum ?tip)
|
||||||
[?c :charge/tip ?tip]]
|
:with ?c
|
||||||
(dc/db conn)
|
:in $ [?clients ?start-date ?end-date]
|
||||||
[[c] date date]))
|
:where [(iol-ion.query/scan-sales-orders $ ?clients ?start-date ?end-date) [[?e _ ?sort-default] ...]]
|
||||||
0.0)})
|
[?e :sales-order/charges ?c]
|
||||||
|
[?c :charge/tip ?tip]]
|
||||||
|
(dc/db conn)
|
||||||
|
[[c] date date]))
|
||||||
|
0.0)})
|
||||||
|
|
||||||
(defn- get-sales [c date]
|
(defn- get-sales [c date]
|
||||||
(let [sales (->> (dc/q '[:find ?category (sum ?total) (sum ?tax) (sum ?discount)
|
(let [sales (->> (dc/q '[:find ?category (sum ?total) (sum ?tax) (sum ?discount)
|
||||||
@@ -204,6 +247,7 @@
|
|||||||
(for [[category total tax discount] sales]
|
(for [[category total tax discount] sales]
|
||||||
{:db/id (str (java.util.UUID/randomUUID))
|
{:db/id (str (java.util.UUID/randomUUID))
|
||||||
:sales-summary-item/category category
|
:sales-summary-item/category category
|
||||||
|
:sales-summary-item/sort-order 0
|
||||||
:sales-summary-item/total total
|
:sales-summary-item/total total
|
||||||
:sales-summary-item/net (- (+ total discount) tax)
|
:sales-summary-item/net (- (+ total discount) tax)
|
||||||
:sales-summary-item/tax tax
|
:sales-summary-item/tax tax
|
||||||
@@ -212,6 +256,23 @@
|
|||||||
:ledger-mapped/amount (- (+ total discount) tax)
|
:ledger-mapped/amount (- (+ total discount) tax)
|
||||||
#_#_:ledger-mapped/account nil})))
|
#_#_:ledger-mapped/account nil})))
|
||||||
|
|
||||||
|
(defn- get-returns [c date]
|
||||||
|
(when-let [amount (ffirst (dc/q '[:find (sum ?r)
|
||||||
|
:with ?e
|
||||||
|
:in $ [?clients ?start-date ?end-date]
|
||||||
|
:where [(iol-ion.query/scan-sales-orders $ ?clients ?start-date ?end-date) [[?e _ ?sort-default] ...]]
|
||||||
|
[?e :sales-order/returns ?r]
|
||||||
|
|
||||||
|
#_[?e :sales-order/charges ?c]
|
||||||
|
#_[?c :charge/tax ?tax]]
|
||||||
|
(dc/db conn)
|
||||||
|
[[c] date date]))]
|
||||||
|
{:db/id (str (java.util.UUID/randomUUID))
|
||||||
|
:sales-summary-item/category "Returns"
|
||||||
|
|
||||||
|
:ledger-mapped/amount amount
|
||||||
|
:ledger-mapped/ledger-side :ledger-side/debit}))
|
||||||
|
|
||||||
(defn sales-summaries-v2 []
|
(defn sales-summaries-v2 []
|
||||||
(doseq [[c client-code] (dc/q '[:find ?c ?client-code
|
(doseq [[c client-code] (dc/q '[:find ?c ?client-code
|
||||||
:in $
|
:in $
|
||||||
@@ -227,51 +288,25 @@
|
|||||||
:sales-summary/dirty false
|
:sales-summary/dirty false
|
||||||
:sales-summary/client+date [c date]
|
:sales-summary/client+date [c date]
|
||||||
|
|
||||||
:sales-summary/sales-items
|
:sales-summary/items
|
||||||
(conj (get-sales c date)
|
(->>
|
||||||
{:db/id (str (java.util.UUID/randomUUID))
|
(get-sales c date)
|
||||||
:sales-summary-item/category "Returns"
|
(concat (get-payment-items c date))
|
||||||
|
(concat (get-refund-items c date))
|
||||||
:ledger-mapped/amount (or (ffirst (dc/q '[:find (sum ?r)
|
(cons (get-discounts c date))
|
||||||
:with ?e
|
(cons (get-fees c date))
|
||||||
:in $ [?clients ?start-date ?end-date]
|
(cons (get-tax c date))
|
||||||
:where [(iol-ion.query/scan-sales-orders $ ?clients ?start-date ?end-date) [[?e _ ?sort-default] ...]]
|
(cons (get-tip c date))
|
||||||
[?e :sales-order/returns ?r]
|
(cons (get-returns c date))
|
||||||
|
(filter identity)
|
||||||
#_[?e :sales-order/charges ?c]
|
(map (fn [z]
|
||||||
#_[?c :charge/tax ?tax]]
|
(assoc z :ledger-mapped/account (some-> z :sales-summary-item/category str/lower-case name->number lookup-account)
|
||||||
(dc/db conn)
|
:sales-summary-item/manual? false))
|
||||||
[[c] date date]))
|
)) }]
|
||||||
0.0)
|
(if (seq (:sales-summary/items result))
|
||||||
:ledger-mapped/ledger-side :ledger-side/debit})
|
|
||||||
:sales-summary/payment-items
|
|
||||||
(->> (get-payment-items c date)
|
|
||||||
(concat (get-refund-items c date))
|
|
||||||
(cons (get-discounts c date))
|
|
||||||
(cons (get-fees c date))
|
|
||||||
(filter identity))
|
|
||||||
|
|
||||||
:sales-summary/total-tax
|
|
||||||
(get-tax c date)
|
|
||||||
:sales-summary/total-tip
|
|
||||||
(get-tip c date)
|
|
||||||
|
|
||||||
#_#_:sales-summary/total-card-payments
|
|
||||||
(or (ffirst (dc/q '[:find (sum ?total)
|
|
||||||
:with ?c
|
|
||||||
:in $ [?clients ?start-date ?end-date]
|
|
||||||
:where [(iol-ion.query/scan-sales-orders $ ?clients ?start-date ?end-date) [[?e _ ?sort-default] ...]]
|
|
||||||
[?e :sales-order/charges ?c]
|
|
||||||
[?c :charge/type-name "CARD"]
|
|
||||||
[?c :charge/total ?total]]
|
|
||||||
(dc/db conn)
|
|
||||||
[[c] date date]))
|
|
||||||
0.0)
|
|
||||||
#_:sales-summary/total-unknown-processor-payments}]
|
|
||||||
(if (seq (:sales-summary/sales-items result))
|
|
||||||
(do
|
(do
|
||||||
(alog/info ::upserting-summaries
|
(alog/info ::upserting-summaries
|
||||||
:category-count (count (:sales-summary/sales-items result)))
|
:category-count (count (:sales-summary/items result)))
|
||||||
@(dc/transact conn [[:upsert-entity result]]))
|
@(dc/transact conn [[:upsert-entity result]]))
|
||||||
@(dc/transact conn [{:db/id id :sales-summary/dirty false}]))))))
|
@(dc/transact conn [{:db/id id :sales-summary/dirty false}]))))))
|
||||||
|
|
||||||
@@ -281,199 +316,6 @@
|
|||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
#_(defn sales-summaries-v1 []
|
|
||||||
(doseq [[c client-code] (dc/q '[:find ?c ?client-code
|
|
||||||
:in $
|
|
||||||
:where [?c :client/code ?client-code]]
|
|
||||||
(dc/db conn))
|
|
||||||
{:sales-summary/keys [date] :db/keys [id]} (dirty-sales-summaries c)]
|
|
||||||
(mu/with-context {:client-code client-code
|
|
||||||
:date date}
|
|
||||||
(alog/info ::updating)
|
|
||||||
(let [sales (->> (dc/q '[:find ?item-name ?category (sum ?total) (sum ?tax) (sum ?discount)
|
|
||||||
:with ?e ?li
|
|
||||||
:in $ [?clients ?start-date ?end-date]
|
|
||||||
:where [(iol-ion.query/scan-sales-orders $ ?clients ?start-date ?end-date) [[?e _ ?sort-default] ...]]
|
|
||||||
[?e :sales-order/line-items ?li]
|
|
||||||
[(get-else $ ?li :order-line-item/item-name "Unknown") ?item-name]
|
|
||||||
[?li :order-line-item/category ?category]
|
|
||||||
[?li :order-line-item/total ?total]
|
|
||||||
[?li :order-line-item/tax ?tax]
|
|
||||||
[?li :order-line-item/discount ?discount]]
|
|
||||||
(dc/db conn)
|
|
||||||
[[c] date date]))
|
|
||||||
result {:db/id id
|
|
||||||
:sales-summary/client c
|
|
||||||
:sales-summary/date date
|
|
||||||
:sales-summary/dirty false
|
|
||||||
:sales-summary/client+date [c date]
|
|
||||||
:sales-summary/discount (or (ffirst (dc/q '[:find (sum ?discount)
|
|
||||||
:with ?e
|
|
||||||
:in $ [?clients ?start-date ?end-date]
|
|
||||||
:where [(iol-ion.query/scan-sales-orders $ ?clients ?start-date ?end-date) [[?e _ ?sort-default] ...]]
|
|
||||||
[?e :sales-order/discount ?discount]]
|
|
||||||
(dc/db conn)
|
|
||||||
[[c] date date]))
|
|
||||||
0.0)
|
|
||||||
:sales-summary/total-returns (or (ffirst (dc/q '[:find (sum ?r)
|
|
||||||
:with ?e
|
|
||||||
:in $ [?clients ?start-date ?end-date]
|
|
||||||
:where [(iol-ion.query/scan-sales-orders $ ?clients ?start-date ?end-date) [[?e _ ?sort-default] ...]]
|
|
||||||
[?e :sales-order/returns ?r]
|
|
||||||
|
|
||||||
#_[?e :sales-order/charges ?c]
|
|
||||||
#_[?c :charge/tax ?tax]]
|
|
||||||
(dc/db conn)
|
|
||||||
[[c] date date]))
|
|
||||||
0.0)
|
|
||||||
|
|
||||||
:sales-summary/sales-items
|
|
||||||
(for [[item-name category total tax discount] sales]
|
|
||||||
{:db/id (str (java.util.UUID/randomUUID))
|
|
||||||
:sales-summary-item/item-name item-name
|
|
||||||
:sales-summary-item/category category
|
|
||||||
:sales-summary-item/total total
|
|
||||||
:sales-summary-item/tax tax
|
|
||||||
:sales-summary-item/discount discount})
|
|
||||||
|
|
||||||
|
|
||||||
:sales-summary/total-tax
|
|
||||||
(or (ffirst (dc/q '[:find (sum ?tax)
|
|
||||||
:with ?e
|
|
||||||
:in $ [?clients ?start-date ?end-date]
|
|
||||||
:where [(iol-ion.query/scan-sales-orders $ ?clients ?start-date ?end-date) [[?e _ ?sort-default] ...]]
|
|
||||||
[?e :sales-order/tax ?tax]
|
|
||||||
#_[?e :sales-order/charges ?c]
|
|
||||||
#_[?c :charge/tax ?tax]]
|
|
||||||
(dc/db conn)
|
|
||||||
[[c] date date]))
|
|
||||||
0.0)
|
|
||||||
:sales-summary/total-tip
|
|
||||||
(or (ffirst (dc/q '[:find (sum ?tip)
|
|
||||||
:with ?c
|
|
||||||
:in $ [?clients ?start-date ?end-date]
|
|
||||||
:where [(iol-ion.query/scan-sales-orders $ ?clients ?start-date ?end-date) [[?e _ ?sort-default] ...]]
|
|
||||||
[?e :sales-order/charges ?c]
|
|
||||||
[?c :charge/tip ?tip]]
|
|
||||||
(dc/db conn)
|
|
||||||
[[c] date date]))
|
|
||||||
0.0)
|
|
||||||
|
|
||||||
:sales-summary/total-card-payments
|
|
||||||
(or (ffirst (dc/q '[:find (sum ?total)
|
|
||||||
:with ?c
|
|
||||||
:in $ [?clients ?start-date ?end-date]
|
|
||||||
:where [(iol-ion.query/scan-sales-orders $ ?clients ?start-date ?end-date) [[?e _ ?sort-default] ...]]
|
|
||||||
[?e :sales-order/charges ?c]
|
|
||||||
[?c :charge/type-name "CARD"]
|
|
||||||
[?c :charge/total ?total]]
|
|
||||||
(dc/db conn)
|
|
||||||
[[c] date date]))
|
|
||||||
0.0)
|
|
||||||
:sales-summary/total-unknown-processor-payments
|
|
||||||
(or (ffirst (dc/q '[:find (sum ?total)
|
|
||||||
:with ?c
|
|
||||||
:in $ [?clients ?start-date ?end-date]
|
|
||||||
:where [(iol-ion.query/scan-sales-orders $ ?clients ?start-date ?end-date) [[?e _ ?sort-default] ...]]
|
|
||||||
[?e :sales-order/charges ?c]
|
|
||||||
[?c :charge/type-name "OTHER"]
|
|
||||||
[?c :charge/processor :ccp-processor/na]
|
|
||||||
[?c :charge/total ?total]]
|
|
||||||
(dc/db conn)
|
|
||||||
[[c] date date]))
|
|
||||||
0.0)
|
|
||||||
:sales-summary/total-card-fees
|
|
||||||
(or (ffirst (dc/q '[:find ?f
|
|
||||||
:in $ ?client ?d
|
|
||||||
:where
|
|
||||||
[?e :expected-deposit/client ?client]
|
|
||||||
[?e :expected-deposit/sales-date ?d]
|
|
||||||
[?e :expected-deposit/fee ?f]]
|
|
||||||
(dc/db conn)
|
|
||||||
c
|
|
||||||
date))
|
|
||||||
0.0)
|
|
||||||
:sales-summary/total-card-refunds
|
|
||||||
(or (ffirst (dc/q '[:find (sum ?t)
|
|
||||||
:in $ [?clients ?start-date ?end-date]
|
|
||||||
:where
|
|
||||||
:where [(iol-ion.query/scan-sales-refunds $ ?clients ?start-date ?end-date) [[?e _ ?sort-default] ...]]
|
|
||||||
[?e :sales-refund/type "CARD"]
|
|
||||||
[?e :sales-refund/total ?t]]
|
|
||||||
(dc/db conn)
|
|
||||||
[[c] date date]))
|
|
||||||
0.0)
|
|
||||||
|
|
||||||
:sales-summary/total-cash-payments
|
|
||||||
(or (ffirst (dc/q '[:find (sum ?total)
|
|
||||||
:with ?c
|
|
||||||
:in $ [?clients ?start-date ?end-date]
|
|
||||||
:where [(iol-ion.query/scan-sales-orders $ ?clients ?start-date ?end-date) [[?e _ ?sort-default] ...]]
|
|
||||||
[?e :sales-order/charges ?c]
|
|
||||||
[?c :charge/total ?total]
|
|
||||||
[?c :charge/type-name "CASH"]]
|
|
||||||
(dc/db conn)
|
|
||||||
[[c] date date]))
|
|
||||||
0.0)
|
|
||||||
|
|
||||||
:sales-summary/total-gift-card-payments
|
|
||||||
(or (ffirst (dc/q '[:find (sum ?total)
|
|
||||||
:with ?c
|
|
||||||
:in $ [?clients ?start-date ?end-date]
|
|
||||||
:where [(iol-ion.query/scan-sales-orders $ ?clients ?start-date ?end-date) [[?e _ ?sort-default] ...]]
|
|
||||||
[?e :sales-order/charges ?c]
|
|
||||||
[?c :charge/total ?total]
|
|
||||||
(or [?c :charge/type-name "SQUARE_GIFT_CARD"]
|
|
||||||
[?c :charge/type-name "WALLET"]
|
|
||||||
[?c :charge/type-name "GIFT_CARD"])]
|
|
||||||
(dc/db conn)
|
|
||||||
[[c] date date]))
|
|
||||||
0.0)
|
|
||||||
|
|
||||||
:sales-summary/total-cash-refunds
|
|
||||||
(or (ffirst (dc/q '[:find (sum ?t)
|
|
||||||
:in $ [?clients ?start-date ?end-date]
|
|
||||||
:where
|
|
||||||
:where [(iol-ion.query/scan-sales-refunds $ ?clients ?start-date ?end-date) [[?e _ ?sort-default] ...]]
|
|
||||||
[?e :sales-refund/type "CASH"]
|
|
||||||
[?e :sales-refund/total ?t]]
|
|
||||||
(dc/db conn)
|
|
||||||
[[c] date date]))
|
|
||||||
0.0)
|
|
||||||
|
|
||||||
:sales-summary/total-food-app-payments
|
|
||||||
|
|
||||||
(or (ffirst (dc/q '[:find (sum ?total)
|
|
||||||
:with ?c
|
|
||||||
:in $ [?clients ?start-date ?end-date] [?processor ...]
|
|
||||||
:where [(iol-ion.query/scan-sales-orders $ ?clients ?start-date ?end-date) [[?e _ ?sort-default] ...]]
|
|
||||||
[?e :sales-order/charges ?c]
|
|
||||||
[?c :charge/processor ?processor]
|
|
||||||
[?c :charge/total ?total]]
|
|
||||||
(dc/db conn)
|
|
||||||
[[c] date date]
|
|
||||||
#{:ccp-processor/toast
|
|
||||||
#_:ccp-processor/ezcater
|
|
||||||
#_:ccp-processor/koala
|
|
||||||
:ccp-processor/doordash
|
|
||||||
:ccp-processor/grubhub
|
|
||||||
:ccp-processor/uber-eats}))
|
|
||||||
0.0)
|
|
||||||
:sales-summary/total-food-app-refunds
|
|
||||||
(or (ffirst (dc/q '[:find (sum ?t)
|
|
||||||
:in $ [?clients ?start-date ?end-date]
|
|
||||||
:where [(iol-ion.query/scan-sales-refunds $ ?clients ?start-date ?end-date) [[?e _ ?sort-default] ...]]
|
|
||||||
[?e :sales-refund/type "EXTERNAL"]
|
|
||||||
[?e :sales-refund/total ?t]]
|
|
||||||
(dc/db conn)
|
|
||||||
[[c] date date]))
|
|
||||||
0.0)}]
|
|
||||||
(if (seq (:sales-summary/sales-items result))
|
|
||||||
(do
|
|
||||||
(alog/info ::upserting-summaries
|
|
||||||
:category-count (count (:sales-summary/sales-items result)))
|
|
||||||
@(dc/transact conn [[:upsert-entity result]]))
|
|
||||||
@(dc/transact conn [{:db/id id :sales-summary/dirty false}]))))))
|
|
||||||
|
|
||||||
(defn reset-summaries []
|
(defn reset-summaries []
|
||||||
@(dc/transact conn (->> (dc/q '[:find ?sos
|
@(dc/transact conn (->> (dc/q '[:find ?sos
|
||||||
|
|||||||
@@ -2,23 +2,28 @@
|
|||||||
(:require [auto-ap.datomic
|
(:require [auto-ap.datomic
|
||||||
:refer [apply-pagination apply-sort-3 conn merge-query pull-many
|
:refer [apply-pagination apply-sort-3 conn merge-query pull-many
|
||||||
query2]]
|
query2]]
|
||||||
|
[auto-ap.datomic.accounts :as d-accounts]
|
||||||
[auto-ap.graphql.utils :refer [extract-client-ids]]
|
[auto-ap.graphql.utils :refer [extract-client-ids]]
|
||||||
[auto-ap.routes.admin.sales-summaries :as route]
|
[auto-ap.routes.admin.sales-summaries :as route]
|
||||||
[auto-ap.routes.utils
|
[auto-ap.routes.utils
|
||||||
:refer [wrap-admin wrap-client-redirect-unauthenticated]]
|
:refer [wrap-admin wrap-client-redirect-unauthenticated]]
|
||||||
[auto-ap.ssr-routes :as ssr-routes]
|
[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 :as com]
|
||||||
[auto-ap.ssr.components.multi-modal :as mm]
|
[auto-ap.ssr.components.multi-modal :as mm]
|
||||||
[auto-ap.ssr.form-cursor :as fc]
|
[auto-ap.ssr.form-cursor :as fc]
|
||||||
[auto-ap.ssr.grid-page-helper :as helper]
|
[auto-ap.ssr.grid-page-helper :as helper]
|
||||||
|
[auto-ap.ssr.hx :as hx]
|
||||||
[auto-ap.ssr.svg :as svg]
|
[auto-ap.ssr.svg :as svg]
|
||||||
[auto-ap.ssr.utils
|
[auto-ap.ssr.utils
|
||||||
:refer [apply-middleware-to-all-handlers entity-id
|
:refer [apply-middleware-to-all-handlers entity-id html-response
|
||||||
wrap-schema-enforce]]
|
money strip temp-id wrap-schema-enforce]]
|
||||||
[auto-ap.time :as atime]
|
[auto-ap.time :as atime]
|
||||||
[bidi.bidi :as bidi]
|
[bidi.bidi :as bidi]
|
||||||
[clj-time.coerce :as c]
|
[clj-time.coerce :as c]
|
||||||
|
[clojure.string :as str]
|
||||||
[datomic.api :as dc]
|
[datomic.api :as dc]
|
||||||
|
[hiccup.util :as hu]
|
||||||
[iol-ion.query :refer [dollars=]]
|
[iol-ion.query :refer [dollars=]]
|
||||||
[malli.core :as mc]
|
[malli.core :as mc]
|
||||||
[malli.util :as mut]))
|
[malli.util :as mut]))
|
||||||
@@ -50,20 +55,18 @@
|
|||||||
:size :small}))]])
|
:size :small}))]])
|
||||||
|
|
||||||
(def default-read '[:db/id
|
(def default-read '[:db/id
|
||||||
|
|
||||||
*
|
*
|
||||||
[:sales-summary/date :xform clj-time.coerce/from-date]
|
[:sales-summary/date :xform clj-time.coerce/from-date]
|
||||||
{:sales-summary/client [:client/code :client/name]}
|
{:sales-summary/client [:client/code :client/name :db/id]}
|
||||||
{:sales-summary/total-tax [{[:ledger-mapped/ledger-side :xform iol-ion.query/ident] [:db/ident]}
|
{:sales-summary/items [{[:ledger-mapped/ledger-side :xform iol-ion.query/ident] [:db/ident]
|
||||||
:ledger-mapped/amount]}
|
} ;; TODO clientize
|
||||||
{:sales-summary/total-tip [{[:ledger-mapped/ledger-side :xform iol-ion.query/ident] [:db/ident]}
|
:ledger-mapped/account
|
||||||
:ledger-mapped/amount]}
|
|
||||||
{:sales-summary/sales-items [{[:ledger-mapped/ledger-side :xform iol-ion.query/ident] [:db/ident]}
|
|
||||||
:ledger-mapped/amount
|
:ledger-mapped/amount
|
||||||
:sales-summary-item/category]}
|
:sales-summary-item/category
|
||||||
{:sales-summary/payment-items [{[:ledger-mapped/ledger-side :xform iol-ion.query/ident] [:db/ident]}
|
:sales-summary-item/sort-order
|
||||||
:ledger-mapped/amount
|
:db/id
|
||||||
:sales-summary-item/category]}]) ;; TODO
|
:sales-summary-item/manual?]
|
||||||
|
} ]) ;; TODO
|
||||||
|
|
||||||
(defn fetch-ids [db request]
|
(defn fetch-ids [db request]
|
||||||
(let [query-params (:parsed-query-params request)
|
(let [query-params (:parsed-query-params request)
|
||||||
@@ -133,11 +136,22 @@
|
|||||||
:total-unknown-processor-payments (:sales-summary/total-unknown-processor-payments ss 0.0)
|
:total-unknown-processor-payments (:sales-summary/total-unknown-processor-payments ss 0.0)
|
||||||
:discounts (+ (:sales-summary/discount ss 0.0))
|
:discounts (+ (:sales-summary/discount ss 0.0))
|
||||||
:returns (+ (:sales-summary/total-returns ss 0.0))})
|
:returns (+ (:sales-summary/total-returns ss 0.0))})
|
||||||
|
(defn sort-items [ss]
|
||||||
|
(sort-by (juxt :ledger-mapped/ledger-side :sales-summary-item/sort-order :sales-summary-item/category) ss))
|
||||||
|
|
||||||
(defn all-items [ss]
|
|
||||||
(->> [(:sales-summary/total-tax ss) (:sales-summary/total-tip ss)]
|
|
||||||
(into (:sales-summary/payment-items ss))
|
(defn total-debits [items]
|
||||||
(into (:sales-summary/sales-items ss))))
|
(->> items
|
||||||
|
(filter #(= :ledger-side/debit (:ledger-mapped/ledger-side %)))
|
||||||
|
(map #(:ledger-mapped/amount % 0.0))
|
||||||
|
(reduce + 0.0)))
|
||||||
|
|
||||||
|
(defn total-credits [items]
|
||||||
|
(->> items
|
||||||
|
(filter #(= :ledger-side/credit (:ledger-mapped/ledger-side %)))
|
||||||
|
(map #(:ledger-mapped/amount % 0.0))
|
||||||
|
(reduce + 0.0)))
|
||||||
|
|
||||||
(def grid-page
|
(def grid-page
|
||||||
(helper/build {:id "entity-table"
|
(helper/build {:id "entity-table"
|
||||||
@@ -146,7 +160,7 @@
|
|||||||
:fetch-page fetch-page
|
:fetch-page fetch-page
|
||||||
:page-specific-nav filters
|
:page-specific-nav filters
|
||||||
:row-buttons (fn [_ entity]
|
:row-buttons (fn [_ entity]
|
||||||
[(com/icon-button {:hx-put (bidi/path-for ssr-routes/only-routes
|
[(com/icon-button {:hx-get (bidi/path-for ssr-routes/only-routes
|
||||||
::route/edit-wizard
|
::route/edit-wizard
|
||||||
:db/id (:db/id entity))}
|
:db/id (:db/id entity))}
|
||||||
svg/pencil)])
|
svg/pencil)])
|
||||||
@@ -180,56 +194,40 @@
|
|||||||
:sort-key "date"
|
:sort-key "date"
|
||||||
:render #(some-> % :sales-summary/date (atime/unparse-local atime/normal-date))}
|
:render #(some-> % :sales-summary/date (atime/unparse-local atime/normal-date))}
|
||||||
|
|
||||||
{:key "credits"
|
|
||||||
:name "credits"
|
|
||||||
:sort-key "credits"
|
|
||||||
:render (fn [ss]
|
|
||||||
(let [total-debits (->> (all-items ss)
|
|
||||||
(filter #(= :ledger-side/debit (:ledger-mapped/ledger-side %)))
|
|
||||||
(map #(:ledger-mapped/amount % 0.0))
|
|
||||||
(reduce + 0.0))
|
|
||||||
total-credits (->> (all-items ss)
|
|
||||||
(filter #(= :ledger-side/credit (:ledger-mapped/ledger-side %)))
|
|
||||||
(map #(:ledger-mapped/amount % 0.0))
|
|
||||||
(reduce + 0.0))]
|
|
||||||
[:ul
|
|
||||||
(for [si (:sales-summary/sales-items ss)
|
|
||||||
:when (= :ledger-side/credit (:ledger-mapped/ledger-side si))]
|
|
||||||
[:li (:sales-summary-item/category si) ": " (format "$%,.2f" (:ledger-mapped/amount si))])
|
|
||||||
[:li "Sales subtotal: " (format "$%,.2f" (reduce + 0.0 (map :ledger-mapped/amount (:sales-summary/sales-items ss))))]
|
|
||||||
(for [si (:sales-summary/payment-items ss)
|
|
||||||
:when (= :ledger-side/credit (:ledger-mapped/ledger-side si))]
|
|
||||||
[:li (:sales-summary-item/category si) ": " (format "$%,.2f" (:ledger-mapped/amount si))])
|
|
||||||
|
|
||||||
[:li "Tax: " (format "$%,.2f" (:ledger-mapped/amount (:sales-summary/total-tax ss)))]
|
|
||||||
[:li "Tips: " (format "$%,.2f" (:ledger-mapped/amount (:sales-summary/total-tip ss)))]
|
|
||||||
[:li (com/pill {:color (if (dollars= total-debits total-credits)
|
|
||||||
:primary
|
|
||||||
:red)} "Total: " (format "$%,.2f" total-credits))]])
|
|
||||||
|
|
||||||
#_(count))}
|
|
||||||
|
|
||||||
{:key "debits"
|
{:key "debits"
|
||||||
:name "debits"
|
:name "debits"
|
||||||
:sort-key "debits"
|
:sort-key "debits"
|
||||||
:render (fn [ss]
|
:render (fn [ss]
|
||||||
(let [ total-debits (->> (all-items ss)
|
(let [total-debits (total-debits (:sales-summary/items ss))
|
||||||
(filter #(= :ledger-side/debit (:ledger-mapped/ledger-side %)))
|
total-credits (total-credits (:sales-summary/items ss))]
|
||||||
(map #(:ledger-mapped/amount % 0.0))
|
|
||||||
(reduce + 0.0))
|
|
||||||
total-credits (->> (all-items ss)
|
|
||||||
(filter #(= :ledger-side/credit (:ledger-mapped/ledger-side %)))
|
|
||||||
(map #(:ledger-mapped/amount % 0.0))
|
|
||||||
(reduce + 0.0))]
|
|
||||||
[:ul
|
[:ul
|
||||||
(for [si (:sales-summary/payment-items ss)
|
(for [si (sort-items (:sales-summary/items ss))
|
||||||
:when (= :ledger-side/debit (:ledger-mapped/ledger-side si))]
|
:when (= :ledger-side/debit (:ledger-mapped/ledger-side si))]
|
||||||
[:li (:sales-summary-item/category si) ": " (format "$%,.2f" (:ledger-mapped/amount 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)
|
[:li (com/pill {:color (if (dollars= total-debits total-credits)
|
||||||
:primary
|
:primary
|
||||||
:red)} "Total: " (format "$%,.2f" total-debits))]])
|
:red)} "Total: " (format "$%,.2f" total-debits))]]))}
|
||||||
|
{:key "credits"
|
||||||
#_(count))}]}))
|
: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))]]))}]}))
|
||||||
|
|
||||||
;; TODO schema cleanup
|
;; TODO schema cleanup
|
||||||
;; Decide on what should be calculated as generating ledger entries, and what should be calculated
|
;; Decide on what should be calculated as generating ledger entries, and what should be calculated
|
||||||
@@ -245,9 +243,154 @@
|
|||||||
(def edit-schema
|
(def edit-schema
|
||||||
[:map
|
[:map
|
||||||
[:db/id entity-id]
|
[:db/id entity-id]
|
||||||
[:sales-summary/sales-items
|
[:sales-summary/client [:map [:db/id entity-id]]]
|
||||||
|
[:sales-summary/items
|
||||||
[:vector {:coerce? true}
|
[:vector {:coerce? true}
|
||||||
[:map [:db/id entity-id]]]] ])
|
[: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
|
||||||
|
(boolean? x)
|
||||||
|
x
|
||||||
|
(nil? x)
|
||||||
|
false
|
||||||
|
(str/blank? x)
|
||||||
|
false
|
||||||
|
:else
|
||||||
|
true))} :boolean]
|
||||||
|
[:ledger-mapped/account entity-id]
|
||||||
|
[:credit {:optional true} [:maybe money]]
|
||||||
|
[:debit {:optional true} [:maybe money]]]
|
||||||
|
[:fn {:error/message "Must choose one of credit/debit"
|
||||||
|
:error/path [:credit]}
|
||||||
|
(fn [x]
|
||||||
|
(not (and (:credit x)
|
||||||
|
(:debit x))))]]]] ])
|
||||||
|
|
||||||
|
|
||||||
|
(defn summary-total-row* [request]
|
||||||
|
(let [total-credits (-> request
|
||||||
|
:multi-form-state
|
||||||
|
:step-params
|
||||||
|
:sales-summary/items
|
||||||
|
(total-credits))
|
||||||
|
total-debits (-> request
|
||||||
|
:multi-form-state
|
||||||
|
:step-params
|
||||||
|
:sales-summary/items
|
||||||
|
(total-debits))]
|
||||||
|
|
||||||
|
(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 "TOTAL"])
|
||||||
|
(com/data-grid-cell {:class "text-right"}
|
||||||
|
(format "$%,.2f" total-debits))
|
||||||
|
(com/data-grid-cell {:class "text-right"}
|
||||||
|
(format "$%,.2f" total-credits)))))
|
||||||
|
|
||||||
|
(defn unbalanced-row* [request]
|
||||||
|
(let [total-credits (-> request
|
||||||
|
:multi-form-state
|
||||||
|
:step-params
|
||||||
|
:sales-summary/items
|
||||||
|
(total-credits))
|
||||||
|
total-debits (-> request
|
||||||
|
:multi-form-state
|
||||||
|
:step-params
|
||||||
|
:sales-summary/items
|
||||||
|
(total-debits))]
|
||||||
|
|
||||||
|
(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 (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
|
||||||
|
(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
|
||||||
|
: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)))})])
|
||||||
|
|
||||||
|
(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 {})}
|
||||||
|
(fc/field-value (:new? value)) (hx/htmx-transition-appear ))
|
||||||
|
(fc/with-field :db/id
|
||||||
|
(com/hidden {:name (fc/field-name)
|
||||||
|
:value (fc/field-value)}))
|
||||||
|
(when manual?
|
||||||
|
(fc/with-field :sales-summary-item/manual?
|
||||||
|
(com/hidden {:name (fc/field-name)
|
||||||
|
:value true})))
|
||||||
|
(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)}))
|
||||||
|
|
||||||
|
(list
|
||||||
|
(com/hidden {:name (fc/field-name)
|
||||||
|
:value (fc/field-value)})
|
||||||
|
(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)
|
||||||
|
:client-id client-id
|
||||||
|
: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"
|
||||||
|
:name (fc/field-name)
|
||||||
|
:value (fc/field-value)})))
|
||||||
|
(when (= (fc/field-value (:ledger-mapped/ledger-side value))
|
||||||
|
:ledger-side/debit)
|
||||||
|
(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"
|
||||||
|
:name (fc/field-name)
|
||||||
|
:value (fc/field-value)})))
|
||||||
|
(when (= (fc/field-value (:ledger-mapped/ledger-side value))
|
||||||
|
:ledger-side/credit)
|
||||||
|
(format "$%,.2f" (fc/field-value (:ledger-mapped/amount value))))))
|
||||||
|
(com/data-grid-cell {:class "align-top"}
|
||||||
|
(when manual?
|
||||||
|
(com/a-icon-button {"@click.prevent.stop" "$refs.p.remove()"} svg/x))))))
|
||||||
|
|
||||||
(defrecord MainStep [linear-wizard]
|
(defrecord MainStep [linear-wizard]
|
||||||
mm/ModalWizardStep
|
mm/ModalWizardStep
|
||||||
@@ -260,7 +403,7 @@
|
|||||||
[])
|
[])
|
||||||
|
|
||||||
(step-schema [_]
|
(step-schema [_]
|
||||||
(mut/select-keys (mm/form-schema linear-wizard) #{:db/id}))
|
(mut/select-keys (mm/form-schema linear-wizard) #{:db/id :sales-summary/items}))
|
||||||
|
|
||||||
(render-step
|
(render-step
|
||||||
[this {:keys [multi-form-state] :as request}]
|
[this {:keys [multi-form-state] :as request}]
|
||||||
@@ -273,16 +416,39 @@
|
|||||||
(fc/with-field :db/id
|
(fc/with-field :db/id
|
||||||
(com/hidden {:name (fc/field-name)
|
(com/hidden {:name (fc/field-name)
|
||||||
:value (fc/field-value)}))
|
:value (fc/field-value)}))
|
||||||
|
(com/data-grid {:headers
|
||||||
|
[(com/data-grid-header {} "Category")
|
||||||
(pr-str multi-form-state) ])
|
(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))) }))
|
||||||
|
;; TODO
|
||||||
|
(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))
|
||||||
|
:tr-params {:hx-vals (hx/json {:client-id (:db/id (:sales-summary/client (:snapshot multi-form-state)))})}} ;; TODO
|
||||||
|
"New Summary Item")))
|
||||||
|
(summary-total-row* request)
|
||||||
|
(unbalanced-row* request)) ])
|
||||||
|
|
||||||
:footer
|
:footer
|
||||||
(mm/default-step-footer linear-wizard this :validation-route ::route/edit-wizard-navigate
|
(mm/default-step-footer linear-wizard this :validation-route ::route/edit-wizard-navigate)
|
||||||
:next-button (com/button {:color :primary :x-ref "next" :class "w-32"
|
:validation-route ::route/edit-wizard-navigate
|
||||||
:hx-put (bidi.bidi/path-for ssr-routes/only-routes
|
:width-height-class "lg:w-[850px] lg:h-[900px]")))
|
||||||
::route/edit-wizard-navigate)} "Save"))
|
|
||||||
:validation-route ::route/edit-wizard-navigate)))
|
(defn attach-ledger [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
|
||||||
|
:ledger-mapped/amount (:debit i))
|
||||||
|
true (dissoc :credit :debit)
|
||||||
|
true (assoc :sales-summary-item/manual? true)))
|
||||||
|
|
||||||
(defrecord EditWizard [_ current-step]
|
(defrecord EditWizard [_ current-step]
|
||||||
mm/LinearModalWizard
|
mm/LinearModalWizard
|
||||||
@@ -299,8 +465,8 @@
|
|||||||
this request
|
this request
|
||||||
:form-params
|
:form-params
|
||||||
(-> mm/default-form-props
|
(-> mm/default-form-props
|
||||||
(assoc :hx-post
|
(assoc :hx-put
|
||||||
(str (bidi/path-for ssr-routes/only-routes ::route/edit-submit))))
|
(str (bidi/path-for ssr-routes/only-routes ::route/edit-wizard-submit))))
|
||||||
:render-timeline? false))
|
:render-timeline? false))
|
||||||
(steps [_]
|
(steps [_]
|
||||||
[:main])
|
[:main])
|
||||||
@@ -311,76 +477,36 @@
|
|||||||
(form-schema [_]
|
(form-schema [_]
|
||||||
edit-schema)
|
edit-schema)
|
||||||
(submit [this {:keys [multi-form-state request-method identity] :as request}]
|
(submit [this {:keys [multi-form-state request-method identity] :as request}]
|
||||||
#_(let [invoice (:snapshot multi-form-state)
|
(let [result (:snapshot multi-form-state )
|
||||||
|
transaction [:upsert-entity {:db/id (:db/id result)
|
||||||
_ (alog/peek invoice)
|
:sales-summary/items (map
|
||||||
extant? (:db/id invoice)
|
(fn [i]
|
||||||
client-id (->db-id (:invoice/client invoice))
|
(if (:sales-summary-item/manual? i)
|
||||||
vendor-id (->db-id (:invoice/vendor invoice))
|
(attach-ledger i)
|
||||||
paid-amount (if-let [outstanding-balance
|
{:db/id (:db/id i)
|
||||||
(and extant?
|
:ledger-mapped/account (:ledger-mapped/account i)
|
||||||
(-
|
}))
|
||||||
(pull-attr (dc/db conn)
|
(:sales-summary/items result))}]]
|
||||||
:invoice/total
|
(clojure.pprint/pprint (:sales-summary/items result))
|
||||||
(:db/id invoice))
|
@(dc/transact conn [ transaction])
|
||||||
(pull-attr (dc/db conn)
|
(html-response
|
||||||
:invoice/outstanding-balance
|
(row* identity (dc/pull (dc/db conn) default-read (:db/id result))
|
||||||
(:db/id invoice))))]
|
{:flash? true
|
||||||
outstanding-balance
|
:request request})
|
||||||
0.0)
|
:headers (cond-> {"hx-trigger" "modalclose"
|
||||||
outstanding-balance (- (or
|
"hx-retarget" (format "#entity-table tr[data-id=\"%d\"]" (:db/id result))
|
||||||
(:invoice/total (:step-params multi-form-state))
|
"hx-reswap" "outerHTML"})))))
|
||||||
(:invoice/total (:snapshot multi-form-state)))
|
|
||||||
paid-amount)
|
|
||||||
|
|
||||||
transaction [:upsert-invoice (-> multi-form-state
|
|
||||||
:snapshot
|
|
||||||
(assoc :db/id (or (:db/id invoice) "invoice"))
|
|
||||||
(dissoc :customize-due-and-scheduled? :invoice/journal-entry :invoice/payments :customize-accounts)
|
|
||||||
(assoc :invoice/expense-accounts (if (= :customize (:customize-accounts invoice))
|
|
||||||
(-> multi-form-state :step-params :invoice/expense-accounts)
|
|
||||||
[{:db/id "123"
|
|
||||||
:invoice-expense-account/location "Shared"
|
|
||||||
:invoice-expense-account/account (:db/id (:vendor/default-account (clientize-vendor (get-vendor vendor-id)
|
|
||||||
client-id)))
|
|
||||||
:invoice-expense-account/amount (or (:invoice/total (:step-params multi-form-state))
|
|
||||||
(:invoice/total (:snapshot multi-form-state)))}]))
|
|
||||||
(assoc
|
|
||||||
:invoice/outstanding-balance outstanding-balance
|
|
||||||
:invoice/import-status :import-status/imported
|
|
||||||
:invoice/status (if (dollars= 0.0 outstanding-balance)
|
|
||||||
:invoice-status/paid
|
|
||||||
:invoice-status/unpaid))
|
|
||||||
(maybe-spread-locations)
|
|
||||||
(update :invoice/date coerce/to-date)
|
|
||||||
(update :invoice/due coerce/to-date)
|
|
||||||
(update :invoice/scheduled-payment coerce/to-date))]]
|
|
||||||
(assert-invoice-amounts-add-up (second transaction))
|
|
||||||
(when-not extant?
|
|
||||||
(assert-no-conflicting invoice))
|
|
||||||
(exception->4xx #(assert-can-see-client (:identity request) client-id))
|
|
||||||
|
|
||||||
(exception->4xx #(assert-not-locked client-id (:invoice/date invoice)))
|
|
||||||
(let [transaction-result (audit-transact [transaction] (:identity request))]
|
|
||||||
(solr/touch-with-ledger (get-in transaction-result [:tempids "invoice"]))
|
|
||||||
(if extant?
|
|
||||||
|
|
||||||
(html-response
|
|
||||||
(@(resolve 'auto-ap.ssr.invoices/row*) identity (dc/pull (dc/db conn) default-read (:db/id invoice)) {:flash? true
|
|
||||||
:request request})
|
|
||||||
:headers (cond-> {"hx-trigger" "modalclose"
|
|
||||||
"hx-retarget" (format "#entity-table tr[data-id=\"%d\"]" (:db/id invoice))
|
|
||||||
"hx-reswap" "outerHTML"}))
|
|
||||||
|
|
||||||
(assoc-in (mm/navigate-handler {:request (assoc-in request [:multi-form-state :snapshot :db/id] (get-in transaction-result [:tempids "invoice"]))
|
|
||||||
:to-step :next-steps})
|
|
||||||
[:headers "hx-trigger"] "invalidated"))))))
|
|
||||||
|
|
||||||
(def edit-wizard (->EditWizard nil nil))
|
(def edit-wizard (->EditWizard nil nil))
|
||||||
|
|
||||||
(defn initial-edit-wizard-state [request]
|
(defn initial-edit-wizard-state [request]
|
||||||
(let [entity (dc/pull (dc/db conn) default-read (:db/id (:route-params request)))
|
(let [entity (dc/pull (dc/db conn) default-read (:db/id (:route-params request)))
|
||||||
entity (select-keys entity (mut/keys edit-schema))]
|
entity (select-keys entity (mut/keys edit-schema))
|
||||||
|
entity (update entity :sales-summary/items (comp #(map (fn [x]
|
||||||
|
(if (= :ledger-side/debit (:ledger-mapped/ledger-side x))
|
||||||
|
(assoc x :debit (:ledger-mapped/amount x))
|
||||||
|
(assoc x :credit (:ledger-mapped/amount x))))
|
||||||
|
%) sort-items))]
|
||||||
|
|
||||||
(mm/->MultiStepFormState entity [] entity)))
|
(mm/->MultiStepFormState entity [] entity)))
|
||||||
|
|
||||||
@@ -395,7 +521,20 @@
|
|||||||
(wrap-schema-enforce :route-schema [:map [:db/id entity-id]]))
|
(wrap-schema-enforce :route-schema [:map [:db/id entity-id]]))
|
||||||
::route/edit-wizard-navigate (-> mm/next-handler
|
::route/edit-wizard-navigate (-> mm/next-handler
|
||||||
(mm/wrap-wizard edit-wizard)
|
(mm/wrap-wizard edit-wizard)
|
||||||
(mm/wrap-decode-multi-form-state))})
|
(mm/wrap-decode-multi-form-state))
|
||||||
|
::route/new-summary-item (-> (add-new-entity-handler [:step-params :sales-summary/items]
|
||||||
|
(fn render [cursor request]
|
||||||
|
(sales-summary-item-row*
|
||||||
|
{:value cursor
|
||||||
|
:client-id (:client-id (:query-params request)) }))
|
||||||
|
(fn build-new-row [base _]
|
||||||
|
(assoc base :sales-summary-item/manual? true)))
|
||||||
|
(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))})
|
||||||
(fn [h]
|
(fn [h]
|
||||||
(-> h
|
(-> h
|
||||||
(wrap-admin)
|
(wrap-admin)
|
||||||
|
|||||||
@@ -138,7 +138,7 @@
|
|||||||
(a-button- (merge
|
(a-button- (merge
|
||||||
(dissoc params :index :colspan)
|
(dissoc params :index :colspan)
|
||||||
{
|
{
|
||||||
"@click" "$dispatch('newRow', {index: (newRowIndex++)})"
|
"@click.prevent" "$dispatch('newRow', {index: (newRowIndex++)})"
|
||||||
:color :secondary
|
:color :secondary
|
||||||
:hx-trigger "newRow"
|
:hx-trigger "newRow"
|
||||||
:hx-vals (hiccup/raw "js:{index: event.detail.index }")
|
:hx-vals (hiccup/raw "js:{index: event.detail.index }")
|
||||||
|
|||||||
@@ -165,12 +165,13 @@
|
|||||||
:else
|
:else
|
||||||
[:div "No action possible."])]])
|
[:div "No action possible."])]])
|
||||||
|
|
||||||
(defn default-render-step [linear-wizard step & {:keys [head body footer validation-route discard-route]}]
|
(defn default-render-step [linear-wizard step & {:keys [head body footer validation-route discard-route width-height-class]}]
|
||||||
(let [is-last? (= (step-key step) (last (steps linear-wizard)))]
|
(let [is-last? (= (step-key step) (last (steps linear-wizard)))]
|
||||||
(com/modal-card-advanced
|
(com/modal-card-advanced
|
||||||
{"@keydown.enter.prevent.stop" "if ($refs.next ) {$refs.next.click()}"
|
{"@keydown.enter.prevent.stop" "if ($refs.next ) {$refs.next.click()}"
|
||||||
:class (str
|
:class (str
|
||||||
"w-full h-full md:w-[750px] md:h-[600px]
|
(or width-height-class " md:w-[750px] md:h-[600px] ")
|
||||||
|
" w-full h-full
|
||||||
group-[.forward]/transition:htmx-swapping:opacity-0
|
group-[.forward]/transition:htmx-swapping:opacity-0
|
||||||
group-[.forward]/transition:htmx-swapping:-translate-x-1/4
|
group-[.forward]/transition:htmx-swapping:-translate-x-1/4
|
||||||
group-[.forward]/transition:htmx-swapping:scale-75
|
group-[.forward]/transition:htmx-swapping:scale-75
|
||||||
|
|||||||
@@ -379,8 +379,7 @@
|
|||||||
(:account/name (d-accounts/clientize (dc/pull (dc/db conn) d-accounts/default-read value)
|
(:account/name (d-accounts/clientize (dc/pull (dc/db conn) d-accounts/default-read value)
|
||||||
client-id)))})])
|
client-id)))})])
|
||||||
|
|
||||||
(defn- invoice-expense-account-row*
|
(defn- invoice-expense-account-row* [{:keys [value client-id]}]
|
||||||
[{:keys [value client-id]}]
|
|
||||||
(com/data-grid-row
|
(com/data-grid-row
|
||||||
(-> {:x-data (hx/json {:show (boolean (not (fc/field-value (:new? value))))
|
(-> {:x-data (hx/json {:show (boolean (not (fc/field-value (:new? value))))
|
||||||
:accountId (fc/field-value (:invoice-expense-account/account value))})
|
:accountId (fc/field-value (:invoice-expense-account/account value))})
|
||||||
|
|||||||
@@ -439,10 +439,14 @@
|
|||||||
:explain
|
:explain
|
||||||
(me/humanize {:errors (assoc me/default-errors
|
(me/humanize {:errors (assoc me/default-errors
|
||||||
::mc/missing-key {:error/message {:en "required"}})}))
|
::mc/missing-key {:error/message {:en "required"}})}))
|
||||||
(map (fn [[k v]]
|
(map (fn [x]
|
||||||
(str (if (keyword? k)
|
(if (and (sequential? x)
|
||||||
(name k)
|
(= (count x) 2))
|
||||||
k) ": " (str/join ", " v))))
|
(let [[k v] x]
|
||||||
|
(str (if (keyword? k)
|
||||||
|
(name k)
|
||||||
|
k) ": " (str/join ", " v))
|
||||||
|
(str x)))))
|
||||||
(str/join ", "))
|
(str/join ", "))
|
||||||
{:type :schema-validation
|
{:type :schema-validation
|
||||||
:decoded (:value (:data (ex-data e)))
|
:decoded (:value (:data (ex-data e)))
|
||||||
@@ -539,7 +543,8 @@
|
|||||||
{:path (:in e)
|
{:path (:in e)
|
||||||
:message (get-in humanized (:in e))})
|
:message (get-in humanized (:in e))})
|
||||||
(:errors (:explain (:error e))))]
|
(:errors (:explain (:error e))))]
|
||||||
(alog/warn ::form-4xx :errors errors)
|
(alog/warn ::form-4xx :errors errors
|
||||||
|
:data e)
|
||||||
(form-handler (assoc request
|
(form-handler (assoc request
|
||||||
:form-params (:decoded e)
|
:form-params (:decoded e)
|
||||||
:field-validation-errors errors
|
:field-validation-errors errors
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
(ns auto-ap.routes.admin.sales-summaries)
|
(ns auto-ap.routes.admin.sales-summaries)
|
||||||
(def routes {"" {:get ::page}
|
(def routes {"" {:get ::page
|
||||||
|
:put ::edit-wizard-submit}
|
||||||
"/table" ::table
|
"/table" ::table
|
||||||
|
|
||||||
["/" [#"\d+" :db/id]] {:put ::edit-wizard}
|
["/" [#"\d+" :db/id]] {:get ::edit-wizard }
|
||||||
"/edit/navigate" ::edit-wizard-navigate })
|
|
||||||
|
"/edit/navigate" ::edit-wizard-navigate
|
||||||
|
"/edit/sales-summary-item" ::new-summary-item})
|
||||||
Reference in New Issue
Block a user