summaryrefslogtreecommitdiff
path: root/pages/shop/order
diff options
context:
space:
mode:
Diffstat (limited to 'pages/shop/order')
-rw-r--r--pages/shop/order/_id.fnl35
-rw-r--r--pages/shop/order/index.fnl72
-rw-r--r--pages/shop/order/list.fnl72
-rw-r--r--pages/shop/order/state.fnl18
4 files changed, 197 insertions, 0 deletions
diff --git a/pages/shop/order/_id.fnl b/pages/shop/order/_id.fnl
new file mode 100644
index 0000000..5c929a3
--- /dev/null
+++ b/pages/shop/order/_id.fnl
@@ -0,0 +1,35 @@
+(import-macros {:compile-html HTML} :macros)
+(local templates (require :templates))
+(local shop (require :shop))
+(local lib (require :lib))
+(local dicts (require :dicts))
+
+(local texts
+ {:thanks
+ (lib.improve-typography
+ "Мы получили оповещение о заказе. В ближайшее время свяжемся с вами
+ для уточнения деталей.")})
+
+(fn content [order-lines authenticated?]
+ (local total (accumulate [sum 0 _ v (ipairs order-lines)]
+ (+ sum (* v.quantity v.price-per))))
+
+ [(HTML [:aside {}
+ (templates.header "/shop/order" authenticated?)])
+ (HTML
+ [:div {:class "content"}
+ [:div {:class "back"} [:a {:href "/shop"} "⟵ Обратно к списку"]]
+ [:section {}
+ [:h2 {} "Спасибо за заказ!"]
+ [:p {} texts.thanks]
+ (templates.order-lines order-lines)
+ [:div {} "—"]
+ [:div {} [:strong {} (.. "Итого: " (lib.format-price total) "₽")]]]])])
+
+(fn render [request db authenticated?]
+ (let [order-lines (shop.basket db request.params._id)]
+ (if (< 0 (# order-lines))
+ (values 200 {} (templates.base (content order-lines authenticated?)))
+ (values 302 {:Location "/shop"} ""))))
+
+{: render}
diff --git a/pages/shop/order/index.fnl b/pages/shop/order/index.fnl
new file mode 100644
index 0000000..7d25a40
--- /dev/null
+++ b/pages/shop/order/index.fnl
@@ -0,0 +1,72 @@
+(import-macros {:compile-html HTML} :macros)
+(local forms (require :forms))
+(local lib (require :lib))
+(local shop (require :shop))
+(local templates (require :templates))
+
+(local order-form
+ [{:title ""
+ :fields [
+ (forms.text-input "name" "Как к вам обращаться?" true)
+ (forms.text-input "contact" "Телеграм или E-mail для связи" true)
+ (forms.checkbox-input "correct-order" "Данные заказа верны" true)
+ (forms.checkbox-input "consent"
+ (..
+ "Я даю согласие ИП «Горенкин Владислав Константинович» (ИНН ...)"
+ " на хранение и обработку предоставленных персональных данных для уточнения деталей заказа.")
+ true)]}])
+
+(fn content-template [db basket data errors]
+ [(HTML
+ [:aside {}
+ (templates.header "/shop/order")
+ (if (< 0 (# basket)) (templates.basket basket "/shop/order") "")])
+ (HTML
+ [:div {:class "content"}
+ [:div {:class "back"} [:a {:href "/shop"} "⟵ Обратно к списку"]]
+ [:section {}
+ [:h2 {} "Оформление заказа"]
+ (forms.render-form order-form data errors)]])])
+
+(fn check-stocks [db basket]
+ (var error nil)
+
+ (each [_ line (ipairs basket) &until error]
+ (local product
+ (. (_G.must
+ (luna.db.query-assoc
+ db "SELECT title, stock FROM products WHERE name = ?"
+ [line.name]))
+ 1))
+ (when (< (- product.stock line.quantity) 0)
+ (set error (.. "К сожалению, товар «" product.title "» закончился."
+ " Пожалуйста, уберите его из корзины и попробуйте оформить заказ снова."))))
+
+ error)
+
+(fn render [request db]
+ (let [order-id (shop.order-id request)
+ basket (if order-id (shop.basket db order-id) [])]
+ (if (= request.method "POST")
+ (let [data (forms.html-form->data order-form request.form)
+ stock-error (check-stocks db basket)]
+ (if (not stock-error)
+ (do
+ (shop.place-order db order-id data.name data.contact data.consent)
+ (lib.notify
+ (.. "Новый заказ "
+ "<a href=\"https://whitetoad.ru/shop/order/" order-id "\">"
+ order-id
+ "</a> от <b>" data.name "</b>.\n\n"
+ "<a href=\"https://whitetoad.ru/shop/order/list\">Список заказов</a>"))
+ ;; redirect and clear order cookie
+ (values 302 {:Location (.. "/shop/order/" order-id)
+ :Set-Cookie "order=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT"}
+ ""))
+ (values 400 {}
+ (templates.base (content-template db basket data {:consent stock-error})))))
+ (if (< 0 (# basket))
+ (values 200 {} (templates.base (content-template db basket {} {})))
+ (values 302 {:Location "/shop"} "")))))
+
+{: render}
diff --git a/pages/shop/order/list.fnl b/pages/shop/order/list.fnl
new file mode 100644
index 0000000..2f64f7b
--- /dev/null
+++ b/pages/shop/order/list.fnl
@@ -0,0 +1,72 @@
+(import-macros {:compile-html HTML} :macros)
+(local lib (require :lib))
+(local templates (require :templates))
+(local dicts (require :dicts))
+
+(fn all-orders [db]
+ (lib.group-by
+ (_G.must
+ (luna.db.query-assoc db
+ "SELECT orders.id,
+ orders.placement_time AS \"placement-time\",
+ orders.name \"contact-name\",
+ orders.contact,
+ orders.state,
+ order_lines.id AS \"order-line-id\",
+ order_lines.quantity,
+ products.image1,
+ products.name,
+ products.type,
+ products.packaging,
+ products.title,
+ products.price_per \"price-per\"
+ FROM orders
+ INNER JOIN order_lines ON orders.id = order_lines.order_id
+ INNER JOIN products ON products.name = order_lines.product_name
+ WHERE orders.state != 'cart'
+ ORDER BY orders.placement_time DESC, orders.id"
+ {}))
+ [:id :placement-time :state :contact-name :contact]))
+
+(fn content [orders authenticated?]
+ [(HTML
+ [:aside {}
+ (templates.header "/shop/orders" authenticated?)])
+ (HTML
+ [:section {:class "content"}
+ [:div {:class "mb-1"} [:a {:href "/"} "⟵ Обратно к списку"]]
+ [:h2 {:class "product-page-title"} "Список заказов"]
+
+ (table.concat
+ (icollect [_ order (ipairs orders)]
+ (let [total (accumulate [sum 0 _ v (ipairs order.rest)]
+ (+ sum (* v.quantity v.price-per)))]
+ (HTML
+ [:section {:class "mb-2 font-size-0-875" }
+ [:h3 {:class "mb-0-25"} [:a {:href (.. "/shop/order/" order.id)} order.id]]
+ [:div {:class "mb-0-25 d-flex gap-0-25"}
+ (templates.order-state order.state)
+ (if (= "placed" order.state)
+ (HTML
+ [:form {:action "/shop/order/state" :method "POST"}
+ [:select {:name "state" :required true}
+ [:option {:value ""} "Новое состояние"]
+ [:option {:value "done"} "Выполнен"]
+ [:option {:value "canceled"} "Отменен"]]
+ [:input {:type "hidden" :name "id" :value order.id}]
+ [:button {:type "submit"} "Применить"]])
+ "")]
+ [:em {:class "d-block mb-0-5"}
+ "Дата: " order.placement-time
+ " / " "контакт: " order.contact
+ " / " "как обращаться: " order.contact-name]
+ (templates.order-lines order.rest)
+ [:div {} "—"]
+ [:div {} [:strong {} (.. "Итого: " (lib.format-price total) "₽")]]]))))])])
+
+(fn render [request db authenticated?]
+ (if authenticated?
+ (values 200 {} (templates.base (content (all-orders db) authenticated?)))
+ (values 302 {:Location "/"} "")))
+
+{: render}
diff --git a/pages/shop/order/state.fnl b/pages/shop/order/state.fnl
new file mode 100644
index 0000000..1ff1ca9
--- /dev/null
+++ b/pages/shop/order/state.fnl
@@ -0,0 +1,18 @@
+(local lib (require :lib))
+(local shop (require :shop))
+
+(fn render [request db authenticated?]
+ (if (and (= request.method "POST") authenticated?)
+ (let [vals (lib.parse-values request.body)]
+ (if (and vals vals.id
+ vals.state (or (= vals.state "done")
+ (= vals.state "canceled")))
+ (do
+ (if (= "done" vals.state)
+ (shop.finish-order db vals.id)
+ (shop.cancel-order db vals.id))
+ (values 302 {:Location "/shop/order/list"} ""))
+ (values 400 {} "bad body")))
+ (values 404 {} "not found")))
+
+{: render}