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