allows importing only the invoices that were successful.
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
(ns auto-ap.ssr.invoice.import
|
(ns auto-ap.ssr.invoice.import
|
||||||
(:require [amazonica.aws.s3 :as s3]
|
(:require
|
||||||
|
[amazonica.aws.s3 :as s3]
|
||||||
[auto-ap.datomic
|
[auto-ap.datomic
|
||||||
:refer [add-sorter-fields apply-pagination apply-sort-3
|
:refer [add-sorter-fields apply-pagination apply-sort-3
|
||||||
audit-transact conn merge-query observable-query
|
audit-transact conn merge-query observable-query
|
||||||
@@ -7,9 +8,8 @@
|
|||||||
[auto-ap.datomic.clients :as d-clients]
|
[auto-ap.datomic.clients :as d-clients]
|
||||||
[auto-ap.datomic.invoices :as d-invoices]
|
[auto-ap.datomic.invoices :as d-invoices]
|
||||||
[auto-ap.datomic.vendors :as d-vendors]
|
[auto-ap.datomic.vendors :as d-vendors]
|
||||||
[auto-ap.graphql.utils :refer [assert-can-see-client
|
[auto-ap.graphql.utils :refer [assert-can-see-client assert-not-locked
|
||||||
assert-not-locked
|
can-see-client? exception->notification
|
||||||
exception->notification
|
|
||||||
extract-client-ids]]
|
extract-client-ids]]
|
||||||
[auto-ap.logging :as alog]
|
[auto-ap.logging :as alog]
|
||||||
[auto-ap.parse :as parse]
|
[auto-ap.parse :as parse]
|
||||||
@@ -25,9 +25,8 @@
|
|||||||
[auto-ap.ssr.svg :as svg]
|
[auto-ap.ssr.svg :as svg]
|
||||||
[auto-ap.ssr.utils
|
[auto-ap.ssr.utils
|
||||||
:refer [apply-middleware-to-all-handlers clj-date-schema
|
:refer [apply-middleware-to-all-handlers clj-date-schema
|
||||||
entity-id html-response main-transformer ref->enum-schema
|
entity-id html-response ref->enum-schema strip
|
||||||
strip wrap-entity wrap-implied-route-param
|
wrap-entity wrap-implied-route-param wrap-schema-enforce]]
|
||||||
wrap-schema-enforce]]
|
|
||||||
[auto-ap.time :as atime]
|
[auto-ap.time :as atime]
|
||||||
[auto-ap.utils :refer [dollars=]]
|
[auto-ap.utils :refer [dollars=]]
|
||||||
[bidi.bidi :as bidi]
|
[bidi.bidi :as bidi]
|
||||||
@@ -37,10 +36,9 @@
|
|||||||
[com.brunobonacci.mulog :as mu]
|
[com.brunobonacci.mulog :as mu]
|
||||||
[config.core :refer [env]]
|
[config.core :refer [env]]
|
||||||
[datomic.api :as dc]
|
[datomic.api :as dc]
|
||||||
[hiccup2.core :as hiccup]
|
[malli.core :as mc])
|
||||||
[malli.core :as mc]
|
(:import
|
||||||
[auto-ap.client-routes :as client-routes])
|
[java.util UUID]))
|
||||||
(:import [java.util UUID]))
|
|
||||||
|
|
||||||
|
|
||||||
(defn exact-match-id* [request]
|
(defn exact-match-id* [request]
|
||||||
@@ -661,17 +659,28 @@
|
|||||||
:invoice/status :invoice-status/unpaid}))
|
:invoice/status :invoice-status/unpaid}))
|
||||||
|
|
||||||
(defn validate-invoice [invoice user]
|
(defn validate-invoice [invoice user]
|
||||||
(when-not (:invoice/client invoice)
|
(let [missing-keys (for [k [:invoice/invoice-number :invoice/client :invoice/vendor :invoice/total :invoice/outstanding-balance :invoice/date]
|
||||||
(throw (ex-info (str "Searched clients for '" (:invoice/client-identifier invoice) "'. No client found in file. Select a client first.")
|
:when (not (get invoice k))]
|
||||||
{:invoice-number (:invoice/invoice-number invoice)
|
k
|
||||||
:customer-identifier (:invoice/client-identifier invoice)})))
|
)]
|
||||||
(assert-can-see-client user (:invoice/client invoice))
|
(cond
|
||||||
(doseq [k [:invoice/invoice-number :invoice/client :invoice/vendor :invoice/total :invoice/outstanding-balance :invoice/date]]
|
(not (:invoice/client invoice))
|
||||||
(when (not (get invoice k))
|
(do
|
||||||
(throw (ex-info (str (name k) "not found on invoice " invoice)
|
(alog/warn ::no-client :invoice invoice)
|
||||||
invoice))))
|
(assoc invoice :error-message (str "Searched clients for '" (:invoice/client-identifier invoice) "'. No client found in file. Select a client first.")))
|
||||||
|
|
||||||
invoice)
|
(not (can-see-client? user (:invoice/client invoice)))
|
||||||
|
(do
|
||||||
|
(alog/warn ::cant-see-client :invoice invoice )
|
||||||
|
(assoc invoice :error-message "No access for the client in this file.")
|
||||||
|
)
|
||||||
|
|
||||||
|
(seq missing-keys)
|
||||||
|
(do
|
||||||
|
(alog/warn ::mising-keys :keys missing-keys)
|
||||||
|
(assoc invoice :error-message (str "Missing the key " missing-keys)))
|
||||||
|
:else
|
||||||
|
invoice)))
|
||||||
|
|
||||||
(defn admin-only-if-multiple-clients [is]
|
(defn admin-only-if-multiple-clients [is]
|
||||||
(let [client-count (->> is
|
(let [client-count (->> is
|
||||||
@@ -688,19 +697,29 @@
|
|||||||
(map #(import->invoice % user))
|
(map #(import->invoice % user))
|
||||||
(map #(validate-invoice % user))
|
(map #(validate-invoice % user))
|
||||||
admin-only-if-multiple-clients
|
admin-only-if-multiple-clients
|
||||||
|
)
|
||||||
|
errored-invoices (->> potential-invoices
|
||||||
|
(filter #(:error-message %)))
|
||||||
|
successful-invoices (->> potential-invoices
|
||||||
|
(filter #(not (:error-message %))))
|
||||||
|
proposed-invoices (->> potential-invoices
|
||||||
|
(filter #(not (:error-message %)))
|
||||||
(mapv d-invoices/code-invoice)
|
(mapv d-invoices/code-invoice)
|
||||||
(mapv (fn [i] [:propose-invoice i])))]
|
(mapv (fn [i] [:propose-invoice i])))]
|
||||||
|
|
||||||
(alog/info ::creating-invoice :invoices potential-invoices)
|
(alog/info ::creating-invoice :invoices proposed-invoices)
|
||||||
(let [tx (audit-transact potential-invoices user)]
|
(let [tx (audit-transact proposed-invoices user)]
|
||||||
(when-not (seq (dc/q '[:find ?i
|
#_(when-not (seq (dc/q '[:find ?i
|
||||||
:in $ [?i ...]
|
:in $ [?i ...]
|
||||||
:where [?i :invoice/invoice-number]]
|
:where [?i :invoice/invoice-number]]
|
||||||
(:db-after tx)
|
(:db-after tx)
|
||||||
(map :e (:tx-data tx))))
|
(map :e (:tx-data tx))))
|
||||||
(throw (ex-info "No new invoices found."
|
(throw (ex-info "No new invoices found."
|
||||||
{:template (:template (first imports))})))
|
{:template (:template (first imports))})))
|
||||||
tx)))
|
{:tx tx
|
||||||
|
:errored-invoices errored-invoices
|
||||||
|
:successful-invoices successful-invoices
|
||||||
|
:imports imports})))
|
||||||
|
|
||||||
(defn import-internal [tempfile filename force-client force-location force-vendor force-chatgpt identity]
|
(defn import-internal [tempfile filename force-client force-location force-vendor force-chatgpt identity]
|
||||||
(mu/with-context {:parsing-file filename}
|
(mu/with-context {:parsing-file filename}
|
||||||
@@ -727,6 +746,7 @@
|
|||||||
(try
|
(try
|
||||||
|
|
||||||
(import-uploaded-invoice identity imports)
|
(import-uploaded-invoice identity imports)
|
||||||
|
|
||||||
(catch Exception e
|
(catch Exception e
|
||||||
(alog/warn ::couldnt-import-upload
|
(alog/warn ::couldnt-import-upload
|
||||||
:error e
|
:error e
|
||||||
@@ -734,8 +754,7 @@
|
|||||||
(throw (ex-info (ex-message e)
|
(throw (ex-info (ex-message e)
|
||||||
{:template (:template ( first imports))
|
{:template (:template ( first imports))
|
||||||
:sample (first imports)}
|
:sample (first imports)}
|
||||||
e))))
|
e)))))
|
||||||
imports)
|
|
||||||
(catch Exception e
|
(catch Exception e
|
||||||
(alog/warn ::couldnt-import-upload
|
(alog/warn ::couldnt-import-upload
|
||||||
:error e)
|
:error e)
|
||||||
@@ -751,59 +770,72 @@
|
|||||||
(fn [result {:keys [filename tempfile]}]
|
(fn [result {:keys [filename tempfile]}]
|
||||||
(try
|
(try
|
||||||
(let [i (import-internal tempfile filename force-client force-location force-vendor force-chatgpt (:identity request))]
|
(let [i (import-internal tempfile filename force-client force-location force-vendor force-chatgpt (:identity request))]
|
||||||
(update result :results conj {:filename filename
|
(alog/info ::failure-error-count :count (count (:errored-invoices i)) )
|
||||||
:response "success!"
|
|
||||||
:template (:template (first i))}))
|
(-> result
|
||||||
|
(update :error? #(or %
|
||||||
|
(boolean (seq (:errored-invoices i)))))
|
||||||
|
|
||||||
|
(update :files conj {:filename filename
|
||||||
|
:error? (boolean (seq (:errored-invoices i)))
|
||||||
|
:successful-invoices (count (:successful-invoices i))
|
||||||
|
:errors [:div
|
||||||
|
[:p.text-green-500 [:b (count (:successful-invoices i)) " succeeded in total."]
|
||||||
|
]
|
||||||
|
[:p [:b (count (:errored-invoices i)) " failed in total."]
|
||||||
|
]
|
||||||
|
[:ul
|
||||||
|
(for [e (take 5 (:errored-invoices i))]
|
||||||
|
[:li (:error-message e)]) ]]
|
||||||
|
:template (:template (first (:imports i)))})))
|
||||||
(catch Exception e
|
(catch Exception e
|
||||||
(-> result
|
(-> result
|
||||||
(assoc :error? true)
|
(assoc :error? true)
|
||||||
(update :results conj {:filename filename
|
(update :files conj {:filename filename
|
||||||
|
:errors "Can't process file"
|
||||||
:response (.getMessage e)
|
:response (.getMessage e)
|
||||||
:sample (:sample (ex-data e))
|
:sample (:sample (ex-data e))
|
||||||
:template (:template (ex-data e))})))))
|
:template (:template (ex-data e))})))))
|
||||||
{:error? false :results []}
|
{:error? false
|
||||||
|
:files []
|
||||||
|
}
|
||||||
file)]
|
file)]
|
||||||
|
|
||||||
(html-response [:div#page-notification.p-4.rounded-lg
|
(html-response [:div#page-notification.p-4.rounded-lg
|
||||||
{:class (if (:error? results)
|
|
||||||
"bg-red-50 text-red-700"
|
|
||||||
"bg-primary-50 text-primary-700")}
|
|
||||||
[:table
|
[:table
|
||||||
[:thead
|
[:thead
|
||||||
[:tr [:td "File"] [:td "Result"]
|
[:tr [:td "File"] [:td "Result"]
|
||||||
[:td "Template"]
|
[:td "Template"]
|
||||||
(if (:error? results)
|
[:td "Sample"]]
|
||||||
[:td "Sample match"])]
|
|
||||||
#_[:tr "Result"]
|
#_[:tr "Result"]
|
||||||
#_[:tr "Template"]
|
#_[:tr "Template"]]
|
||||||
]
|
(for [r (:files results)]
|
||||||
(for [r (:results results)]
|
|
||||||
[:tr
|
[:tr
|
||||||
[:td.p-2.border
|
[:td.p-2.border.align-top
|
||||||
{:class (if (:error? results)
|
{:class (if (:error? r)
|
||||||
"bg-red-50 text-red-700 border-red-300"
|
"bg-red-50 text-red-700 border-red-300"
|
||||||
"bg-primary-50 text-primary-700 border-green-500")}
|
"bg-primary-50 text-primary-700 border-green-500")}
|
||||||
(:filename r)]
|
(:filename r)]
|
||||||
[:td.p-2.border
|
[:td.p-2.border.align-top
|
||||||
{:class (if (:error? results)
|
{:class (if (:error? r)
|
||||||
"bg-red-50 text-red-700 border-red-300"
|
"bg-red-50 text-red-700 border-red-300"
|
||||||
"bg-primary-50 text-primary-700 border-green-500")}
|
"bg-primary-50 text-primary-700 border-green-500")}
|
||||||
(:response r)]
|
(:errors r)]
|
||||||
[:td.p-2.border
|
[:td.p-2.border.align-top
|
||||||
{:class (if (:error? results)
|
{:class (if (:error? r)
|
||||||
"bg-red-50 text-red-700 border-red-300"
|
"bg-red-50 text-red-700 border-red-300"
|
||||||
"bg-primary-50 text-primary-700 border-green-500")}
|
"bg-primary-50 text-primary-700 border-green-500")}
|
||||||
"Template: " (:template r)]
|
"Template: " (:template r)]
|
||||||
(if (:error? results )
|
[:td.p-2.border.align-top
|
||||||
[:td.p-2.border
|
{:class (if (:error? r)
|
||||||
{:class "bg-red-50 text-red-700 border-red-300"}
|
"bg-red-50 text-red-700 border-red-300"
|
||||||
|
"bg-primary-50 text-primary-700 border-green-500")}
|
||||||
[:ul
|
[:ul
|
||||||
(for [[k v] (dissoc (:sample r) :template :source-url :full-text :text)]
|
(for [[k v] (dissoc (:sample r) :template :source-url :full-text :text)]
|
||||||
[:li (name k) ": " (str v)])]
|
[:li (name k) ": " (str v)])]
|
||||||
#_(:template r)])])]]
|
#_(:template r)]])]]
|
||||||
:headers
|
:headers
|
||||||
{"hx-trigger" "invalidated"})
|
{"hx-trigger" "invalidated"})))
|
||||||
))
|
|
||||||
|
|
||||||
#_(defn wrap-test [handler]
|
#_(defn wrap-test [handler]
|
||||||
(fn [request]
|
(fn [request]
|
||||||
|
|||||||
Reference in New Issue
Block a user