(ns auto-ap.routes.ezcater-xls (:require [auto-ap.datomic :refer [audit-transact conn]] [auto-ap.logging :as alog] [clojure.data.json :as json] [auto-ap.parse.excel :as excel] [auto-ap.parse :as parse] [amazonica.aws.lambda :as lambda] [config.core :refer [env]] [auto-ap.shared-views.admin.side-bar :refer [admin-side-bar]] [auto-ap.ssr-routes :as ssr-routes] [auto-ap.ssr.ui :refer [base-page]] [auto-ap.ssr.utils :refer [html-response]] [auto-ap.time :as atime] [bidi.bidi :as bidi] [clj-time.coerce :as coerce] [clojure.java.io :as io] [com.brunobonacci.mulog :as mu] [datomic.api :as dc] [hiccup2.core :as hiccup] [amazonica.aws.s3 :as s3] [auto-ap.ssr.components :as com])) (defn fmt-amount [a] (with-precision 2 (some-> a bigdec (.setScale 2 java.math.RoundingMode/HALF_UP) (double)))) (defn rows->maps [rows] (let [[headers & rows] rows] (for [r rows] (into {} (map vector headers r))))) (defn map->sales-order [r clients] (println r) (let [order-number (get r "Order Number") event-date (get r "Event Date") store-name (get r "Store Name") adjustments (some-> (get r "Adjustments") not-empty (Double/parseDouble)) tax (some-> (get r "Sales Tax") not-empty (Double/parseDouble)) food-total (some-> (get r "Food Total") not-empty (Double/parseDouble)) commission (some-> (get r "Commission") not-empty (Double/parseDouble)) fee (some-> (get r "Payment Transaction Fee") not-empty (Double/parseDouble)) tip (some-> (get r "Tip") not-empty (Double/parseDouble)) caterer-name (get r "Caterer Name") client (some->> caterer-name not-empty (parse/exact-match clients)) client-id (:db/id client) location (first (:client/locations client)) event-date (some-> (excel/xls-date->date event-date) coerce/to-date-time atime/as-local-time coerce/to-date )] (cond (and event-date client-id location ) [:order #:sales-order {:date event-date :external-id (str "ezcater/order/" client-id "-" location "-" order-number) :client client-id :location location :reference-link (str order-number) :line-items [#:order-line-item {:external-id (str "ezcater/order/" client-id "-" location "-" order-number "-" 0) :item-name "EZCater Catering" :category "EZCater Catering" :discount (fmt-amount (or adjustments 0.0)) :tax (fmt-amount tax) :total (fmt-amount (+ food-total tax))}] :charges [#:charge {:type-name "CARD" :date event-date :client client-id :location location :external-id (str "ezcater/charge/" client-id "-" location "-" order-number "-" 0) :processor :ccp-processor/ezcater :total (fmt-amount (+ food-total tax tip)) :tip (fmt-amount tip)}] :total (fmt-amount (+ food-total tax (or adjustments 0.0))) :discount (fmt-amount (or adjustments 0.0)) :service-charge (fmt-amount (+ fee commission)) :tax (fmt-amount tax) :tip (fmt-amount tip) :returns 0.0 :vendor :vendor/ccp-ezcater}] caterer-name (do (alog/warn ::missing-client :order order-number :store-name store-name :caterer-name caterer-name) [:missing caterer-name]) :else nil))) (defn stream->sales-orders [s] (let [clients (map first (dc/q '[:find (pull ?c [:client/code :db/id :client/feature-flags {:client/location-matches [:location-match/matches :location-match/location]} :client/name :client/matches :client/locations]) :where [?c :client/code]] (dc/db conn))) object (str "/ezcater-xls/" (str (java.util.UUID/randomUUID)))] (mu/log ::writing-temp-xls :location object) (s3/put-object {:bucket-name (:data-bucket env) :key object :input-stream s}) (into [] (->> (excel/extract-sheet-details (:data-bucket env) object) rows->maps (map #(map->sales-order % clients)) (filter identity))))) (defn import-stuff [] (with-open [s (io/input-stream "/home/brycecovert/Downloads/test_2023-04-01_2023-04-30_2023-05-04.xlsx")] (stream->sales-orders s))) (defn page* [] [:div.mt-4 (com/card {} [:div.px-4.py-3.space-y-4.flex.flex-col [:h1.text-2xl.mb-3.font-bold "EZCater XLS Import"] [:p.text-sm.italic "Please go to " (com/link {:href "https://www.ezcater.com/ez_manage/reports/new" :target "_blank"} "EZCater's report page") " to generate a new report. Then drop it below."] [:div#page-notification.notification.block {:style {:display "none"}}] [:form.bg-blue-100.border-2.border-dashed.rounded-lg.border-blue-300.p-4.max-w-md.w-md.text-center.cursor-pointer {:action (bidi/path-for ssr-routes/only-routes :admin-ezcater-xls) :method "POST" :id "ezcater"} "Drop the ezcater xls here."] [:script (hiccup/raw " ezcater_dropzone = new Dropzone (\"#ezcater\", { success: function (file, response) { document.getElementById(\"page-notification\").innerHTML = response; document.getElementById(\"page-notification\").style[\"display\"] = \"block\"; }, acceptedFiles: '.xls,.xlsx', disablePreviews: true });")]])]) (defn upload-xls [{:keys [identity] :as request}] (let [file (or (get (:params request) :file) (get (:params request) "file"))] (mu/log ::uploading-file :file file) (with-open [s (io/input-stream (:tempfile file))] (try (let [parse-results (stream->sales-orders s) new-orders (->> parse-results (filter (comp #{:order} first)) (map last)) missing-location (->> parse-results (filter (comp #{:missing} first)) (map last))] (audit-transact new-orders identity) (html-response [:div (format "Successfully imported %d orders." (count new-orders)) (when (seq missing-location) [:div "Missing the following locations" [:ul.ul (for [ml (into #{} missing-location)] [:li ml])]])])) (catch Exception e (alog/error ::import-error :error e) (html-response [:div (.getMessage e)])))))) (defn page [{:keys [matched-route request-method] :as request}] (mu/log ::method :method request-method) (if (= :post request-method) (upload-xls request) (base-page request (com/page {:nav com/admin-aside-nav :client-selection (:client-selection request) :client (:client request) :clients (:clients request) :identity (:identity request) :request request :app-params {:hx-get (bidi/path-for ssr-routes/only-routes :admin-ezcater-xls) :hx-trigger "clientSelected from:body" :hx-select "#app-contents" :hx-swap "outerHTML swap:300ms"}} (com/breadcrumbs {} [:a {:href (bidi/path-for ssr-routes/only-routes :admin)} "Admin"] [:a {:href (bidi/path-for ssr-routes/only-routes :admin-ezcater-xls)} "EZCater XLS Import"]) (page*)) "Invoice Glimpse")))