Merge branch 'master' into ssr-invoices

This commit is contained in:
2024-04-01 08:27:20 -07:00
8 changed files with 930 additions and 350 deletions

View File

@@ -1847,6 +1847,97 @@
:db/valueType :db.type/tuple :db/valueType :db.type/tuple
:db/tupleAttrs [:expected-deposit/client :expected-deposit/date] :db/tupleAttrs [:expected-deposit/client :expected-deposit/date]
:db/cardinality :db.cardinality/one :db/cardinality :db.cardinality/one
:db/index true}] :db/index true}
{:db/ident :sales-summary/client
:db/valueType :db.type/ref
:db/cardinality :db.cardinality/one
:db/index true}
{:db/ident :sales-summary/date
:db/valueType :db.type/instant
:db/cardinality :db.cardinality/one
:db/index true}
{:db/ident :sales-summary/sales-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 :sales-summary-item/total
: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
:db/cardinality :db.cardinality/one}
{:db/ident :sales-summary-item/discount
:db/noHistory true,
:db/valueType :db.type/double
: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/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/valueType :db.type/double
: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
: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-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}]

View File

@@ -0,0 +1,256 @@
(ns auto-ap.jobs.sales-summaries
(:require [auto-ap.datomic :refer [conn]]
[auto-ap.jobs.core :refer [execute]]
[auto-ap.logging :as alog]
[auto-ap.time :as atime]
[clj-time.coerce :as c]
[clj-time.core :as time]
[clj-time.periodic :as per]
[com.brunobonacci.mulog :as mu]
[datomic.api :as dc]))
(defn mark-dirty [client start end]
(let [client (dc/entid (dc/db conn) client)]
@(dc/transact conn
(for [s (per/periodic-seq start
end
(time/days 1))]
{:sales-summary/client client
:sales-summary/date (c/to-date s)
:sales-summary/dirty true
:sales-summary/client+date [client (c/to-date s)]}))))
(defn last-n-days [n]
[(.toDateMidnight (atime/localize (time/plus (time/now) (time/days (- n)))))
(.toDateMidnight (atime/localize (time/now)))])
(defn mark-all-dirty [days]
(doseq [[c] (dc/q '[:find ?c
:in $
:where [_ :sales-order/client ?c]]
(dc/db conn))]
(apply mark-dirty c (last-n-days days))))
(defn dirty-sales-summaries [c]
(let [client-id (dc/entid (dc/db conn) c)]
(->> (dc/index-pull (dc/db conn)
{:index :avet
:selector '[:sales-summary/date :sales-summary/client :db/id]
:start [:sales-summary/client+dirty [client-id true]]})
(filter (fn [sales-summary]
(= client-id (:db/id (:sales-summary/client sales-summary))))))))
(defn sales-summaries []
(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]
[?li :order-line-item/item-name ?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-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-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
:where [(iol-ion.query/scan-sales-refunds $ ?clients ?start-date ?end-date) [[?e _ ?sort-default] ...]]
(not [?e :sales-refund/type "CASH"])
(not [?e :sales-refund/type "CARD"])
[?e :sales-refund/total ?t]]
(dc/db conn)
[[c] date date]))
0.0)}]
(when (seq (:sales-summary/sales-items result))
(alog/info ::upserting-summaries
:category-count (count (:sales-summary/sales-items result)))
@(dc/transact conn [ [:upsert-entity result]]))))))
(defn reset-summaries []
@(dc/transact conn (->> (dc/q '[:find ?sos
:in $
:where [?sos :sales-summary/client]]
(dc/db conn))
(map (fn [[sos]]
[:db/retractEntity sos])))))
(comment
(auto-ap.datomic/transact-schema conn)
(apply mark-dirty [:client/code "NGCL"] (last-n-days 12))
(mark-all-dirty 30)
(sales-summaries)
(dc/q '[:find (pull ?sos [* {:sales-summary/sales-items [*]}])
:in $
:where [?sos :sales-summary/client [:client/code "NGCL"]]
[?sos :sales-summary/date ?d]
[(= ?d #inst "2024-03-25T00:00:00-07:00")]]
(dc/db conn))
)
(defn -main [& _]
(execute "sales-summaries" sales-summaries))

View File

@@ -99,7 +99,7 @@
{:vendor "Southern Glazers" {:vendor "Southern Glazers"
:keywords [#"Southern Glazer's"] :keywords [#"Southern Glazer's"]
:extract {:date #"INVOICE DATE(?s:.*)(?= (?:[0-9]+/[0-9]+/[0-9]+)\s+([0-9]+/[0-9]+/[0-9]+)) " :extract {:date #"INVOICE DATE(?s:.*)(?= (?:[0-9]+/[0-9]+/[0-9]+)\s+([0-9]+/[0-9]+/[0-9]+)) "
:invoice-number #"INVOICE\n(?:.*?)(?=\d{4,})(\d+)" :invoice-number #"(?s)INVOICE\n(?:.*?)(?=\d{4,})(\d+)"
:total #"PAY THIS AMOUNT(?s:.*)(?= ([0-9,]+\.[0-9]{2}))" :total #"PAY THIS AMOUNT(?s:.*)(?= ([0-9,]+\.[0-9]{2}))"
:account-number #"ACCOUNT #.*\n.*?[\n]?\s+(\d+)"} :account-number #"ACCOUNT #.*\n.*?[\n]?\s+(\d+)"}
:parser {:date [:clj-time "MM/dd/yy"] :parser {:date [:clj-time "MM/dd/yy"]
@@ -317,6 +317,17 @@
:parser {:date [:clj-time "MM/dd/yyyy"] :parser {:date [:clj-time "MM/dd/yyyy"]
:total [:trim-commas nil]}} :total [:trim-commas nil]}}
;; Breakthru Bev
{:vendor "Wine Warehouse"
:keywords [#"BREAKTHRU BEVERAGE"]
:extract {:date #"INVOICE DATE\s+([0-9]+/[0-9]+/[0-9]+)"
:account-number #"CUSTOMER NUMBER\s+(\d+)"
:invoice-number #"INV #\s+(.*?)\n"
:total #"INVOICE AMOUNT\s+\$\s*([\d,\.\-]+)"}
:parser {:date [:clj-time "MM/dd/yyyy"]
:total [:trim-commas nil]}}
;; THE WATER PROS ;; THE WATER PROS
{:vendor "The Water Pros" {:vendor "The Water Pros"
:keywords [#"The Water Pros, Inc"] :keywords [#"The Water Pros, Inc"]

View File

@@ -178,14 +178,10 @@
(defn pc [start end] (defn pc [start end]
{"query" {"filter" {"date_time_filter" {"query" {"filter" {"date_time_filter"
{ {"created_at" {"start_at" (->square-date start)
"created_at" { "end_at" (->square-date end)}}}
"start_at" (->square-date start)
"end_at" (->square-date end)
}}}
"sort" { "sort" {"sort_field" "CREATED_AT"
"sort_field" "CREATED_AT"
"sort_order" "DESC"}}}) "sort_order" "DESC"}}})
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]} #_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
@@ -197,8 +193,7 @@
(let [result (->> (client/get (str "https://connect.squareup.com/v2/orders/" order-id) (let [result (->> (client/get (str "https://connect.squareup.com/v2/orders/" order-id)
{:headers (client-base-headers client) {:headers (client-base-headers client)
:as :json}) :as :json})
:body :body)]
)]
result))) result)))
(defn continue-search [client location start end cursor] (defn continue-search [client location start end cursor]
@@ -268,8 +263,7 @@
(:sales-order/service-charge i)) (:sales-order/service-charge i))
(:sales-order/returns i) (:sales-order/returns i)
(:sales-order/discount i) (:sales-order/discount i))))
)))
0.0 0.0
[])) []))
@@ -281,7 +275,7 @@
:client (:db/id client) :client (:db/id client)
:note (:note t) :note (:note t)
:location (:square-location/client-location location) :location (:square-location/client-location location)
:reference-link (str (url/url "https://squareup.com/receipt/preview" (:id t) )) :reference-link (str (url/url "https://squareup.com/receipt/preview" (:id t)))
:external-id (when (:id t) :external-id (when (:id t)
(str "square/charge/" (:id t))) (str "square/charge/" (:id t)))
:processor (condp = (:type t) :processor (condp = (:type t)
@@ -317,7 +311,7 @@
(defn order->sales-order [client location order] (defn order->sales-order [client location order]
(capture-context->lc (capture-context->lc
(let [is-order-only-for-charge? (= ["CUSTOM_AMOUNT"] (let [is-order-only-for-charge? (= ["CUSTOM_AMOUNT"]
(mapv :item_type (:line_items order )))] (mapv :item_type (:line_items order)))]
(if is-order-only-for-charge? (if is-order-only-for-charge?
(de/success-deferred (de/success-deferred
(->> (:tenders order) (->> (:tenders order)
@@ -345,8 +339,7 @@
(de/catch (fn [e] (de/catch (fn [e]
(log/error ::cant-transform (log/error ::cant-transform
:exception e :exception e
:line-item li))))))) :line-item li))))))))
)
(s/buffer 5) (s/buffer 5)
(s/realize-each) (s/realize-each)
(s/reduce conj []))] (s/reduce conj []))]
@@ -420,7 +413,7 @@
(capture-context->lc lc (capture-context->lc lc
(de/chain (de/chain
(manifold-api-call (manifold-api-call
{:url (str "https://connect.squareup.com/v2/payouts/" poi "/payout-entries" "?cursor=" cursor ) {:url (str "https://connect.squareup.com/v2/payouts/" poi "/payout-entries" "?cursor=" cursor)
:method :get :method :get
:headers (client-base-headers c "2023-04-19") :headers (client-base-headers c "2023-04-19")
:as :json}) :as :json})
@@ -465,10 +458,9 @@
(url/map->query (url/map->query
{:location_id (:square-location/square-id location) {:location_id (:square-location/square-id location)
:begin_time (->square-date start) :begin_time (->square-date start)
:end_time (->square-date end)}) ) :end_time (->square-date end)}))
:method :get :method :get
:headers (client-base-headers client "2023-04-19") :headers (client-base-headers client "2023-04-19")})
})
:body :body
:payouts :payouts
(fn [payouts] (fn [payouts]
@@ -481,7 +473,7 @@
:payout-id (:id payout)) :payout-id (:id payout))
(de/chain (get-payout-entry-list client location (:id payout)) (de/chain (get-payout-entry-list client location (:id payout))
(fn [payout-entries] (fn [payout-entries]
(assoc payout :payout_entries payout-entries )))))) (assoc payout :payout_entries payout-entries))))))
(s/buffer 10) (s/buffer 10)
(s/realize-each) (s/realize-each)
(s/reduce conj [])) (s/reduce conj []))
@@ -530,12 +522,10 @@
[?s :expected-deposit/external-id ?eid] [?s :expected-deposit/external-id ?eid]
[(clojure.string/includes? ?eid "settlement")] [(clojure.string/includes? ?eid "settlement")]
[?s :expected-deposit/total ?t] [?s :expected-deposit/total ?t]
[(iol-ion.query/dollars= ?t ?a)] [(iol-ion.query/dollars= ?t ?a)]]
]
(dc/db conn) (dc/db conn)
(:db/id client) (:db/id client)
(amount->money (:amount_money payout)) (amount->money (:amount_money payout))))]
))]
:when (not equivalent-already-exists?)] :when (not equivalent-already-exists?)]
#:expected-deposit {:external-id (str "square/payout/" (:id payout)) #:expected-deposit {:external-id (str "square/payout/" (:id payout))
:vendor :vendor/ccp-square :vendor :vendor/ccp-square
@@ -574,7 +564,7 @@
(fn [refunds] (fn [refunds]
(->> refunds (->> refunds
(filter (fn [r] (= "COMPLETED" (:status r)))) (filter (fn [r] (= "COMPLETED" (:status r))))
(s/->source ) (s/->source)
(s/map (fn [r] (s/map (fn [r]
(de/chain (de/chain
(get-payment client (:payment_id r)) (get-payment client (:payment_id r))
@@ -597,7 +587,7 @@
(s/realize-each) (s/realize-each)
(s/reduce conj [])))))) (s/reduce conj []))))))
(defn upsert (defn upsert
([client ] ([client]
(apply de/zip (apply de/zip
(for [square-location (:client/square-locations client) (for [square-location (:client/square-locations client)
:when (:square-location/client-location square-location)] :when (:square-location/client-location square-location)]
@@ -605,7 +595,7 @@
([client location start end] ([client location start end]
(capture-context->lc (capture-context->lc
(de/chain (daily-results client location start end) (de/chain (daily-results client location start end)
(fn [results ] (fn [results]
(mu/with-context lc (mu/with-context lc
(doseq [x (partition-all 100 results)] (doseq [x (partition-all 100 results)]
(log/info ::loading-orders (log/info ::loading-orders
@@ -663,9 +653,7 @@
(defn get-cash-shift [client id] (defn get-cash-shift [client id]
(de/chain (manifold-api-call {:url (str (url/url "https://connect.squareup.com/v2/cash-drawers/shifts" id (de/chain (manifold-api-call {:url (str (url/url "https://connect.squareup.com/v2/cash-drawers/shifts" id))
))
:method :get :method :get
:headers (client-base-headers client "2023-04-19") :headers (client-base-headers client "2023-04-19")
@@ -694,7 +682,7 @@
(fn [shifts] (fn [shifts]
(->> shifts (->> shifts
(filter (fn [r] (= "ENDED" (:state r)))) (filter (fn [r] (= "ENDED" (:state r))))
(s/->source ) (s/->source)
(s/map (fn [s] (s/map (fn [s]
(de/chain (de/chain
(get-cash-shift client (:id s)) (get-cash-shift client (:id s))
@@ -707,8 +695,7 @@
:opened-cash (amount->money (:opened_cash_money cash-drawer-shift)) :opened-cash (amount->money (:opened_cash_money cash-drawer-shift))
:date (coerce/to-date (:opened_at cash-drawer-shift)) :date (coerce/to-date (:opened_at cash-drawer-shift))
:client (:db/id client) :client (:db/id client)
:location (:square-location/client-location l) :location (:square-location/client-location l)}))))
}))))
(s/buffer 5) (s/buffer 5)
(s/realize-each) (s/realize-each)
(s/reduce conj [])))))) (s/reduce conj []))))))
@@ -754,7 +741,7 @@
:in $ :in $
:where [?c :client/square-auth-token]] :where [?c :client/square-auth-token]]
(dc/db conn)))) (dc/db conn))))
([ & codes] ([& codes]
(map first (dc/q '[:find (pull ?c [:db/id (map first (dc/q '[:find (pull ?c [:db/id
:client/code :client/code
:client/square-auth-token :client/square-auth-token
@@ -812,7 +799,7 @@
:db/id (or (-> client :client/square-integration-status :db/id) :db/id (or (-> client :client/square-integration-status :db/id)
(str (java.util.UUID/randomUUID))))}])) (str (java.util.UUID/randomUUID))))}]))
(defn upsert-all [ & clients] (defn upsert-all [& clients]
(capture-context->lc (capture-context->lc
(log/info ::starting-upsert) (log/info ::starting-upsert)
(->> (apply get-square-clients clients) (->> (apply get-square-clients clients)
@@ -875,3 +862,4 @@
[:clients clients] [:clients clients]
@(apply upsert-all clients))) @(apply upsert-all clients)))

View File

@@ -0,0 +1,227 @@
(ns auto-ap.ssr.admin.sales-summaries
(:require [auto-ap.datomic
:refer [apply-pagination apply-sort-3 conn merge-query pull-many
query2]]
[auto-ap.graphql.utils :refer [extract-client-ids]]
[auto-ap.routes.admin.sales-summaries :as route]
[auto-ap.routes.utils
:refer [wrap-admin wrap-client-redirect-unauthenticated]]
[auto-ap.ssr-routes :as ssr-routes]
[auto-ap.ssr.components :as com]
[auto-ap.ssr.grid-page-helper :as helper]
[auto-ap.ssr.utils
:refer [apply-middleware-to-all-handlers]]
[auto-ap.time :as atime]
[bidi.bidi :as bidi]
[clj-time.coerce :as c]
[datomic.api :as dc]
[iol-ion.query :refer [dollars=]]))
(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"}
#_[:fieldset.space-y-6
(date-range-field {:value {:start (:start-date (:parsed-query-params request))
:end (:end-date (:parsed-query-params request))}
:id "date-range"})
(com/field {:label "Source"}
(com/select {:name "source"
:class "hot-filter w-full"
:value (:source (:parsed-query-params request))
:placeholder ""
:options (ref->select-options "import-source" :allow-nil? true)}))
#_(com/field {:label "Code"}
(com/text-input {:name "code"
:id "code"
:class "hot-filter"
:value (:code (:parsed-query-params request))
:placeholder "11101"
:size :small}))]])
(def default-read '[:db/id
[:sales-summary/date :xform clj-time.coerce/from-date]
*]) ;; TODO
(defn fetch-ids [db request]
(let [query-params (:parsed-query-params request)
valid-clients (extract-client-ids (:clients request)
(:client request)
(:client-id query-params)
(when (:client-code query-params)
[:client/code (:client-code query-params)]))
query (cond-> {:query {:find []
:in '[$ [?client ...]]
:where '[[?e :sales-summary/client ?client]]}
: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]
:where [[(>= ?d ?start-date)]]}
:args [(-> query-params :start-date c/to-date)]})
(:end-date query-params)
(merge-query {:query '{:in [?end-date]
:where [[(< ?d ?end-date)]]}
:args [(-> query-params :end-date c/to-date)]})
true
(merge-query {:query {:find ['?sort-default '?e]
:where ['[?e :sales-summary/date ?sort-default]]}}))]
(cond->> (query2 query)
true (apply-sort-3 query-params)
true (apply-pagination query-params))))
(defn hydrate-results [ids db _]
(let [results (->> (pull-many db default-read ids)
(group-by :db/id))
refunds (->> ids
(map results)
(map first))]
refunds))
(defn fetch-page [request]
(let [db (dc/db conn)
{ids-to-retrieve :ids matching-count :count} (fetch-ids db request)]
[(->> (hydrate-results ids-to-retrieve db request))
matching-count]))
(defn get-credits [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)))
:food-app-payments (+ (:sales-summary/total-food-app-payments ss 0.0)
(:sales-summary/total-food-app-fees ss 0.0)
(- (:sales-summary/total-food-app-refunds ss 0.0)))
:fees (- (:sales-summary/total-card-fees ss 0.0))
:cash-payments (+ (:sales-summary/total-cash-payments ss 0.0)
(- (:sales-summary/total-cash-refunds ss 0.0)))
:discounts (+ (:sales-summary/discount ss 0.0))
:returns (+ (:sales-summary/total-returns ss 0.0))})
(def grid-page
(helper/build {:id "entity-table"
:id-fn :db/id
:nav (com/admin-aside-nav)
:fetch-page fetch-page
:page-specific-nav filters
:row-buttons (fn [_ entity]
[])
:oob-render
(fn [request]
[#_(assoc-in (date-range-field {:value {:start (:start-date (:parsed-query-params request))
:end (:end-date (:parsed-query-params request))}
:id "date-range"}) [1 :hx-swap-oob] true)]) ;; TODO
:parse-query-params (comp
(helper/default-parse-query-params grid-page))
:breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes
:admin)}
"Admin"]
[: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 "date"
:name "Date"
:sort-key "date"
:render #(some-> % :sales-summary/date (atime/unparse-local atime/normal-date))}
{:key "credits"
:name "Credits"
:sort-key "credits"
:render (fn [ss]
(let [total-credits (reduce + 0.0 (vals (get-credits ss)))
total-debits (+ (- (+ (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))]
[: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))]
[:li (com/pill {:color (if (dollars= total-credits total-debits)
:primary
:red)} "Total: " (format "$%,.2f" total-debits))]])
#_(count))}
{:key "debits"
:name "debits"
:sort-key "debits"
:render (fn [ss]
(let [{:keys [card-payments food-app-payments
cash-payments discounts fees
returns] :as credits} (get-credits ss)
total-credits (reduce + 0.0 (vals credits))
total-debits (+ (- (+ (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))]
[:ul
[:li "Card Payments: "
(format "$%,.2f" card-payments)]
[:li "Food App Payments: "
(format "$%,.2f" food-app-payments)]
[:li "Cash Payments: "
(format "$%,.2f" cash-payments)]
[:li "Discounts: "
(format "$%,.2f" discounts)]
[:li "Fees: "
(format "$%,.2f" fees)]
[:li "Returns: "
(format "$%,.2f" returns)]
[:li (com/pill {:color (if (dollars= total-credits total-debits)
:primary
:red)} "Total: " (format "$%,.2f" total-credits))]])
#_(count))}]}))
;; TODO schema cleanup
;; Decide on what should be calculated as generating ledger entries, and what should be calculated
;; as part of the summary
;; default thought here is that the summary has more detail (e.g., line items), fees broken out by type
;; and aggregated into the final ledger entry
;; that allows customization at any level.
;; TODO rename refunds/returns
(def row* (partial helper/row* grid-page))
(def table* (partial helper/table* grid-page))
(def key->handler
(apply-middleware-to-all-handlers
(->>
{::route/page (helper/page-route grid-page)
::route/table (helper/table-route grid-page)})
(fn [h]
(-> h
(wrap-admin)
(wrap-client-redirect-unauthenticated)))))

View File

@@ -14,6 +14,7 @@
[auto-ap.ssr.admin.transaction-rules :as admin-rules] [auto-ap.ssr.admin.transaction-rules :as admin-rules]
[auto-ap.ssr.admin.vendors :as admin-vendors] [auto-ap.ssr.admin.vendors :as admin-vendors]
[auto-ap.ssr.admin.clients :as admin-clients] [auto-ap.ssr.admin.clients :as admin-clients]
[auto-ap.ssr.admin.sales-summaries :as admin-sales-summaries]
[auto-ap.ssr.auth :as auth] [auto-ap.ssr.auth :as auth]
[auto-ap.ssr.indicators :as indicators] [auto-ap.ssr.indicators :as indicators]
[auto-ap.ssr.company :as company] [auto-ap.ssr.company :as company]
@@ -94,6 +95,7 @@
(into admin-excel-invoices/key->handler) (into admin-excel-invoices/key->handler)
(into admin/key->handler) (into admin/key->handler)
(into admin-jobs/key->handler) (into admin-jobs/key->handler)
(into admin-sales-summaries/key->handler)
(into admin-vendors/key->handler) (into admin-vendors/key->handler)
(into admin-clients/key->handler) (into admin-clients/key->handler)
(into admin-rules/key->handler) (into admin-rules/key->handler)

View File

@@ -0,0 +1,3 @@
(ns auto-ap.routes.admin.sales-summaries)
(def routes {"" {:get ::page}
"/table" ::table})

View File

@@ -7,6 +7,7 @@
[auto-ap.routes.payments :as p-routes] [auto-ap.routes.payments :as p-routes]
[auto-ap.routes.invoice :as i-routes] [auto-ap.routes.invoice :as i-routes]
[auto-ap.routes.admin.clients :as ac-routes] [auto-ap.routes.admin.clients :as ac-routes]
[auto-ap.routes.admin.sales-summaries :as ss-routes]
[auto-ap.routes.admin.transaction-rules :as tr-routes])) [auto-ap.routes.admin.transaction-rules :as tr-routes]))
(def routes {"impersonate" :impersonate (def routes {"impersonate" :impersonate
@@ -51,7 +52,8 @@
["/disapprove/" [#"\d+" :transaction-id]] {:delete :transaction-insight-disapprove} ["/disapprove/" [#"\d+" :transaction-id]] {:delete :transaction-insight-disapprove}
["/rows/" [#"\d+" :after]] {:get :transaction-insight-rows} ["/rows/" [#"\d+" :after]] {:get :transaction-insight-rows}
["/explain/" [#"\d+" :transaction-id]] {:get :transaction-insight-explain}}} ["/explain/" [#"\d+" :transaction-id]] {:get :transaction-insight-explain}}}
"pos" {"/sales" {"" {:get :pos-sales} "pos" {"/summaries" ss-routes/routes
"/sales" {"" {:get :pos-sales}
"/table" {:get :pos-sales-table}} "/table" {:get :pos-sales-table}}
"/expected-deposit" {"" {:get :pos-expected-deposits} "/expected-deposit" {"" {:get :pos-expected-deposits}
"/table" {:get :pos-expected-deposit-table}} "/table" {:get :pos-expected-deposit-table}}