bulk upload a little better.

This commit is contained in:
Bryce Covert
2018-11-09 23:28:56 -08:00
parent e15245690d
commit 77d7b99ebb

View File

@@ -1,17 +1,15 @@
(ns auto-ap.routes.invoices (ns auto-ap.routes.invoices
(:require [auto-ap.db.companies :as companies] (:require [auto-ap.datomic.clients :as d-clients]
[auto-ap.db.vendors :as vendors] [auto-ap.datomic.vendors :as d-vendors]
[auto-ap.db.invoices :as invoices] [auto-ap.datomic.invoices :as d-invoices]
[auto-ap.db.invoices-checks :as invoices-checks]
[auto-ap.db.checks :as checks]
[auto-ap.db.utils :refer [query]]
[auto-ap.yodlee.import :refer [manual-import]] [auto-ap.yodlee.import :refer [manual-import]]
[auto-ap.utils :refer [by]] [auto-ap.utils :refer [by]]
[auto-ap.datomic :refer [remove-nils uri]]
[datomic.api :as d]
[auto-ap.parse :as parse] [auto-ap.parse :as parse]
[auto-ap.graphql.utils :refer [assert-admin]] [auto-ap.graphql.utils :refer [assert-admin]]
[auto-ap.routes.utils :refer [wrap-secure]] [auto-ap.routes.utils :refer [wrap-secure]]
[clj-time.coerce :refer [to-date]] [clj-time.coerce :refer [to-date]]
[auto-ap.db.invoices-expense-accounts :as expense-accounts]
[ring.middleware.json :refer [wrap-json-response]] [ring.middleware.json :refer [wrap-json-response]]
[compojure.core :refer [GET POST context defroutes [compojure.core :refer [GET POST context defroutes
wrap-routes]] wrap-routes]]
@@ -23,24 +21,24 @@
nil nil
n)))) n))))
(defn assoc-company-code [i] (defn assoc-client-code [i]
(-> i (-> i
(assoc :company-code (first (str/split (:location i) #"-" ))) (assoc :client-code (first (str/split (:location i) #"-" )))
(assoc :default-location (second (str/split (:location i) #"-" ))))) (assoc :default-location (second (str/split (:location i) #"-" )))))
(defn parse-company [{:keys [company-code company]} companies] (defn parse-client [{:keys [client-code client]} clients]
(if-let [id (:id (or (companies company-code) (if-let [id (:db/id (or (clients client-code)
(companies company)))] (clients client)))]
id id
(throw (Exception. (str "Company code '" company-code "' and company named '" company "' not found."))))) (throw (Exception. (str "Company code '" client-code "' and company named '" client "' not found.")))))
(defn parse-invoice-number [{:keys [invoice-number]}] (defn parse-invoice-number [{:keys [invoice-number]}]
(or invoice-number "")) (or invoice-number ""))
(defn parse-vendor [{:keys [vendor-name check]} vendors] (defn parse-vendor [{:keys [vendor-name check]} vendors]
(let [id (:id (vendors vendor-name))] (let [v (vendors vendor-name)]
(cond id (cond v
id v
(= "Cash" check) (= "Cash" check)
nil nil
@@ -48,6 +46,9 @@
:else :else
(throw (Exception. (str "Vendor '" vendor-name "' not found.")))))) (throw (Exception. (str "Vendor '" vendor-name "' not found."))))))
(defn parse-vendor-id [{:keys [vendor]}]
(:db/id vendor))
(defn parse-amount [i] (defn parse-amount [i]
(try (try
(Double/parseDouble (str/replace (or (second (Double/parseDouble (str/replace (or (second
@@ -144,16 +145,17 @@
{{:keys [excel-rows]} :edn-params user :identity} {{:keys [excel-rows]} :edn-params user :identity}
(assert-admin user) (assert-admin user)
(let [columns [:raw-date :vendor-name :check :location :invoice-number :amount :company :bill-entered :bill-rejected :added-on :exported-on :default-expense-account] (let [columns [:raw-date :vendor-name :check :location :invoice-number :amount :company :bill-entered :bill-rejected :added-on :exported-on :default-expense-account]
all-vendors (by :name (vendors/get-all)) all-vendors (by :vendor/name (d-vendors/get-graphql {}))
all-companies (companies/get-all) all-clients (d-clients/get-all)
all-companies (merge (by :code all-companies) (by :name all-companies)) all-clients (merge (by :client/code all-clients) (by :client/name all-clients))
rows (->> (str/split excel-rows #"\n" ) rows (->> (str/split excel-rows #"\n" )
(map #(str/split % #"\t")) (map #(str/split % #"\t"))
(map #(into {} (map (fn [c k] [k c] ) % columns))) (map #(into {} (map (fn [c k] [k c] ) % columns)))
(map reset-id) (map reset-id)
(map assoc-company-code) (map assoc-client-code)
(map (parse-or-error :company-id #(parse-company % all-companies))) (map (parse-or-error :client-id #(parse-client % all-clients)))
(map (parse-or-error :vendor-id #(parse-vendor % all-vendors))) (map (parse-or-error :vendor #(parse-vendor % all-vendors)))
(map (parse-or-error :vendor-id #(parse-vendor-id %)))
(map (parse-or-error :default-expense-account parse-default-expense-account)) (map (parse-or-error :default-expense-account parse-default-expense-account))
(map (parse-or-error :invoice-number parse-invoice-number)) (map (parse-or-error :invoice-number parse-invoice-number))
(map (parse-or-error :total parse-amount)) (map (parse-or-error :total parse-amount))
@@ -164,43 +166,54 @@
(not= "Cash" (:check %)))) (not= "Cash" (:check %))))
(map :vendor-name) (map :vendor-name)
set) set)
insert-rows (vec (->> (filter #(not (seq (:errors %))) rows) get-dupe-keys (fn [i]
(map (fn [{:keys [vendor-id total company-id amount date invoice-number default-location default-expense-account check]}] (select-keys i [:invoice/vendor :invoice/client :invoice/invoice-number]))
{:vendor-id vendor-id existing-rows (->> (d-invoices/raw-graphql {})
:company-id company-id (filter (fn [i] (not= :invoice-status/voided (:invoice/status i))))
:default-location default-location (map (fn [i] (-> i
:default-expense-account default-expense-account (update :invoice/client :db/id)
:total total (update :invoice/vendor :db/id))))
:outstanding-balance (if (= "Cash" check) (map get-dupe-keys)
0 set)
total) total-rows (->> (filter #(not (seq (:errors %))) rows)
:imported true (map (fn [{:keys [vendor-id total client-id amount date invoice-number default-location default-expense-account check vendor]}]
:status (if (= "Cash" check) #:invoice {:db/id (.toString (java.util.UUID/randomUUID))
"paid" :vendor vendor-id
"unpaid") :client client-id
:invoice-number invoice-number :default-location default-location
:date date})))) :default-expense-account default-expense-account
:total total
inserted-rows (invoices/upsert-multi! insert-rows) :outstanding-balance (if (= "Cash" check)
already-imported-count (- (count insert-rows) (count inserted-rows))] 0.0
(doseq [inserted-row inserted-rows total)
:when (= "paid" (:status inserted-row))] :status (if (= "Cash" check)
(let [inserted-check (checks/insert! {:vendor-id (:vendor-id inserted-row) :invoice-status/paid
:company-id (:company-id inserted-row) :invoice-status/unpaid)
:bank-account-id 0 :invoice-number invoice-number
:type "cash" :date (to-date date)
:amount (:total inserted-row) :expense-accounts [#:invoice-expense-account {:expense-account-id (or default-expense-account (:vendor/default-expense-account vendor))
:status "cleared" :location default-location
:date (:date inserted-row)})] :amount total}]})))
(invoices-checks/insert-multi! [{:amount (:total inserted-row) insert-rows (vec (->> total-rows
:invoice-id (:id inserted-row) (filter #(not (existing-rows (get-dupe-keys %) )))
:check-id (:id inserted-check)}]))) (mapcat (fn [i]
(expense-accounts/assign-defaults!) (if (= :invoice-status/paid (:invoice/status i))
[i #:invoice-payment {:invoice (:db/id i)
:amount (:invoice/total i)
:payment (remove-nils #:payment {:db/id (.toString (java.util.UUID/randomUUID))
:vendor (:invoice/vendor i)
:client (:invoice/client i)
:type :payment-type/cash
:amount (:invoice/total i)
:status :payment-status/cleared
:date (:invoice/date i)})}]
[i])))
(map remove-nils)))
inserted-rows @(d/transact (d/connect uri) insert-rows)
already-imported-count (- (count total-rows) (count insert-rows))]
{:status 200 {:status 200
:body (pr-str {:imported (count inserted-rows) :body (pr-str {:imported (count total-rows)
:already-imported already-imported-count :already-imported already-imported-count
:vendors-not-found vendors-not-found :vendors-not-found vendors-not-found
:errors (map #(dissoc % :date) error-rows)}) :errors (map #(dissoc % :date) error-rows)})