add description status logic

This commit is contained in:
CITE\amentis 2024-09-18 14:10:44 +03:00
parent 99023ed159
commit 4dc0927140
38 changed files with 560 additions and 164 deletions

View File

@ -39,6 +39,10 @@ public class DescriptionEntity extends TenantScopedBaseEntity {
private DescriptionStatus status;
public static final String _status = "status";
@Column(name = "status_id", nullable = true)
private UUID statusId;
public static final String _statusId = "statusId";
@Column(name = "description")
private String description;
@ -187,5 +191,13 @@ public class DescriptionEntity extends TenantScopedBaseEntity {
public void setDescriptionTemplateId(UUID descriptionTemplateId) {
this.descriptionTemplateId = descriptionTemplateId;
}
public UUID getStatusId() {
return statusId;
}
public void setStatusId(UUID statusId) {
this.statusId = statusId;
}
}

View File

@ -1,6 +1,5 @@
package org.opencdmp.model;
import org.opencdmp.commons.enums.DescriptionStatus;
import java.time.Instant;
import java.util.UUID;
@ -15,7 +14,7 @@ public class PublicDescription {
public static final String _label = "label";
private DescriptionStatus status;
private PublicDescriptionStatus status;
public static final String _status = "status";
@ -65,11 +64,11 @@ public class PublicDescription {
}
public DescriptionStatus getStatus() {
public PublicDescriptionStatus getStatus() {
return status;
}
public void setStatus(DescriptionStatus status) {
public void setStatus(PublicDescriptionStatus status) {
this.status = status;
}

View File

@ -0,0 +1,25 @@
package org.opencdmp.model;
import java.util.UUID;
public class PublicDescriptionStatus {
public final static String _id = "id";
private UUID id;
public final static String _name = "name";
private String name;
public final static String _internalStatus = "internalStatus";
private org.opencdmp.commons.enums.DescriptionStatus internalStatus;
public UUID getId() { return this.id; }
public void setId(UUID id) { this.id = id; }
public String getName() { return this.name; }
public void setName(String name) { this.name = name; }
public org.opencdmp.commons.enums.DescriptionStatus getInternalStatus() { return this.internalStatus; }
public void setInternalStatus(org.opencdmp.commons.enums.DescriptionStatus internalStatus) { this.internalStatus = internalStatus; }
}

View File

@ -10,10 +10,8 @@ import gr.cite.tools.logging.LoggerService;
import org.opencdmp.authorization.AuthorizationFlags;
import org.opencdmp.convention.ConventionService;
import org.opencdmp.data.DescriptionEntity;
import org.opencdmp.model.PublicDescription;
import org.opencdmp.model.PublicDescriptionTemplate;
import org.opencdmp.model.PublicPlan;
import org.opencdmp.model.PublicPlanDescriptionTemplate;
import org.opencdmp.model.*;
import org.opencdmp.query.DescriptionStatusQuery;
import org.opencdmp.query.DescriptionTemplateQuery;
import org.opencdmp.query.PlanDescriptionTemplateQuery;
import org.opencdmp.query.PlanQuery;
@ -66,12 +64,15 @@ public class PublicDescriptionBuilder extends BaseBuilder<PublicDescription, Des
FieldSet planFields = fields.extractPrefixed(this.asPrefix(PublicDescription._plan));
Map<UUID, PublicPlan> planItemsMap = this.collectPlans(planFields, data);
FieldSet descriptionStatusFields = fields.extractPrefixed(this.asPrefix(PublicDescription._status));
Map<UUID, PublicDescriptionStatus> descriptionStatusItemsMap = this.collectDescriptionStatuses(descriptionStatusFields, data);
List<PublicDescription> models = new ArrayList<>();
for (DescriptionEntity d : data) {
PublicDescription m = new PublicDescription();
if (fields.hasField(this.asIndexer(PublicDescription._id))) m.setId(d.getId());
if (fields.hasField(this.asIndexer(PublicDescription._label))) m.setLabel(d.getLabel());
if (fields.hasField(this.asIndexer(PublicDescription._status))) m.setStatus(d.getStatus());
if (!descriptionStatusFields.isEmpty() && descriptionStatusItemsMap != null && descriptionStatusItemsMap.containsKey(d.getStatusId())) m.setStatus(descriptionStatusItemsMap.get(d.getStatusId()));
if (fields.hasField(this.asIndexer(PublicDescription._description))) m.setDescription(d.getDescription());
if (fields.hasField(this.asIndexer(PublicDescription._createdAt))) m.setCreatedAt(d.getCreatedAt());
if (fields.hasField(this.asIndexer(PublicDescription._updatedAt))) m.setUpdatedAt(d.getUpdatedAt());
@ -177,4 +178,34 @@ public class PublicDescriptionBuilder extends BaseBuilder<PublicDescription, Des
return itemMap;
}
private Map<UUID, PublicDescriptionStatus> collectDescriptionStatuses(FieldSet fields, List<DescriptionEntity> data) throws MyApplicationException {
if (fields.isEmpty() || data.isEmpty())
return null;
this.logger.debug("checking related - {}", PublicDescriptionStatus.class.getSimpleName());
Map<UUID, PublicDescriptionStatus> itemMap;
if (!fields.hasOtherField(this.asIndexer(PublicDescriptionStatus._id))) {
itemMap = this.asEmpty(
data.stream().map(DescriptionEntity::getDescriptionTemplateId).distinct().collect(Collectors.toList()),
x -> {
PublicDescriptionStatus item = new PublicDescriptionStatus();
item.setId(x);
return item;
},
PublicDescriptionStatus::getId);
} else {
FieldSet clone = new BaseFieldSet(fields.getFields()).ensure(PublicDescriptionStatus._id);
DescriptionStatusQuery q = this.queryFactory.query(DescriptionStatusQuery.class).disableTracking().authorize(this.authorize).ids(data.stream().map(DescriptionEntity::getStatusId).distinct().collect(Collectors.toList()));
itemMap = this.builderFactory.builder(PublicDescriptionStatusBuilder.class).authorize(this.authorize).asForeignKey(q, clone, PublicDescriptionStatus::getId);
}
if (!fields.hasField(PublicDescriptionStatus._id)) {
itemMap.forEach((id, item) -> {
if (item != null)
item.setId(null);
});
}
return itemMap;
}
}

View File

@ -0,0 +1,54 @@
package org.opencdmp.model.builder;
import gr.cite.tools.exception.MyApplicationException;
import gr.cite.tools.fieldset.FieldSet;
import gr.cite.tools.logging.DataLogEntry;
import gr.cite.tools.logging.LoggerService;
import org.opencdmp.authorization.AuthorizationFlags;
import org.opencdmp.convention.ConventionService;
import org.opencdmp.data.DescriptionStatusEntity;
import org.opencdmp.model.PublicDescriptionStatus;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.util.*;
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PublicDescriptionStatusBuilder extends BaseBuilder<PublicDescriptionStatus, DescriptionStatusEntity> {
private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None);
@Autowired
public PublicDescriptionStatusBuilder(
ConventionService conventionService) {
super(conventionService, new LoggerService(LoggerFactory.getLogger(PublicDescriptionStatusBuilder.class)));
}
public PublicDescriptionStatusBuilder authorize(EnumSet<AuthorizationFlags> values) {
this.authorize = values;
return this;
}
@Override
public List<PublicDescriptionStatus> build(FieldSet fields, List<DescriptionStatusEntity> data) throws MyApplicationException {
this.logger.debug("building for {} items requesting {} fields", Optional.ofNullable(data).map(List::size).orElse(0), Optional.ofNullable(fields).map(FieldSet::getFields).map(Set::size).orElse(0));
this.logger.trace(new DataLogEntry("requested fields", fields));
if (fields == null || data == null || fields.isEmpty())
return new ArrayList<>();
List<PublicDescriptionStatus> models = new ArrayList<>();
for (DescriptionStatusEntity d : data) {
PublicDescriptionStatus m = new PublicDescriptionStatus();
if (fields.hasField(this.asIndexer(PublicDescriptionStatus._id))) m.setId(d.getId());
if (fields.hasField(this.asIndexer(PublicDescriptionStatus._name))) m.setName(d.getName());
if (fields.hasField(this.asIndexer(PublicDescriptionStatus._internalStatus))) m.setInternalStatus(d.getInternalStatus());
models.add(m);
}
this.logger.debug("build {} items", Optional.of(models).map(List::size).orElse(0));
return models;
}
}

View File

@ -26,10 +26,12 @@ import org.opencdmp.model.builder.DescriptionTagBuilder;
import org.opencdmp.model.builder.PlanDescriptionTemplateBuilder;
import org.opencdmp.model.builder.UserBuilder;
import org.opencdmp.model.builder.descriptionreference.DescriptionReferenceBuilder;
import org.opencdmp.model.builder.descriptionstatus.DescriptionStatusBuilder;
import org.opencdmp.model.builder.descriptiontemplate.DescriptionTemplateBuilder;
import org.opencdmp.model.builder.plan.PlanBuilder;
import org.opencdmp.model.description.Description;
import org.opencdmp.model.descriptionreference.DescriptionReference;
import org.opencdmp.model.descriptionstatus.DescriptionStatus;
import org.opencdmp.model.descriptiontemplate.DescriptionTemplate;
import org.opencdmp.model.plan.Plan;
import org.opencdmp.model.user.User;
@ -85,6 +87,9 @@ public class DescriptionBuilder extends BaseBuilder<Description, DescriptionEnti
if (fields == null || data == null || fields.isEmpty())
return new ArrayList<>();
FieldSet statusFields = fields.extractPrefixed(this.asPrefix(Description._status));
Map<UUID, DescriptionStatus> statusItemsMap = this.collectDescriptionStatuses(statusFields, data);
FieldSet planDescriptionTemplateFields = fields.extractPrefixed(this.asPrefix(Description._planDescriptionTemplate));
Map<UUID, PlanDescriptionTemplate> planDescriptionTemplateItemsMap = this.collectPlanDescriptionTemplates(planDescriptionTemplateFields, data);
@ -116,7 +121,7 @@ public class DescriptionBuilder extends BaseBuilder<Description, DescriptionEnti
if (fields.hasField(this.asIndexer(Description._id))) m.setId(d.getId());
if (fields.hasField(this.asIndexer(Description._tenantId))) m.setTenantId(d.getTenantId());
if (fields.hasField(this.asIndexer(Description._label))) m.setLabel(d.getLabel());
if (fields.hasField(this.asIndexer(Description._status))) m.setStatus(d.getStatus());
if (!statusFields.isEmpty() && statusItemsMap != null && statusItemsMap.containsKey(d.getStatusId())) m.setStatus(statusItemsMap.get(d.getStatusId()));
if (fields.hasField(this.asIndexer(Description._description))) m.setDescription(d.getDescription());
if (fields.hasField(this.asIndexer(Description._createdAt))) m.setCreatedAt(d.getCreatedAt());
if (fields.hasField(this.asIndexer(Description._updatedAt))) m.setUpdatedAt(d.getUpdatedAt());
@ -143,6 +148,36 @@ public class DescriptionBuilder extends BaseBuilder<Description, DescriptionEnti
return models;
}
private Map<UUID, DescriptionStatus> collectDescriptionStatuses(FieldSet fields, List<DescriptionEntity> data) throws MyApplicationException {
if (fields.isEmpty() || data.isEmpty())
return null;
this.logger.debug("checking related - {}", DescriptionStatus.class.getSimpleName());
Map<UUID, DescriptionStatus> itemMap;
if (!fields.hasOtherField(this.asIndexer(DescriptionStatus._id))) {
itemMap = this.asEmpty(
data.stream().map(DescriptionEntity::getStatusId).distinct().collect(Collectors.toList()),
x -> {
DescriptionStatus item = new DescriptionStatus();
item.setId(x);
return item;
},
DescriptionStatus::getId);
} else {
FieldSet clone = new BaseFieldSet(fields.getFields()).ensure(DescriptionStatus._id);
DescriptionStatusQuery q = this.queryFactory.query(DescriptionStatusQuery.class).disableTracking().authorize(this.authorize).ids(data.stream().map(DescriptionEntity::getStatusId).distinct().collect(Collectors.toList()));
itemMap = this.builderFactory.builder(DescriptionStatusBuilder.class).authorize(this.authorize).asForeignKey(q, clone, DescriptionStatus::getId);
}
if (!fields.hasField(DescriptionStatus._id)) {
itemMap.forEach((id, item) -> {
if (item != null)
item.setId(null);
});
}
return itemMap;
}
private Map<UUID, User> collectUsers(FieldSet fields, List<DescriptionEntity> data) throws MyApplicationException {
if (fields.isEmpty() || data.isEmpty())
return null;

View File

@ -1,6 +1,6 @@
package org.opencdmp.model.description;
import org.opencdmp.commons.enums.DescriptionStatus;
import org.opencdmp.model.descriptionstatus.DescriptionStatus;
import org.opencdmp.commons.enums.IsActive;
import org.opencdmp.model.*;
import org.opencdmp.model.descriptionreference.DescriptionReference;

View File

@ -30,7 +30,7 @@ public class DescriptionToPublicApiDatasetMapper {
model.setDescription(description.getDescription());
model.setReference("");
model.setUri("");
model.setStatus(description.getStatus());
// TODO status model.setStatus(description.getStatus());
model.setDmp(dmp);
model.setDatasetProfileDefinition(descriptionTemplateToPublicApiDatasetProfileMapper.toPublicModel(description.getDescriptionTemplate()));

View File

@ -46,9 +46,9 @@ public class DescriptionPersist {
public static final String _descriptionTemplateId = "descriptionTemplateId";
private DescriptionStatus status;
private UUID statusId;
public static final String _status = "status";
public static final String _statusId = "statusId";
private String description;
@ -97,12 +97,12 @@ public class DescriptionPersist {
this.planDescriptionTemplateId = planDescriptionTemplateId;
}
public DescriptionStatus getStatus() {
return this.status;
public UUID getStatusId() {
return statusId;
}
public void setStatus(DescriptionStatus status) {
this.status = status;
public void setStatusId(UUID statusId) {
this.statusId = statusId;
}
public String getDescription() {
@ -178,16 +178,19 @@ public class DescriptionPersist {
DescriptionTemplateEntity descriptionTemplate = null;
PlanEntity planEntity = null;
PlanBlueprintEntity planBlueprintEntity = null;
DescriptionStatusEntity statusEntity = null;
try {
descriptionTemplate = this.isValidGuid(item.getDescriptionTemplateId()) ? this.entityManager.find(DescriptionTemplateEntity.class, item.getDescriptionTemplateId(), true) : null;
planEntity = this.isValidGuid(item.getPlanId()) ? this.entityManager.find(PlanEntity.class, item.getPlanId(), true) : null;
planBlueprintEntity = planEntity == null ? null : this.entityManager.find(PlanBlueprintEntity.class, planEntity.getBlueprintId());
statusEntity = this.isValidGuid(item.getStatusId()) ? this.entityManager.find(DescriptionStatusEntity.class, item.getStatusId(), true) : null;
} catch (InvalidApplicationException e) {
throw new RuntimeException(e);
}
DefinitionEntity definition = descriptionTemplate == null ? null : this.xmlHandlingService.fromXmlSafe(DefinitionEntity.class, descriptionTemplate.getDefinition());
PlanBlueprintEntity finalPlanBlueprintEntity = planBlueprintEntity;
DescriptionStatusEntity finalStatusEntity = statusEntity;
return Arrays.asList(
this.spec()
.iff(() -> this.isValidGuid(item.getId()))
@ -214,21 +217,22 @@ public class DescriptionPersist {
.must(() -> this.isValidGuid(item.getPlanDescriptionTemplateId()))
.failOn(DescriptionPersist._planDescriptionTemplateId).failWith(this.messageSource.getMessage("Validation_Required", new Object[]{DescriptionPersist._planDescriptionTemplateId}, LocaleContextHolder.getLocale())),
this.spec()
.must(() -> !this.isNull(item.getStatus()))
.failOn(DescriptionPersist._status).failWith(this.messageSource.getMessage("Validation_Required", new Object[]{DescriptionPersist._status}, LocaleContextHolder.getLocale())),
.iff(() -> this.isValidGuid(item.getId()))
.must(() -> this.isValidGuid(item.getStatusId()))
.failOn(DescriptionPersist._statusId).failWith(this.messageSource.getMessage("Validation_Required", new Object[]{DescriptionPersist._statusId}, LocaleContextHolder.getLocale())),
this.spec()
.iff(() -> item.getStatus() == DescriptionStatus.Finalized)
.iff(() -> finalStatusEntity != null && finalStatusEntity.getInternalStatus() != null && finalStatusEntity.getInternalStatus() == DescriptionStatus.Finalized)
.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)
.iff(() -> finalStatusEntity != null && finalStatusEntity.getInternalStatus() != null && finalStatusEntity.getInternalStatus() == 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()
.iff(() -> item.getStatus() == DescriptionStatus.Finalized)
.iff(() -> finalStatusEntity != null && finalStatusEntity.getInternalStatus() != null && finalStatusEntity.getInternalStatus() == DescriptionStatus.Finalized)
.on(DescriptionPersist._properties)
.over(item.getProperties())
.using(() -> this.validatorFactory.validator(PropertyDefinitionPersist.PropertyDefinitionPersistValidator.class).setStatus(item.getStatus()).withDefinition(definition).setVisibilityService(definition, item.getProperties()))
.using(() -> this.validatorFactory.validator(PropertyDefinitionPersist.PropertyDefinitionPersistValidator.class).setStatus(finalStatusEntity.getInternalStatus()).withDefinition(definition).setVisibilityService(definition, item.getProperties()))
// this.navSpec()
// .iff(() -> !this.isNull(item.getTags()))
// .on(DescriptionPersist._tags)

View File

@ -1,6 +1,5 @@
package org.opencdmp.model.persist;
import org.opencdmp.commons.enums.DescriptionStatus;
import org.opencdmp.commons.validation.BaseValidator;
import gr.cite.tools.validation.specification.Specification;
import org.opencdmp.convention.ConventionService;
@ -21,7 +20,7 @@ public class DescriptionStatusPersist {
public static final String _id = "id";
private DescriptionStatus status;
private UUID statusId;
public static final String _status = "status";
@ -37,12 +36,12 @@ public class DescriptionStatusPersist {
this.id = id;
}
public DescriptionStatus getStatus() {
return status;
public UUID getStatusId() {
return statusId;
}
public void setStatus(DescriptionStatus status) {
this.status = status;
public void setStatusId(UUID statusId) {
this.statusId = statusId;
}
public String getHash() {
@ -82,7 +81,7 @@ public class DescriptionStatusPersist {
.must(() -> this.isValidHash(item.getHash()))
.failOn(DescriptionStatusPersist._hash).failWith(messageSource.getMessage("Validation_Required", new Object[]{DescriptionStatusPersist._hash}, LocaleContextHolder.getLocale())),
this.spec()
.must(() -> !this.isNull(item.getStatus()))
.must(() -> this.isValidGuid(item.getStatusId()))
.failOn(DescriptionStatusPersist._status).failWith(messageSource.getMessage("Validation_Required", new Object[]{DescriptionStatusPersist._status}, LocaleContextHolder.getLocale()))
);
}

View File

@ -400,6 +400,8 @@ public class DescriptionQuery extends QueryBase<DescriptionEntity> {
return DescriptionEntity._properties;
else if (item.match(Description._status) || item.match(PublicDescription._status))
return DescriptionEntity._status;
else if (item.prefix(Description._status) || item.prefix(PublicDescription._status))
return DescriptionEntity._statusId;
else if (item.match(Description._description) || item.match(PublicDescription._description))
return DescriptionEntity._description;
else if (item.match(Description._createdBy))
@ -444,6 +446,7 @@ public class DescriptionQuery extends QueryBase<DescriptionEntity> {
item.setLabel(QueryBase.convertSafe(tuple, columns, DescriptionEntity._label, String.class));
item.setProperties(QueryBase.convertSafe(tuple, columns, DescriptionEntity._properties, String.class));
item.setStatus(QueryBase.convertSafe(tuple, columns, DescriptionEntity._status, DescriptionStatus.class));
item.setStatusId(QueryBase.convertSafe(tuple, columns, DescriptionEntity._statusId, UUID.class));
item.setDescription(QueryBase.convertSafe(tuple, columns, DescriptionEntity._description, String.class));
item.setCreatedAt(QueryBase.convertSafe(tuple, columns, DescriptionEntity._createdAt, Instant.class));
item.setUpdatedAt(QueryBase.convertSafe(tuple, columns, DescriptionEntity._updatedAt, Instant.class));

View File

@ -77,9 +77,9 @@ public class DashboardServiceImpl implements DashboardService {
descriptionLookup.getPage().setOffset(0);
descriptionLookup.getPage().setSize(model.getPage().getSize()+model.getPage().getOffset());
QueryResult<Description> descriptions = this.elasticQueryHelperService.collect(descriptionLookup, AuthorizationFlags.AllExceptPublic, new BaseFieldSet().ensure(Description._id).ensure(Description._updatedAt).ensure(Description._status).ensure(Description._label));
QueryResult<Description> descriptions = this.elasticQueryHelperService.collect(descriptionLookup, AuthorizationFlags.AllExceptPublic, new BaseFieldSet().ensure(Description._id).ensure(Description._updatedAt).ensure(String.join(".",Description._status, org.opencdmp.model.descriptionstatus.DescriptionStatus._internalStatus)).ensure(Description._label));
if (!this.conventionService.isListNullOrEmpty(descriptions.getItems())) {
for (Description description : descriptions.getItems()) recentActivityItemEntities.add(new RecentActivityItemEntity(RecentActivityItemType.Description, description.getId(), description.getUpdatedAt(), description.getLabel(), description.getStatus().getValue()));
for (Description description : descriptions.getItems()) recentActivityItemEntities.add(new RecentActivityItemEntity(RecentActivityItemType.Description, description.getId(), description.getUpdatedAt(), description.getLabel(), description.getStatus().getInternalStatus().getValue()));
}
}

View File

@ -69,6 +69,7 @@ import org.opencdmp.model.referencetype.ReferenceType;
import org.opencdmp.query.*;
import org.opencdmp.service.accounting.AccountingService;
import org.opencdmp.service.descriptiontemplate.DescriptionTemplateService;
import org.opencdmp.service.descriptionworkflow.DescriptionWorkflowService;
import org.opencdmp.service.elastic.ElasticService;
import org.opencdmp.service.filetransformer.FileTransformerService;
import org.opencdmp.service.responseutils.ResponseUtilsService;
@ -141,6 +142,7 @@ public class DescriptionServiceImpl implements DescriptionService {
private final TagService tagService;
private final UsageLimitService usageLimitService;
private final AccountingService accountingService;
private final DescriptionWorkflowService descriptionWorkflowService;
@Autowired
public DescriptionServiceImpl(
@ -155,7 +157,7 @@ public class DescriptionServiceImpl implements DescriptionService {
QueryFactory queryFactory,
JsonHandlingService jsonHandlingService,
UserScope userScope,
XmlHandlingService xmlHandlingService, NotifyIntegrationEventHandler eventHandler, NotificationProperties notificationProperties, FileTransformerService fileTransformerService, ElasticService elasticService, ValidatorFactory validatorFactory, StorageFileProperties storageFileConfig, StorageFileService storageFileService, AuthorizationContentResolver authorizationContentResolver, AnnotationEntityTouchedIntegrationEventHandler annotationEntityTouchedIntegrationEventHandler, AnnotationEntityRemovalIntegrationEventHandler annotationEntityRemovalIntegrationEventHandler, TenantScope tenantScope, ResponseUtilsService responseUtilsService, DescriptionTemplateService descriptionTemplateService, TagService tagService, UsageLimitService usageLimitService, AccountingService accountingService) {
XmlHandlingService xmlHandlingService, NotifyIntegrationEventHandler eventHandler, NotificationProperties notificationProperties, FileTransformerService fileTransformerService, ElasticService elasticService, ValidatorFactory validatorFactory, StorageFileProperties storageFileConfig, StorageFileService storageFileService, AuthorizationContentResolver authorizationContentResolver, AnnotationEntityTouchedIntegrationEventHandler annotationEntityTouchedIntegrationEventHandler, AnnotationEntityRemovalIntegrationEventHandler annotationEntityRemovalIntegrationEventHandler, TenantScope tenantScope, ResponseUtilsService responseUtilsService, DescriptionTemplateService descriptionTemplateService, TagService tagService, UsageLimitService usageLimitService, AccountingService accountingService, DescriptionWorkflowService descriptionWorkflowService) {
this.entityManager = entityManager;
this.authorizationService = authorizationService;
this.deleterFactory = deleterFactory;
@ -184,6 +186,7 @@ public class DescriptionServiceImpl implements DescriptionService {
this.tagService = tagService;
this.usageLimitService = usageLimitService;
this.accountingService = accountingService;
this.descriptionWorkflowService = descriptionWorkflowService;
}
@Override
@ -223,9 +226,17 @@ public class DescriptionServiceImpl implements DescriptionService {
data = this.entityManager.find(DescriptionEntity.class, model.getId());
if (data == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), Description.class.getSimpleName()}, LocaleContextHolder.getLocale()));
if (!this.conventionService.hashValue(data.getUpdatedAt()).equals(model.getHash())) throw new MyValidationException(this.errors.getHashConflict().getCode(), this.errors.getHashConflict().getMessage());
if (data.getStatus().equals(DescriptionStatus.Finalized)) throw new MyValidationException(this.errors.getDescriptionIsFinalized().getCode(), this.errors.getDescriptionIsFinalized().getMessage());
DescriptionStatusEntity oldDescriptionStatusEntity = this.entityManager.find(DescriptionStatusEntity.class, data.getStatusId(), true);
if (oldDescriptionStatusEntity == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{data.getStatusId(), org.opencdmp.model.descriptionstatus.DescriptionStatus.class.getSimpleName()}, LocaleContextHolder.getLocale()));
if (oldDescriptionStatusEntity.getInternalStatus() != null && oldDescriptionStatusEntity.getInternalStatus().equals(DescriptionStatus.Finalized)) throw new MyValidationException(this.errors.getDescriptionIsFinalized().getCode(), this.errors.getDescriptionIsFinalized().getMessage());
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());
if (model.getStatusId() != null && !model.getStatusId().equals(data.getStatusId())) {
data.setStatusId(model.getStatusId());
DescriptionStatusEntity newDescriptionStatusEntity = this.entityManager.find(DescriptionStatusEntity.class, model.getStatusId(), true);
if (newDescriptionStatusEntity == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{model.getStatusId(), org.opencdmp.model.descriptionstatus.DescriptionStatus.class.getSimpleName()}, LocaleContextHolder.getLocale()));
if (newDescriptionStatusEntity.getInternalStatus() != null && newDescriptionStatusEntity.getInternalStatus().equals(DescriptionStatus.Finalized)) data.setFinalizedAt(Instant.now());
}
} else {
this.usageLimitService.checkIncrease(UsageLimitTargetMetric.DESCRIPTION_COUNT);
@ -244,6 +255,8 @@ public class DescriptionServiceImpl implements DescriptionService {
data.setCreatedById(this.userScope.getUserId());
data.setPlanId(model.getPlanId());
data.setPlanDescriptionTemplateId(model.getPlanDescriptionTemplateId());
data.setStatus(DescriptionStatus.Draft);
data.setStatusId(this.descriptionWorkflowService.getWorkFlowDefinition().getStartingStatusId());
}
DescriptionTemplateEntity descriptionTemplateEntity = this.entityManager.find(DescriptionTemplateEntity.class, model.getDescriptionTemplateId(), true);
@ -257,8 +270,6 @@ public class DescriptionServiceImpl implements DescriptionService {
if (plan.getStatus().equals(PlanStatus.Finalized) && isUpdate) throw new MyValidationException(this.errors.getPlanIsFinalized().getCode(), this.errors.getPlanIsFinalized().getMessage());
data.setLabel(model.getLabel());
data.setStatus(model.getStatus());
if (model.getStatus() == DescriptionStatus.Finalized) data.setFinalizedAt(Instant.now());
data.setDescription(model.getDescription());
data.setDescriptionTemplateId(model.getDescriptionTemplateId());
data.setUpdatedAt(Instant.now());
@ -376,6 +387,10 @@ public class DescriptionServiceImpl implements DescriptionService {
}
private void sendNotification(DescriptionEntity description, Boolean isUpdate) throws InvalidApplicationException {
DescriptionStatusEntity descriptionStatusEntity = this.entityManager.find(DescriptionStatusEntity.class, description.getStatusId(), true);
if (descriptionStatusEntity == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{description, DescriptionStatus.class.getSimpleName()}, LocaleContextHolder.getLocale()));
if (descriptionStatusEntity.getInternalStatus() == null || descriptionStatusEntity.getInternalStatus().equals(DescriptionStatus.Canceled)) return;
List<PlanUserEntity> existingUsers = this.queryFactory.query(PlanUserQuery.class).disableTracking()
.planIds(description.getPlanId())
.isActives(IsActive.Active)
@ -388,7 +403,7 @@ public class DescriptionServiceImpl implements DescriptionService {
if (!planUser.getUserId().equals(this.userScope.getUserIdSafe())){
UserEntity user = this.queryFactory.query(UserQuery.class).disableTracking().ids(planUser.getUserId()).first();
if (user == null || user.getIsActive().equals(IsActive.Inactive)) throw new MyValidationException(this.errors.getPlanInactiveUser().getCode(), this.errors.getPlanInactiveUser().getMessage());
this.createDescriptionNotificationEvent(description, user, isUpdate);
this.createDescriptionNotificationEvent(description, descriptionStatusEntity.getInternalStatus(), user, isUpdate);
}
}
}
@ -437,11 +452,11 @@ public class DescriptionServiceImpl implements DescriptionService {
return cleanData;
}
private void createDescriptionNotificationEvent(DescriptionEntity description, UserEntity user, Boolean isUpdate) throws InvalidApplicationException {
private void createDescriptionNotificationEvent(DescriptionEntity description, DescriptionStatus internalStatus, UserEntity user, Boolean isUpdate) throws InvalidApplicationException {
NotifyIntegrationEvent event = new NotifyIntegrationEvent();
event.setUserId(user.getId());
this.applyNotificationType(description.getStatus(), event, isUpdate);
this.applyNotificationType(internalStatus, event, isUpdate);
NotificationFieldData data = new NotificationFieldData();
List<FieldInfo> fieldInfoList = new ArrayList<>();
fieldInfoList.add(new FieldInfo("{recipient}", DataType.String, user.getName()));
@ -482,16 +497,20 @@ public class DescriptionServiceImpl implements DescriptionService {
DescriptionEntity data = this.entityManager.find(DescriptionEntity.class, model.getId());
if (data == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), Description.class.getSimpleName()}, LocaleContextHolder.getLocale()));
if (!this.conventionService.hashValue(data.getUpdatedAt()).equals(model.getHash())) throw new MyValidationException(this.errors.getHashConflict().getCode(), this.errors.getHashConflict().getMessage());
if (!data.getStatus().equals(model.getStatus())){
if (data.getStatus().equals(DescriptionStatus.Finalized)){
if (!data.getStatusId().equals(model.getStatusId())){
DescriptionStatusEntity oldStatusEntity = this.queryFactory.query(DescriptionStatusQuery.class).disableTracking().ids(data.getStatusId()).isActive(IsActive.Active).firstAs(new BaseFieldSet().ensure(org.opencdmp.model.descriptionstatus.DescriptionStatus._id).ensure(org.opencdmp.model.descriptionstatus.DescriptionStatus._internalStatus));
if (oldStatusEntity == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{data.getStatusId(), DescriptionStatusEntity.class.getSimpleName()}, LocaleContextHolder.getLocale()));
if (oldStatusEntity.getInternalStatus() != null && oldStatusEntity.getInternalStatus().equals(DescriptionStatus.Finalized)){
this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.descriptionAffiliation(model.getId())), Permission.FinalizeDescription);
PlanEntity planEntity = this.entityManager.find(PlanEntity.class, data.getPlanId(), true);
if (planEntity == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{data.getPlanId(), PlanEntity.class.getSimpleName()}, LocaleContextHolder.getLocale()));
if(!planEntity.getStatus().equals(PlanStatus.Draft)) throw new MyValidationException(this.errors.getPlanIsFinalized().getCode(), this.errors.getPlanIsFinalized().getMessage());
}
data.setStatus(model.getStatus());
if (model.getStatus() == DescriptionStatus.Finalized) data.setFinalizedAt(Instant.now());
data.setStatusId(model.getStatusId());
DescriptionStatusEntity newStatusEntity = this.queryFactory.query(DescriptionStatusQuery.class).disableTracking().ids(model.getStatusId()).isActive(IsActive.Active).firstAs(new BaseFieldSet().ensure(org.opencdmp.model.descriptionstatus.DescriptionStatus._id).ensure(org.opencdmp.model.descriptionstatus.DescriptionStatus._internalStatus));
if (newStatusEntity == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{model.getStatusId(), DescriptionStatusEntity.class.getSimpleName()}, LocaleContextHolder.getLocale()));
if (newStatusEntity.getInternalStatus() != null && newStatusEntity.getInternalStatus().equals(DescriptionStatus.Finalized)) data.setFinalizedAt(Instant.now());
data.setUpdatedAt(Instant.now());
this.entityManager.merge(data);
@ -501,7 +520,7 @@ public class DescriptionServiceImpl implements DescriptionService {
this.eventBroker.emit(new DescriptionTouchedEvent(data.getId()));
this.annotationEntityTouchedIntegrationEventHandler.handleDescription(data.getId());
if (data.getStatus().equals(DescriptionStatus.Finalized)) this.sendNotification(data, true);
if (newStatusEntity.getInternalStatus() != null && newStatusEntity.getInternalStatus().equals(DescriptionStatus.Finalized)) this.sendNotification(data, true);
}
return this.builderFactory.builder(DescriptionBuilder.class).authorize(AuthorizationFlags.AllExceptPublic).build(BaseFieldSet.build(fields, Description._id), data);
}
@ -1003,7 +1022,8 @@ public class DescriptionServiceImpl implements DescriptionService {
persist.setId(data.getId());
persist.setLabel(data.getLabel());
persist.setStatus(DescriptionStatus.Finalized);
DescriptionStatusEntity statusEntity = this.queryFactory.query(DescriptionStatusQuery.class).disableTracking().internalStatuses(DescriptionStatus.Finalized).isActive(IsActive.Active).firstAs(new BaseFieldSet().ensure(org.opencdmp.model.descriptionstatus.DescriptionStatus._id));
if (statusEntity != null) persist.setStatusId(statusEntity.getId());
persist.setDescription(data.getDescription());
persist.setDescriptionTemplateId(data.getDescriptionTemplateId());
persist.setPlanId(data.getPlanId());
@ -1382,7 +1402,8 @@ public class DescriptionServiceImpl implements DescriptionService {
DescriptionPersist persist = new DescriptionPersist();
persist.setLabel(descriptionXml.getLabel());
persist.setDescription(descriptionXml.getDescription());
persist.setStatus(DescriptionStatus.Draft);
DescriptionStatusEntity statusEntity = this.queryFactory.query(DescriptionStatusQuery.class).disableTracking().isActive(IsActive.Active).internalStatuses(DescriptionStatus.Draft).firstAs(new BaseFieldSet().ensure(org.opencdmp.model.descriptionstatus.DescriptionStatus._id));
if (statusEntity != null) persist.setStatusId(statusEntity.getId());
persist.setPlanId(planId);
persist.setDescriptionTemplateId(this.xmlToDescriptionTemplatePersist(descriptionXml));
persist.setPlanDescriptionTemplateId(this.xmlToPlanDescriptionTemplatePersist(descriptionXml, planId));
@ -1598,7 +1619,8 @@ public class DescriptionServiceImpl implements DescriptionService {
DescriptionPersist persist = new DescriptionPersist();
persist.setLabel(model.getLabel());
persist.setDescription(model.getDescription());
persist.setStatus(DescriptionStatus.Draft);
DescriptionStatusEntity statusEntity = this.queryFactory.query(DescriptionStatusQuery.class).disableTracking().isActive(IsActive.Active).internalStatuses(DescriptionStatus.Draft).firstAs(new BaseFieldSet().ensure(org.opencdmp.model.descriptionstatus.DescriptionStatus._id));
if (statusEntity != null) persist.setStatusId(statusEntity.getId());
persist.setPlanId(planId);
persist.setDescriptionTemplateId(this.commonModelToDescriptionTemplatePersist(model));
persist.setPlanDescriptionTemplateId(this.commonModelTToPlanDescriptionTemplatePersist(model, planId));

View File

@ -10,6 +10,7 @@ import org.opencdmp.model.descriptionstatus.DescriptionStatus;
import org.opencdmp.model.persist.descriptionstatus.DescriptionStatusPersist;
import javax.management.InvalidApplicationException;
import java.util.List;
import java.util.UUID;
public interface DescriptionStatusService {
@ -17,4 +18,6 @@ public interface DescriptionStatusService {
DescriptionStatus persist(DescriptionStatusPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, JAXBException;
void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException;
List<DescriptionStatus> getAvailableTransitionStatuses(UUID descriptionId) throws InvalidApplicationException;
}

View File

@ -3,6 +3,7 @@ package org.opencdmp.service.descriptionstatus;
import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.tools.data.builder.BuilderFactory;
import gr.cite.tools.data.deleter.DeleterFactory;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.exception.MyApplicationException;
import gr.cite.tools.exception.MyForbiddenException;
import gr.cite.tools.exception.MyNotFoundException;
@ -12,22 +13,29 @@ import gr.cite.tools.fieldset.FieldSet;
import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.logging.MapLogEntry;
import jakarta.xml.bind.JAXBException;
import org.opencdmp.authorization.AuthorizationFlags;
import org.opencdmp.authorization.Permission;
import org.opencdmp.commons.XmlHandlingService;
import org.opencdmp.commons.enums.IsActive;
import org.opencdmp.commons.types.descriptionstatus.DescriptionStatusDefinitionAuthorizationEntity;
import org.opencdmp.commons.types.descriptionstatus.DescriptionStatusDefinitionAuthorizationItemEntity;
import org.opencdmp.commons.types.descriptionstatus.DescriptionStatusDefinitionEntity;
import org.opencdmp.commons.types.descriptionworkflow.DescriptionWorkflowDefinitionEntity;
import org.opencdmp.commons.types.descriptionworkflow.DescriptionWorkflowDefinitionTransitionEntity;
import org.opencdmp.convention.ConventionService;
import org.opencdmp.data.DescriptionEntity;
import org.opencdmp.data.DescriptionStatusEntity;
import org.opencdmp.data.TenantEntityManager;
import org.opencdmp.model.builder.descriptionstatus.DescriptionStatusBuilder;
import org.opencdmp.model.deleter.DescriptionStatusDeleter;
import org.opencdmp.model.description.Description;
import org.opencdmp.model.descriptionstatus.DescriptionStatus;
import org.opencdmp.model.persist.descriptionstatus.DescriptionStatusDefinitionAuthorizationItemPersist;
import org.opencdmp.model.persist.descriptionstatus.DescriptionStatusDefinitionAuthorizationPersist;
import org.opencdmp.model.persist.descriptionstatus.DescriptionStatusDefinitionPersist;
import org.opencdmp.model.persist.descriptionstatus.DescriptionStatusPersist;
import org.opencdmp.query.DescriptionStatusQuery;
import org.opencdmp.service.descriptionworkflow.DescriptionWorkflowService;
import org.slf4j.LoggerFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
@ -35,8 +43,10 @@ import org.springframework.stereotype.Service;
import javax.management.InvalidApplicationException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
@Service
public class DescriptionStatusServiceImpl implements DescriptionStatusService {
@ -51,8 +61,10 @@ public class DescriptionStatusServiceImpl implements DescriptionStatusService {
private final ConventionService conventionService;
private final MessageSource messageSource;
private final XmlHandlingService xmlHandlingService;
private final QueryFactory queryFactory;
private final DescriptionWorkflowService descriptionWorkflowService;
public DescriptionStatusServiceImpl(BuilderFactory builderFactory, DeleterFactory deleterFactory, AuthorizationService authService, TenantEntityManager entityManager, ConventionService conventionService, MessageSource messageSource, XmlHandlingService xmlHandlingService) {
public DescriptionStatusServiceImpl(BuilderFactory builderFactory, DeleterFactory deleterFactory, AuthorizationService authService, TenantEntityManager entityManager, ConventionService conventionService, MessageSource messageSource, XmlHandlingService xmlHandlingService, QueryFactory queryFactory, DescriptionWorkflowService descriptionWorkflowService) {
this.builderFactory = builderFactory;
this.deleterFactory = deleterFactory;
@ -61,6 +73,8 @@ public class DescriptionStatusServiceImpl implements DescriptionStatusService {
this.conventionService = conventionService;
this.messageSource = messageSource;
this.xmlHandlingService = xmlHandlingService;
this.queryFactory = queryFactory;
this.descriptionWorkflowService = descriptionWorkflowService;
}
@ -141,4 +155,20 @@ public class DescriptionStatusServiceImpl implements DescriptionStatusService {
return data;
}
public List<DescriptionStatus> getAvailableTransitionStatuses(UUID descriptionId) throws InvalidApplicationException {
DescriptionWorkflowDefinitionEntity definition = this.descriptionWorkflowService.getWorkFlowDefinition();
DescriptionEntity description = this.entityManager.find(DescriptionEntity.class, descriptionId);
if (description == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{descriptionId, Description.class.getSimpleName()}, LocaleContextHolder.getLocale()));
List<DescriptionWorkflowDefinitionTransitionEntity> availableTransitions = definition.getStatusTransitions().stream().filter(x -> x.getFromStatusId().equals(description.getStatusId())).collect(Collectors.toList());
if (!this.conventionService.isListNullOrEmpty(availableTransitions)){
DescriptionStatusQuery query = this.queryFactory.query(DescriptionStatusQuery.class).authorize(AuthorizationFlags.AllExceptPublic).isActive(IsActive.Active).ids(availableTransitions.stream().map(DescriptionWorkflowDefinitionTransitionEntity::getToStatusId).distinct().toList());
FieldSet fieldSet = new BaseFieldSet().ensure(DescriptionStatus._id).ensure(DescriptionStatus._name).ensure(DescriptionStatus._internalStatus);
return this.builderFactory.builder(DescriptionStatusBuilder.class).authorize(AuthorizationFlags.AllExceptPublic).build(fieldSet, query.collectAs(fieldSet));
}
return new ArrayList<>();
}
}

View File

@ -1,6 +1,7 @@
package org.opencdmp.service.descriptionworkflow;
import gr.cite.tools.fieldset.FieldSet;
import org.opencdmp.commons.types.descriptionworkflow.DescriptionWorkflowDefinitionEntity;
import org.opencdmp.model.descriptionworkflow.DescriptionWorkflow;
import org.opencdmp.model.persist.descriptionworkflow.DescriptionWorkflowPersist;
@ -11,4 +12,6 @@ public interface DescriptionWorkflowService {
DescriptionWorkflow persist(DescriptionWorkflowPersist persist, FieldSet fields) throws InvalidApplicationException;
void deleteAndSave(UUID id) throws InvalidApplicationException;
DescriptionWorkflowDefinitionEntity getWorkFlowDefinition() throws InvalidApplicationException;
}

View File

@ -3,15 +3,19 @@ package org.opencdmp.service.descriptionworkflow;
import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.tools.data.builder.BuilderFactory;
import gr.cite.tools.data.deleter.DeleterFactory;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.exception.MyApplicationException;
import gr.cite.tools.exception.MyNotFoundException;
import gr.cite.tools.exception.MyValidationException;
import gr.cite.tools.fieldset.BaseFieldSet;
import gr.cite.tools.fieldset.FieldSet;
import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.logging.MapLogEntry;
import org.opencdmp.authorization.AuthorizationFlags;
import org.opencdmp.authorization.Permission;
import org.opencdmp.commons.XmlHandlingService;
import org.opencdmp.commons.enums.IsActive;
import org.opencdmp.commons.scope.tenant.TenantScope;
import org.opencdmp.commons.types.descriptionworkflow.DescriptionWorkflowDefinitionEntity;
import org.opencdmp.commons.types.descriptionworkflow.DescriptionWorkflowDefinitionTransitionEntity;
import org.opencdmp.convention.ConventionService;
@ -24,6 +28,7 @@ import org.opencdmp.model.descriptionworkflow.DescriptionWorkflow;
import org.opencdmp.model.persist.descriptionworkflow.DescriptionWorkflowDefinitionPersist;
import org.opencdmp.model.persist.descriptionworkflow.DescriptionWorkflowDefinitionTransitionPersist;
import org.opencdmp.model.persist.descriptionworkflow.DescriptionWorkflowPersist;
import org.opencdmp.query.DescriptionWorkflowQuery;
import org.slf4j.LoggerFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
@ -48,8 +53,10 @@ public class DescriptionWorkflowServiceImpl implements DescriptionWorkflowServic
private final TenantEntityManager entityManager;
private final MessageSource messageSource;
private final ErrorThesaurusProperties errors;
private final TenantScope tenantScope;
private final QueryFactory queryFactory;
public DescriptionWorkflowServiceImpl(AuthorizationService authService, ConventionService conventionService, XmlHandlingService xmlHandlingService, BuilderFactory builderFactory, DeleterFactory deleterFactory, TenantEntityManager entityManager, MessageSource messageSource, ErrorThesaurusProperties errors) {
public DescriptionWorkflowServiceImpl(AuthorizationService authService, ConventionService conventionService, XmlHandlingService xmlHandlingService, BuilderFactory builderFactory, DeleterFactory deleterFactory, TenantEntityManager entityManager, MessageSource messageSource, ErrorThesaurusProperties errors, TenantScope tenantScope, QueryFactory queryFactory) {
this.authService = authService;
this.conventionService = conventionService;
this.xmlHandlingService = xmlHandlingService;
@ -58,6 +65,8 @@ public class DescriptionWorkflowServiceImpl implements DescriptionWorkflowServic
this.entityManager = entityManager;
this.messageSource = messageSource;
this.errors = errors;
this.tenantScope = tenantScope;
this.queryFactory = queryFactory;
}
@ -132,4 +141,22 @@ public class DescriptionWorkflowServiceImpl implements DescriptionWorkflowServic
return data;
}
@Override
public DescriptionWorkflowDefinitionEntity getWorkFlowDefinition() throws InvalidApplicationException {
DescriptionWorkflowQuery query = this.queryFactory.query(DescriptionWorkflowQuery.class).authorize(AuthorizationFlags.AllExceptPublic).isActives(IsActive.Active);
if (this.tenantScope.isDefaultTenant())
query = query.defaultTenant(true);
else
query = query.tenantIds(this.tenantScope.getTenant());
DescriptionWorkflowEntity entity = query.first();
if (entity == null) throw new MyApplicationException("Description workflow not found!");
DescriptionWorkflowDefinitionEntity definition = this.xmlHandlingService.fromXmlSafe(DescriptionWorkflowDefinitionEntity.class, entity.getDefinition());
if (definition == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{entity.getId(), DescriptionWorkflowDefinitionEntity.class.getSimpleName()}, LocaleContextHolder.getLocale()));
return definition;
}
}

View File

@ -98,6 +98,7 @@ import org.opencdmp.service.accounting.AccountingService;
import org.opencdmp.service.actionconfirmation.ActionConfirmationService;
import org.opencdmp.service.description.DescriptionService;
import org.opencdmp.service.descriptiontemplate.DescriptionTemplateService;
import org.opencdmp.service.descriptionworkflow.DescriptionWorkflowService;
import org.opencdmp.service.elastic.ElasticService;
import org.opencdmp.service.filetransformer.FileTransformerService;
import org.opencdmp.service.planblueprint.PlanBlueprintService;
@ -183,6 +184,7 @@ public class PlanServiceImpl implements PlanService {
private final PlanBlueprintService planBlueprintService;
private final UsageLimitService usageLimitService;
private final AccountingService accountingService;
private final DescriptionWorkflowService descriptionWorkflowService;
@Autowired
public PlanServiceImpl(
@ -205,7 +207,7 @@ public class PlanServiceImpl implements PlanService {
FileTransformerService fileTransformerService,
ValidatorFactory validatorFactory,
ElasticService elasticService, DescriptionTemplateService descriptionTemplateService,
AnnotationEntityTouchedIntegrationEventHandler annotationEntityTouchedIntegrationEventHandler, AnnotationEntityRemovalIntegrationEventHandler annotationEntityRemovalIntegrationEventHandler, AuthorizationContentResolver authorizationContentResolver, TenantScope tenantScope, ResponseUtilsService responseUtilsService, PlanBlueprintService planBlueprintService, UsageLimitService usageLimitService, AccountingService accountingService) {
AnnotationEntityTouchedIntegrationEventHandler annotationEntityTouchedIntegrationEventHandler, AnnotationEntityRemovalIntegrationEventHandler annotationEntityRemovalIntegrationEventHandler, AuthorizationContentResolver authorizationContentResolver, TenantScope tenantScope, ResponseUtilsService responseUtilsService, PlanBlueprintService planBlueprintService, UsageLimitService usageLimitService, AccountingService accountingService, DescriptionWorkflowService descriptionWorkflowService) {
this.entityManager = entityManager;
this.authorizationService = authorizationService;
this.deleterFactory = deleterFactory;
@ -234,6 +236,7 @@ public class PlanServiceImpl implements PlanService {
this.planBlueprintService = planBlueprintService;
this.usageLimitService = usageLimitService;
this.accountingService = accountingService;
this.descriptionWorkflowService = descriptionWorkflowService;
}
public Plan persist(PlanPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, JAXBException, IOException {
@ -620,6 +623,7 @@ public class PlanServiceImpl implements PlanService {
newDescription.setLabel(existing.getLabel());
newDescription.setDescription(existing.getDescription());
newDescription.setStatus(DescriptionStatus.Draft);
newDescription.setStatusId(this.descriptionWorkflowService.getWorkFlowDefinition().getStartingStatusId());
newDescription.setProperties(existing.getProperties());
newDescription.setPlanId(planId);
if (newPlanDescriptionTemplateId == null && planDescriptionTemplateRemap != null) newDescription.setPlanDescriptionTemplateId(planDescriptionTemplateRemap.get(existing.getPlanDescriptionTemplateId()));
@ -740,6 +744,7 @@ public class PlanServiceImpl implements PlanService {
newDescription.setLabel(existing.getLabel());
newDescription.setDescription(existing.getDescription());
newDescription.setStatus(DescriptionStatus.Draft);
newDescription.setStatusId(this.descriptionWorkflowService.getWorkFlowDefinition().getStartingStatusId());
newDescription.setProperties(existing.getProperties());
newDescription.setPlanId(planId);
if (planDescriptionTemplateRemap != null) newDescription.setPlanDescriptionTemplateId(planDescriptionTemplateRemap.get(existing.getPlanDescriptionTemplateId()));
@ -1593,25 +1598,36 @@ public class PlanServiceImpl implements PlanService {
List<DescriptionEntity> descriptions = this.queryFactory.query(DescriptionQuery.class)
.authorize(AuthorizationFlags.AllExceptPublic).planIds(id).isActive(IsActive.Active).collect();
if (!this.conventionService.isListNullOrEmpty(descriptions)) {
List<DescriptionStatusEntity> statusEntities = this.queryFactory.query(DescriptionStatusQuery.class).authorize(AuthorizationFlags.AllExceptPublic).ids(descriptions.stream().map(DescriptionEntity::getStatusId).distinct().toList()).isActive(IsActive.Active).collect();
if (this.conventionService.isListNullOrEmpty(statusEntities)) throw new MyApplicationException("Not found description statuses");
DescriptionStatusEntity finalizedStatusEntity = this.queryFactory.query(DescriptionStatusQuery.class).disableTracking().internalStatuses(DescriptionStatus.Finalized).isActive(IsActive.Active).firstAs(new BaseFieldSet().ensure(org.opencdmp.model.descriptionstatus.DescriptionStatus._id));
if (finalizedStatusEntity == null) throw new MyApplicationException("finalized status not found");
DescriptionStatusEntity canceledStatusEntity = this.queryFactory.query(DescriptionStatusQuery.class).disableTracking().internalStatuses(DescriptionStatus.Canceled).isActive(IsActive.Active).firstAs(new BaseFieldSet().ensure(org.opencdmp.model.descriptionstatus.DescriptionStatus._id));
if (canceledStatusEntity == null) throw new MyApplicationException("canceled status not found");
for (DescriptionEntity description: descriptions) {
DescriptionStatusEntity currentStatusEntity = statusEntities.stream().filter(x -> x.getId().equals(description.getStatusId())).findFirst().orElse(null);
if (descriptionIds.contains(description.getId())){
// description to be finalized
if (description.getStatus().equals(DescriptionStatus.Finalized)){
if (currentStatusEntity != null && currentStatusEntity.getInternalStatus()!= null && currentStatusEntity.getInternalStatus().equals(DescriptionStatus.Finalized)){
throw new MyApplicationException("Description is already finalized");
}
if (this.descriptionService.validate(List.of(description.getId())).getFirst().getResult().equals(DescriptionValidationOutput.Invalid)){
throw new MyApplicationException("Description is invalid");
}
description.setStatus(DescriptionStatus.Finalized);
if (finalizedStatusEntity != null) description.setStatusId(finalizedStatusEntity.getId());
description.setUpdatedAt(Instant.now());
description.setFinalizedAt(Instant.now());
this.entityManager.merge(description);
} else if (description.getStatus().equals(DescriptionStatus.Draft)) {
} else if (currentStatusEntity != null && currentStatusEntity.getInternalStatus()!= null && !currentStatusEntity.getInternalStatus().equals(DescriptionStatus.Finalized)) {
// description to be canceled
description.setStatus(DescriptionStatus.Canceled);
description.setStatusId(canceledStatusEntity.getId());
this.deleterFactory.deleter(DescriptionDeleter.class).delete(List.of(description), true);
}
}
}
PlanStatus previousStatus = plan.getStatus();
plan.setStatus(PlanStatus.Finalized);

View File

@ -20,6 +20,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse;
import jakarta.xml.bind.JAXBException;
import org.opencdmp.audit.AuditableAction;
import org.opencdmp.authorization.AuthorizationFlags;
import org.opencdmp.commons.enums.IsActive;
import org.opencdmp.controllers.swagger.SwaggerHelpers;
import org.opencdmp.controllers.swagger.annotation.OperationWithTenantHeader;
import org.opencdmp.controllers.swagger.annotation.Swagger400;
@ -27,11 +28,15 @@ import org.opencdmp.controllers.swagger.annotation.Swagger404;
import org.opencdmp.controllers.swagger.annotation.SwaggerCommonErrorResponses;
import org.opencdmp.data.DescriptionStatusEntity;
import org.opencdmp.model.builder.descriptionstatus.DescriptionStatusBuilder;
import org.opencdmp.model.builder.descriptionworkflow.DescriptionWorkflowBuilder;
import org.opencdmp.model.censorship.descriptionstatus.DescriptionStatusCensor;
import org.opencdmp.model.censorship.descriptionworkflow.DescriptionWorkflowCensor;
import org.opencdmp.model.descriptionstatus.DescriptionStatus;
import org.opencdmp.model.descriptionworkflow.DescriptionWorkflow;
import org.opencdmp.model.persist.descriptionstatus.DescriptionStatusPersist;
import org.opencdmp.model.result.QueryResult;
import org.opencdmp.query.DescriptionStatusQuery;
import org.opencdmp.query.DescriptionWorkflowQuery;
import org.opencdmp.query.lookup.DescriptionStatusLookup;
import org.opencdmp.service.descriptionstatus.DescriptionStatusService;
import org.slf4j.LoggerFactory;
@ -183,4 +188,23 @@ public class DescriptionStatusController {
this.auditService.track(AuditableAction.DescriptionStatus_Delete, "id", id);
}
@GetMapping("available-transitions/{descriptionId}")
@OperationWithTenantHeader(summary = "Get available status transitions for description", description = "",
responses = @ApiResponse(description = "OK", responseCode = "200", content = @Content(
schema = @Schema(
implementation = DescriptionWorkflow.class
))
))
@Swagger404
public List<DescriptionStatus> GetAvailableTransitions(
@Parameter(name = "descriptionId", description = "The id of description", example = "c0c163dc-2965-45a5-9608-f76030578609", required = true) @PathVariable UUID descriptionId
) throws InvalidApplicationException {
logger.debug(new MapLogEntry("retrieving available statuses" + DescriptionStatus.class.getSimpleName()));
List<DescriptionStatus> availableTransitionStatuses = this.descriptionStatusService.getAvailableTransitionStatuses(descriptionId);
this.auditService.track(AuditableAction.DescriptionStatus_Delete, "descriptionId", descriptionId);
return availableTransitionStatuses;
}
}

View File

@ -7,9 +7,11 @@ import { Reference, ReferencePersist } from "../reference/reference";
import { Tag, TagPersist } from "../tag/tag";
import { User } from "../user/user";
import { AppPermission } from "@app/core/common/enum/permission.enum";
import { DescriptionStatus } from "../description-status/description-status";
export interface Description extends BaseDescription {
label?: string;
status?: DescriptionStatus;
properties?: DescriptionPropertyDefinition;
description?: string;
createdBy?: User;
@ -76,7 +78,7 @@ export interface DescriptionPersist extends BaseEntityPersist {
planId: Guid;
planDescriptionTemplateId: Guid;
descriptionTemplateId: Guid;
status: DescriptionStatusEnum;
statusId: Guid;
description: string;
properties: DescriptionPropertyDefinitionPersist;
tags: string[];
@ -120,7 +122,7 @@ export interface DescriptionReferencePersist {
export interface DescriptionStatusPersist {
id: Guid;
status: DescriptionStatusEnum;
statusId?: Guid;
hash: string;
}
@ -130,7 +132,7 @@ export interface DescriptionStatusPersist {
export interface PublicDescription extends BaseDescription {
label?: string;
status?: DescriptionStatusEnum;
status?: PublicDescriptionStatus;
description?: string;
finalizedAt?: Date;
descriptionTemplate?: PublicDescriptionTemplate;
@ -148,6 +150,13 @@ export interface PublicDescriptionTemplate {
label: string;
description: string;
}
export interface PublicDescriptionStatus {
id: Guid;
name: string;
internalStatus: DescriptionStatusEnum;
}
export interface DescriptionSectionPermissionResolver {
planId: Guid;
sectionIds: Guid[];
@ -161,5 +170,4 @@ export interface UpdateDescriptionTemplatePersist {
export interface BaseDescription extends BaseEntity {
tenantId?: Guid;
status?: DescriptionStatusEnum;
}

View File

@ -56,6 +56,15 @@ export class DescriptionStatusService {
catchError((error: any) => throwError(() => error)));
}
getAvailableTransitions(descriptionId: Guid, reqFields: string[] = []): Observable<Array<DescriptionStatus>> {
const url = `${this.apiBase}/available-transitions/${descriptionId}`;
const options = { params: { f: reqFields } };
return this.http
.get<Array<DescriptionStatus>>(url, options).pipe(
catchError((error: any) => throwError(() => error)));
}
buildLookup(params: {
like?: string,
excludedIds?: Guid[],

View File

@ -28,6 +28,7 @@ import { BehaviorSubject } from 'rxjs';
import { debounceTime, map, takeUntil } from 'rxjs/operators';
import { nameof } from 'ts-simple-nameof';
import { ActivityListingType } from '../dashboard.component';
import { DescriptionStatus } from '@app/core/model/description-status/description-status';
@Component({
selector: 'app-recent-edited-activity',
@ -232,16 +233,16 @@ export class RecentEditedActivityComponent extends BaseComponent implements OnIn
if (item.plan){
if (item.plan.descriptions) {
if (item.plan.status == PlanStatusEnum.Finalized) {
item.plan.descriptions = item.plan.descriptions.filter(x => x.isActive === IsActive.Active && x.status === DescriptionStatusEnum.Finalized);
item.plan.descriptions = item.plan.descriptions.filter(x => x.isActive === IsActive.Active && x.status?.internalStatus === DescriptionStatusEnum.Finalized);
} else {
item.plan.descriptions = item.plan.descriptions.filter(x => x.isActive === IsActive.Active && x.status != DescriptionStatusEnum.Canceled);
item.plan.descriptions = item.plan.descriptions.filter(x => x.isActive === IsActive.Active && x.status?.internalStatus != DescriptionStatusEnum.Canceled);
}
}
item.plan.planUsers = item.plan.planUsers.filter(x=> x.isActive === IsActive.Active);
this.listingItems.push(item);
}
if (item.description){
if (item.description.status != DescriptionStatusEnum.Canceled) this.listingItems.push(item);
if (item.description?.status?.internalStatus != DescriptionStatusEnum.Canceled) this.listingItems.push(item);
}
})
@ -288,7 +289,9 @@ export class RecentEditedActivityComponent extends BaseComponent implements OnIn
[nameof<RecentActivityItem>(x => x.plan), nameof<Plan>(x => x.authorizationFlags), AppPermission.EditPlan].join('.'),
[nameof<RecentActivityItem>(x => x.plan), nameof<Plan>(x => x.descriptions), nameof<Description>(x => x.id)].join('.'),
[nameof<RecentActivityItem>(x => x.plan), nameof<Plan>(x => x.descriptions), nameof<Description>(x => x.label)].join('.'),
[nameof<RecentActivityItem>(x => x.plan), nameof<Plan>(x => x.descriptions), nameof<Description>(x => x.status)].join('.'),
[nameof<RecentActivityItem>(x => x.plan), nameof<Plan>(x => x.descriptions), nameof<Description>(x => x.status), nameof<DescriptionStatus>(x => x.id)].join('.'),
[nameof<RecentActivityItem>(x => x.plan), nameof<Plan>(x => x.descriptions), nameof<Description>(x => x.status), nameof<DescriptionStatus>(x => x.name)].join('.'),
[nameof<RecentActivityItem>(x => x.plan), nameof<Plan>(x => x.descriptions), nameof<Description>(x => x.status), nameof<DescriptionStatus>(x => x.internalStatus)].join('.'),
[nameof<RecentActivityItem>(x => x.plan), nameof<Plan>(x => x.descriptions), nameof<Description>(x => x.isActive)].join('.'),
[nameof<RecentActivityItem>(x => x.plan), nameof<Plan>(x => x.blueprint), nameof<PlanBlueprint>(x => x.id)].join('.'),
[nameof<RecentActivityItem>(x => x.plan), nameof<Plan>(x => x.blueprint), nameof<PlanBlueprint>(x => x.label)].join('.'),
@ -318,7 +321,9 @@ export class RecentEditedActivityComponent extends BaseComponent implements OnIn
return [
[nameof<RecentActivityItem>(x => x.description), nameof<Description>(x => x.id)].join('.'),
[nameof<RecentActivityItem>(x => x.description), nameof<Description>(x => x.label)].join('.'),
[nameof<RecentActivityItem>(x => x.description), nameof<Description>(x => x.status)].join('.'),
[nameof<RecentActivityItem>(x => x.description), nameof<Description>(x => x.status), nameof<DescriptionStatus>(x => x.id)].join('.'),
[nameof<RecentActivityItem>(x => x.description), nameof<Description>(x => x.status), nameof<DescriptionStatus>(x => x.name)].join('.'),
[nameof<RecentActivityItem>(x => x.description), nameof<Description>(x => x.status), nameof<DescriptionStatus>(x => x.internalStatus)].join('.'),
[nameof<RecentActivityItem>(x => x.description), nameof<Description>(x => x.updatedAt)].join('.'),
[nameof<RecentActivityItem>(x => x.description), nameof<Description>(x => x.isActive)].join('.'),
[nameof<RecentActivityItem>(x => x.description), nameof<Description>(x => x.authorizationFlags), AppPermission.EditDescription].join('.'),

View File

@ -46,7 +46,7 @@ export class DescriptionBaseFieldsEditorComponent extends BaseComponent {
if (this.description?.descriptionTemplate != null) {
const isPreviousVersion: boolean = this.description.descriptionTemplate.versionStatus === DescriptionTemplateVersionStatus.Previous;
if (isPreviousVersion === true) {
if (this.description.status === DescriptionStatusEnum.Draft) {
if (this.description.status?.internalStatus === DescriptionStatusEnum.Draft) {
this.openDeprecatedDescriptionTemplateDialog();
} else {
this.availableDescriptionTemplates.push(this.description.descriptionTemplate);

View File

@ -67,9 +67,12 @@
<button [disabled]="saving" mat-menu-item (click)="saveAndClose()" type="button">{{ 'DESCRIPTION-EDITOR.ACTIONS.SAVE-AND-CLOSE' | translate }}</button>
<button [disabled]="saving" mat-menu-item (click)="saveAndContinue()" type="button">{{ 'DESCRIPTION-EDITOR.ACTIONS.SAVE-AND-CONTINUE' | translate }}</button>
</mat-menu>
<button [disabled]="saving" *ngIf="canEdit && !isLocked && !viewOnly && hasReversableStatus() == false && canEdit" mat-button class="rounded-btn neutral mr-2" type="button" (click)="finalize()">{{ 'DESCRIPTION-EDITOR.ACTIONS.FINALIZE' | translate }}</button>
<ng-container *ngIf="availableStatusesTransitions && availableStatusesTransitions.length > 0 && !isLocked && item.id">
<button *ngFor='let status of availableStatusesTransitions' [disabled]="saving" mat-button class="rounded-btn neutral mr-2" type="button" (click)="persistStatus(status)">{{ status.name }}</button>
</ng-container>
<!-- <button [disabled]="saving" *ngIf="canEdit && !isLocked && !viewOnly && hasReversableStatus() == false && canEdit" mat-button class="rounded-btn neutral mr-2" type="button" (click)="finalize()">{{ 'DESCRIPTION-EDITOR.ACTIONS.FINALIZE' | translate }}</button> -->
<button [disabled]="saving" *ngIf="isLocked" mat-button disabled class="rounded-btn neutral cursor-default" type="button">{{ 'PLAN-OVERVIEW.LOCKED' | translate}}</button>
<button [disabled]="saving" *ngIf="hasReversableStatus() && !isLocked && canEdit" mat-button class="rounded-btn neutral mr-2" (click)="reverse()" type="button">{{ 'DESCRIPTION-EDITOR.ACTIONS.REVERSE' | translate }}</button>
<!-- <button [disabled]="saving" *ngIf="hasReversableStatus() && !isLocked && canEdit" mat-button class="rounded-btn neutral mr-2" (click)="reverse()" type="button">{{ 'DESCRIPTION-EDITOR.ACTIONS.REVERSE' | translate }}</button> -->
</div>
</div>
</div>

View File

@ -45,6 +45,8 @@ import { DescriptionEditorEntityResolver } from './resolvers/description-editor-
import { ToCEntry } from './table-of-contents/models/toc-entry';
import { TableOfContentsService } from './table-of-contents/services/table-of-contents-service';
import { TableOfContentsComponent } from './table-of-contents/table-of-contents.component';
import { DescriptionStatusService } from '@app/core/services/description-status/description-status.service';
import { DescriptionStatus } from '@app/core/model/description-status/description-status';
@Component({
selector: 'app-description-editor-component',
@ -82,6 +84,9 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
private initialTemplateId: string = Guid.EMPTY;
private permissionPerSection: Map<Guid, string[]>;
availableStatusesTransitions: DescriptionStatus[];
oldStatusId: Guid;
constructor(
// BaseFormEditor injected dependencies
public routerUtils: RouterUtilsService,
@ -111,6 +116,7 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
private tableOfContentsService: TableOfContentsService,
private descriptionFormService: DescriptionFormService,
private formAnnotationService: FormAnnotationService,
private descriptionStatusService: DescriptionStatusService
) {
const descriptionLabel: string = route.snapshot.data['entity']?.label;
if (descriptionLabel) {
@ -190,7 +196,8 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
}
if (this.route.snapshot.url[1] && this.route.snapshot.url[1].path == 'finalize' && !this.lockStatus && !this.viewOnly) {
setTimeout(() => {
this.finalize();
const finalizedStatus = this.availableStatusesTransitions?.find(x => x.internalStatus === DescriptionStatusEnum.Finalized) || null;
if (finalizedStatus) this.finalize(finalizedStatus.id);
}, 0);
}
});
@ -215,6 +222,10 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
prepareForm(data: Description) {
try {
this.editorModel = data ? new DescriptionEditorModel().fromModel(data, data.descriptionTemplate) : new DescriptionEditorModel();
if (data) {
if (data.id) this.getAvailableStatuses(data.id);
if (data.status?.id) this.oldStatusId = data.status.id
}
if (data && data?.plan?.planUsers) data.plan.planUsers = data.plan.planUsers.filter(x => x.isActive === IsActive.Active);
this.item = data;
this.initialTemplateId = data?.descriptionTemplate?.id?.toString();
@ -227,6 +238,13 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
}
this.isDeleted = data ? data.isActive === IsActive.Inactive : false;
this.buildForm();
if (data?.status?.internalStatus == DescriptionStatusEnum.Finalized || this.isDeleted || !this.canEdit) {
this.viewOnly = true;
this.isFinalized = true;
this.formGroup.disable();
} else {
this.viewOnly = false;
}
if (this.isDeleted || (this.formGroup && this.editorModel.belongsToCurrentTenant == false)) {
this.formGroup.disable();
this.canEdit = false;
@ -245,17 +263,19 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
// this.selectedSystemFields = this.selectedSystemFieldDisabled();
this.descriptionEditorService.setValidationErrorModel(this.editorModel.validationErrorModel);
if (this.editorModel.status == DescriptionStatusEnum.Finalized || this.isDeleted || !this.canEdit) {
this.viewOnly = true;
this.isFinalized = this.editorModel.status == DescriptionStatusEnum.Finalized;
this.formGroup.disable();
} else {
this.viewOnly = false;
}
this.registerFormListeners();
}
getAvailableStatuses(id: Guid){
this.descriptionStatusService.getAvailableTransitions(id).pipe(takeUntil(this._destroyed))
.subscribe(
(statuses) => {
this.availableStatusesTransitions = statuses;
},
(error) => this.httpErrorHandlingService.handleBackedRequestError(error)
); }
calculateMultiplicityRejectedPlanDescriptionTemplates(section: PlanBlueprintDefinitionSection, descriptions: Description[]): PlanDescriptionTemplate[] {
if (section.descriptionTemplates?.length > 0) {
descriptions = descriptions?.filter(x => x?.planDescriptionTemplate?.sectionId === section.id) || [];
@ -302,17 +322,18 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
persistEntity(onSuccess?: (response) => void): void {
const formData = this.formService.getValue(this.formGroup.value) as DescriptionPersist;
const finalizedStatus = this.availableStatusesTransitions?.find(x => x.internalStatus === DescriptionStatusEnum.Finalized) || null;
this.descriptionService.persist(formData)
.pipe(takeUntil(this._destroyed)).subscribe(
complete => {
onSuccess ? onSuccess(complete) : this.onCallbackSuccess(complete);
this.descriptionIsOnceSaved = true;
if (this.formGroup.get('status').value == DescriptionStatusEnum.Finalized) this.isFinalized = true;
if (finalizedStatus && this.formGroup.get('statusId').value == finalizedStatus.id) this.isFinalized = true;
},
error => {
if (this.formGroup.get('status').value == DescriptionStatusEnum.Finalized) {
this.formGroup.get('status').setValue(DescriptionStatusEnum.Draft);
if (finalizedStatus && this.formGroup.get('statusId').value == finalizedStatus.id) {
this.formGroup.get('statusId').setValue(this.oldStatusId);
this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.UNSUCCESSFUL-FINALIZE'), SnackBarNotificationLevel.Error);
} else {
this.onCallbackError(error);
@ -324,7 +345,7 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
formSubmit(onSuccess?: (response) => void): void {
this.formService.removeAllBackEndErrors(this.formGroup);
if (this.formGroup.get('label').valid && this.formGroup.get('planId').valid && this.formGroup.get('planDescriptionTemplateId').valid
&& this.formGroup.get('descriptionTemplateId').valid && this.formGroup.get('status').valid) {
&& this.formGroup.get('descriptionTemplateId').valid) {// && this.formGroup.get('statusId').valid) {
this.persistEntity(onSuccess);
} else {
const errorMessages = this._buildSemiFormErrorMessages();
@ -698,7 +719,19 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
return [];
}
finalize() {
persistStatus(status: DescriptionStatus) {
if (status.internalStatus != null && status.internalStatus === DescriptionStatusEnum.Finalized) {
this.finalize(status.id);
} else if (status.internalStatus != null && this.item.status.internalStatus === DescriptionStatusEnum.Finalized){
this.reverse(status.id);
} else {
// other statuses
this.formGroup.get('statusId').setValue(status.id);
this.persistEntity();
}
}
finalize(statusId: Guid) {
this.formService.removeAllBackEndErrors(this.formGroup);
this.formService.touchAllFormFields(this.formGroup);
this.formService.validateAllFormFields(this.formGroup);
@ -727,13 +760,13 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
});
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => {
if (result) {
this.formGroup.get('status').setValue(DescriptionStatusEnum.Finalized);
this.formGroup.get('statusId').setValue(statusId);
this.persistEntity();
}
});
}
reverse() {
reverse(statusId: Guid) {
const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
restoreFocus: false,
data: {
@ -747,7 +780,7 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
if (result) {
const planUserRemovePersist: DescriptionStatusPersist = {
id: this.formGroup.get('id').value,
status: DescriptionStatusEnum.Draft,
statusId: statusId,
hash: this.formGroup.get('hash').value
};
this.descriptionService.persistStatus(planUserRemovePersist, DescriptionEditorEntityResolver.lookupFields()).pipe(takeUntil(this._destroyed))

View File

@ -18,7 +18,7 @@ export class DescriptionEditorModel extends BaseEditorModel implements Descripti
planId: Guid;
planDescriptionTemplateId: Guid;
descriptionTemplateId: Guid;
status: DescriptionStatusEnum;
statusId: Guid;
description: string;
properties: DescriptionPropertyDefinitionEditorModel = new DescriptionPropertyDefinitionEditorModel(this.validationErrorModel);
tags: string[] = [];
@ -36,7 +36,7 @@ export class DescriptionEditorModel extends BaseEditorModel implements Descripti
this.planId = item.plan?.id;
this.planDescriptionTemplateId = item.planDescriptionTemplate?.id;
this.descriptionTemplateId = item.descriptionTemplate?.id;
this.status = item.status ?? DescriptionStatusEnum.Draft;
this.statusId = item.status?.id;
this.description = item.description;
this.tags = item.descriptionTags?.filter(x => x.isActive === IsActive.Active).map(x => x.tag?.label);
this.properties = new DescriptionPropertyDefinitionEditorModel(this.validationErrorModel).fromModel(item.properties, descriptionTemplate, item.descriptionReferences);
@ -53,7 +53,7 @@ export class DescriptionEditorModel extends BaseEditorModel implements Descripti
planId: [{ value: this.planId, disabled: disabled }, context.getValidation('planId').validators],
planDescriptionTemplateId: [{ value: this.planDescriptionTemplateId, disabled: disabled }, context.getValidation('planDescriptionTemplateId').validators],
descriptionTemplateId: [{ value: this.descriptionTemplateId, disabled: disabled }, context.getValidation('descriptionTemplateId').validators],
status: [{ value: this.status, disabled: disabled }, context.getValidation('status').validators],
statusId: [{ value: this.statusId, disabled: disabled }, context.getValidation('statusId').validators],
description: [{ value: this.description, disabled: disabled }, context.getValidation('description').validators],
tags: [{ value: this.tags, disabled: disabled }, context.getValidation('tags').validators],
properties: this.buildProperties(visibilityRulesService),
@ -76,7 +76,7 @@ export class DescriptionEditorModel extends BaseEditorModel implements Descripti
baseValidationArray.push({ key: 'planId', validators: [CustomValidators.required(), BackendErrorValidator(this.validationErrorModel, 'planId')] });
baseValidationArray.push({ key: 'planDescriptionTemplateId', validators: [CustomValidators.required(), BackendErrorValidator(this.validationErrorModel, 'planDescriptionTemplateId')] });
baseValidationArray.push({ key: 'descriptionTemplateId', validators: [CustomValidators.required(), BackendErrorValidator(this.validationErrorModel, 'descriptionTemplateId')] });
baseValidationArray.push({ key: 'status', validators: [CustomValidators.required(), BackendErrorValidator(this.validationErrorModel, 'status')] });
baseValidationArray.push({ key: 'statusId', validators: [BackendErrorValidator(this.validationErrorModel, 'statusId')] });
baseValidationArray.push({ key: 'description', validators: [BackendErrorValidator(this.validationErrorModel, 'description')] });
baseValidationArray.push({ key: 'tags', validators: [BackendErrorValidator(this.validationErrorModel, 'tags')] });
baseValidationArray.push({ key: 'hash', validators: [] });

View File

@ -1,8 +1,8 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { DescriptionStatusEnum } from '@app/core/common/enum/description-status';
import { IsActive } from '@app/core/common/enum/is-active.enum';
import { AppPermission } from '@app/core/common/enum/permission.enum';
import { DescriptionStatus } from '@app/core/model/description-status/description-status';
import { DescriptionTemplate } from '@app/core/model/description-template/description-template';
import { Description, DescriptionExternalIdentifier, DescriptionField, DescriptionPropertyDefinition, DescriptionPropertyDefinitionFieldSet, DescriptionPropertyDefinitionFieldSetItem, DescriptionReference, DescriptionReferenceData, DescriptionTag } from '@app/core/model/description/description';
import { DescriptionTemplatesInSection, PlanBlueprint, PlanBlueprintDefinition, PlanBlueprintDefinitionSection } from '@app/core/model/plan-blueprint/plan-blueprint';
@ -52,9 +52,10 @@ export class DescriptionEditorEntityResolver extends BaseEditorResolver {
...BaseEditorResolver.lookupFields(),
nameof<Description>(x => x.id),
nameof<Description>(x => x.label),
nameof<Description>(x => x.status),
nameof<Description>(x => x.description),
nameof<Description>(x => x.status),
[nameof<Description>(x => x.status), nameof<DescriptionStatus>(x => x.id)].join('.'),
[nameof<Description>(x => x.status), nameof<DescriptionStatus>(x => x.name)].join('.'),
[nameof<Description>(x => x.status), nameof<DescriptionStatus>(x => x.internalStatus)].join('.'),
[nameof<Description>(x => x.authorizationFlags), AppPermission.EditDescription].join('.'),
[nameof<Description>(x => x.authorizationFlags), AppPermission.DeleteDescription].join('.'),
@ -203,7 +204,7 @@ export class DescriptionEditorEntityResolver extends BaseEditorResolver {
description.hash = null;
description.isActive = IsActive.Active;
description.belongsToCurrentTenant = true;
description.status = DescriptionStatusEnum.Draft;
description.status = null;
description.plan = plan;
description.planDescriptionTemplate = {
id: plan.planDescriptionTemplates.filter(x => x.sectionId == Guid.parse(planSectionId) && x.descriptionTemplateGroupId == description.descriptionTemplate.groupId)[0].id,

View File

@ -43,6 +43,7 @@ import { InterceptorType } from '@common/http/interceptors/interceptor-type';
import { PrincipalService } from '@app/core/services/http/principal.service';
import { DescriptionListingFilters } from './filtering/description-filter.component';
import { MatSelectChange } from '@angular/material/select';
import { DescriptionStatus } from '@app/core/model/description-status/description-status';
@Component({
selector: 'app-description-listing-component',
@ -490,7 +491,9 @@ export class DescriptionListingComponent extends BaseListingComponent<BaseDescri
nameof<Description>(x => x.id),
nameof<Description>(x => x.tenantId),
nameof<Description>(x => x.label),
nameof<Description>(x => x.status),
[nameof<Description>(x => x.status), nameof<DescriptionStatus>(x => x.id)].join('.'),
[nameof<Description>(x => x.status), nameof<DescriptionStatus>(x => x.name)].join('.'),
[nameof<Description>(x => x.status), nameof<DescriptionStatus>(x => x.internalStatus)].join('.'),
nameof<Description>(x => x.updatedAt),
nameof<Description>(x => x.belongsToCurrentTenant),
nameof<Description>(x => x.finalizedAt),

View File

@ -13,15 +13,18 @@
</div>
</div>
</div>
<div *ngIf="description.status === descriptionStatusEnum.Finalized" class="col-auto description-title">{{description.label}}</div>
<div *ngIf="description.status === descriptionStatusEnum.Draft" class="col-auto description-title-draft">{{description.label}}</div>
<div *ngIf="description.status.internalStatus === descriptionStatusEnum.Finalized" class="col-auto description-title">{{description.label}}</div>
<div *ngIf="description.status.internalStatus === descriptionStatusEnum.Draft" class="col-auto description-title-draft">{{description.label}}</div>
<div *ngIf="description.status.internalStatus === descriptionStatusEnum.Canceled" class="col-auto description-title-draft">{{description.label}}</div>
<div *ngIf="description.status.internalStatus === null" class="col-auto description-title-draft">{{description.label}}</div>
<div class="description-subtitle">
<span *ngIf="isUserPlanRelated()" class="col-auto">{{ enumUtils.toPlanUserRolesString(planService.getCurrentUserRolesInPlan(description?.plan?.planUsers)) }}</span>
<span *ngIf="isUserPlanRelated()">.</span>
<span class="col-auto" *ngIf="description.status === descriptionStatusEnum.Finalized && description.plan.accessType === planAccessTypeEnum.Public"><span class="material-icons icon-align">public</span>{{'DESCRIPTION-LISTING.STATES.PUBLIC' | translate}}</span>
<span *ngIf="description.status === descriptionStatusEnum.Finalized && description.plan.accessType != planAccessTypeEnum.Public; else draft" class="col-auto"><span class="material-icons icon-align">done</span>{{ enumUtils.toDescriptionStatusString(description.status) }}</span>
<ng-template #draft><span *ngIf="description.status === descriptionStatusEnum.Draft && canEditDescription(); else preview" class=" col-auto draft"><span class="material-icons icon-align">create</span>{{ enumUtils.toDescriptionStatusString(description.status) }}</span></ng-template>
<ng-template #preview><span *ngIf="description.status === descriptionStatusEnum.Draft && !canEditDescription()" class=" col-auto draft"><span class="material-icons-outlined mr-1 icon-align">visibility</span>{{ enumUtils.toDescriptionStatusString(description.status) }}</span></ng-template>
<span class="col-auto" *ngIf="description.status.internalStatus === descriptionStatusEnum.Finalized && description.plan.accessType === planAccessTypeEnum.Public"><span class="material-icons icon-align">public</span>{{'DESCRIPTION-LISTING.STATES.PUBLIC' | translate}}</span>
<span *ngIf="description.status.internalStatus === descriptionStatusEnum.Finalized && description.plan.accessType != planAccessTypeEnum.Public; else draft" class="col-auto"><span class="material-icons icon-align">done</span>{{ description.status.name }}</span>
<ng-template #draft><span *ngIf="description.status.internalStatus === descriptionStatusEnum.Draft && canEditDescription(); else preview" class=" col-auto draft"><span class="material-icons icon-align">create</span>{{ description.status.name }}</span></ng-template>
<ng-template #preview><span *ngIf="description.status.internalStatus === descriptionStatusEnum.Draft && !canEditDescription(); else otherStatus" class=" col-auto draft"><span class="material-icons-outlined mr-1 icon-align">visibility</span>{{ description.status.name }}</span></ng-template>
<ng-template #otherStatus><span class=" col-auto draft">{{ description.status.name }}</span></ng-template>
<span>.</span>
<span class="col">{{'DESCRIPTION-LISTING.GRANT' | translate}}: {{referenceService.getReferencesForTypesFirstSafe(description?.plan?.planReferences, [this.referenceTypeService.getGrantReferenceType()])?.reference?.label}}</span>
</div>

View File

@ -82,7 +82,7 @@ export class DescriptionListingItemComponent extends BaseComponent implements On
this.analyticsService.trackPageView(AnalyticsService.DescriptionListingItem);
if (this.description.isActive === IsActive.Inactive) {
this.isDeleted = true;
} else if (this.description.status === DescriptionStatusEnum.Draft) {
} else if (this.description?.status?.internalStatus === DescriptionStatusEnum.Draft) {
this.isDraft = true;
this.isDeleted = false;
} else {

View File

@ -46,7 +46,7 @@
</div>
<div class="row mb-4 pb-3">
<div class="col-auto pr-0">
@if(isActive && (canEdit || canAnnotate) && isDraftDescription(description) && !isLocked){
@if(isActive && (canEdit || canAnnotate) && isNotFinalizedDescription(description) && !isLocked){
<button (click)="editClicked(description)" mat-mini-fab class="mr-3 actions-btn" matTooltip="{{'DESCRIPTION-OVERVIEW.ACTIONS.EDIT' | translate}}" matTooltipPosition="above">
<mat-icon class="mat-mini-fab-icon">create</mat-icon>
</button>
@ -134,15 +134,19 @@
<div class="row">
<div class="col-12">
<div class="frame mb-3 pt-4 pl-4 pr-5 pb-3">
<ng-container *ngIf="canFinalize && isDraftDescription(description) && !isLocked">
<div class="row align-items-center" (click)="finalize(description)">
<ng-container *ngIf="availableStatusesTransitions && availableStatusesTransitions.length > 0 && !isLocked">
<div *ngFor='let status of availableStatusesTransitions'>
<div class="row align-items-center" (click)="persistStatus(status, description)">
<div class="col-auto pr-0">
<button mat-mini-fab class="finalize-btn">
<button *ngIf="status.internalStatus === descriptionStatusEnum.Finalized && description.status?.internalStatus != descriptionStatusEnum.Finalized" mat-mini-fab class="finalize-btn">
<mat-icon class="mat-mini-fab-icon check-icon">check</mat-icon>
</button>
<button *ngIf="description.status?.internalStatus === descriptionStatusEnum.Finalized" mat-mini-fab class="frame-btn">
<mat-icon class="mat-mini-fab-icon">unarchive</mat-icon>
</button>
</div>
<div class="col-auto pl-0">
<p class="mb-0 pl-2 frame-txt">{{ 'DESCRIPTION-OVERVIEW.ACTIONS.FINALIZE' | translate }}</p>
<p class="mb-0 pl-2 frame-txt">{{ status.name }}</p>
</div>
</div>
<div class="row align-items-center">
@ -150,17 +154,6 @@
<hr class="hr-line">
</div>
</div>
</ng-container>
<ng-container *ngIf="hasReversableStatus(description)">
<div class="row mb-3 align-items-center" (click)="reverseFinalization(description)">
<div class="col-auto pr-0">
<button mat-mini-fab class="frame-btn">
<mat-icon class="mat-mini-fab-icon">unarchive</mat-icon>
</button>
</div>
<div class="col-auto pl-0">
<p class="mb-0 mr-0 pl-2 frame-txt">{{ 'DESCRIPTION-OVERVIEW.ACTIONS.REVERSE' | translate }}</p>
</div>
</div>
</ng-container>
<ng-container *ngIf="fileTransformerService.availableFormatsFor(fileTransformerEntityTypeEnum.Description).length > 0">

View File

@ -44,6 +44,8 @@ import { map, takeUntil } from 'rxjs/operators';
import { nameof } from 'ts-simple-nameof';
import { DescriptionCopyDialogComponent } from '../description-copy-dialog/description-copy-dialog.component';
import { RouterUtilsService } from '@app/core/services/router/router-utils.service';
import { DescriptionStatus } from '@app/core/model/description-status/description-status';
import { DescriptionStatusService } from '@app/core/services/description-status/description-status.service';
@Component({
@ -73,6 +75,7 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
canFinalize = false;
canAnnotate = false;
canInvitePlanUsers = false;
availableStatusesTransitions: DescriptionStatus[];
canAssignPlanUsers(): boolean {
const authorizationFlags = !this.isPublicView ? (this.description?.plan as Plan)?.authorizationFlags : [];
return (authorizationFlags?.some(x => x === AppPermission.AssignPlanUsers) || this.authentication.hasPermission(AppPermission.AssignPlanUsers)) &&
@ -106,6 +109,7 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
private breadcrumbService: BreadcrumbService,
private httpErrorHandlingService: HttpErrorHandlingService,
private userService: UserService,
private descriptionStatusService: DescriptionStatusService
) {
super();
}
@ -135,6 +139,7 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
this.breadcrumbService.addIdResolvedValue(data.id.toString(), data.label);
this.description = data;
this.getAvailableStatuses(this.description.id);
this.description.plan.planUsers = this.isActive ? data.plan.planUsers.filter(x => x.isActive === IsActive.Active) : data.plan.planUsers;
this.researchers = this.referenceService.getReferencesForTypes(this.description?.plan?.planReferences, [this.referenceTypeService.getResearcherReferenceType()]);
this.checkLockStatus(this.description.id);
@ -216,6 +221,19 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
return this.language.instant('DESCRIPTION-OVERVIEW.INFOS.UNAUTHORIZED-ORCID');
}
getAvailableStatuses(id: Guid){
this.descriptionStatusService.getAvailableTransitions(id).pipe(takeUntil(this._destroyed))
.subscribe(
(statuses) => {
this.availableStatusesTransitions = statuses;
},
(error) => this.httpErrorHandlingService.handleBackedRequestError(error)
); }
hasAvailableFinalizeStatus() {
return this.availableStatusesTransitions?.find(x => x.internalStatus === DescriptionStatusEnum.Finalized) != null;
}
checkLockStatus(id: Guid) {
this.lockService.checkLockStatus(id).pipe(takeUntil(this._destroyed))
.subscribe({
@ -290,8 +308,8 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
return this.authentication.currentAccountIsAuthenticated();
}
isDraftDescription(description: Description) {
return description.status == DescriptionStatusEnum.Draft;
isNotFinalizedDescription(description: Description) {
return description?.status?.internalStatus != DescriptionStatusEnum.Finalized;
}
editClicked(description: Description) {
@ -426,7 +444,30 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
});
}
finalize(description: Description) {
persistStatus(status: DescriptionStatus, description: Description) {
if (status.internalStatus != null && status.internalStatus === DescriptionStatusEnum.Finalized) {
this.finalize(description, status.id);
} else if (status.internalStatus != null && description.status.internalStatus === DescriptionStatusEnum.Finalized){
this.reverseFinalization(description, status.id);
} else {
// other statuses
const descriptionStatusPersist: DescriptionStatusPersist = {
id: description.id,
statusId: status.id,
hash: description.hash
};
this.descriptionService.persistStatus(descriptionStatusPersist).pipe(takeUntil(this._destroyed))
.subscribe({
next: () => {
this.reloadPage();
this.onUpdateCallbackSuccess()
},
error: (error: any) => this.onUpdateCallbackError(error)
})
}
}
finalize(description: Description, statusId: Guid) {
this.descriptionService.validate([description.id]).pipe(takeUntil(this._destroyed))
.subscribe({
@ -448,7 +489,7 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
if (result) {
const descriptionStatusPersist: DescriptionStatusPersist = {
id: description.id,
status: DescriptionStatusEnum.Finalized,
statusId: statusId,
hash: description.hash
};
this.descriptionService.persistStatus(descriptionStatusPersist).pipe(takeUntil(this._destroyed))
@ -472,10 +513,10 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
}
hasReversableStatus(description: Description): boolean {
return description.plan.status == PlanStatusEnum.Draft && description.status == DescriptionStatusEnum.Finalized && this.canFinalize
return description.plan.status == PlanStatusEnum.Draft && description?.status?.internalStatus == DescriptionStatusEnum.Finalized && this.canFinalize && this.availableStatusesTransitions?.find(x => x.internalStatus === DescriptionStatusEnum.Draft) != null
}
reverseFinalization(description: Description) {
reverseFinalization(description: Description, statusId: Guid) {
const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
restoreFocus: false,
data: {
@ -489,7 +530,7 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
if (result) {
const planUserRemovePersist: DescriptionStatusPersist = {
id: description.id,
status: DescriptionStatusEnum.Draft,
statusId: statusId,
hash: description.hash
};
this.descriptionService.persistStatus(planUserRemovePersist).pipe(takeUntil(this._destroyed))
@ -512,7 +553,9 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
nameof<Description>(x => x.id),
nameof<Description>(x => x.label),
nameof<Description>(x => x.description),
nameof<Description>(x => x.status),
[nameof<Description>(x => x.status), nameof<DescriptionStatus>(x => x.id)].join('.'),
[nameof<Description>(x => x.status), nameof<DescriptionStatus>(x => x.name)].join('.'),
[nameof<Description>(x => x.status), nameof<DescriptionStatus>(x => x.internalStatus)].join('.'),
nameof<Description>(x => x.updatedAt),
nameof<Description>(x => x.belongsToCurrentTenant),
nameof<Description>(x => x.hash),

View File

@ -44,6 +44,7 @@ import { PlanFilterDialogComponent } from './filtering/plan-filter-dialog/plan-f
import { PlanListingFilters } from './filtering/plan-filter.component';
import { Lookup } from '@common/model/lookup';
import { MatSelectChange } from '@angular/material/select';
import { DescriptionStatus } from '@app/core/model/description-status/description-status';
@Component({
selector: 'app-plan-listing-component',
@ -513,10 +514,11 @@ export class PlanListingComponent extends BaseListingComponent<BasePlan, PlanLoo
[nameof<Plan>(x => x.descriptions), nameof<Description>(x => x.id)].join('.'),
[nameof<Plan>(x => x.descriptions), nameof<Description>(x => x.label)].join('.'),
[nameof<Plan>(x => x.descriptions), nameof<Description>(x => x.status)].join('.'),
[nameof<Plan>(x => x.descriptions), nameof<Description>(x => x.descriptionTemplate), nameof<DescriptionTemplate>(x => x.groupId)].join('.'),
[nameof<Plan>(x => x.descriptions), nameof<Description>(x => x.planDescriptionTemplate), nameof<PlanDescriptionTemplate>(x => x.sectionId)].join('.'),
[nameof<Plan>(x => x.descriptions), nameof<Description>(x => x.isActive)].join('.'),
[nameof<Plan>(x => x.descriptions), nameof<Description>(x => x.status), nameof<DescriptionStatus>(x => x.id)].join('.'),
[nameof<Plan>(x => x.descriptions), nameof<Description>(x => x.status), nameof<DescriptionStatus>(x => x.name)].join('.'),
[nameof<Plan>(x => x.descriptions), nameof<Description>(x => x.status), nameof<DescriptionStatus>(x => x.internalStatus)].join('.'), [nameof<Plan>(x => x.descriptions), nameof<Description>(x => x.descriptionTemplate), nameof<DescriptionTemplate>(x => x.groupId)].join('.'),
[nameof<Plan>(x => x.descriptions), nameof<Plan>(x => x.descriptions), nameof<Description>(x => x.planDescriptionTemplate), nameof<PlanDescriptionTemplate>(x => x.sectionId)].join('.'),
[nameof<Plan>(x => x.descriptions), nameof<Plan>(x => x.descriptions), nameof<Description>(x => x.isActive)].join('.'),
[nameof<Plan>(x => x.blueprint), nameof<PlanBlueprint>(x => x.id)].join('.'),
[nameof<Plan>(x => x.blueprint), nameof<PlanBlueprint>(x => x.label)].join('.'),

View File

@ -50,6 +50,7 @@ import { PlanFinalizeDialogComponent, PlanFinalizeDialogOutput } from '../plan-f
import { PlanInvitationDialogComponent } from '../invitation/dialog/plan-invitation-dialog.component';
import { NewVersionPlanDialogComponent } from '../new-version-dialog/plan-new-version-dialog.component';
import { RouterUtilsService } from '@app/core/services/router/router-utils.service';
import { DescriptionStatus } from '@app/core/model/description-status/description-status';
@Component({
selector: 'app-plan-overview',
@ -133,9 +134,9 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
this.plan.otherPlanVersions = data.otherPlanVersions?.filter(x => x.isActive === IsActive.Active) || null;
if (this.plan.descriptions) {
if (this.plan.status == PlanStatusEnum.Finalized) {
this.plan.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status === DescriptionStatusEnum.Finalized);
this.plan.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status?.internalStatus === DescriptionStatusEnum.Finalized);
} else {
this.plan.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status !== DescriptionStatusEnum.Canceled);
this.plan.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status?.internalStatus !== DescriptionStatusEnum.Canceled);
}
}
if (data.entityDois && data.entityDois.length > 0) this.plan.entityDois = data.entityDois.filter(x => x.isActive === IsActive.Active);
@ -660,7 +661,9 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
[nameof<Plan>(x => x.entityDois), nameof<EntityDoi>(x => x.isActive)].join('.'),
[nameof<Plan>(x => x.descriptions), nameof<Description>(x => x.id)].join('.'),
[nameof<Plan>(x => x.descriptions), nameof<Description>(x => x.label)].join('.'),
[nameof<Plan>(x => x.descriptions), nameof<Description>(x => x.status)].join('.'),
[nameof<Plan>(x => x.descriptions), nameof<Description>(x => x.status), nameof<DescriptionStatus>(x => x.id)].join('.'),
[nameof<Plan>(x => x.descriptions), nameof<Description>(x => x.status), nameof<DescriptionStatus>(x => x.name)].join('.'),
[nameof<Plan>(x => x.descriptions), nameof<Description>(x => x.status), nameof<DescriptionStatus>(x => x.internalStatus)].join('.'),
[nameof<Plan>(x => x.descriptions), nameof<Description>(x => x.isActive)].join('.'),
[nameof<Plan>(x => x.planUsers), nameof<PlanUser>(x => x.id)].join('.'),
[nameof<Plan>(x => x.planUsers), nameof<PlanUser>(x => x.sectionId)].join('.'),

View File

@ -263,9 +263,9 @@ export class PlanEditorComponent extends BaseEditor<PlanEditorModel, Plan> imple
if (data) {
if (data.descriptions) {
if (data.status == PlanStatusEnum.Finalized) {
data.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status === DescriptionStatusEnum.Finalized);
data.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status.internalStatus === DescriptionStatusEnum.Finalized);
} else {
data.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status !== DescriptionStatusEnum.Canceled);
data.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status.internalStatus !== DescriptionStatusEnum.Canceled);
}
}
if (data.planDescriptionTemplates) {

View File

@ -15,6 +15,7 @@ import { Guid } from '@common/types/guid';
import { takeUntil, tap } from 'rxjs/operators';
import { nameof } from 'ts-simple-nameof';
import { EntityDoi } from '@app/core/model/entity-doi/entity-doi';
import { DescriptionStatus } from '@app/core/model/description-status/description-status';
@Injectable()
export class PlanEditorEntityResolver extends BaseEditorResolver {
@ -56,7 +57,9 @@ export class PlanEditorEntityResolver extends BaseEditorResolver {
[nameof<Plan>(x => x.descriptions), nameof<Description>(x => x.id)].join('.'),
[nameof<Plan>(x => x.descriptions), nameof<Description>(x => x.label)].join('.'),
[nameof<Plan>(x => x.descriptions), nameof<Description>(x => x.status)].join('.'),
[nameof<Plan>(x => x.descriptions), nameof<Description>(x => x.status), nameof<DescriptionStatus>(x => x.id)].join('.'),
[nameof<Plan>(x => x.descriptions), nameof<Description>(x => x.status), nameof<DescriptionStatus>(x => x.name)].join('.'),
[nameof<Plan>(x => x.descriptions), nameof<Description>(x => x.status), nameof<DescriptionStatus>(x => x.internalStatus)].join('.'),
[nameof<Plan>(x => x.descriptions), nameof<Description>(x => x.isActive)].join('.'),
[nameof<Plan>(x => x.descriptions), nameof<Description>(x => x.planDescriptionTemplate), nameof<PlanDescriptionTemplate>(x => x.id)].join('.'),
[nameof<Plan>(x => x.descriptions), nameof<Description>(x => x.planDescriptionTemplate), nameof<PlanDescriptionTemplate>(x => x.sectionId)].join('.'),

View File

@ -30,10 +30,10 @@
</mat-expansion-panel-header>
<div *ngIf="plan.descriptions && plan.descriptions.length > 0">
<div *ngFor="let description of plan.descriptions" class="row pl-3 descriptions">
<mat-icon *ngIf="description.status == descriptionStatusEnum.Draft" class="col-1 draft-bookmark">bookmark</mat-icon>
<mat-icon *ngIf="description.status == descriptionStatusEnum.Finalized" class="col-1 finalized-bookmark">bookmark</mat-icon>
<h4 *ngIf="description.status == descriptionStatusEnum.Draft" class="col-11 ml-auto mt-1 mb-4">
<span>{{ 'TYPES.DESCRIPTION-STATUS.DRAFT' | translate }}
<mat-icon *ngIf="description?.status?.internalStatus == descriptionStatusEnum.Draft" class="col-1 draft-bookmark">bookmark</mat-icon>
<mat-icon *ngIf="description?.status?.internalStatus == descriptionStatusEnum.Finalized" class="col-1 finalized-bookmark">bookmark</mat-icon>
<h4 *ngIf="description.status?.internalStatus == descriptionStatusEnum.Draft" class="col-11 ml-auto mt-1 mb-4">
<span>{{ description?.status?.name}}
<ng-container *ngIf="!isDescriptionValid(description.id)">
({{'PLAN-FINALISE-DIALOG.INVALID' | translate}})
</ng-container>
@ -41,7 +41,7 @@
{{ description.label }}
<i *ngIf="(descriptionValidationOutputMap.get(description.id) != descriptionValidationOutputEnum.Invalid) && (descriptionValidationOutputMap.get(description.id) != descriptionValidationOutputEnum.Valid)" class="fa fa-spinner fa-spin" ></i>
</h4>
<h4 *ngIf="description.status == descriptionStatusEnum.Finalized" class="col-11 ml-auto mt-1 mb-4">{{ description.label }}</h4>
<h4 *ngIf="description.status.internalStatus == descriptionStatusEnum.Finalized" class="col-11 ml-auto mt-1 mb-4">{{ description.label }}</h4>
</div>
</div>
<div *ngIf="!plan.descriptions" class="emptyList">{{ 'PLAN-FINALISE-DIALOG.EMPTY' | translate }} </div>

View File

@ -74,7 +74,7 @@ export class PlanFinalizeDialogComponent extends BaseComponent implements OnInit
getFinalizedDescriptions() {
if (!this.plan.descriptions) return [];
const finalizedDescriptions = this.plan.descriptions.filter(x => x.status === DescriptionStatusEnum.Finalized);
const finalizedDescriptions = this.plan.descriptions.filter(x => x.status.internalStatus === DescriptionStatusEnum.Finalized);
if (finalizedDescriptions?.length > 0){
finalizedDescriptions.forEach(finalize => {
this.descriptionValidationOutputMap.set(finalize.id, DescriptionValidationOutput.Valid);
@ -88,16 +88,16 @@ export class PlanFinalizeDialogComponent extends BaseComponent implements OnInit
}
validateDescriptions(plan: Plan) {
if (!plan.descriptions?.some(x => x.status == DescriptionStatusEnum.Draft)) return;
if (!plan.descriptions?.some(x => x.status.internalStatus == DescriptionStatusEnum.Draft)) return;
const draftDescriptions = this.plan.descriptions.filter(x => x.status == DescriptionStatusEnum.Draft) || [];
const draftDescriptions = this.plan.descriptions.filter(x => x.status.internalStatus == DescriptionStatusEnum.Draft) || [];
if ( draftDescriptions.length > 0){
draftDescriptions.forEach(draft => {
this.descriptionValidationOutputMap.set(draft.id, DescriptionValidationOutput.Pending);
});
}
this.descriptionService.validate(plan.descriptions.filter(x => x.status == DescriptionStatusEnum.Draft).map(x => x.id)).pipe(takeUntil(this._destroyed),)
this.descriptionService.validate(plan.descriptions.filter(x => x.status.internalStatus == DescriptionStatusEnum.Draft).map(x => x.id)).pipe(takeUntil(this._destroyed),)
.subscribe(result => {
this.validationResults = result;
},