(ns auto-ap.ssr.pos.sales-orders (:require [auto-ap.datomic :refer [add-sorter-fields apply-pagination apply-sort-3 conn merge-query pull-many query2]] [auto-ap.datomic.sales-orders :as d-sales] [auto-ap.query-params :as query-params :refer [wrap-copy-qp-pqp]] [auto-ap.ssr-routes :as ssr-routes] [auto-ap.ssr.components :as com] [auto-ap.ssr.grid-page-helper :as helper :refer [wrap-apply-sort]] [auto-ap.ssr.pos.common :refer [date-range-field* processor-field* total-field*]] [auto-ap.ssr.svg :as svg] [auto-ap.ssr.utils :refer [apply-middleware-to-all-handlers clj-date-schema default-grid-fields-schema ref->enum-schema wrap-merge-prior-hx wrap-schema-enforce]] [auto-ap.time :as atime] [bidi.bidi :as bidi] [clj-time.coerce :as c] [datomic.api :as dc] [malli.core :as mc])) (def query-schema (mc/schema [:maybe (into [:map {:date-range [:date-range :start-date :end-date]} [:total-gte {:optional true :default nil} [:maybe :double]] [:total-lte {:optional true :default nil} [:maybe :double]] [:start-date {:optional true} [:maybe clj-date-schema]] [:end-date {:optional true} [:maybe clj-date-schema]] [:client {:optional true :default nil} [:maybe [:entity-map {:pull [:db/id :client/name]}]]] [:processor {:optional true} [:maybe (ref->enum-schema "ccp-processor")]]] default-grid-fields-schema)])) (defn filters [request] [:form {"hx-trigger" "datesApplied, change delay:500ms, keyup changed from:.hot-filter delay:1000ms" "hx-get" (bidi/path-for ssr-routes/only-routes :pos-sales-table) "hx-target" "#sales-table" "hx-indicator" "#sales-table" #_#_:hx-disabled-elt "find fieldset"} [:fieldset.space-y-6 (date-range-field* request) (total-field* request) [:div (com/field {:label "Payment Method"} (com/radio-card {:size :small :name "payment-method" :options [{:value "" :content "All"} {:value "CASH" :content "Cash"} {:value "CARD" :content "Card"} {:value "SQUARE_GIFT_CARD" :content "Gift Card"} {:value "OTHER" :content "Other"}]}))] [:div (processor-field* request)] [:div (com/field {:label "Category"} (com/text-input {:name "category" :class "hot-filter" :id "category" :hx-preserve "true" :value (:category (:query-params request)) :placeholder "Fries" :size :small}))]]]) (def default-read '[:db/id :sales-order/external-id, :sales-order/location, [:sales-order/date, :xform clj-time.coerce/from-date] :sales-order/total, :sales-order/tax, :sales-order/tip, :sales-order/line-items, :sales-order/discount, :sales-order/returns, :sales-order/service-charge, :sales-order/vendor, :sales-order/source, :sales-order/reference-link, {:sales-order/client [:client/name :db/id :client/code] :sales-order/charges [:charge/type-name, :charge/total, :charge/tax, :charge/tip, :charge/external-id, :charge/note, :charge/date, :charge/client, :charge/location, :charge/reference-link, {:charge/processor [:db/ident]} {:expected-deposit/_charges [:db/id]}]}]) (defn fetch-ids [db request] (let [query-params (:query-params request) query (cond-> {:query {:find [] :in ['$ '[?clients ?start-date ?end-date]] :where '[[(iol-ion.query/scan-sales-orders $ ?clients ?start-date ?end-date) [[?e _ ?sort-default] ...]]]} :args [db [(:trimmed-clients request) (some-> (:start-date query-params) c/to-date) (some-> (:end-date query-params) c/to-date)]]} (:sort query-params) (add-sorter-fields {"client" ['[?e :sales-order/client ?c] '[?c :client/name ?sort-client]] "date" ['[?e :sales-order/date ?sort-date]] "total" ['[?e :sales-order/total ?sort-total]] "tax" ['[?e :sales-order/tax ?sort-tax]] "tip" ['[?e :sales-order/tip ?sort-tip]] "source" ['[?e :sales-order/source ?sort-source]] "processor" ['[?e :sales-order/processor ?p] '[?p :db/ident ?p2] '[(name ?p2) ?sort-processor]]} query-params) (:exact-match-id query-params) (merge-query {:query {:in ['?e] :where []} :args [(:exact-match-id query-params)]}) (:total-gte query-params) (merge-query {:query {:in ['?total-gte] :where ['[?e :sales-order/total ?a] '[(>= ?a ?total-gte)]]} :args [(:total-gte query-params)]}) (:total-lte query-params) (merge-query {:query {:in ['?total-lte] :where ['[?e :sales-order/total ?a] '[(<= ?a ?total-lte)]]} :args [(:total-lte query-params)]}) (not-empty (:payment-method query-params)) (merge-query {:query {:in ['?type-name] :where ['[?e :sales-order/charges ?chg] '[?chg :charge/type-name ?type-name]]} :args [(:payment-method query-params)]}) (not-empty (:category query-params)) (merge-query {:query {:in ['?category] :where ['[?e :sales-order/line-items ?li] '[?li :order-line-item/category ?category]]} :args [(:category query-params)]}) (:processor query-params) (merge-query {:query {:in ['?processor] :where ['[?e :sales-order/charges ?chg] '[?chg :charge/processor ?processor]]} :args [(:processor query-params)]}) true (merge-query {:query {:find ['?sort-default '?e]}}))] (clojure.pprint/pprint (update-in query [:args] #(drop 1 %))) (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)) charges (->> ids (map results) (map first))] charges)) (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])) (def grid-page (helper/build {:id "sales-table" :nav com/main-aside-nav :page-specific-nav filters :fetch-page fetch-page :query-schema query-schema :oob-render (fn [request] [(assoc-in (date-range-field* request) [1 :hx-swap-oob] true)]) :breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes :company)} "POS"] [:a {:href (bidi/path-for ssr-routes/only-routes :pos-sales)} "Sales"]] :title "Sales orders" :entity-name "Sales orders" :route :pos-sales-table :action-buttons (fn [request] (let [{:keys [total tax]} (d-sales/summarize-orders (:ids (fetch-ids (dc/db conn) request)))] (when (and total tax) [(com/pill {:color :primary} (format "Total $%.2f" total)) (com/pill {:color :secondary} (format "Tax $%.2f" tax))]))) :row-buttons (fn [_ e] (when (:sales-order/reference-link e) [(com/a-icon-button {:href (:sales-order/reference-link e)} svg/external-link)])) :headers [{:key "client" :name "Client" :sort-key "client" :hide? (fn [args] (= (count (:clients args)) 1)) :render #(-> % :sales-order/client :client/code)} {:key "date" :name "Date" :sort-key "date" :render #(atime/unparse-local (:sales-order/date %) atime/standard-time)} {:key "source" :name "Source" :sort-key "source" :render (fn [sales-order] (when (:sales-order/source sales-order) (com/pill {:color :primary} (:sales-order/source sales-order))))} {:key "total" :name "Total" :sort-key "total" :render #(some->> % :sales-order/total (format "$%.2f"))} {:key "tax" :name "Tax" :sort-key "tax" :render #(some->> % :sales-order/tax (format "$%.2f"))} {:key "tip" :name "Tip" :sort-key "tip" :render #(some->> % :sales-order/tip (format "$%.2f"))} {:key "Payment methods" :name "Payment Methods" :render (fn [sales-order] [:div.flex.space-x-2 (for [payment-method (->> sales-order :sales-order/charges (map :charge/type-name) set)] (com/pill {:color :primary} (condp = payment-method "CASH" "cash" "" nil "ALL" nil "CARD" "card" "SQUARE_GIFT_CARD" "gift card" "OTHER" "other" nil)))])}]})) (def row* (partial helper/row* grid-page)) (def table* (partial helper/table* grid-page)) (def key->handler (apply-middleware-to-all-handlers {:pos-sales (helper/page-route grid-page) :pos-sales-table (helper/table-route grid-page)} (fn [h] (-> h (wrap-copy-qp-pqp) (wrap-apply-sort grid-page) (wrap-merge-prior-hx) (wrap-schema-enforce :query-schema query-schema) (wrap-schema-enforce :hx-schema query-schema)))))