(ns auto-ap.graphql.vendors (:require [auto-ap.datomic :refer [audit-transact conn remove-nils]] [auto-ap.datomic.vendors :as d-vendors] [auto-ap.graphql.utils :refer [->graphql <-graphql assert-admin assert-failure cleanse-query enum->keyword is-admin? result->page]] [auto-ap.solr :as solr] [clojure.set :as set] [clojure.string :as str] [datomic.api :as dc] [iol-ion.tx :refer [random-tempid]] [manifold.deferred :as de] [manifold.executor :as ex])) (defn can-user-edit-vendor? [vendor-id id] (if (is-admin? id) true (empty? (set/difference (set (->> (dc/q '[:find ?c :in $ ?v :where [?vu :vendor-usage/vendor ?v] [?vu :vendor-usage/client ?c] [?vu :vendor-usage/count ?d] [(>= ?d 0)]] (dc/db conn) vendor-id) (map first))) (set (map :db/id (:user/clients id))))))) (defn upsert-vendor [context {{:keys [id name hidden terms code print_as primary_contact plaid_merchant secondary_contact address default_account_id invoice_reminder_schedule schedule_payment_dom terms_overrides account_overrides] :as in} :vendor} _] (when (and id (not (can-user-edit-vendor? id (:id context)))) (assert-failure "This vendor is managed by Integreat. Please reach out to ben@integreatconsult.com for your changes.")) (when (->> schedule_payment_dom (group-by :client_id) vals (filter #(> (count %) 1)) seq) (assert-failure "Only one schedule payment override allowed per client.")) (when (->> terms_overrides (group-by :client_id) vals (filter #(> (count %) 1)) seq) (assert-failure "Only one terms override allowed per client.")) (when (->> account_overrides (group-by :client_id) vals (filter #(> (count %) 1)) seq) (assert-failure "Only one account override allowed per client.")) (let [hidden (if (is-admin? (:id context)) hidden false) terms-overrides (mapv (fn [to] #:vendor-terms-override {:client (:client_id to) :terms (:terms to) :db/id (or (:id to) (random-tempid))}) terms_overrides) account-overrides (mapv (fn [ao] #:vendor-account-override {:client (:client_id ao) :account (:account_id ao) :db/id (or (:id ao) (random-tempid))}) account_overrides) schedule-payment-dom (mapv (fn [ao] #:vendor-schedule-payment-dom {:client (:client_id ao) :dom (:dom ao) :db/id (or (:id ao) (random-tempid))}) schedule_payment_dom) transaction [:upsert-entity (cond-> #:vendor {:db/id (if id id "vendor") :name name :code code :hidden hidden :terms terms :print-as print_as :default-account default_account_id :invoice-reminder-schedule (keyword invoice_reminder_schedule) :address (when address (remove-nils #:address {:db/id (if (:id address) (:id address) "address") :street1 (:street1 address) :street2 (:street2 address) :city (:city address) :state (:state address) :zip (:zip address)})) :primary-contact (when primary_contact (remove-nils #:contact {:db/id (if (:id primary_contact) (:id primary_contact) "primary") :name (:name primary_contact) :phone (:phone primary_contact) :email (:email primary_contact)})) :secondary-contact (when secondary_contact (remove-nils #:contact {:db/id (if (:id secondary_contact) (:id secondary_contact) "secondary") :name (:name secondary_contact) :phone (:phone secondary_contact) :email (:email secondary_contact)})) :search-terms [name]} (is-admin? (:id context)) (assoc :vendor/legal-entity-name (:legal_entity_name in) :vendor/legal-entity-first-name (:legal_entity_first_name in) :vendor/legal-entity-middle-name (:legal_entity_middle_name in) :vendor/legal-entity-last-name (:legal_entity_last_name in) :vendor/legal-entity-tin (:legal_entity_tin in) :vendor/legal-entity-tin-type (enum->keyword (:legal_entity_tin_type in) "legal-entity-tin-type") :vendor/legal-entity-1099-type (enum->keyword (:legal_entity_1099_type in) "legal-entity-1099-type") :vendor/plaid-merchant plaid_merchant :vendor/account-overrides account-overrides :vendor/terms-overrides terms-overrides :vendor/schedule-payment-dom schedule-payment-dom :vendor/automatically-paid-when-due (:automatically_paid_when_due in)))] transaction-result (audit-transact [transaction] (:id context)) new-vendor (d-vendors/get-by-id (or (-> transaction-result :tempids (get "vendor")) id))] (auto-ap.solr/index-documents-raw auto-ap.solr/impl "vendors" [{"id" (:db/id new-vendor) "name" (:vendor/name new-vendor) "hidden" (boolean (:vendor/hidden new-vendor))}]) (-> new-vendor (->graphql)))) (defn merge-vendors [context {:keys [from to]} _] (let [transaction (->> (dc/q {:find '[?x ?a2] :in '[$ ?vendor-from] :where ['[?x ?a ?vendor-from] '[?a :db/ident ?a2]]} (dc/db conn) from) (mapcat (fn [[src attr]] [[:db/retract src attr from] [:db/add src attr to]]))) transaction (conj transaction [:db/retractEntity from])] (audit-transact transaction (:id context)) to)) (defn get-graphql [context args _] (assert-admin (:id context)) (let [args (assoc args :id (:id context)) [vendors vendors-count] (d-vendors/get-graphql (<-graphql args))] (result->page vendors vendors-count :vendors args))) (defn get-by-id [context args _] (->graphql (d-vendors/get-graphql-by-id (assoc args :id (:id context)) (:id args)))) (defn partial-match-first [query matches] (if-let [best-match (->> matches (filter (fn [[n]] (str/starts-with? (str/lower-case n) (str/lower-case query)))) first)] (cons best-match (filter (complement #{best-match}) matches)) matches)) (defn search [context args _] (if-let [query (not-empty (cleanse-query (:query args)))] (let [search-query (str "name:(" query ")")] (for [{:keys [id name]} (solr/query solr/impl "vendors" {"query" (cond-> search-query (not (is-admin? (:id context))) (str " hidden:false")) "fields" "id, name"})] {:id (Long/parseLong id) :name (first name)})) [])) (def single-thread (ex/fixed-thread-executor 1)) (defn rebuild-search-index [] (de/future-with single-thread (auto-ap.solr/index-documents-raw auto-ap.solr/impl "vendors" (for [[result] (dc/qseq {:query '[:find (pull ?v [:vendor/search-terms :db/id :vendor/name :vendor/hidden]) :in $ :where [?v :vendor/name]] :args [(dc/db conn)]})] {"id" (:db/id result) "name" (:vendor/name result) "hidden" (boolean (:vendor/hidden result))})))) #_(rebuild-search-index)