Adding vendor import stuff.

This commit is contained in:
Bryce Covert
2018-05-10 17:32:22 -07:00
parent df3755d099
commit 798bfae78a
9 changed files with 173 additions and 32 deletions

View File

@@ -0,0 +1,3 @@
-- 1525464770 DOWN change-amount-to-number
alter table invoices drop column total;
alter table invoices add column total varchar(255);

View File

@@ -0,0 +1,3 @@
-- 1525464770 UP change-amount-to-number
alter table invoices drop column total;
alter table invoices add column total decimal;

View File

@@ -36,7 +36,10 @@
[:and [:and
[:= :exist.invoice-number :v.invoice-number] [:= :exist.invoice-number :v.invoice-number]
[:= :exist.company-id :v.company-id] [:= :exist.company-id :v.company-id]
[:= :exist.vendor-id :v.vendor-id]]] [:or [:= :exist.vendor-id :v.vendor-id]
[:and
[:= :exist.vendor-id nil]
[:= :v.vendor-id nil]]]]]
:where [:= :exist.id nil] }] }))))) :where [:= :exist.id nil] }] })))))
0 0
(partition-all 2000 rows)))) (partition-all 2000 rows))))

View File

@@ -40,6 +40,7 @@
(let [[id] (-> (sql/build :insert-into :vendors (let [[id] (-> (sql/build :insert-into :vendors
:values [(unparse data)]) :values [(unparse data)])
execute!)] execute!)]
(println "inserted vendor: " data ", id " id)
(get-by-id id))) (get-by-id id)))
(defn find-with-reminders [] (defn find-with-reminders []

View File

@@ -27,10 +27,16 @@
(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]} vendors] (defn parse-vendor [{:keys [vendor-name check]} vendors]
(if-let [id (:id (vendors vendor-name))] (let [id (:id (vendors vendor-name))]
(cond id
id id
(throw (Exception. (str "Vendor '" vendor-name "' not found.")))))
(= "Cash" check)
nil
:else
(throw (Exception. (str "Vendor '" vendor-name "' not found."))))))
(defn parse-amount [i] (defn parse-amount [i]
(try (try
@@ -127,6 +133,11 @@
(map (parse-or-error :total parse-amount)) (map (parse-or-error :total parse-amount))
(map (parse-or-error :date parse-date))) (map (parse-or-error :date parse-date)))
error-rows (filter :errors rows) 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) insert-rows (vec (->> (filter #(not (seq (:errors %))) rows)
(map (fn [{:keys [vendor-id total company-id amount date invoice-number]}] (map (fn [{:keys [vendor-id total company-id amount date invoice-number]}]
{:vendor-id vendor-id {:vendor-id vendor-id
@@ -138,13 +149,15 @@
:date date})))) :date date}))))
inserted-row-count (invoices/upsert-multi! insert-rows) inserted-row-count (invoices/upsert-multi! insert-rows)
already-imported-count (- (count insert-rows) inserted-row-count)] already-imported-count (- (count insert-rows) inserted-row-count)
]
{:status 200 {:status 200
:body (pr-str {:imported inserted-row-count :body (pr-str {:imported inserted-row-count
:already-imported already-imported-count :already-imported already-imported-count
:vendors-not-found vendors-not-found
:errors (map #(dissoc % :date) error-rows)}) :errors (map #(dissoc % :date) error-rows)})
:headers {"Content-Type" "application/edn"}})) :headers {"Content-Type" "application/edn"}}))

View File

@@ -5,7 +5,7 @@
[cljs-time.coerce :as c] [cljs-time.coerce :as c]
[cljs-time.core :as time] [cljs-time.core :as time]
[cljs-time.format :as format] [cljs-time.format :as format]
[cljs.core.async :refer [<!]] [cljs.core.async :refer [<! ] :as async]
[clojure.string :as str] [clojure.string :as str]
[clojure.walk :as walk] [clojure.walk :as walk]
[venia.core :as v] [venia.core :as v]
@@ -68,6 +68,31 @@
(conj on-success) (conj on-success)
(re-frame/dispatch))))))) (re-frame/dispatch)))))))
(re-frame/reg-fx
:https
(fn [{:keys [requests on-success on-failure]}]
(go
(let [results (->>
(for [{:keys [method body headers uri token]} requests]
(go
(let [headers (if token
(assoc headers "Authorization" (str "Token " token))
headers)
response (<! (http/request {:method method
:body body
:headers headers
:url uri}))]
(if (>= (:status response) 400)
:error
:success))))
(async/merge)
(async/reduce conj [])
(async/<!))]
(if (some #{:error} results)
(re-frame/dispatch on-failure)
(re-frame/dispatch on-success))))))
(defn kebab->snake [s] (defn kebab->snake [s]
(str/replace s #"-" "_")) (str/replace s #"-" "_"))

View File

@@ -6,7 +6,8 @@
[auto-ap.views.components.sorter :refer [sorted-column]] [auto-ap.views.components.sorter :refer [sorted-column]]
[reagent.core :as reagent] [reagent.core :as reagent]
[clojure.string :as str] [clojure.string :as str]
[cljs-time.format :as format])) [cljs-time.format :as format]
[goog.string :as gstring]))
@@ -23,7 +24,7 @@
(defn invoice-table [{:keys [id invoice-page status on-params-change vendors params]}] (defn invoice-table [{:keys [id invoice-page status on-params-change vendors params check-boxes on-check-changed]}]
(let [state (reagent/atom (or @params {})) (let [state (reagent/atom (or @params {}))
opc (fn [p] opc (fn [p]
(swap! state merge p) (swap! state merge p)
@@ -40,6 +41,8 @@
[:table.table.is-fullwidth [:table.table.is-fullwidth
[:thead [:thead
[:tr [:tr
(when check-boxes
[:th])
[sorted-column {:on-sort opc [sorted-column {:on-sort opc
:style {:width "25%" :cursor "pointer"} :style {:width "25%" :cursor "pointer"}
:sort-key "vendor" :sort-key "vendor"
@@ -78,8 +81,12 @@
(for [{:keys [company invoice-number date total id vendor] :as i} (:invoices @invoice-page)] (for [{:keys [company invoice-number date total id vendor] :as i} (:invoices @invoice-page)]
^{:key id} ^{:key id}
[:tr [:tr
(when check-boxes
[:td [:input.checkbox {:type "checkbox" :on-change (fn [x e] (when on-check-changed
(on-check-changed id)))} ]])
[:td (:name vendor)] [:td (:name vendor)]
[:td (:name company)] [:td (:name company)]
[:td invoice-number] [:td invoice-number]
[:td (date->str date) ] [:td (date->str date) ]
[:td total]]))]]]))))
[:td (gstring/format "$%.2f" total )]]))]]]))))

View File

@@ -59,11 +59,79 @@
(assoc-in [::excel-import :rows] nil) (assoc-in [::excel-import :rows] nil)
(assoc-in [::excel-import :saving?] false))})) (assoc-in [::excel-import :saving?] false))}))
(re-frame/reg-event-db
::toggle-vendor
(fn [db [_ data]]
(update-in db [::excel-import :create-vendors] (fn [x]
(let [x (or x #{})]
(if (x data)
(disj x data)
(conj x data)))))))
(re-frame/reg-event-fx
::create-vendors
(fn [{:keys [db]}]
(let [excel-import (::excel-import db)]
{:https {:requests (map (fn [v]
{:token (:user db)
:method :post
:body (pr-str {:name v})
:headers {"Content-Type" "application/edn"}
:uri (str "/api/vendors/")})
(get-in db [::excel-import :create-vendors]))
:on-success [::create-vendor-complete]
:on-error [::create-vendor-error]}
:db (-> db
(assoc-in [::excel-import :saving-vendors?] true))})))
(re-frame/reg-event-db
::create-vendor-complete
(fn [db [_ data]]
(-> db
(update-in [::excel-import :rows :vendors-not-found]
(fn [v]
(reduce disj v (get-in db [::excel-import :create-vendors]))))
(update-in [::excel-import] dissoc :create-vendors))))
(defn admin-excel-import-page [] (defn admin-excel-import-page []
[:div [:div
(let [excel-import-data @(re-frame/subscribe [::excel-import])] (let [{{:keys [vendors-not-found already-imported imported]} :rows
:keys [create-vendors]
:or {create-vendors #{}}
:as excel-import-data} @(re-frame/subscribe [::excel-import])]
[:div [:div
[:h1.title "Import Invoices from Integreat Excel"] [:h1.title "Import Invoices from Integreat Excel"]
(when (seq vendors-not-found)
[:article.message.is-warning.is-paddingless
[:div.message-header
"Some vendors could not be found"]
[:div.message-body
[:h2 "Check the vendors you want to create"]
[:div.columns
(for [[i vendor-group] (map vector (range) (partition-all (max 1 (/ (count vendors-not-found) 3)) vendors-not-found))]
^{:key i}
[:div.column
(for [v vendor-group]
^{:key v} [:div.control
[:label.checkbox
[:input {:value v
:checked (if (create-vendors v)
"checked"
"")
:type "checkbox"
:on-change (fn []
(re-frame/dispatch [::toggle-vendor v]))}]
(str " " v)]])])]
[:div
[:button.button.is-pulled-right
{:on-click (dispatch-event [::create-vendors])
:disabled (when-not (seq create-vendors)
"disabled")
}
(str "Create " (count create-vendors) " vendors")]]
[:div.is-clearfix]]])
[bind-field [bind-field
[:textarea.textarea {:rows "20" [:textarea.textarea {:rows "20"
:field :excel-rows :field :excel-rows
@@ -76,17 +144,17 @@
"is-loading") "is-loading")
:disabled (when (:saving? excel-import-data) "disabled")} "Import"] :disabled (when (:saving? excel-import-data) "disabled")} "Import"]
[:div.is-clearfix]
[:div.is-clearfix [:div.is-clearfix
[:p [:p
(when-let [imported (:imported (:rows excel-import-data))] (when imported
(str imported " rows imported."))] (str imported " rows imported."))]
[:p [:p
(when-let [already-imported (:already-imported (:rows excel-import-data))] (when already-imported
(str already-imported " rows already imported."))]] (str already-imported " rows already imported."))]]
(when-let [errors (:errors (:rows excel-import-data))] (when-let [errors (:errors (:rows excel-import-data))]
[:div [:div
[:h3 (str "Import errors (" (min 100 (count errors)) ")")] [:h3 (str "Import errors (" (min 100 (count errors)) " / " (count errors) " )")]
[:table.table.is-fullwidth [:table.table.is-fullwidth
[:thead [:thead
[:th "Date"] [:th "Date"]
@@ -95,11 +163,13 @@
[:th "Vendor"] [:th "Vendor"]
[:th "Amount"] [:th "Amount"]
[:th "Errors"]] [:th "Errors"]]
(for [{:keys [raw-date invoice-number company vendor-name amount] row-errors :errors} (take 100 errors)] (for [{:keys [raw-date invoice-number company vendor-name amount] row-errors :errors} (take 100 errors)]
^{:key (str raw-date invoice-number company vendor-name amount)}
[:tr [:tr
[:td raw-date] [:td raw-date]
[:td invoice-number] [:td invoice-number]
[:td company] [:td company]
[:td vendor-name] [:td vendor-name]
[:td amount] [:td amount]
[:td (map #(vector :p (:info %)) row-errors)]])]])])]) [:td (map (fn [{:keys [info]}] ^{:key info} [:p info]) row-errors)]])]])])])

View File

@@ -34,6 +34,16 @@
(assoc ::invoice-page (first (:invoice-page data))) (assoc ::invoice-page (first (:invoice-page data)))
(assoc-in [:status :loading] false)))) (assoc-in [:status :loading] false))))
(re-frame/reg-event-db
::toggle-check
(fn [db [_ data]]
(update-in db [::invoice-page :checked] (fn [x]
(let [x (or x #{})]
(if (x data)
(disj x data)
(conj x data)))))))
(re-frame/reg-event-fx (re-frame/reg-event-fx
::invalidated ::invalidated
(fn [cofx [_ params]] (fn [cofx [_ params]]
@@ -44,10 +54,16 @@
(fn [] (fn []
[:div [:div
[:h1.title "Unpaid invoices"] [:h1.title "Unpaid invoices"]
[:div.is-pulled-right
[:button.button.is-primary "Print check(s)"]]
[invoice-table {:id :unpaid [invoice-table {:id :unpaid
:params (re-frame/subscribe [::params]) :params (re-frame/subscribe [::params])
:invoice-page (re-frame/subscribe [::invoice-page]) :invoice-page (re-frame/subscribe [::invoice-page])
:status (re-frame/subscribe [::subs/status]) :status (re-frame/subscribe [::subs/status])
:on-params-change (fn [params] :on-params-change (fn [params]
(re-frame/dispatch [::params-change params])) }]]) (re-frame/dispatch [::params-change params]))
:check-boxes true
:on-check-changed (fn [which]
(re-frame/dispatch [::toggle-check which]))}]])
{:component-will-mount #(re-frame/dispatch-sync [::params-change {}]) })) {:component-will-mount #(re-frame/dispatch-sync [::params-change {}]) }))