Allows users to self set up square
This commit is contained in:
@@ -115,6 +115,4 @@
|
||||
|
||||
:intuit {:client-id "ABBAQI0qeck149vEC1e8tV6b3YJNujOCdwsUMkJ1ZoptzumyYu"
|
||||
:client-secret "7DriIEend1K9RHlzhupIxPFQozXHELLfeFW2GfTR"
|
||||
:redirect-uri "https://developer.intuit.com/v2/OAuth2Playground/RedirectUrl"}
|
||||
|
||||
}
|
||||
:redirect-uri "https://developer.intuit.com/v2/OAuth2Playground/RedirectUrl"}}
|
||||
|
||||
@@ -23,6 +23,10 @@
|
||||
(defn get-all []
|
||||
(->> (d/q '[:find (pull ?e [*
|
||||
{:client/address [*]}
|
||||
{:client/square-locations [:square-location/square-id
|
||||
:square-location/name
|
||||
:square-location/client-location
|
||||
:db/id]}
|
||||
{:client/bank-accounts [* {:bank-account/type [*]
|
||||
:bank-account/yodlee-account [:yodlee-account/name :yodlee-account/id :yodlee-account/number]
|
||||
:bank-account/plaid-account [:plaid-account/name :db/id :plaid-account/number :plaid-account/balance]
|
||||
|
||||
@@ -250,5 +250,26 @@
|
||||
:db/doc "The type of refund"
|
||||
:db/valueType :db.type/string
|
||||
:db/cardinality :db.cardinality/one}]]
|
||||
:requires [:add-refunds]}})
|
||||
:requires [:add-refunds]}
|
||||
:add-square-locations-3 {:txes [[{:db/ident :client/square-auth-token
|
||||
:db/doc "A token that can be used to authenticate with square"
|
||||
:db/valueType :db.type/string
|
||||
:db/cardinality :db.cardinality/one}
|
||||
{:db/ident :client/square-locations
|
||||
:db/doc "Locations in square"
|
||||
:db/valueType :db.type/ref
|
||||
:db/isComponent true
|
||||
:db/cardinality :db.cardinality/many}
|
||||
{:db/ident :square-location/square-id
|
||||
:db/doc "An id for a location in square"
|
||||
:db/valueType :db.type/string
|
||||
:db/cardinality :db.cardinality/one}
|
||||
{:db/ident :square-location/name
|
||||
:db/doc "Name of the location in square e.g. Woodland Hills"
|
||||
:db/valueType :db.type/string
|
||||
:db/cardinality :db.cardinality/one}
|
||||
{:db/ident :square-location/client-location
|
||||
:db/doc "The client location that this location should match to. e.g. WH"
|
||||
:db/valueType :db.type/string
|
||||
:db/cardinality :db.cardinality/one}]]}})
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
[auto-ap.graphql.utils :refer [->graphql assert-admin can-see-client? is-admin?]]
|
||||
[auto-ap.utils :refer [by]]
|
||||
[auto-ap.yodlee.core :refer [in-memory-cache]]
|
||||
[auto-ap.square.core :as square]
|
||||
[com.walmartlabs.lacinia.util :refer [attach-resolvers]]
|
||||
[clj-time.coerce :as coerce]
|
||||
[config.core :refer [env]]
|
||||
@@ -14,7 +15,8 @@
|
||||
[clojure.java.io :as io]
|
||||
[amazonica.aws.s3 :as s3]
|
||||
[yang.scheduler :as scheduler]
|
||||
[mount.core :as mount])
|
||||
[mount.core :as mount]
|
||||
[auto-ap.square.core :as square])
|
||||
(:import [org.apache.commons.codec.binary Base64]
|
||||
java.util.UUID))
|
||||
|
||||
@@ -54,7 +56,7 @@
|
||||
(> cnt 1)))))
|
||||
(throw (ex-info "Cannot reuse yodlee/plaid/intuit account" {:validation-error (str "Cannot reuse yodlee/plaid/intuit account")})))))
|
||||
|
||||
(defn edit-client [context {:keys [edit_client new_bank_accounts] :as args} value]
|
||||
(defn edit-client [context {:keys [edit_client]} _]
|
||||
(assert-admin (:id context))
|
||||
(when-not (:id edit_client)
|
||||
(assert-client-code-is-unique (:code edit_client)))
|
||||
@@ -100,6 +102,13 @@
|
||||
:client/week-a-debits (:week_a_debits edit_client)
|
||||
:client/week-a-credits (:week_a_credits edit_client)
|
||||
:client/week-b-debits (:week_b_debits edit_client)
|
||||
:client/square-auth-token (:square_auth_token edit_client)
|
||||
:client/square-locations (map
|
||||
(fn [sl]
|
||||
(remove-nils
|
||||
{:db/id (:id sl)
|
||||
:square-location/client-location (:client_location sl)}))
|
||||
(:square_locations edit_client))
|
||||
:client/week-b-credits (:week_b_credits edit_client)
|
||||
:client/location-matches (->> (:location_matches edit_client)
|
||||
(filter (fn [lm] (and (:location lm) (:match lm))))
|
||||
@@ -153,7 +162,9 @@
|
||||
_ (assert-no-shared-transaction-sources client-code transactions)
|
||||
_ (log/info "upserting client" transactions)
|
||||
result (audit-transact transactions (:id context))]
|
||||
(-> result :tempids (get id) (or id) d-clients/get-by-id
|
||||
(when (:square_auth_token edit_client)
|
||||
(square/upsert-locations [(-> result :tempids (get id) (or id) d-clients/get-by-id)]))
|
||||
(-> (-> result :tempids (get id) (or id) d-clients/get-by-id)
|
||||
(update :client/location-matches
|
||||
(fn [lms]
|
||||
(mapcat (fn [lm]
|
||||
@@ -273,6 +284,12 @@
|
||||
:match {:type 'String}
|
||||
:id {:type :id}}}
|
||||
|
||||
:square_location
|
||||
{:fields {:client_location {:type 'String}
|
||||
:name {:type 'String}
|
||||
:square_id {:type 'String}
|
||||
:id {:type :id}}}
|
||||
|
||||
:email_contact {:fields {:id {:type :id}
|
||||
:email {:type 'String}
|
||||
:description {:type 'String}}}
|
||||
@@ -282,6 +299,7 @@
|
||||
:name {:type 'String}
|
||||
:locked_until {:type :iso_date}
|
||||
:code {:type 'String}
|
||||
:square_auth_token {:type 'String}
|
||||
:signature_file {:type 'String}
|
||||
:week_a_debits {:type :money}
|
||||
:week_a_credits {:type :money}
|
||||
@@ -294,6 +312,7 @@
|
||||
:locations {:type '(list String)}
|
||||
:matches {:type '(list String)}
|
||||
:bank_accounts {:type '(list :bank_account)}
|
||||
:square_locations {:type '(list :square_location)}
|
||||
:forecasted_transactions {:type '(list :forecasted_transaction)}
|
||||
:yodlee_provider_accounts {:type '(list :yodlee_provider_account)}
|
||||
:plaid_items {:type '(list :plaid_item)}}}
|
||||
@@ -342,6 +361,9 @@
|
||||
:match {:type 'String}
|
||||
:id {:type :id}}}
|
||||
|
||||
:edit_square_location {:fields {:client_location {:type 'String}
|
||||
:id {:type :id}}}
|
||||
|
||||
:edit_forecasted_transaction {:fields {:identifier {:type 'String}
|
||||
:id {:type :id}
|
||||
:day_of_month {:type 'Int}
|
||||
@@ -353,6 +375,7 @@
|
||||
:name {:type 'String}
|
||||
:locked_until {:type :iso_date}
|
||||
:code {:type 'String}
|
||||
:square_auth_token {:type 'String}
|
||||
:signature_data {:type 'String}
|
||||
:email {:type 'String}
|
||||
:emails {:type '(list :edit_email_contact)}
|
||||
@@ -364,6 +387,7 @@
|
||||
:locations {:type '(list String)}
|
||||
:matches {:type '(list String)}
|
||||
:location_matches {:type '(list :edit_location_match)}
|
||||
:square_locations {:type '(list :edit_square_location)}
|
||||
:bank_accounts {:type '(list :edit_bank_account)}
|
||||
:forecasted_transactions {:type '(list :edit_forecasted_transaction)}}}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
[clj-time.core :as time]
|
||||
[clj-time.periodic :as periodic]
|
||||
[clj-time.format :as f]
|
||||
[config.core :refer [env] :as cfg ]
|
||||
[clojure.string :as str]
|
||||
[clojure.data.json :as json]
|
||||
[clojure.tools.logging :as log]
|
||||
@@ -17,10 +16,14 @@
|
||||
[yang.scheduler :as scheduler]
|
||||
[clojure.core.async :as async]))
|
||||
|
||||
(defn base-headers [client]
|
||||
(defn client-base-headers [client]
|
||||
{"Square-Version" "2021-08-18"
|
||||
"Authorization" (str "Bearer " (get-in env [:square-config client :token]))
|
||||
"Content-Type" "application/json"})
|
||||
"Authorization" (str "Bearer " (:client/square-auth-token client))
|
||||
"Content-Type" "application/json"})
|
||||
|
||||
(defn retry-4 [ex try-count _]
|
||||
(log/warn "Retrying after failure " ex)
|
||||
(if (> try-count 4) false true))
|
||||
|
||||
(defn lookup-dates []
|
||||
(->> (periodic/periodic-seq (time/plus (time/now) (time/days -15))
|
||||
@@ -38,12 +41,23 @@
|
||||
:body
|
||||
:locations))
|
||||
|
||||
(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
|
||||
(log/warn e)
|
||||
[])))
|
||||
|
||||
(defn fetch-catalog [client i]
|
||||
(if i
|
||||
(try
|
||||
(log/info "looking up catalog for" (str "https://connect.squareup.com/v2/catalog/object/" i))
|
||||
(->> (client/get (str "https://connect.squareup.com/v2/catalog/object/" i)
|
||||
{:headers (base-headers client)
|
||||
{:headers (client-base-headers client)
|
||||
:query-params {"include_related_items" "true"}
|
||||
:as :json})
|
||||
:body
|
||||
@@ -71,28 +85,6 @@
|
||||
(log/warn "couldn't look up" i)
|
||||
"Uncategorized"))))
|
||||
|
||||
(defn categories []
|
||||
(by :id (comp :name :category_data) ))
|
||||
|
||||
(def potential-query
|
||||
{"query" {"filter" {"date_time_filter"
|
||||
{
|
||||
"created_at" {
|
||||
"start_at" "2020-08-28T00:00:00-07:00",
|
||||
"end_at" "2020-08-28T23:59:59-07:00"
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
"state_filter" {"states" ["COMPLETED"]}}
|
||||
|
||||
"sort" {
|
||||
"sort_field" "CREATED_AT"
|
||||
"sort_order" "DESC"
|
||||
}}})
|
||||
|
||||
|
||||
(defn pc [d] {"query" {"filter" {"date_time_filter"
|
||||
{
|
||||
"created_at" {
|
||||
@@ -111,17 +103,14 @@
|
||||
|
||||
|
||||
(defn search
|
||||
([client]
|
||||
(search client (get-in env [:square-config client :square-location])))
|
||||
([client l]
|
||||
(search client l nil))
|
||||
([client l d]
|
||||
(log/info "Searching for" l)
|
||||
([client location d]
|
||||
(log/info "Searching for" (:square-location/client-location location))
|
||||
(let [result (->> (client/post "https://connect.squareup.com/v2/orders/search"
|
||||
{:headers (base-headers client)
|
||||
:body (json/write-str (cond-> {"location_ids" [l] "limit" 10000}
|
||||
d (merge (pc d))))
|
||||
:as :json})
|
||||
(doto {:headers (client-base-headers client)
|
||||
:body (json/write-str (cond-> {"location_ids" [(:square-location/square-id location)] "limit" 10000}
|
||||
d (merge (pc d))))
|
||||
:as :json}
|
||||
clojure.pprint/pprint))
|
||||
:body
|
||||
:orders)]
|
||||
(log/info "found " (count result))
|
||||
@@ -143,8 +132,6 @@
|
||||
(defn amount->money [amt]
|
||||
(* 0.01 (or (:amount amt) 0.0)))
|
||||
|
||||
(defn location_id->client-location [location]
|
||||
(get-in env [:square-location location]))
|
||||
|
||||
;; to get totals:
|
||||
(comment
|
||||
@@ -164,9 +151,9 @@
|
||||
(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 [:client/code client]
|
||||
:location location
|
||||
:external-id (str "square/order/" client "-" location "-" (:id order))
|
||||
: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)
|
||||
@@ -211,10 +198,8 @@
|
||||
:discount (amount->money (:total_discount_money li))}))))}))
|
||||
|
||||
(defn daily-results
|
||||
([client]
|
||||
(daily-results client nil))
|
||||
([client d]
|
||||
(daily-results client (get-in env [:square-config client :square-location]) d))
|
||||
([client location]
|
||||
(daily-results client location nil))
|
||||
([client location d]
|
||||
(->> (search client location d)
|
||||
(filter (fn [order]
|
||||
@@ -228,7 +213,7 @@
|
||||
(and
|
||||
(not= "Koala" (:name (:source order)))
|
||||
(not= "koala-production" (:name (:source order))))))
|
||||
(map #(order->sales-order client (get-in env [:square-config client :location]) %)))))
|
||||
(map #(order->sales-order client location %)))))
|
||||
|
||||
#_(daily-results)
|
||||
|
||||
@@ -246,11 +231,9 @@
|
||||
|
||||
(defn get-payment [client p]
|
||||
(:payment (:body (retry #(client/get (str "https://connect.squareup.com/v2/payments/" p)
|
||||
{:headers (base-headers client)
|
||||
{:headers (client-base-headers client)
|
||||
:as :json
|
||||
:retry-handler (fn [ex try-count http-context]
|
||||
(log/warn "Retrying after failure " ex)
|
||||
(if (> try-count 4) false true))})))))
|
||||
:retry-handler retry-4})))))
|
||||
|
||||
(defn get-settlement-sales-date [client settlement]
|
||||
(let [concurrent 10
|
||||
@@ -282,24 +265,23 @@
|
||||
(drop 2)
|
||||
first)))
|
||||
|
||||
(defn get-settlement-details [client settlements] ;; pairs of [location settlement]
|
||||
(defn get-settlement-details [client location settlements] ;; pairs of [location settlement]
|
||||
(log/info "getting settlement details for " settlements)
|
||||
(let [concurrent 10
|
||||
output-chan (async/chan)]
|
||||
(async/pipeline-blocking concurrent
|
||||
output-chan
|
||||
(map (fn [[location s :as g]]
|
||||
(map (fn [s]
|
||||
(lc/with-context {:source "Square settlements loading "}
|
||||
(log/info "Looking up settlement " s " for location " location)
|
||||
(let [settlement (:body (retry #(client/get (str "https://connect.squareup.com/v1/" location "/settlements/" s)
|
||||
{:headers (base-headers client)
|
||||
(log/info "Looking up settlement " s " for location " (:square-location/client-location location))
|
||||
(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 (fn [ex try-count http-context]
|
||||
(log/warn "Retrying after failure " ex)
|
||||
(if (> try-count 4) false true))})))]
|
||||
:retry-handler retry-4})))
|
||||
sales-date (get-settlement-sales-date client settlement)]
|
||||
|
||||
(log/info "sales date for " s " is " (get-settlement-sales-date client settlement))
|
||||
(assoc settlement :sales-date (get-settlement-sales-date client settlement) )))))
|
||||
(log/info "sales date for " s " is " sales-date)
|
||||
(assoc settlement :sales-date sales-date)))))
|
||||
(async/to-chan settlements)
|
||||
true
|
||||
(fn [e]
|
||||
@@ -308,14 +290,14 @@
|
||||
(async/<!! (async/into [] output-chan))))
|
||||
|
||||
(defn settlements
|
||||
([client l] (settlements client l (lookup-dates)))
|
||||
([client l lookup-dates]
|
||||
(log/info "Searching for" l)
|
||||
([client location] (settlements client location (lookup-dates)))
|
||||
([client location lookup-dates]
|
||||
(log/info "Searching for" (:square-location/client-location location))
|
||||
(->> lookup-dates
|
||||
(mapcat (fn [[start-date end-date]]
|
||||
(log/info "looking up settlements for " l " on dates " start-date " to " end-date)
|
||||
(let [settlements (->> (client/get (str "https://connect.squareup.com/v1/" l "/settlements")
|
||||
{:headers (base-headers client)
|
||||
(log/info "looking up settlements for " (:square-location/client-location location) " on dates " start-date " to " end-date)
|
||||
(let [settlements (->> (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})
|
||||
@@ -324,22 +306,17 @@
|
||||
settlements)))
|
||||
set
|
||||
seq
|
||||
(map (fn [s]
|
||||
[l s]))
|
||||
(get-settlement-details client)
|
||||
)))
|
||||
(get-settlement-details client location))))
|
||||
|
||||
(defn daily-settlements
|
||||
([client] (daily-settlements client
|
||||
(get-in env [:square-config client :square-location])))
|
||||
([client location-id]
|
||||
(->> (for [settlement (settlements client location-id)]
|
||||
([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 [:client/code client]
|
||||
:location (get-in env [:square-config client :location])
|
||||
: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)))
|
||||
@@ -354,49 +331,47 @@
|
||||
(filter :expected-deposit/date))))
|
||||
|
||||
(defn refunds
|
||||
([client]
|
||||
(refunds client (get-in env [:square-config client :square-location])))
|
||||
([client l]
|
||||
(let [refunds (:refunds (:body (client/get (str "https://connect.squareup.com/v2/refunds?location_id=" l)
|
||||
{:headers (base-headers client)
|
||||
:as :json
|
||||
:retry-handler (fn [ex try-count http-context]
|
||||
(log/warn "Retrying after failure " ex)
|
||||
(if (> try-count 4) false true))})))]
|
||||
(->> 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 [:client/code client]
|
||||
:location (get-in env [:square-config client :location])
|
||||
:date (coerce/to-date (:created_at r))
|
||||
:type (:source_type (get-payment client (:payment_id r)))}))))))
|
||||
(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 ]
|
||||
(upsert client nil))
|
||||
([client d]
|
||||
(doseq [square-location (:client/square-locations client)
|
||||
:when (:square-location/client-location square-location)]
|
||||
(upsert client square-location nil)))
|
||||
([client location d]
|
||||
(lc/with-context {:source "Square loading"}
|
||||
(try
|
||||
(let [existing (->> (d/query {:query {:find ['?external-id]
|
||||
:in ['$ '?client]
|
||||
:where ['[?o :sales-order/client ?client]
|
||||
'[?o :sales-order/external-id ?external-id]]}
|
||||
:args [(d/db conn) [:client/code client]]})
|
||||
(let [existing (->> (d/query {:query {:find ['?external-id]
|
||||
:in ['$ '?client]
|
||||
:where ['[?o :sales-order/client ?client]
|
||||
'[?o :sales-order/external-id ?external-id]]}
|
||||
:args [(d/db conn) (:db/id client)]})
|
||||
(map first)
|
||||
set)
|
||||
_ (log/info (count existing) "Sales orders already exist")
|
||||
_ (log/info (count existing) "Sales orders already exist")
|
||||
to-create (filter #(not (existing (:sales-order/external-id %)))
|
||||
(daily-results client d))]
|
||||
(daily-results client location d))]
|
||||
(doseq [x (partition-all 20 to-create)]
|
||||
(log/info "Loading " (count x))
|
||||
@(d/transact conn x)))
|
||||
@@ -404,20 +379,23 @@
|
||||
(log/error e))))))
|
||||
|
||||
(defn upsert-settlements
|
||||
([client] (upsert-settlements client (get-in env [:square-config client :square-location])))
|
||||
([client location-id]
|
||||
([client]
|
||||
(doseq [square-location (:client/square-locations client)
|
||||
:when (:square-location/client-location square-location)]
|
||||
(upsert-settlements client square-location)))
|
||||
([client location]
|
||||
(lc/with-context {:source "Square settlements loading"}
|
||||
(try
|
||||
(let [existing (->> (d/query {:query {:find ['?external-id]
|
||||
:in ['$ '?client]
|
||||
:where ['[?d :expected-deposit/client ?client]
|
||||
'[?d :expected-deposit/external-id ?external-id]]}
|
||||
:args [(d/db conn) [:client/code client]]})
|
||||
:args [(d/db conn) (:db/id client)]})
|
||||
(map first)
|
||||
set)
|
||||
_ (log/info (count existing) "settlements already exist")
|
||||
to-create (filter #(not (existing (:expected-deposit/external-id %)))
|
||||
(daily-settlements client location-id))]
|
||||
(daily-settlements client location))]
|
||||
(doseq [x (partition-all 20 to-create)]
|
||||
(log/info "Loading expected deposit" (count x))
|
||||
@(d/transact conn x)))
|
||||
@@ -426,16 +404,20 @@
|
||||
(log/info "Done loading settlements"))))
|
||||
|
||||
(defn upsert-refunds
|
||||
([client] (upsert-refunds client (get-in env [:square-config client :square-location])))
|
||||
([client]
|
||||
(doseq [square-location (:client/square-locations client)
|
||||
:when (:square-location/client-location square-location)]
|
||||
(upsert-refunds client square-location)))
|
||||
([client location]
|
||||
(lc/with-context {:source "Loading Square Settlements"
|
||||
:client client}
|
||||
:client (:client/code client)
|
||||
:location (:square-location/client-location client)}
|
||||
(try
|
||||
(let [existing (->> (d/query {:query {:find ['?external-id]
|
||||
:in ['$ '?client]
|
||||
:where ['[?r :sales-refund/client ?client]
|
||||
'[?r :sales-refund/external-id ?external-id]]}
|
||||
:args [(d/db conn) [:client/code client]]})
|
||||
:args [(d/db conn) (:db/id client)]})
|
||||
(map first)
|
||||
set)
|
||||
_ (log/info (count existing) "refunds already exist")
|
||||
@@ -448,6 +430,37 @@
|
||||
(log/error e)))
|
||||
(log/info "Done loading refunds"))))
|
||||
|
||||
(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 []
|
||||
(d/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]}]) ...]
|
||||
:where [?c :client/square-auth-token]]
|
||||
(d/db conn)))
|
||||
|
||||
(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)))]
|
||||
(->> (for [square-location (client-locations client)]
|
||||
{:db/id (or (square-id->id (:id square-location)) (d/tempid :db.part/user))
|
||||
:client/_square-locations (:db/id client)
|
||||
:square-location/name (:name square-location)
|
||||
:square-location/square-id (:id square-location)})
|
||||
(d/transact conn)
|
||||
deref))))
|
||||
|
||||
(defn reset []
|
||||
(->>
|
||||
(d/query {:query {:find ['?e]
|
||||
@@ -458,17 +471,17 @@
|
||||
(map (fn [x] [:db/retractEntity x]))))
|
||||
|
||||
(defn upsert-all []
|
||||
(doseq [[client-code] (get-in env [:square-config])
|
||||
:when (d/entid (d/db conn) [:client/code client-code])]
|
||||
(lc/with-context {:source (str "Square loading for " client-code)
|
||||
:client client-code}
|
||||
(doseq [client (get-square-clients)
|
||||
:when (seq (filter :square-location/client-location (:client/square-locations client)))]
|
||||
(lc/with-context {:source "Square loading"
|
||||
:client (:client/code client)}
|
||||
(upsert-locations client)
|
||||
(log/info "Loading Orders")
|
||||
(upsert client-code)
|
||||
(upsert client)
|
||||
(log/info "Loading Settlements")
|
||||
(upsert-settlements client-code)
|
||||
(upsert-settlements client)
|
||||
(log/info "Loading refunds")
|
||||
(upsert-refunds client-code)
|
||||
)))
|
||||
(upsert-refunds client))))
|
||||
|
||||
(mount/defstate square-loader
|
||||
:start (scheduler/every (* 4 59 60 1000) upsert-all)
|
||||
@@ -478,18 +491,4 @@
|
||||
|
||||
|
||||
|
||||
(comment
|
||||
(daily-results)
|
||||
(mount/stop (mount/only #{'auto-ap.square.core/square-settlement-loader}))
|
||||
|
||||
(mount/stop (mount/only #{'auto-ap.square.core/square-refund-loader}))
|
||||
|
||||
(mount/stop (mount/only #{'auto-ap.square.core/square-loader}))
|
||||
|
||||
(mount/start (mount/only #{'auto-ap.square.core/square-settlement-loader}))
|
||||
(mount/start (mount/only #{'auto-ap.square.core/square-refund-loader}))
|
||||
(mount/start (mount/only #{'auto-ap.square.core/square-loader}))
|
||||
(do (upsert) nil)
|
||||
|
||||
(do @(d/transact conn (reset)) nil))
|
||||
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
(:require [auto-ap.datomic :refer [uri]]
|
||||
[config.core :refer [env]]
|
||||
[auto-ap.utils :refer [by]]
|
||||
[auto-ap.time :as atime]
|
||||
[clojure.core.async :as async]
|
||||
#_[auto-ap.ledger :as l]
|
||||
[unilog.context :as lc]
|
||||
[auto-ap.square.core :as square]
|
||||
[mount.core :as mount]
|
||||
[auto-ap.server ]
|
||||
[datomic.api :as d]
|
||||
@@ -610,29 +612,35 @@
|
||||
|
||||
|
||||
(defn historical-load-sales [client-code days]
|
||||
(println "orders")
|
||||
(lc/with-context {:source "Historical loading data"}
|
||||
(doseq [d (clj-time.periodic/periodic-seq (t/plus (t/now) (t/days (- days)))
|
||||
(t/now)
|
||||
(t/days 1))]
|
||||
(println d)
|
||||
(auto-ap.square.core/upsert client-code d)))
|
||||
(let [client (d/pull (d/db auto-ap.datomic/conn)
|
||||
square/square-read
|
||||
[:client/code client-code])]
|
||||
(doseq [square-location (:client/square-locations client)
|
||||
:when (:square-location/client-location square-location)]
|
||||
|
||||
(println "orders")
|
||||
(lc/with-context {:source "Historical loading data"}
|
||||
(doseq [d (clj-time.periodic/periodic-seq (t/plus (t/now) (t/days (- days)))
|
||||
(t/now)
|
||||
(t/days 1))]
|
||||
(println d)
|
||||
(square/upsert client square-location d)))
|
||||
|
||||
(println "refunds")
|
||||
(auto-ap.square.core/upsert-refunds client-code)
|
||||
(println "refunds")
|
||||
(square/upsert-refunds client square-location)
|
||||
|
||||
|
||||
(println "settlements")
|
||||
(with-redefs [auto-ap.square.core/lookup-dates (fn lookup-dates []
|
||||
(->> (clj-time.periodic/periodic-seq (t/plus (t/now) (t/days (- days)))
|
||||
(t/now)
|
||||
(t/days 2))
|
||||
(map (fn [d]
|
||||
[(auto-ap.time/unparse (t/plus d (t/days 1)) auto-ap.time/iso-date)
|
||||
(println "settlements")
|
||||
(with-redefs [square/lookup-dates (fn lookup-dates []
|
||||
(->> (clj-time.periodic/periodic-seq (t/plus (t/now) (t/days (- days)))
|
||||
(t/now)
|
||||
(t/days 2))
|
||||
(map (fn [d]
|
||||
[(atime/unparse (t/plus d (t/days 1)) atime/iso-date)
|
||||
|
||||
(auto-ap.time/unparse (t/plus d (t/days 2)) auto-ap.time/iso-date)]))))]
|
||||
(atime/unparse (t/plus d (t/days 2)) atime/iso-date)]))))]
|
||||
|
||||
(auto-ap.square.core/upsert-settlements client-code)))
|
||||
(square/upsert-settlements client square-location)))))
|
||||
|
||||
(defn upsert-invoice-amounts [tsv]
|
||||
(let [data (with-open [reader (io/reader (char-array tsv))]
|
||||
@@ -668,7 +676,7 @@
|
||||
current-account-id (:db/id (:invoice-expense-account/account invoice-expense-account))
|
||||
target-account-id (Long/parseLong (str/trim target-account))
|
||||
|
||||
target-date (clj-time.coerce/to-date (auto-ap.time/parse target-date auto-ap.time/normal-date))
|
||||
target-date (clj-time.coerce/to-date (atime/parse target-date atime/normal-date))
|
||||
current-date (:invoice/date invoice)
|
||||
|
||||
|
||||
|
||||
@@ -31,7 +31,8 @@
|
||||
[:address [:street1 :street2 :city :state :zip]]])
|
||||
|
||||
(defn client-query [token]
|
||||
(cond-> [:id :name :signature-file :code :email :matches :week-a-debits :week-a-credits :week-b-debits :week-b-credits :locations :locked-until
|
||||
(cond-> [:id :name :signature-file :code :email :matches :week-a-debits :week-a-credits :week-b-debits :week-b-credits :locations :locked-until :square-auth-token
|
||||
[:square-locations [:square-id :id :name :client-location]]
|
||||
[:emails [:id :email :description]]
|
||||
[:location-matches [:id :location :match]]
|
||||
[:bank-accounts [:id :start-date :numeric-code :code :number :bank-name :bank-code :check-number :name :routing :type :sort-order :visible :yodlee-account-id
|
||||
|
||||
@@ -108,6 +108,12 @@
|
||||
:name (:name new-client-data)
|
||||
:code (:code new-client-data) ;; TODO add validation can't change
|
||||
:emails (:emails new-client-data)
|
||||
:square-auth-token (:square-auth-token new-client-data)
|
||||
:square-locations (map
|
||||
(fn [x]
|
||||
{:id (:id x)
|
||||
:client-location (:client-location x)})
|
||||
(:square-locations new-client-data))
|
||||
|
||||
:locked-until (cond (not (:locked-until new-client-data))
|
||||
nil
|
||||
@@ -654,5 +660,24 @@
|
||||
:style {:width "7em"}
|
||||
:field [:amount]
|
||||
:step "0.01"}]]}])]]
|
||||
(field "Square Authentication Token"
|
||||
[:input.input {:type "text"
|
||||
:field [:square-auth-token]}])
|
||||
[:div.field
|
||||
[:label.label "Square Locations"]
|
||||
[:div.control
|
||||
(raw-field
|
||||
[multi-field {:type "multi-field"
|
||||
:field :square-locations
|
||||
:template [[:input.input {:type "text"
|
||||
:style {:width "15em"}
|
||||
:disabled true
|
||||
:field [:name]}]
|
||||
[:input.input {:type "text"
|
||||
:style {:width "4em"}
|
||||
:field [:client-location]
|
||||
:step "0.01"}]]
|
||||
:disable-remove? true
|
||||
:disable-new? true}])]]
|
||||
(error-notification)
|
||||
(submit-button "Save")])]]))
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
(ns auto-ap.views.utils
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[reagent.core :as reagent]
|
||||
[react-transition-group :as react-transition-group]
|
||||
[react-datepicker]
|
||||
[clojure.spec.alpha :as s]
|
||||
[cljs-time.coerce :as c]
|
||||
[cljs-time.core :as time]
|
||||
[auto-ap.subs :as subs]
|
||||
[cljs-time.format :as format]
|
||||
[goog.i18n.NumberFormat.Format]
|
||||
[cljs-time.core :as t]
|
||||
[clojure.string :as str]
|
||||
[goog.crypt.base64 :as base64]
|
||||
[cljs.tools.reader.edn :as edn]
|
||||
[reagent.core :as r])
|
||||
(:require
|
||||
[cemerick.url]
|
||||
[cljs-time.coerce :as c]
|
||||
[cljs-time.core :as t]
|
||||
[cljs-time.format :as format]
|
||||
[cljs.tools.reader.edn :as edn]
|
||||
[clojure.spec.alpha :as s]
|
||||
[clojure.string :as str]
|
||||
[goog.crypt.base64 :as base64]
|
||||
[re-frame.core :as re-frame]
|
||||
[react-transition-group :as react-transition-group]
|
||||
#_{:clj-kondo/ignore [:unused-namespace]}
|
||||
[react-datepicker :as react-datepicker]
|
||||
[reagent.core :as reagent])
|
||||
(:import
|
||||
(goog.i18n NumberFormat)
|
||||
(goog.i18n.NumberFormat Format)))
|
||||
|
||||
(def nff
|
||||
(NumberFormat. Format/CURRENCY))
|
||||
|
||||
@@ -27,8 +27,6 @@
|
||||
(defn ->$ [x]
|
||||
(nf x))
|
||||
|
||||
|
||||
|
||||
(defn- nf%
|
||||
[num]
|
||||
(.format (doto
|
||||
@@ -59,7 +57,7 @@
|
||||
|
||||
|
||||
(defn delayed-dispatch [e]
|
||||
(fn [x]
|
||||
(fn [_]
|
||||
(js/setTimeout #(re-frame/dispatch e) 151)
|
||||
false))
|
||||
|
||||
@@ -72,7 +70,7 @@
|
||||
(re-frame/dispatch-sync event)))
|
||||
|
||||
(defn dispatch-event-with-propagation [event]
|
||||
(fn [e]
|
||||
(fn [_]
|
||||
(re-frame/dispatch-sync event)))
|
||||
|
||||
(def pretty-long (format/formatter "MM/dd/yyyy HH:mm:ss"))
|
||||
@@ -94,23 +92,23 @@
|
||||
(format/parse f d)))
|
||||
|
||||
(defn dispatch-date-change [event]
|
||||
(fn [e g]
|
||||
(fn [e]
|
||||
(re-frame/dispatch (conj event
|
||||
(if (str/blank? e)
|
||||
e
|
||||
(date->str (time/from-default-time-zone (c/from-date e)) standard))))))
|
||||
(date->str (t/from-default-time-zone (c/from-date e)) standard))))))
|
||||
|
||||
(defn dispatch-cljs-date-change [event]
|
||||
(fn [e g]
|
||||
(fn [e]
|
||||
(re-frame/dispatch (conj event
|
||||
(if (str/blank? e)
|
||||
e
|
||||
(time/from-default-time-zone (c/from-date e)))))))
|
||||
(c/to-local-date e))))))
|
||||
|
||||
;; TODO inline on-changes causes each field to be rerendered each time. When we fix this
|
||||
;; let's make sure that we find away not to trigger a re-render for every component any time any form field
|
||||
;; changes
|
||||
(defmulti do-bind (fn [a {:keys [type] :as x}]
|
||||
(defmulti do-bind (fn [_ {:keys [type]}]
|
||||
type))
|
||||
|
||||
(defn with-keys [children]
|
||||
@@ -121,7 +119,7 @@
|
||||
(reagent/adapt-react-class react-transition-group/CSSTransition))
|
||||
|
||||
|
||||
(defn appearing [{:keys [visible? enter-class exit-class timeout]} & children ]
|
||||
(defn appearing [{:keys [visible? enter-class exit-class timeout]}]
|
||||
(let [final-state (reagent/atom visible?)]
|
||||
(fn [{:keys [visible?]} & children]
|
||||
[css-transition-group {:in visible? :class-names {:exit exit-class :enter enter-class} :timeout timeout :onEnter (fn [] (reset! final-state true )) :onExited (fn [] (reset! final-state false))}
|
||||
@@ -130,15 +128,15 @@
|
||||
[:span])])))
|
||||
|
||||
|
||||
(defn multi-field [{:keys [change-event data value template on-change allow-change?]} ]
|
||||
(let [value-repr (r/atom (mapv
|
||||
(defn multi-field [{:keys [value]} ]
|
||||
(let [value-repr (reagent/atom (mapv
|
||||
(fn [x]
|
||||
(assoc x :key (random-uuid) :new? false))
|
||||
value))]
|
||||
(fn [{:keys [change-event data value template on-change allow-change?]} ]
|
||||
(fn [{:keys [template on-change allow-change? disable-new? disable-remove?]} ]
|
||||
(let [value @value-repr
|
||||
already-has-new-row? (= [:key :new?] (keys (last value)))
|
||||
value (if already-has-new-row?
|
||||
value (if (or already-has-new-row? disable-new?)
|
||||
value
|
||||
(conj value {:key (random-uuid)
|
||||
:new? true}))]
|
||||
@@ -153,7 +151,7 @@
|
||||
[:div.level-item
|
||||
(if (:new? override)
|
||||
|
||||
[:div.icon.is-medium {:class (if (not= i (dec (count value)))
|
||||
[:div.icon.is-medium {:class (when (not= i (dec (count value)))
|
||||
"has-text-info")}
|
||||
[:i.fa.fa-plus]]
|
||||
[:div.icon.is-medium])]
|
||||
@@ -169,7 +167,7 @@
|
||||
(if (= value {})
|
||||
nil
|
||||
value))
|
||||
:disabled is-disabled?
|
||||
:disabled (or is-disabled? (get-in template [1 :disabled]))
|
||||
:on-change (fn [e]
|
||||
(reset! value-repr
|
||||
(into []
|
||||
@@ -184,29 +182,27 @@
|
||||
(dissoc v :new? :key))
|
||||
@value-repr))))])
|
||||
]
|
||||
[:div.level-item
|
||||
[:a.button.level-item
|
||||
|
||||
{:disabled is-disabled?
|
||||
:on-click (fn []
|
||||
(when-not disable-remove?
|
||||
[:div.level-item
|
||||
[:a.button.level-item
|
||||
{:disabled is-disabled?
|
||||
:on-click (fn []
|
||||
(when-not is-disabled?
|
||||
(reset! value-repr (into []
|
||||
(filter (fn [{:keys [key ]}]
|
||||
(not= key (:key override)))
|
||||
(filter (fn [r]
|
||||
(not= [:key :new?] (keys r)))
|
||||
value))))
|
||||
|
||||
(when-not is-disabled?
|
||||
(reset! value-repr (into []
|
||||
|
||||
(filter (fn [{:keys [key ] :as v}]
|
||||
(not= key (:key override)))
|
||||
(filter (fn [r]
|
||||
(not= [:key :new?] (keys r)))
|
||||
value))))
|
||||
|
||||
(on-change (mapv
|
||||
(fn [v]
|
||||
(dissoc v :new? :key))
|
||||
@value-repr))))}
|
||||
[:span.icon [:span.icon-remove]]]]
|
||||
(on-change (mapv
|
||||
(fn [v]
|
||||
(dissoc v :new? :key))
|
||||
@value-repr))))}
|
||||
[:span.icon [:span.icon-remove]]]])
|
||||
]])]))))
|
||||
|
||||
(defmethod do-bind "select" [dom {:keys [field allow-nil? subscription event class value spec] :as keys} & rest]
|
||||
(defmethod do-bind "select" [dom {:keys [field allow-nil? subscription event class spec] :as keys} & rest]
|
||||
(let [field (if (keyword? field) [field] field)
|
||||
event (if (keyword? event) [event] event)
|
||||
keys (assoc keys
|
||||
@@ -235,7 +231,7 @@
|
||||
keys (dissoc keys :field :subscription :event :spec)]
|
||||
(into [dom keys] (with-keys rest))))
|
||||
|
||||
(defmethod do-bind "checkbox" [dom {:keys [field subscription event class value spec] :as keys} & rest]
|
||||
(defmethod do-bind "checkbox" [dom {:keys [field subscription event class spec] :as keys} & rest]
|
||||
(let [field (if (keyword? field) [field] field)
|
||||
event (if (keyword? event) [event] event)
|
||||
keys (assoc keys
|
||||
@@ -253,7 +249,7 @@
|
||||
(let [field (if (keyword? field) [field] field)
|
||||
event (if (keyword? event) [event] event)
|
||||
keys (assoc keys
|
||||
:on-change (fn [selected text-description text-value]
|
||||
:on-change (fn [selected text-value]
|
||||
(re-frame/dispatch (conj (conj event field) selected))
|
||||
(when text-field
|
||||
(re-frame/dispatch (conj (conj (or text-event event) text-field) text-value))))
|
||||
@@ -278,7 +274,7 @@
|
||||
(into [dom keys] (with-keys rest))))
|
||||
|
||||
|
||||
(defmethod do-bind "typeahead-entity" [dom {:keys [field event text-event subscription class spec match->text] :as keys} & rest]
|
||||
(defmethod do-bind "typeahead-entity" [dom {:keys [field event subscription class spec] :as keys} & rest]
|
||||
(let [field (if (keyword? field) [field] field)
|
||||
event (if (keyword? event) [event] event)
|
||||
keys (assoc keys
|
||||
@@ -293,7 +289,7 @@
|
||||
keys (dissoc keys :field :subscription :event :spec)]
|
||||
(into [dom keys] (with-keys rest))))
|
||||
|
||||
(defmethod do-bind "typeahead-v3" [dom {:keys [field event text-event subscription class spec match->text] :as keys} & rest]
|
||||
(defmethod do-bind "typeahead-v3" [dom {:keys [field event subscription class spec] :as keys} & rest]
|
||||
(let [field (if (keyword? field) [field] field)
|
||||
event (if (keyword? event) [event] event)
|
||||
keys (assoc keys
|
||||
@@ -314,10 +310,10 @@
|
||||
selected (get-in subscription field)
|
||||
|
||||
selected (cond (string? selected)
|
||||
(c/to-date (time/to-default-time-zone (time/from-default-time-zone (str->date selected standard))))
|
||||
(c/to-date (t/to-default-time-zone (t/from-default-time-zone (str->date selected standard))))
|
||||
|
||||
(instance? goog.date.DateTime selected)
|
||||
(c/to-date (time/to-default-time-zone (time/from-default-time-zone selected)))
|
||||
(c/to-date (t/to-default-time-zone (t/from-default-time-zone selected)))
|
||||
|
||||
(instance? goog.date.Date selected)
|
||||
(c/to-date selected)
|
||||
@@ -361,7 +357,7 @@
|
||||
keys (dissoc keys :field :event :subscription :spec)]
|
||||
(into [dom keys] (with-keys rest))))
|
||||
|
||||
(defmethod do-bind "number" [dom {:keys [field precision event subscription class spec] :as keys :or {precision 2}} & rest]
|
||||
(defmethod do-bind "number" [dom {:keys [field event subscription class spec] :as keys} & rest]
|
||||
(let [field (if (keyword? field) [field] field)
|
||||
event (if (keyword? event) [event] event)
|
||||
keys (assoc keys
|
||||
@@ -380,14 +376,13 @@
|
||||
:else
|
||||
val))))))
|
||||
:value (get-in subscription field)
|
||||
|
||||
:class (str class
|
||||
(when (and spec (not (s/valid? spec (get-in subscription field))))
|
||||
" is-danger")))
|
||||
keys (dissoc keys :field :subscription :event :spec)]
|
||||
(into [dom keys] (with-keys rest))))
|
||||
|
||||
(defmethod do-bind "textarea->table" [dom {:keys [field event subscription class spec] :as keys :or {precision 2}} & rest]
|
||||
(defmethod do-bind "textarea->table" [dom {:keys [field event subscription class spec] :as keys} & rest]
|
||||
(let [field (if (keyword? field) [field] field)
|
||||
event (if (keyword? event) [event] event)
|
||||
keys (assoc keys
|
||||
@@ -396,7 +391,6 @@
|
||||
(conj field)
|
||||
(conj x))))
|
||||
:value (get-in subscription field)
|
||||
|
||||
:class (str class
|
||||
(when (and spec (not (s/valid? spec (get-in subscription field))))
|
||||
" is-danger")))
|
||||
@@ -446,8 +440,7 @@
|
||||
(with-keys (map (fn [x] [:div.field x]) controls)))])
|
||||
|
||||
(def date-picker
|
||||
(do
|
||||
(reagent/adapt-react-class (.-default react-datepicker))))
|
||||
(reagent/adapt-react-class (.-default react-datepicker)))
|
||||
|
||||
(defn date-picker-friendly [params]
|
||||
[date-picker (assoc params
|
||||
@@ -506,15 +499,6 @@
|
||||
{}
|
||||
(:query (cemerick.url/url (.-location js/window)))))
|
||||
|
||||
(defn loading [db]
|
||||
(-> db
|
||||
(assoc-in [:status] :loading)
|
||||
(assoc-in [:error] nil)))
|
||||
|
||||
(defn triggers-loading [form]
|
||||
(re-frame/enrich
|
||||
(fn [db event]
|
||||
(loading db))))
|
||||
|
||||
(defn action-cell-width [cnt]
|
||||
(str (inc (* cnt 51)) "px"))
|
||||
|
||||
Reference in New Issue
Block a user