(ns auto-ap.ssr.transaction.edit (:require [auto-ap.datomic :refer [audit-transact conn pull-attr pull-ref]] [auto-ap.datomic.accounts :as d-accounts] [auto-ap.datomic.checks :as d-checks] [auto-ap.datomic.invoices :as d-invoices] [auto-ap.datomic.transactions :as d-transactions] [auto-ap.graphql.utils :refer [assert-can-see-client assert-not-locked exception->4xx]] [auto-ap.import.transactions :as i-transactions] [auto-ap.logging :as alog] [auto-ap.permissions :refer [wrap-must]] [auto-ap.routes.payments :as payment-route] [auto-ap.routes.transactions :as route] [auto-ap.routes.utils :refer [wrap-client-redirect-unauthenticated]] [auto-ap.rule-matching :as rm] [auto-ap.solr :as solr] [auto-ap.ssr-routes :as ssr-routes] [auto-ap.ssr.components :as com] [auto-ap.ssr.components.inputs :as inputs] [auto-ap.ssr.components.selmer :as sc] [auto-ap.ssr.grid-page-helper :as helper] [auto-ap.ssr.transaction.common :refer [grid-page]] [auto-ap.ssr.hx :as hx] [auto-ap.ssr.nested-form-params :refer [wrap-nested-form-params]] [auto-ap.ssr.selmer :as sel] [auto-ap.ssr.utils :refer [->db-id apply-middleware-to-all-handlers assert-schema check-allowance check-location-belongs entity-id form-validation-error html-response main-transformer modal-response path->name2 ref->enum-schema strip temp-id wrap-form-4xx-2 wrap-schema-enforce]] [auto-ap.time :as atime] [bidi.bidi :as bidi] [clj-time.coerce :as coerce] [clojure.edn :as edn] [clojure.string :as str] [datomic.api :as dc] [hiccup.util :as hu] [iol-ion.query :refer [dollars=]] [iol-ion.tx :refer [random-tempid]] [malli.core :as mc])) (declare render-full-form wrap-div) (def transaction-approval-status {:transaction-approval-status/unapproved "Unapproved" :transaction-approval-status/approved "Approved" :transaction-approval-status/suppressed "Client Review"}) (def row* (partial helper/row* grid-page)) (defn get-vendor [vendor-id] (dc/pull (dc/db conn) [:vendor/terms :vendor/automatically-paid-when-due {:vendor/default-account d-accounts/default-read :vendor/account-overrides [:vendor-account-override/client {:vendor-account-override/account d-accounts/default-read}]} {:vendor/terms-overrides [:vendor-terms-override/client :vendor-terms-override/terms]}] vendor-id)) (defn check-vendor-default-account [vendor-id] (some? (:vendor/default-account (get-vendor vendor-id)))) (defn require-approval [s] [:and s [:fn {:error/message "Approved transactions must have accounts assigned."} (fn [{:transaction/keys [accounts approval-status]}] (or (not= approval-status :transaction-approval-status/approved) (seq accounts)))]]) (def edit-form-schema (mc/schema [:and [:map [:db/id {:optional true} [:maybe entity-id]] [:action [:enum :apply-rule :unlink-payment :link-unpaid-invoices :link-autopay-invoices :link-payment :manual]] [:transaction/memo {:optional true} [:maybe [:string {:decode/string strip}]]] [:transaction/vendor {:optional true} [:maybe entity-id]] [:transaction/approval-status {:optional true} [:maybe (ref->enum-schema "transaction-approval-status")]] [:amount-mode {:optional true} [:maybe [:enum "$" "%"]]] [:mode {:optional true} [:maybe [:enum "simple" "advanced"]]] [:transaction/accounts {:optional true} [:maybe [:vector {:coerce? true} [:and [:map [:db/id {:optional true} [:maybe [:or temp-id entity-id]]] [:transaction-account/account [:and entity-id [:fn {:error/message "Not an allowed account."} #(check-allowance % :account/default-allowance)]]] [:transaction-account/location :string] [:transaction-account/amount :double]] [:fn {:error/fn (fn [r x] (:type r)) :error/path [:transaction-account/location]} (fn [iea] (check-location-belongs (:transaction-account/location iea) (:transaction-account/account iea)))]]]]]] [:multi {:dispatch :action} [:apply-rule [:map [:rule-id {:optional true} [:maybe entity-id]]]] [:unlink-payment [:map [:transaction-id entity-id]]] [:link-unpaid-invoices [:map [:unpaid-invoice-ids {:decode/string (fn [x] (edn/read-string x))} [:vector {:coerce? true} entity-id]]]] [:link-autopay-invoices [:map [:autopay-invoice-ids {:decode/string (fn [x] (edn/read-string x))} [:vector {:coerce? true} entity-id]]]] [:link-payment [:map [:payment-id entity-id]]] [:manual (require-approval [:map])]]])) (defn clientize-vendor [{:vendor/keys [terms-overrides automatically-paid-when-due default-account account-overrides] :as vendor} client-id] (if (nil? vendor) nil (let [terms-override (->> terms-overrides (filter (fn [to] (= (->db-id (:vendor-terms-override/client to)) client-id))) (map :vendor-terms-override/terms) first) account (or (->> account-overrides (filter (fn [to] (= (->db-id (:vendor-account-override/client to)) client-id))) (map :vendor-account-override/account) first) default-account) account (d-accounts/clientize account client-id) automatically-paid-when-due (->> automatically-paid-when-due (filter (fn [to] (= (->db-id to) client-id))) seq boolean) vendor (cond-> vendor terms-override (assoc :vendor/terms terms-override) true (assoc :vendor/automatically-paid-when-due automatically-paid-when-due :vendor/default-account account) true (dissoc :vendor/account-overrides :vendor/terms-overrides))] vendor))) (defn vendor-default-account [vendor-id client-id] (when vendor-id (let [vendor (get-vendor vendor-id) clientized (clientize-vendor vendor client-id)] (:vendor/default-account clientized)))) (defn location-select* "The location