From f1ef35ed40aa902b3283a46aa0c7625c90bfe9a8 Mon Sep 17 00:00:00 2001 From: amentis Date: Tue, 9 Jul 2024 15:02:16 +0300 Subject: [PATCH] blueprint description template multiplicity changes --- .../errorcode/ErrorThesaurusProperties.java | 10 +++++ .../model/persist/DescriptionPersist.java | 1 + .../description/DescriptionServiceImpl.java | 42 +++++++++++++++++++ .../web/src/main/resources/config/errors.yml | 5 ++- .../core/common/enum/respone-error-code.ts | 3 ++ .../editor/description-editor.component.ts | 2 +- .../plan-editor.component.ts | 8 ++++ frontend/src/assets/i18n/baq.json | 3 +- frontend/src/assets/i18n/de.json | 3 +- frontend/src/assets/i18n/en.json | 3 +- frontend/src/assets/i18n/es.json | 3 +- frontend/src/assets/i18n/gr.json | 3 +- frontend/src/assets/i18n/hr.json | 3 +- frontend/src/assets/i18n/pl.json | 3 +- frontend/src/assets/i18n/pt.json | 3 +- frontend/src/assets/i18n/sk.json | 3 +- frontend/src/assets/i18n/sr.json | 3 +- frontend/src/assets/i18n/tr.json | 3 +- 18 files changed, 91 insertions(+), 13 deletions(-) diff --git a/backend/core/src/main/java/org/opencdmp/errorcode/ErrorThesaurusProperties.java b/backend/core/src/main/java/org/opencdmp/errorcode/ErrorThesaurusProperties.java index 280bad596..070710b6a 100644 --- a/backend/core/src/main/java/org/opencdmp/errorcode/ErrorThesaurusProperties.java +++ b/backend/core/src/main/java/org/opencdmp/errorcode/ErrorThesaurusProperties.java @@ -378,4 +378,14 @@ public class ErrorThesaurusProperties { public void setRequestHasExpired(ErrorDescription requestHasExpired) { this.requestHasExpired = requestHasExpired; } + + private ErrorDescription maxDescriptionsExceeded; + + public ErrorDescription getMaxDescriptionsExceeded() { + return maxDescriptionsExceeded; + } + + public void setMaxDescriptionsExceeded(ErrorDescription maxDescriptionsExceeded) { + this.maxDescriptionsExceeded = maxDescriptionsExceeded; + } } diff --git a/backend/core/src/main/java/org/opencdmp/model/persist/DescriptionPersist.java b/backend/core/src/main/java/org/opencdmp/model/persist/DescriptionPersist.java index 8d83fb872..3899436d2 100644 --- a/backend/core/src/main/java/org/opencdmp/model/persist/DescriptionPersist.java +++ b/backend/core/src/main/java/org/opencdmp/model/persist/DescriptionPersist.java @@ -222,6 +222,7 @@ public class DescriptionPersist { .must(() -> !this.isNull(item.getProperties())) .failOn(DescriptionPersist._properties).failWith(this.messageSource.getMessage("Validation_Required", new Object[]{DescriptionPersist._properties}, LocaleContextHolder.getLocale())), this.spec() + .iff(() -> item.getStatus() == DescriptionStatus.Finalized) .must(() -> this.isDescriptionTemplateMaxMultiplicityValid(finalPlanBlueprintEntity, item.getPlanId(), item.getPlanDescriptionTemplateId(), this.isValidGuid(item.getId()))) .failOn(DescriptionPersist._descriptionTemplateId).failWith(this.messageSource.getMessage("Validation.InvalidDescriptionTemplateMultiplicity", new Object[]{DescriptionPersist._descriptionTemplateId}, LocaleContextHolder.getLocale())), this.refSpec() diff --git a/backend/core/src/main/java/org/opencdmp/service/description/DescriptionServiceImpl.java b/backend/core/src/main/java/org/opencdmp/service/description/DescriptionServiceImpl.java index 20bf21530..3e3075e95 100644 --- a/backend/core/src/main/java/org/opencdmp/service/description/DescriptionServiceImpl.java +++ b/backend/core/src/main/java/org/opencdmp/service/description/DescriptionServiceImpl.java @@ -39,6 +39,7 @@ import org.opencdmp.commons.types.descriptiontemplate.importexport.fielddata.Ref import org.opencdmp.commons.types.notification.DataType; import org.opencdmp.commons.types.notification.FieldInfo; import org.opencdmp.commons.types.notification.NotificationFieldData; +import org.opencdmp.commons.types.planblueprint.SectionEntity; import org.opencdmp.commons.types.reference.DefinitionEntity; import org.opencdmp.convention.ConventionService; import org.opencdmp.data.*; @@ -221,6 +222,14 @@ public class DescriptionServiceImpl implements DescriptionService { if (!data.getPlanId().equals(model.getPlanId())) throw new MyValidationException(this.errors.getPlanCanNotChange().getCode(), this.errors.getPlanCanNotChange().getMessage()); if (!data.getPlanDescriptionTemplateId().equals(model.getPlanDescriptionTemplateId())) throw new MyValidationException(this.errors.getPlanDescriptionTemplateCanNotChange().getCode(), this.errors.getPlanDescriptionTemplateCanNotChange().getMessage()); } else { + PlanEntity planEntity = this.entityManager.find(PlanEntity.class, model.getPlanId(), true); + if (planEntity == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{model.getPlanId(), Plan.class.getSimpleName()}, LocaleContextHolder.getLocale())); + + PlanBlueprintEntity planBlueprintEntity = this.entityManager.find(PlanBlueprintEntity.class, planEntity.getBlueprintId()); + if (planBlueprintEntity == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{planEntity.getBlueprintId(), PlanBlueprint.class.getSimpleName()}, LocaleContextHolder.getLocale())); + + if (!this.isDescriptionTemplateMaxMultiplicityValid(planBlueprintEntity, model.getPlanId(),model.getPlanDescriptionTemplateId(), false)) throw new MyValidationException(this.errors.getMaxDescriptionsExceeded().getCode(), this.errors.getMaxDescriptionsExceeded().getMessage()); + data = new DescriptionEntity(); data.setId(UUID.randomUUID()); data.setIsActive(IsActive.Active); @@ -275,6 +284,37 @@ public class DescriptionServiceImpl implements DescriptionService { this.elasticService.persistDescription(data); return this.builderFactory.builder(DescriptionBuilder.class).authorize(AuthorizationFlags.AllExceptPublic).build(BaseFieldSet.build(fields, Description._id), data); } + + private boolean isDescriptionTemplateMaxMultiplicityValid(PlanBlueprintEntity planBlueprintEntity, UUID planId, UUID planDescriptionTemplateId, Boolean isUpdate){ + org.opencdmp.commons.types.planblueprint.DefinitionEntity definition = this.xmlHandlingService.fromXmlSafe(org.opencdmp.commons.types.planblueprint.DefinitionEntity.class, planBlueprintEntity.getDefinition()); + if (definition == null || this.conventionService.isListNullOrEmpty(definition.getSections())) return true; + + PlanDescriptionTemplateEntity planDescriptionTemplateEntity = this.queryFactory.query(PlanDescriptionTemplateQuery.class).disableTracking().ids(planDescriptionTemplateId).isActive(IsActive.Active).planIds(planId).first(); + if (planDescriptionTemplateEntity == null) return true; + + List descriptionEntities = this.queryFactory.query(DescriptionQuery.class).disableTracking().authorize(AuthorizationFlags.AllExceptPublic).planIds(planId).planDescriptionTemplateIds(planDescriptionTemplateId).isActive(IsActive.Active).collect(); + + for (SectionEntity section: definition.getSections()) { + if (planDescriptionTemplateEntity.getSectionId().equals(section.getId()) && section.getHasTemplates() && !this.conventionService.isListNullOrEmpty(section.getDescriptionTemplates())){ + int descriptionsCount; + if (isUpdate) descriptionsCount = -1; + else descriptionsCount = 0; + + for (org.opencdmp.commons.types.planblueprint.DescriptionTemplateEntity sectionDescriptionTemplate: section.getDescriptionTemplates()) { + if (sectionDescriptionTemplate.getDescriptionTemplateGroupId().equals(planDescriptionTemplateEntity.getDescriptionTemplateGroupId())){ + for (DescriptionEntity description: descriptionEntities){ + if (description.getPlanDescriptionTemplateId().equals(planDescriptionTemplateEntity.getId())) descriptionsCount++; + } + if (sectionDescriptionTemplate.getMaxMultiplicity() != null && sectionDescriptionTemplate.getMaxMultiplicity() <= descriptionsCount) return false; + } + + } + + } + + } + return true; + } @Override public void updateDescriptionTemplate(UpdateDescriptionTemplatePersist model) throws InvalidApplicationException, IOException { logger.debug(new MapLogEntry("update description template").And("model", model)); @@ -950,12 +990,14 @@ public class DescriptionServiceImpl implements DescriptionService { DescriptionTemplateEntity descriptionTemplateEntity = this.entityManager.find(DescriptionTemplateEntity.class, data.getDescriptionTemplateId(), true); if (descriptionTemplateEntity == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{data.getDescriptionTemplateId(), DescriptionTemplate.class.getSimpleName()}, LocaleContextHolder.getLocale())); + persist.setId(data.getId()); persist.setLabel(data.getLabel()); persist.setStatus(DescriptionStatus.Finalized); persist.setDescription(data.getDescription()); persist.setDescriptionTemplateId(data.getDescriptionTemplateId()); persist.setPlanId(data.getPlanId()); persist.setPlanDescriptionTemplateId(data.getPlanDescriptionTemplateId()); + persist.setHash(this.conventionService.hashValue(data.getUpdatedAt())); org.opencdmp.commons.types.descriptiontemplate.DefinitionEntity definition = this.xmlHandlingService.fromXmlSafe(org.opencdmp.commons.types.descriptiontemplate.DefinitionEntity.class, descriptionTemplateEntity.getDefinition()); diff --git a/backend/web/src/main/resources/config/errors.yml b/backend/web/src/main/resources/config/errors.yml index 8b2171f00..0e937d749 100644 --- a/backend/web/src/main/resources/config/errors.yml +++ b/backend/web/src/main/resources/config/errors.yml @@ -118,4 +118,7 @@ error-thesaurus: message: Invite user already confirmed requestHasExpired: code: 143 - message: Request has expired \ No newline at end of file + message: Request has expired + maxDescriptionsExceeded: + code: 144 + message: Max descriptions exceeded for this plan \ No newline at end of file diff --git a/frontend/src/app/core/common/enum/respone-error-code.ts b/frontend/src/app/core/common/enum/respone-error-code.ts index 8391bdcd7..7678cfd2c 100644 --- a/frontend/src/app/core/common/enum/respone-error-code.ts +++ b/frontend/src/app/core/common/enum/respone-error-code.ts @@ -41,6 +41,7 @@ export enum ResponseErrorCode { PrefillingSourceCodeExists = 141, InviteUserAlreadyConfirmed = 142, RequestHasExpired = 143, + MaxDescriptionsExceeded = 144, // Notification & Annotation Errors InvalidApiKey = 200, @@ -153,6 +154,8 @@ export class ResponseErrorCodeHelper { return language.instant("GENERAL.BACKEND-ERRORS.INVITE-USER-ALREADY-CONFIRMED"); case ResponseErrorCode.RequestHasExpired: return language.instant("GENERAL.BACKEND-ERRORS.REQUEST-HAS-EXPIRED"); + case ResponseErrorCode.MaxDescriptionsExceeded: + return language.instant("GENERAL.BACKEND-ERRORS.MAX-DESCRIPTION-EXCEEDED"); default: return language.instant("GENERAL.SNACK-BAR.NOT-FOUND"); } diff --git a/frontend/src/app/ui/description/editor/description-editor.component.ts b/frontend/src/app/ui/description/editor/description-editor.component.ts index bade72910..edb1312d3 100644 --- a/frontend/src/app/ui/description/editor/description-editor.component.ts +++ b/frontend/src/app/ui/description/editor/description-editor.component.ts @@ -261,7 +261,7 @@ export class DescriptionEditorComponent extends BaseEditor x.planDescriptionTemplate.descriptionTemplateGroupId == sectionDescriptionTemplate.descriptionTemplateGroupId); - if (commonDescriptions && commonDescriptions.length >= sectionDescriptionTemplate.maxMultiplicity) { + if (commonDescriptions && commonDescriptions.length > sectionDescriptionTemplate.maxMultiplicity) { rejectedPlanDescriptionTemplates.push.apply(rejectedPlanDescriptionTemplates, commonDescriptions.map(x => x.planDescriptionTemplate)); } } diff --git a/frontend/src/app/ui/plan/plan-editor-blueprint/plan-editor.component.ts b/frontend/src/app/ui/plan/plan-editor-blueprint/plan-editor.component.ts index 74f6ec141..5dc8ef427 100644 --- a/frontend/src/app/ui/plan/plan-editor-blueprint/plan-editor.component.ts +++ b/frontend/src/app/ui/plan/plan-editor-blueprint/plan-editor.component.ts @@ -674,16 +674,24 @@ export class PlanEditorComponent extends BaseEditor imple } let multiplicityValidResults: boolean[] = []; + const descriptionTemplatesGroupIds = this.formGroup.get('descriptionTemplates').get(section.id.toString()).value as Guid[] || []; + let descriptionTemplatesGroupIdsWithMaxMultitplicity: Guid[] = []; section.descriptionTemplates.forEach(sectionDescriptionTemplate => { if (sectionDescriptionTemplate.maxMultiplicity != null) { const count = descriptions.filter(x => x.planDescriptionTemplate.descriptionTemplateGroupId == sectionDescriptionTemplate.descriptionTemplateGroupId).length || 0; if (count >= sectionDescriptionTemplate.maxMultiplicity) multiplicityValidResults.push(false); else multiplicityValidResults.push(true); + descriptionTemplatesGroupIdsWithMaxMultitplicity.push(sectionDescriptionTemplate.descriptionTemplateGroupId); } else { multiplicityValidResults.push(true); } }) + if (descriptionTemplatesGroupIdsWithMaxMultitplicity.length > 0 && descriptionTemplatesGroupIds.length > 0){ + const descriptionTemplatesWithoutMaxMultiplicity = descriptionTemplatesGroupIds.filter(x => !descriptionTemplatesGroupIdsWithMaxMultitplicity.map(y => y).includes(x)) || []; + if (descriptionTemplatesWithoutMaxMultiplicity.length > 0 && this.formGroup.pristine) return true; + } + if (multiplicityValidResults.includes(true)) return true else return false; } else { diff --git a/frontend/src/assets/i18n/baq.json b/frontend/src/assets/i18n/baq.json index 35baa9912..9ecedc9d5 100644 --- a/frontend/src/assets/i18n/baq.json +++ b/frontend/src/assets/i18n/baq.json @@ -78,7 +78,8 @@ "PREFILLING-SOURCE-CODE-EXISTS": "The prefilling source code you provided already exists. Please choose a different code.", "DUPLICATE-PLAN-USER": "You can't invite authors with same role and plan section more than once", "INVITE-USER-ALREADY-CONFIRMED": "Ιnvitation has already confirmed", - "REQUEST-HAS-EXPIRED": "Request has expired" + "REQUEST-HAS-EXPIRED": "Request has expired", + "MAX-DESCRIPTION-EXCEEDED": "This plan has reached the maximun descriptions for this description template" }, "FORM-VALIDATION-DISPLAY-DIALOG": { "WARNING": "Kontuz!", diff --git a/frontend/src/assets/i18n/de.json b/frontend/src/assets/i18n/de.json index 1f65d4f32..c8b268631 100644 --- a/frontend/src/assets/i18n/de.json +++ b/frontend/src/assets/i18n/de.json @@ -78,7 +78,8 @@ "PREFILLING-SOURCE-CODE-EXISTS": "The prefilling source code you provided already exists. Please choose a different code.", "DUPLICATE-PLAN-USER": "You can't invite authors with same role and plan section more than once", "INVITE-USER-ALREADY-CONFIRMED": "Ιnvitation has already confirmed", - "REQUEST-HAS-EXPIRED": "Request has expired" + "REQUEST-HAS-EXPIRED": "Request has expired", + "MAX-DESCRIPTION-EXCEEDED": "This plan has reached the maximun descriptions for this description template" }, "FORM-VALIDATION-DISPLAY-DIALOG": { "WARNING": "Warnung!", diff --git a/frontend/src/assets/i18n/en.json b/frontend/src/assets/i18n/en.json index 43a40dc41..ca9eebc36 100644 --- a/frontend/src/assets/i18n/en.json +++ b/frontend/src/assets/i18n/en.json @@ -78,7 +78,8 @@ "PREFILLING-SOURCE-CODE-EXISTS": "The prefilling source code you provided already exists. Please choose a different code.", "DUPLICATE-PLAN-USER": "You can't invite authors with same role and plan section more than once", "INVITE-USER-ALREADY-CONFIRMED": "Ιnvitation has already confirmed", - "REQUEST-HAS-EXPIRED": "Request has expired" + "REQUEST-HAS-EXPIRED": "Request has expired", + "MAX-DESCRIPTION-EXCEEDED": "This plan has reached the maximun descriptions for this description template" }, "FORM-VALIDATION-DISPLAY-DIALOG": { "WARNING": "Warning!", diff --git a/frontend/src/assets/i18n/es.json b/frontend/src/assets/i18n/es.json index 74b5f8907..309814238 100644 --- a/frontend/src/assets/i18n/es.json +++ b/frontend/src/assets/i18n/es.json @@ -78,7 +78,8 @@ "PREFILLING-SOURCE-CODE-EXISTS": "The prefilling source code you provided already exists. Please choose a different code.", "DUPLICATE-PLAN-USER": "You can't invite authors with same role and plan section more than once", "INVITE-USER-ALREADY-CONFIRMED": "Ιnvitation has already confirmed", - "REQUEST-HAS-EXPIRED": "Request has expired" + "REQUEST-HAS-EXPIRED": "Request has expired", + "MAX-DESCRIPTION-EXCEEDED": "This plan has reached the maximun descriptions for this description template" }, "FORM-VALIDATION-DISPLAY-DIALOG": { "WARNING": "Atención!", diff --git a/frontend/src/assets/i18n/gr.json b/frontend/src/assets/i18n/gr.json index 3465460a9..18c4a2d4d 100644 --- a/frontend/src/assets/i18n/gr.json +++ b/frontend/src/assets/i18n/gr.json @@ -78,7 +78,8 @@ "PREFILLING-SOURCE-CODE-EXISTS": "The prefilling source code you provided already exists. Please choose a different code.", "DUPLICATE-PLAN-USER": "You can't invite authors with same role and plan section more than once", "INVITE-USER-ALREADY-CONFIRMED": "Ιnvitation has already confirmed", - "REQUEST-HAS-EXPIRED": "Request has expired" + "REQUEST-HAS-EXPIRED": "Request has expired", + "MAX-DESCRIPTION-EXCEEDED": "This plan has reached the maximun descriptions for this description template" }, "FORM-VALIDATION-DISPLAY-DIALOG": { "WARNING": "Προσοχή!", diff --git a/frontend/src/assets/i18n/hr.json b/frontend/src/assets/i18n/hr.json index c8ca651b3..36ffd20f9 100644 --- a/frontend/src/assets/i18n/hr.json +++ b/frontend/src/assets/i18n/hr.json @@ -78,7 +78,8 @@ "PREFILLING-SOURCE-CODE-EXISTS": "The prefilling source code you provided already exists. Please choose a different code.", "DUPLICATE-PLAN-USER": "You can't invite authors with same role and plan section more than once", "INVITE-USER-ALREADY-CONFIRMED": "Ιnvitation has already confirmed", - "REQUEST-HAS-EXPIRED": "Request has expired" + "REQUEST-HAS-EXPIRED": "Request has expired", + "MAX-DESCRIPTION-EXCEEDED": "This plan has reached the maximun descriptions for this description template" }, "FORM-VALIDATION-DISPLAY-DIALOG": { "WARNING": "Oprez!", diff --git a/frontend/src/assets/i18n/pl.json b/frontend/src/assets/i18n/pl.json index 05fb8a848..d9a83b71c 100644 --- a/frontend/src/assets/i18n/pl.json +++ b/frontend/src/assets/i18n/pl.json @@ -78,7 +78,8 @@ "PREFILLING-SOURCE-CODE-EXISTS": "The prefilling source code you provided already exists. Please choose a different code.", "DUPLICATE-PLAN-USER": "You can't invite authors with same role and plan section more than once", "INVITE-USER-ALREADY-CONFIRMED": "Ιnvitation has already confirmed", - "REQUEST-HAS-EXPIRED": "Request has expired" + "REQUEST-HAS-EXPIRED": "Request has expired", + "MAX-DESCRIPTION-EXCEEDED": "This plan has reached the maximun descriptions for this description template" }, "FORM-VALIDATION-DISPLAY-DIALOG": { "WARNING": "Ostrzeżenie!", diff --git a/frontend/src/assets/i18n/pt.json b/frontend/src/assets/i18n/pt.json index 29b82c956..eb751f52d 100644 --- a/frontend/src/assets/i18n/pt.json +++ b/frontend/src/assets/i18n/pt.json @@ -78,7 +78,8 @@ "PREFILLING-SOURCE-CODE-EXISTS": "The prefilling source code you provided already exists. Please choose a different code.", "DUPLICATE-PLAN-USER": "You can't invite authors with same role and plan section more than once", "INVITE-USER-ALREADY-CONFIRMED": "Ιnvitation has already confirmed", - "REQUEST-HAS-EXPIRED": "Request has expired" + "REQUEST-HAS-EXPIRED": "Request has expired", + "MAX-DESCRIPTION-EXCEEDED": "This plan has reached the maximun descriptions for this description template" }, "FORM-VALIDATION-DISPLAY-DIALOG": { "WARNING": "Atenção!", diff --git a/frontend/src/assets/i18n/sk.json b/frontend/src/assets/i18n/sk.json index 2256e7ba8..d48e5a61a 100644 --- a/frontend/src/assets/i18n/sk.json +++ b/frontend/src/assets/i18n/sk.json @@ -78,7 +78,8 @@ "PREFILLING-SOURCE-CODE-EXISTS": "The prefilling source code you provided already exists. Please choose a different code.", "DUPLICATE-PLAN-USER": "You can't invite authors with same role and plan section more than once", "INVITE-USER-ALREADY-CONFIRMED": "Ιnvitation has already confirmed", - "REQUEST-HAS-EXPIRED": "Request has expired" + "REQUEST-HAS-EXPIRED": "Request has expired", + "MAX-DESCRIPTION-EXCEEDED": "This plan has reached the maximun descriptions for this description template" }, "FORM-VALIDATION-DISPLAY-DIALOG": { "WARNING": "Upozornenie!", diff --git a/frontend/src/assets/i18n/sr.json b/frontend/src/assets/i18n/sr.json index 2a6b16e78..263dbfd42 100644 --- a/frontend/src/assets/i18n/sr.json +++ b/frontend/src/assets/i18n/sr.json @@ -78,7 +78,8 @@ "PREFILLING-SOURCE-CODE-EXISTS": "The prefilling source code you provided already exists. Please choose a different code.", "DUPLICATE-PLAN-USER": "You can't invite authors with same role and plan section more than once", "INVITE-USER-ALREADY-CONFIRMED": "Ιnvitation has already confirmed", - "REQUEST-HAS-EXPIRED": "Request has expired" + "REQUEST-HAS-EXPIRED": "Request has expired", + "MAX-DESCRIPTION-EXCEEDED": "This plan has reached the maximun descriptions for this description template" }, "FORM-VALIDATION-DISPLAY-DIALOG": { "WARNING": "Oprez!", diff --git a/frontend/src/assets/i18n/tr.json b/frontend/src/assets/i18n/tr.json index d50b698fa..7b4103dda 100644 --- a/frontend/src/assets/i18n/tr.json +++ b/frontend/src/assets/i18n/tr.json @@ -78,7 +78,8 @@ "PREFILLING-SOURCE-CODE-EXISTS": "The prefilling source code you provided already exists. Please choose a different code.", "DUPLICATE-PLAN-USER": "You can't invite authors with same role and plan section more than once", "INVITE-USER-ALREADY-CONFIRMED": "Ιnvitation has already confirmed", - "REQUEST-HAS-EXPIRED": "Request has expired" + "REQUEST-HAS-EXPIRED": "Request has expired", + "MAX-DESCRIPTION-EXCEEDED": "This plan has reached the maximun descriptions for this description template" }, "FORM-VALIDATION-DISPLAY-DIALOG": { "WARNING": "Uyarı!",