Allow "from here" in recurring events

This commit is contained in:
Daniel O'Connell 2022-05-12 20:24:09 +02:00
parent f7de4a90df
commit def43c1bed
4 changed files with 52 additions and 21 deletions

View File

@ -173,6 +173,10 @@
(group-by :day) (group-by :day)
(merge (reduce #(assoc %1 (t/format-date %2) {}) {} days))))) (merge (reduce #(assoc %1 (t/format-date %2) {}) {} days)))))
(defn get-fortnight [tx user-id from]
(->> (get-orders tx from (t/plus from 14 :days) "o.user_id = ?" [user-id])
(group-by :day)))
(defn replace! (defn replace!
([user-id order] (jdbc/with-transaction [tx db/db-uri] (replace! tx user-id order))) ([user-id order] (jdbc/with-transaction [tx db/db-uri] (replace! tx user-id order)))
([tx user-id {:keys [who products day order-date] :as order}] ([tx user-id {:keys [who products day order-date] :as order}]
@ -181,7 +185,7 @@
(products/update-products-mapping! tx user-id :order (products/update-products-mapping! tx user-id :order
(upsert-order! tx user-id customer-id order) (upsert-order! tx user-id customer-id order)
products) products)
(orders-for-days tx user-id day order-date)))) (get-fortnight tx user-id (t/earliest day order-date)))))
(defn change-state! (defn change-state!
"Update the state of the given order and also modify the number of products available: "Update the state of the given order and also modify the number of products available:
@ -208,7 +212,7 @@
(defn- full-delete [tx user-id id] (defn- full-delete [tx user-id id]
(when-let [{:orders/keys [order_date end_date]} (some->> id (db/get-by-id tx user-id :orders))] (when-let [{:orders/keys [order_date end_date]} (some->> id (db/get-by-id tx user-id :orders))]
(sql/delete! tx :orders {:id id :user_id user-id}) (sql/delete! tx :orders {:id id :user_id user-id})
(orders-for-days tx user-id order_date end_date))) (get-fortnight tx user-id (t/earliest order_date end_date))))
(defn delete! [user-id day action-type id] (defn delete! [user-id day action-type id]
(jdbc/with-transaction [tx db/db-uri] (jdbc/with-transaction [tx db/db-uri]

View File

@ -2,6 +2,7 @@
(:require [clojure.set :as set]) (:require [clojure.set :as set])
(:import [java.time Instant LocalDate ZoneOffset] (:import [java.time Instant LocalDate ZoneOffset]
[java.time.format DateTimeFormatter] [java.time.format DateTimeFormatter]
(java.time.temporal ChronoUnit)
[java.sql Timestamp] [java.sql Timestamp]
[org.dmfs.rfc5545.recur RecurrenceRule Freq] [org.dmfs.rfc5545.recur RecurrenceRule Freq]
[org.dmfs.rfc5545 DateTime])) [org.dmfs.rfc5545 DateTime]))
@ -11,12 +12,31 @@
(-> date (LocalDate/parse) (.atStartOfDay) (.toInstant ZoneOffset/UTC)) (-> date (LocalDate/parse) (.atStartOfDay) (.toInstant ZoneOffset/UTC))
(Instant/parse date))) (Instant/parse date)))
(def chrono-units {:centuries ChronoUnit/CENTURIES
:days ChronoUnit/DAYS
:decades ChronoUnit/DECADES
:eras ChronoUnit/ERAS
:forever ChronoUnit/FOREVER
:half-days ChronoUnit/HALF_DAYS
:hours ChronoUnit/HOURS
:micros ChronoUnit/MICROS
:millennia ChronoUnit/MILLENNIA
:millis ChronoUnit/MILLIS
:minutes ChronoUnit/MINUTES
:months ChronoUnit/MONTHS
:nanos ChronoUnit/NANOS
:seconds ChronoUnit/SECONDS
:weeks ChronoUnit/WEEKS
:years ChronoUnit/YEARS})
(defprotocol TimeHelpers (defprotocol TimeHelpers
(to-inst [d]) (to-inst [d])
(to-db-date [d]) (to-db-date [d])
(format-date [date]) (format-date [date])
(before [d1 d2]) (before [d1 d2])
(after [d1 d2])) (after [d1 d2])
(minus [d amount unit])
(plus [d amount unit]))
(extend-type Instant (extend-type Instant
TimeHelpers TimeHelpers
@ -27,7 +47,9 @@
(.withZone ZoneOffset/UTC) (.withZone ZoneOffset/UTC)
(.format date))) (.format date)))
(before [d1 d2] (.isBefore d1 d2)) (before [d1 d2] (.isBefore d1 d2))
(after [d1 d2] (.isBefore d2 d1))) (after [d1 d2] (.isBefore d2 d1))
(plus [d amount unit] (.plus d amount (chrono-units unit)))
(minus [d amount unit] (.minus d amount (chrono-units unit))))
(extend-type java.util.Date (extend-type java.util.Date
TimeHelpers TimeHelpers
@ -35,7 +57,9 @@
(to-db-date [d] (-> d to-inst to-db-date)) (to-db-date [d] (-> d to-inst to-db-date))
(format-date [date] (format-date (to-inst date))) (format-date [date] (format-date (to-inst date)))
(before [d1 d2] (< (.compareTo d1 d2) 0)) (before [d1 d2] (< (.compareTo d1 d2) 0))
(after [d1 d2] (> (.compareTo d1 d2) 0))) (after [d1 d2] (> (.compareTo d1 d2) 0))
(plus [d amount unit] (plus (to-inst d) amount unit))
(minus [d amount unit] (minus (to-inst d) amount unit)))
(extend-type java.lang.String (extend-type java.lang.String
TimeHelpers TimeHelpers
@ -43,10 +67,12 @@
(to-db-date [d] (-> d to-inst to-db-date)) (to-db-date [d] (-> d to-inst to-db-date))
(format-date [date] (format-date (to-inst date))) (format-date [date] (format-date (to-inst date)))
(before [d1 d2] (before (to-inst d1) (to-inst d2))) (before [d1 d2] (before (to-inst d1) (to-inst d2)))
(after [d1 d2] (after (to-inst d1) (to-inst d2)))) (after [d1 d2] (after (to-inst d1) (to-inst d2)))
(plus [d amount unit] (plus (to-inst d) amount unit))
(minus [d amount unit] (minus (to-inst d) amount unit)))
(defn earliest [& ds] (->> ds (map to-inst) (sort before) first)) (defn earliest [& ds] (->> ds (remove nil?) (map to-inst) (sort before) first))
(defn latest [& ds] (->> ds (map to-inst) (sort after) first)) (defn latest [& ds] (->> ds (remove nil?) (map to-inst) (sort after) first))
(defn between [d1 d2 d3] (and (not (before d2 d1)) (not (after d2 d3)))) (defn between [d1 d2 d3] (and (not (before d2 d1)) (not (after d2 d3))))
(defn same-day [d1 d2] (defn same-day [d1 d2]
(when (and d1 d2) (when (and d1 d2)
@ -86,6 +112,7 @@
(keep-indexed (fn [i d] (when (same-day d day) i))) (keep-indexed (fn [i d] (when (same-day d day) i)))
first)) first))
;; Recurrence handlers
(def freq-units {"day" Freq/DAILY "week" Freq/WEEKLY "month" Freq/MONTHLY "year" Freq/YEARLY}) (def freq-units {"day" Freq/DAILY "week" Freq/WEEKLY "month" Freq/MONTHLY "year" Freq/YEARLY})
(defn set-freq [rule freq] (defn set-freq [rule freq]
(.toString (.toString

View File

@ -165,10 +165,10 @@
(is (= table :orders)) (is (= table :orders))
(is (= by {:id 1 :user_id :user-id}))) (is (= by {:id 1 :user_id :user-id})))
sql/query (constantly (raw-order-row :id 4))] sql/query (constantly (raw-order-row :id 4))]
(is (= (sut/delete! :user-id nil nil 1) (is (= {"2020-01-01" [{:id 4, :notes "note", :state :waiting, :day "2020-01-01",
{"2020-01-01" [{:id 4, :notes "note", :state :waiting, :day "2020-01-01",
:who {:id 2, :name "mr blobby"}, :recurrence nil :who {:id 2, :name "mr blobby"}, :recurrence nil
:products {:eggs {:amount 12 :price nil} :milk {:amount 3 :price 423}}}]})))) :products {:eggs {:amount 12 :price nil} :milk {:amount 3 :price 423}}}]}
(sut/delete! :user-id nil nil 1)))))
(testing "nothing returned if no date set for the given order" (testing "nothing returned if no date set for the given order"
(with-redefs [jdbc/transact (fn [_ f & args] (apply f args)) (with-redefs [jdbc/transact (fn [_ f & args] (apply f args))
@ -191,19 +191,19 @@
sql/insert! (fn [_ table values] (swap! invocations conj ["inserting" table values]))] sql/insert! (fn [_ table values] (swap! invocations conj ["inserting" table values]))]
(testing "deleting without provided a date will remove the whole order" (testing "deleting without provided a date will remove the whole order"
(reset! invocations []) (reset! invocations [])
(is (= (sut/delete! :user-id nil nil 1) (is (= {"2020-01-01" [{:id 4, :notes "note", :state :waiting, :day "2020-01-01",
{"2020-01-01" [{:id 4, :notes "note", :state :waiting, :day "2020-01-01",
:who {:id 2, :name "mr blobby"}, :recurrence nil :who {:id 2, :name "mr blobby"}, :recurrence nil
:products {:eggs {:amount 12 :price nil} :milk {:amount 3 :price 423}}}]})) :products {:eggs {:amount 12 :price nil} :milk {:amount 3 :price 423}}}]}
(sut/delete! :user-id nil nil 1)))
(is (= [["deleting" :orders {:id 1 :user_id :user-id}]] (is (= [["deleting" :orders {:id 1 :user_id :user-id}]]
@invocations))) @invocations)))
(testing "a provided date is ignored and will full delete" (testing "a provided date is ignored and will full delete"
(reset! invocations []) (reset! invocations [])
(is (= (sut/delete! :user-id "2020-01-01" nil 1) (is (= {"2020-01-01" [{:id 4, :notes "note", :state :waiting, :day "2020-01-01",
{"2020-01-01" [{:id 4, :notes "note", :state :waiting, :day "2020-01-01",
:who {:id 2, :name "mr blobby"}, :recurrence nil :who {:id 2, :name "mr blobby"}, :recurrence nil
:products {:eggs {:amount 12 :price nil} :milk {:amount 3 :price 423}}}]})) :products {:eggs {:amount 12 :price nil} :milk {:amount 3 :price 423}}}]}
(sut/delete! :user-id "2020-01-01" nil 1)))
(is (= [["deleting" :orders {:id 1 :user_id :user-id}]] (is (= [["deleting" :orders {:id 1 :user_id :user-id}]]
@invocations))) @invocations)))
@ -213,10 +213,10 @@
{:orders/order_date #inst "2020-01-01"}))] {:orders/order_date #inst "2020-01-01"}))]
(reset! invocations []) (reset! invocations [])
(is (= (sut/delete! :user-id "2020-01-01" :single 1) (is (= {"2020-01-01" [{:id 4, :notes "note", :state :waiting, :day "2020-01-01",
{"2020-01-01" [{:id 4, :notes "note", :state :waiting, :day "2020-01-01",
:who {:id 2, :name "mr blobby"}, :recurrence nil :who {:id 2, :name "mr blobby"}, :recurrence nil
:products {:eggs {:amount 12 :price nil} :milk {:amount 3 :price 423}}}]})) :products {:eggs {:amount 12 :price nil} :milk {:amount 3 :price 423}}}]}
(sut/delete! :user-id "2020-01-01" :single 1)))
(is (= [["deleting" :orders {:id 1 :user_id :user-id}]] (is (= [["deleting" :orders {:id 1 :user_id :user-id}]]
@invocations))))))) @invocations)))))))

View File

@ -117,7 +117,7 @@
:order-type-edit :order-type-edit
[:div [:div
(html/input :single "tylko to" {:type :radio :name :type-choose :defaultChecked true}) (html/input :single "tylko to" {:type :radio :name :type-choose :defaultChecked true})
;; (html/input :from-here "od tego" {:type :radio :name :type-choose}) (html/input :from-here "od tego" {:type :radio :name :type-choose})
(html/input :all "wszystkie" {:type :radio :name :type-choose})] (html/input :all "wszystkie" {:type :radio :name :type-choose})]
;; On success ;; On success
:on-submit (fn [form] (re-frame/dispatch (conj event (form "type-choose" "single"))) :close-modal)))) :on-submit (fn [form] (re-frame/dispatch (conj event (form "type-choose" "single"))) :close-modal))))