170 lines
7.8 KiB
Clojure
170 lines
7.8 KiB
Clojure
(ns auto-ap.routes.invoices
|
|
(:require [auto-ap.db.companies :as companies]
|
|
[auto-ap.db.vendors :as vendors]
|
|
[auto-ap.db.invoices :as invoices]
|
|
[auto-ap.db.utils :refer [query]]
|
|
[auto-ap.parse :as parse]
|
|
[auto-ap.routes.utils :refer [wrap-secure]]
|
|
[compojure.core :refer [GET POST context defroutes
|
|
wrap-routes]]
|
|
[clojure.string :as str]))
|
|
|
|
(defn reset-id [i]
|
|
(update i :invoice-number
|
|
(fn [n] (if (re-matches #"#+" n)
|
|
nil
|
|
n))))
|
|
|
|
(defn assoc-company-code [i]
|
|
(assoc i :company-code (first (str/split (:location i) #"-" ))))
|
|
|
|
(defn parse-company [{:keys [company-code company]} companies]
|
|
(if-let [id (:id (or (companies company-code)
|
|
(companies company)))]
|
|
id
|
|
(throw (Exception. (str "Company code '" company-code "' and company named '" company "' not found.")))))
|
|
|
|
(defn parse-invoice-number [{:keys [invoice-number]}]
|
|
(or invoice-number ""))
|
|
|
|
(defn parse-vendor [{:keys [vendor-name check]} vendors]
|
|
(let [id (:id (vendors vendor-name))]
|
|
(cond id
|
|
id
|
|
|
|
(= "Cash" check)
|
|
nil
|
|
|
|
:else
|
|
(throw (Exception. (str "Vendor '" vendor-name "' not found."))))))
|
|
|
|
(defn parse-amount [i]
|
|
(try
|
|
(Double/parseDouble (str/replace (or (second
|
|
(re-matches #"[^0-9\.,]*([0-9\.,]+)[^0-9\.,]*" (:amount i)))
|
|
"0")
|
|
#"," ""))
|
|
(catch Exception e
|
|
(throw (Exception. (str "Could not parse total from value '" (:amount i) "'") e)))))
|
|
|
|
(defn parse-date [{:keys [raw-date]}]
|
|
(try
|
|
(parse/parse-value :clj-time "MM/dd/yyyy" raw-date)
|
|
(catch Exception e
|
|
(throw (Exception. (str "Could not parse date from '" raw-date "'") e)))))
|
|
|
|
(defn parse-or-error [key f]
|
|
(fn [x]
|
|
(try
|
|
(assoc x key (f x))
|
|
(catch Exception e
|
|
(update x :errors conj {:info (.getMessage e)
|
|
:details (str e)})))))
|
|
|
|
(defroutes routes
|
|
(wrap-routes
|
|
(context "/invoices" []
|
|
(GET "/" []
|
|
{:status 200
|
|
:body (pr-str (invoices/get-all))
|
|
:headers {"Content-Type" "application/edn"}})
|
|
|
|
(GET "/unpaid" {:keys [query-params] :as r}
|
|
{:status 200
|
|
:body (pr-str (invoices/get-unpaid (query-params "company")))
|
|
:headers {"Content-Type" "application/edn"}})
|
|
|
|
(GET "/pending" {:keys [query-params]}
|
|
{:status 200
|
|
:body (pr-str (invoices/get-pending (query-params "company")))
|
|
:headers {"Content-Type" "application/edn"}})
|
|
(POST "/" {:keys [edn-params]}
|
|
(invoices/insert-multi! (:rows edn-params))
|
|
{:status 200
|
|
:body (pr-str (invoices/get-all))
|
|
:headers {"Content-Type" "application/edn"}})
|
|
(POST "/approve" {:keys [query-params]}
|
|
(invoices/approve)
|
|
{:status 200
|
|
:body (pr-str (invoices/get-pending (query-params "company")))
|
|
:headers {"Content-Type" "application/edn"}})
|
|
(POST "/reject" {:keys [query-params]}
|
|
(invoices/reject)
|
|
{:status 200
|
|
:body (pr-str (invoices/get-pending (query-params "company")))
|
|
:headers {"Content-Type" "application/edn"}})
|
|
(POST "/upload"
|
|
{{ files "file"} :params :as params}
|
|
(let [{:keys [filename tempfile]} files
|
|
companies (companies/get-all)
|
|
vendors (vendors/get-all)]
|
|
(invoices/import (parse/parse-file (.getPath tempfile) filename) companies vendors)
|
|
{:status 200
|
|
:body (pr-str (invoices/get-pending ((:query-params params ) "company")))
|
|
:headers {"Content-Type" "application/edn"}}))
|
|
|
|
(POST "/upload-integreat"
|
|
{{:keys [excel-rows]} :edn-params}
|
|
(let [columns [:raw-date :vendor-name :check :location :invoice-number :amount :company :bill-entered :bill-rejected :added-on :exported-on]
|
|
|
|
all-vendors (reduce
|
|
(fn [x y]
|
|
(assoc x (:name y) y))
|
|
{}
|
|
(vendors/get-all))
|
|
|
|
all-companies (reduce
|
|
(fn [x y]
|
|
(-> x
|
|
(assoc (:code y) y)
|
|
(assoc (:name y) y)))
|
|
{}
|
|
(companies/get-all))
|
|
|
|
|
|
rows (->> (str/split excel-rows #"\n" )
|
|
(map #(str/split % #"\t"))
|
|
(map #(into {} (map (fn [c k] [k c] ) % columns)))
|
|
(map reset-id)
|
|
(map assoc-company-code)
|
|
(map (parse-or-error :company-id #(parse-company % all-companies)))
|
|
(map (parse-or-error :vendor-id #(parse-vendor % all-vendors)))
|
|
(map (parse-or-error :invoice-number parse-invoice-number))
|
|
(map (parse-or-error :total parse-amount))
|
|
(map (parse-or-error :date parse-date)))
|
|
error-rows (filter :errors rows)
|
|
vendors-not-found (->> rows
|
|
(filter #(and (nil? (:vendor-id %))
|
|
(not= "Cash" (:check %))))
|
|
(map :vendor-name)
|
|
set)
|
|
insert-rows (vec (->> (filter #(not (seq (:errors %))) rows)
|
|
(map (fn [{:keys [vendor-id total company-id amount date invoice-number]}]
|
|
{:vendor-id vendor-id
|
|
:company-id company-id
|
|
:total total
|
|
:imported true
|
|
:status "unpaid"
|
|
:invoice-number invoice-number
|
|
:date date}))))
|
|
|
|
inserted-row-count (invoices/upsert-multi! insert-rows)
|
|
already-imported-count (- (count insert-rows) inserted-row-count)
|
|
]
|
|
|
|
|
|
|
|
{:status 200
|
|
:body (pr-str {:imported inserted-row-count
|
|
:already-imported already-imported-count
|
|
:vendors-not-found vendors-not-found
|
|
:errors (map #(dissoc % :date) error-rows)})
|
|
:headers {"Content-Type" "application/edn"}}))
|
|
|
|
;; Removing the export view for now...
|
|
#_(wrap-json-response (GET "/export" {:keys [query-params]}
|
|
(println query-params)
|
|
(doto (invoices/get-unpaid (query-params "company"))
|
|
println))))
|
|
wrap-secure))
|