2021-03-06 21:55:41 +01:00

98 lines
4.3 KiB
Clojure

(ns chicken-master.orders
(:require [next.jdbc :as jdbc]
[next.jdbc.types :as jdbc.types]
[next.jdbc.sql :as sql]
[chicken-master.db :as db]
[chicken-master.products :as products]
[chicken-master.customers :as customers]
[chicken-master.time :as t]))
(defn upsert-order! [tx user-id customer-id {:keys [id day state notes]}]
(let [order {:customer_id customer-id
:notes notes
:status (some-> (or state "waiting") name jdbc.types/as-other)
:order_date (some-> day t/parse-date t/inst->timestamp)}]
(if (db/get-by-id tx user-id :orders id)
(do (sql/update! tx :orders order {:id id}) id)
(:orders/id (sql/insert! tx :orders (assoc order :user_id user-id))))))
(defn- structure-order [items]
{:id (-> items first :orders/id)
:notes (-> items first :orders/notes)
:state (-> items first :orders/status keyword)
:day (-> items first :orders/order_date (.toInstant) str (subs 0 10))
:who {:id (-> items first :customers/id)
:name (-> items first :customers/name)}
:products (into {}
(for [{:keys [order_products/amount products/name]} items]
[(keyword name) amount]))})
(def orders-query
"SELECT o.id, o.notes, o.status, o.order_date, c.id, c.name, p.name, op.amount
FROM orders o JOIN customers c ON o.customer_id = c.id
JOIN order_products op ON o.id = op.order_id
JOIN products p on p.id = op.product_id ")
(defn- get-orders [tx where params]
(->> (into [(if where (str orders-query where) orders-query)] params)
(sql/query tx)
(group-by :orders/id)
vals
(map structure-order)))
(defn get-order [tx user-id id]
(first (get-orders tx "WHERE o.id = ? AND o.user_id = ?" [id user-id])))
(defn get-all [user-id] (group-by :day (get-orders db/db-uri "WHERE o.user_id = ?" [user-id])))
(defn- orders-for-days [tx user-id & days]
(let [days (remove nil? days)]
(->> days
(map t/inst->timestamp)
(map jdbc.types/as-date)
(into [user-id])
(get-orders tx (str "WHERE o.user_id = ? AND o.order_date::date IN " (db/psql-list days)))
(group-by :day)
(merge (reduce #(assoc %1 (t/format-date %2) {}) {} days)))))
(defn replace! [user-id {:keys [who products] :as order}]
(jdbc/with-transaction [tx db/db-uri]
(let [customer-id (or (:id who)
(customers/get-by-name tx user-id who))
products-map (products/products-map tx user-id products)
previous-day (some->> order :id (db/get-by-id tx user-id :orders) :orders/order_date (.toInstant))
order-id (upsert-order! tx user-id customer-id order)]
(sql/delete! tx :order_products {:order_id order-id})
(sql/insert-multi! tx :order_products
[:order_id :product_id :amount]
(for [[n amount] products
:let [product-id (-> n name products-map)]
:when product-id]
[order-id product-id amount]))
(orders-for-days tx user-id previous-day (some-> order :day t/parse-date)))))
(defn delete! [user-id id]
(jdbc/with-transaction [tx db/db-uri]
(let [day (some->> id (db/get-by-id tx user-id :orders) :orders/order_date (.toInstant))]
(sql/delete! tx :orders {:id id :user_id user-id})
(when day (orders-for-days tx user-id day)))))
(defn change-state!
"Update the state of the given order and also modify the number of products available:
* when `fulfilled` decrement the number of products
* when `waiting` increment the number (as this means a previously fulfilled order has been returned)"
[user-id id state]
(jdbc/with-transaction [tx db/db-uri]
(let [order (get-order tx user-id id)
operator (condp = state
"fulfilled" "-"
"waiting" "+")]
(when (not= (:state order) (keyword state))
(doseq [[prod amount] (:products order)]
(jdbc/execute-one! tx
[(str "UPDATE products SET amount = amount " operator " ? WHERE name = ?")
amount (name prod)]))
(sql/update! tx :orders {:status (jdbc.types/as-other state)} {:id id}))
(orders-for-days tx user-id (-> order :day t/parse-date)))))