From 6f7ee5e1d5c5927dac7057296e75a65cb3d9c32c Mon Sep 17 00:00:00 2001 From: Olli Pikarinen Date: Fri, 17 Jul 2026 10:03:07 +0300 Subject: [PATCH 1/6] chore: sort ns form --- src/cljs/rems/administration/catalogue_item.cljs | 2 +- test/clj/rems/application/test_commands.clj | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cljs/rems/administration/catalogue_item.cljs b/src/cljs/rems/administration/catalogue_item.cljs index cba7afed0..55c21d23d 100644 --- a/src/cljs/rems/administration/catalogue_item.cljs +++ b/src/cljs/rems/administration/catalogue_item.cljs @@ -6,8 +6,8 @@ [rems.administration.status-flags :as status-flags] [rems.atoms :as atoms :refer [document-title readonly-checkbox]] [rems.collapsible :as collapsible] - [rems.flash-message :as flash-message] [rems.common.roles :as roles] + [rems.flash-message :as flash-message] [rems.spinner :as spinner] [rems.text :refer [get-localized-title localize-time localized text]] [rems.util :refer [fetch]])) diff --git a/test/clj/rems/application/test_commands.clj b/test/clj/rems/application/test_commands.clj index ee1231a18..947ccc9f7 100644 --- a/test/clj/rems/application/test_commands.clj +++ b/test/clj/rems/application/test_commands.clj @@ -1,13 +1,13 @@ (ns rems.application.test-commands - (:require [clojure.test :refer [deftest is testing]] + (:require [clj-time.core :as time] + [clojure.test :refer [deftest is testing]] [rems.application.commands :as commands] [rems.application.events :as events] [rems.application.model :as model] [rems.common.util :refer [getx]] [rems.permissions :as permissions] - [rems.util :refer [assert-ex]] [rems.testing-util :refer [with-fixed-time]] - [clj-time.core :as time]) + [rems.util :refer [assert-ex]]) (:import [clojure.lang ExceptionInfo] [java.util UUID] [org.joda.time DateTime])) From c91c6d8bc3231ae1b15cce8be6aeccc57f8c8461 Mon Sep 17 00:00:00 2001 From: Olli Pikarinen Date: Fri, 17 Jul 2026 10:05:25 +0300 Subject: [PATCH 2/6] chore: normalize alias --- src/cljs/rems/flash_message.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cljs/rems/flash_message.cljs b/src/cljs/rems/flash_message.cljs index 6262140b3..d9de67112 100644 --- a/src/cljs/rems/flash_message.cljs +++ b/src/cljs/rems/flash_message.cljs @@ -1,5 +1,5 @@ (ns rems.flash-message - (:require [clojure.string :as string] + (:require [clojure.string :as str] [clojure.test :refer [deftest is are]] [clojure.walk] [re-frame.core :as rf] @@ -130,7 +130,7 @@ ((apply some-fn id-keys)) vector flatten - (string/join ", "))]))) + (str/join ", "))]))) (deftest test-argumentize-some-key (are [expected input] (= expected ((argumentize-some-key :a :b) input)) From 9893cbb08f8e5fc6929b237d7a351c34077c6ba2 Mon Sep 17 00:00:00 2001 From: Olli Pikarinen Date: Fri, 17 Jul 2026 15:12:39 +0300 Subject: [PATCH 3/6] Update manual --- manual/owner.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/manual/owner.md b/manual/owner.md index 890261768..711092bcd 100644 --- a/manual/owner.md +++ b/manual/owner.md @@ -180,7 +180,9 @@ This will **disable and archive the old catalogue item** and create a new catalo Catalogue items can be grouped under **Categories** that display as folding sub-headers on the catalogue page, also referred to as the **catalogue tree**. Categories can have subcategories and they can be nested in a tree-like structure. -Additionally, catalogue items may specify **complementary items** that must be applied for together with the **top-level catalogue item**. A top-level catalogue item may have many complementary items, and there can only be one top-level item for a given complementary item. A catalogue item that is already referenced by a top-level item may not have complementary items of it's own. +Additionally, catalogue items may specify **complementary items** that must be applied for together with the **top-level catalogue item**. To use this feature, configure `:enable-catalogue-hierarchy` to `true`. + +A top-level catalogue item may have many complementary items, and there can only be one top-level item for a given complementary item. A catalogue item that is already referenced by a top-level item may not have complementary items of it's own. Once the applicant has been given entitlement to the resource of a top-level item, it's complementary items become applicable on their own as ordinary catalogue items. ## Disabling, and archiving items From 56faf61b8a79ed8b0ec8631d523490c7194e1248 Mon Sep 17 00:00:00 2001 From: Olli Pikarinen Date: Fri, 17 Jul 2026 16:34:35 +0300 Subject: [PATCH 4/6] Update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f8b2ae669..82fb57cb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,9 @@ have notable changes. Changes since v2.39.1 +### Additions +- Added applicant UI for catalogue item hierarchy (see previous release note), and a configuration option to switch the feature on. (#3460, #3473). See (manual/owner.md)[manual/owner.md] and + ### Fixes - Fixed an issue where the application page would get stuck on a loop, displaying the "loading" indicator, when there was an error while creating the application (#3460). From 519e729da0bd25789023b94fa1cf1a017f488f7d Mon Sep 17 00:00:00 2001 From: Olli Pikarinen Date: Fri, 17 Jul 2026 16:18:27 +0300 Subject: [PATCH 5/6] Add feature flag for catalogue item hierarchy --- dev-config.edn | 1 + resources/config-defaults.edn | 4 + src/clj/rems/api/public.clj | 1 + src/clj/rems/application/commands.clj | 27 ++++--- src/clj/rems/service/public.clj | 1 + .../rems/administration/catalogue_item.cljs | 29 +++---- .../administration/create_catalogue_item.cljs | 7 +- src/cljs/rems/cart.cljs | 79 ++++++++++--------- test-config.edn | 1 + test/clj/rems/api/test_applications.clj | 7 +- test/clj/rems/application/test_commands.clj | 67 ++++++++++++---- test/clj/rems/test_browser.clj | 56 ++++++++++++- 12 files changed, 192 insertions(+), 88 deletions(-) diff --git a/dev-config.edn b/dev-config.edn index 70dfa76e0..fea74d86f 100644 --- a/dev-config.edn +++ b/dev-config.edn @@ -89,6 +89,7 @@ :enable-doi true :enable-duo true :enable-catalogue-tree true + :enable-catalogue-hierarchy true :enable-save-compaction true :enable-autosave true :enable-extended-logging true diff --git a/resources/config-defaults.edn b/resources/config-defaults.edn index 147c15181..67e8bf555 100644 --- a/resources/config-defaults.edn +++ b/resources/config-defaults.edn @@ -425,6 +425,10 @@ :enable-save-compaction false ; whether to merge consecutive saves into one event (EXPERIMENTAL) :enable-autosave false ; whether to automatically save the application as the applicant types (EXPERIMENTAL) + ;; Enable + ;; See `manual/owner.md` and `docs/architecture/023-catalogue-item-hierarchy.md` + :enable-catalogue-hierarchy false + ;; Extended logging for routes that should additionally log content of entity mutation. ;; logging is at INFO level and the log message is prefixed with "extended-logging (:uri request)" :enable-extended-logging false diff --git a/src/clj/rems/api/public.clj b/src/clj/rems/api/public.clj index 9fdc76932..90037c267 100644 --- a/src/clj/rems/api/public.clj +++ b/src/clj/rems/api/public.clj @@ -43,6 +43,7 @@ (s/optional-key :enable-catalogue-tree) s/Bool (s/optional-key :catalogue-tree-show-matching-parents) s/Bool (s/optional-key :enable-cart) s/Bool + (s/optional-key :enable-catalogue-hierarchy) s/Bool (s/optional-key :application-list-hidden-columns) [s/Keyword] (s/optional-key :enable-autosave) s/Bool (s/optional-key :show-resources-section) s/Bool diff --git a/src/clj/rems/application/commands.clj b/src/clj/rems/application/commands.clj index 4f71e526d..fd0e81a92 100644 --- a/src/clj/rems/application/commands.clj +++ b/src/clj/rems/application/commands.clj @@ -105,19 +105,20 @@ (defn- invalid-catalogue-item-hierarchy-error "For such an item in `catalogue-item-ids` that has a parent (dependent), check that the parent item is also included in `catalogue-item-ids`, or `actor` has an entitlement to the parent item's resource." - [catalogue-item-ids actor {:keys [get-catalogue-item get-dependents get-entitlements]}] - (let [entitled-to-resids (into #{} (map :resourceid) (get-entitlements actor)) - missing (into [] - (comp (mapcat (fn [id] (get-dependents {:catalogue-item/id id}))) - (map :catalogue-item/id) - (remove (set catalogue-item-ids)) - (map get-catalogue-item) - (filter (complement (comp entitled-to-resids :resource-id))) - (map :id)) - catalogue-item-ids)] - (when (seq missing) - {:errors [{:type :t.applications.errors/missing-top-level-item - :catalogue-item-ids missing}]}))) + [catalogue-item-ids actor {:keys [get-catalogue-item get-dependents get-entitlements get-config]}] + (when (:enable-catalogue-hierarchy (get-config)) + (let [entitled-to-resids (into #{} (map :resourceid) (get-entitlements actor)) + missing (into [] + (comp (mapcat (fn [id] (get-dependents {:catalogue-item/id id}))) + (map :catalogue-item/id) + (remove (set catalogue-item-ids)) + (map get-catalogue-item) + (filter (complement (comp entitled-to-resids :resource-id))) + (map :id)) + catalogue-item-ids)] + (when (seq missing) + {:errors [{:type :t.applications.errors/missing-top-level-item + :catalogue-item-ids missing}]})))) (defn- licenses-not-accepted-error [application userid] (when-not (application-util/accepted-licenses? application userid) diff --git a/src/clj/rems/service/public.clj b/src/clj/rems/service/public.clj index 22a5a79fc..e5ebc1fc0 100644 --- a/src/clj/rems/service/public.clj +++ b/src/clj/rems/service/public.clj @@ -36,6 +36,7 @@ :enable-catalogue-tree :catalogue-tree-show-matching-parents :enable-cart + :enable-catalogue-hierarchy :application-list-hidden-columns :enable-autosave :show-resources-section diff --git a/src/cljs/rems/administration/catalogue_item.cljs b/src/cljs/rems/administration/catalogue_item.cljs index 55c21d23d..939ff3632 100644 --- a/src/cljs/rems/administration/catalogue_item.cljs +++ b/src/cljs/rems/administration/catalogue_item.cljs @@ -8,6 +8,7 @@ [rems.collapsible :as collapsible] [rems.common.roles :as roles] [rems.flash-message :as flash-message] + [rems.globals] [rems.spinner :as spinner] [rems.text :refer [get-localized-title localize-time localized text]] [rems.util :refer [fetch]])) @@ -95,22 +96,22 @@ ^{:key (:category/id cat)} [atoms/link nil (str "/administration/categories/" (:category/id cat)) - (localized (:category/title cat))])))] - [inline-info-field (text :t.administration/catalogue-item-hierarchy-parent) - (let [{id :catalogue-item/id} (:part-of catalogue-item)] - (when id + (localized (:category/title cat))])))]] + (when (:enable-catalogue-hierarchy @rems.globals/config) + [[inline-info-field (text :t.administration/catalogue-item-hierarchy-parent) + (when-let [id (:catalogue-item/id (:part-of catalogue-item))] [atoms/link {:href (str "/administration/catalogue-items/" id) :label id - :target :_blank}]))] - [inline-info-field (text :t.administration/catalogue-item-hierarchy-children) - (when-let [children (:children catalogue-item)] - (doall - (for [{id :catalogue-item/id} children] - ^{:key id} - [atoms/link {:href (str "/administration/catalogue-items/" id) - :label id - :target :_blank}])))] - [inline-info-field (text :t.administration/start) (localize-time (:start catalogue-item))] + :target :_blank}])] + [inline-info-field (text :t.administration/catalogue-item-hierarchy-children) + (when-let [children (:children catalogue-item)] + (doall + (for [{id :catalogue-item/id} children] + ^{:key id} + [atoms/link {:href (str "/administration/catalogue-items/" id) + :label id + :target :_blank}])))]]) + [[inline-info-field (text :t.administration/start) (localize-time (:start catalogue-item))] [inline-info-field (text :t.administration/end) (localize-time (:end catalogue-item))] [inline-info-field (text :t.administration/active) [readonly-checkbox {:value (status-flags/active? catalogue-item)}]]]))}] (let [id (:id catalogue-item)] diff --git a/src/cljs/rems/administration/create_catalogue_item.cljs b/src/cljs/rems/administration/create_catalogue_item.cljs index 738e29127..d332fba1d 100644 --- a/src/cljs/rems/administration/create_catalogue_item.cljs +++ b/src/cljs/rems/administration/create_catalogue_item.cljs @@ -396,9 +396,10 @@ [catalogue-item-resource-field] [catalogue-item-form-field] [catalogue-item-categories-field] - (if parent-item - [catalogue-item-parent-field parent-item] - [catalogue-item-children-field]) + (when (:enable-catalogue-hierarchy @rems.globals/config) + (if parent-item + [catalogue-item-parent-field parent-item] + [catalogue-item-children-field])) [:div.col.commands [cancel-button catalogue-item-id] diff --git a/src/cljs/rems/cart.cljs b/src/cljs/rems/cart.cljs index 412c7836f..7d3e5b92a 100644 --- a/src/cljs/rems/cart.cljs +++ b/src/cljs/rems/cart.cljs @@ -6,6 +6,7 @@ [re-frame.core :as rf] [rems.atoms :as atoms] [rems.common.catalogue-util :refer [catalogue-items->ids]] + [rems.globals] [rems.guide-util :refer [component-info example]] [rems.text :refer [get-localized-title text text-format]] [rems.util :refer [navigate!]])) @@ -28,49 +29,53 @@ (update db ::cart #(remove (comp #{item-id} :id) %)))) (defn disable-remove-from-cart-button? [item cart-item-ids entitlement-catids] - (when-let [children-ids (seq (map :catalogue-item/id (:children item)))] - (and (not (contains? (set entitlement-catids) (:id item))) - (some (set cart-item-ids) children-ids)))) + (when (:enable-catalogue-hierarchy @rems.globals/config) + (when-let [children-ids (seq (map :catalogue-item/id (:children item)))] + (and (not (contains? (set entitlement-catids) (:id item))) + (some (set cart-item-ids) children-ids))))) (deftest test-disable-remove-from-cart-button - (testing "without hierarchy" - (is (nil? (disable-remove-from-cart-button? {:id 1} [1] nil)) - "can remove item") - (is (nil? (disable-remove-from-cart-button? {:id 1} [1 2 3] nil)) - "can remove item")) - (testing "with item and children in cart" - (is (disable-remove-from-cart-button? {:id 2 :children [{:catalogue-item/id 3}]} [2 3] nil) - "cannot remove parent") - (is (nil? (disable-remove-from-cart-button? {:id 3 :part-of {:catalogue-item/id 2}} [2 3] nil)) - "can remove child")) - (testing "with parent in cart" - (is (nil? (disable-remove-from-cart-button? {:id 2 :children [{:catalogue-item/id 3}]} [2] nil)) - "can remove item")) - (testing "with item and children in cart, and previous entitlement to item" - (is (false? (disable-remove-from-cart-button? {:id 2 :children [{:catalogue-item/id 3}]} [2 3] [2])) - "can remove item"))) + (with-redefs [rems.globals/config (atom {:enable-catalogue-hierarchy true})] + (testing "without hierarchy" + (is (nil? (disable-remove-from-cart-button? {:id 1} [1] nil)) + "can remove item") + (is (nil? (disable-remove-from-cart-button? {:id 1} [1 2 3] nil)) + "can remove item")) + (testing "with item and children in cart" + (is (disable-remove-from-cart-button? {:id 2 :children [{:catalogue-item/id 3}]} [2 3] nil) + "cannot remove parent") + (is (nil? (disable-remove-from-cart-button? {:id 3 :part-of {:catalogue-item/id 2}} [2 3] nil)) + "can remove child")) + (testing "with parent in cart" + (is (nil? (disable-remove-from-cart-button? {:id 2 :children [{:catalogue-item/id 3}]} [2] nil)) + "can remove item")) + (testing "with item and children in cart, and previous entitlement to item" + (is (false? (disable-remove-from-cart-button? {:id 2 :children [{:catalogue-item/id 3}]} [2 3] [2])) + "can remove item")))) (defn disable-add-to-cart-button? [item cart-item-ids entitlement-catids] - (when-let [parent-id (-> item :part-of :catalogue-item/id)] - (not (contains? (set/union (set cart-item-ids) (set entitlement-catids)) - parent-id)))) + (when (:enable-catalogue-hierarchy @rems.globals/config) + (when-let [parent-id (-> item :part-of :catalogue-item/id)] + (not (contains? (set/union (set cart-item-ids) (set entitlement-catids)) + parent-id))))) (deftest test-disable-add-to-cart-button - (testing "without hierarchy" - (is (nil? (disable-add-to-cart-button? {:id 1} nil nil)) - "can add to cart")) - (testing "with parent, and unrelated item in cart" - (is (nil? (disable-add-to-cart-button? {:id 2 :children [{:catalogue-item/id 3}]} [1] nil)) - "can add to cart")) - (testing "with child, without parent in cart" - (is (true? (disable-add-to-cart-button? {:id 3 :part-of {:catalogue-item/id 2}} [1] nil)) - "cannot add to cart")) - (testing "with child, and parent in cart" - (is (false? (disable-add-to-cart-button? {:id 3 :part-of {:catalogue-item/id 2}} [2] nil)) - "can add to cart")) - (testing "with child, without parent in cart, with entitlement to parent" - (is (false? (disable-add-to-cart-button? {:id 3 :part-of {:catalogue-item/id 2}} [1] [2])) - "can add to cart"))) + (with-redefs [rems.globals/config (atom {:enable-catalogue-hierarchy true})] + (testing "without hierarchy" + (is (nil? (disable-add-to-cart-button? {:id 1} nil nil)) + "can add to cart")) + (testing "with parent, and unrelated item in cart" + (is (nil? (disable-add-to-cart-button? {:id 2 :children [{:catalogue-item/id 3}]} [1] nil)) + "can add to cart")) + (testing "with child, without parent in cart" + (is (true? (disable-add-to-cart-button? {:id 3 :part-of {:catalogue-item/id 2}} [1] nil)) + "cannot add to cart")) + (testing "with child, and parent in cart" + (is (false? (disable-add-to-cart-button? {:id 3 :part-of {:catalogue-item/id 2}} [2] nil)) + "can add to cart")) + (testing "with child, without parent in cart, with entitlement to parent" + (is (false? (disable-add-to-cart-button? {:id 3 :part-of {:catalogue-item/id 2}} [1] [2])) + "can add to cart")))) (defn add-to-cart-button "Hiccup fragment that contains a button that adds the given item to the cart" diff --git a/test-config.edn b/test-config.edn index dcc52ce84..7c28a1f4e 100644 --- a/test-config.edn +++ b/test-config.edn @@ -70,5 +70,6 @@ :enable-doi true :enable-duo true :enable-catalogue-tree true + :enable-catalogue-hierarchy true :enable-voting true :enable-processing-states true} diff --git a/test/clj/rems/api/test_applications.clj b/test/clj/rems/api/test_applications.clj index bc60dfe81..b1d3cea68 100644 --- a/test/clj/rems/api/test_applications.clj +++ b/test/clj/rems/api/test_applications.clj @@ -771,7 +771,12 @@ (api-call :post "/api/applications/create" {:catalogue-item-ids [cat-id-child]} api-key user-id))) (is (match? {:success true :application-id number?} - (api-call :post "/api/applications/create" {:catalogue-item-ids [cat-id-parent cat-id-child]} api-key user-id))))) + (api-call :post "/api/applications/create" {:catalogue-item-ids [cat-id-parent cat-id-child]} api-key user-id))) + (testing "with hierarchy toggled off" + (with-redefs [rems.config/env (assoc rems.config/env :enable-catalogue-hierarchy false)] + (is (match? {:success true + :application-id number?} + (api-call :post "/api/applications/create" {:catalogue-item-ids [cat-id-child]} api-key user-id))))))) (testing "no forms" (let [no-form (test-helpers/create-catalogue-item! {:form-id nil}) diff --git a/test/clj/rems/application/test_commands.clj b/test/clj/rems/application/test_commands.clj index 947ccc9f7..4215e9320 100644 --- a/test/clj/rems/application/test_commands.clj +++ b/test/clj/rems/application/test_commands.clj @@ -503,7 +503,9 @@ :workflow/type :workflow/default} (ok-command {:type :application.command/create :actor applicant-user-id - :catalogue-item-ids [8 9]}))) + :catalogue-item-ids [8 9]} + nil + (assoc command-injections :get-config (constantly {:enable-catalogue-hierarchy true}))))) (is (= {:event/type :application.event/created :event/actor applicant-user-id :event/time (DateTime. 1000) @@ -516,27 +518,60 @@ :workflow/type :workflow/default} (ok-command {:type :application.command/create :actor applicant-user-id - :catalogue-item-ids [8]}))) - (is (= {:event/type :application.event/created - :event/actor applicant-user-id - :event/time (DateTime. 1000) - :application/id new-app-id - :application/external-id new-external-id - :application/resources [{:catalogue-item/id 11, :resource/ext-id "res-complementary-2"}] - :application/licenses [{:license/id 1}] - :application/forms [{:form/id 1}] - :workflow/id 1 - :workflow/type :workflow/default} - (ok-command {:type :application.command/create - :actor applicant-user-id - :catalogue-item-ids [11]})))) + :catalogue-item-ids [8]} + nil + (assoc command-injections :get-config (constantly {:enable-catalogue-hierarchy true}))))) + (testing "with existing entitlement to top-level resource" + (is (= {:event/type :application.event/created + :event/actor applicant-user-id + :event/time (DateTime. 1000) + :application/id new-app-id + :application/external-id new-external-id + :application/resources [{:catalogue-item/id 11, :resource/ext-id "res-complementary-2"}] + :application/licenses [{:license/id 1}] + :application/forms [{:form/id 1}] + :workflow/id 1 + :workflow/type :workflow/default} + (ok-command {:type :application.command/create + :actor applicant-user-id + :catalogue-item-ids [11]} + nil + (assoc command-injections :get-config (constantly {:enable-catalogue-hierarchy true}))))))) (testing "error: missing top level item" (is (= {:errors [{:type :t.applications.errors/missing-top-level-item :catalogue-item-ids [8]}]} (fail-command {:type :application.command/create :actor applicant-user-id - :catalogue-item-ids [9]})))) + :catalogue-item-ids [9]} + nil + (assoc command-injections :get-config (constantly {:enable-catalogue-hierarchy true})))))) + + (testing "with hierarchy toggled off" + (is (= {:application/external-id new-external-id + :application/id new-app-id + :event/time (DateTime. 1000) + :workflow/type :workflow/default + :application/resources [{:catalogue-item/id 9, :resource/ext-id "res-complementary"}] + :application/forms [{:form/id 1}] + :workflow/id 1 + :event/actor applicant-user-id + :event/type :application.event/created + :application/licenses [{:license/id 1}]} + (ok-command {:type :application.command/create + :actor applicant-user-id + :catalogue-item-ids [9]}) + (ok-command {:type :application.command/create + :actor applicant-user-id + :catalogue-item-ids [9]} + nil + command-injections) + (ok-command {:type :application.command/create + :actor applicant-user-id + :catalogue-item-ids [9]} + nil + (assoc command-injections :get-config (constantly {:enable-catalogue-hierarchy false})))) + "the feature is not used unless config key exists and is truthy")) (testing "cannot execute the create command for an existing application" (reset! allocated-new-ids? false) diff --git a/test/clj/rems/test_browser.clj b/test/clj/rems/test_browser.clj index 0abedd69b..ff5f13ab3 100644 --- a/test/clj/rems/test_browser.clj +++ b/test/clj/rems/test_browser.clj @@ -625,6 +625,9 @@ :headers {"x-rems-api-key" "42" "x-rems-user-id" (or userid "handler")}}))) +(defn- restart-handler! [] + (mount/stop #'rems.handler/handler #'rems.main/http-server) + (mount/start #'rems.handler/handler #'rems.main/http-server)) ;;; tests (deftest test-new-application @@ -2585,7 +2588,55 @@ (into [] (map btu/value-of-el) (-> (btu/query [{:class :application-resources}]) - (btu/children {:fn/has-class :application-resource})))))))))) + (btu/children {:fn/has-class :application-resource})))))))) + + (testing "with feature flag off" + (try + (with-redefs [rems.config/env (assoc rems.config/env :enable-catalogue-hierarchy false)] + (restart-handler!) + + (testing "admin ui components are not visible" + (logout) + (login-as "owner") + + (testing "create" + (go-to-admin "Catalogue items") + (btu/scroll-and-click :create-catalogue-item) + (wait-page-title "Create catalogue item – REMS") + (btu/wait-page-loaded) + (btu/screenshot "create-catalogue-item-when-hierarchy-off") + (is (= {} + (select-keys + (slurp-fields :catalogue-item-editor) + ["Complementary items" + "Top-level catalogue item"])))) + + (testing "edit" + (btu/go (str (btu/get-server-url) "administration/catalogue-items/edit/" (btu/context-getx :parent-2-id))) + (btu/wait-page-loaded) + (btu/screenshot "edit-catalogue-item-when-hierarchy-off") + (is (= {} + (select-keys + (slurp-fields :catalogue-item-editor) + ["Complementary items" + "Top-level catalogue item"]))))) + + (testing "applicant can apply for child item on it's own" + (logout) + (login-as "alice") + (go-to-catalogue) + (add-to-cart (btu/context-getx :child-1-title-en)) + (btu/screenshot "child-in-cart-when-hierarchy-off") + (click-cart-apply-single) + (wait-page-title (->> (get-application-from-api (get-application-id) "alice") + :application/external-id + (format "Application %s – REMS"))) + (btu/screenshot "application-draft") + (send-application))) + + (finally + (restart-handler!) + (btu/reload)))))) (deftest test-update-catalogue-item (btu/with-postmortem @@ -4269,9 +4320,6 @@ (rems.db.user-settings/delete-user-settings! "alice") (rems.db.user-settings/delete-user-settings! "elsa")))) ; clear language settings -(defn- restart-handler! [] - (mount/stop #'rems.handler/handler #'rems.main/http-server) - (mount/start #'rems.handler/handler #'rems.main/http-server)) (deftest test-hooks (btu/with-postmortem From 5430b701a055a77112e09954118cdc81c3c998dd Mon Sep 17 00:00:00 2001 From: Olli Pikarinen Date: Thu, 23 Jul 2026 14:32:36 +0300 Subject: [PATCH 6/6] Fix config documentation --- resources/config-defaults.edn | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/resources/config-defaults.edn b/resources/config-defaults.edn index 67e8bf555..76ee71b79 100644 --- a/resources/config-defaults.edn +++ b/resources/config-defaults.edn @@ -425,8 +425,7 @@ :enable-save-compaction false ; whether to merge consecutive saves into one event (EXPERIMENTAL) :enable-autosave false ; whether to automatically save the application as the applicant types (EXPERIMENTAL) - ;; Enable - ;; See `manual/owner.md` and `docs/architecture/023-catalogue-item-hierarchy.md` + ;; Enable catalogue hierarchy. See `manual/owner.md` and `docs/architecture/023-catalogue-item-hierarchy.md` :enable-catalogue-hierarchy false ;; Extended logging for routes that should additionally log content of entity mutation.