implement public clone plan, description

This commit is contained in:
CITE\amentis 2024-08-05 10:27:39 +03:00
parent e843a2487f
commit bed157b320
8 changed files with 430 additions and 8 deletions

View File

@ -49,6 +49,7 @@ public class AuditableAction {
public static final EventId Plan_Import = new EventId(5017, "Plan_Import"); public static final EventId Plan_Import = new EventId(5017, "Plan_Import");
public static final EventId Plan_GetPublicXml = new EventId(5017, "Plan_GetPublicXml"); public static final EventId Plan_GetPublicXml = new EventId(5017, "Plan_GetPublicXml");
public static final EventId Plan_ExportPublic = new EventId(5018, "Plan_ExportPublic"); public static final EventId Plan_ExportPublic = new EventId(5018, "Plan_ExportPublic");
public static final EventId Plan_PublicClone = new EventId(5019, "Plan_PublicClone");
public static final EventId Description_Query = new EventId(6000, "Description_Query"); public static final EventId Description_Query = new EventId(6000, "Description_Query");

View File

@ -43,6 +43,7 @@ public interface PlanService {
Plan createNewVersion(NewVersionPlanPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, JAXBException, ParserConfigurationException, IOException, TransformerException; Plan createNewVersion(NewVersionPlanPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, JAXBException, ParserConfigurationException, IOException, TransformerException;
Plan buildClone(ClonePlanPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, IOException, InvalidApplicationException; Plan buildClone(ClonePlanPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, IOException, InvalidApplicationException;
Plan buildPublicClone(ClonePlanPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, IOException, InvalidApplicationException;
List<PlanUser> assignUsers(UUID planId, List<PlanUserPersist> model, FieldSet fields, boolean disableDelete) throws InvalidApplicationException, IOException; List<PlanUser> assignUsers(UUID planId, List<PlanUserPersist> model, FieldSet fields, boolean disableDelete) throws InvalidApplicationException, IOException;
Plan removeUser(PlanUserRemovePersist model, FieldSet fields) throws InvalidApplicationException, IOException; Plan removeUser(PlanUserRemovePersist model, FieldSet fields) throws InvalidApplicationException, IOException;

View File

@ -69,6 +69,7 @@ import org.opencdmp.integrationevent.outbox.annotationentityremoval.AnnotationEn
import org.opencdmp.integrationevent.outbox.annotationentitytouch.AnnotationEntityTouchedIntegrationEventHandler; import org.opencdmp.integrationevent.outbox.annotationentitytouch.AnnotationEntityTouchedIntegrationEventHandler;
import org.opencdmp.integrationevent.outbox.notification.NotifyIntegrationEvent; import org.opencdmp.integrationevent.outbox.notification.NotifyIntegrationEvent;
import org.opencdmp.integrationevent.outbox.notification.NotifyIntegrationEventHandler; import org.opencdmp.integrationevent.outbox.notification.NotifyIntegrationEventHandler;
import org.opencdmp.model.PlanDescriptionTemplate;
import org.opencdmp.model.PlanUser; import org.opencdmp.model.PlanUser;
import org.opencdmp.model.PlanValidationResult; import org.opencdmp.model.PlanValidationResult;
import org.opencdmp.model.PublicPlan; import org.opencdmp.model.PublicPlan;
@ -168,7 +169,7 @@ public class PlanServiceImpl implements PlanService {
private final ActionConfirmationService actionConfirmationService; private final ActionConfirmationService actionConfirmationService;
private final gr.cite.tools.validation.ValidatorFactory validatorFactory; private final ValidatorFactory validatorFactory;
private final ElasticService elasticService; private final ElasticService elasticService;
private final DescriptionTemplateService descriptionTemplateService; private final DescriptionTemplateService descriptionTemplateService;
@ -676,6 +677,173 @@ public class PlanServiceImpl implements PlanService {
this.accountingService.increase(UsageLimitTargetMetric.DESCRIPTION_COUNT.getValue()); this.accountingService.increase(UsageLimitTargetMetric.DESCRIPTION_COUNT.getValue());
} }
private void clonePublicDescription(UUID planId, Map<UUID, UUID> planDescriptionTemplateRemap, UUID descriptionId, org.opencdmp.commons.types.planblueprint.DefinitionEntity blueprintDefinition) throws InvalidApplicationException, IOException {
logger.debug("cloning public description: {} with description: {}", descriptionId, planId);
this.usageLimitService.checkIncrease(UsageLimitTargetMetric.DESCRIPTION_COUNT);
this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.descriptionAffiliation(descriptionId)), Permission.PublicCloneDescription);
DescriptionEntity existing = null;
List<DescriptionReferenceEntity> descriptionReferences = new ArrayList<>();
List<ReferenceEntity> referenceEntities = new ArrayList<>();
List<DescriptionTagEntity> descriptionTags = new ArrayList<>();
List<TagEntity> tags = new ArrayList<>();
try {
this.entityManager.disableTenantFilters();
existing = this.queryFactory.query(DescriptionQuery.class).disableTracking().authorize(EnumSet.of(Public)).ids(descriptionId).planSubQuery(this.queryFactory.query(PlanQuery.class).isActive(IsActive.Active).statuses(PlanStatus.Finalized).accessTypes(PlanAccessType.Public)).first();
if (existing == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{descriptionId, Description.class.getSimpleName()}, LocaleContextHolder.getLocale()));
PlanDescriptionTemplateEntity planDescriptionTemplate = this.queryFactory.query(PlanDescriptionTemplateQuery.class).disableTracking().ids(existing.getPlanDescriptionTemplateId()).first();
if (planDescriptionTemplate == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{existing.getPlanDescriptionTemplateId(), PlanDescriptionTemplate.class.getSimpleName()}, LocaleContextHolder.getLocale()));
if (blueprintDefinition != null && planDescriptionTemplate.getSectionId() != null && !this.conventionService.isListNullOrEmpty(blueprintDefinition.getSections())) {
if (blueprintDefinition.getSections().stream().filter(x -> x.getId().equals(planDescriptionTemplate.getSectionId()) && x.getHasTemplates()).findFirst().orElse(null) == null) {
// ignore this description because don't exist templates in this section
return;
}
}
this.entityManager.disableTenantFilters();
descriptionReferences = this.queryFactory.query(DescriptionReferenceQuery.class).disableTracking()
.descriptionIds(existing.getId())
.isActive(IsActive.Active)
.collect();
if (!this.conventionService.isListNullOrEmpty(descriptionReferences)) {
this.entityManager.disableTenantFilters();
referenceEntities = this.queryFactory.query(ReferenceQuery.class).disableTracking()
.ids(descriptionReferences.stream().map(DescriptionReferenceEntity::getReferenceId).distinct().toList())
.collect();
}
this.entityManager.disableTenantFilters();
descriptionTags = this.queryFactory.query(DescriptionTagQuery.class).disableTracking()
.descriptionIds(existing.getId())
.isActive(IsActive.Active)
.collect();
if (!this.conventionService.isListNullOrEmpty(descriptionTags)) {
this.entityManager.disableTenantFilters();
tags = this.queryFactory.query(TagQuery.class).disableTracking()
.ids(descriptionTags.stream().map(DescriptionTagEntity::getTagId).distinct().toList())
.collect();
}
this.entityManager.reloadTenantFilters();
} finally {
this.entityManager.reloadTenantFilters();
DescriptionEntity newDescription = new DescriptionEntity();
newDescription.setId(UUID.randomUUID());
newDescription.setLabel(existing.getLabel());
newDescription.setDescription(existing.getDescription());
newDescription.setStatus(DescriptionStatus.Draft);
newDescription.setProperties(existing.getProperties());
newDescription.setPlanId(planId);
if (planDescriptionTemplateRemap != null) newDescription.setPlanDescriptionTemplateId(planDescriptionTemplateRemap.get(existing.getPlanDescriptionTemplateId()));
newDescription.setDescriptionTemplateId(existing.getDescriptionTemplateId());
newDescription.setCreatedById(this.userScope.getUserId());
newDescription.setCreatedAt(Instant.now());
newDescription.setUpdatedAt(Instant.now());
newDescription.setIsActive(IsActive.Active);
this.entityManager.persist(newDescription);
if (newDescription.getId() != null){
for (DescriptionReferenceEntity descriptionReference : descriptionReferences) {
if (!this.conventionService.isListNullOrEmpty(referenceEntities)){
ReferenceEntity existingReference = referenceEntities.stream().filter(x -> x.getId().equals(descriptionReference.getReferenceId())).findFirst().orElse(null);
if (existingReference != null) {
// persist reference for selected tenant
ReferenceEntity existingTenantReference = this.queryFactory.query(ReferenceQuery.class).ids(existingReference.getId()).first(); //TODO: optimize
if (existingTenantReference == null) existingTenantReference = this.queryFactory.query(ReferenceQuery.class).references(existingReference.getReference()).typeIds(existingReference.getTypeId()).sources(existingReference.getSource()).first();
UUID referenceId;
if (existingTenantReference == null) {
ReferenceTypeEntity referenceTypeEntity = this.queryFactory.query(ReferenceTypeQuery.class).ids(existingReference.getTypeId()).firstAs(new BaseFieldSet().ensure(ReferenceType._id));//TODO: optimize
if (referenceTypeEntity == null) continue;
ReferenceEntity newReferenceEntity = new ReferenceEntity();
newReferenceEntity.setId(UUID.randomUUID());
newReferenceEntity.setLabel(existingReference.getLabel());
newReferenceEntity.setReference(existingReference.getReference());
newReferenceEntity.setTypeId(referenceTypeEntity.getId());
newReferenceEntity.setSource(existingReference.getSource());
newReferenceEntity.setSourceType(existingReference.getSourceType());
newReferenceEntity.setAbbreviation(existingReference.getAbbreviation());
newReferenceEntity.setDescription(existingReference.getDescription());
newReferenceEntity.setDefinition(existingReference.getDefinition());
this.entityManager.persist(newReferenceEntity);
referenceId = newReferenceEntity.getId();
} else {
referenceId = existingTenantReference.getId();
}
DescriptionReferenceEntity newDescriptionReference = new DescriptionReferenceEntity();
newDescriptionReference.setId(UUID.randomUUID());
newDescriptionReference.setDescriptionId(newDescription.getId());
newDescriptionReference.setReferenceId(referenceId);
newDescriptionReference.setData(descriptionReference.getData());
newDescriptionReference.setCreatedAt(Instant.now());
newDescriptionReference.setUpdatedAt(Instant.now());
newDescriptionReference.setIsActive(IsActive.Active);
this.entityManager.persist(newDescriptionReference);
}
}
}
for (DescriptionTagEntity descriptionTag : descriptionTags) {
if (!this.conventionService.isListNullOrEmpty(tags)) {
TagEntity existingTag = tags.stream().filter(x -> x.getId().equals(descriptionTag.getTagId())).findFirst().orElse(null);
if (existingTag != null) {
UUID tagId;
// persist tag for selected tenant
TagEntity existingTenantTag = this.queryFactory.query(TagQuery.class).ids(existingTag.getId()).first(); //TODO: optimize
if (existingTenantTag == null) {
TagEntity tagEntity = new TagEntity();
tagEntity.setId(UUID.randomUUID());
tagEntity.setId(UUID.randomUUID());
tagEntity.setLabel(existingTag.getLabel());
tagEntity.setIsActive(IsActive.Active);
tagEntity.setCreatedAt(Instant.now());
tagEntity.setUpdatedAt(Instant.now());
this.entityManager.persist(tagEntity);
tagId = tagEntity.getId();
} else {
tagId = existingTenantTag.getId();
}
DescriptionTagEntity newTag = new DescriptionTagEntity();
newTag.setId(UUID.randomUUID());
newTag.setDescriptionId(newDescription.getId());
newTag.setTagId(tagId);
newTag.setCreatedAt(Instant.now());
newTag.setUpdatedAt(Instant.now());
newTag.setIsActive(IsActive.Active);
this.entityManager.persist(newTag);
}
}
}
this.entityManager.flush();
this.elasticService.persistDescription(newDescription);
this.annotationEntityTouchedIntegrationEventHandler.handleDescription(newDescription.getId());
this.annotationEntityTouchedIntegrationEventHandler.handleDescription(existing.getId());
this.accountingService.increase(UsageLimitTargetMetric.DESCRIPTION_COUNT.getValue());
this.entityManager.reloadTenantFilters();
}
}
}
private void updateVersionStatusAndSave(PlanEntity data, PlanStatus previousStatus, PlanStatus newStatus) throws InvalidApplicationException { private void updateVersionStatusAndSave(PlanEntity data, PlanStatus previousStatus, PlanStatus newStatus) throws InvalidApplicationException {
@ -833,6 +1001,207 @@ public class PlanServiceImpl implements PlanService {
return this.builderFactory.builder(PlanBuilder.class).build(fields, resultingPlanEntity); return this.builderFactory.builder(PlanBuilder.class).build(fields, resultingPlanEntity);
} }
public Plan buildPublicClone(ClonePlanPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, IOException, InvalidApplicationException {
this.usageLimitService.checkIncrease(UsageLimitTargetMetric.PLAN_COUNT);
PlanEntity existingPlanEntity = null;
PlanEntity newPlan = new PlanEntity();
PlanBlueprintEntity existingBlueprintEntity = null;
org.opencdmp.commons.types.planblueprint.DefinitionEntity definition = null;
List<PlanReferenceEntity> planReferences = new ArrayList<>();
List<ReferenceEntity> referenceEntities = new ArrayList<>();
List<PlanDescriptionTemplateEntity> planDescriptionTemplates = new ArrayList<>();
List<org.opencdmp.data.DescriptionTemplateEntity> descriptionTemplates = new ArrayList<>();
List<DescriptionTemplateTypeEntity> descriptionTemplateTypes = new ArrayList<>();
try {
this.entityManager.disableTenantFilters();
// query for public plan
existingPlanEntity = this.queryFactory.query(PlanQuery.class).disableTracking().authorize(EnumSet.of(Public)).ids(model.getId()).isActive(IsActive.Active).statuses(PlanStatus.Finalized).accessTypes(PlanAccessType.Public).firstAs(fields);
if (existingPlanEntity == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), PublicPlan.class.getSimpleName()}, LocaleContextHolder.getLocale()));
this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.planAffiliation( model.getId())), Permission.PublicClonePlan);
// find blueprint code to check if exist in current tenant
existingBlueprintEntity = this.queryFactory.query(PlanBlueprintQuery.class).disableTracking()
.ids(existingPlanEntity.getBlueprintId())
.first();
if (existingBlueprintEntity == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{existingPlanEntity.getBlueprintId(), PlanBlueprint.class.getSimpleName()}, LocaleContextHolder.getLocale()));
this.entityManager.disableTenantFilters();
planReferences = this.queryFactory.query(PlanReferenceQuery.class).disableTracking()
.planIds(model.getId())
.isActives(IsActive.Active)
.collect();
this.entityManager.disableTenantFilters();
planDescriptionTemplates = this.queryFactory.query(PlanDescriptionTemplateQuery.class).disableTracking()
.planIds(model.getId())
.isActive(IsActive.Active)
.collect();
if (!this.conventionService.isListNullOrEmpty(planReferences)) {
this.entityManager.disableTenantFilters();
referenceEntities = this.queryFactory.query(ReferenceQuery.class).disableTracking()
.ids(planReferences.stream().map(PlanReferenceEntity::getReferenceId).distinct().toList())
.collect();
}
if (!this.conventionService.isListNullOrEmpty(planDescriptionTemplates)) {
this.entityManager.disableTenantFilters();
descriptionTemplates = this.queryFactory.query(DescriptionTemplateQuery.class).disableTracking()
.groupIds(planDescriptionTemplates.stream().map(PlanDescriptionTemplateEntity::getDescriptionTemplateGroupId).distinct().toList())
.versionStatuses(DescriptionTemplateVersionStatus.Current)
.collect();
}
if (!this.conventionService.isListNullOrEmpty(descriptionTemplates)){
this.entityManager.disableTenantFilters();
descriptionTemplateTypes = this.queryFactory.query(DescriptionTemplateTypeQuery.class).disableTracking()
.ids(descriptionTemplates.stream().map(org.opencdmp.data.DescriptionTemplateEntity::getTypeId).distinct().toList())
.collect();
}
this.entityManager.reloadTenantFilters();
} finally {
this.entityManager.reloadTenantFilters();
if (existingPlanEntity != null && existingBlueprintEntity != null) {
PlanBlueprintEntity blueprintEntityByTenant = this.queryFactory.query(PlanBlueprintQuery.class).disableTracking().ids(existingBlueprintEntity.getId()).first();
if (blueprintEntityByTenant == null){
blueprintEntityByTenant = this.queryFactory.query(PlanBlueprintQuery.class).disableTracking().groupIds(existingBlueprintEntity.getGroupId()).versionStatuses(PlanBlueprintVersionStatus.Current).isActive(IsActive.Active).statuses(PlanBlueprintStatus.Finalized).first();
if (blueprintEntityByTenant == null){
blueprintEntityByTenant = this.queryFactory.query(PlanBlueprintQuery.class).disableTracking().codes(existingBlueprintEntity.getCode()).first();
if (blueprintEntityByTenant == null) {
throw new MyValidationException(this.errors.getPlanBlueprintImportNotFound().getCode(), existingBlueprintEntity.getCode());
}
}
}
if (!blueprintEntityByTenant.getStatus().equals(PlanBlueprintStatus.Finalized)) throw new MyValidationException(this.errors.getPlanBlueprintImportDraft().getCode(), existingBlueprintEntity.getCode());
definition = this.xmlHandlingService.fromXmlSafe(org.opencdmp.commons.types.planblueprint.DefinitionEntity.class, blueprintEntityByTenant.getDefinition());
if (definition == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{blueprintEntityByTenant.getId(), org.opencdmp.commons.types.planblueprint.DefinitionEntity.class.getSimpleName()}, LocaleContextHolder.getLocale()));
newPlan.setId(UUID.randomUUID());
newPlan.setIsActive(IsActive.Active);
newPlan.setCreatedAt(Instant.now());
newPlan.setUpdatedAt(Instant.now());
newPlan.setGroupId(UUID.randomUUID());
newPlan.setVersion((short) 1);
newPlan.setVersionStatus(PlanVersionStatus.NotFinalized);
newPlan.setDescription(model.getDescription());
newPlan.setLabel(model.getLabel());
newPlan.setLanguage(existingPlanEntity.getLanguage());
newPlan.setStatus(PlanStatus.Draft);
newPlan.setProperties(existingPlanEntity.getProperties());
newPlan.setBlueprintId(blueprintEntityByTenant.getId());
newPlan.setAccessType(existingPlanEntity.getAccessType());
newPlan.setCreatorId(this.userScope.getUserId());
this.entityManager.persist(newPlan);
}
if (newPlan.getId() != null) {
this.addOwner(newPlan);
for (PlanReferenceEntity planReference : planReferences) {
if (!this.conventionService.isListNullOrEmpty(referenceEntities)){
ReferenceEntity existingReference = referenceEntities.stream().filter(x -> x.getId().equals(planReference.getReferenceId())).findFirst().orElse(null);
if (existingReference != null) {
// persist reference for selected tenant
ReferenceEntity existingTenantReference = this.queryFactory.query(ReferenceQuery.class).ids(existingReference.getId()).first(); //TODO: optimize
if (existingTenantReference == null) existingTenantReference = this.queryFactory.query(ReferenceQuery.class).references(existingReference.getReference()).typeIds(existingReference.getTypeId()).sources(existingReference.getSource()).first();
UUID referenceId;
if (existingTenantReference == null) {
ReferenceTypeEntity referenceTypeEntity = this.queryFactory.query(ReferenceTypeQuery.class).ids(existingReference.getTypeId()).firstAs(new BaseFieldSet().ensure(ReferenceType._id));//TODO: optimize
if (referenceTypeEntity == null) continue;
ReferenceEntity newReferenceEntity = new ReferenceEntity();
newReferenceEntity.setId(UUID.randomUUID());
newReferenceEntity.setLabel(existingReference.getLabel());
newReferenceEntity.setReference(existingReference.getReference());
newReferenceEntity.setTypeId(referenceTypeEntity.getId());
newReferenceEntity.setSource(existingReference.getSource());
newReferenceEntity.setSourceType(existingReference.getSourceType());
newReferenceEntity.setAbbreviation(existingReference.getAbbreviation());
newReferenceEntity.setDescription(existingReference.getDescription());
newReferenceEntity.setDefinition(existingReference.getDefinition());
this.entityManager.persist(newReferenceEntity);
referenceId = newReferenceEntity.getId();
} else {
referenceId = existingTenantReference.getId();
}
PlanReferenceEntity newPlanReference = new PlanReferenceEntity();
newPlanReference.setId(UUID.randomUUID());
newPlanReference.setPlanId(newPlan.getId());
newPlanReference.setReferenceId(referenceId);
newPlanReference.setData(planReference.getData());
newPlanReference.setCreatedAt(Instant.now());
newPlanReference.setUpdatedAt(Instant.now());
newPlanReference.setIsActive(IsActive.Active);
this.entityManager.persist(newPlanReference);
}
}
}
Map<UUID, UUID> planDescriptionTemplateRemap = new HashMap<>();
for (PlanDescriptionTemplateEntity planDescriptionTemplate : planDescriptionTemplates) {
if (!this.conventionService.isListNullOrEmpty(descriptionTemplates)) {
org.opencdmp.data.DescriptionTemplateEntity existingDescriptionTemplate = descriptionTemplates.stream().filter(x -> x.getGroupId().equals(planDescriptionTemplate.getDescriptionTemplateGroupId())).findFirst().orElse(null);
if (existingDescriptionTemplate != null) {
DescriptionTemplateTypeEntity existingDescriptionTemplateType = descriptionTemplateTypes.stream().filter(x -> x.getId().equals(existingDescriptionTemplate.getTypeId())).findFirst().orElse(null);
if (existingDescriptionTemplateType != null) {
DescriptionTemplateTypeEntity existingTenantTemplateType = this.queryFactory.query(DescriptionTemplateTypeQuery.class).ids(existingDescriptionTemplateType.getId()).first();
if (existingTenantTemplateType == null) existingTenantTemplateType = this.queryFactory.query(DescriptionTemplateTypeQuery.class).codes(existingDescriptionTemplateType.getCode()).first();
if (existingTenantTemplateType == null) throw new MyValidationException(this.errors.getDescriptionTemplateTypeImportNotFound().getCode(), existingTenantTemplateType.getCode());
if (!existingTenantTemplateType.getStatus().equals(DescriptionTemplateTypeStatus.Finalized)) throw new MyValidationException(this.errors.getDescriptionTemplateTypeImportDraft().getCode(), existingTenantTemplateType.getCode());
}
org.opencdmp.data.DescriptionTemplateEntity existingTenantTemplate = this.queryFactory.query(DescriptionTemplateQuery.class).ids(existingDescriptionTemplate.getId()).first();
if (existingTenantTemplate == null) existingTenantTemplate = this.queryFactory.query(DescriptionTemplateQuery.class).groupIds(existingDescriptionTemplate.getId()).first();
if (existingTenantTemplate == null) existingTenantTemplate = this.queryFactory.query(DescriptionTemplateQuery.class).codes(existingDescriptionTemplate.getCode()).first();
if (existingTenantTemplate == null) throw new MyValidationException(this.errors.getDescriptionTemplateImportNotFound().getCode(), existingTenantTemplate.getCode());
if (!existingTenantTemplate.getStatus().equals(DescriptionTemplateStatus.Finalized)) throw new MyValidationException(this.errors.getPlanDescriptionTemplateImportDraft().getCode(), existingTenantTemplate.getCode());
PlanDescriptionTemplateEntity newTemplate = new PlanDescriptionTemplateEntity();
newTemplate.setId(UUID.randomUUID());
newTemplate.setPlanId(newPlan.getId());
newTemplate.setDescriptionTemplateGroupId(planDescriptionTemplate.getDescriptionTemplateGroupId());
newTemplate.setSectionId(planDescriptionTemplate.getSectionId());
newTemplate.setCreatedAt(Instant.now());
newTemplate.setUpdatedAt(Instant.now());
newTemplate.setIsActive(IsActive.Active);
planDescriptionTemplateRemap.put(planDescriptionTemplate.getId(), newTemplate.getId());
this.entityManager.persist(newTemplate);
}
}
}
this.entityManager.flush();
this.elasticService.persistPlan(newPlan);
this.annotationEntityTouchedIntegrationEventHandler.handlePlan(newPlan.getId());
this.accountingService.increase(UsageLimitTargetMetric.PLAN_COUNT.getValue());
if (!this.conventionService.isListNullOrEmpty(model.getDescriptions())){
for (UUID description: model.getDescriptions()) {
this.clonePublicDescription(newPlan.getId(), planDescriptionTemplateRemap, description, definition);
}
}
}
}
return this.builderFactory.builder(PlanBuilder.class).build(fields, existingPlanEntity);
}
@Override @Override
public List<PlanUser> assignUsers(UUID planId, List<PlanUserPersist> model, FieldSet fieldSet, boolean disableDelete) throws InvalidApplicationException, IOException { public List<PlanUser> assignUsers(UUID planId, List<PlanUserPersist> model, FieldSet fieldSet, boolean disableDelete) throws InvalidApplicationException, IOException {
this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.planAffiliation(planId)), Permission.AssignPlanUsers); this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.planAffiliation(planId)), Permission.AssignPlanUsers);
@ -1963,6 +2332,7 @@ public class PlanServiceImpl implements PlanService {
PlanBlueprintEntity planBlueprintEntity = planXml.getBlueprint().getId() != null ? this.queryFactory.query(PlanBlueprintQuery.class).disableTracking().ids(planXml.getBlueprint().getId()).first() : null; PlanBlueprintEntity planBlueprintEntity = planXml.getBlueprint().getId() != null ? this.queryFactory.query(PlanBlueprintQuery.class).disableTracking().ids(planXml.getBlueprint().getId()).first() : null;
if (planBlueprintEntity == null) planBlueprintEntity = planXml.getBlueprint().getGroupId() != null ? this.queryFactory.query(PlanBlueprintQuery.class).disableTracking().groupIds(planXml.getBlueprint().getGroupId()).versionStatuses(PlanBlueprintVersionStatus.Current).isActive(IsActive.Active).statuses(PlanBlueprintStatus.Finalized).first() : null; if (planBlueprintEntity == null) planBlueprintEntity = planXml.getBlueprint().getGroupId() != null ? this.queryFactory.query(PlanBlueprintQuery.class).disableTracking().groupIds(planXml.getBlueprint().getGroupId()).versionStatuses(PlanBlueprintVersionStatus.Current).isActive(IsActive.Active).statuses(PlanBlueprintStatus.Finalized).first() : null;
if (planBlueprintEntity != null){ if (planBlueprintEntity != null){
if (!planBlueprintEntity.getStatus().equals(PlanBlueprintStatus.Finalized)) throw new MyValidationException(this.errors.getPlanBlueprintImportDraft().getCode(), planBlueprintEntity.getCode());
return planBlueprintEntity.getId(); return planBlueprintEntity.getId();
} else { } else {
planBlueprintEntity = !this.conventionService.isNullOrEmpty(planXml.getBlueprint().getCode()) ? this.queryFactory.query(PlanBlueprintQuery.class).disableTracking().codes(planXml.getBlueprint().getCode()).first() : null; planBlueprintEntity = !this.conventionService.isNullOrEmpty(planXml.getBlueprint().getCode()) ? this.queryFactory.query(PlanBlueprintQuery.class).disableTracking().codes(planXml.getBlueprint().getCode()).first() : null;

View File

@ -334,6 +334,35 @@ public class PlanController {
return clone; return clone;
} }
@PostMapping("public-clone")
@OperationWithTenantHeader(summary = "Create a clone of an existing public plan", description = "",
responses = @ApiResponse(description = "OK", responseCode = "200", content = @Content(
schema = @Schema(
implementation = Plan.class
))
))
@Swagger400
@Swagger404
@Transactional
@ValidationFilterAnnotation(validator = ClonePlanPersist.ClonePlanPersistValidator.ValidatorName, argumentName = "model")
public Plan buildPublicClone(
@RequestBody ClonePlanPersist model,
@Parameter(name = "fieldSet", description = SwaggerHelpers.Commons.fieldset_description, required = true) FieldSet fieldSet
) throws MyApplicationException, MyForbiddenException, MyNotFoundException, IOException, InvalidApplicationException {
logger.debug(new MapLogEntry("clone public" + PublicPlan.class.getSimpleName()).And("model", model).And("fields", fieldSet));
this.censorFactory.censor(PublicPlanCensor.class).censor(fieldSet);
Plan clone = this.planService.buildPublicClone(model, fieldSet);
this.auditService.track(AuditableAction.Plan_PublicClone, Map.ofEntries(
new AbstractMap.SimpleEntry<String, Object>("model", model),
new AbstractMap.SimpleEntry<String, Object>("fields", fieldSet)
));
return clone;
}
@PostMapping("new-version") @PostMapping("new-version")
@OperationWithTenantHeader(summary = "Create a new version of an existing plan", description = "", @OperationWithTenantHeader(summary = "Create a new version of an existing plan", description = "",
responses = @ApiResponse(description = "OK", responseCode = "200", content = @Content( responses = @ApiResponse(description = "OK", responseCode = "200", content = @Content(

View File

@ -129,6 +129,15 @@ export class PlanService {
catchError((error: any) => throwError(error))); catchError((error: any) => throwError(error)));
} }
publicClone(item: ClonePlanPersist, reqFields: string[] = []): Observable<Plan> {
const url = `${this.apiBase}/public-clone`;
const options = { params: { f: reqFields } };
return this.http
.post<Plan>(url, item, options).pipe(
catchError((error: any) => throwError(error)));
}
newVersion(item: NewVersionPlanPersist, reqFields: string[] = []): Observable<Plan> { newVersion(item: NewVersionPlanPersist, reqFields: string[] = []): Observable<Plan> {
const url = `${this.apiBase}/new-version`; const url = `${this.apiBase}/new-version`;
const options = { params: { f: reqFields } }; const options = { params: { f: reqFields } };

View File

@ -22,6 +22,7 @@ export class ClonePlanDialogComponent extends BaseComponent {
plan: Plan; plan: Plan;
editorModel: PlanCloneDialogEditorModel; editorModel: PlanCloneDialogEditorModel;
formGroup: UntypedFormGroup; formGroup: UntypedFormGroup;
isPublic: boolean = false;
constructor( constructor(
public dialogRef: MatDialogRef<ClonePlanDialogComponent>, public dialogRef: MatDialogRef<ClonePlanDialogComponent>,
@ -33,6 +34,7 @@ export class ClonePlanDialogComponent extends BaseComponent {
) { ) {
super(); super();
this.plan = data.plan; this.plan = data.plan;
this.isPublic = data.isPublic;
} }
@ -72,11 +74,19 @@ export class ClonePlanDialogComponent extends BaseComponent {
confirm() { confirm() {
if (!this.formGroup.valid) { return; } if (!this.formGroup.valid) { return; }
const value: ClonePlanPersist = this.formGroup.value; const value: ClonePlanPersist = this.formGroup.value;
if (this.isPublic) {
this.planService.publicClone(value, PlanEditorEntityResolver.lookupFields()).pipe(takeUntil(this._destroyed)).subscribe(
plan => this.dialogRef.close(plan),
error => this.onCallbackError(error)
);
} else {
this.planService.clone(value, PlanEditorEntityResolver.lookupFields()).pipe(takeUntil(this._destroyed)).subscribe( this.planService.clone(value, PlanEditorEntityResolver.lookupFields()).pipe(takeUntil(this._destroyed)).subscribe(
plan => this.dialogRef.close(plan), plan => this.dialogRef.close(plan),
error => this.onCallbackError(error) error => this.onCallbackError(error)
); );
} }
}
toggleAllDescriptions(event: any) { toggleAllDescriptions(event: any) {
if (event === true) { if (event === true) {

View File

@ -131,7 +131,8 @@ export class PlanListingItemComponent extends BaseComponent implements OnInit {
maxWidth: '700px', maxWidth: '700px',
maxHeight: '80vh', maxHeight: '80vh',
data: { data: {
plan: this.plan plan: this.plan,
isPublic: this.isPublic ? this.isPublic : false
} }
}); });
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe((result: Plan) => { dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe((result: Plan) => {

View File

@ -288,7 +288,8 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
maxWidth: '700px', maxWidth: '700px',
maxHeight: '80vh', maxHeight: '80vh',
data: { data: {
plan: this.plan plan: this.plan,
isPublic: this.isPublicView ? this.isPublicView : false
} }
}); });
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe((result: Plan) => { dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe((result: Plan) => {