payments ssr
voiding supports bulk void. exact match id linking voidnig payments works. minor tweak.
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -1334,10 +1334,7 @@
|
|||||||
:db/cardinality #:db{:ident :db.cardinality/one},
|
:db/cardinality #:db{:ident :db.cardinality/one},
|
||||||
:db/doc "The specific client this rule is for",
|
:db/doc "The specific client this rule is for",
|
||||||
:db/ident :transaction-rule/client}
|
:db/ident :transaction-rule/client}
|
||||||
{:db/valueType #:db{:ident :db.type/string},
|
{:db/valueType #:db{:ident :db.type/string}, :db/cardinality #:db{:ident :db.cardinality/one}, :db/doc "The specific client group this rule is for", :db/ident :transaction-rule/client-group}
|
||||||
:db/cardinality #:db{:ident :db.cardinality/one},
|
|
||||||
:db/doc "The specific client group this rule is for",
|
|
||||||
:db/ident :transaction-rule/client-group}
|
|
||||||
{:db/valueType #:db{:ident :db.type/ref},
|
{:db/valueType #:db{:ident :db.type/ref},
|
||||||
:db/cardinality #:db{:ident :db.cardinality/one},
|
:db/cardinality #:db{:ident :db.cardinality/one},
|
||||||
:db/doc "The specific bank account this rule is for",
|
:db/doc "The specific bank account this rule is for",
|
||||||
|
|||||||
@@ -570,6 +570,7 @@
|
|||||||
|
|
||||||
(defn get-all-payments [context args _]
|
(defn get-all-payments [context args _]
|
||||||
(assert-admin (:id context))
|
(assert-admin (:id context))
|
||||||
|
(println "HERE")
|
||||||
(map
|
(map
|
||||||
->graphql
|
->graphql
|
||||||
(first (d-checks/get-graphql (assoc (<-graphql (assoc args :clients (:clients context))) :count Integer/MAX_VALUE)))))
|
(first (d-checks/get-graphql (assoc (<-graphql (assoc args :clients (:clients context))) :count Integer/MAX_VALUE)))))
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
(ns auto-ap.graphql.utils
|
(ns auto-ap.graphql.utils
|
||||||
(:require [clojure.string :as str]
|
(:require [auto-ap.datomic :refer [conn]]
|
||||||
[auto-ap.datomic :refer [conn]]
|
[auto-ap.logging :as alog]
|
||||||
[clj-time.coerce :as coerce]
|
|
||||||
[auto-ap.time :as atime]
|
[auto-ap.time :as atime]
|
||||||
[buddy.auth :refer [throw-unauthorized]]
|
[buddy.auth :refer [throw-unauthorized]]
|
||||||
[datomic.api :as dc]
|
[cheshire.core :as cheshire]
|
||||||
[iol-ion.query :refer [entid]]
|
[clj-time.coerce :as coerce]
|
||||||
[clojure.walk :as walk]
|
|
||||||
[com.walmartlabs.lacinia.util :refer [attach-resolvers]]
|
|
||||||
[com.brunobonacci.mulog :as mu]
|
|
||||||
[clojure.set :as set]
|
[clojure.set :as set]
|
||||||
[auto-ap.logging :as alog]))
|
[clojure.string :as str]
|
||||||
|
[clojure.walk :as walk]
|
||||||
|
[com.brunobonacci.mulog :as mu]
|
||||||
|
[com.walmartlabs.lacinia.util :refer [attach-resolvers]]
|
||||||
|
[datomic.api :as dc]
|
||||||
|
[hiccup2.core :as hiccup]
|
||||||
|
[iol-ion.query :refer [entid]]))
|
||||||
|
|
||||||
|
|
||||||
(defn snake->kebab [s]
|
(defn snake->kebab [s]
|
||||||
@@ -192,3 +194,19 @@
|
|||||||
(if (seq extra-client-ids)
|
(if (seq extra-client-ids)
|
||||||
(set/intersection user-client-ids extra-client-ids)
|
(set/intersection user-client-ids extra-client-ids)
|
||||||
user-client-ids)))
|
user-client-ids)))
|
||||||
|
|
||||||
|
(defn exception->notification [f]
|
||||||
|
(try
|
||||||
|
(f)
|
||||||
|
(catch Throwable e
|
||||||
|
(throw (ex-info (.getMessage e)
|
||||||
|
{:type :notification}
|
||||||
|
e)))))
|
||||||
|
|
||||||
|
(defn notify-if-locked [client-id date]
|
||||||
|
(try
|
||||||
|
(assert-not-locked client-id date)
|
||||||
|
(catch Exception e
|
||||||
|
(throw (ex-info (.getMessage e)
|
||||||
|
{:type :notification}
|
||||||
|
e)))))
|
||||||
|
|||||||
@@ -1,42 +1,44 @@
|
|||||||
(ns auto-ap.handler
|
(ns auto-ap.handler
|
||||||
(:require
|
(:require [amazonica.core :refer [defcredential]]
|
||||||
[amazonica.core :refer [defcredential]]
|
[auto-ap.client-routes :as client-routes]
|
||||||
[auto-ap.client-routes :as client-routes]
|
[auto-ap.datomic :refer [conn pull-many]]
|
||||||
[auto-ap.datomic :refer [conn pull-many]]
|
[auto-ap.datomic.clients :as d-clients]
|
||||||
[auto-ap.datomic.clients :as d-clients]
|
[auto-ap.graphql.utils :refer [assert-can-see-client
|
||||||
[auto-ap.graphql.utils :refer [assert-can-see-client limited-clients]]
|
limited-clients]]
|
||||||
[auto-ap.logging :as alog]
|
[auto-ap.logging :as alog]
|
||||||
[auto-ap.routes.auth :as auth]
|
[auto-ap.routes.auth :as auth]
|
||||||
[auto-ap.routes.exports :as exports]
|
[auto-ap.routes.exports :as exports]
|
||||||
[auto-ap.routes.ezcater :as ezcater]
|
[auto-ap.routes.ezcater :as ezcater]
|
||||||
[auto-ap.routes.graphql :as graphql]
|
[auto-ap.routes.graphql :as graphql]
|
||||||
[auto-ap.routes.health :as health]
|
[auto-ap.routes.health :as health]
|
||||||
[auto-ap.routes.invoices :as invoices]
|
[auto-ap.routes.invoices :as invoices]
|
||||||
[auto-ap.routes.queries :as queries]
|
[auto-ap.routes.queries :as queries]
|
||||||
[auto-ap.routes.yodlee2 :as yodlee2]
|
[auto-ap.routes.yodlee2 :as yodlee2]
|
||||||
[auto-ap.ssr-routes :as ssr-routes]
|
[auto-ap.ssr-routes :as ssr-routes]
|
||||||
[auto-ap.ssr.core :as ssr]
|
[auto-ap.ssr.core :as ssr]
|
||||||
[bidi.bidi :as bidi]
|
[bidi.bidi :as bidi]
|
||||||
[bidi.ring :refer [->ResourcesMaybe make-handler]]
|
[bidi.ring :refer [->ResourcesMaybe make-handler]]
|
||||||
[buddy.auth.backends.session :refer [session-backend]]
|
[buddy.auth.backends.session :refer [session-backend]]
|
||||||
[buddy.auth.backends.token :refer [jws-backend]]
|
[buddy.auth.backends.token :refer [jws-backend]]
|
||||||
[buddy.auth.middleware :refer [wrap-authentication wrap-authorization]]
|
[buddy.auth.middleware :refer [wrap-authentication
|
||||||
[cemerick.url :as url]
|
wrap-authorization]]
|
||||||
[clj-time.coerce :as coerce]
|
[cemerick.url :as url]
|
||||||
[clj-time.core :as time]
|
[cheshire.core :as cheshire]
|
||||||
[clojure.string :as str]
|
[clj-time.coerce :as coerce]
|
||||||
[clojure.edn :as edn]
|
[clj-time.core :as time]
|
||||||
[com.brunobonacci.mulog :as mu]
|
[clojure.edn :as edn]
|
||||||
[config.core :refer [env]]
|
[clojure.set :as set]
|
||||||
[datomic.api :as dc]
|
[clojure.string :as str]
|
||||||
[ring.middleware.edn :refer [wrap-edn-params]]
|
[com.brunobonacci.mulog :as mu]
|
||||||
[ring.middleware.multipart-params :as mp]
|
[config.core :refer [env]]
|
||||||
[ring.middleware.params :refer [wrap-params]]
|
[datomic.api :as dc]
|
||||||
[ring.middleware.reload :refer [wrap-reload]]
|
[hiccup2.core :as hiccup]
|
||||||
[ring.middleware.session :refer [wrap-session]]
|
[ring.middleware.edn :refer [wrap-edn-params]]
|
||||||
[ring.middleware.session.cookie :refer [cookie-store]]
|
[ring.middleware.multipart-params :as mp]
|
||||||
[ring.util.response :as response]
|
[ring.middleware.params :refer [wrap-params]]
|
||||||
[clojure.set :as set]))
|
[ring.middleware.session :refer [wrap-session]]
|
||||||
|
[ring.middleware.session.cookie :refer [cookie-store]]
|
||||||
|
[ring.util.response :as response]))
|
||||||
|
|
||||||
(when (:aws-access-key-id env)
|
(when (:aws-access-key-id env)
|
||||||
(defcredential (:aws-access-key-id env) (:aws-secret-access-key env) (:aws-region env)))
|
(defcredential (:aws-access-key-id env) (:aws-secret-access-key env) (:aws-region env)))
|
||||||
@@ -284,6 +286,19 @@
|
|||||||
(clojure.pprint/pprint (:session request))
|
(clojure.pprint/pprint (:session request))
|
||||||
(handler request)))
|
(handler request)))
|
||||||
|
|
||||||
|
(defn wrap-error [handler]
|
||||||
|
(fn error-handling-request [request]
|
||||||
|
(try
|
||||||
|
(handler request)
|
||||||
|
(catch Throwable e
|
||||||
|
(if (= :notification (:type (ex-data e)))
|
||||||
|
{:status 200
|
||||||
|
:headers {"hx-trigger" (cheshire/generate-string
|
||||||
|
{"notification" (str (hiccup/html [:div (.getMessage e)]))})
|
||||||
|
"hx-reswap" "none"}}
|
||||||
|
{:status 500
|
||||||
|
:body (pr-str e)})))))
|
||||||
|
|
||||||
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
|
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
|
||||||
(defonce app
|
(defonce app
|
||||||
(-> route-handler
|
(-> route-handler
|
||||||
@@ -308,4 +323,5 @@
|
|||||||
#_(wrap-reload)
|
#_(wrap-reload)
|
||||||
(wrap-params)
|
(wrap-params)
|
||||||
(mp/wrap-multipart-params)
|
(mp/wrap-multipart-params)
|
||||||
(wrap-edn-params)))
|
(wrap-edn-params)
|
||||||
|
(wrap-error)))
|
||||||
|
|||||||
@@ -25,20 +25,20 @@
|
|||||||
|
|
||||||
(defn parse-sort [grid-spec q]
|
(defn parse-sort [grid-spec q]
|
||||||
(if (not-empty q)
|
(if (not-empty q)
|
||||||
(->>
|
(->>
|
||||||
(str/split q #",")
|
(str/split q #",")
|
||||||
(map (fn [k]
|
(map (fn [k]
|
||||||
(let [[key asc?] (str/split k #":")
|
(let [[key asc?] (str/split k #":")
|
||||||
matching-header (first (filter #(= (str key) (:sort-key %)) (:headers grid-spec)))]
|
matching-header (first (filter #(= (str key) (:sort-key %)) (:headers grid-spec)))]
|
||||||
{:sort-key (str key)
|
{:sort-key (str key)
|
||||||
:asc (boolean (= "asc" asc?))
|
:asc (boolean (= "asc" asc?))
|
||||||
:matching-header matching-header
|
:matching-header matching-header
|
||||||
:name (:name matching-header)
|
:name (:name matching-header)
|
||||||
:sort-icon (if (= (boolean (= "asc" asc?)) true)
|
:sort-icon (if (= (boolean (= "asc" asc?)) true)
|
||||||
svg/sort-down
|
svg/sort-down
|
||||||
svg/sort-up)})))
|
svg/sort-up)})))
|
||||||
(filter :matching-header)
|
(filter :matching-header)
|
||||||
(into []))
|
(into []))
|
||||||
[]))
|
[]))
|
||||||
|
|
||||||
(defn parse-long [l]
|
(defn parse-long [l]
|
||||||
@@ -55,30 +55,30 @@
|
|||||||
|
|
||||||
(defn apply-date-range [source-key start-date-key end-date-key]
|
(defn apply-date-range [source-key start-date-key end-date-key]
|
||||||
(fn [query-params]
|
(fn [query-params]
|
||||||
(dissoc
|
(dissoc
|
||||||
(condp = (source-key query-params)
|
(condp = (source-key query-params)
|
||||||
"week"
|
"week"
|
||||||
(assoc query-params
|
(assoc query-params
|
||||||
start-date-key (time/plus (time/now) (time/days -7))
|
start-date-key (time/plus (time/now) (time/days -7))
|
||||||
end-date-key (time/now))
|
end-date-key (time/now))
|
||||||
|
|
||||||
"month"
|
"month"
|
||||||
(assoc query-params
|
(assoc query-params
|
||||||
start-date-key (time/plus (time/now) (time/months -1))
|
start-date-key (time/plus (time/now) (time/months -1))
|
||||||
end-date-key (time/now))
|
end-date-key (time/now))
|
||||||
|
|
||||||
"year"
|
"year"
|
||||||
(assoc query-params
|
(assoc query-params
|
||||||
start-date-key (time/plus (time/now) (time/years -1))
|
start-date-key (time/plus (time/now) (time/years -1))
|
||||||
end-date-key (time/now))
|
end-date-key (time/now))
|
||||||
|
|
||||||
"all"
|
"all"
|
||||||
(assoc query-params
|
(assoc query-params
|
||||||
start-date-key (time/plus (time/now) (time/years -3))
|
start-date-key (time/plus (time/now) (time/years -3))
|
||||||
end-date-key (time/now))
|
end-date-key (time/now))
|
||||||
|
|
||||||
query-params)
|
query-params)
|
||||||
:date-range)))
|
:date-range)))
|
||||||
|
|
||||||
(defn apply-toggle-sort [grid-spec]
|
(defn apply-toggle-sort [grid-spec]
|
||||||
(fn toggle-sort [query-params]
|
(fn toggle-sort [query-params]
|
||||||
|
|||||||
@@ -660,7 +660,7 @@
|
|||||||
:export-ntg-sales-snapshot (-> export-ntg-sales-snapshot wrap-csv-response
|
:export-ntg-sales-snapshot (-> export-ntg-sales-snapshot wrap-csv-response
|
||||||
(wrap-schema-enforce :query-schema (mc/schema [:map
|
(wrap-schema-enforce :query-schema (mc/schema [:map
|
||||||
[:date {:required true
|
[:date {:required true
|
||||||
:decode/string #(try (atime/parse % atime/iso-date) (catch Exception e nil))} :some]]) )
|
:decode/string #(try (atime/parse % atime/iso-date) (catch Exception _ nil))} :some]]) )
|
||||||
(wrap-form-4xx-2 (fn [_] {:body "Invalid Date"}))
|
(wrap-form-4xx-2 (fn [_] {:body "Invalid Date"}))
|
||||||
(wrap-predetermined-api-key "fd07755a-ed4c-4c9a-ad85-fbdd8af37206")
|
(wrap-predetermined-api-key "fd07755a-ed4c-4c9a-ad85-fbdd8af37206")
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,3 +1,29 @@
|
|||||||
|
(ns auto-ap.routes.exports
|
||||||
|
(:require
|
||||||
|
[auto-ap.datomic :refer [conn pull-attr pull-many]]
|
||||||
|
[auto-ap.datomic.accounts :as accounts]
|
||||||
|
[auto-ap.datomic.clients :as d-clients]
|
||||||
|
[auto-ap.datomic.transactions :as d-transactions]
|
||||||
|
[auto-ap.datomic.vendors :as vendor]
|
||||||
|
[auto-ap.graphql :as graphql]
|
||||||
|
[auto-ap.graphql.utils
|
||||||
|
:refer [->graphql <-graphql assert-admin assert-can-see-client]]
|
||||||
|
[auto-ap.logging :as alog]
|
||||||
|
[auto-ap.routes.utils :refer [wrap-secure]]
|
||||||
|
[auto-ap.ssr.utils :refer [wrap-schema-enforce wrap-form-4xx-2]]
|
||||||
|
[auto-ap.time :as atime]
|
||||||
|
[buddy.sign.jwt :as jwt]
|
||||||
|
[cheshire.generate :as generate]
|
||||||
|
[clj-time.coerce :as coerce :refer [to-date]]
|
||||||
|
[clj-time.core :as time]
|
||||||
|
[clojure.data.csv :as csv]
|
||||||
|
[clojure.edn :refer [read-string]]
|
||||||
|
[com.unbounce.dogstatsd.core :as statsd]
|
||||||
|
[config.core :refer [env]]
|
||||||
|
[datomic.api :as dc]
|
||||||
|
[malli.core :as mc]
|
||||||
|
[ring.middleware.json :refer [wrap-json-response]]
|
||||||
|
[venia.core :as venia]))
|
||||||
|
|
||||||
(let [query [[:all_payments
|
(let [query [[:all_payments
|
||||||
{:client-code "VS"
|
{:client-code "VS"
|
||||||
|
|||||||
@@ -43,12 +43,8 @@
|
|||||||
[manifold.deferred :as de])
|
[manifold.deferred :as de])
|
||||||
(:import [java.util UUID]))
|
(:import [java.util UUID]))
|
||||||
|
|
||||||
|
|
||||||
;; TODO make more reusable malli schemas, use unions if it would be helpful
|
;; TODO make more reusable malli schemas, use unions if it would be helpful
|
||||||
;; TODO copy save logic from graphql version
|
|
||||||
;; TODO cash drawer shift
|
;; TODO cash drawer shift
|
||||||
;; TODO a few bug fixes from slack
|
|
||||||
;; TOOD check pinecone
|
|
||||||
|
|
||||||
(defn filters [request]
|
(defn filters [request]
|
||||||
[:form {"hx-trigger" "change delay:500ms, keyup changed from:.hot-filter delay:1000ms"
|
[:form {"hx-trigger" "change delay:500ms, keyup changed from:.hot-filter delay:1000ms"
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
(ns auto-ap.ssr.components.aside
|
(ns auto-ap.ssr.components.aside
|
||||||
(:require [auto-ap.ssr.svg :as svg]
|
(:require [auto-ap.client-routes :as client-routes]
|
||||||
[hiccup2.core :as hiccup]
|
|
||||||
[bidi.bidi :as bidi]
|
|
||||||
[auto-ap.ssr-routes :as ssr-routes]
|
|
||||||
[auto-ap.client-routes :as client-routes]
|
|
||||||
[auto-ap.ssr.hx :as hx]
|
|
||||||
[auto-ap.routes.admin.transaction-rules :as transaction-rules]
|
|
||||||
[auto-ap.ssr.hiccup-helper :as hh]
|
|
||||||
[auto-ap.routes.admin.import-batch :as ib-routes]
|
|
||||||
[auto-ap.routes.admin.clients :as ac-routes]
|
[auto-ap.routes.admin.clients :as ac-routes]
|
||||||
[auto-ap.routes.admin.excel-invoices :as ei-routes]
|
[auto-ap.routes.admin.excel-invoices :as ei-routes]
|
||||||
|
[auto-ap.routes.admin.import-batch :as ib-routes]
|
||||||
|
[auto-ap.routes.admin.transaction-rules :as transaction-rules]
|
||||||
[auto-ap.routes.admin.vendors :as v-routes]
|
[auto-ap.routes.admin.vendors :as v-routes]
|
||||||
[auto-ap.graphql.clients :as clients]))
|
[auto-ap.routes.payments :as payment-routes]
|
||||||
|
[auto-ap.ssr-routes :as ssr-routes]
|
||||||
|
[auto-ap.ssr.hiccup-helper :as hh]
|
||||||
|
[auto-ap.ssr.hx :as hx]
|
||||||
|
[auto-ap.ssr.svg :as svg]
|
||||||
|
[bidi.bidi :as bidi]
|
||||||
|
[hiccup.util :as hu]))
|
||||||
|
|
||||||
(defn menu-button- [params & children]
|
(defn menu-button- [params & children]
|
||||||
[:div
|
[:div
|
||||||
@@ -118,14 +118,21 @@
|
|||||||
:icon svg/payments}
|
:icon svg/payments}
|
||||||
"Payments")
|
"Payments")
|
||||||
(sub-menu- (hx/alpine-appear {:x-show "open"})
|
(sub-menu- (hx/alpine-appear {:x-show "open"})
|
||||||
(menu-button- {:href (bidi/path-for client-routes/routes
|
(menu-button- {:href (hu/url (bidi/path-for ssr-routes/only-routes
|
||||||
:payments)} "All")
|
::payment-routes/page)
|
||||||
(menu-button- {:href (bidi/path-for client-routes/routes
|
{ :date-range "month"})} "All")
|
||||||
:payments)} "Pending")
|
(menu-button- {:href (hu/url (bidi/path-for ssr-routes/only-routes
|
||||||
(menu-button- {:href (bidi/path-for client-routes/routes
|
::payment-routes/page)
|
||||||
:payments)} "Cleared")
|
{:status "pending"
|
||||||
(menu-button- {:href (bidi/path-for client-routes/routes
|
:date-range "month"})} "Pending")
|
||||||
:payments)} "Voided"))]
|
(menu-button- {:href (hu/url (bidi/path-for ssr-routes/only-routes
|
||||||
|
::payment-routes/page)
|
||||||
|
{:status "cleared"
|
||||||
|
:date-range "month"})} "Cleared")
|
||||||
|
(menu-button- {:href (hu/url (bidi/path-for ssr-routes/only-routes
|
||||||
|
::payment-routes/page)
|
||||||
|
{:status "voided"
|
||||||
|
:date-range "month"})} "Voided"))]
|
||||||
|
|
||||||
[:li {:x-data (hx/json {:open false})}
|
[:li {:x-data (hx/json {:open false})}
|
||||||
(menu-button- {"@click" "open = !open"
|
(menu-button- {"@click" "open = !open"
|
||||||
|
|||||||
31
src/clj/auto_ap/ssr/components/bank_account_icon.clj
Normal file
31
src/clj/auto_ap/ssr/components/bank_account_icon.clj
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
(ns auto-ap.ssr.components.bank-account-icon
|
||||||
|
(:require [auto-ap.ssr.hiccup-helper :as hh]
|
||||||
|
[auto-ap.ssr.svg :as svg]))
|
||||||
|
|
||||||
|
(defmulti icon :bank-account/type)
|
||||||
|
(defmethod icon :bank-account-type/cash [_]
|
||||||
|
[:div.grow-0.flex.flex-col.justify-center
|
||||||
|
[:div.p-1.m-2.rounded-full
|
||||||
|
{:class
|
||||||
|
"bg-blue-50"}
|
||||||
|
[:div {:class
|
||||||
|
(hh/add-class "p-1.5 w-8 h-8" "text-green-600")}
|
||||||
|
svg/dollar]]])
|
||||||
|
|
||||||
|
(defmethod icon :bank-account-type/credit [_]
|
||||||
|
[:div.grow-0.flex.flex-col.justify-center
|
||||||
|
[:div.p-1.m-2.rounded-full
|
||||||
|
{:class
|
||||||
|
"bg-purple-50"}
|
||||||
|
[:div {:class
|
||||||
|
(hh/add-class "p-1.5 w-8 h-8" "text-purple-600")}
|
||||||
|
svg/credit-card]]])
|
||||||
|
|
||||||
|
(defmethod icon :bank-account-type/check [_]
|
||||||
|
[:div.grow-0.flex.flex-col.justify-center
|
||||||
|
[:div.p-1.m-2.rounded-full
|
||||||
|
{:class
|
||||||
|
"bg-blue-50"}
|
||||||
|
[:div {:class
|
||||||
|
(hh/add-class "p-1.5 w-8 h-8" "text-blue-600")}
|
||||||
|
svg/check]]])
|
||||||
@@ -11,6 +11,9 @@
|
|||||||
(= :secondary color)
|
(= :secondary color)
|
||||||
"blue"
|
"blue"
|
||||||
|
|
||||||
|
(= :red color)
|
||||||
|
"red"
|
||||||
|
|
||||||
(nil? color)
|
(nil? color)
|
||||||
"white"
|
"white"
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@
|
|||||||
(defn checkbox-header- [params & rest]
|
(defn checkbox-header- [params & rest]
|
||||||
[:th {:scope "col", :class "p-4"}
|
[:th {:scope "col", :class "p-4"}
|
||||||
[:div {:class "flex items-center"}
|
[:div {:class "flex items-center"}
|
||||||
[:input {:id "checkbox-all", :type "checkbox", :class inputs/default-checkbox-classes :name (:name params) :value (:value params)}]
|
[:input (merge {:id "checkbox-all", :type "checkbox", :class inputs/default-checkbox-classes :name (:name params) :value (:value params)} params)]
|
||||||
[:label {:for "checkbox-all", :class "sr-only"} "checkbox"]]])
|
[:label {:for "checkbox-all", :class "sr-only"} "checkbox"]]])
|
||||||
|
|
||||||
(defn data-grid- [{:keys [headers thead-params id]} & rest]
|
(defn data-grid- [{:keys [headers thead-params id]} & rest]
|
||||||
|
|||||||
@@ -201,8 +201,8 @@
|
|||||||
rest
|
rest
|
||||||
(errors- {:errors (:errors params)})))
|
(errors- {:errors (:errors params)})))
|
||||||
|
|
||||||
(defn hidden- [{:keys [name value]}]
|
(defn hidden- [{:keys [name value] :as params}]
|
||||||
[:input {:type "hidden" :value value :name name}])
|
[:input (merge {:type "hidden" :value value :name name} params)])
|
||||||
|
|
||||||
(defn checkbox- [params & rest]
|
(defn checkbox- [params & rest]
|
||||||
(if (seq rest)
|
(if (seq rest)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
[auto-ap.routes.utils
|
[auto-ap.routes.utils
|
||||||
:refer [wrap-admin wrap-client-redirect-unauthenticated wrap-secure]]
|
:refer [wrap-admin wrap-client-redirect-unauthenticated wrap-secure]]
|
||||||
[auto-ap.ssr.account :as account]
|
[auto-ap.ssr.account :as account]
|
||||||
|
[auto-ap.ssr.payments :as payments]
|
||||||
[auto-ap.ssr.admin :as admin]
|
[auto-ap.ssr.admin :as admin]
|
||||||
[auto-ap.ssr.admin.accounts :as admin-accounts]
|
[auto-ap.ssr.admin.accounts :as admin-accounts]
|
||||||
[auto-ap.ssr.admin.background-jobs :as admin-jobs]
|
[auto-ap.ssr.admin.background-jobs :as admin-jobs]
|
||||||
@@ -94,6 +95,6 @@
|
|||||||
(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)
|
||||||
(into indicators/key->handler)))
|
(into indicators/key->handler)
|
||||||
|
(into payments/key->handler)))
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +1,32 @@
|
|||||||
(ns auto-ap.ssr.grid-page-helper
|
(ns auto-ap.ssr.grid-page-helper
|
||||||
(:require
|
(:require [auto-ap.graphql.utils :refer [extract-client-ids]]
|
||||||
[auto-ap.graphql.utils :refer [extract-client-ids]]
|
[auto-ap.logging :as alog]
|
||||||
[auto-ap.query-params :as query-params]
|
[auto-ap.query-params :as query-params]
|
||||||
[auto-ap.routes.utils
|
[auto-ap.routes.utils
|
||||||
:refer [wrap-client-redirect-unauthenticated wrap-secure]]
|
:refer [wrap-client-redirect-unauthenticated wrap-secure]]
|
||||||
[auto-ap.ssr-routes :as ssr-routes]
|
[auto-ap.ssr-routes :as ssr-routes]
|
||||||
[auto-ap.ssr.components :as com]
|
[auto-ap.ssr.components :as com]
|
||||||
[auto-ap.ssr.svg :as svg]
|
[auto-ap.ssr.hiccup-helper :as hh]
|
||||||
[auto-ap.ssr.ui :refer [base-page]]
|
[auto-ap.ssr.hx :as hx]
|
||||||
[auto-ap.ssr.utils :refer [html-response]]
|
[auto-ap.ssr.svg :as svg]
|
||||||
[auto-ap.time :as atime]
|
[auto-ap.ssr.ui :refer [base-page]]
|
||||||
[malli.core :as m]
|
[auto-ap.ssr.utils :refer [html-response main-transformer]]
|
||||||
[bidi.bidi :as bidi]
|
[auto-ap.time :as atime]
|
||||||
[cemerick.url :as url]
|
[bidi.bidi :as bidi]
|
||||||
[clojure.string :as str]
|
[cemerick.url :as url]
|
||||||
[hiccup2.core :as hiccup]
|
[clojure.string :as str]
|
||||||
[malli.transform :as mt2]
|
[hiccup2.core :as hiccup]
|
||||||
[auto-ap.ssr.hiccup-helper :as hh]))
|
[malli.core :as m]
|
||||||
|
[malli.transform :as mt2]
|
||||||
|
[malli.transform :as mt]
|
||||||
|
[taoensso.encore :refer [filter-vals]]))
|
||||||
|
|
||||||
(defn row* [gridspec user entity {:keys [flash? delete-after-settle? request class] :as options}]
|
(defn row* [gridspec user entity {:keys [flash? delete-after-settle? request class] :as options}]
|
||||||
(let [cells (->> gridspec
|
(let [cells (if (:check-boxes? gridspec)
|
||||||
|
[(com/data-grid-cell {} (com/checkbox {:name "id" :value ((:id-fn gridspec) entity)
|
||||||
|
:x-model "selected"}))]
|
||||||
|
[])
|
||||||
|
cells (->> gridspec
|
||||||
:headers
|
:headers
|
||||||
(filter (fn [h]
|
(filter (fn [h]
|
||||||
(if (and (:hide? h)
|
(if (and (:hide? h)
|
||||||
@@ -30,14 +37,15 @@
|
|||||||
(com/data-grid-cell {:class (if-let [show-starting (:show-starting header)]
|
(com/data-grid-cell {:class (if-let [show-starting (:show-starting header)]
|
||||||
(format "hidden %s:table-cell" show-starting)
|
(format "hidden %s:table-cell" show-starting)
|
||||||
(:class header))}
|
(:class header))}
|
||||||
((:render header) entity)))))
|
((:render header) entity))))
|
||||||
|
(into cells))
|
||||||
cells (conj cells (com/data-grid-right-stack-cell {}
|
cells (conj cells (com/data-grid-right-stack-cell {}
|
||||||
(into [:form.flex.space-x-2
|
(into [:form.flex.space-x-2
|
||||||
[:input {:type :hidden :name "id" :value ((:id-fn gridspec) entity)}]]
|
[:input {:type :hidden :name "id" :value ((:id-fn gridspec) entity)}]]
|
||||||
((:row-buttons gridspec) request entity))))] ;; TODO double check usage of row buttons user and identity in callers
|
((:row-buttons gridspec) request entity))))] ;; TODO double check usage of row buttons user and identity in callers
|
||||||
(apply com/data-grid-row
|
(apply com/data-grid-row
|
||||||
{:class (cond-> (or class "")
|
{:class (cond-> (or class "")
|
||||||
flash? (hh/add-class "live-added"))
|
flash? (hh/add-class "live-added"))
|
||||||
"_" (hiccup/raw (when delete-after-settle?
|
"_" (hiccup/raw (when delete-after-settle?
|
||||||
" on htmx:afterSettle wait 400ms then remove me"))
|
" on htmx:afterSettle wait 400ms then remove me"))
|
||||||
|
|
||||||
@@ -52,24 +60,18 @@
|
|||||||
|
|
||||||
(defn sort-by-list [grid-spec sort]
|
(defn sort-by-list [grid-spec sort]
|
||||||
(if (seq sort)
|
(if (seq sort)
|
||||||
(into
|
(into
|
||||||
[:div.flex.gap-2.items-center
|
[:div.flex.gap-2.items-center
|
||||||
|
|
||||||
"sorted by"
|
|
||||||
|
|
||||||
]
|
"sorted by"]
|
||||||
(for [{:keys [name sort-icon sort-key ]} sort]
|
(for [{:keys [name sort-icon sort-key]} sort]
|
||||||
[:div.py-1.px-3.text-sm.rounded.bg-gray-100.dark:bg-gray-600.flex.items-center.gap-2.relative name [:div.h-4.w-4.mr-3 sort-icon]
|
[:div.py-1.px-3.text-sm.rounded.bg-gray-100.dark:bg-gray-600.flex.items-center.gap-2.relative name [:div.h-4.w-4.mr-3 sort-icon]
|
||||||
[:div {:class "absolute inline-flex items-center justify-center w-6 h-6 text-xs font-bold text-white hover:scale-110 transition-all duration-300 bg-gray-400 border-2 border-white rounded-full -top-2 -right-2 dark:border-gray-900"}
|
[:div {:class "absolute inline-flex items-center justify-center w-6 h-6 text-xs font-bold text-white hover:scale-110 transition-all duration-300 bg-gray-400 border-2 border-white rounded-full -top-2 -right-2 dark:border-gray-900"}
|
||||||
[:a {:href (str (bidi/path-for ssr-routes/only-routes
|
[:a {:href (str (bidi/path-for ssr-routes/only-routes
|
||||||
(:route grid-spec)) "?remove-sort=" sort-key)
|
(:route grid-spec)) "?remove-sort=" sort-key)
|
||||||
:hx-boost "true"
|
:hx-boost "true"
|
||||||
:hx-target (str "#" (:id grid-spec))
|
:hx-target (str "#" (:id grid-spec))}
|
||||||
|
[:div.h-4.w-4 svg/x]]]]))
|
||||||
}
|
|
||||||
[:div.h-4.w-4 svg/x]]
|
|
||||||
]]
|
|
||||||
))
|
|
||||||
"default sort"))
|
"default sort"))
|
||||||
|
|
||||||
(defn table* [grid-spec user {{:keys [start per-page flash-id sort]} :parsed-query-params :as request}]
|
(defn table* [grid-spec user {{:keys [start per-page flash-id sort]} :parsed-query-params :as request}]
|
||||||
@@ -79,7 +81,9 @@
|
|||||||
request)]
|
request)]
|
||||||
|
|
||||||
(com/data-grid-card {:id (:id grid-spec)
|
(com/data-grid-card {:id (:id grid-spec)
|
||||||
:title (:title grid-spec)
|
:title (if (string? (:title grid-spec))
|
||||||
|
(:title grid-spec)
|
||||||
|
((:title grid-spec) request))
|
||||||
:route (:route grid-spec)
|
:route (:route grid-spec)
|
||||||
:start start
|
:start start
|
||||||
:per-page per-page
|
:per-page per-page
|
||||||
@@ -87,7 +91,18 @@
|
|||||||
:subtitle [:div.flex.items-center.gap-2
|
:subtitle [:div.flex.items-center.gap-2
|
||||||
[:span (format "Total %s: %d, " (:entity-name grid-spec) total)]
|
[:span (format "Total %s: %d, " (:entity-name grid-spec) total)]
|
||||||
(sort-by-list grid-spec sort)]
|
(sort-by-list grid-spec sort)]
|
||||||
:action-buttons ((:action-buttons grid-spec) request)
|
:action-buttons (cond->> ((:action-buttons grid-spec) request)
|
||||||
|
(:check-boxes? grid-spec) (into [(com/pill {:color :primary
|
||||||
|
:x-show "selected.length > 0"}
|
||||||
|
[:div.flex.space-x-2.items-center
|
||||||
|
[:div
|
||||||
|
|
||||||
|
[:span {:x-text "selected.length" :x-show "!all_selected"}]
|
||||||
|
[:span {:x-show "all_selected"} "All"]
|
||||||
|
" selected"]
|
||||||
|
[:div.w-3.h-3
|
||||||
|
(com/link {"@click" "selected=[]; all_selected=false"}
|
||||||
|
svg/x)]])]))
|
||||||
:rows (for [entity entities]
|
:rows (for [entity entities]
|
||||||
(row* grid-spec user entity {:flash? (= flash-id ((:id-fn grid-spec) entity)) :request request}))
|
(row* grid-spec user entity {:flash? (= flash-id ((:id-fn grid-spec) entity)) :request request}))
|
||||||
:thead-params {:hx-get (bidi/path-for ssr-routes/only-routes
|
:thead-params {:hx-get (bidi/path-for ssr-routes/only-routes
|
||||||
@@ -97,37 +112,37 @@
|
|||||||
:hx-trigger "sorted once"
|
:hx-trigger "sorted once"
|
||||||
:hx-vals "js:{\"toggle-sort\": event.detail.key || \"\"}"}
|
:hx-vals "js:{\"toggle-sort\": event.detail.key || \"\"}"}
|
||||||
:headers
|
:headers
|
||||||
(conj
|
(conj
|
||||||
(->> grid-spec
|
(->> grid-spec
|
||||||
:headers
|
:headers
|
||||||
(map
|
(map
|
||||||
(fn [h]
|
(fn [h]
|
||||||
(cond
|
(cond
|
||||||
(and (:hide? h)
|
(and (:hide? h)
|
||||||
((:hide? h) request))
|
((:hide? h) request))
|
||||||
nil
|
nil
|
||||||
|
|
||||||
(:sort-key h)
|
(:sort-key h)
|
||||||
(com/data-grid-sort-header {:class (if-let [show-starting (:show-starting h)]
|
(com/data-grid-sort-header {:class (if-let [show-starting (:show-starting h)]
|
||||||
(format "hidden %s:table-cell" show-starting)
|
(format "hidden %s:table-cell" show-starting)
|
||||||
(:class h))
|
(:class h))
|
||||||
:sort-key (:sort-key h)}
|
:sort-key (:sort-key h)}
|
||||||
|
|
||||||
[:div.flex.gap-4.items-center
|
[:div.flex.gap-4.items-center
|
||||||
(:name h)
|
(:name h)
|
||||||
[:div.h-6.w-6.text-gray-400.dark:text-gray-500 (sort-icon sort (:sort-key h))]])
|
[:div.h-6.w-6.text-gray-400.dark:text-gray-500 (sort-icon sort (:sort-key h))]])
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(com/data-grid-header {:class (if-let [show-starting (:show-starting h)]
|
(com/data-grid-header {:class (if-let [show-starting (:show-starting h)]
|
||||||
(format "hidden %s:table-cell" show-starting)
|
(format "hidden %s:table-cell" show-starting)
|
||||||
(:class h))
|
(:class h))
|
||||||
:sort-key (:sort-key h)}
|
:sort-key (:sort-key h)}
|
||||||
(:name h))
|
(:name h)))))
|
||||||
|
(filter identity)
|
||||||
)))
|
(into (if (:check-boxes? grid-spec)
|
||||||
(filter identity)
|
[(com/data-grid-checkbox-header {:name "all" :value "all" :x-model "all_selected"})]
|
||||||
(into []))
|
[])))
|
||||||
(com/data-grid-header {}))})))
|
(com/data-grid-header {}))})))
|
||||||
|
|
||||||
|
|
||||||
(defn sort->query [s]
|
(defn sort->query [s]
|
||||||
@@ -137,47 +152,47 @@
|
|||||||
s)))
|
s)))
|
||||||
|
|
||||||
(defn default-unparse-query-params [query-params]
|
(defn default-unparse-query-params [query-params]
|
||||||
(reduce
|
(reduce
|
||||||
(fn [query-params [k value]]
|
(fn [query-params [k value]]
|
||||||
(assoc query-params k
|
(assoc query-params k
|
||||||
(cond (= k :sort)
|
(cond (= k :sort)
|
||||||
(sort->query value)
|
(sort->query value)
|
||||||
|
|
||||||
(instance? org.joda.time.base.AbstractInstant value)
|
(instance? org.joda.time.base.AbstractInstant value)
|
||||||
(atime/unparse-local value atime/normal-date)
|
(atime/unparse-local value atime/normal-date)
|
||||||
|
|
||||||
(instance? Long value)
|
(instance? Long value)
|
||||||
(str value)
|
(str value)
|
||||||
|
|
||||||
(instance? Double value)
|
(instance? Double value)
|
||||||
(format "%.2f" value)
|
(format "%.2f" value)
|
||||||
|
|
||||||
(instance? Float value)
|
(instance? Float value)
|
||||||
(format "%.2f" value)
|
(format "%.2f" value)
|
||||||
|
|
||||||
(keyword? value)
|
(keyword? value)
|
||||||
(name value)
|
(name value)
|
||||||
|
|
||||||
(and (map? value)
|
(and (map? value)
|
||||||
(:db/id value))
|
(:db/id value))
|
||||||
(:db/id value)
|
(:db/id value)
|
||||||
|
|
||||||
:else
|
:else
|
||||||
value)))
|
value)))
|
||||||
query-params
|
query-params
|
||||||
query-params))
|
query-params))
|
||||||
|
|
||||||
(defn default-parse-query-params [grid-spec]
|
(defn default-parse-query-params [grid-spec]
|
||||||
(comp
|
(comp
|
||||||
(query-params/apply-remove-sort)
|
(query-params/apply-remove-sort)
|
||||||
(query-params/apply-toggle-sort grid-spec)
|
(query-params/apply-toggle-sort grid-spec)
|
||||||
(query-params/apply-date-range :date-range :start-date :end-date)
|
(query-params/apply-date-range :date-range :start-date :end-date)
|
||||||
(query-params/parse-key :exact-match-id query-params/parse-long)
|
(query-params/parse-key :exact-match-id query-params/parse-long)
|
||||||
(query-params/parse-key :sort #(query-params/parse-sort grid-spec %))
|
(query-params/parse-key :sort #(query-params/parse-sort grid-spec %))
|
||||||
(query-params/parse-key :per-page query-params/parse-long)
|
(query-params/parse-key :per-page query-params/parse-long)
|
||||||
(query-params/parse-key :start query-params/parse-long)
|
(query-params/parse-key :start query-params/parse-long)
|
||||||
(query-params/parse-key :start-date query-params/parse-date)
|
(query-params/parse-key :start-date query-params/parse-date)
|
||||||
(query-params/parse-key :end-date query-params/parse-date)))
|
(query-params/parse-key :end-date query-params/parse-date)))
|
||||||
|
|
||||||
(defn wrap-trim-client-ids [handler]
|
(defn wrap-trim-client-ids [handler]
|
||||||
(fn trim-client-ids [request]
|
(fn trim-client-ids [request]
|
||||||
@@ -196,10 +211,17 @@
|
|||||||
(let [unparse-query-params (or (:unparse-query grid-spec)
|
(let [unparse-query-params (or (:unparse-query grid-spec)
|
||||||
default-unparse-query-params)]
|
default-unparse-query-params)]
|
||||||
(html-response (table*
|
(html-response (table*
|
||||||
grid-spec
|
grid-spec
|
||||||
identity
|
identity
|
||||||
request)
|
request)
|
||||||
:headers {"hx-push-url" (str "?" (url/map->query (unparse-query-params (:parsed-query-params request))))}
|
:headers {"hx-push-url" (str "?" (url/map->query
|
||||||
|
(if (:query-schema grid-spec)
|
||||||
|
(update (filter-vals #(not (nil? %))
|
||||||
|
(m/encode (:query-schema grid-spec)
|
||||||
|
(:query-params request)
|
||||||
|
main-transformer))
|
||||||
|
"sort" sort->query)
|
||||||
|
(unparse-query-params (:parsed-query-params request)))))}
|
||||||
:oob (when-let [oob-render (:oob-render grid-spec)]
|
:oob (when-let [oob-render (:oob-render grid-spec)]
|
||||||
(oob-render request)))))
|
(oob-render request)))))
|
||||||
(wrap-trim-client-ids)
|
(wrap-trim-client-ids)
|
||||||
@@ -208,22 +230,23 @@
|
|||||||
(wrap-secure)
|
(wrap-secure)
|
||||||
(wrap-client-redirect-unauthenticated)))
|
(wrap-client-redirect-unauthenticated)))
|
||||||
|
|
||||||
(defn page-route [grid-spec ]
|
(defn page-route [grid-spec]
|
||||||
(-> (fn page [{:keys [identity] :as request}]
|
(-> (fn page [{:keys [identity] :as request}]
|
||||||
(base-page
|
(base-page
|
||||||
request
|
request
|
||||||
(com/page {:nav (:nav grid-spec)
|
(com/page {:nav (:nav grid-spec)
|
||||||
:page-specific (when-let [page-specific-nav (:page-specific-nav grid-spec)]
|
:page-specific (when-let [page-specific-nav (:page-specific-nav grid-spec)]
|
||||||
[:div#page-specific-nav (page-specific-nav request)])
|
[:div#page-specific-nav (page-specific-nav request)])
|
||||||
:client-selection (:client-selection (:session request))
|
:client-selection (:client-selection (:session request))
|
||||||
:clients (:clients request)
|
:clients (:clients request)
|
||||||
:client (:client request)
|
:client (:client request)
|
||||||
:identity (:identity request)}
|
:identity (:identity request)}
|
||||||
(apply com/breadcrumbs {} (:breadcrumbs grid-spec))
|
(apply com/breadcrumbs {} (:breadcrumbs grid-spec))
|
||||||
|
[:div {:x-data (hx/json {:selected [] :all_selected false})}
|
||||||
(table* grid-spec
|
(table* grid-spec
|
||||||
identity
|
identity
|
||||||
request))
|
request)])
|
||||||
(:title grid-spec)))
|
(:title grid-spec)))
|
||||||
(wrap-trim-client-ids)
|
(wrap-trim-client-ids)
|
||||||
(query-params/wrap-parse-query-params (or (:parse-query-params grid-spec)
|
(query-params/wrap-parse-query-params (or (:parse-query-params grid-spec)
|
||||||
(default-parse-query-params grid-spec)))
|
(default-parse-query-params grid-spec)))
|
||||||
@@ -235,6 +258,7 @@
|
|||||||
(def header-spec (m/schema [:map
|
(def header-spec (m/schema [:map
|
||||||
[:key :string]
|
[:key :string]
|
||||||
[:name :string]
|
[:name :string]
|
||||||
|
[:header-class {:optional true} [:maybe :string]]
|
||||||
[:sort-key {:optional true} :string]
|
[:sort-key {:optional true} :string]
|
||||||
[:render [:=> [:cat entity-spec] :any]]
|
[:render [:=> [:cat entity-spec] :any]]
|
||||||
[:hide? {:optional true} [:=> [:cat entity-spec] :boolean]]]))
|
[:hide? {:optional true} [:=> [:cat entity-spec] :boolean]]]))
|
||||||
@@ -264,10 +288,12 @@
|
|||||||
{:optional true
|
{:optional true
|
||||||
:default (fn [request])}
|
:default (fn [request])}
|
||||||
[:=>
|
[:=>
|
||||||
[:cat request-spec]
|
[:cat request-spec]
|
||||||
vector?]]
|
vector?]]
|
||||||
[:breadcrumbs [:vector vector?]]
|
[:breadcrumbs [:vector vector?]]
|
||||||
[:title :string]
|
[:title [:or :string
|
||||||
|
[:=> [:cat [:map-of :keyword :any]]
|
||||||
|
:string]]]
|
||||||
[:entity-name :string]
|
[:entity-name :string]
|
||||||
[:route :keyword]
|
[:route :keyword]
|
||||||
[:action-buttons
|
[:action-buttons
|
||||||
@@ -290,4 +316,13 @@
|
|||||||
(m/explain grid-spec grid-page))))
|
(m/explain grid-spec grid-page))))
|
||||||
(m/decode grid-spec grid-page (mt2/default-value-transformer {::mt2/add-optional-keys true})))
|
(m/decode grid-spec grid-page (mt2/default-value-transformer {::mt2/add-optional-keys true})))
|
||||||
|
|
||||||
|
(defn wrap-apply-sort [handler grid-spec]
|
||||||
|
(fn apply-sort [request]
|
||||||
|
(handler (update request :query-params
|
||||||
|
(fn [qp]
|
||||||
|
((comp
|
||||||
|
(query-params/apply-remove-sort)
|
||||||
|
(query-params/apply-toggle-sort grid-spec)
|
||||||
|
(query-params/parse-key :sort #(query-params/parse-sort grid-spec %)))
|
||||||
|
qp))))))
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
(defn alpine-appear [m]
|
(defn alpine-appear [m]
|
||||||
(assoc m
|
(assoc m
|
||||||
"x-transition:enter" "transition duration-500"
|
"x-transition:enter" "transition-opacity duration-500"
|
||||||
"x-transition:enter-start" "opacity-0"
|
"x-transition:enter-start" "opacity-0"
|
||||||
"x-transition:enter-end" "opacity-100"))
|
"x-transition:enter-end" "opacity-100"))
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
(ns auto-ap.ssr.indicators
|
(ns auto-ap.ssr.indicators
|
||||||
(:require [auto-ap.routes.indicators :as route]
|
(:require [auto-ap.routes.indicators :as route]
|
||||||
[auto-ap.ssr.components :as com]
|
[auto-ap.ssr.components :as com]
|
||||||
[auto-ap.ssr.utils :refer [html-response wrap-schema-enforce]]
|
[auto-ap.ssr.utils :refer [clj-date-schema html-response
|
||||||
[auto-ap.time :as atime]
|
wrap-schema-enforce]]
|
||||||
[clj-time.coerce :as c]
|
[clj-time.coerce :as c]
|
||||||
[clj-time.core :as t]))
|
[clj-time.core :as t]))
|
||||||
|
|
||||||
@@ -32,9 +32,5 @@
|
|||||||
(def key->handler
|
(def key->handler
|
||||||
{::route/days-ago (wrap-schema-enforce days-ago
|
{::route/days-ago (wrap-schema-enforce days-ago
|
||||||
:query-schema
|
:query-schema
|
||||||
[:map [:date {:optional false
|
[:map [:date {:optional false}
|
||||||
:decode/arbitrary (fn [m]
|
clj-date-schema ]])})
|
||||||
(if (string? m)
|
|
||||||
(c/to-date (atime/parse m atime/normal-date))
|
|
||||||
m))}
|
|
||||||
inst?]])})
|
|
||||||
564
src/clj/auto_ap/ssr/payments.clj
Normal file
564
src/clj/auto_ap/ssr/payments.clj
Normal file
@@ -0,0 +1,564 @@
|
|||||||
|
(ns auto-ap.ssr.payments
|
||||||
|
(:require [auto-ap.client-routes :as client-routes]
|
||||||
|
[auto-ap.datomic
|
||||||
|
:refer [add-sorter-fields apply-pagination apply-sort-3
|
||||||
|
audit-transact conn merge-query observable-query
|
||||||
|
pull-many]]
|
||||||
|
[auto-ap.graphql.utils :refer [assert-can-see-client
|
||||||
|
exception->notification
|
||||||
|
extract-client-ids notify-if-locked]]
|
||||||
|
[auto-ap.logging :as alog]
|
||||||
|
[auto-ap.permissions :refer [can?]]
|
||||||
|
[auto-ap.routes.payments :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.components.bank-account-icon :as bank-account-icon]
|
||||||
|
[auto-ap.ssr.grid-page-helper :as helper :refer [sort->query
|
||||||
|
wrap-apply-sort]]
|
||||||
|
[auto-ap.ssr.hx :as hx]
|
||||||
|
[auto-ap.ssr.pos.common :refer [date-range-field*]]
|
||||||
|
[auto-ap.ssr.svg :as svg]
|
||||||
|
[auto-ap.ssr.utils
|
||||||
|
:refer [apply-middleware-to-all-handlers clj-date-schema
|
||||||
|
dissoc-nil-transformer entity-id html-response
|
||||||
|
main-transformer modal-response ref->enum-schema strip
|
||||||
|
wrap-entity wrap-merge-prior-hx wrap-schema-enforce]]
|
||||||
|
[auto-ap.time :as atime]
|
||||||
|
[bidi.bidi :as bidi]
|
||||||
|
[clj-time.coerce :as coerce]
|
||||||
|
[clojure.string :as str]
|
||||||
|
[datomic.api :as dc]
|
||||||
|
[hiccup.util :as hu]
|
||||||
|
[iol-ion.query :refer [dollars-0?]]
|
||||||
|
[malli.core :as mc]
|
||||||
|
[malli.transform :as mt]))
|
||||||
|
|
||||||
|
(defn exact-match-id* [request]
|
||||||
|
(if (nat-int? (:exact-match-id (:parsed-query-params request)))
|
||||||
|
[:div {:x-data (hx/json {:exact_match (:exact-match-id (:parsed-query-params request))}) :id "exact-match-id-tag"}
|
||||||
|
(com/hidden {:name "exact-match-id"
|
||||||
|
"x-model" "exact_match"})
|
||||||
|
(com/pill {:color :primary}
|
||||||
|
[:span.inline-flex.space-x-2.items-center
|
||||||
|
[:div "exact match"]
|
||||||
|
[:div.w-3.h-3
|
||||||
|
(com/link {"@click" "exact_match=null; $nextTick(() => $dispatch('change'))"}
|
||||||
|
svg/x)]])]
|
||||||
|
[:div {:id "exact-match-id-tag"}]))
|
||||||
|
|
||||||
|
(defn filters [request]
|
||||||
|
[:form#payment-filters {"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"}
|
||||||
|
|
||||||
|
(com/hidden {:name "status"
|
||||||
|
:value (some-> (:status (:parsed-query-params request)) name)})
|
||||||
|
[:fieldset.space-y-6
|
||||||
|
(com/field {:label "Vendor"}
|
||||||
|
(com/typeahead {:name "vendor"
|
||||||
|
:id "vendor"
|
||||||
|
:url (bidi/path-for ssr-routes/only-routes :vendor-search)
|
||||||
|
:value (:vendor (:parsed-query-params request))
|
||||||
|
:value-fn :db/id
|
||||||
|
:content-fn :vendor/name}))
|
||||||
|
(date-range-field* request)
|
||||||
|
(com/field {:label "Check #"}
|
||||||
|
(com/text-input {:name "check-number"
|
||||||
|
:id "check-number"
|
||||||
|
:class "hot-filter"
|
||||||
|
:value (:check-number (:parsed-query-params request))
|
||||||
|
:placeholder "10001"
|
||||||
|
:size :small}))
|
||||||
|
(com/field {:label "Invoice #"}
|
||||||
|
(com/text-input {:name "invoice-number"
|
||||||
|
:id "invoice-number"
|
||||||
|
:class "hot-filter"
|
||||||
|
:value (:invoice-number (:parsed-query-params request))
|
||||||
|
:placeholder "10001"
|
||||||
|
:size :small}))
|
||||||
|
|
||||||
|
(com/field {:label "Amount"}
|
||||||
|
[:div.flex.space-x-4.items-baseline
|
||||||
|
(com/money-input {:name "amount-gte"
|
||||||
|
:id "amount-gte"
|
||||||
|
:hx-preserve "true"
|
||||||
|
:class "hot-filter w-20"
|
||||||
|
:value (:amount-gte (:parsed-query-params request))
|
||||||
|
:placeholder "0.01"
|
||||||
|
:size :small})
|
||||||
|
[:div.align-baseline
|
||||||
|
"to"]
|
||||||
|
(com/money-input {:name "amount-lte"
|
||||||
|
:hx-preserve "true"
|
||||||
|
:id "amount-lte"
|
||||||
|
:class "hot-filter w-20"
|
||||||
|
:value (:amount-lte (:parsed-query-params request))
|
||||||
|
:placeholder "9999.34"
|
||||||
|
:size :small})])
|
||||||
|
(com/field {:label "Payment Type"}
|
||||||
|
(com/radio {:size :small
|
||||||
|
:name "payment-type"
|
||||||
|
:value (:payment-type (:parsed-query-params request))
|
||||||
|
:options [{:value ""
|
||||||
|
:content "All"}
|
||||||
|
{:value "cash"
|
||||||
|
:content "Cash"}
|
||||||
|
{:value "check"
|
||||||
|
:content "Check"}
|
||||||
|
{:value "debit"
|
||||||
|
:content "Debit"}]}))
|
||||||
|
(exact-match-id* request)]])
|
||||||
|
|
||||||
|
|
||||||
|
(def default-read '[*
|
||||||
|
|
||||||
|
[:payment/date :xform clj-time.coerce/from-date]
|
||||||
|
{:invoice-payment/_payment [* {:invoice-payment/invoice [*]}]}
|
||||||
|
{:payment/client [:client/name :db/id :client/code]}
|
||||||
|
{:payment/bank-account [* {[:bank-account/type :xform iol-ion.query/ident] [:db/ident]}]}
|
||||||
|
{:payment/invoices [:db/id :invoice/invoice-number]}
|
||||||
|
{:payment/vendor [:vendor/name {:vendor/default-account
|
||||||
|
[:account/name :account/numeric-code :db/id]} :db/id {:vendor/primary-contact [*]} {:vendor/address [*]}]}
|
||||||
|
{[:payment/status :xform iol-ion.query/ident] [:db/ident]}
|
||||||
|
{:payment/type [:db/ident]}
|
||||||
|
{:transaction/_payment [:db/id :transaction/date]}])
|
||||||
|
|
||||||
|
(defn fetch-ids [db {:keys [query-params] :as request}]
|
||||||
|
(let [_ (alog/peek :qp (pr-str query-params))
|
||||||
|
valid-clients (extract-client-ids (:clients request)
|
||||||
|
(:client request)
|
||||||
|
(:client-id query-params)
|
||||||
|
(when (:client-code query-params)
|
||||||
|
[:client/code (:client-code query-params)]))
|
||||||
|
|
||||||
|
check-number-like (try (Long/parseLong (:check-number query-params)) (catch Exception _ nil))
|
||||||
|
query (if (:exact-match-id query-params)
|
||||||
|
{:query {:find '[?e]
|
||||||
|
:in '[$ ?e [?c ...]]
|
||||||
|
:where '[[?e :payment/client ?c]]}
|
||||||
|
:args [db
|
||||||
|
(:exact-match-id query-params)
|
||||||
|
valid-clients]}
|
||||||
|
(cond-> {:query {:find []
|
||||||
|
:in '[$ [?clients ?start ?end]]
|
||||||
|
:where '[[(iol-ion.query/scan-payments $ ?clients ?start ?end) [[?e _ ?sort-default] ...]]]}
|
||||||
|
:args [db
|
||||||
|
[valid-clients
|
||||||
|
(some-> (:start-date query-params) coerce/to-date)
|
||||||
|
(some-> (:end-date query-params) coerce/to-date)]]}
|
||||||
|
(:sort query-params) (add-sorter-fields {"client" ['[?e :payment/client ?c]
|
||||||
|
'[?c :client/name ?sort-client]]
|
||||||
|
"vendor" ['[?e :payment/vendor ?v]
|
||||||
|
'[?v :vendor/name ?sort-vendor]]
|
||||||
|
"bank-account" ['[?e :payment/bank-account ?ba]
|
||||||
|
'[?ba :bank-account/name ?sort-bank-account]]
|
||||||
|
"check-number" ['[(get-else $ ?e :payment/check-number 0) ?sort-check-number]]
|
||||||
|
"date" ['[?e :payment/date ?sort-date]]
|
||||||
|
"amount" ['[?e :payment/amount ?sort-amount]]
|
||||||
|
"status" ['[?e :payment/status ?sort-status]]}
|
||||||
|
query-params)
|
||||||
|
(:exact-match-id query-params)
|
||||||
|
(merge-query {:query {:in ['?e]
|
||||||
|
:where []}
|
||||||
|
:args [(:exact-match-id query-params)]})
|
||||||
|
|
||||||
|
(:vendor query-params)
|
||||||
|
(merge-query {:query {:in ['?vendor-id]
|
||||||
|
:where ['[?e :payment/vendor ?vendor-id]]}
|
||||||
|
:args [(:db/id (:vendor query-params))]})
|
||||||
|
|
||||||
|
(:original-id query-params)
|
||||||
|
(merge-query {:query {:in ['?original-id]
|
||||||
|
:where ['[?e :payment/client ?c]
|
||||||
|
'[?c :client/original-id ?original-id]]}
|
||||||
|
:args [(:original-id query-params)]})
|
||||||
|
|
||||||
|
(:check-number-like query-params)
|
||||||
|
(merge-query {:query {:in ['?check-number]
|
||||||
|
:where ['[?e :payment/check-number ?check-number]]}
|
||||||
|
:args [(:check-number-like query-params)]})
|
||||||
|
|
||||||
|
(not-empty (:invoice-number query-params))
|
||||||
|
(merge-query {:query {:in ['?invoice-number]
|
||||||
|
:where ['[?e :payment/invoices ?i]
|
||||||
|
'[?i :invoice/invoice-number ?invoice-number]]}
|
||||||
|
:args [(:invoice-number query-params)]})
|
||||||
|
|
||||||
|
(:bank-account-id query-params)
|
||||||
|
(merge-query {:query {:in ['?bank-account-id]
|
||||||
|
:where ['[?e :payment/bank-account ?bank-account-id]]}
|
||||||
|
:args [(:bank-account-id query-params)]})
|
||||||
|
|
||||||
|
(:amount-gte query-params)
|
||||||
|
(merge-query {:query {:in ['?amount-gte]
|
||||||
|
:where ['[?e :payment/amount ?a]
|
||||||
|
'[(>= ?a ?amount-gte)]]}
|
||||||
|
:args [(:amount-gte query-params)]})
|
||||||
|
|
||||||
|
(:amount-lte query-params)
|
||||||
|
(merge-query {:query {:in ['?amount-lte]
|
||||||
|
:where ['[?e :payment/amount ?a]
|
||||||
|
'[(<= ?a ?amount-lte)]]}
|
||||||
|
:args [(:amount-lte query-params)]})
|
||||||
|
|
||||||
|
(:amount query-params)
|
||||||
|
(merge-query {:query {:in ['?amount]
|
||||||
|
:where ['[?e :payment/amount ?transaction-amount]
|
||||||
|
'[(iol-ion.query/dollars= ?transaction-amount ?amount)]]}
|
||||||
|
:args [(:amount query-params)]})
|
||||||
|
|
||||||
|
|
||||||
|
(:status query-params)
|
||||||
|
(merge-query {:query {:in ['?status]
|
||||||
|
:where ['[?e :payment/status ?status]]}
|
||||||
|
:args [(:status query-params)]})
|
||||||
|
|
||||||
|
(:payment-type query-params)
|
||||||
|
(merge-query {:query {:in '[?payment-type]
|
||||||
|
:where ['[?e :payment/type ?payment-type]]}
|
||||||
|
:args [(:payment-type query-params)]})
|
||||||
|
|
||||||
|
check-number-like
|
||||||
|
(merge-query {:query {:in '[?check-number-like]
|
||||||
|
:where ['[?e :payment/check-number ?check-number-like]]}
|
||||||
|
:args [check-number-like]})
|
||||||
|
|
||||||
|
true
|
||||||
|
(merge-query {:query {:find ['?sort-default '?e]}})))]
|
||||||
|
(cond->> (observable-query 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- render-links [links]
|
||||||
|
(if (<= (count links) 2)
|
||||||
|
[:div.flex.flex-col.space-y-1
|
||||||
|
(for [l links]
|
||||||
|
[:a {:href (:link l)}
|
||||||
|
(com/pill {:color (or (:color l) :primary) :class "truncate w-24 block shrink grow-0"}
|
||||||
|
(:content l))])]
|
||||||
|
[:div {:x-data (hx/json {:popper nil
|
||||||
|
:show false})
|
||||||
|
"@click.outside" "show=false"
|
||||||
|
|
||||||
|
:x-init "popper = Popper.createPopper($refs.link, $refs.tooltip, {placement: 'bottom', strategy: 'fixed'})"}
|
||||||
|
(com/a-icon-button {:x-ref "link" "@click.prevent" "show=!show; $nextTick(() => popper.update());" :class "relative"}
|
||||||
|
svg/three-dots
|
||||||
|
(com/badge {} (count links)))
|
||||||
|
[:div.divide-y.divide-gray-200.bg-white.rounded-lg.shadow (hx/alpine-appear {:x-ref "tooltip" :x-show "show" :data-key "show"})
|
||||||
|
[:div {:class "p-3 overflow-y-auto text-sm text-gray-700 dark:text-gray-200"}
|
||||||
|
[:div.flex.flex-col.space-y-1
|
||||||
|
(for [l links]
|
||||||
|
[:a {:href (:link l)}
|
||||||
|
(com/pill {:color (or (:color l) :primary) :class "truncate w-24 block shrink grow-0"}
|
||||||
|
(:content l))])]]]]))
|
||||||
|
|
||||||
|
(def query-schema (mc/schema
|
||||||
|
[:maybe [:map {:date-range [:date-range :start-date :end-date] }
|
||||||
|
[:sort {:optional true} [:maybe [:any]]]
|
||||||
|
[:per-page {:optional true :default 25} [:maybe :int]]
|
||||||
|
[:start {:optional true :default 0} [:maybe :int]]
|
||||||
|
[:amount-gte {:optional true} [:maybe :double]]
|
||||||
|
[:amount-lte {:optional true} [:maybe :double]]
|
||||||
|
[:payment-type {:optional true} [:maybe (ref->enum-schema "payment-type")]]
|
||||||
|
[:vendor {:optional true :default nil} [:maybe [:entity-map {:pull [:db/id :vendor/name]}]]]
|
||||||
|
[:check-number {:optional true} [:maybe [:string {:decode/string strip}]]]
|
||||||
|
[:invoice-number {:optional true} [:maybe [:string {:decode/string strip}]]]
|
||||||
|
[:status {:optional true} [:maybe (ref->enum-schema "payment-status")]]
|
||||||
|
[:exact-match-id {:optional true} [:maybe entity-id]]
|
||||||
|
[:all-selected {:optional true :default nil} [:maybe :boolean]]
|
||||||
|
[:selected {:optional true :default nil} [:maybe [:vector {:coerce? true}
|
||||||
|
entity-id]]]
|
||||||
|
[:start-date {:optional true}
|
||||||
|
[:maybe clj-date-schema]]
|
||||||
|
[:end-date {:optional true}
|
||||||
|
[:maybe clj-date-schema]]]]))
|
||||||
|
|
||||||
|
(comment
|
||||||
|
(mc/decode query-schema
|
||||||
|
{:start " "}
|
||||||
|
main-transformer))
|
||||||
|
|
||||||
|
;; TODO fix parsing of query params
|
||||||
|
(def grid-page
|
||||||
|
(helper/build {:id "entity-table"
|
||||||
|
:nav (com/main-aside-nav)
|
||||||
|
:check-boxes? true
|
||||||
|
:page-specific-nav filters
|
||||||
|
:fetch-page fetch-page
|
||||||
|
:oob-render
|
||||||
|
(fn [request]
|
||||||
|
[(assoc-in (date-range-field* request) [1 :hx-swap-oob] true)
|
||||||
|
(assoc-in (exact-match-id* request) [1 :hx-swap-oob] true)])
|
||||||
|
:query-schema query-schema
|
||||||
|
:parse-query-params (fn [p]
|
||||||
|
(mc/decode query-schema p main-transformer))
|
||||||
|
:action-buttons (fn [request]
|
||||||
|
[(when (can? (:identity request) {:subject :payment :activity :bulk-delete})
|
||||||
|
(com/button {:hx-get (str (bidi/path-for ssr-routes/only-routes ::route/bulk-delete))
|
||||||
|
"x-bind:hx-vals" "JSON.stringify({selected: $data.selected, 'all-selected': $data.all_selected})"
|
||||||
|
"hx-include" "#payment-filters"
|
||||||
|
:color :red}
|
||||||
|
"Void selected"))])
|
||||||
|
:row-buttons (fn [_ entity]
|
||||||
|
[(when (not= :payment-status/voided (:payment/status entity))
|
||||||
|
(com/icon-button {:hx-delete (bidi/path-for ssr-routes/only-routes
|
||||||
|
::route/delete
|
||||||
|
:db/id (:db/id entity))
|
||||||
|
:hx-confirm "Are you sure you want to void this payment?"}
|
||||||
|
svg/trash))])
|
||||||
|
:breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes ::route/page)}
|
||||||
|
"Payments"]]
|
||||||
|
:title (fn [r]
|
||||||
|
(str
|
||||||
|
(some-> r :query-params :status name str/capitalize (str " "))
|
||||||
|
"Payments"))
|
||||||
|
:entity-name "payments"
|
||||||
|
:route ::route/table
|
||||||
|
:headers [{:key "client"
|
||||||
|
:name "Client"
|
||||||
|
:sort-key "client"
|
||||||
|
:hide? (fn [args]
|
||||||
|
(= (count (:clients args)) 1))
|
||||||
|
:render #(-> % :payment/client :client/code)}
|
||||||
|
{:key "vendor"
|
||||||
|
:name "Vendor"
|
||||||
|
:sort-key "vendor"
|
||||||
|
:render #(-> % :payment/vendor :vendor/name)}
|
||||||
|
{:key "bank-account"
|
||||||
|
:name "Bank account"
|
||||||
|
:sort-key "bank-account"
|
||||||
|
:show-starting "xl"
|
||||||
|
:render (fn [p]
|
||||||
|
[:div.flex.items-center
|
||||||
|
(when (:payment/bank-account p)
|
||||||
|
(bank-account-icon/icon (:payment/bank-account p)))
|
||||||
|
[:div (-> p :payment/bank-account :bank-account/name)]])}
|
||||||
|
{:key "check-number"
|
||||||
|
:name "Check #"
|
||||||
|
:sort-key "check-number"
|
||||||
|
:render (fn [{:payment/keys [s3-url check-number]}]
|
||||||
|
(if s3-url
|
||||||
|
(com/link {:href s3-url :target "_new"} [:div.flex.items-center.gap-x-2 check-number [:div.w-4.h-4 svg/external-link]])
|
||||||
|
check-number))}
|
||||||
|
{:key "status"
|
||||||
|
:name "Status"
|
||||||
|
:render (fn [{:payment/keys [status]}]
|
||||||
|
(condp = status
|
||||||
|
:payment-status/cleared
|
||||||
|
(com/pill {:color :primary} "cleared")
|
||||||
|
|
||||||
|
:payment-status/pending
|
||||||
|
(com/pill {:color :secondary} "pending")
|
||||||
|
:payment-status/voided
|
||||||
|
(com/pill {:color :red} "voided")
|
||||||
|
nil
|
||||||
|
""))}
|
||||||
|
{:key "date"
|
||||||
|
:name "Date"
|
||||||
|
:show-starting "lg"
|
||||||
|
:render (fn [{:payment/keys [date]}]
|
||||||
|
(some-> date (atime/unparse-local atime/normal-date)))}
|
||||||
|
{:key "amount"
|
||||||
|
:name "Amount"
|
||||||
|
:render (fn [{:payment/keys [amount]}]
|
||||||
|
(some->> amount (format "$%.2f")))}
|
||||||
|
{:key "links"
|
||||||
|
:name "Links"
|
||||||
|
:class "w-8"
|
||||||
|
:render (fn [p]
|
||||||
|
(render-links (concat (->> p :payment/invoices (map (fn [invoice]
|
||||||
|
{:link (hu/url (bidi/path-for client-routes/routes
|
||||||
|
:invoices)
|
||||||
|
{:exact-match-id (:db/id invoice)})
|
||||||
|
:content (str "Inv. " (:invoice/invoice-number invoice))})))
|
||||||
|
(some-> p :transaction/_payment ((fn [t]
|
||||||
|
[{:link (hu/url (bidi/path-for client-routes/routes
|
||||||
|
:transactions)
|
||||||
|
{:exact-match-id 1})
|
||||||
|
:color :secondary
|
||||||
|
:content "Transaction"}]))))))}]}))
|
||||||
|
|
||||||
|
(def row* (partial helper/row* grid-page))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(comment
|
||||||
|
(mc/decode query-schema {"exact-match-id" "123"} (mt/transformer main-transformer mt/strip-extra-keys-transformer))
|
||||||
|
(mc/decode query-schema {} (mt/transformer main-transformer mt/strip-extra-keys-transformer))
|
||||||
|
(mc/decode query-schema {"exact-match-id" nil} (mt/transformer main-transformer mt/strip-extra-keys-transformer))
|
||||||
|
(mc/decode query-schema {"exact-match-id" ""} (mt/transformer main-transformer mt/strip-extra-keys-transformer))
|
||||||
|
(mc/decode query-schema {"start-date" "12/21/2023"} (mt/transformer main-transformer mt/strip-extra-keys-transformer))
|
||||||
|
(mc/decode query-schema {"payment-type" "food"} (mt/transformer main-transformer mt/strip-extra-keys-transformer))
|
||||||
|
(mc/decode query-schema {"vendor" "87"} (mt/transformer main-transformer mt/strip-extra-keys-transformer))
|
||||||
|
|
||||||
|
|
||||||
|
(mc/decode query-schema {"start-date" #inst "2023-12-21T08:00:00.000-00:00"} (mt/transformer main-transformer mt/strip-extra-keys-transformer)))
|
||||||
|
|
||||||
|
(defn delete [{check :entity :as request identity :identity}]
|
||||||
|
(exception->notification
|
||||||
|
#(when-not (or (= :payment-status/pending (:payment/status check))
|
||||||
|
(#{:payment-type/cash :payment-type/debit :payment-type/balance-credit} (:payment/type check)))
|
||||||
|
(throw (ex-info "Payment must be pending." {}))))
|
||||||
|
|
||||||
|
(exception->notification
|
||||||
|
#(assert-can-see-client identity (:db/id (:payment/client check))))
|
||||||
|
(notify-if-locked (:db/id (:payment/client check))
|
||||||
|
(:payment/date check))
|
||||||
|
(let [removing-payments (mapcat (fn [x]
|
||||||
|
(let [invoice (:invoice-payment/invoice x)
|
||||||
|
new-balance (+ (:invoice/outstanding-balance invoice)
|
||||||
|
(:invoice-payment/amount x))]
|
||||||
|
[[:db/retractEntity (:db/id x)]
|
||||||
|
[:upsert-invoice {:db/id (:db/id invoice)
|
||||||
|
:invoice/outstanding-balance new-balance
|
||||||
|
:invoice/status (if (dollars-0? new-balance)
|
||||||
|
(:invoice/status invoice)
|
||||||
|
:invoice-status/unpaid)}]]))
|
||||||
|
(:invoice-payment/_payment check))
|
||||||
|
updated-payment {:db/id (:db/id check)
|
||||||
|
:payment/amount 0.0
|
||||||
|
:payment/status :payment-status/voided}]
|
||||||
|
(audit-transact (conj removing-payments updated-payment)
|
||||||
|
identity)
|
||||||
|
|
||||||
|
(html-response (row* (:identity request) updated-payment {:delete-after-settle? true :class "live-removed"})
|
||||||
|
:headers {"hx-retarget" (format "#entity-table tr[data-id=\"%d\"]" (:db/id check))})))
|
||||||
|
|
||||||
|
;; TODO use decoding here
|
||||||
|
(defn bulk-delete-dialog [request]
|
||||||
|
(alog/peek :selected (pr-str (:selected (:query-params request))))
|
||||||
|
(let [all-selected (:all-selected (:query-params request))
|
||||||
|
selected (:selected (:query-params request))
|
||||||
|
ids (cond
|
||||||
|
all-selected
|
||||||
|
(:ids (fetch-ids (dc/db conn) (-> request
|
||||||
|
(assoc-in [:query-params :start] 0)
|
||||||
|
(assoc-in [:query-params :per-page] 250))))
|
||||||
|
:else
|
||||||
|
selected)]
|
||||||
|
(modal-response
|
||||||
|
(com/modal {}
|
||||||
|
(com/modal-card-advanced
|
||||||
|
{}
|
||||||
|
|
||||||
|
(com/modal-body {}
|
||||||
|
[:div.flex.flex-col.mt-4.space-y-4.items-center
|
||||||
|
[:div.w-24.h-24.bg-red-50.rounded-full.p-4.text-red-300
|
||||||
|
|
||||||
|
svg/alert]
|
||||||
|
[:div "You are about to void " (count ids) " payments. Are you sure you want to do this?"]])
|
||||||
|
(com/modal-footer {} [:div.flex.justify-end (com/button {:color :primary
|
||||||
|
:hx-vals (hx/json (mc/encode
|
||||||
|
query-schema
|
||||||
|
(dissoc (:query-params request) :sort)
|
||||||
|
(mt/transformer
|
||||||
|
main-transformer
|
||||||
|
dissoc-nil-transformer
|
||||||
|
mt/strip-extra-keys-transformer)))
|
||||||
|
:hx-delete (hu/url (bidi/path-for ssr-routes/only-routes
|
||||||
|
::route/bulk-delete-confirm))}
|
||||||
|
"Void payments")])))
|
||||||
|
:headers (-> {}
|
||||||
|
(assoc "hx-retarget" ".modal-stack")
|
||||||
|
(assoc "hx-reswap" "beforeend")))))
|
||||||
|
|
||||||
|
(defn void-payments-internal [all-ids id]
|
||||||
|
(let [payments-to-update (->> all-ids
|
||||||
|
(dc/q '[:find (pull ?p [:db/id
|
||||||
|
{:invoice-payment/_payment [:invoice-payment/amount
|
||||||
|
:db/id
|
||||||
|
{:invoice-payment/invoice [:db/id :invoice/outstanding-balance]}]}])
|
||||||
|
:in $ [?p ...]
|
||||||
|
:where
|
||||||
|
(not [_ :transaction/payment ?p])
|
||||||
|
(not [?p :payment/status :payment-status/voided])
|
||||||
|
[?p :payment/client ?c]
|
||||||
|
[(get-else $ ?c :client/locked-until #inst "2000-01-01") ?lu]
|
||||||
|
[?p :payment/date ?d]
|
||||||
|
[(>= ?d ?lu)]]
|
||||||
|
(dc/db conn))
|
||||||
|
(map first))]
|
||||||
|
(audit-transact (->> payments-to-update
|
||||||
|
(mapcat (fn [{:keys [:db/id]
|
||||||
|
invoices :invoice-payment/_payment}]
|
||||||
|
(into
|
||||||
|
[{:db/id id
|
||||||
|
:payment/amount 0.0
|
||||||
|
:payment/status :payment-status/voided}]
|
||||||
|
(->> invoices
|
||||||
|
(mapcat (fn [{:keys [:invoice-payment/invoice :db/id :invoice-payment/amount]}]
|
||||||
|
(let [new-balance (+ (:invoice/outstanding-balance invoice)
|
||||||
|
amount)]
|
||||||
|
[[:db.fn/retractEntity id]
|
||||||
|
[:upsert-invoice {:db/id (:db/id invoice)
|
||||||
|
:invoice/outstanding-balance new-balance
|
||||||
|
:invoice/status (if (dollars-0? new-balance)
|
||||||
|
(:invoice/status invoice)
|
||||||
|
:invoice-status/unpaid)}]]))))))))
|
||||||
|
id)
|
||||||
|
(count payments-to-update)))
|
||||||
|
|
||||||
|
(defn bulk-delete-dialog-confirm [request]
|
||||||
|
(alog/peek (:form-params request))
|
||||||
|
(let [all-selected (:all-selected (:form-params request))
|
||||||
|
selected (:selected (:form-params request))
|
||||||
|
ids (cond
|
||||||
|
all-selected
|
||||||
|
(:ids (fetch-ids (dc/db conn) (-> request
|
||||||
|
(assoc :query-params (:form-params request))
|
||||||
|
(assoc-in [:query-params :start] 0)
|
||||||
|
(assoc-in [:query-params :per-page] 250))))
|
||||||
|
|
||||||
|
|
||||||
|
:else
|
||||||
|
selected)
|
||||||
|
updated-count (void-payments-internal ids (:identity request))]
|
||||||
|
|
||||||
|
(html-response [:div]
|
||||||
|
:headers {"hx-trigger" (hx/json {:modalclose ""
|
||||||
|
:notification (format "Successfully voided %d of %d payments."
|
||||||
|
updated-count
|
||||||
|
(count ids))})})))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(def key->handler
|
||||||
|
(apply-middleware-to-all-handlers
|
||||||
|
{::route/page (helper/page-route grid-page)
|
||||||
|
::route/delete (-> delete
|
||||||
|
(wrap-entity [:route-params :db/id] default-read)
|
||||||
|
(wrap-schema-enforce :route-params [:map [:db/id entity-id]]))
|
||||||
|
::route/bulk-delete-confirm (-> bulk-delete-dialog-confirm
|
||||||
|
(wrap-schema-enforce :form-schema query-schema)
|
||||||
|
(wrap-admin))
|
||||||
|
::route/bulk-delete (-> bulk-delete-dialog
|
||||||
|
(wrap-admin))
|
||||||
|
|
||||||
|
|
||||||
|
::route/table (helper/table-route grid-page)}
|
||||||
|
(fn [h]
|
||||||
|
(-> h
|
||||||
|
(wrap-apply-sort grid-page)
|
||||||
|
(wrap-merge-prior-hx)
|
||||||
|
(wrap-schema-enforce :query-schema query-schema)
|
||||||
|
(wrap-schema-enforce :hx-schema query-schema)
|
||||||
|
(wrap-client-redirect-unauthenticated)))))
|
||||||
@@ -96,6 +96,14 @@
|
|||||||
[:svg {:fill "currentColor", :viewbox "0 0 20 20", :xmlns "http://www.w3.org/2000/svg"}
|
[:svg {:fill "currentColor", :viewbox "0 0 20 20", :xmlns "http://www.w3.org/2000/svg"}
|
||||||
[:path {:d "M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z"}]])
|
[:path {:d "M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z"}]])
|
||||||
|
|
||||||
|
(def three-dots
|
||||||
|
[:svg {:xmlns "http://www.w3.org/2000/svg", :viewbox "0 0 24 24"}
|
||||||
|
[:defs]
|
||||||
|
[:title "navigation-menu-horizontal"]
|
||||||
|
[:path {:d "M0.5 12a2.5 2.5 0 1 0 5 0 2.5 2.5 0 1 0 -5 0", :fill "none", :stroke "currentcolor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1"}]
|
||||||
|
[:path {:d "M9.5 12a2.5 2.5 0 1 0 5 0 2.5 2.5 0 1 0 -5 0", :fill "none", :stroke "currentcolor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1"}]
|
||||||
|
[:path {:d "M18.5 12a2.5 2.5 0 1 0 5 0 2.5 2.5 0 1 0 -5 0", :fill "none", :stroke "currentcolor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1"}]])
|
||||||
|
|
||||||
(def breadcrumb-component
|
(def breadcrumb-component
|
||||||
[:svg {:fill "currentColor", :viewbox "0 0 20 20", :xmlns "http://www.w3.org/2000/svg"}
|
[:svg {:fill "currentColor", :viewbox "0 0 20 20", :xmlns "http://www.w3.org/2000/svg"}
|
||||||
[:path {:fill-rule "evenodd", :d "M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z", :clip-rule "evenodd"}]])
|
[:path {:fill-rule "evenodd", :d "M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z", :clip-rule "evenodd"}]])
|
||||||
|
|||||||
@@ -1,15 +1,19 @@
|
|||||||
(ns auto-ap.ssr.utils
|
(ns auto-ap.ssr.utils
|
||||||
(:require
|
(:require [auto-ap.datomic :refer [all-schema conn]]
|
||||||
[auto-ap.datomic :refer [all-schema conn]]
|
[auto-ap.logging :as alog]
|
||||||
[auto-ap.logging :as alog]
|
[auto-ap.time :as atime]
|
||||||
[clojure.string :as str]
|
[clj-time.coerce :as coerce]
|
||||||
[config.core :refer [env]]
|
[clj-time.core :as time]
|
||||||
[datomic.api :as dc]
|
[clojure.string :as str]
|
||||||
[hiccup2.core :as hiccup]
|
[datomic.api :as dc]
|
||||||
[malli.core :as mc]
|
[hiccup2.core :as hiccup]
|
||||||
[malli.error :as me]
|
[malli.core :as mc]
|
||||||
[malli.transform :as mt2]
|
[malli.core :as m]
|
||||||
[slingshot.slingshot :refer [throw+ try+]]))
|
[malli.error :as me]
|
||||||
|
[malli.registry :as mr]
|
||||||
|
[malli.transform :as mt2]
|
||||||
|
[slingshot.slingshot :refer [throw+ try+]]
|
||||||
|
[taoensso.encore :refer [filter-vals]]))
|
||||||
|
|
||||||
(defn html-response [hiccup & {:keys [status headers oob] :or {status 200 headers {} oob []}}]
|
(defn html-response [hiccup & {:keys [status headers oob] :or {status 200 headers {} oob []}}]
|
||||||
{:status status
|
{:status status
|
||||||
@@ -96,11 +100,17 @@
|
|||||||
:long empty->nil
|
:long empty->nil
|
||||||
'nat-int? empty->nil}}))
|
'nat-int? empty->nil}}))
|
||||||
|
|
||||||
(def entity-id (mc/schema [nat-int? {:error/message "required"
|
(def raw-entity-id [nat-int? {:error/message "required"
|
||||||
:decode/arbitrary (fn [e]
|
:decode/arbitrary (fn [e]
|
||||||
(if (and (map? e) (:db/id e))
|
(if (and (map? e) (:db/id e))
|
||||||
(:db/id e)
|
(:db/id e)
|
||||||
e))}]))
|
e))}])
|
||||||
|
|
||||||
|
(def entity-id (mc/schema [nat-int? {:error/message "required"
|
||||||
|
:decode/arbitrary (fn [e]
|
||||||
|
(if (and (map? e) (:db/id e))
|
||||||
|
(:db/id e)
|
||||||
|
e))}]))
|
||||||
|
|
||||||
(def temp-id (mc/schema [:string {:min 1}]))
|
(def temp-id (mc/schema [:string {:min 1}]))
|
||||||
(def money (mc/schema [:double]))
|
(def money (mc/schema [:double]))
|
||||||
@@ -158,13 +168,134 @@
|
|||||||
(throw+ (ex-info m (merge data {:type :form-validation
|
(throw+ (ex-info m (merge data {:type :form-validation
|
||||||
:form-validation-errors [m]}))))
|
:form-validation-errors [m]}))))
|
||||||
|
|
||||||
|
(def clj-date-schema
|
||||||
|
(mc/schema [inst? {:decode/arbitrary (fn [m]
|
||||||
|
(alog/peek ::decode
|
||||||
|
(if (string? m)
|
||||||
|
(coerce/to-date-time (atime/parse m atime/normal-date))
|
||||||
|
|
||||||
|
m)))
|
||||||
|
:encode/arbitrary (fn [m]
|
||||||
|
(alog/peek ::encode
|
||||||
|
(cond
|
||||||
|
(inst? m)
|
||||||
|
(atime/unparse-local (coerce/to-date-time m) atime/normal-date)
|
||||||
|
|
||||||
|
(instance? org.joda.time.DateTime m)
|
||||||
|
(atime/unparse-local m atime/normal-date)
|
||||||
|
|
||||||
|
:else
|
||||||
|
m)))}]))
|
||||||
|
|
||||||
|
|
||||||
|
(def date-range-transformer
|
||||||
|
(mt2/transformer {:decoders
|
||||||
|
{:map {:compile (fn [schema _]
|
||||||
|
(let [properties (mc/properties schema)]
|
||||||
|
(fn [m]
|
||||||
|
(if (:date-range properties)
|
||||||
|
(let [[date-range-key start-date-key end-date-key] (:date-range properties)
|
||||||
|
date-range-value (get m date-range-key)]
|
||||||
|
(if date-range-value
|
||||||
|
(-> (condp = date-range-value
|
||||||
|
"week"
|
||||||
|
(assoc m
|
||||||
|
start-date-key (time/plus (time/now) (time/days -7))
|
||||||
|
end-date-key (time/now))
|
||||||
|
|
||||||
|
"month"
|
||||||
|
(assoc m
|
||||||
|
start-date-key (time/plus (time/now) (time/months -1))
|
||||||
|
end-date-key (time/now))
|
||||||
|
|
||||||
|
"year"
|
||||||
|
(assoc m
|
||||||
|
start-date-key (time/plus (time/now) (time/years -1))
|
||||||
|
end-date-key (time/now))
|
||||||
|
|
||||||
|
"all"
|
||||||
|
(assoc m start-date-key (time/plus (time/now) (time/years -3))
|
||||||
|
end-date-key (time/now))
|
||||||
|
|
||||||
|
m)
|
||||||
|
(dissoc date-range-key))
|
||||||
|
m))
|
||||||
|
m))))}}}))
|
||||||
|
|
||||||
|
(def pull-transformer
|
||||||
|
(mt2/transformer {:decoders
|
||||||
|
{:entity-map
|
||||||
|
{:compile (fn [schema _]
|
||||||
|
(let [pull-expr (:pull (mc/properties schema))]
|
||||||
|
(if pull-expr
|
||||||
|
(fn pull-data [m]
|
||||||
|
(cond
|
||||||
|
(nat-int? m)
|
||||||
|
(dc/pull (dc/db conn) pull-expr m)
|
||||||
|
(and (string? m) (not-empty m))
|
||||||
|
(dc/pull (dc/db conn) pull-expr (Long/parseLong m))
|
||||||
|
:else
|
||||||
|
nil))
|
||||||
|
identity)))}}
|
||||||
|
:encoders
|
||||||
|
{:entity-map
|
||||||
|
{:compile (fn [schema _]
|
||||||
|
(let [pull-expr (:pull (mc/properties schema))]
|
||||||
|
(if pull-expr
|
||||||
|
(fn pull-data [m]
|
||||||
|
(cond
|
||||||
|
(map? m)
|
||||||
|
(:db/id m)
|
||||||
|
(nat-int? m)
|
||||||
|
m
|
||||||
|
(and (string? m) (not-empty m))
|
||||||
|
(Long/parseLong m)
|
||||||
|
|
||||||
|
:else
|
||||||
|
m))
|
||||||
|
identity)))}}}))
|
||||||
|
|
||||||
|
(def coerce-vector
|
||||||
|
(mt2/transformer {:decoders {:vector {:compile (fn [schema _]
|
||||||
|
(when (:coerce? (m/properties schema))
|
||||||
|
(fn [data]
|
||||||
|
(cond (sequential? data)
|
||||||
|
data
|
||||||
|
(nil? data)
|
||||||
|
nil
|
||||||
|
:else
|
||||||
|
[data]))))}}}))
|
||||||
|
|
||||||
|
(defn wrap-merge-prior-hx [handler]
|
||||||
|
;; TODO this should just be automatic
|
||||||
|
(fn [request]
|
||||||
|
(handler (update request :query-params (fn [qp]
|
||||||
|
(->> (concat (:hx-query-params request) qp)
|
||||||
|
(into {})))))))
|
||||||
|
|
||||||
|
|
||||||
|
(def dissoc-nil-transformer
|
||||||
|
(let [e {:map {:compile (fn [schema _]
|
||||||
|
(fn [data]
|
||||||
|
(if (map? data)
|
||||||
|
(filter-vals
|
||||||
|
(fn [x]
|
||||||
|
(not (nil? x)))
|
||||||
|
data)
|
||||||
|
data)))}}]
|
||||||
|
(mt2/transformer {:encoders e
|
||||||
|
:decoders e})))
|
||||||
|
|
||||||
(def main-transformer
|
(def main-transformer
|
||||||
(mt2/transformer
|
(mt2/transformer
|
||||||
parse-empty-as-nil
|
parse-empty-as-nil
|
||||||
(mt2/key-transformer {:encode keyword->str :decode str->keyword})
|
(mt2/key-transformer {:encode keyword->str :decode str->keyword})
|
||||||
|
(mt2/transformer {:name :arbitrary})
|
||||||
mt2/string-transformer
|
mt2/string-transformer
|
||||||
mt2/json-transformer
|
mt2/json-transformer
|
||||||
(mt2/transformer {:name :arbitrary})
|
coerce-vector
|
||||||
|
date-range-transformer
|
||||||
|
pull-transformer
|
||||||
mt2/default-value-transformer))
|
mt2/default-value-transformer))
|
||||||
|
|
||||||
(defn strip [s]
|
(defn strip [s]
|
||||||
@@ -192,7 +323,7 @@
|
|||||||
:error {:explain (mc/explain schema entity)}}))))
|
:error {:explain (mc/explain schema entity)}}))))
|
||||||
|
|
||||||
|
|
||||||
(defn schema-enforce-request [{:keys [form-params query-params params] :as request} & {:keys [form-schema query-schema route-schema params-schema]}]
|
(defn schema-enforce-request [{:keys [form-params query-params hx-query-params params] :as request} & {:keys [form-schema hx-schema query-schema route-schema params-schema]}]
|
||||||
(let [request (try
|
(let [request (try
|
||||||
(cond-> request
|
(cond-> request
|
||||||
(and (:params request) params-schema)
|
(and (:params request) params-schema)
|
||||||
@@ -216,6 +347,14 @@
|
|||||||
form-params
|
form-params
|
||||||
main-transformer))
|
main-transformer))
|
||||||
|
|
||||||
|
(and hx-schema hx-query-params)
|
||||||
|
(assoc :hx-query-params
|
||||||
|
(mc/coerce
|
||||||
|
hx-schema
|
||||||
|
hx-query-params
|
||||||
|
main-transformer))
|
||||||
|
|
||||||
|
|
||||||
(and query-schema query-params)
|
(and query-schema query-params)
|
||||||
(assoc :query-params
|
(assoc :query-params
|
||||||
(mc/coerce
|
(mc/coerce
|
||||||
@@ -241,9 +380,10 @@
|
|||||||
:error (:data (ex-data e))}))))]
|
:error (:data (ex-data e))}))))]
|
||||||
request))
|
request))
|
||||||
|
|
||||||
(defn wrap-schema-enforce [handler & {:keys [form-schema query-schema route-schema params-schema]}]
|
(defn wrap-schema-enforce [handler & {:keys [form-schema query-schema route-schema params-schema hx-schema]}]
|
||||||
(fn [request]
|
(fn [request]
|
||||||
(handler (schema-enforce-request request
|
(handler (schema-enforce-request request
|
||||||
|
:hx-schema hx-schema
|
||||||
:form-schema form-schema
|
:form-schema form-schema
|
||||||
:query-schema query-schema
|
:query-schema query-schema
|
||||||
:route-schema route-schema
|
:route-schema route-schema
|
||||||
@@ -293,7 +433,10 @@
|
|||||||
(into [:enum {:decode/string #(if (keyword? %)
|
(into [:enum {:decode/string #(if (keyword? %)
|
||||||
%
|
%
|
||||||
(when (not-empty %)
|
(when (not-empty %)
|
||||||
(keyword n %)))}]
|
(keyword n %)))
|
||||||
|
:encode/string #(if (keyword? %)
|
||||||
|
(name %)
|
||||||
|
%)}]
|
||||||
(for [{:db/keys [ident]} (all-schema)
|
(for [{:db/keys [ident]} (all-schema)
|
||||||
:when (= n (namespace ident))]
|
:when (= n (namespace ident))]
|
||||||
ident)))
|
ident)))
|
||||||
@@ -375,4 +518,25 @@
|
|||||||
(handler (if entity
|
(handler (if entity
|
||||||
(assoc request
|
(assoc request
|
||||||
:entity entity)
|
:entity entity)
|
||||||
request)))))
|
request)))))
|
||||||
|
|
||||||
|
(mr/set-default-registry!
|
||||||
|
(mr/composite-registry
|
||||||
|
(mc/default-schemas)
|
||||||
|
{:entity-id entity-id
|
||||||
|
:entity-map
|
||||||
|
(mc/-simple-schema {:type :entity-map
|
||||||
|
:pred map?})
|
||||||
|
#_[:map {:name :entity-map} [:db/id nat-int?]]}))
|
||||||
|
|
||||||
|
(comment
|
||||||
|
|
||||||
|
(mc/coerce [:map [:x {:optional true} [:maybe [:entity-map {:pull '[:db/id]}]]]]
|
||||||
|
{:x nil :g 1}
|
||||||
|
main-transformer)
|
||||||
|
|
||||||
|
(mc/decode [:map [:x [:entity-map {:pull '[:db/id :db/ident]}]]]
|
||||||
|
{:x 87}
|
||||||
|
main-transformer))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -150,7 +150,6 @@
|
|||||||
{:db/id c :client/groups ["NTG"]}))))
|
{:db/id c :client/groups ["NTG"]}))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(dc/q '[:find (count ?je)
|
(dc/q '[:find (count ?je)
|
||||||
:in $$
|
:in $$
|
||||||
:where [$$ ?je :journal-entry/client 17592238607837]]
|
:where [$$ ?je :journal-entry/client 17592238607837]]
|
||||||
|
|||||||
6
src/cljc/auto_ap/routes/payments.cljc
Normal file
6
src/cljc/auto_ap/routes/payments.cljc
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
(ns auto-ap.routes.payments)
|
||||||
|
(def routes {"" {:get ::page}
|
||||||
|
"/bulk-delete" {:get ::bulk-delete
|
||||||
|
:delete ::bulk-delete-confirm}
|
||||||
|
["/" [#"\d+" :db/id]] {:delete ::delete}
|
||||||
|
"/table" ::table})
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
[auto-ap.routes.admin.import-batch :as ib-routes]
|
[auto-ap.routes.admin.import-batch :as ib-routes]
|
||||||
[auto-ap.routes.indicators :as indicator-routes]
|
[auto-ap.routes.indicators :as indicator-routes]
|
||||||
[auto-ap.routes.admin.vendors :as v-routes]
|
[auto-ap.routes.admin.vendors :as v-routes]
|
||||||
|
[auto-ap.routes.payments :as p-routes]
|
||||||
[auto-ap.routes.admin.clients :as ac-routes]
|
[auto-ap.routes.admin.clients :as ac-routes]
|
||||||
[auto-ap.routes.admin.transaction-rules :as tr-routes]))
|
[auto-ap.routes.admin.transaction-rules :as tr-routes]))
|
||||||
|
|
||||||
@@ -64,6 +65,7 @@
|
|||||||
"/cash-drawer-shifts" {"" {:get :pos-cash-drawer-shifts}
|
"/cash-drawer-shifts" {"" {:get :pos-cash-drawer-shifts}
|
||||||
"/table" {:get :pos-cash-drawer-shift-table}}}
|
"/table" {:get :pos-cash-drawer-shift-table}}}
|
||||||
|
|
||||||
|
"payment" p-routes/routes
|
||||||
"vendor" {"/search" :vendor-search}
|
"vendor" {"/search" :vendor-search}
|
||||||
;; TODO Include IDS in routes for company-specific things, as opposed to headers
|
;; TODO Include IDS in routes for company-specific things, as opposed to headers
|
||||||
"company" {"" :company
|
"company" {"" :company
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
(:require [auto-ap.events :as events]
|
(:require [auto-ap.events :as events]
|
||||||
[auto-ap.routes :as routes]
|
[auto-ap.routes :as routes]
|
||||||
[auto-ap.subs :as subs]
|
[auto-ap.subs :as subs]
|
||||||
|
[auto-ap.routes.payments :as payment-routes]
|
||||||
[auto-ap.status :as status]
|
[auto-ap.status :as status]
|
||||||
[auto-ap.views.components.buttons :as buttons]
|
[auto-ap.views.components.buttons :as buttons]
|
||||||
[auto-ap.views.components.dropdown
|
[auto-ap.views.components.dropdown
|
||||||
@@ -17,7 +18,8 @@
|
|||||||
[goog.string :as gstring]
|
[goog.string :as gstring]
|
||||||
[re-frame.core :as re-frame]
|
[re-frame.core :as re-frame]
|
||||||
[auto-ap.views.components.expense-accounts-dialog :as expense-accounts-dialog]
|
[auto-ap.views.components.expense-accounts-dialog :as expense-accounts-dialog]
|
||||||
[auto-ap.views.pages.data-page :as data-page]))
|
[auto-ap.views.pages.data-page :as data-page]
|
||||||
|
[auto-ap.ssr-routes :as ssr-routes]))
|
||||||
|
|
||||||
(defn data-params->query-params [params]
|
(defn data-params->query-params [params]
|
||||||
(if (:exact-match-id params)
|
(if (:exact-match-id params)
|
||||||
@@ -195,7 +197,7 @@
|
|||||||
[:td (:post-date (:transaction (:payment invoice-payment)))]
|
[:td (:post-date (:transaction (:payment invoice-payment)))]
|
||||||
[:td
|
[:td
|
||||||
[buttons/fa-icon {:icon "fa-external-link"
|
[buttons/fa-icon {:icon "fa-external-link"
|
||||||
:href (str (bidi/path-for routes/routes :payments )
|
:href (str (bidi/path-for ssr-routes/only-routes ::payment-routes/page )
|
||||||
"?"
|
"?"
|
||||||
(url/map->query {:exact-match-id (:id (:payment invoice-payment))}))}]]])
|
(url/map->query {:exact-match-id (:id (:payment invoice-payment))}))}]]])
|
||||||
(when source-url
|
(when source-url
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
[auto-ap.forms.builder :as form-builder]
|
[auto-ap.forms.builder :as form-builder]
|
||||||
[auto-ap.routes :as routes]
|
[auto-ap.routes :as routes]
|
||||||
[auto-ap.ssr-routes :as ssr-routes]
|
[auto-ap.ssr-routes :as ssr-routes]
|
||||||
|
[auto-ap.routes.payments :as payment-routes]
|
||||||
[auto-ap.subs :as subs]
|
[auto-ap.subs :as subs]
|
||||||
[auto-ap.views.components.modal :as modal]
|
[auto-ap.views.components.modal :as modal]
|
||||||
[auto-ap.views.components.search :as search]
|
[auto-ap.views.components.search :as search]
|
||||||
@@ -179,7 +180,7 @@
|
|||||||
"Invoices" ])
|
"Invoices" ])
|
||||||
(when (p/can? @user {:subject :payment-page})
|
(when (p/can? @user {:subject :payment-page})
|
||||||
[:a.navbar-item {:class [(active-when ap = :payments)]
|
[:a.navbar-item {:class [(active-when ap = :payments)]
|
||||||
:href (bidi/path-for routes/routes :payments)}
|
:href (bidi/path-for ssr-routes/only-routes ::payment-routes/page)}
|
||||||
"Payments" ])
|
"Payments" ])
|
||||||
(when (p/can? @user {:subject :pos-page})
|
(when (p/can? @user {:subject :pos-page})
|
||||||
[:a.navbar-item {:class [(active-when ap = :pos-sales)]
|
[:a.navbar-item {:class [(active-when ap = :pos-sales)]
|
||||||
|
|||||||
@@ -1,25 +1,22 @@
|
|||||||
(ns auto-ap.views.pages.transactions.table
|
(ns auto-ap.views.pages.transactions.table
|
||||||
(:require
|
(:require [auto-ap.events :as events]
|
||||||
[auto-ap.events :as events]
|
[auto-ap.routes :as routes]
|
||||||
[auto-ap.routes :as routes]
|
[auto-ap.ssr-routes :as ssr-routes]
|
||||||
[auto-ap.status :as status]
|
[auto-ap.status :as status]
|
||||||
[auto-ap.subs :as subs]
|
[auto-ap.routes.payments :as payment-route]
|
||||||
[auto-ap.views.components.buttons :as buttons]
|
[auto-ap.subs :as subs]
|
||||||
[auto-ap.views.components.dropdown
|
[auto-ap.views.components.buttons :as buttons]
|
||||||
:refer [drop-down drop-down-contents]]
|
[auto-ap.views.components.dropdown
|
||||||
[auto-ap.views.components.grid :as grid]
|
:refer [drop-down drop-down-contents]]
|
||||||
[auto-ap.views.pages.data-page :as data-page]
|
[auto-ap.views.components.grid :as grid]
|
||||||
[auto-ap.views.pages.transactions.form :as edit]
|
[auto-ap.views.pages.data-page :as data-page]
|
||||||
[auto-ap.views.utils
|
[auto-ap.views.pages.transactions.form :as edit]
|
||||||
:refer [action-cell-width
|
[auto-ap.views.utils
|
||||||
date->str
|
:refer [action-cell-width date->str dispatch-event-with-propagation nf
|
||||||
dispatch-event-with-propagation
|
pretty with-role]]
|
||||||
nf
|
[bidi.bidi :as bidi]
|
||||||
pretty
|
[cemerick.url :as url]
|
||||||
with-role]]
|
[re-frame.core :as re-frame]))
|
||||||
[bidi.bidi :as bidi]
|
|
||||||
[cemerick.url :as url]
|
|
||||||
[re-frame.core :as re-frame]))
|
|
||||||
|
|
||||||
(re-frame/reg-event-fx
|
(re-frame/reg-event-fx
|
||||||
::editing-matches-found
|
::editing-matches-found
|
||||||
@@ -133,7 +130,7 @@
|
|||||||
[:td (date->str (:date payment) pretty)]
|
[:td (date->str (:date payment) pretty)]
|
||||||
[:td
|
[:td
|
||||||
[buttons/fa-icon {:icon "fa-external-link"
|
[buttons/fa-icon {:icon "fa-external-link"
|
||||||
:href (str (bidi/path-for routes/routes :payments)
|
:href (str (bidi/path-for ssr-routes/only-routes ::payment-route/page)
|
||||||
"?"
|
"?"
|
||||||
(url/map->query {:exact-match-id (:id payment)}))}]]])
|
(url/map->query {:exact-match-id (:id payment)}))}]]])
|
||||||
(when expected-deposit
|
(when expected-deposit
|
||||||
|
|||||||
Reference in New Issue
Block a user