133 lines
6.3 KiB
Clojure
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)))))
|