From 11f61464f55fa61b5ce155c80a5e136e3b1f9540 Mon Sep 17 00:00:00 2001 From: Bryce Covert Date: Fri, 4 Sep 2020 19:53:39 -0700 Subject: [PATCH] everything is audited. --- src/clj/auto_ap/datomic.clj | 6 +- src/clj/auto_ap/datomic/migrate/sales.clj | 32 +++++- src/clj/auto_ap/datomic/sales_orders.clj | 5 +- src/clj/auto_ap/graphql.clj | 98 +++++++++++-------- src/clj/auto_ap/graphql/clients.clj | 20 ++-- src/clj/auto_ap/graphql/ledger.clj | 39 ++++---- src/clj/auto_ap/graphql/transaction_rules.clj | 52 +++++----- src/clj/auto_ap/graphql/users.clj | 18 ++-- src/clj/auto_ap/graphql/vendors.clj | 10 +- src/clj/auto_ap/routes/exports.clj | 64 +++++++++--- src/clj/auto_ap/square/core.clj | 71 ++++++++++++++ .../auto_ap/entities/transaction_rule.cljc | 6 +- .../auto_ap/views/components/typeahead.cljs | 4 +- .../views/pages/admin/accounts/form.cljs | 2 + .../auto_ap/views/pages/admin/rules/form.cljs | 1 + 15 files changed, 297 insertions(+), 131 deletions(-) diff --git a/src/clj/auto_ap/datomic.clj b/src/clj/auto_ap/datomic.clj index fd2e1475..2c98f705 100644 --- a/src/clj/auto_ap/datomic.clj +++ b/src/clj/auto_ap/datomic.clj @@ -789,13 +789,14 @@ (:sort args))) (defn apply-sort-3 [args results] + (let [sort-bys (conj (:sort args) {:sort-key "default" :asc true}) length (count sort-bys) comparator (fn [xs ys] (reduce (fn [_ i] - (let [comparison (if (:asc (sort-bys i)) + (let [comparison (if (:asc (nth sort-bys i)) (compare (nth xs i) (nth ys i)) (compare (nth ys i) (nth xs i)))] @@ -819,9 +820,10 @@ (let [batch-id (.toString (java.util.UUID/randomUUID))] (reduce (fn [full-tx batch] - (let [batch (conj batch {:db/id "datomic.tx" + (let [batch (conj (vec batch) {:db/id "datomic.tx" :audit/user (str (:user/role id) "-" (:user/name id)) :audit/batch batch-id}) + _ (log/info "transacting batch " batch-id " " (count batch)) tx-result @(d/transact conn batch)] (cond-> full-tx diff --git a/src/clj/auto_ap/datomic/migrate/sales.clj b/src/clj/auto_ap/datomic/migrate/sales.clj index e5d8db5c..1a2e4096 100644 --- a/src/clj/auto_ap/datomic/migrate/sales.clj +++ b/src/clj/auto_ap/datomic/migrate/sales.clj @@ -91,7 +91,37 @@ :db/valueType :db.type/double :db/cardinality :db.cardinality/one} - ]]}}) + ]]} + :add-expected-deposits {:txes [[{:db/ident :expected-deposit/external-id + :db/doc "The external id for the deposit" + :db/valueType :db.type/string + :db/cardinality :db.cardinality/one + :db/unique :db.unique/identity} + + {:db/ident :expected-deposit/client + :db/doc "The client for the deposit" + :db/valueType :db.type/ref + :db/cardinality :db.cardinality/one} + + {:db/ident :expected-deposit/location + :db/doc "The location of the sale" + :db/valueType :db.type/string + :db/cardinality :db.cardinality/one} + + {:db/ident :expected-deposit/date + :db/doc "The date the deposit was initiated" + :db/valueType :db.type/instant + :db/cardinality :db.cardinality/one} + + {:db/ident :expected-deposit/total + :db/doc "The total amount on the deposit" + :db/valueType :db.type/double + :db/cardinality :db.cardinality/one} + + {:db/ident :expected-deposit/fee + :db/doc "The total fee on the deposit" + :db/valueType :db.type/double + :db/cardinality :db.cardinality/one}]]} }) diff --git a/src/clj/auto_ap/datomic/sales_orders.clj b/src/clj/auto_ap/datomic/sales_orders.clj index be008507..4f3742ad 100644 --- a/src/clj/auto_ap/datomic/sales_orders.clj +++ b/src/clj/auto_ap/datomic/sales_orders.clj @@ -3,7 +3,8 @@ [auto-ap.graphql.utils :refer [limited-clients]] [auto-ap.utils :refer [dollars=]] [clj-time.coerce :as c] - [datomic.api :as d])) + [datomic.api :as d] + [clojure.tools.logging :as log])) (defn <-datomic [result] (-> result @@ -75,7 +76,6 @@ true (merge-query {:query {:find ['?sort-default '?e] :where ['[?e :sales-order/date ?sort-default]]}}))] - (cond->> query true (d/query) @@ -92,6 +92,7 @@ payments)) (defn get-graphql [args] + (log/info "ARGS" args) (let [db (d/db (d/connect uri)) {ids-to-retrieve :ids matching-count :count} (raw-graphql-ids db args)] diff --git a/src/clj/auto_ap/graphql.clj b/src/clj/auto_ap/graphql.clj index 99e70098..69eb0eca 100644 --- a/src/clj/auto_ap/graphql.clj +++ b/src/clj/auto_ap/graphql.clj @@ -1,43 +1,35 @@ (ns auto-ap.graphql - (:require - [com.walmartlabs.lacinia.util :refer [attach-resolvers]] - [com.walmartlabs.lacinia.schema :as schema] - [com.walmartlabs.lacinia :refer [execute]] - [com.walmartlabs.lacinia.executor :as executor] - [com.walmartlabs.lacinia.resolve :as resolve] - [buddy.auth :refer [throw-unauthorized]] - [auto-ap.utils :refer [by]] - [auto-ap.logging :refer [info-event warn-event error-event]] - [auto-ap.graphql.utils :refer [assert-admin can-see-client? assert-can-see-client]] - [auto-ap.datomic :refer [uri merge-query]] - [datomic.api :as d] - [clj-time.coerce :as coerce] - [clj-time.core :as t] - [auto-ap.datomic.clients :as d-clients] - [auto-ap.datomic.checks :as d-checks] - [auto-ap.datomic.users :as d-users] - [auto-ap.datomic.invoices :as d-invoices] - [auto-ap.datomic.vendors :as d-vendors] - [auto-ap.graphql.users :as gq-users] - [auto-ap.graphql.yodlee-merchants :as ym] - [auto-ap.graphql.ledger :as gq-ledger] - [auto-ap.graphql.sales-orders :as gq-sales-orders] - [auto-ap.graphql.accounts :as gq-accounts] - [auto-ap.graphql.clients :as gq-clients] - [auto-ap.graphql.vendors :as gq-vendors] - [auto-ap.graphql.checks :as gq-checks] - [auto-ap.graphql.invoices :as gq-invoices] - [auto-ap.graphql.transactions :as gq-transactions] - [auto-ap.graphql.transaction-rules :as gq-transaction-rules] - [auto-ap.time :as time] - [clojure.walk :as walk] - [clojure.string :as str] - [clojure.tools.logging :as log] - [yang.time :refer [time-it]] - [unilog.context :as lc]) - (:import - (clojure.lang IPersistentMap))) - + (:require [auto-ap.datomic :refer [merge-query uri]] + [auto-ap.datomic.checks :as d-checks] + [auto-ap.datomic.sales-orders :as d-sales-orders] + [auto-ap.datomic.users :as d-users] + [auto-ap.graphql.accounts :as gq-accounts] + [auto-ap.graphql.checks :as gq-checks] + [auto-ap.graphql.clients :as gq-clients] + [auto-ap.graphql.expected-deposit :as gq-expected-deposit] + [auto-ap.graphql.invoices :as gq-invoices] + [auto-ap.graphql.ledger :as gq-ledger] + [auto-ap.graphql.sales-orders :as gq-sales-orders] + [auto-ap.graphql.transaction-rules :as gq-transaction-rules] + [auto-ap.graphql.transactions :as gq-transactions] + [auto-ap.graphql.users :as gq-users] + [auto-ap.graphql.utils :refer [assert-admin assert-can-see-client]] + [auto-ap.graphql.vendors :as gq-vendors] + [auto-ap.graphql.yodlee-merchants :as ym] + [auto-ap.logging :refer [error-event info-event warn-event]] + [auto-ap.time :as time] + [clj-time.coerce :as coerce] + [clj-time.core :as t] + [clojure.string :as str] + [clojure.tools.logging :as log] + [clojure.walk :as walk] + [com.walmartlabs.lacinia :refer [execute]] + [com.walmartlabs.lacinia.schema :as schema] + [com.walmartlabs.lacinia.util :refer [attach-resolvers]] + [datomic.api :as d] + [unilog.context :as lc] + [yang.time :refer [time-it]]) + (:import clojure.lang.IPersistentMap)) (def integreat-schema { @@ -221,6 +213,15 @@ :charges {:type '(list :charge)} :line_items {:type '(list :order_line_item)}}} + :expected_deposit + {:fields {:id {:type :id} + :location {:type 'String} + :external_id {:type 'String} + :total {:type :money} + :fee {:type :money} + :client {:type :client} + :date {:type 'String}}} + :check {:fields {:id {:type :id} :type {:type 'String} :amount {:type 'String} @@ -496,6 +497,17 @@ :statuses {:type '(list String)}} :resolve :get-all-payments} + :all_expected_deposits {:type '(list :expected_deposit) + :args {:client_id {:type :id} + :client_code {:type 'String}} + :resolve :get-all-expected-deposits} + + :all_sales_orders {:type '(list :sales_order) + :args {:client_id {:type :id} + :date_range {:type :date_range} + :client_code {:type 'String}} + :resolve :get-all-sales-orders} + :yodlee_merchants {:type '(list :yodlee_merchant) :args {} :resolve :get-yodlee-merchants} @@ -927,6 +939,12 @@ ->graphql (first (d-checks/get-graphql (assoc (<-graphql args) :count Integer/MAX_VALUE))))) +(defn get-all-sales-orders [context args value] + (assert-admin (:id context)) + (map + ->graphql + (first (d-sales-orders/get-graphql (assoc (<-graphql args) :count Integer/MAX_VALUE))))) + (defn get-user [context args value] (assert-admin (:id context)) @@ -1106,6 +1124,8 @@ (attach-resolvers {:get-invoice-page gq-invoices/get-invoice-page :get-all-invoices gq-invoices/get-all-invoices :get-all-payments get-all-payments + :get-all-expected-deposits gq-expected-deposit/get-all-expected-deposits + :get-all-sales-orders get-all-sales-orders :get-payment-page gq-checks/get-payment-page :get-potential-payments gq-checks/get-potential-payments :get-accounts gq-accounts/get-accounts diff --git a/src/clj/auto_ap/graphql/clients.clj b/src/clj/auto_ap/graphql/clients.clj index cc53f8bf..192c9349 100644 --- a/src/clj/auto_ap/graphql/clients.clj +++ b/src/clj/auto_ap/graphql/clients.clj @@ -1,17 +1,17 @@ (ns auto-ap.graphql.clients - (:require [auto-ap.datomic.clients :as d-clients] - [datomic.api :as d] - [auto-ap.datomic :refer [uri remove-nils]] + (:require [auto-ap.datomic :refer [audit-transact conn remove-nils]] + [auto-ap.datomic.clients :as d-clients] [auto-ap.graphql.utils :refer [->graphql assert-admin can-see-client?]] + [clj-time.coerce :as coerce] [clojure.string :as str] [clojure.tools.logging :as log] - [clj-time.coerce :as coerce])) + [datomic.api :as d])) (defn assert-client-code-is-unique [code] (when (seq (d/query {:query {:find '[?id] :in ['$ '?code] :where ['[?id :client/code ?code]]} - :args [(d/db (d/connect uri)) code]})) + :args [(d/db conn) code]})) (throw (ex-info "Client is not unique" {:validation-error (str "Client code '" code "' is not unique.")})))) (defn edit-client [context {:keys [edit_client new_bank_accounts] :as args} value] @@ -22,10 +22,10 @@ (let [client (when (:id edit_client) (d-clients/get-by-id (:id edit_client))) id (or (:db/id client) "new-client") _ (when client - @(d/transact (d/connect uri) - (into - (mapv (fn [lm] [:db/retractEntity (:db/id lm)]) (:client/location-matches client)) - (mapv (fn [m] [:db/retract (:db/id client) :client/matches m]) (:client/matches client))))) + (audit-transact (into + (mapv (fn [lm] [:db/retractEntity (:db/id lm)]) (:client/location-matches client)) + (mapv (fn [m] [:db/retract (:db/id client) :client/matches m]) (:client/matches client))) + (:id context))) transactions [(remove-nils {:db/id id :client/code (if (str/blank? (:client/code client)) (:code edit_client) @@ -80,7 +80,7 @@ ) (:forecasted_transactions edit_client))]] _ (log/info "upserting client" transactions) - result @(d/transact (d/connect uri) transactions)] + result (audit-transact transactions (:id context))] (-> result :tempids (get id) (or id) d-clients/get-by-id (update :client/location-matches (fn [lms] diff --git a/src/clj/auto_ap/graphql/ledger.clj b/src/clj/auto_ap/graphql/ledger.clj index 3cd590fb..a56d5546 100644 --- a/src/clj/auto_ap/graphql/ledger.clj +++ b/src/clj/auto_ap/graphql/ledger.clj @@ -1,19 +1,19 @@ (ns auto-ap.graphql.ledger - (:require [auto-ap.datomic :refer [uri remove-nils]] + (:require [auto-ap.datomic :refer [audit-transact-batch remove-nils uri]] + [auto-ap.datomic.accounts :as a] + [auto-ap.datomic.clients :as d-clients] [auto-ap.datomic.ledger :as l] [auto-ap.datomic.vendors :as d-vendors] - [auto-ap.datomic.accounts :as a] - [auto-ap.utils :refer [by dollars=]] - [auto-ap.time :refer [parse iso-date]] - [auto-ap.graphql.utils :refer [->graphql <-graphql limited-clients assert-admin result->page assert-can-see-client]] - [clj-time.coerce :as coerce] - [clojure.string :as str] - [clj-time.core :as time] + [auto-ap.graphql.utils + :refer + [->graphql <-graphql assert-admin assert-can-see-client result->page]] [auto-ap.parse.util :as parse] - [datomic.api :as d] - [auto-ap.parse.templates :as t] - [auto-ap.datomic.clients :as d-clients] + [auto-ap.utils :refer [by dollars=]] + [clj-time.coerce :as coerce] + [clj-time.core :as time] + [clojure.string :as str] [clojure.tools.logging :as log] + [datomic.api :as d] [unilog.context :as lc])) (defn get-ledger-page [context args value] @@ -217,7 +217,8 @@ :db/id vendor_name}))) {} (:entries args)) - all-vendors (into all-vendors new-hidden-vendors) + _ (audit-transact-batch (vec (vals new-hidden-vendors)) (:id context)) + all-vendors (by :vendor/name (d-vendors/get-graphql {})) all-accounts (transduce (map (comp str :account/numeric-code)) conj #{} (a/get-accounts)) transaction (doall (map (assoc-error (fn [entry] @@ -250,7 +251,8 @@ :journal-entry/client [:client/code (:client_code entry)] :journal-entry/date (coerce/to-date (parse/parse-value :clj-time "MM/dd/yyyy" (:date entry))) :journal-entry/external-id (:external_id entry) - :journal-entry/vendor (:db/id (all-vendors (:vendor_name entry))) + :journal-entry/vendor (:db/id (doto (all-vendors (:vendor_name entry)) + println)) :journal-entry/amount (:amount entry) :journal-entry/note (:note entry) :journal-entry/cleared-against (:cleared_against entry) @@ -286,12 +288,11 @@ retraction (mapv (fn [x] [:db/retractEntity [:journal-entry/external-id (:journal-entry/external-id x)]]) success)] (log/info "manual ledger import has " (count success) " new rows") - (run! (fn [batch] - (log/info "transacting retraction batch" (first batch)) - @(d/transact (d/connect uri) batch)) (partition-all 100 retraction)) - (run! (fn [batch] - (log/info "transacting success batch" (first batch)) - @(d/transact (d/connect uri) batch)) (partition-all 100 success)) + + + (audit-transact-batch retraction (:id context)) + (audit-transact-batch success (:id context)) + {:successful (map (fn [x] {:external_id (:journal-entry/external-id x)}) success) :existing [] :errors (map (fn [x] {:external_id (:external_id x) diff --git a/src/clj/auto_ap/graphql/transaction_rules.clj b/src/clj/auto_ap/graphql/transaction_rules.clj index 7f7cbac2..06c968bd 100644 --- a/src/clj/auto_ap/graphql/transaction_rules.clj +++ b/src/clj/auto_ap/graphql/transaction_rules.clj @@ -1,20 +1,26 @@ (ns auto-ap.graphql.transaction-rules - (:require [auto-ap.datomic.transaction-rules :as tr] - [auto-ap.datomic.transactions :as t] - [datomic.api :as d] - [auto-ap.datomic :refer [remove-nils uri merge-query replace-nils-with-retract]] + (:require [auto-ap.datomic + :refer + [audit-transact merge-query remove-nils replace-nils-with-retract uri]] + [auto-ap.datomic.transaction-rules :as tr] + [auto-ap.datomic.transactions :as d-transactions] + [auto-ap.graphql.utils + :refer + [->graphql + <-graphql + assert-admin + ident->enum-f + limited-clients + result->page + snake->kebab]] + [auto-ap.rule-matching :as rm] [auto-ap.utils :refer [dollars=]] - [auto-ap.graphql.utils :refer [->graphql <-graphql limited-clients assert-admin result->page snake->kebab ident->enum-f]] - [clj-time.coerce :as c] + [clj-time.coerce :as coerce] [clojure.set :as set] [clojure.string :as str] - [auto-ap.datomic.transactions :as d-transactions] - [auto-ap.rule-matching :as rm] - [clj-time.coerce :as coerce] - [clojure.tools.logging :as log]) - (:import [java.time.temporal ChronoField])) - - + [clojure.tools.logging :as log] + [datomic.api :as d]) + (:import java.time.temporal.ChronoField)) (defn get-transaction-rule-page [context args value] (let [args (assoc args :id (:id context)) @@ -52,7 +58,7 @@ (when-not (:transaction-rule/description existing-transaction-rule) (throw (ex-info "Transaction rule not found" {:validation-error "Transaction Rule not found"}))) - @(d/transact (d/connect uri) [[:db/retractEntity transaction_rule_id]]) + (audit-transact [[:db/retractEntity transaction_rule_id]] (:id context)) transaction_rule_id)) (defn upsert-transaction-rule [context {{:keys [id description yodlee_merchant_id note client_id bank_account_id amount_lte amount_gte vendor_id accounts transaction_approval_status dom_gte dom_lte]} :transaction_rule :as z} value] @@ -67,9 +73,10 @@ (nil? yodlee_merchant_id)) (let [error (str "You must provide a description or a yodlee merchant")] (throw (ex-info error {:validation-error error})))) - transaction (replace-nils-with-retract #:transaction-rule {:db/id (if id - id - "transaction-rule") + rule-id (if id + id + "transaction-rule") + transaction (replace-nils-with-retract #:transaction-rule {:db/id rule-id :description description :note note :client client_id @@ -84,16 +91,13 @@ (some->> transaction_approval_status name snake->kebab - (keyword "transaction-approval-status")) - :accounts (map transaction-rule-account->entity accounts)} + (keyword "transaction-approval-status"))} existing-transaction) - transaction (into transaction - (map (fn [d] - [:db/retract id :transaction-rule/accounts d]) - deleted)) - transaction-result @(d/transact (d/connect uri) transaction)] + transaction (conj transaction + [:reset rule-id :transaction-rule/accounts (map transaction-rule-account->entity accounts)]) + transaction-result (audit-transact transaction (:id context))] (-> (tr/get-by-id (or (-> transaction-result :tempids (get "transaction-rule")) id)) ((ident->enum-f :transaction-rule/transaction-approval-status)) diff --git a/src/clj/auto_ap/graphql/users.clj b/src/clj/auto_ap/graphql/users.clj index bc0f5804..58290ff5 100644 --- a/src/clj/auto_ap/graphql/users.clj +++ b/src/clj/auto_ap/graphql/users.clj @@ -1,8 +1,7 @@ (ns auto-ap.graphql.users - (:require [auto-ap.datomic.users :as d-users] - [datomic.api :as d] - [auto-ap.datomic :refer [uri]] - [auto-ap.graphql.utils :refer [->graphql assert-admin]])) + (:require [auto-ap.datomic :refer [audit-transact]] + [auto-ap.datomic.users :as d-users] + [auto-ap.graphql.utils :refer [->graphql assert-admin]])) (def role->datomic-role {":none" :user-role/none ":admin" :user-role/admin @@ -18,12 +17,11 @@ (filter #(not (new-clients %)) ))] - @(d/transact (d/connect uri) - - (-> [{:db/id (:db/id user) - :user/role (role->datomic-role (:role edit_user)) - :user/clients new-clients}] - (into (map (fn [c] [:db/retract (:db/id user) :user/clients c]) clients-to-remove)))) + (audit-transact (-> [{:db/id (:db/id user) + :user/role (role->datomic-role (:role edit_user)) + :user/clients new-clients}] + (into (map (fn [c] [:db/retract (:db/id user) :user/clients c]) clients-to-remove))) + (:id context)) (->graphql (d-users/get-by-id (:id edit_user))))) diff --git a/src/clj/auto_ap/graphql/vendors.clj b/src/clj/auto_ap/graphql/vendors.clj index 2cd2bb89..9c293d7c 100644 --- a/src/clj/auto_ap/graphql/vendors.clj +++ b/src/clj/auto_ap/graphql/vendors.clj @@ -3,7 +3,7 @@ [auto-ap.datomic.vendors :as d-vendors] [auto-ap.time :refer [parse iso-date]] [datomic.api :as d] - [auto-ap.datomic :refer [uri remove-nils audit-transact]] + [auto-ap.datomic :refer [uri remove-nils audit-transact conn]] [clj-time.coerce :as coerce] [clojure.set :as set])) @@ -86,19 +86,17 @@ (->graphql)))) (defn merge-vendors [context {:keys [from to]} value] - (let [conn (d/connect uri) - transaction (->> (d/query {:query {:find '[?x ?a2] + (let [transaction (->> (d/query {:query {:find '[?x ?a2] :in '[$ ?vendor-from ] :where ['[?x ?a ?vendor-from] '[?a :db/ident ?a2]]} - :args [(d/db conn) - from]}) + :args [(d/db conn) from]}) (mapcat (fn [[src attr]] [[:db/retract src attr from] [:db/add src attr to]]))) transaction (conj transaction [:db/retractEntity from])] - @(d/transact conn transaction) + (audit-transact transaction (:id context)) to)) (defn get-graphql [context args value] diff --git a/src/clj/auto_ap/routes/exports.clj b/src/clj/auto_ap/routes/exports.clj index 57cc5a8a..d3f794ca 100644 --- a/src/clj/auto_ap/routes/exports.clj +++ b/src/clj/auto_ap/routes/exports.clj @@ -1,19 +1,20 @@ (ns auto-ap.routes.exports (:require - [auto-ap.datomic.clients :as d-clients] - [auto-ap.datomic.vendors :as d-vendors] - [auto-ap.datomic.ledger :as d-ledger] - [auto-ap.datomic.transactions :as d-transactions] - [auto-ap.utils :refer [by]] - [auto-ap.parse :as parse] - [auto-ap.graphql :as graphql] - [auto-ap.graphql.utils :refer [<-graphql ->graphql assert-admin]] - [auto-ap.routes.utils :refer [wrap-secure]] - [clj-time.coerce :refer [to-date]] - [ring.middleware.json :refer [wrap-json-response]] - [compojure.core :refer [GET POST context defroutes wrap-routes]] - [clojure.string :as str] - [venia.core :as venia])) + [auto-ap.datomic.clients :as d-clients] + [auto-ap.datomic.vendors :as d-vendors] + [auto-ap.datomic.ledger :as d-ledger] + [auto-ap.datomic.transactions :as d-transactions] + [auto-ap.utils :refer [by]] + [auto-ap.parse :as parse] + [auto-ap.graphql :as graphql] + [auto-ap.graphql.utils :refer [<-graphql ->graphql assert-admin]] + [auto-ap.routes.utils :refer [wrap-secure]] + [clj-time.coerce :refer [to-date]] + [ring.middleware.json :refer [wrap-json-response]] + [compojure.core :refer [GET POST context defroutes wrap-routes]] + [clojure.string :as str] + [venia.core :as venia] + [clojure.tools.logging :as log])) (defroutes routes (wrap-routes @@ -48,6 +49,41 @@ payments (graphql/query identity (venia/graphql-query {:venia/queries (->graphql query)}))] (list (:all-payments (:data payments))))) + (GET "/sales/export" {:keys [query-params identity]} + (assert-admin identity) + (let [query [[:all_sales_orders + (cond-> {:client-code (query-params "client-code")} + (query-params "after") (assoc :date-range {:start (query-params "after") + :end nil})) + [:id + :location + :external_id + :total + :tip + :tax + :date + [:charges [:type_name :total :tip]] + [:line_items [:item_name :total :tax]] + [:client [:id :name :code]]]]] + payments (graphql/query identity (venia/graphql-query {:venia/queries (->graphql query)}))] + (seq (:all-sales-orders (:data payments))))) + + (GET "/expected-deposit/export" {:keys [query-params identity]} + (assert-admin identity) + (let [query [[:all_expected_deposits + (cond-> {:client-code (query-params "client-code")} + (query-params "after") (assoc :date-range {:start (query-params "after") + :end nil})) + [:id + [:client [:id :name :code]] + :location + :external_id + :total + :fee + :date]]] + payments (graphql/query identity (venia/graphql-query {:venia/queries (->graphql query)}))] + (seq (:all-expected-deposits (:data payments))))) + (GET "/clients/export" {:keys [query-params identity]} (assert-admin identity) (map <-graphql (d-clients/get-all))) diff --git a/src/clj/auto_ap/square/core.clj b/src/clj/auto_ap/square/core.clj index c2efd87d..a95b4551 100644 --- a/src/clj/auto_ap/square/core.clj +++ b/src/clj/auto_ap/square/core.clj @@ -31,6 +31,7 @@ :orders)) + (defn amount->money [amt] (* 0.01 (or (:amount amt) 0.0))) @@ -77,6 +78,53 @@ #_(daily-results) +(defn retry + ([f] (retry f 0)) + ([f i] + (if (< i 5) + (try + (f) + (catch Exception e + (log/warn "error pulling http " e) + (retry f (inc i)))) + (log/error "Too many failures")))) + +(defn settlements [l] + (log/info "Searching for" l) + (let [settlements (->> (client/get (str "https://connect.squareup.com/v1/" l "/settlements") + {:headers {"Authorization" "Bearer EAAAEO2xSqesDutZz71hz3eulKmrlKTiEqG3uZ4j25x5GYlOluQ2cj2JxNUXqXD7" + "Content-Type" "application/json"} + :as :json}) + :body + (map :id))] + (loop [[s & xs] (take 5 settlements) + result []] + (log/info "Looking up settlement " s " for location " l) + (let [n (:body (retry #(client/get (str "https://connect.squareup.com/v1/" l "/settlements/" s) + {:headers {"Authorization" "Bearer EAAAEO2xSqesDutZz71hz3eulKmrlKTiEqG3uZ4j25x5GYlOluQ2cj2JxNUXqXD7" + "Content-Type" "application/json"} + :as :json + :retry-handler (fn [ex try-count http-context] + (log/warn "Retrying after failure " ex) + (if (> try-count 4) false true))})))] + (if (seq xs) + (recur xs (conj result n)) + (conj result n)))))) + +(defn daily-settlements [] + (->> (locations) + (map :id) + (filter location_id->client-location) + (mapcat (fn [l] + (for [settlement (settlements l) + :let [[client loc] (location_id->client-location l)]] + #:expected-deposit {:external-id (str "square/settlement/" (:id settlement)) + :total (amount->money (:total_money settlement)) + :client [:client/code client] + :location loc + :fee (- (reduce + 0 (map (comp amount->money :fee_money) (:entries settlement)))) + :date (-> (:initiated_at settlement) + (coerce/to-date))}))))) (defn upsert [] (lc/with-context {:source "Square loading"} @@ -96,6 +144,25 @@ (catch Exception e (log/error e))))) +(defn upsert-settlements [] + (lc/with-context {:source "Square settlements loading "} + (try + (let [existing (->> (d/query {:query {:find ['?external-id] + :in ['$] + :where ['[_ :expected-deposit/external-id ?external-id]]} + :args [(d/db conn)]}) + (map first) + set) + _ (log/info (count existing) "settlements already exist") + to-create (filter #(not (existing (:expected-deposit/external-id %))) + (daily-settlements))] + (doseq [x (partition-all 20 to-create)] + (log/info "Loading expected deposit" (count x)) + @(d/transact conn x))) + (catch Exception e + (log/error e))) + (log/info "Done loading settlements"))) + (defn reset [] (->> (d/query {:query {:find ['?e] @@ -109,6 +176,10 @@ :start (scheduler/every (* 15 60 1000) upsert) :stop (scheduler/stop square-loader)) +(mount/defstate square-settlement-loader + :start (scheduler/every (* 15 60 1000) upsert-settlements) + :stop (scheduler/stop square-settlement-loader)) + (comment (daily-results) diff --git a/src/cljc/auto_ap/entities/transaction_rule.cljc b/src/cljc/auto_ap/entities/transaction_rule.cljc index 2fe80d95..f88d4ca5 100644 --- a/src/cljc/auto_ap/entities/transaction_rule.cljc +++ b/src/cljc/auto_ap/entities/transaction_rule.cljc @@ -15,8 +15,10 @@ true (catch js/Error _ false))))))) -(s/def ::amount-gte (s/nilable double?)) -(s/def ::amount-lte (s/nilable double?)) +(s/def ::amount-gte (s/or :double (s/nilable double?) + :string (s/nilable string?))) +(s/def ::amount-lte (s/or :double (s/nilable double?) + :string (s/nilable string?))) (s/def ::dom-gte (s/nilable int?)) (s/def ::dom-lte (s/nilable int?)) (s/def ::note (s/nilable string?)) diff --git a/src/cljs/auto_ap/views/components/typeahead.cljs b/src/cljs/auto_ap/views/components/typeahead.cljs index 19814606..b403324c 100644 --- a/src/cljs/auto_ap/views/components/typeahead.cljs +++ b/src/cljs/auto_ap/views/components/typeahead.cljs @@ -131,7 +131,7 @@ highlighted (r/atom nil) ] (r/create-class - {:reagent-render (fn [{:keys [matches on-change disabled match->text field value class not-found-description include-keys]}] + {:reagent-render (fn [{:keys [matches on-change disabled match->text field value class not-found-description include-keys style]}] (let [select (fn [entity] (when on-change (if (= :not-found entity) @@ -142,7 +142,7 @@ text (or (when value (match->text value)) @text) valid-matches (get-valid-entity-matches matches not-found-description not-found-value text match->text)] - [:div.typeahead + [:div.typeahead {:style style} (if disabled ^{:key (str "typeahead" text) } [:input.input {:disabled "disabled" :value text} ] diff --git a/src/cljs/auto_ap/views/pages/admin/accounts/form.cljs b/src/cljs/auto_ap/views/pages/admin/accounts/form.cljs index 81f0f571..6ee49961 100644 --- a/src/cljs/auto_ap/views/pages/admin/accounts/form.cljs +++ b/src/cljs/auto_ap/views/pages/admin/accounts/form.cljs @@ -139,10 +139,12 @@ [multi-field {:type "multi-field" :field [:client-overrides] :template [[typeahead-entity {:matches @(re-frame/subscribe [::subs/clients]) + :style {:width "20em"} :match->text :name :type "typeahead" :field [:client]}] [:input.input {:type "text" + :style {:width "20em"} :placeholder "Bubblegum" :field [:name]}] ]}]) diff --git a/src/cljs/auto_ap/views/pages/admin/rules/form.cljs b/src/cljs/auto_ap/views/pages/admin/rules/form.cljs index ce29b9c4..ed28a16f 100644 --- a/src/cljs/auto_ap/views/pages/admin/rules/form.cljs +++ b/src/cljs/auto_ap/views/pages/admin/rules/form.cljs @@ -47,6 +47,7 @@ ::can-submit :<- [::forms/form ::form] (fn [{:keys [data status]} _] + (println (s/explain ::entity/transaction-rule data)) (s/valid? ::entity/transaction-rule data))) (re-frame/reg-sub