removing old square

This commit is contained in:
2023-11-01 10:02:57 -07:00
parent 31dc514cdd
commit 3211635dce
13 changed files with 172 additions and 1250 deletions

View File

@@ -12,7 +12,7 @@
result->page
is-admin?]]
[auto-ap.routes.queries :as q]
[auto-ap.square.core :as square]
[auto-ap.square.core3 :as square]
[auto-ap.utils :refer [heartbeat]]
[clj-time.coerce :as coerce]
[clojure.java.io :as io]
@@ -152,7 +152,7 @@
result (audit-transact [[:upsert-entity updated-entity]] (:id context))]
(when (:square_auth_token edit_client)
(square/upsert-locations (-> result :tempids (get id) (or id) d-clients/get-by-id)))
@(square/upsert-locations (-> result :tempids (get id) (or id) d-clients/get-by-id)))
(let [updated-client (-> result :tempids (get id) (or id) d-clients/get-by-id)]
(when (:client/name updated-client)
(solr/index-documents-raw solr/impl "clients"

View File

@@ -3,7 +3,6 @@
(:require
[auto-ap.datomic :refer [conn]]
[auto-ap.jobs.core :refer [execute]]
[auto-ap.square.core :as square]
[auto-ap.square.core3 :as square3]
[clj-time.coerce :as coerce]
[clj-time.core :as time]
@@ -12,42 +11,11 @@
[config.core :refer [env]]
[datomic.api :as dc]))
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
(defn historical-load-sales [client days]
(alog/info ::old-sales-loading)
(alog/info ::new-sales-loading :client (:client/code client) :days days)
(let [client (dc/pull (dc/db auto-ap.datomic/conn)
square/square-read
client)]
(doseq [square-location (:client/square-locations client)
:when (:square-location/client-location square-location)]
(println "orders")
(doseq [d (per/periodic-seq (time/plus (time/today) (time/days (- days)))
(time/today)
(time/days 1))]
(println d)
(square/upsert client square-location (coerce/to-date-time d) (coerce/to-date-time (time/plus d (time/days 1)))))
(println "refunds")
(square/upsert-refunds client square-location)
#_(with-redefs [square/lookup-dates (fn lookup-dates []
(->> (per/periodic-seq (time/plus (time/today) (time/days (- days)))
(time/today)
(time/days 2))
(map (fn [d]
[(atime/unparse (time/plus d (time/days 1)) atime/iso-date)
(atime/unparse (time/plus d (time/days 2)) atime/iso-date)]))))]
(square/upsert-settlements client square-location)))))
(defn historical-load-sales2 [client days]
(alog/info ::new-sales-loading)
(let [client (dc/pull (dc/db auto-ap.datomic/conn)
square/square-read
square3/square-read
client)
days (Long/parseLong days)]
(doseq [square-location (:client/square-locations client)
@@ -67,13 +35,8 @@
(defn load-historical-sales [args]
(let [{:keys [days client]} args
client (Long/parseLong client)
feature-flags (-> (dc/pull (dc/db conn) '[:client/feature-flags] client)
:client/feature-flags
set)]
(if (get feature-flags "new-square")
(historical-load-sales2 client days)
(historical-load-sales client days))))
client (Long/parseLong client)]
(historical-load-sales client days)))
(defn -main [& _]
(execute "load-historical-sales" #(load-historical-sales (:args env))))

View File

@@ -2,8 +2,8 @@
(:gen-class)
(:require
[auto-ap.jobs.core :refer [execute]]
[auto-ap.square.core :as square]))
[auto-ap.square.core3 :as square3]))
(defn -main [& _]
(execute "square-loading" square/upsert-all))
(execute "square-loading" square3/do-upsert-all))

View File

@@ -1,9 +0,0 @@
(ns auto-ap.jobs.square2
(:gen-class)
(:require
[auto-ap.jobs.core :refer [execute]]
[auto-ap.square.core3 :as square3]))
(defn -main [& _]
(execute "square3-loading" square3/do-upsert-all))

View File

@@ -16,7 +16,6 @@
[auto-ap.jobs.plaid :as job-plaid]
[auto-ap.jobs.register-invoice-import :as job-register-invoice-import]
[auto-ap.jobs.square :as job-square]
[auto-ap.jobs.square2 :as job-square2]
[auto-ap.jobs.sysco :as job-sysco]
[auto-ap.jobs.vendor-usages :as job-vendor-usages]
[auto-ap.jobs.yodlee2 :as job-yodlee2]
@@ -95,9 +94,6 @@
(cond (= job "square-import-job")
(job-square/-main)
(= job "square2-import-job")
(job-square2/-main)
(= job "reconcile-ledger")
(job-reconcile-ledger/-main)

View File

@@ -1,501 +0,0 @@
(ns auto-ap.square.core
(:require
[auto-ap.datomic :refer [conn random-tempid remove-nils]]
[auto-ap.time :as atime]
[clj-http.client :as client]
[clj-time.coerce :as coerce]
[clj-time.core :as time]
[clj-time.format :as f]
[clj-time.periodic :as periodic]
[clojure.core.async :as async]
[clojure.data.json :as json]
[clojure.set :as set]
[clojure.string :as str]
[datomic.api :as dc]
[slingshot.slingshot :refer [try+]]
))
(defn client-base-headers [client]
{"Square-Version" "2021-08-18"
"Authorization" (str "Bearer " (:client/square-auth-token client))
"Content-Type" "application/json"})
(defn retry-4 [ex try-count _]
(if (> try-count 4) false true))
(defn lookup-dates []
(->> (periodic/periodic-seq (time/plus (time/now) (time/days -15))
(time/now)
(time/days 5))
(map (fn [d]
[(atime/unparse (time/plus d (time/days 1)) atime/iso-date)
(atime/unparse (time/plus d (time/days 5)) atime/iso-date)]))))
(defn client-locations [client]
(try
(->> (client/get "https://connect.squareup.com/v2/locations"
{:headers (client-base-headers client)
:as :json})
:body
:locations)
(catch Exception e
[])))
(defn fetch-catalog [client i]
(if i
(try
(->> (client/get (str "https://connect.squareup.com/v2/catalog/object/" i)
{:headers (client-base-headers client)
:query-params {"include_related_items" "true"}
:as :json})
:body
:object)
(catch Exception e
nil))))
(def fetch-catalog-fast (memoize fetch-catalog))
(defn item-id->category-name [client i]
(let [item (fetch-catalog-fast client i)]
(cond (:item_variation_data item)
(item-id->category-name client (:item_id (:item_variation_data item)))
(:category_id (:item_data item))
(:name (:category_data (fetch-catalog-fast client (:category_id (:item_data item)))))
(:item_data item)
"Uncategorized"
:else
(do
"Uncategorized"))))
(defn pc [start end]
{"query" {"filter" {"date_time_filter"
{
"created_at" {
"start_at" (f/unparse (f/formatter "YYYY-MM-dd'T'HH:mm:ssZZ") start)
"end_at" (f/unparse (f/formatter "YYYY-MM-dd'T'HH:mm:ssZZ") end)
}
}}
"sort" {
"sort_field" "CREATED_AT"
"sort_order" "DESC"
}}})
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
(defn get-order
([client location order-id]
(let [result (->> (client/get (str "https://connect.squareup.com/v2/orders/" order-id)
{:headers (client-base-headers client)
:as :json})
:body
)]
result)))
(defn continue-search [client location start end cursor]
(let [result (->> (client/post "https://connect.squareup.com/v2/orders/search"
{:headers (client-base-headers client)
:body (json/write-str (cond-> {"location_ids" [(:square-location/square-id location)]
"limit" 10000
"cursor" cursor}
start (merge (pc start end))))
:as :json})
:body)]
(if (not-empty (:cursor result))
(concat (:orders result) (continue-search client location start end (:cursor result)))
(:orders result))))
(defn search
([client location start end]
(let [result (->> (client/post "https://connect.squareup.com/v2/orders/search"
{:headers (client-base-headers client)
:body (json/write-str (cond-> {"location_ids" [(:square-location/square-id location)] "limit" 10000}
start (merge (pc start end))))
:as :json})
:body)]
(if (not-empty (:cursor result))
(concat (:orders result) (continue-search client location start end (:cursor result)))
(:orders result)))))
(defn amount->money [amt]
(* 0.01 (or (:amount amt) 0.0)))
;; to get totals:
(comment
(reduce
(fn [total i]
(+ total (+ (- (:sales-order/total i)
(:sales-order/tax i)
(:sales-order/tip i)
(:sales-order/service-charge i))
(:sales-order/returns i)
(:sales-order/discount i)
)))
0.0
[]))
(defn order->sales-order [client location order]
(remove-nils
#:sales-order
{:date (coerce/to-date (time/to-time-zone (coerce/to-date-time (:created_at order)) (time/time-zone-for-id "America/Los_Angeles")))
:client (:db/id client)
:location (:square-location/client-location location)
:external-id (str "square/order/" (:client/code client) "-" (:square-location/client-location location) "-" (:id order))
:vendor :vendor/ccp-square
:total (-> order :net_amounts :total_money amount->money)
:tax (-> order :net_amounts :tax_money amount->money)
:tip (-> order :net_amounts :tip_money amount->money)
:discount (-> order :net_amounts :discount_money amount->money)
:service-charge (-> order :net_amounts :service_charge_money amount->money)
:returns (+ (- (-> order :return_amounts :total_money amount->money)
(-> order :return_amounts :tax_money amount->money)
(-> order :return_amounts :tip_money amount->money)
(-> order :return_amounts :service_charge_money amount->money))
(-> order :return_amounts :discount_money amount->money))
:charges (->> (:tenders order)
(map (fn [t]
(remove-nils
#:charge
{:type-name (:type t)
:external-id (when (:id t)
(str "square/charge/" (:id t)))
:processor (condp = (some-> (:note t) str/lower-case)
"doordash" :ccp-processor/doordash
"dd" :ccp-processor/doordash
"ubereats" :ccp-processor/uber-eats
"ue" :ccp-processor/uber-eats
"grubhub" :ccp-processor/grubhub
"grub" :ccp-processor/grubhub
"gh" :ccp-processor/grubhub
(condp = (:name (:source order))
"GRUBHUB" :ccp-processor/grubhub
"UBEREATS" :ccp-processor/uber-eats
"DOORDASH" :ccp-processor/doordash
:ccp-processor/na))
:total (amount->money (:amount_money t))
:tip (amount->money (:tip_money t))}))))
:line-items (->> (:line_items order)
(map-indexed (fn [i li]
(remove-nils
#:order-line-item
{:external-id (str "square/order/" (:client/code client) "-" (:square-location/client-location location) "-" (:id order) "-" i)
:item-name (:name li)
:category (if (= "GIFT_CARD" (:item_type li))
"Gift Card"
(item-id->category-name client (:catalog_object_id li)))
:total (amount->money (:total_money li))
:tax (amount->money (:total_tax_money li))
:discount (amount->money (:total_discount_money li))}))))}))
(defn daily-results
([client location]
(daily-results client location (time/plus (time/now) (time/days -3)) (time/now)))
([client location start end]
(let [search-results (search client location start end)
koala-production-ids (->> search-results
(filter #(= "koala-production" (:name (:source %))))
(map :id)
(into #{}))]
(->> search-results
(filter (fn [order]
;; sometimes orders stay open in square. At least one payment
;; is needed to import, in order to avoid importing orders in-progress.
(and (or (> (count (:tenders order)) 0)
(seq (:returns order)))
(or (= #{} (set (map #(:status (:card_details %)) (:tenders order))))
(not= #{} (set/difference
(set (map #(:status (:card_details %)) (:tenders order)))
#{"FAILED" "VOIDED"}))))))
(filter (fn [order]
(not= "Koala" (:name (:source order)))))
(filter (fn has-linked-koala-production? [order]
;; if a POS order is linked (via note) to a koala-production order, we want
;; to keep the koala-production order, because it has taxes correct
(not (and (:line_items order) ;; returns do not have line items, so they should be allowed
(->> (:line_items order)
(map :note)
(every? koala-production-ids))))))
(map #(order->sales-order client location %))))))
(defn retry
([f] (retry f 0))
([f i]
(if (< i 5)
(try
(f)
(catch Exception e
(retry f (inc i)))))))
(defn get-payment [client p]
(:payment (:body (retry #(client/get (str "https://connect.squareup.com/v2/payments/" p)
{:headers (client-base-headers client)
:as :json
:retry-handler retry-4})))))
(defn halt-if-error [x]
(if (instance? Throwable x)
(throw x)
x))
(defn get-settlement-sales-date [client settlement]
(let [concurrent 10
output-chan (async/chan)]
(async/pipeline-blocking concurrent
output-chan
(map (fn [p]
(or
(-> (get-payment client p)
:created_at
coerce/to-date)
(coerce/to-date (time/now)))))
(async/to-chan! (->> settlement
:entries
(filter #(= "CHARGE" (:type %)))
(map :payment_id)
(filter identity)
(set)
(take 20)
))
true
(fn [e]
e))
(->> (async/<!! (async/into [] output-chan))
(map halt-if-error)
sort
(drop 2)
first)))
(defn get-settlement-details [client location settlements] ;; pairs of [location settlement]
(let [concurrent 10
output-chan (async/chan)]
(async/pipeline-blocking concurrent
output-chan
(map (fn [s]
(let [settlement (:body (retry #(client/get (str "https://connect.squareup.com/v1/" (:square-location/square-id location) "/settlements/" s)
{:headers (client-base-headers client)
:as :json
:retry-handler retry-4})))
sales-date (get-settlement-sales-date client settlement)]
(assoc settlement :sales-date sales-date))))
(async/to-chan! settlements)
true
(fn [e]
e))
(->> (async/<!! (async/into [] output-chan))
(map halt-if-error))))
(defn settlements
([client location] (settlements client location (lookup-dates)))
([client location lookup-dates]
(->> lookup-dates
(mapcat (fn [[start-date end-date]]
(let [settlements (->> (retry #(client/get (str "https://connect.squareup.com/v1/" (:square-location/square-id location) "/settlements")
{:headers (client-base-headers client)
:query-params {"begin_time" start-date
"end_time" end-date}
:as :json
:retry-handler retry-4}))
:body
(map :id))]
settlements)))
set
seq
(get-settlement-details client location))))
(defn daily-settlements
([client location]
(->> (for [settlement (settlements client location)]
#:expected-deposit {:external-id (str "square/settlement/" (:id settlement))
:vendor :vendor/ccp-square
:status :expected-deposit-status/pending
:total (amount->money (:total_money settlement))
:client (:db/id client)
:location (:square-location/client-location location)
:fee (- (reduce + 0.0 (map (fn [entry]
(if (= (:type entry) "REFUND")
(- (amount->money (:fee_money entry)))
(amount->money (:fee_money entry))))
(:entries settlement))))
:date (-> (:initiated_at settlement)
(coerce/to-date))
:sales-date (or (:sales-date settlement)
(-> (:initiated_at settlement)
(coerce/to-date)))
:charges (->> (:entries settlement)
(filter :payment_id)
(map (fn [p] {:charge/external-id (str "square/charge/" (:payment_id p))})))})
(filter :expected-deposit/date))))
(defn refunds
([client l]
(let [refunds (:refunds (:body (client/get (str "https://connect.squareup.com/v2/refunds?location_id=" (:square-location/square-id l))
{:headers (client-base-headers client)
:as :json
:retry-handler retry-4})))]
(->> refunds
(filter (fn [r] (= "COMPLETED" (:status r))))
(map (fn [r]
#:sales-refund {:external-id (str "square/refund/" (:id r))
:vendor :vendor/ccp-square
:total (amount->money (:amount_money r))
:fee (transduce
(comp (filter #(= "ADJUSTMENT" (:type %)))
(map :amount_money)
(map amount->money))
+
0.0
(:processing_fee r))
:client (:db/id client)
:location (:square-location/client-location l)
:date (coerce/to-date (:created_at r))
:type (:source_type (get-payment client (:payment_id r)))}))))))
(defn upsert
([client ]
(doseq [square-location (:client/square-locations client)
:when (:square-location/client-location square-location)]
(upsert client square-location (time/plus (time/now) (time/days -3)) (time/now))))
([client location start end]
(let [existing (->> (dc/q {:find ['?external-id]
:in ['$ '?client]
:where ['[?o :sales-order/client ?client]
'[?o :sales-order/external-id ?external-id]]}
(dc/db conn) (:db/id client))
(map first)
set)
to-create (filter #(not (existing (:sales-order/external-id %)))
(daily-results client location start end))]
(doseq [x (partition-all 20 to-create)]
@(dc/transact-async conn x)))))
(defn upsert-settlements
([client]
(doseq [square-location (:client/square-locations client)
:when (:square-location/client-location square-location)]
(upsert-settlements client square-location)))
([client location]
(doseq [x (partition-all 20 (daily-settlements client location))]
@(dc/transact-async conn x))))
(defn upsert-refunds
([client]
(doseq [square-location (:client/square-locations client)
:when (:square-location/client-location square-location)]
(upsert-refunds client square-location)))
([client location]
(doseq [x (partition-all 20 (refunds client location))]
@(dc/transact-async conn x))))
(def square-read [:db/id
:client/code
:client/square-auth-token
{:client/square-locations [:db/id :square-location/name :square-location/square-id :square-location/client-location]}])
(defn get-square-clients
([]
(map first (dc/q '[:find (pull ?c [:db/id
:client/square-integration-status
:client/code
:client/square-auth-token
{:client/square-locations [:db/id :square-location/name :square-location/square-id :square-location/client-location]}])
:in $
:where [?c :client/square-auth-token]
(not [?c :client/feature-flags "new-square"])]
(dc/db conn))))
([ & codes]
(map first (dc/q '[:find (pull ?c [:db/id
:client/code
:client/square-auth-token
{:client/square-locations [:db/id :square-location/name :square-location/square-id :square-location/client-location]}])
:in $ [?code ...]
:where [?c :client/square-auth-token]
(not [?c :client/feature-flags "new-square"])
[?c :client/code ?code]]
(dc/db conn)
codes))))
(defn upsert-locations
([] (doseq [client (get-square-clients)]
(upsert-locations client)))
([client]
(let [square-id->id (into {}
(map
(fn [sl]
[(:square-location/square-id sl)
(:db/id sl)])
(:client/square-locations client)))]
@(dc/transact-async conn (for [square-location (client-locations client)]
{:db/id (or (square-id->id (:id square-location)) (random-tempid))
:client/_square-locations (:db/id client)
:square-location/name (:name square-location)
:square-location/square-id (:id square-location)})))))
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
(defn reset []
(->>
(dc/q {:find ['?e]
:in ['$]
:where ['(or [?e :sales-order/date]
[?e :expected-deposit/date])]}
(dc/db conn))
(map first)
(map (fn [x] [:db/retractEntity x]))))
(defn mark-integration-status [client integration-status]
@(dc/transact-async conn
[{:db/id (:db/id client)
:client/square-integration-status (assoc integration-status
:db/id (or (-> client :client/square-integration-status :db/id)
(random-tempid)))}]))
(defn upsert-all [ & clients]
(doseq [client (apply get-square-clients clients)
:when (seq (filter :square-location/client-location (:client/square-locations client)))]
(mark-integration-status client {:integration-status/last-attempt (coerce/to-date (time/now))})
(try+
(upsert-locations client)
(upsert client)
#_(upsert-settlements client) ;; currently settlements v1 is broken
(upsert-refunds client)
(mark-integration-status client {:integration-status/state :integration-state/success
:integration-status/last-updated (coerce/to-date (time/now))})
(catch [:status 401] data
(mark-integration-status client {:integration-status/state :integration-state/unauthorized
:integration-status/message (-> data :body str)}))
(catch [:status 503] data
(mark-integration-status client {:integration-status/state :integration-state/failed
:integration-status/message (-> data :body str)}))
(catch Object _
(mark-integration-status client {:integration-status/state :integration-state/failed
:integration-status/message (or (some-> (:wrapper &throw-context) (.getMessage ))
(some-> (:object &throw-context) str)
"Unknown error")})))))

View File

@@ -1,511 +0,0 @@
(ns auto-ap.square.core2
(:require
[auto-ap.datomic :refer [conn remove-nils random-tempid]]
[auto-ap.time :as atime]
[clj-http.client :as client]
[clj-time.coerce :as coerce]
[clj-time.core :as time]
[clj-time.format :as f]
[clj-time.periodic :as periodic]
[clojure.core.async :as async]
[clojure.data.json :as json]
[clojure.set :as set]
[clojure.string :as str]
[cemerick.url :as url]
[datomic.api :as dc]
[slingshot.slingshot :refer [try+]]
))
(defn client-base-headers [client]
{"Square-Version" "2021-08-18"
"Authorization" (str "Bearer " (:client/square-auth-token client))
"Content-Type" "application/json"})
(defn retry-4 [ex try-count _]
(if (> try-count 4) false true))
(defn lookup-dates []
(->> (periodic/periodic-seq (time/plus (time/now) (time/days -15))
(time/now)
(time/days 5))
(map (fn [d]
[(atime/unparse (time/plus d (time/days 1)) atime/iso-date)
(atime/unparse (time/plus d (time/days 5)) atime/iso-date)]))))
(defn client-locations [client]
(try
(->> (client/get "https://connect.squareup.com/v2/locations"
{:headers (client-base-headers client)
:as :json})
:body
:locations)
(catch Exception e
[])))
(defn fetch-catalog [client i]
(if i
(try
(->> (client/get (str "https://connect.squareup.com/v2/catalog/object/" i)
{:headers (client-base-headers client)
:query-params {"include_related_items" "true"}
:as :json})
:body
:object)
(catch Exception e
nil))))
(def fetch-catalog-fast (memoize fetch-catalog))
(defn item-id->category-name [client i]
(let [item (fetch-catalog-fast client i)]
(cond (:item_variation_data item)
(item-id->category-name client (:item_id (:item_variation_data item)))
(:category_id (:item_data item))
(:name (:category_data (fetch-catalog-fast client (:category_id (:item_data item)))))
(:item_data item)
"Uncategorized"
:else
(do
"Uncategorized"))))
(defn pc [start end]
{"query" {"filter" {"date_time_filter"
{
"created_at" {
"start_at" (f/unparse (f/formatter "YYYY-MM-dd'T'HH:mm:ssZZ") start)
"end_at" (f/unparse (f/formatter "YYYY-MM-dd'T'HH:mm:ssZZ") end)
}
}}
"sort" {
"sort_field" "CREATED_AT"
"sort_order" "DESC"
}}})
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
(defn get-order
([client location order-id]
(let [result (->> (client/get (str "https://connect.squareup.com/v2/orders/" order-id)
{:headers (client-base-headers client)
:as :json})
:body
)]
result)))
(defn continue-search [client location start end cursor]
(let [result (->> (client/post "https://connect.squareup.com/v2/orders/search"
{:headers (client-base-headers client)
:body (json/write-str (cond-> {"location_ids" [(:square-location/square-id location)]
"limit" 10000
"cursor" cursor}
start (merge (pc start end))))
:as :json})
:body)]
(if (not-empty (:cursor result))
(concat (:orders result) (continue-search client location start end (:cursor result)))
(:orders result))))
(defn search
([client location start end]
(let [result (->> (client/post "https://connect.squareup.com/v2/orders/search"
{:headers (client-base-headers client)
:body (json/write-str (cond-> {"location_ids" [(:square-location/square-id location)] "limit" 10000}
start (merge (pc start end))))
:as :json})
:body)]
(if (not-empty (:cursor result))
(concat (:orders result) (continue-search client location start end (:cursor result)))
(:orders result)))))
(defn amount->money [amt]
(* 0.01 (or (:amount amt) 0.0)))
;; to get totals:
(comment
(reduce
(fn [total i]
(+ total (+ (- (:sales-order/total i)
(:sales-order/tax i)
(:sales-order/tip i)
(:sales-order/service-charge i))
(:sales-order/returns i)
(:sales-order/discount i)
)))
0.0
[]))
(defn tender->charge [order client location t]
(remove-nils
#:charge
{:type-name (:type t)
:date (coerce/to-date (time/to-time-zone (coerce/to-date-time (:created_at order)) (time/time-zone-for-id "America/Los_Angeles")))
:client (:db/id client)
:note (:note t)
:location (:square-location/client-location location)
:reference-link (str (url/url "https://squareup.com/receipt/preview" (:id t) ))
:external-id (when (:id t)
(str "square/charge/" (:id t)))
:processor (condp = (:type t)
"OTHER"
(condp = (some-> (:note t) str/lower-case)
"doordash" :ccp-processor/doordash
"dd" :ccp-processor/doordash
"ubereats" :ccp-processor/uber-eats
"ue" :ccp-processor/uber-eats
"grubhub" :ccp-processor/grubhub
"grub" :ccp-processor/grubhub
"gh" :ccp-processor/grubhub
(condp = (:name (:source order))
"GRUBHUB" :ccp-processor/grubhub
"UBEREATS" :ccp-processor/uber-eats
"DOORDASH" :ccp-processor/doordash
"Koala" :ccp-processor/koala
"koala-production" :ccp-processor/koala
:ccp-processor/na))
"CARD"
:ccp-processor/square
"SQUARE_GIFT_CARD"
:ccp-processor/square
"CASH"
:ccp-processor/na
:ccp-processor/na)
:total (amount->money (:amount_money t))
:tip (amount->money (:tip_money t))}))
(defn order->sales-order [client location order]
(let [is-order-only-for-charge?
(= ["CUSTOM_AMOUNT"]
(mapv :item_type (:line_items order )))]
(if is-order-only-for-charge?
(->> (:tenders order)
(mapv #(tender->charge order client location %)))
[(remove-nils
#:sales-order
{:date (coerce/to-date (time/to-time-zone (coerce/to-date-time (:created_at order)) (time/time-zone-for-id "America/Los_Angeles")))
:client (:db/id client)
:location (:square-location/client-location location)
:external-id (str "square/order/" (:client/code client) "-" (:square-location/client-location location) "-" (:id order))
:source (or (:name (:source order))
"Square")
:vendor :vendor/ccp-square
:reference-link (str (url/url "https://squareup.com/dashboard/sales/transactions" (:id order) "by-unit" (:square-location/square-id location)))
:total (-> order :net_amounts :total_money amount->money)
:tax (-> order :net_amounts :tax_money amount->money)
:tip (-> order :net_amounts :tip_money amount->money)
:discount (-> order :net_amounts :discount_money amount->money)
:service-charge (-> order :net_amounts :service_charge_money amount->money)
:returns (+ (- (-> order :return_amounts :total_money amount->money)
(-> order :return_amounts :tax_money amount->money)
(-> order :return_amounts :tip_money amount->money)
(-> order :return_amounts :service_charge_money amount->money))
(-> order :return_amounts :discount_money amount->money))
:charges (->> (:tenders order)
(map #(tender->charge order client location %)))
:line-items (->> (:line_items order)
(map-indexed (fn [i li]
(remove-nils
#:order-line-item
{:external-id (str "square/order/" (:client/code client) "-" (:square-location/client-location location) "-" (:id order) "-" i)
:item-name (:name li)
:category (if (= "GIFT_CARD" (:item_type li))
"Gift Card"
(item-id->category-name client (:catalog_object_id li)))
:total (amount->money (:total_money li))
:tax (amount->money (:total_tax_money li))
:discount (amount->money (:total_discount_money li))})))
(into []))})])))
(defn daily-results
([client location]
(daily-results client location (time/plus (time/now) (time/days -45)) (time/now)))
([client location start end]
(let [search-results (search client location start end)]
(->> search-results
(filter (fn [order]
;; sometimes orders stay open in square. At least one payment
;; is needed to import, in order to avoid importing orders in-progress.
(and
(or (> (count (:tenders order)) 0)
(seq (:returns order)))
(or (= #{} (set (map #(:status (:card_details %)) (:tenders order))))
(not= #{} (set/difference
(set (map #(:status (:card_details %)) (:tenders order)))
#{"FAILED" "VOIDED"}))))))
(mapcat #(order->sales-order client location %))))))
(defn retry
([f] (retry f 0))
([f i]
(if (< i 5)
(try
(f)
(catch Exception e
(retry f (inc i))))
)))
(defn get-payment [client p]
(:payment (:body (retry #(client/get (str "https://connect.squareup.com/v2/payments/" p)
{:headers (client-base-headers client)
:as :json
:retry-handler retry-4})))))
(defn halt-if-error [x]
(if (instance? Throwable x)
(throw x)
x))
(defn get-settlement-details [client location settlements] ;; pairs of [location settlement]
(let [concurrent 4
output-chan (async/chan)]
(async/pipeline-blocking concurrent
output-chan
(map (fn [s]
(:body (retry #(client/get (str "https://connect.squareup.com/v1/" (:square-location/square-id location) "/settlements/" (:id s))
{:headers (client-base-headers client)
:as :json
:retry-handler retry-4})))))
(async/to-chan! settlements)
true
(fn [e]
e))
(->> (async/<!! (async/into [] output-chan))
(map halt-if-error))))
(defn settlements
([client location] (settlements client location (lookup-dates)))
([client location lookup-dates]
(->> lookup-dates
(mapcat (fn [[start-date end-date]]
(let [settlements (->> (retry #(client/get (str "https://connect.squareup.com/v1/" (:square-location/square-id location) "/settlements")
{:headers (client-base-headers client)
:query-params {"begin_time" start-date
"end_time" end-date}
:as :json
:retry-handler retry-4}))
:body)]
settlements)))
set
seq
(get-settlement-details client location))))
(defn daily-settlements
([client location]
(->> (for [settlement (settlements client location)
:let [best-sales-date (->> (dc/q '[:find ?s4 (count ?s)
:in $ ?settlement-id
:where
[?settlement :expected-deposit/external-id ?settlement-id]
[?settlement :expected-deposit/charges ?c]
[?s :sales-order/charges ?c]
[?s :sales-order/date ?sales-date]
[(clj-time.coerce/to-date-time ?sales-date) ?s2]
[(auto-ap.time/localize ?s2) ?s3]
[(clj-time.coerce/to-local-date ?s3) ?s4]]
(dc/db conn)
(str "square/settlement/" (:id settlement)))
(sort-by last)
last
first
coerce/to-date-time
atime/as-local-time
coerce/to-date)]]
#:expected-deposit {:external-id (str "square/settlement/" (:id settlement))
:vendor :vendor/ccp-square
:status :expected-deposit-status/pending
:total (amount->money (:total_money settlement))
:client (:db/id client)
:location (:square-location/client-location location)
:fee (- (reduce + 0.0 (map (fn [entry]
(if (= (:type entry) "REFUND")
(- (amount->money (:fee_money entry)))
(amount->money (:fee_money entry))))
(:entries settlement))))
:date (-> (:initiated_at settlement)
(coerce/to-date))
:sales-date (or best-sales-date
(-> (:initiated_at settlement)
(coerce/to-date)))
:charges (->> (:entries settlement)
(filter :payment_id)
(map (fn [p] {:charge/external-id (str "square/charge/" (:payment_id p))})))})
(filter :expected-deposit/date))))
(defn refunds
([client l]
(let [refunds (:refunds (:body (client/get (str "https://connect.squareup.com/v2/refunds?location_id=" (:square-location/square-id l))
{:headers (client-base-headers client)
:as :json
:retry-handler retry-4})))]
(->> refunds
(filter (fn [r] (= "COMPLETED" (:status r))))
(map (fn [r]
#:sales-refund {:external-id (str "square/refund/" (:id r))
:vendor :vendor/ccp-square
:total (amount->money (:amount_money r))
:fee (transduce
(comp (filter #(= "ADJUSTMENT" (:type %)))
(map :amount_money)
(map amount->money))
+
0.0
(:processing_fee r))
:client (:db/id client)
:location (:square-location/client-location l)
:date (coerce/to-date (:created_at r))
:type (:source_type (get-payment client (:payment_id r)))}))))))
(defn upsert
([client ]
(doseq [square-location (:client/square-locations client)
:when (:square-location/client-location square-location)]
(upsert client square-location (time/plus (time/now) (time/days -45)) (time/now))))
([client location start end]
(doseq [x (partition-all 20 (daily-results client location start end))]
@(dc/transact conn x))))
(defn upsert-settlements
([client]
(doseq [square-location (:client/square-locations client)
:when (:square-location/client-location square-location)]
(upsert-settlements client square-location)))
([client location]
(doseq [x (partition-all 20 (daily-settlements client location))]
@(dc/transact conn x))))
(defn upsert-refunds
([client]
(doseq [square-location (:client/square-locations client)
:when (:square-location/client-location square-location)]
(upsert-refunds client square-location)))
([client location]
(doseq [x (partition-all 20 (refunds client location))]
@(dc/transact conn x))))
(def square-read [:db/id
:client/code
:client/square-auth-token
{:client/square-locations [:db/id :square-location/name :square-location/square-id :square-location/client-location]}])
(defn get-square-clients
([]
(dc/q '[:find [(pull ?c [:db/id
:client/square-integration-status
:client/code
:client/square-auth-token
{:client/square-locations [:db/id :square-location/name :square-location/square-id :square-location/client-location]}]) ...]
:in $
:where [?c :client/square-auth-token]
[?c :client/feature-flags "new-square"]]
(dc/db conn)))
([ & codes]
(dc/q '[:find [(pull ?c [:db/id
:client/code
:client/square-auth-token
{:client/square-locations [:db/id :square-location/name :square-location/square-id :square-location/client-location]}]) ...]
:in $ [?code ...]
:where [?c :client/square-auth-token]
[?c :client/feature-flags "new-square"]
[?c :client/code ?code]]
(dc/db conn)
codes)))
(defn upsert-locations
([] (doseq [client (get-square-clients)]
(upsert-locations client)))
([client]
(let [square-id->id (into {}
(map
(fn [sl]
[(:square-location/square-id sl)
(:db/id sl)])
(:client/square-locations client)))]
@(dc/transact conn (for [square-location (client-locations client)]
{:db/id (or (square-id->id (:id square-location)) (random-tempid))
:client/_square-locations (:db/id client)
:square-location/name (:name square-location)
:square-location/square-id (:id square-location)})))))
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
(defn reset []
(->>
(dc/q {:find ['?e]
:in ['$]
:where ['(or [?e :sales-order/date]
[?e :expected-deposit/date])]}
(dc/db conn))
(map first)
(map (fn [x] [:db/retractEntity x]))))
(defn mark-integration-status [client integration-status]
@(dc/transact conn
[{:db/id (:db/id client)
:client/square-integration-status (assoc integration-status
:db/id (or (-> client :client/square-integration-status :db/id)
(random-tempid)))}]))
(defn upsert-all [ & clients]
(doseq [client (apply get-square-clients clients)
:when (seq (filter :square-location/client-location (:client/square-locations client)))]
(mark-integration-status client {:integration-status/last-attempt (coerce/to-date (time/now))})
(try+
(upsert-locations client)
(upsert client)
(upsert-settlements client)
(upsert-refunds client)
(mark-integration-status client {:integration-status/state :integration-state/success
:integration-status/last-updated (coerce/to-date (time/now))})
(catch [:status 401] data
(mark-integration-status client {:integration-status/state :integration-state/unauthorized
:integration-status/message (-> data :body str)}))
(catch [:status 503] data
(mark-integration-status client {:integration-status/state :integration-state/failed
:integration-status/message (-> data :body str)}))
(catch Object _
(mark-integration-status client {:integration-status/state :integration-state/failed
:integration-status/message (or (some-> (:wrapper &throw-context) (.getMessage ))
(some-> (:object &throw-context) str)
"Unknown error")})))))
(defn preview-changes
([client]
(first (for [client (get-square-clients client)
:when (seq (filter :square-location/client-location (:client/square-locations client)))
square-location (:client/square-locations client)
:when (:square-location/client-location square-location)]
(preview-changes client square-location (time/plus (time/now) (time/days -30)) (time/now)))))
([client location start end]
(daily-results client location start end)))

View File

@@ -752,8 +752,7 @@
:client/square-auth-token
{:client/square-locations [:db/id :square-location/name :square-location/square-id :square-location/client-location]}])
:in $
:where [?c :client/square-auth-token]
[?c :client/feature-flags "new-square"]]
:where [?c :client/square-auth-token]]
(dc/db conn))))
([ & codes]
(map first (dc/q '[:find (pull ?c [:db/id
@@ -762,7 +761,6 @@
{:client/square-locations [:db/id :square-location/name :square-location/square-id :square-location/client-location]}])
:in $ [?code ...]
:where [?c :client/square-auth-token]
[?c :client/feature-flags "new-square"]
[?c :client/code ?code]]
(dc/db conn)
codes))))

View File

@@ -6,8 +6,6 @@
[clj-http.core :as http]
[clj-http.client :as client]
[auto-ap.server]
[auto-ap.square.core :as square]
[auto-ap.square.core2 :as square2]
[auto-ap.time :as atime]
[auto-ap.utils :refer [by]]
[clj-time.coerce :as c]