347 lines
19 KiB
Clojure
347 lines
19 KiB
Clojure
(ns auto-ap.ezcater.core
|
|
(:require
|
|
[auto-ap.datomic :refer [conn]]
|
|
[datomic.api :as d]
|
|
[clj-http.client :as client]
|
|
[venia.core :as v]
|
|
[clojure.string :as str]
|
|
[clojure.data.json :as json]
|
|
[clj-time.coerce :as coerce]
|
|
[clojure.tools.logging :as log]
|
|
[clj-time.core :as time]
|
|
[clojure.set :as set]
|
|
[auto-ap.time :as atime]
|
|
[auto-ap.logging :as alog]
|
|
[cemerick.url :as url]))
|
|
|
|
(defn query [{:ezcater-integration/keys [api-key]} q]
|
|
(-> (client/post "https://api.ezcater.com/graphql/"
|
|
{:headers {"Authorization" api-key
|
|
"Content-Type" "application/json"}
|
|
:body (json/write-str {"query" (v/graphql-query q)})
|
|
:as :json})
|
|
:body
|
|
:data
|
|
))
|
|
|
|
(defn get-caterers [integration]
|
|
(:caterers (query integration {:venia/queries [{:query/data
|
|
[:caterers [:name :uuid [:address [:name :street]]]]}]} )))
|
|
|
|
(defn get-subscriptions [integration]
|
|
(->> (query integration {:venia/queries [{:query/data
|
|
[:subscribers [:id [:subscriptions [:parentId :parentEntity :eventEntity :eventKey]] ]]}]} )
|
|
:subscribers
|
|
first
|
|
:subscriptions))
|
|
|
|
(defn get-integrations []
|
|
(d/q '[:find [(pull ?i [:ezcater-integration/api-key
|
|
:ezcater-integration/subscriber-uuid
|
|
:db/id
|
|
:ezcater-integration/integration-status [:db/id]]) ...]
|
|
:in $
|
|
:where [?i :ezcater-integration/api-key]]
|
|
(d/db conn)))
|
|
|
|
(defn mark-integration-status [integration integration-status]
|
|
@(d/transact conn
|
|
[{:db/id (:db/id integration)
|
|
:ezcater-integration/integration-status (assoc integration-status
|
|
:db/id (or (-> integration :ezcater-integration/integration-status :db/id)
|
|
#db/id [:db.part/user]))}]))
|
|
|
|
(defn upsert-caterers
|
|
([integration]
|
|
@(d/transact
|
|
conn
|
|
(for [caterer (get-caterers integration)]
|
|
{:db/id (:db/id integration)
|
|
:ezcater-integration/caterers [{:ezcater-caterer/name (str (:name caterer) " (" (:street (:address caterer)) ")")
|
|
:ezcater-caterer/search-terms (str (:name caterer) " " (:street (:address caterer)))
|
|
:ezcater-caterer/uuid (:uuid caterer)}]}))))
|
|
|
|
(defn upsert-used-subscriptions
|
|
([integration]
|
|
(let [extant (get-subscriptions integration)
|
|
to-ensure (set (d/q '[:find [?cu ...]
|
|
:in $
|
|
:where [_ :client/ezcater-locations ?el]
|
|
[?el :ezcater-location/caterer ?c]
|
|
[?c :ezcater-caterer/uuid ?cu]]
|
|
(d/db conn)))
|
|
to-create (set/difference
|
|
to-ensure
|
|
(set (map :parentId extant)))]
|
|
(doseq [parentId to-create]
|
|
(query integration
|
|
{:venia/operation {:operation/type :mutation
|
|
:operation/name "createSubscription"}
|
|
:venia/queries [[:createSubscription
|
|
{:subscriptionParams {:subscriberId (:ezcater-integration/subscriber-uuid integration)
|
|
:parentEntity 'Caterer
|
|
:parentId parentId
|
|
:eventEntity 'Order
|
|
:eventKey 'accepted}}
|
|
[[:subscription [:parentId :parentEntity :eventEntity :eventKey]]]]]})
|
|
(query integration
|
|
{:venia/operation {:operation/type :mutation
|
|
:operation/name "createSubscription"}
|
|
:venia/queries [[:createSubscription
|
|
{:subscriptionParams {:subscriberId (:ezcater-integration/subscriber-uuid integration)
|
|
:parentEntity 'Caterer
|
|
:parentId parentId
|
|
:eventEntity 'Order
|
|
:eventKey 'cancelled}}
|
|
[[:subscription [:parentId :parentEntity :eventEntity :eventKey]]]]]})))))
|
|
|
|
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
|
|
(defn upsert-ezcater
|
|
([] (upsert-ezcater (get-integrations)))
|
|
([integrations]
|
|
(doseq [integration integrations]
|
|
(mark-integration-status integration {:integration-status/last-attempt (coerce/to-date (time/now))})
|
|
(try
|
|
(upsert-caterers integration)
|
|
(upsert-used-subscriptions integration)
|
|
(mark-integration-status integration {:integration-status/state :integration-state/success
|
|
:integration-status/last-updated (coerce/to-date (time/now))})
|
|
|
|
(catch Exception e
|
|
(log/warn e)
|
|
(mark-integration-status integration {:integration-status/state :integration-state/failed
|
|
:integration-status/message (.getMessage e)}))))))
|
|
|
|
(defn get-caterer [caterer-uuid]
|
|
(d/pull (d/db conn)
|
|
'[:ezcater-caterer/name
|
|
{:ezcater-integration/_caterers [:ezcater-integration/api-key]}
|
|
{:ezcater-location/_caterer [:ezcater-location/location
|
|
{:client/_ezcater-locations [:client/code]}]}]
|
|
[:ezcater-caterer/uuid caterer-uuid]))
|
|
|
|
|
|
(defn round-carry-cents [f]
|
|
(with-precision 2 (double (.setScale (bigdec f) 2 java.math.RoundingMode/HALF_UP))))
|
|
|
|
(defn commision [order]
|
|
|
|
(let [commision% (cond
|
|
(= "CLUB_SODA" (:orderSourceType order))
|
|
0.25M
|
|
|
|
(= "MARKETPLACE" (:orderSourceType order))
|
|
0.15M
|
|
:else
|
|
0.07M)]
|
|
(round-carry-cents
|
|
(* commision%
|
|
0.01M
|
|
(+
|
|
(-> order :totals :subTotal :subunits )
|
|
(reduce +
|
|
0
|
|
(map (comp :subunits :cost) (:feesAndDiscounts (:catererCart order)))))))))
|
|
|
|
(defn ccp-fee [order]
|
|
(round-carry-cents
|
|
(* 0.000299M
|
|
(+
|
|
(-> order :totals :subTotal :subunits )
|
|
(-> order :totals :salesTax :subunits )
|
|
(reduce +
|
|
0
|
|
(map (comp :subunits :cost) (:feesAndDiscounts (:catererCart order))))))))
|
|
|
|
(defn order->sales-order [{{:keys [timestamp]} :event {:keys [orderItems]} :catererCart :keys [client-code client-location uuid] :as order}]
|
|
(let [adjustment (round-carry-cents (- (+ (-> order :totals :subTotal :subunits (* 0.01))
|
|
(-> order :totals :salesTax :subunits (* 0.01)))
|
|
(-> order :catererCart :totals :catererTotalDue )
|
|
(commision order)
|
|
(ccp-fee order)))
|
|
service-charge (+ (commision order) (ccp-fee order))
|
|
tax (-> order :totals :salesTax :subunits (* 0.01))
|
|
tip (-> order :totals :tip :subunits (* 0.01))]
|
|
#:sales-order
|
|
{:date (atime/localize (coerce/to-date-time timestamp))
|
|
:external-id (str "ezcater/order/" client-code "-" client-location "-" uuid)
|
|
:client [:client/code client-code]
|
|
:location client-location
|
|
:reference-link (str (url/url "https://ezmanage.ezcater.com/orders/" uuid ))
|
|
:line-items [#:order-line-item
|
|
{:external-id (str "ezcater/order/" client-code "-" client-location "-" uuid "-" 0)
|
|
:item-name "EZCater Catering"
|
|
:category "EZCater Catering"
|
|
:discount adjustment
|
|
:tax tax
|
|
:total (+ (-> order :totals :subTotal :subunits (* 0.01))
|
|
tax
|
|
tip)}]
|
|
:charges [#:charge
|
|
{:type-name "CARD"
|
|
:date (atime/localize (coerce/to-date-time timestamp))
|
|
:client [:client/code client-code]
|
|
:location client-location
|
|
:external-id (str "ezcater/charge/" uuid)
|
|
:processor :ccp-processor/ezcater
|
|
:total (+ (-> order :totals :subTotal :subunits (* 0.01))
|
|
tax
|
|
tip)
|
|
:tip tip}]
|
|
|
|
:total (+ (-> order :totals :subTotal :subunits (* 0.01))
|
|
tax
|
|
tip)
|
|
:discount adjustment
|
|
:service-charge service-charge
|
|
:tax tax
|
|
:tip tip
|
|
:returns 0.0
|
|
:vendor :vendor/ccp-ezcater}))
|
|
|
|
(defn lookup-order [json]
|
|
(let [caterer (get-caterer (get json "parent_id"))
|
|
integration (:ezcater-integration/_caterers caterer)
|
|
client (-> caterer :ezcater-location/_caterer first :client/_ezcater-locations :client/code)
|
|
location (-> caterer :ezcater-location/_caterer first :ezcater-location/location)]
|
|
(if (and client location)
|
|
(doto
|
|
(-> (query
|
|
integration
|
|
{:venia/queries [[:order {:id (get json "entity_id")}
|
|
[:uuid
|
|
:orderSourceType
|
|
[:caterer
|
|
[:name
|
|
:uuid
|
|
[:address [:street]]]]
|
|
[:event
|
|
[:timestamp
|
|
:catererHandoffFoodTime
|
|
:orderType]]
|
|
[:catererCart [[:orderItems
|
|
[:name
|
|
:quantity
|
|
:posItemId
|
|
[:totalInSubunits
|
|
[:currency
|
|
:subunits]]]]
|
|
[:totals
|
|
[:catererTotalDue]]
|
|
[:feesAndDiscounts
|
|
{:type 'DELIVERY_FEE}
|
|
[[:cost
|
|
[:currency
|
|
:subunits]]]]]]
|
|
[:totals [[:customerTotalDue
|
|
[
|
|
:currency
|
|
:subunits
|
|
]]
|
|
[:pointOfSaleIntegrationFee
|
|
[
|
|
:currency
|
|
:subunits
|
|
]]
|
|
[:tip
|
|
[:currency
|
|
:subunits]]
|
|
[:salesTax
|
|
[
|
|
:currency
|
|
:subunits
|
|
]]
|
|
[:salesTaxRemittance
|
|
[:currency
|
|
:subunits
|
|
]]
|
|
[:subTotal
|
|
[:currency
|
|
:subunits]]]]]]]})
|
|
(:order)
|
|
(assoc :client-code client
|
|
:client-location location))
|
|
(#(alog/info ::order-details :detail %)))
|
|
(alog/warn ::caterer-no-longer-has-location :json json))))
|
|
|
|
(defn import-order [json]
|
|
;; {"id" "bf3dcf5c-a68f-42d9-9084-049133e03d3d", "parent_type" "Caterer", "parent_id" "91541331-d7ae-4634-9e8b-ccbbcfb2ce70", "entity_type" "Order", "entity_id" "9ab05fee-a9c5-483b-a7f2-14debde4b7a8", "key" "accepted", "occurred_at" "2022-07-21T19:21:07.549Z"}
|
|
(alog/info
|
|
::try-import-order
|
|
:json json)
|
|
@(d/transact conn [(some-> json
|
|
(lookup-order)
|
|
(order->sales-order)
|
|
(update :sales-order/date coerce/to-date)
|
|
(update-in [:sales-order/charges 0 :charge/date] coerce/to-date))]))
|
|
|
|
(defn upsert-recent []
|
|
(let [last-sunday (coerce/to-date (time/plus (second (->> (time/today)
|
|
(iterate #(time/plus % (time/days -1)))
|
|
(filter #(= 7 (time/day-of-week %)))))
|
|
(time/days 1)))
|
|
orders-to-update (doall (for [[order uuid] (d/q '[:find ?eid ?uuid
|
|
:in $ ?start
|
|
:where [?e :sales-order/vendor :vendor/ccp-ezcater]
|
|
[?e :sales-order/date ?d]
|
|
[(>= ?d ?start)]
|
|
[?e :sales-order/external-id ?eid]
|
|
[?e :sales-order/client ?c]
|
|
[?c :client/ezcater-locations ?l]
|
|
[?l :ezcater-location/caterer ?c2]
|
|
[?c2 :ezcater-caterer/uuid ?uuid]]
|
|
(d/db conn)
|
|
last-sunday)
|
|
:let [_ (log/info "Considering updating " order)
|
|
id (last (str/split order #"/"))
|
|
id (str/join "-" (drop 2 (str/split order #"-")))
|
|
lookup-map {"id" "bf3dcf5c-a68f-42d9-9084-049133e03d3d",
|
|
"parent_type" "Caterer",
|
|
"parent_id" uuid,
|
|
"entity_type" "Order",
|
|
"entity_id" id,
|
|
"key" "accepted",
|
|
"occurred_at" "2022-07-21T19:21:07.549Z"}
|
|
ezcater-order (lookup-order lookup-map)
|
|
extant-order (d/pull (d/db conn) '[:sales-order/total
|
|
:sales-order/tax
|
|
:sales-order/tip
|
|
:sales-order/discount
|
|
:sales-order/external-id
|
|
{:sales-order/charges [:charge/tax
|
|
:charge/tip
|
|
:charge/total
|
|
:charge/external-id]
|
|
:sales-order/line-items [:order-line-item/external-id
|
|
:order-line-item/total
|
|
:order-line-item/tax
|
|
:order-line-item/discount]}]
|
|
[:sales-order/external-id order])
|
|
|
|
updated-order (-> (order->sales-order ezcater-order)
|
|
(select-keys
|
|
#{:sales-order/total
|
|
:sales-order/tax
|
|
:sales-order/tip
|
|
:sales-order/discount
|
|
:sales-order/charges
|
|
:sales-order/external-id
|
|
:sales-order/line-items})
|
|
(update :sales-order/line-items
|
|
(fn [c]
|
|
(map #(select-keys % #{:order-line-item/external-id
|
|
:order-line-item/total
|
|
:order-line-item/tax
|
|
:order-line-item/discount}) c)))
|
|
(update :sales-order/charges (fn [c]
|
|
(map #(select-keys % #{:charge/tax :charge/tip :charge/total
|
|
:charge/external-id}) c))))]
|
|
|
|
:when (not= updated-order extant-order)]
|
|
|
|
updated-order))]
|
|
(log/info "Found these orders to update:" orders-to-update)
|
|
@(d/transact conn orders-to-update)))
|
|
|
|
|