Files
integreat/src/clj/auto_ap/graphql/invoices.clj
2019-01-18 08:10:54 -08:00

133 lines
6.3 KiB
Clojure

(ns auto-ap.graphql.invoices
(:require [auto-ap.graphql.utils :refer [->graphql <-graphql assert-can-see-client assert-admin]]
[auto-ap.datomic.vendors :as d-vendors]
[auto-ap.datomic.invoices :as d-invoices]
[auto-ap.expense-accounts :as expense-accounts]
[auto-ap.time :refer [parse iso-date]]
[datomic.api :as d]
[auto-ap.datomic :refer [uri]]
[clj-time.coerce :as coerce]
[clojure.set :as set]))
(defn get-invoice-page [context args value]
(let [args (assoc args :id (:id context))
invoices (map
->graphql
(d-invoices/get-graphql (<-graphql (assoc args :id (:id context)))))
invoice-count (d-invoices/count-graphql (<-graphql args))]
[{:invoices invoices
:total invoice-count
:count (count invoices)
:start (:start args 0)
:end (+ (:start args 0) (count invoices))}]))
(defn get-all-invoices [context args value]
(assert-admin (:id context))
(map
->graphql
(d-invoices/get-graphql (assoc (<-graphql args)
:limit Integer/MAX_VALUE))))
(defn add-invoice [context {{:keys [total invoice_number location client_id vendor_id vendor_name date] :as in} :invoice} value]
(when (seq (d-invoices/find-conflicting {:invoice/invoice-number invoice_number
:invoice/vendor vendor_id
:invoice/client client_id}))
(throw (ex-info (str "Invoice '" invoice_number "' already exists.") {:invoice-number invoice_number})))
(let [_ (assert-can-see-client (:id context) client_id)
vendor (d-vendors/get-by-id vendor_id)
expense-account-id (:vendor/default-expense-account vendor)
_ (when-not expense-account-id
(throw (ex-info (str "Vendor '" (:vendor/name vendor) "' does not have a default expense acount.") {:vendor-id vendor_id} )))
transaction [{:db/id "invoice"
:invoice/invoice-number invoice_number
:invoice/client client_id
:invoice/vendor vendor_id
:invoice/total total
:invoice/outstanding-balance total
:invoice/status :invoice-status/unpaid
:invoice/date (coerce/to-date date)
:invoice/expense-accounts [{:invoice-expense-account/expense-account-id expense-account-id
:invoice-expense-account/location (get-in expense-accounts/expense-accounts [expense-account-id :location] location)
:invoice-expense-account/amount total}]}]
transaction-result @(d/transact (d/connect uri) transaction)
]
(-> (d-invoices/get-by-id (get-in transaction-result [:tempids "invoice"]))
(->graphql))))
(defn edit-invoice [context {{:keys [id invoice_number total vendor_id date client_id] :as in} :invoice} value]
(let [invoice (d-invoices/get-by-id id)
_ (when (seq (doto (d-invoices/find-conflicting {:db/id id
:invoice/invoice-number invoice_number
:invoice/vendor (:db/id (:invoice/vendor invoice))
:invoice/client (:db/id (:invoice/client invoice))})
println))
(throw (ex-info (str "Invoice '" invoice_number "' already exists.") {:invoice-number invoice_number})))
paid-amount (- (:invoice/total invoice) (:invoice/outstanding-balance invoice))
_ (assert-can-see-client (:id context) (:db/id (:invoice/client invoice)))
updated-invoice (d-invoices/update {:db/id id
:invoice/invoice-number invoice_number
:invoice/date (coerce/to-date (parse date iso-date))
:invoice/total total
:invoice/outstanding-balance (- total paid-amount)})]
(-> updated-invoice
(->graphql))))
(defn void-invoice [context {id :invoice_id} value]
(let [invoice (d-invoices/get-by-id id)
_ (assert-can-see-client (:id context) (:db/id (:invoice/client invoice)))
updated-invoice (d-invoices/update {:db/id id
:invoice/total 0.0
:invoice/outstanding-balance 0.0
:invoice/status :invoice-status/voided})]
;; TODO void out all expense accounts
(-> updated-invoice
(->graphql))))
(defn edit-expense-accounts [context args value]
;; TODO - Can expense account id be used as a unique field? It may compose with component, meaning
;; that you don't have to figure out which ones to delete and which ones to add. Just set to 0.
(assert-can-see-client (:id context) (:db/id (:invoice/client (d-invoices/get-by-id (:invoice_id args)))))
(let [current-expense-accounts (:invoice/expense-accounts (d-invoices/get-by-id (:invoice_id args)))
invoice-id (:invoice_id args)
specified-ids (->> (:expense_accounts args)
(map :id)
set)
existing-ids (->> current-expense-accounts
(map :db/id)
set)
deleted (set/difference existing-ids specified-ids)
updated (map
(fn [{:keys [id expense_account_id amount location]}]
{:db/id invoice-id
:invoice/expense-accounts [#:invoice-expense-account {
:amount (Double/parseDouble amount)
:db/id id
:expense-account-id expense_account_id
:location location}
]}
)
(:expense_accounts args))]
@(d/transact (d/connect uri) (concat updated
(map (fn [d] [:db/retract invoice-id :invoice/expense-accounts d])deleted)))
(->graphql
(d-invoices/get-by-id (:invoice_id args)))))