several fixes for invoices.

This commit is contained in:
Bryce Covert
2020-05-26 07:39:33 -07:00
parent 4429fa384a
commit f78133cbea
4 changed files with 101 additions and 57 deletions

View File

@@ -55,14 +55,29 @@
:total #"Total:\s+\$ ([0-9.]+)"}
:parser {:date [:clj-time "MM/dd/yy"]}}
;; DAYLIGHT FOOD STATEMENT
{:vendor "Daylight Foods"
:keywords [#"DAYLIGHT FOODS" #"Customer Statement"]
:extract {:date #"^.*?([0-9]+/[0-9]+/[0-9]+)"
:customer-identifier #"Phone:.*\n+\s+(.*)"
:invoice-number #"\s+(\w+)"
:total #"([\-]?[0-9]+\.[0-9]+)"}
:parser {:date [:clj-time "MM/dd/yy"]
:total [:trim-commas-and-negate nil]}
:multi #"\n"
:multi-match? #"^\s*[A-Z]\d+\s+([0-9]+/[0-9]+/[0-9]+)"}
;; DAYLIGHT FOOD
{:vendor "Daylight Foods"
:keywords [#"DAYLIGHT FOODS"]
:extract {:date #"\n\s*Date[^\n]+\n\s*([0-9]+/[0-9]+/[0-9]+)"
:customer-identifier #"Bill To:[^\n]+\n\s*([\w ]+)"
:customer-identifier #"Bill To:.*\n\s*(.*?)\s{2,}"
:invoice-number #"Invoice\s([\w\./]+)*"
:total #"Total Invoice\s+([0-9.]+)"}
:parser {:date [:clj-time "MM/dd/yy"]}}
:total #"Total Invoice\s+([\-]?[0-9.]+)"}
:parser {:date [:clj-time "MM/dd/yy"]
:total [:trim-commas-and-negate nil]}}
;; SOUTHBAY FRESH
{:vendor "Southbay Fresh Produce"
@@ -105,6 +120,16 @@
:parser {:date [:clj-time "EEE MMM dd, yyyy HH:mm aa"]
:total [:trim-commas nil]}}
;; GOLDEN BRANDS
{:vendor "Bigoli Fresh Pasta"
:keywords [#"bigolifreshpasta.com"]
:extract {:date #"INVOICE #.*?\n.*?([0-9]+/[0-9]+/[0-9]+)" ;; HOW TO GO TO SPCIFIC LINE
:customer-identifier #"BILL TO.*\n\s+(.*?)\s{2,}"
:invoice-number #"INVOICE #.*?\n(\d+)"
:total #" TOTAL\s+([0-9,]+\.[0-9]{2})"}
:parser {:date [:clj-time "MM/dd/yyyy"]
:total [:trim-commas nil]}}
;; Del Monte Meats
{:vendor "Del Monte Meat Co"
:keywords [#"Del Monte"]
@@ -226,11 +251,13 @@
:keywords [#"Suncrest.*Invoice"]
:extract {:date #"Date.*\n\s*\n(?:.*?)([0-9]+/[0-9]+/[0-9]+)"
:customer-identifier #"Bill To(?:.*?)\n\n(.*?)\s{2,}"
:invoice-number #"Invoice #.*\n\s*\n(?:.*?)\s{2,}(\d{5,})"
:invoice-number #"Invoice #.*\n\s*\n.*? (\d{3,})"
:total #"Balance Due\s+\$([0-9,]+\.[0-9]{2})"}
:parser {:date [:clj-time "MM/dd/yyyy"]
:total [:trim-commas nil]}}
;; PACIFIC SEAFOOD
{:vendor "Pacific Seafood"
:keywords [#"pacseafood"]
@@ -266,12 +293,12 @@
;; US FOODS
{:vendor "US Foods"
:keywords [#"US Foods"]
:extract {:date #"INVOICE NUMBER[^\n]+\n\n\d+\s+\d+\s+([0-9]+/[0-9]+/[0-9]+)"
:extract {:date #"INVOICE DATE.*\n+.*?(?=([0-9]+/[0-9]+/[0-9]+))([0-9]+/[0-9]+/[0-9]+)"
:customer-identifier #"BILL TO[^\n]+\n([\S ]+?)(?=\s{2,})" ;; ([\S ]+)\s{2,}
:invoice-number #"INVOICE NUMBER[^\n]+\n\n\d+\s+(\d+)"
:total #"(?:DELIVERED AMOUNT|PLEASE REMIT).*(?=\$)\$([0-9.,]+)\s*\n"}
:parser {:date [:clj-time "MM/dd/yyyy"]
:total [:trim-commas nil]}}
:total #"(?:DELIVERED AMOUNT|PLEASE REMIT|AMOUNT).*?\$([0-9.,]+( CR)?)\n"}
:parser {:date [:clj-time ["MM/dd/yyyy" "yyyy/MM/dd"]]
:total [:trim-commas-and-negate nil]}}
;; SYSCO
{:vendor "Sysco"
@@ -387,7 +414,7 @@
:extract {:date #"([0-9]+/[0-9]+/[0-9]+)"
:customer-identifier #"SOLD\s+([\S ]+?)(?=(\s{2,}|\n))"
:invoice-number #"(\S+)\s+(?=[0-9]+/[0-9]+/[0-9]+)"
:total #"(?:INVOICE|TOTAL)\s+([\d\.,\-]+\.[\d\-]+( CR)?)"}
:total #"(?:INVOICE|TOTAL|CREDIT)\s+([\d\.,\-]+\.[\d\-]+( CR)?)"}
:parser {:date [:clj-time "MM/dd/yyyy"]
:total [:trim-commas-and-negate nil]}}])

View File

@@ -27,8 +27,20 @@
(defmethod parse-value :clj-time
[_ format value]
(time/from-time-zone (f/parse (f/formatter format) value)
(time/time-zone-for-id "America/Los_Angeles")))
(let [format (if (sequential? format)
format
[format])]
(reduce
(fn [_ format]
(try
(reduced (time/from-time-zone (f/parse (f/formatter format) value)
(time/time-zone-for-id "America/Los_Angeles")))
(catch Exception e
(println e)
nil)))
nil
format)
))
(defmethod parse-value nil
[_ _ value]

View File

@@ -207,7 +207,7 @@
(= (:db/id c) (Long/parseLong client)))
clients))))
_ (when-not matching-client
(throw (ex-info (str "No client found in file. Select a client first.")
(throw (ex-info (str "Searched clients for '" customer-identifier "'. No client found in file. Select a client first.")
{:invoice-number invoice-number
:customer-identifier customer-identifier
:vendor-code vendor-code})))
@@ -237,7 +237,9 @@
(throw (ex-info (str "Failed to find potential matching invoice with"
" invoice " invoice-number
" vendor " matching-vendor
" client " (:client/name matching-client))
" client " (:client/name matching-client)
". "
(.toString e))
{:args [ invoice-number matching-vendor (:db/id matching-client)]})))
))]
@@ -270,7 +272,7 @@
imports)]
(when-not (seq transactions)
(throw (ex-info "No invoices found."
{:imports imports})))
{:imports (str imports)})))
@(d/transact (d/connect uri) (vec (set transactions)))))

View File

@@ -15,50 +15,52 @@
[cljsjs.dropzone :as dropzone]
[cljs.reader :as edn]
[clojure.string :as str]))
(def dropzone
(defn dropzone []
(let [client (re-frame/subscribe [::subs/client])
token (re-frame/subscribe [::subs/token])
vendor (reagent/atom nil)]
(with-meta
(fn []
[:form.dz {:action "/api/invoices/upload"}
[:div.field.has-addons
[:p.control
[:a.button.is-static "Force Location"]]
[:p.control
[:input.input {:name "location" :placeholder "SG" :size "4" :maxlength "2" :style {:display "inline"}}]]]
[:div.field.has-addons
[:p.control
[:a.button.is-static "Force vendor"]]
[typeahead-entity {:matches @(re-frame/subscribe [::subs/vendors])
:match->text :name
:name "vendor"
:type "typeahead"
:on-change (fn [v]
(reset! vendor v))
:value @vendor}]
]
[:div.tile.notification
[:div.has-text-centered {:style {:padding "80px 0px" :width "100%"}}
[:span
[:span {:class "icon"}
[:i {:class "fa fa-cloud-download"}]]
"Drop any invoices you want to process here"]]]])
{:component-did-mount (fn [this]
(js/Dropzone. (rdom/dom-node this)
(clj->js {:init (fn []
(.on (js-this) "success" (fn [_ files]
(re-frame/dispatch [::invalidated])))
(.on (js-this) "error" (fn [_ error]
(re-frame/dispatch [::errored error]))))
:paramName "file"
:headers {"Authorization" (str "Token " @token)}
:url (str "/api/invoices/upload"
(when-let [client-name (-> @client :id)]
(str "?client=" client-name)))
:previewsContainer "#dz-hidden"
:previewTemplate "<div class='dz-hidden-preview'></div>"})))})))
(reagent/create-class
{:display-name "dropzone"
:component-did-mount (fn [this]
(js/Dropzone. (rdom/dom-node this)
(clj->js {:init (fn []
(.on (js-this) "success" (fn [_ files]
(re-frame/dispatch [::invalidated])))
(.on (js-this) "error" (fn [_ error]
(re-frame/dispatch [::errored error]))))
:paramName "file"
:headers {"Authorization" (str "Token " @token)}
:url (str "/api/invoices/upload"
(when-let [client-name (-> @client :id)]
(str "?client=" client-name)))
:previewsContainer "#dz-hidden"
:previewTemplate "<div class='dz-hidden-preview'></div>"})))
:reagent-render (fn []
[:form.dz {:action "/api/invoices/upload"}
[:div.field.has-addons
[:p.control
[:a.button.is-static "Force Location"]]
[:p.control
[:input.input {:name "location" :placeholder "SG" :size "4" :maxlength "2" :style {:display "inline"}}]]]
[:div.field.has-addons
[:p.control
[:a.button.is-static "Force vendor"]]
[typeahead-entity {:matches @(re-frame/subscribe [::subs/vendors])
:match->text :name
:name "vendor"
:type "typeahead"
:on-change (fn [v]
(reset! vendor v))
:value @vendor}]
]
[:div.tile.notification
[:div.has-text-centered {:style {:padding "80px 0px" :width "100%"}}
[:span
[:span {:class "icon"}
[:i {:class "fa fa-cloud-download"}]]
"Drop any invoices you want to process here"]]]])})
))
(re-frame/reg-sub
@@ -191,12 +193,13 @@
(fn []
(let [invoice-page (re-frame/subscribe [::invoice-page])
status (re-frame/subscribe [::subs/status])
error (re-frame/subscribe [::error])]
error (re-frame/subscribe [::error])
client (:id @(re-frame/subscribe [::subs/client]))]
^{:key client}
[:div
[:h1.title "Upload invoices"]
[dropzone]
[:div {:class "section"}
(when @error