(cloud) Made search work consistently

This commit is contained in:
2023-05-04 21:02:03 -07:00
parent e42fe2189c
commit 4e7e19da97
12 changed files with 308 additions and 186 deletions

View File

@@ -14,7 +14,18 @@
[datomic.api :as dc])) [datomic.api :as dc]))
(defn raw-graphql-ids [db args] (defn raw-graphql-ids [db args]
(let [query (cond-> {:query {:find [] (let [query
(if (:exact-match-id args)
(cond-> {:query {:find '[?e]
:in '[$ ?e]
:where '[[?e :journal-entry/client ?c]]}
:args [db
(:exact-match-id args)]}
(limited-clients (:id args))
(merge-query {:query {:in ['[?xx ...]]
:where ['[?e :journal-entry/client ?xx]]}
:args [(set (map :db/id (limited-clients (:id args))))]}))
(cond-> {:query {:find []
:in ['$ ] :in ['$ ]
:where []} :where []}
:args [db]} :args [db]}
@@ -126,7 +137,7 @@
args) args)
true true
(merge-query {:query {:find ['?sort-default '?e] :where ['[?e :journal-entry/date ?sort-default]]}}))] (merge-query {:query {:find ['?sort-default '?e] :where ['[?e :journal-entry/date ?sort-default]]}})))]
(->> (query2 query) (->> (query2 query)
(apply-sort-3 (update args :sort conj {:sort-key "default-2" :asc true})) (apply-sort-3 (update args :sort conj {:sort-key "default-2" :asc true}))
(apply-pagination args)))) (apply-pagination args))))

View File

@@ -811,6 +811,7 @@
{:fields {:client_id {:type :id} {:fields {:client_id {:type :id}
:vendor_id {:type :id} :vendor_id {:type :id}
:account_id {:type :id} :account_id {:type :id}
:exact_match_id {:type :id}
:amount_lte {:type :money} :amount_lte {:type :money}
:amount_gte {:type :money} :amount_gte {:type :money}
:bank_account_id {:type :id} :bank_account_id {:type :id}

View File

@@ -147,14 +147,18 @@
(let [session (:session request {}) (let [session (:session request {})
end-time (coerce/to-date-time (::idle-timeout session))] end-time (coerce/to-date-time (::idle-timeout session))]
(if (and end-time (time/before? end-time (time/now))) (if (and end-time (time/before? end-time (time/now)))
(if (get (:headers request) "hx-request")
{:session nil
:status 200
:headers {"hx-redirect" "/login"}}
{:session nil {:session nil
:status 302 :status 302
:headers {"Location" "/login"}} :headers {"Location" "/login"}})
(when-let [response (handler request)] (when-let [response (handler request)]
(let [session (:session response session)] (let [session (:session response session)]
(if (nil? session) (if (nil? session)
response response
(let [end-time (time/plus (time/now) (time/days 2))] (let [end-time (time/plus (time/now) (time/days 14))]
(assoc response :session (assoc session ::idle-timeout (coerce/to-date end-time))))))))))) (assoc response :session (assoc session ::idle-timeout (coerce/to-date end-time)))))))))))
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]} #_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}

View File

@@ -7,8 +7,15 @@
(defn wrap-secure [handler] (defn wrap-secure [handler]
(fn [request] (fn [request]
(if (authenticated? request) (cond (authenticated? request)
(handler request) (handler request)
(get (:headers request) "hx-request")
{:status 401
:headers {"hx-redirect" "/login"}}
:else
{:status 302 {:status 302
:headers {"Location" "/login" }}))) :headers {"Location" "/login" }})))
@@ -25,5 +32,6 @@
(fn [request] (fn [request]
(let [response (handler request)] (let [response (handler request)]
(if (= 401 (get response :status)) (if (= 401 (get response :status))
(assoc-in response [:headers "hx-redirect"] "/login/") (-> response
(assoc-in [:headers "hx-redirect"] "/login/"))
response)))) response))))

View File

@@ -1,21 +1,136 @@
(ns auto-ap.solr (ns auto-ap.solr
(:require [clojure.data.json :as json] (:require
[auto-ap.datomic :refer [conn]]
[clj-http.client :as client] [clj-http.client :as client]
[config.core :refer [env]])) [clojure.data.json :as json]
[config.core :refer [env]]
[datomic.api :as dc]
[clj-time.coerce :as c]
[auto-ap.time :as atime]
[clojure.string :as str]))
(def solr-uri (:solr-uri env)) (def solr-uri (:solr-uri env))
(defn fmt-amount [a]
(with-precision 2
(some-> a
bigdec
(.setScale 2 java.math.RoundingMode/HALF_UP)
(double))))
(defmulti datomic->solr (fn [d]
(let [entity (dc/pull (dc/db conn) '[:transaction/date :invoice/date :payment/date :journal-entry/date] d)]
(cond (:transaction/date entity)
"transaction"
(:invoice/date entity)
"invoice"
(:payment/date entity)
"payment"
(:journal-entry/date entity)
"journal-entry"
:else
(throw (ex-info "Trying to index unknown entity"
{:id d}))))))
(defmethod datomic->solr "transaction" [d]
(let [i (dc/pull (dc/db conn) '[:db/id :transaction/description-original
:transaction/amount
{:transaction/client [:client/code :db/id]
:transaction/vendor [:vendor/name :db/id]}
:transaction/date]
d)]
{"id" (-> i :db/id)
"client_id" (-> i :transaction/client :db/id)
"client_code" (-> i :transaction/client :client/code)
"date" (some-> i :transaction/date c/to-date-time (atime/unparse atime/iso-date) (str "T00:00:00Z"))
"amount" (-> i :transaction/amount fmt-amount)
"description" (-> i :transaction/description-original)
"vendor_name" (-> i :transaction/vendor :vendor/name)
"vendor_id" (-> i :transaction/vendor :db/id)
"type" "transaction"}))
(defmethod datomic->solr "journal-entry" [d]
(let [i (dc/pull (dc/db conn) '[:db/id
:journal-entry/amount
:journal-entry/source
{:journal-entry/client [:client/code :db/id]
:journal-entry/vendor [:vendor/name :db/id]
:journal-entry/line-items [{:journal-entry-line/account [:account/name :account/numeric-code]}]}
:journal-entry/date]
d)]
{"id" (-> i :db/id)
"client_id" (-> i :journal-entry/client :db/id)
"client_code" (-> i :journal-entry/client :client/code)
"date" (some-> i :journal-entry/date c/to-date-time (atime/unparse atime/iso-date) (str "T00:00:00Z"))
"amount" (-> i :journal-entry/amount fmt-amount)
"description" (str
(when (:journal-entry/source i)
(str (:journal-entry/source i) ": "))
(str/join ", " (set (map
(fn [li]
(format "%s (%s)" (:account/name (:journal-entry-line/account li))
(:account/numeric-code (:journal-entry-line/account li))))
(:journal-entry/line-items i)))))
"vendor_name" (-> i :journal-entry/vendor :vendor/name)
"vendor_id" (-> i :journal-entry/vendor :db/id)
"type" "journal-entry"}))
(defmethod datomic->solr "invoice" [d]
(let [i (dc/pull (dc/db conn) '[:db/id :invoice/invoice-number
:invoice/total
{:invoice/client [:client/code :db/id]
:invoice/vendor [:vendor/name :db/id]}
:invoice/date]
d)]
{"id" (-> i :db/id)
"client_id" (-> i :invoice/client :db/id)
"client_code" (-> i :invoice/client :client/code)
"date" (some-> i :invoice/date c/to-date-time (atime/unparse atime/iso-date) (str "T00:00:00Z"))
"amount" (-> i :invoice/total fmt-amount)
"number" (-> i :invoice/invoice-number)
"vendor_name" (-> i :invoice/vendor :vendor/name)
"vendor_id" (-> i :invoice/vendor :db/id)
"type" "invoice"}))
(defmethod datomic->solr "payment" [d]
(let [i (dc/pull (dc/db conn) '[:db/id :payment/check-number
:payment/amount
{:payment/client [:client/code :db/id]
:payment/vendor [:vendor/name :db/id]}
:payment/date]
d)]
{"id" (-> i :db/id)
"client_id" (-> i :payment/client :db/id)
"client_code" (-> i :payment/client :client/code)
"date" (some-> i :payment/date c/to-date-time (atime/unparse atime/iso-date) (str "T00:00:00Z"))
"amount" (-> i :payment/amount fmt-amount)
"description" (-> i :payment/check-number)
"vendor_name" (-> i :payment/vendor :vendor/name)
"vendor_id" (-> i :payment/vendor :db/id)
"type" "payment"}))
(defn index-documents [xs] (defn index-documents [xs]
(client/post (client/post
(str solr-uri "/solr/invoices/update?commitWithin=15000") (str solr-uri "/solr/invoices/update?commitWithin=15000")
{:headers {"Content-Type" "application/json"} {:headers {"Content-Type" "application/json"}
:socket-timeout 30000
:connection-timeout 30000
:method "POST" :method "POST"
:body (json/write-str xs)})) :body (json/write-str (map datomic->solr xs))}))
(defn query [q] (defn query [q]
(-> (client/post (str solr-uri "/solr/invoices/query") (-> (client/post (str solr-uri "/solr/invoices/query")
{:body (json/write-str {"query" q {:body (json/write-str {"query" q
"fields" "id, date, amount, type, description, number, client_code, client_id, vendor_name"}) "fields" "id, date, amount, type, description, number, client_code, client_id, vendor_name"})
:socket-timeout 30000
:connection-timeout 30000
:headers {"Content-Type" "application/json"} :headers {"Content-Type" "application/json"}
:as :json} :as :json}
) )

View File

@@ -43,6 +43,10 @@
(= "transaction" m) (= "transaction" m)
"type:transaction" "type:transaction"
(= "journal-entry" m)
"type:journal-entry"
:else :else
(str "_text_:\"" (try-parse-number (try-cleanse-date m)) ""\")))))))) (str "_text_:\"" (try-parse-number (try-cleanse-date m)) ""\"))))))))
@@ -69,7 +73,13 @@
[:div.card-header-icon.icon-accounting-invoice-mail] [:div.card-header-icon.icon-accounting-invoice-mail]
(= "payment" (:type doc)) (= "payment" (:type doc))
[:div.card-header-icon.icon-check-payment-sign]) [:div.card-header-icon.icon-check-payment-sign]
(= "journal-entry" (:type doc))
[:div.card-header-icon.icon-receipt]
:else
nil)
[:div.card-header-title (clojure.string/capitalize (:type doc)) [:div.card-header-title (clojure.string/capitalize (:type doc))
" " " "
" " " "
@@ -86,6 +96,10 @@
(:type doc)) (:type doc))
"transactions" "transactions"
(= "journal-entry"
(:type doc))
"ledger"
:else :else
"payments") "/?exact-match-id=" (:id doc)) "payments") "/?exact-match-id=" (:id doc))
:target "_blank"}] :target "_blank"}]
@@ -121,6 +135,7 @@
:hx-indicator "#search" :hx-indicator "#search"
:value (:q (:params request)) :value (:q (:params request))
:autofocus true}]] :autofocus true}]]
[:i "Try dates, numbers, vendors. To filter to specific entity type, use 'invoice', 'transaction', 'journal-entry', 'payment'."]
[:style [:style
".htmx-request #search-results {display: none} .htmx-request .htmx-indicator { display: block !important; }"] ".htmx-request #search-results {display: none} .htmx-request .htmx-indicator { display: block !important; }"]
[:div#search-results [:div#search-results

View File

@@ -560,89 +560,51 @@
:separator \tab)))) :separator \tab))))
(defn fmt-amount [a]
(with-precision 2
(some-> a
bigdec
(.setScale 2 java.math.RoundingMode/HALF_UP)
(double))))
(defn index-solr (defn index-solr
[] []
(doseq [batch (->> (dc/qseq {:query '[:find (pull ?i [:db/id :invoice/invoice-number (doseq [batch (->> (dc/qseq {:query '[:find ?i
:invoice/total
{:invoice/client [:client/code :db/id]
:invoice/vendor [:vendor/name :db/id]}
:invoice/date])
:in $ :in $
:where [?i :invoice/invoice-number] :where [?i :invoice/invoice-number]
(not [?i :invoice/status :invoice-status/voided])] (not [?i :invoice/status :invoice-status/voided])]
:args [ :args [
(dc/db conn)]}) (dc/db conn)]})
(map (fn [[i]] (map first)
{"id" (-> i :db/id)
"client_id" (-> i :invoice/client :db/id)
"client_code" (-> i :invoice/client :client/code)
"date" (some-> i :invoice/date c/to-date-time (atime/unparse atime/iso-date) (str "T00:00:00Z"))
"amount" (-> i :invoice/total fmt-amount)
"number" (-> i :invoice/invoice-number)
"vendor_name" (-> i :invoice/vendor :vendor/name)
"vendor_id" (-> i :invoice/vendor :db/id)
"type" "invoice"
}))
(partition-all 1000))] (partition-all 1000))]
(print ".") (print ".")
(flush) (flush)
(solr/index-documents batch)) (solr/index-documents batch))
(doseq [batch (->> (dc/qseq {:query '[:find (pull ?i [:db/id :payment/check-number (doseq [batch (->> (dc/qseq {:query '[:find ?i
:payment/amount
{:payment/client [:client/code :db/id]
:payment/vendor [:vendor/name :db/id]}
:payment/date])
:in $ :in $
:where [?i :payment/date] :where [?i :payment/date]
(not [?i :payment/status :payment-status/voided])] (not [?i :payment/status :payment-status/voided])]
:args [(dc/db conn)]}) :args [(dc/db conn)]})
(map (fn [[i]] (map first)
{"id" (-> i :db/id)
"client_id" (-> i :payment/client :db/id)
"client_code" (-> i :payment/client :client/code)
"date" (some-> i :payment/date c/to-date-time (atime/unparse atime/iso-date) (str "T00:00:00Z"))
"amount" (-> i :payment/amount fmt-amount)
"description" (-> i :payment/check-number)
"vendor_name" (-> i :payment/vendor :vendor/name)
"vendor_id" (-> i :payment/vendor :db/id)
"type" "payment"}))
(partition-all 1000))] (partition-all 1000))]
(print ".") (print ".")
(flush) (flush)
(solr/index-documents batch)) (solr/index-documents batch))
(doseq [batch (->> (dc/qseq {:query '[:find (pull ?i [:db/id :transaction/description-original (doseq [batch (->> (dc/qseq {:query '[:find ?i
:transaction/amount
{:transaction/client [:client/code :db/id]
:transaction/vendor [:vendor/name :db/id]}
:transaction/date])
:in $ :in $
:where [?i :transaction/description-original] :where [?i :transaction/description-original]
(not [?i :transaction/approval-status :transaction-approval-status/suppressed])] (not [?i :transaction/approval-status :transaction-approval-status/suppressed])]
:args [(dc/db conn)]}) :args [(dc/db conn)]})
(map (fn [[i]] (map first)
{"id" (-> i :db/id)
"client_id" (-> i :transaction/client :db/id)
"client_code" (-> i :transaction/client :client/code)
"date" (some-> i :transaction/date c/to-date-time (atime/unparse atime/iso-date) (str "T00:00:00Z"))
"amount" (-> i :transaction/amount fmt-amount)
"description" (-> i :transaction/description-original)
"vendor_name" (-> i :transaction/vendor :vendor/name)
"vendor_id" (-> i :transaction/vendor :db/id)
"type" "transaction"}))
(partition-all 1000))] (partition-all 1000))]
(print ".") (print ".")
(flush) (flush)
(solr/index-documents batch) (solr/index-documents batch))
)) (doseq [batch (->> (dc/qseq {:query '[:find ?i
:in $
:where [?i :journal-entry/date]]
:args [(dc/db conn)]})
(map first)
(partition-all 1000))]
(print ".")
(flush)
(solr/index-documents batch)))
(defn setup-sales-orders [] (defn setup-sales-orders []
(doseq [n (->> (dc/qseq {:query '[:find ?s ?c :where [?s :sales-order/client ?c]] :args [(dc/db auto-ap.datomic/conn)]}) (doseq [n (->> (dc/qseq {:query '[:find ?s ?c :where [?s :sales-order/client ?c]] :args [(dc/db auto-ap.datomic/conn)]})

View File

@@ -66,7 +66,7 @@
:max max :max max
:step "0.01" :step "0.01"
:style (or style :style (or style
{:width "8em"})}] {:width "7em"})}]
[:span.icon.is-left [:span.icon.is-left
[:i.fa.fa-usd]]])) [:i.fa.fa-usd]]]))

View File

@@ -9,7 +9,7 @@
:on-change (fn [v] :on-change (fn [v]
(re-frame/dispatch (conj on-change-event v)))} (re-frame/dispatch (conj on-change-event v)))}
[:div.columns [:div.columns {:style {:width "80%"}}
[:div.column [:div.column
[:div.control [:div.control
[form-builder/raw-field-v2 {:field :amount-gte} [form-builder/raw-field-v2 {:field :amount-gte}
@@ -17,5 +17,6 @@
[:div.column [:div.column
[:div.control [:div.control
[form-builder/raw-field-v2 {:field :amount-lte} [form-builder/raw-field-v2 {:field :amount-lte}
[com/money-input {:placeholder "<="}]]]]]] [com/money-input {:placeholder "<="}]]]]
[:div.column]]]
) )

View File

@@ -20,6 +20,7 @@
(defn data-params->query-params [params] (defn data-params->query-params [params]
{:start (:start params 0) {:start (:start params 0)
:sort (:sort params) :sort (:sort params)
:exact-match-id (:exact-match-id params)
:per-page (:per-page params) :per-page (:per-page params)
:client-id (:id @(re-frame/subscribe [::subs/client])) :client-id (:id @(re-frame/subscribe [::subs/client]))
:vendor-id (:id (:vendor params)) :vendor-id (:id (:vendor params))

View File

@@ -62,10 +62,12 @@
[:span {:class "name"} "External Ledger Import"]]])] [:span {:class "name"} "External Ledger Import"]]])]
(when (#{ :ledger :external-ledger} ap) (when (#{ :ledger :external-ledger} ap)
[:div [:div
(when client
[:<>
[:p.menu-label "Bank Account"] [:p.menu-label "Bank Account"]
[bank-account-filter {:value @(re-frame/subscribe [::data-page/filter data-page :bank-account]) [bank-account-filter {:value @(re-frame/subscribe [::data-page/filter data-page :bank-account])
:bank-accounts @(re-frame/subscribe [::subs/bank-accounts]) :bank-accounts @(re-frame/subscribe [::subs/bank-accounts])
:on-change-event [::data-page/filter-changed data-page :bank-account]}] :on-change-event [::data-page/filter-changed data-page :bank-account]}]])

View File

@@ -62,12 +62,14 @@
[:span {:class "name"} "Insights"]]] [:span {:class "name"} "Insights"]]]
]] ]]
(when client
[:<>
[:p.menu-label "Bank Account"] [:p.menu-label "Bank Account"]
[:div [:div
[bank-account-filter [bank-account-filter
{:on-change-event [::data-page/filter-changed data-page :bank-account] {:on-change-event [::data-page/filter-changed data-page :bank-account]
:value @(re-frame/subscribe [::data-page/filter data-page :bank-account]) :value @(re-frame/subscribe [::data-page/filter data-page :bank-account])
:bank-accounts @(re-frame/subscribe [::subs/bank-accounts])}]] :bank-accounts @(re-frame/subscribe [::subs/bank-accounts])}]]])
[:p.menu-label "Financial Account"] [:p.menu-label "Financial Account"]