diff --git a/packages/distribution/addon/abilities/distribution.js b/packages/distribution/addon/abilities/distribution.js
index d2e4d36b6..9525845ee 100644
--- a/packages/distribution/addon/abilities/distribution.js
+++ b/packages/distribution/addon/abilities/distribution.js
@@ -38,4 +38,13 @@ export default class DistributionAbility extends Ability {
).length > 0
);
}
+
+ get canReopen() {
+ return (
+ !this.config.ui.readonly &&
+ (this.config.permissions.reopenDistribution?.() ?? true) &&
+ this.distribution.controls.value?.case.edges[0]?.node.parentWorkItem
+ .isRedoable
+ );
+ }
}
diff --git a/packages/distribution/addon/components/cd-navigation/controls.hbs b/packages/distribution/addon/components/cd-navigation/controls.hbs
index dcc96f225..bd31a4b65 100644
--- a/packages/distribution/addon/components/cd-navigation/controls.hbs
+++ b/packages/distribution/addon/components/cd-navigation/controls.hbs
@@ -4,6 +4,7 @@
@route="new"
class="uk-icon-button"
title={{t "caluma.distribution.new.title"}}
+ data-test-new-inquiry
>
@@ -38,4 +39,19 @@
{{/if}}
{{/if}}
+ {{#if (can "reopen distribution")}}
+
+ {{/if}}
\ No newline at end of file
diff --git a/packages/distribution/addon/components/cd-navigation/controls.js b/packages/distribution/addon/components/cd-navigation/controls.js
index 17f181401..44f69194d 100644
--- a/packages/distribution/addon/components/cd-navigation/controls.js
+++ b/packages/distribution/addon/components/cd-navigation/controls.js
@@ -8,6 +8,7 @@ import { gql } from "graphql-tag";
import { decodeId } from "@projectcaluma/ember-core/helpers/decode-id";
import config from "@projectcaluma/ember-distribution/config";
import completeWorkItemMutation from "@projectcaluma/ember-distribution/gql/mutations/complete-work-item.graphql";
+import redoWorkItemMutation from "@projectcaluma/ember-distribution/gql/mutations/redo-work-item.graphql";
import incompleteInquiriesQuery from "@projectcaluma/ember-distribution/gql/queries/incomplete-inquiries.graphql";
export default class CdNavigationControlsComponent extends Component {
@@ -63,6 +64,30 @@ export default class CdNavigationControlsComponent extends Component {
}
}
+ @dropTask
+ *reopenDistribution() {
+ try {
+ if (!(yield confirm(this.intl.t("caluma.distribution.reopen-confirm")))) {
+ return;
+ }
+
+ const distributionWorkItemId = decodeId(
+ this.distribution.controls.value?.case.edges[0]?.node.parentWorkItem.id
+ );
+
+ yield this.apollo.mutate({
+ mutation: redoWorkItemMutation,
+ variables: {
+ workItem: distributionWorkItemId,
+ },
+ });
+
+ yield this.distribution.refetchControls();
+ } catch (e) {
+ this.notification.danger(this.intl.t("caluma.distribution.reopen-error"));
+ }
+ }
+
@dropTask
*sendInquiries() {
if (!(yield confirm(this.intl.t("caluma.distribution.send-confirm")))) {
diff --git a/packages/distribution/addon/gql/mutations/redo-work-item.graphql b/packages/distribution/addon/gql/mutations/redo-work-item.graphql
new file mode 100644
index 000000000..d0559a4c7
--- /dev/null
+++ b/packages/distribution/addon/gql/mutations/redo-work-item.graphql
@@ -0,0 +1,8 @@
+mutation RedoWorkItem($workItem: ID!) {
+ redoWorkItem(input: { id: $workItem }) {
+ workItem {
+ id
+ isRedoable
+ }
+ }
+}
diff --git a/packages/distribution/addon/gql/queries/control-work-items.graphql b/packages/distribution/addon/gql/queries/control-work-items.graphql
index 05b71e1c0..dd0961490 100644
--- a/packages/distribution/addon/gql/queries/control-work-items.graphql
+++ b/packages/distribution/addon/gql/queries/control-work-items.graphql
@@ -50,4 +50,15 @@ query ControlWorkItems(
}
}
}
+ case: allCases(filter: [{ id: $caseId }]) {
+ edges {
+ node {
+ id
+ parentWorkItem {
+ id
+ isRedoable
+ }
+ }
+ }
+ }
}
diff --git a/packages/distribution/addon/services/distribution.js b/packages/distribution/addon/services/distribution.js
index 158558778..782f965f5 100644
--- a/packages/distribution/addon/services/distribution.js
+++ b/packages/distribution/addon/services/distribution.js
@@ -29,10 +29,18 @@ export default class DistributionService extends Service {
navigation = trackedTask(this, this.fetchNavigation, () => [this.caseId]);
async refetch() {
- await getObservable(this.controls.value)?.refetch();
+ await this.refetchControls();
+ await this.refetchNavigation();
+ }
+
+ async refetchNavigation() {
await getObservable(this.navigation.value)?.refetch();
}
+ async refetchControls() {
+ await getObservable(this.controls.value)?.refetch();
+ }
+
@dropTask
*fetchControls(caseId) {
return yield this.apollo.watchQuery({
diff --git a/packages/distribution/tests/integration/components/cd-navigation-test.js b/packages/distribution/tests/integration/components/cd-navigation-test.js
index 9c4d156c3..60c75bdec 100644
--- a/packages/distribution/tests/integration/components/cd-navigation-test.js
+++ b/packages/distribution/tests/integration/components/cd-navigation-test.js
@@ -13,7 +13,7 @@ module("Integration | Component | cd-navigation", function (hooks) {
setupIntl(hooks);
hooks.beforeEach(function () {
- distribution(this.server, [
+ const distributionCase = distribution(this.server, [
{ id: "group1" },
{ id: "group2" },
{ id: "group3" },
@@ -21,7 +21,7 @@ module("Integration | Component | cd-navigation", function (hooks) {
{ id: "group5" },
]);
- this.caseId = this.server.db.cases[0].id;
+ this.caseId = distributionCase.id;
this.owner.lookup("service:caluma-options").currentGroupId = "group1";
this.owner.lookup("service:router").isActive = () => true;
diff --git a/packages/distribution/tests/integration/components/cd-navigation/controls-test.js b/packages/distribution/tests/integration/components/cd-navigation/controls-test.js
index 8018e6af5..97e1c4989 100644
--- a/packages/distribution/tests/integration/components/cd-navigation/controls-test.js
+++ b/packages/distribution/tests/integration/components/cd-navigation/controls-test.js
@@ -14,7 +14,7 @@ module("Integration | Component | cd-navigation/controls", function (hooks) {
setupIntl(hooks);
hooks.beforeEach(function () {
- distribution(this.server, [
+ const distributionCase = distribution(this.server, [
{ id: "group1" },
{ id: "group2" },
{ id: "group3" },
@@ -22,7 +22,7 @@ module("Integration | Component | cd-navigation/controls", function (hooks) {
{ id: "group5" },
]);
- this.caseId = this.server.db.cases[0].id;
+ this.caseId = distributionCase.id;
this.owner.lookup("service:caluma-options").currentGroupId = "group1";
Object.defineProperty(this.owner.lookup("service:distribution"), "caseId", {
value: this.caseId,
@@ -94,4 +94,21 @@ module("Integration | Component | cd-navigation/controls", function (hooks) {
)
);
});
+
+ test("it can redo the current distribution", async function (assert) {
+ await assert.expect(3);
+
+ await render(hbs``);
+
+ await click("[data-test-complete-distribution]");
+ await confirm();
+
+ assert.dom("[data-test-reopen-distribution]").exists();
+
+ await click("[data-test-reopen-distribution]");
+ await confirm();
+
+ assert.dom("[data-test-complete-distribution]").exists();
+ assert.dom("[data-test-new-inquiry]").exists();
+ });
});
diff --git a/packages/distribution/translations/de.yaml b/packages/distribution/translations/de.yaml
index e285b0321..bf3c744f5 100644
--- a/packages/distribution/translations/de.yaml
+++ b/packages/distribution/translations/de.yaml
@@ -4,14 +4,17 @@ caluma:
start: "Starten"
send: "Offene Anfragen versenden"
complete: "Zirkulation abschliessen"
+ reopen: "Zirkulation wiedereröffnen"
send-confirm: "Wollen Sie wirklich alle offenen Anfragen versenden?"
complete-confirm:
"Es gibt noch {count} offene {count, plural, =1 {Anfrage} other {Anfragen}}
in der aktuellen Zirkulation. Wenn Sie die Zirkulation abschliessen, werden
alle verbleibenden offenen Anfragen abgebrochen. Möchten Sie fortfahren?"
complete-confirm-empty: "Möchten Sie die Zirkulation wirklich abschliessen?"
+ reopen-confirm: "Wollen Sie die Zirkulation wirklich wiedereröffnen?"
send-error: "Fehler beim Versenden der offenen Anfragen"
complete-error: "Fehler beim Abschliessen der Zirkulation"
+ reopen-error: "Fehler beim Wiedereröffnen der Zirkulation"
more: "mehr"
less: "weniger"
diff --git a/packages/distribution/translations/en.yaml b/packages/distribution/translations/en.yaml
index 763588fe6..e2900b843 100644
--- a/packages/distribution/translations/en.yaml
+++ b/packages/distribution/translations/en.yaml
@@ -5,14 +5,17 @@ caluma:
send: "Send pending inquiries"
complete: "Complete circulation"
send-confirm: "Do you really want to send all pending inquiries?"
+ reopen: "Reopen circulation"
complete-confirm:
"There {count, plural, =1 {is} other {are}} {count} pending
{count, plural, =1 {inquiry} other {inquiries}} on the current
distribution. Completing the distribution will cause all remaining
pending inquiries to be canceled. Would you like to continue?"
complete-confirm-empty: "Do you really want to complete the distribution?"
+ reopen-confirm: "Do you really want to reopen the distribution?"
send-error: "Error while sending pending inquiries"
- complete-error: "Error while completing distribution"
+ complete-error: "Error while completing the distribution"
+ reopen-error: "Error while reopening the distribution"
more: "more"
less: "less"
diff --git a/packages/distribution/translations/fr.yaml b/packages/distribution/translations/fr.yaml
index c03656f4c..ba9949a56 100644
--- a/packages/distribution/translations/fr.yaml
+++ b/packages/distribution/translations/fr.yaml
@@ -5,13 +5,16 @@ caluma:
send: "Envoyer les demandes ouvertes"
complete: "Terminer la circulation"
send-confirm: "Voulez-vous vraiment envoyer toutes les demandes ouvertes ?"
+ reopen: "Rouvrir la procédure de circulation"
complete-confirm:
"Il y a {count} {count, plural, =1 {demande} other {demandes}} en
attente sur la distribution actuelle. Si vous terminez la distribution,
toutes les demandes en attente seront annulées. Voulez-vous continuer ?"
complete-confirm-empty: "Vous voulez vraiment fermer la circulation ?"
+ reopen-confirm: "Vous voulez vraiment rouvrir la circulation ?"
send-error: "Erreur lors de l'envoi des demandes ouvertes"
- complete-error: "Erreur lors de la terminaison de la distribution"
+ complete-error: "Erreur lors de la terminaison de la circulation"
+ reopen-error: "Erreur lors de la réouverture de la circulation"
more: "plus"
less: "moins"
diff --git a/packages/testing/addon-mirage-support/factories/work-item.js b/packages/testing/addon-mirage-support/factories/work-item.js
index 6997cdc89..c9e8db2b1 100644
--- a/packages/testing/addon-mirage-support/factories/work-item.js
+++ b/packages/testing/addon-mirage-support/factories/work-item.js
@@ -16,4 +16,5 @@ export default Factory.extend({
? faker.date.past()
: null;
},
+ redoable: () => false,
});
diff --git a/packages/testing/addon/mirage-graphql/mocks/work-item.js b/packages/testing/addon/mirage-graphql/mocks/work-item.js
index bb86090ee..cbdc0b4ba 100644
--- a/packages/testing/addon/mirage-graphql/mocks/work-item.js
+++ b/packages/testing/addon/mirage-graphql/mocks/work-item.js
@@ -27,6 +27,27 @@ export default class WorkItemMock extends BaseMock {
});
}
+ @register("RedoWorkItemPayload")
+ handleRedoWorkItem(_, { input }) {
+ const { id } = deserialize(input);
+ const workItem = this.collection.find(id);
+
+ if (workItem.taskId === "distribution") {
+ const caseId = workItem.childCaseId;
+
+ this.collection
+ .where({ caseId, taskId: "complete-distribution" })
+ .update({ status: "READY" });
+ this.collection
+ .where({ caseId, taskId: "create-inquiry" })
+ .update({ status: "READY" });
+ }
+
+ return this.handleSavePayload.fn.call(this, _, {
+ input: { id, redoable: false, status: "READY" },
+ });
+ }
+
@register("CompleteWorkItemPayload")
handleCompleteWorkItem(_, { input }) {
const { id } = deserialize(input);
@@ -102,6 +123,10 @@ export default class WorkItemMock extends BaseMock {
addressedGroups: workItem.addressedGroups,
});
} else if (taskId === "complete-distribution") {
+ this.collection
+ .where({ childCaseId: caseId, status: "READY", taskId: "distribution" })
+ .update({ status: "COMPLETED", redoable: true });
+
this.collection
.where({ caseId, status: "READY", taskId: "inquiry" })
.update({ status: "SKIPPED" });
@@ -116,7 +141,7 @@ export default class WorkItemMock extends BaseMock {
}
return this.handleSavePayload.fn.call(this, _, {
- input: { id: input.id, status: "COMPLETED" },
+ input: { id, status: "COMPLETED" },
});
}
}
diff --git a/packages/testing/addon/mirage-graphql/schema.graphql b/packages/testing/addon/mirage-graphql/schema.graphql
index cc7fcc899..df380b3cd 100644
--- a/packages/testing/addon/mirage-graphql/schema.graphql
+++ b/packages/testing/addon/mirage-graphql/schema.graphql
@@ -4048,6 +4048,13 @@ type WorkItem implements Node {
first: Int
last: Int
): WorkItemConnection!
+
+ """
+ This property potentially performs poorly if used in a large setof entries, as
+ the evaluation of the redoable jexl configurationcannot be performed on the
+ database level. Please use carefully.
+ """
+ isRedoable: Boolean
}
type WorkItemConnection {
diff --git a/packages/testing/addon/scenarios/distribution.js b/packages/testing/addon/scenarios/distribution.js
index e40e5fcf0..6e92f2516 100644
--- a/packages/testing/addon/scenarios/distribution.js
+++ b/packages/testing/addon/scenarios/distribution.js
@@ -73,6 +73,7 @@ export function createBlueprint(server) {
server.create("workflow", { slug: "distribution" });
server.create("workflow", { slug: "inquiry" });
+ server.create("task", { slug: "distribution" });
server.create("task", { slug: "create-inquiry" });
server.create("task", { slug: "complete-distribution" });
server.create("task", { slug: "inquiry" });
@@ -225,10 +226,17 @@ export function reviseInquiry(server, { inquiry }) {
}
export function createCase(server, { group }) {
+ const distributionWorkItem = server.create("work-item", {
+ taskId: "distribution",
+ status: "READY",
+ case: server.create("case"),
+ });
+
const distributionCase = server.create("case", {
id: "4222ab21-9c89-47de-98be-d62a8ed0ebeb",
status: "RUNNING",
workflowId: "distribution",
+ parentWorkItem: distributionWorkItem,
});
server.create("work-item", {
@@ -389,4 +397,6 @@ export default function (server, groups) {
status: "inquiry-answer-status-positive",
}),
});
+
+ return distributionCase;
}