From f25ddc2ee4a88d932a4c1d02936af2587f54f446 Mon Sep 17 00:00:00 2001 From: Bryce Date: Mon, 29 Apr 2024 22:44:09 -0700 Subject: [PATCH] Sales summaries in slightly more detail --- resources/schema.edn | 42 ++- src/clj/auto_ap/jobs/sales_summaries.clj | 248 +++++++++++++++++- src/clj/auto_ap/ssr/admin/sales_summaries.clj | 109 ++++---- 3 files changed, 320 insertions(+), 79 deletions(-) diff --git a/resources/schema.edn b/resources/schema.edn index d016a865..3ea377d9 100644 --- a/resources/schema.edn +++ b/resources/schema.edn @@ -1883,16 +1883,36 @@ :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/isComponent true, + :db/cardinality :db.cardinality/many} + {:db/ident :sales-summary-item/category :db/valueType :db.type/string :db/cardinality :db.cardinality/one} - {:db/ident :sales-summary-item/item-name - :db/valueType :db.type/string - :db/cardinality :db.cardinality/one} + {:db/ident :ledger-mapped/ledger-side + :db/valueType :db.type/ref + :db/cardinality :db.cardinality/one + :db/noHistory true} + {:db/ident :ledger-mapped/account + :db/valueType :db.type/ref + :db/cardinality :db.cardinality/one + :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/noHistory true, :db/valueType :db.type/double :db/cardinality :db.cardinality/one} + {:db/ident :sales-summary-item/net + :db/noHistory true, + :db/valueType :db.type/double + :db/cardinality :db.cardinality/one} {:db/ident :sales-summary-item/tax :db/noHistory true, :db/valueType :db.type/double @@ -1927,16 +1947,20 @@ :db/cardinality :db.cardinality/one} {:db/ident :sales-summary/total-tax :db/noHistory true, - :db/valueType :db.type/double - :db/cardinality :db.cardinality/one} + :db/isComponent true + :db/valueType :db.type/ref + :db/cardinality :db.cardinality/one + } + {:db/ident :sales-summary/total-tip + :db/noHistory true, + :db/isComponent true + :db/valueType :db.type/ref + :db/cardinality :db.cardinality/one } {:db/ident :sales-summary/total-returns :db/noHistory true, :db/valueType :db.type/double :db/cardinality :db.cardinality/one} - {:db/ident :sales-summary/total-tip - :db/noHistory true, - :db/valueType :db.type/double - :db/cardinality :db.cardinality/one} + {:db/ident :sales-summary/total-card-fees :db/noHistory true, :db/valueType :db.type/double diff --git a/src/clj/auto_ap/jobs/sales_summaries.clj b/src/clj/auto_ap/jobs/sales_summaries.clj index 367789e0..cbf6a1fa 100644 --- a/src/clj/auto_ap/jobs/sales_summaries.clj +++ b/src/clj/auto_ap/jobs/sales_summaries.clj @@ -52,8 +52,236 @@ (filter (fn [sales-summary] (= client-id (:db/id (:sales-summary/client sales-summary)))))))) +(defn- get-fee [c date] + (- (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))) +(defn get-payment-items [c date] + (->> + (dc/q '[:find ?processor ?type-name (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 ?type-name] + (or-join [?c ?processor] + (and [?c :charge/processor ?p] + [?p :db/ident ?processor]) + (and + (not [?c :charge/processor]) + [(ground :ccp-processor/na) ?processor])) + [?c :charge/total ?total]] + (dc/db conn) + [[c] date date]) + (reduce + (fn [acc [processor type-name total]] + (update + acc + (cond (= type-name "CARD") + "Card Payments" + (= type-name "CASH") + "Cash Payments" + (#{"SQUARE_GIFT_CARD" "WALLET" "GIFT_CARD"} type-name) + "Gift Card Payments" + (#{:ccp-processor/toast + #_:ccp-processor/ezcater + #_:ccp-processor/koala + :ccp-processor/doordash + :ccp-processor/grubhub + :ccp-processor/uber-eats} processor) + "Food App Payments" + :else + "Unknown") + (fnil + 0.0) + total)) + {}) + (map (fn [[k v]] + {:db/id (str (java.util.UUID/randomUUID)) + :sales-summary-item/category k + :ledger-mapped/amount (if (= "Card Payments" k) + (- v (get-fee c date)) + v) + :ledger-mapped/ledger-side :ledger-side/debit})))) -(defn sales-summaries [] +(defn get-discounts [c date] + (when-let [discount (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]))] + {:db/id (str (java.util.UUID/randomUUID)) + :sales-summary-item/category "Discounts" + :ledger-mapped/amount discount + :ledger-mapped/ledger-side :ledger-side/debit })) + +(defn get-refund-items [c date] + (->> + (dc/q '[:find ?type-name (sum ?t) + :with ?e + :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 ?type-name] + [?e :sales-refund/total ?t]] + (dc/db conn) + [[c] date date]) + (reduce + (fn [acc [type-name total]] + (update + acc + (cond (= type-name "CARD") + "Card Refunds" + (= type-name "CASH") + "Cash Refunds" + :else + "Food App Refunds") + (fnil + 0.0) + total)) + {}) + (map (fn [[k v]] + {:db/id (str (java.util.UUID/randomUUID)) + :sales-summary-item/category k + :ledger-mapped/amount v + :ledger-mapped/ledger-side :ledger-side/credit})))) + + + +(defn get-fees [c date] + (when-let [fee (get-fee c date)] + {:db/id (str (java.util.UUID/randomUUID)) + :sales-summary-item/category "Fees" + :ledger-mapped/amount fee + :ledger-mapped/ledger-side :ledger-side/debit})) + +(defn- get-tax [c date] + {:db/id (str (java.util.UUID/randomUUID)) + :ledger-mapped/ledger-side :ledger-side/credit + :ledger-mapped/amount + (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)}) + +(defn- get-tip [c date] + {:ledger-mapped/ledger-side :ledger-side/credit + :ledger-mapped/amount (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)}) + +(defn- get-sales [c date] + (let [sales (->> (dc/q '[:find ?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/category "Unknown") ?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]))] + (for [[category total tax discount] sales] + {:db/id (str (java.util.UUID/randomUUID)) + :sales-summary-item/category category + :sales-summary-item/total total + :sales-summary-item/net (- (+ total discount) tax) + :sales-summary-item/tax tax + :sales-summary-item/discount discount + :ledger-mapped/ledger-side :ledger-side/credit + :ledger-mapped/amount (- (+ total discount) tax) + #_#_:ledger-mapped/account nil}))) + +(defn sales-summaries-v2 [] + (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 [result {:db/id id + :sales-summary/client c + :sales-summary/date date + :sales-summary/dirty false + :sales-summary/client+date [c date] + + :sales-summary/sales-items + (conj (get-sales c date) + {:db/id (str (java.util.UUID/randomUUID)) + :sales-summary-item/category "Returns" + + :ledger-mapped/amount (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) + :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 + (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}])))))) + +(let [c (auto-ap.datomic/pull-attr (dc/db conn) :db/id [:client/code "NGCL" ]) + date #inst "2024-04-14T00:00:00-07:00"] + (get-payment-items c date) + + ) + +#_(defn sales-summaries-v1 [] (doseq [[c client-code] (dc/q '[:find ?c ?client-code :in $ :where [?c :client/code ?client-code]] @@ -100,9 +328,6 @@ 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 @@ -110,6 +335,7 @@ :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) @@ -268,14 +494,16 @@ :db/valueType :db.type/double :db/cardinality :db.cardinality/one}]) - (apply mark-dirty [:client/code "NGCL"] (last-n-days 14)) - (apply mark-dirty [:client/code "NGDG"] (last-n-days 14)) - (apply mark-dirty [:client/code "NGPG"] (last-n-days 14)) + (apply mark-dirty [:client/code "NGCL"] (last-n-days 30)) + + (apply mark-dirty [:client/code "NGDG"] (last-n-days 30)) + + (apply mark-dirty [:client/code "NGPG"] (last-n-days 30)) (mark-all-dirty 50) (delete-all) - (sales-summaries) + (sales-summaries-v2) (dc/q '[:find (pull ?sos [* {:sales-summary/sales-items [*]}]) :in $ @@ -304,11 +532,13 @@ (dc/db conn) [[(auto-ap.datomic/pull-attr (dc/db conn) :db/id [:client/code "NGCL"])] #inst "2024-04-11T00:00:00-07:00" #inst "2024-04-24T00:00:00-07:00"]) +@(dc/transact conn [{:db/id :sales-summary/total-tax :db/ident :sales-summary/total-tax-legacy} + {:db/id :sales-summary/total-tip :db/ident :sales-summary/total-tip-legacy}]) ) (defn -main [& _] - (execute "sales-summaries" sales-summaries)) + (execute "sales-summaries" sales-summaries-v2)) \ No newline at end of file diff --git a/src/clj/auto_ap/ssr/admin/sales_summaries.clj b/src/clj/auto_ap/ssr/admin/sales_summaries.clj index ffa03afa..b4188c6a 100644 --- a/src/clj/auto_ap/ssr/admin/sales_summaries.clj +++ b/src/clj/auto_ap/ssr/admin/sales_summaries.clj @@ -44,9 +44,20 @@ :size :small}))]]) (def default-read '[:db/id + + * [:sales-summary/date :xform clj-time.coerce/from-date] {:sales-summary/client [:client/code :client/name]} - *]) ;; TODO + {:sales-summary/total-tax [{[:ledger-mapped/ledger-side :xform iol-ion.query/ident] [:db/ident]} + :ledger-mapped/amount]} + {:sales-summary/total-tip [{[:ledger-mapped/ledger-side :xform iol-ion.query/ident] [:db/ident]} + :ledger-mapped/amount]} + {:sales-summary/sales-items [{[:ledger-mapped/ledger-side :xform iol-ion.query/ident] [:db/ident]} + :ledger-mapped/amount + :sales-summary-item/category]} + {:sales-summary/payment-items [{[:ledger-mapped/ledger-side :xform iol-ion.query/ident] [:db/ident]} + :ledger-mapped/amount + :sales-summary-item/category]}]) ;; TODO (defn fetch-ids [db request] (let [query-params (:parsed-query-params request) @@ -96,7 +107,7 @@ [(->> (hydrate-results ids-to-retrieve db request)) matching-count])) -(defn get-debits [ss] +#_(defn get-debits [ss] {:card-payments (+ (:sales-summary/total-card-payments ss 0.0) (:sales-summary/total-card-fees ss 0.0) (- (:sales-summary/total-card-refunds ss 0.0))) @@ -117,6 +128,11 @@ :discounts (+ (:sales-summary/discount ss 0.0)) :returns (+ (:sales-summary/total-returns ss 0.0))}) +(defn all-items [ss] + (->> [(:sales-summary/total-tax ss) (:sales-summary/total-tip ss)] + (into (:sales-summary/payment-items ss)) + (into (:sales-summary/sales-items ss)))) + (def grid-page (helper/build {:id "entity-table" :id-fn :db/id @@ -159,25 +175,25 @@ :name "credits" :sort-key "credits" :render (fn [ss] - (let [total-debits (reduce + 0.0 (vals (get-debits ss))) - total-credits (+ (- (+ (reduce + 0.0 (map :sales-summary-item/total (:sales-summary/sales-items ss))) - - (reduce + 0.0 (map :sales-summary-item/discount (:sales-summary/sales-items ss)))) - (reduce + 0.0 (map :sales-summary-item/tax (:sales-summary/sales-items ss)))) - - (:sales-summary/total-tax ss 0.0) - (:sales-summary/total-tip ss 0.0))] + (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 [[n x] (group-by :sales-summary-item/category (:sales-summary/sales-items ss))] - [:li n ": " (format "$%,.2f" (- (+ (reduce + 0.0 (map :sales-summary-item/total x)) - (reduce + 0.0 (map :sales-summary-item/discount x))) - (reduce + 0.0 (map :sales-summary-item/tax x))))]) - [:li "Sales subtotal: " (format "$%,.2f" (- (+ (reduce + 0.0 (map :sales-summary-item/total (:sales-summary/sales-items ss))) - (reduce + 0.0 (map :sales-summary-item/discount (:sales-summary/sales-items ss)))) - - (reduce + 0.0 (map :sales-summary-item/tax (:sales-summary/sales-items ss)))))] - [:li "Tax: " (format "$%,.2f" (:sales-summary/total-tax ss))] - [:li "Tips: " (format "$%,.2f" (:sales-summary/total-tip ss))] + (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))]]) @@ -188,51 +204,22 @@ :name "debits" :sort-key "debits" :render (fn [ss] - (let [{:keys [card-payments food-app-payments - cash-payments discounts fees - gift-card-payments - returns refunds total-unknown-processor-payments] :as debits} (get-debits ss) - - total-debits (reduce + 0.0 (vals debits)) - total-credits (+ (- (+ (reduce + 0.0 (map :sales-summary-item/total (:sales-summary/sales-items ss))) - - (reduce + 0.0 (map :sales-summary-item/discount (:sales-summary/sales-items ss)))) - (reduce + 0.0 (map :sales-summary-item/tax (:sales-summary/sales-items ss)))) - - (:sales-summary/total-tax ss 0.0) - (:sales-summary/total-tip ss 0.0))] + (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 - [:li "Card Payments: " - (format "$%,.2f" card-payments)] - - [:li "Food App Payments: " - (format "$%,.2f" food-app-payments)] - [:li "Gift Card Payments" - (format "$%,.2f" gift-card-payments)] - [:li "Unknown Processor Payments: " - - (format "$%,.2f" total-unknown-processor-payments)] - [:li "Cash Payments: " - - (format "$%,.2f" cash-payments)] - [:li "Discounts: " - - (format "$%,.2f" discounts)] - - [:li "Fees: " - (format "$%,.2f" fees)] - [:li "Returns: " - (format "$%,.2f" returns)] - #_[:li "Refunds: " - (format "$%,.2f" refunds)] - - - - + (for [si (:sales-summary/payment-items ss) + :when (= :ledger-side/debit (:ledger-mapped/ledger-side si))] + [:li (:sales-summary-item/category si) ": " (format "$%,.2f" (:ledger-mapped/amount si))]) [:li (com/pill {:color (if (dollars= total-debits total-credits) :primary :red)} "Total: " (format "$%,.2f" total-debits))]]) - + #_(count))}]})) ;; TODO schema cleanup