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.]+)"} :total #"Total:\s+\$ ([0-9.]+)"}
:parser {:date [:clj-time "MM/dd/yy"]}} :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 ;; DAYLIGHT FOOD
{:vendor "Daylight Foods" {:vendor "Daylight Foods"
:keywords [#"DAYLIGHT FOODS"] :keywords [#"DAYLIGHT FOODS"]
:extract {:date #"\n\s*Date[^\n]+\n\s*([0-9]+/[0-9]+/[0-9]+)" :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\./]+)*" :invoice-number #"Invoice\s([\w\./]+)*"
:total #"Total Invoice\s+([0-9.]+)"} :total #"Total Invoice\s+([\-]?[0-9.]+)"}
:parser {:date [:clj-time "MM/dd/yy"]}} :parser {:date [:clj-time "MM/dd/yy"]
:total [:trim-commas-and-negate nil]}}
;; SOUTHBAY FRESH ;; SOUTHBAY FRESH
{:vendor "Southbay Fresh Produce" {:vendor "Southbay Fresh Produce"
@@ -105,6 +120,16 @@
:parser {:date [:clj-time "EEE MMM dd, yyyy HH:mm aa"] :parser {:date [:clj-time "EEE MMM dd, yyyy HH:mm aa"]
:total [:trim-commas nil]}} :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 ;; Del Monte Meats
{:vendor "Del Monte Meat Co" {:vendor "Del Monte Meat Co"
:keywords [#"Del Monte"] :keywords [#"Del Monte"]
@@ -226,11 +251,13 @@
:keywords [#"Suncrest.*Invoice"] :keywords [#"Suncrest.*Invoice"]
:extract {:date #"Date.*\n\s*\n(?:.*?)([0-9]+/[0-9]+/[0-9]+)" :extract {:date #"Date.*\n\s*\n(?:.*?)([0-9]+/[0-9]+/[0-9]+)"
:customer-identifier #"Bill To(?:.*?)\n\n(.*?)\s{2,}" :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})"} :total #"Balance Due\s+\$([0-9,]+\.[0-9]{2})"}
:parser {:date [:clj-time "MM/dd/yyyy"] :parser {:date [:clj-time "MM/dd/yyyy"]
:total [:trim-commas nil]}} :total [:trim-commas nil]}}
;; PACIFIC SEAFOOD ;; PACIFIC SEAFOOD
{:vendor "Pacific Seafood" {:vendor "Pacific Seafood"
:keywords [#"pacseafood"] :keywords [#"pacseafood"]
@@ -266,12 +293,12 @@
;; US FOODS ;; US FOODS
{:vendor "US Foods" {:vendor "US Foods"
:keywords [#"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,} :customer-identifier #"BILL TO[^\n]+\n([\S ]+?)(?=\s{2,})" ;; ([\S ]+)\s{2,}
:invoice-number #"INVOICE NUMBER[^\n]+\n\n\d+\s+(\d+)" :invoice-number #"INVOICE NUMBER[^\n]+\n\n\d+\s+(\d+)"
:total #"(?:DELIVERED AMOUNT|PLEASE REMIT).*(?=\$)\$([0-9.,]+)\s*\n"} :total #"(?:DELIVERED AMOUNT|PLEASE REMIT|AMOUNT).*?\$([0-9.,]+( CR)?)\n"}
:parser {:date [:clj-time "MM/dd/yyyy"] :parser {:date [:clj-time ["MM/dd/yyyy" "yyyy/MM/dd"]]
:total [:trim-commas nil]}} :total [:trim-commas-and-negate nil]}}
;; SYSCO ;; SYSCO
{:vendor "Sysco" {:vendor "Sysco"
@@ -387,7 +414,7 @@
:extract {:date #"([0-9]+/[0-9]+/[0-9]+)" :extract {:date #"([0-9]+/[0-9]+/[0-9]+)"
:customer-identifier #"SOLD\s+([\S ]+?)(?=(\s{2,}|\n))" :customer-identifier #"SOLD\s+([\S ]+?)(?=(\s{2,}|\n))"
:invoice-number #"(\S+)\s+(?=[0-9]+/[0-9]+/[0-9]+)" :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"] :parser {:date [:clj-time "MM/dd/yyyy"]
:total [:trim-commas-and-negate nil]}}]) :total [:trim-commas-and-negate nil]}}])

View File

@@ -27,8 +27,20 @@
(defmethod parse-value :clj-time (defmethod parse-value :clj-time
[_ format value] [_ format value]
(time/from-time-zone (f/parse (f/formatter format) value) (let [format (if (sequential? format)
(time/time-zone-for-id "America/Los_Angeles"))) 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 (defmethod parse-value nil
[_ _ value] [_ _ value]

View File

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

View File

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