Allows upload of CSV of sysco with line item parsing
This commit is contained in:
@@ -18,7 +18,7 @@
|
||||
[datomic.api :as dc])
|
||||
(:import (java.util UUID)))
|
||||
|
||||
(def sysco-name->line (atom nil))
|
||||
(def sysco-name->line (atom nil))
|
||||
(defn get-sysco->line []
|
||||
(when (nil? @sysco-name->line)
|
||||
(reset! sysco-name->line
|
||||
@@ -90,16 +90,60 @@
|
||||
tax)
|
||||
updated-invoice (assoc invoice :invoice/expense-accounts
|
||||
(for [[account amount] items-with-tax]
|
||||
#:invoice-expense-account {:db/id (random-tempid)
|
||||
:account account
|
||||
#:invoice-expense-account {:db/id (random-tempid)
|
||||
:account account
|
||||
:location (:invoice/location invoice)
|
||||
:amount amount}))]
|
||||
:amount amount}))]
|
||||
(if (check-okay-amount? updated-invoice)
|
||||
updated-invoice
|
||||
(do (alog/warn ::itemized-expenses-not-adding-up
|
||||
:invoice updated-invoice)
|
||||
invoice))))
|
||||
|
||||
(defn code-invoices-list-items [invoice]
|
||||
(with-precision 2
|
||||
(let [line-items (:line-items invoice)
|
||||
invoice-total (if (string? (:invoice/total invoice))
|
||||
(Double/parseDouble (:invoice/total invoice))
|
||||
(:invoice/total invoice))
|
||||
abs-total (Math/abs invoice-total)
|
||||
expense-accounts (reduce
|
||||
(fn [acc {:keys [description amount]}]
|
||||
(let [account (get-line-account description)]
|
||||
(update acc account (fnil + 0.0) amount)))
|
||||
{}
|
||||
line-items)
|
||||
total-line-amount (reduce + 0.0 (vals expense-accounts))
|
||||
accounts (if (zero? total-line-amount)
|
||||
[]
|
||||
(vec (for [[account amount] expense-accounts]
|
||||
(let [ratio (/ amount total-line-amount)
|
||||
cents (int (Math/round (* ratio abs-total 100)))]
|
||||
#:invoice-expense-account {:db/id (random-tempid)
|
||||
:account account
|
||||
:location (:invoice/location invoice)
|
||||
:amount (* 0.01 cents)}))))
|
||||
accounts (mapv
|
||||
(fn [a]
|
||||
(update a :invoice-expense-account/amount
|
||||
#(with-precision 2
|
||||
(double (.setScale (bigdec %) 2 java.math.RoundingMode/HALF_UP)))))
|
||||
accounts)
|
||||
leftover (with-precision 2 (.round (bigdec (- abs-total
|
||||
(reduce + 0.0 (map :invoice-expense-account/amount accounts))))
|
||||
*math-context*))
|
||||
accounts (if (seq accounts)
|
||||
(update-in accounts [(dec (count accounts)) :invoice-expense-account/amount] #(+ % (double leftover)))
|
||||
[])]
|
||||
(dissoc (assoc invoice :invoice/expense-accounts accounts) :line-items))))
|
||||
|
||||
(defn maybe-code-line-items [invoice]
|
||||
(if (and (seq (:line-items invoice))
|
||||
(= "Sysco" (-> (dc/pull (dc/db conn) [:vendor/name] (:invoice/vendor invoice))
|
||||
:vendor/name)))
|
||||
(code-invoices-list-items invoice)
|
||||
(dissoc invoice :line-items)))
|
||||
|
||||
(defn extract-invoice-details [csv-rows sysco-vendor]
|
||||
(let [[header-row & csv-rows] csv-rows
|
||||
header-row (into {} (map vector header-keys header-row))
|
||||
@@ -139,31 +183,31 @@
|
||||
:invoice-number (header-row "InvoiceNumber")
|
||||
:customer-name (header-row "CustomerName"))
|
||||
|
||||
(cond-> #:invoice {:invoice-number (header-row "InvoiceNumber")
|
||||
(cond-> #:invoice {:invoice-number (header-row "InvoiceNumber")
|
||||
:db/id (random-tempid)
|
||||
:total (+ total tax)
|
||||
:total (+ total tax)
|
||||
:outstanding-balance (+ total tax)
|
||||
:location (parse/best-location-match (dc/pull (dc/db conn)
|
||||
[{:client/location-matches [:location-match/location :location-match/matches]}
|
||||
:client/default-location
|
||||
:client/locations]
|
||||
(:db/id matching-client))
|
||||
location-hint
|
||||
location-hint)
|
||||
:date (coerce/to-date date)
|
||||
:vendor (:db/id sysco-vendor)
|
||||
:client (:db/id matching-client)
|
||||
:import-status :import-status/imported
|
||||
:status :invoice-status/unpaid
|
||||
:client-identifier customer-identifier}
|
||||
:location (parse/best-location-match (dc/pull (dc/db conn)
|
||||
[{:client/location-matches [:location-match/location :location-match/matches]}
|
||||
:client/default-location
|
||||
:client/locations]
|
||||
(:db/id matching-client))
|
||||
location-hint
|
||||
location-hint)
|
||||
:date (coerce/to-date date)
|
||||
:vendor (:db/id sysco-vendor)
|
||||
:client (:db/id matching-client)
|
||||
:import-status :import-status/imported
|
||||
:status :invoice-status/unpaid
|
||||
:client-identifier customer-identifier}
|
||||
true (code-invoice)
|
||||
code-items (code-individual-items csv-rows tax))))
|
||||
|
||||
(defn mark-key [k]
|
||||
(s3/copy-object {:source-bucket-name bucket-name
|
||||
(s3/copy-object {:source-bucket-name bucket-name
|
||||
:destination-bucket-name bucket-name
|
||||
:destination-key (str/replace-first k "pending" "imported")
|
||||
:source-key k})
|
||||
:destination-key (str/replace-first k "pending" "imported")
|
||||
:source-key k})
|
||||
(s3/delete-object {:bucket-name bucket-name
|
||||
:key k}))
|
||||
|
||||
@@ -180,7 +224,7 @@
|
||||
([] (get-test-invoice-file 999))
|
||||
([i]
|
||||
(nth (->> (s3/list-objects-v2 {:bucket-name "data.prod.app.integreatconsult.com"
|
||||
:prefix "sysco/imported"})
|
||||
:prefix "sysco/imported"})
|
||||
:object-summaries
|
||||
(map :key))
|
||||
i)))
|
||||
@@ -205,10 +249,10 @@
|
||||
|
||||
(defn import-sysco []
|
||||
(let [sysco-vendor (get-sysco-vendor)
|
||||
keys (->> (s3/list-objects-v2 {:bucket-name bucket-name
|
||||
:prefix "sysco/pending"})
|
||||
:object-summaries
|
||||
(map :key))]
|
||||
keys (->> (s3/list-objects-v2 {:bucket-name bucket-name
|
||||
:prefix "sysco/pending"})
|
||||
:object-summaries
|
||||
(map :key))]
|
||||
|
||||
(alog/info ::importing-sysco
|
||||
:count (count keys)
|
||||
@@ -219,10 +263,10 @@
|
||||
(try
|
||||
(let [invoice-key (str "invoice-files/" (UUID/randomUUID) ".csv") ;
|
||||
invoice-url (str "https://" (:data-bucket env) "/" invoice-key)]
|
||||
(s3/copy-object {:source-bucket-name (:data-bucket env)
|
||||
(s3/copy-object {:source-bucket-name (:data-bucket env)
|
||||
:destination-bucket-name (:data-bucket env)
|
||||
:source-key k
|
||||
:destination-key invoice-key})
|
||||
:source-key k
|
||||
:destination-key invoice-key})
|
||||
[[:propose-invoice
|
||||
(-> k
|
||||
read-sysco-csv
|
||||
@@ -232,13 +276,13 @@
|
||||
(alog/error ::cant-load-file
|
||||
:file k
|
||||
:error e e)
|
||||
(s3/copy-object {:source-bucket-name (:data-bucket env)
|
||||
(s3/copy-object {:source-bucket-name (:data-bucket env)
|
||||
:destination-bucket-name (:data-bucket env)
|
||||
:source-key k
|
||||
:destination-key (str "sysco/error/"
|
||||
(.getName (io/file k)))})
|
||||
:source-key k
|
||||
:destination-key (str "sysco/error/"
|
||||
(.getName (io/file k)))})
|
||||
[])))))
|
||||
result (audit-transact transaction {:user/name "sysco importer" :user/role "admin"})])
|
||||
result (audit-transact transaction {:user/name "sysco importer" :user/role "admin"})])
|
||||
(doseq [k keys]
|
||||
(mark-key k))))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user