implement plan status logic (in progress)
This commit is contained in:
parent
a7a223d2c6
commit
21fd258181
|
@ -50,6 +50,7 @@ public class AuditableAction {
|
|||
public static final EventId Plan_GetPublicXml = new EventId(5017, "Plan_GetPublicXml");
|
||||
public static final EventId Plan_ExportPublic = new EventId(5018, "Plan_ExportPublic");
|
||||
public static final EventId Plan_PublicClone = new EventId(5019, "Plan_PublicClone");
|
||||
public static final EventId Plan_SetStatus = new EventId(5020, "Plan_SetStatus");
|
||||
|
||||
|
||||
public static final EventId Description_Query = new EventId(6000, "Description_Query");
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package org.opencdmp.model;
|
||||
|
||||
import org.opencdmp.commons.enums.PlanAccessType;
|
||||
import org.opencdmp.commons.enums.PlanStatus;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
@ -36,7 +35,7 @@ public class PublicPlan {
|
|||
|
||||
public static final String _publishedAt = "publishedAt";
|
||||
|
||||
private PlanStatus status;
|
||||
private PublicPlanStatus status;
|
||||
public static final String _status = "status";
|
||||
|
||||
private UUID groupId;
|
||||
|
@ -118,11 +117,11 @@ public class PublicPlan {
|
|||
this.publishedAt = publishedAt;
|
||||
}
|
||||
|
||||
public PlanStatus getStatus() {
|
||||
public PublicPlanStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(PlanStatus status) {
|
||||
public void setStatus(PublicPlanStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
package org.opencdmp.model;
|
||||
|
||||
|
||||
import org.opencdmp.commons.enums.PlanStatus;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class PublicPlanStatus {
|
||||
|
||||
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 PlanStatus 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 PlanStatus getInternalStatus() {
|
||||
return internalStatus;
|
||||
}
|
||||
|
||||
public void setInternalStatus(PlanStatus internalStatus) {
|
||||
this.internalStatus = internalStatus;
|
||||
}
|
||||
}
|
|
@ -186,7 +186,7 @@ public class PublicDescriptionBuilder extends BaseBuilder<PublicDescription, Des
|
|||
Map<UUID, PublicDescriptionStatus> itemMap;
|
||||
if (!fields.hasOtherField(this.asIndexer(PublicDescriptionStatus._id))) {
|
||||
itemMap = this.asEmpty(
|
||||
data.stream().map(DescriptionEntity::getDescriptionTemplateId).distinct().collect(Collectors.toList()),
|
||||
data.stream().map(DescriptionEntity::getStatusId).distinct().collect(Collectors.toList()),
|
||||
x -> {
|
||||
PublicDescriptionStatus item = new PublicDescriptionStatus();
|
||||
item.setId(x);
|
||||
|
|
|
@ -71,6 +71,9 @@ public class PublicPlanBuilder extends BaseBuilder<PublicPlan, PlanEntity> {
|
|||
FieldSet otherPlanVersionsFields = fields.extractPrefixed(this.asPrefix(PublicPlan._otherPlanVersions));
|
||||
Map<UUID, List<PublicPlan>> otherPlanVersionsMap = this.collectOtherPlanVersions(otherPlanVersionsFields, data);
|
||||
|
||||
FieldSet planStatusFields = fields.extractPrefixed(this.asPrefix(PublicPlan._status));
|
||||
Map<UUID, PublicPlanStatus> planStatusItemsMap = this.collectPlanStatuses(planStatusFields, data);
|
||||
|
||||
for (PlanEntity d : data) {
|
||||
PublicPlan m = new PublicPlan();
|
||||
if (fields.hasField(this.asIndexer(PublicPlan._id))) m.setId(d.getId());
|
||||
|
@ -80,7 +83,7 @@ public class PublicPlanBuilder extends BaseBuilder<PublicPlan, PlanEntity> {
|
|||
if (fields.hasField(this.asIndexer(PublicPlan._finalizedAt))) m.setFinalizedAt(d.getFinalizedAt());
|
||||
if (fields.hasField(this.asIndexer(PublicPlan._updatedAt))) m.setUpdatedAt(d.getUpdatedAt());
|
||||
if (fields.hasField(this.asIndexer(PublicPlan._accessType))) m.setAccessType(d.getAccessType());
|
||||
if (fields.hasField(this.asIndexer(PublicPlan._status))) m.setStatus(d.getStatus());
|
||||
if (!planStatusFields.isEmpty() && planStatusItemsMap != null && planStatusItemsMap.containsKey(d.getStatusId())) m.setStatus(planStatusItemsMap.get(d.getStatusId()));
|
||||
if (fields.hasField(this.asIndexer(PublicPlan._groupId))) m.setGroupId(d.getGroupId());
|
||||
if (fields.hasField(this.asIndexer(PublicPlan._accessType))) m.setAccessType(d.getAccessType());
|
||||
|
||||
|
@ -190,4 +193,34 @@ public class PublicPlanBuilder extends BaseBuilder<PublicPlan, PlanEntity> {
|
|||
return itemMap;
|
||||
}
|
||||
|
||||
private Map<UUID, PublicPlanStatus> collectPlanStatuses(FieldSet fields, List<PlanEntity> data) throws MyApplicationException {
|
||||
if (fields.isEmpty() || data.isEmpty())
|
||||
return null;
|
||||
this.logger.debug("checking related - {}", PublicPlanStatus.class.getSimpleName());
|
||||
|
||||
Map<UUID, PublicPlanStatus> itemMap;
|
||||
if (!fields.hasOtherField(this.asIndexer(PublicPlanStatus._id))) {
|
||||
itemMap = this.asEmpty(
|
||||
data.stream().map(PlanEntity::getStatusId).distinct().collect(Collectors.toList()),
|
||||
x -> {
|
||||
PublicPlanStatus item = new PublicPlanStatus();
|
||||
item.setId(x);
|
||||
return item;
|
||||
},
|
||||
PublicPlanStatus::getId);
|
||||
} else {
|
||||
FieldSet clone = new BaseFieldSet(fields.getFields()).ensure(PublicPlanStatus._id);
|
||||
PlanStatusQuery q = this.queryFactory.query(PlanStatusQuery.class).disableTracking().authorize(this.authorize).ids(data.stream().map(PlanEntity::getStatusId).distinct().collect(Collectors.toList()));
|
||||
itemMap = this.builderFactory.builder(PublicPlanStatusBuilder.class).authorize(this.authorize).asForeignKey(q, clone, PublicPlanStatus::getId);
|
||||
}
|
||||
if (!fields.hasField(PublicPlanStatus._id)) {
|
||||
itemMap.forEach((id, item) -> {
|
||||
if (item != null)
|
||||
item.setId(null);
|
||||
});
|
||||
}
|
||||
|
||||
return itemMap;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
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.PlanStatusEntity;
|
||||
import org.opencdmp.model.PublicPlanStatus;
|
||||
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 PublicPlanStatusBuilder extends BaseBuilder<PublicPlanStatus, PlanStatusEntity> {
|
||||
|
||||
private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None);
|
||||
@Autowired
|
||||
public PublicPlanStatusBuilder(
|
||||
ConventionService conventionService) {
|
||||
super(conventionService, new LoggerService(LoggerFactory.getLogger(PublicPlanStatusBuilder.class)));
|
||||
}
|
||||
|
||||
public PublicPlanStatusBuilder authorize(EnumSet<AuthorizationFlags> values) {
|
||||
this.authorize = values;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PublicPlanStatus> build(FieldSet fields, List<PlanStatusEntity> 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<PublicPlanStatus> models = new ArrayList<>();
|
||||
for (PlanStatusEntity d : data) {
|
||||
PublicPlanStatus m = new PublicPlanStatus();
|
||||
if (fields.hasField(this.asIndexer(PublicPlanStatus._id))) m.setId(d.getId());
|
||||
if (fields.hasField(this.asIndexer(PublicPlanStatus._name))) m.setName(d.getName());
|
||||
if (fields.hasField(this.asIndexer(PublicPlanStatus._internalStatus))) m.setInternalStatus(d.getInternalStatus());
|
||||
models.add(m);
|
||||
}
|
||||
this.logger.debug("build {} items", Optional.of(models).map(List::size).orElse(0));
|
||||
return models;
|
||||
}
|
||||
}
|
|
@ -87,7 +87,7 @@ public class PlanBuilder extends BaseBuilder<Plan, PlanEntity> {
|
|||
|
||||
List<Plan> models = new ArrayList<>();
|
||||
|
||||
FieldSet statusFields = fields.extractPrefixed(this.asPrefix(Description._status));
|
||||
FieldSet statusFields = fields.extractPrefixed(this.asPrefix(Plan._status));
|
||||
Map<UUID, PlanStatus> statusItemsMap = this.collectPlanStatuses(statusFields, data);
|
||||
|
||||
FieldSet entityDoisFields = fields.extractPrefixed(this.asPrefix(Plan._entityDois));
|
||||
|
|
|
@ -243,7 +243,7 @@ public class PlanPersist {
|
|||
.must(() -> this.isDescriptionTemplateMultiplicityValid(finalPlanBlueprintEntity, item.getId()))
|
||||
.failOn(PlanPersist._descriptionTemplates).failWith(this.messageSource.getMessage("Validation.InvalidDescriptionTemplateMultiplicityOnPlan", new Object[]{PlanPersist._descriptionTemplates}, LocaleContextHolder.getLocale())),
|
||||
this.refSpec()
|
||||
.iff(() -> !this.isNull(item.getProperties()))
|
||||
.iff(() -> !this.isNull(item.getProperties()) && finalStatusEntity != null)
|
||||
.on(PlanPersist._properties)
|
||||
.over(item.getProperties())
|
||||
.using(() -> this.validatorFactory.validator(PlanPropertiesPersist.PlanPropertiesPersistValidator.class).setStatus(finalStatusEntity.getInternalStatus()).withDefinition(definition)),
|
||||
|
|
|
@ -97,6 +97,8 @@ public class Plan {
|
|||
private List<Plan> otherPlanVersions;
|
||||
public static final String _otherPlanVersions = "otherPlanVersions";
|
||||
|
||||
private List<PlanStatus> availableTransitions;
|
||||
public static final String _availableTransitions = "availableTransitions";
|
||||
|
||||
private Boolean belongsToCurrentTenant;
|
||||
public static final String _belongsToCurrentTenant = "belongsToCurrentTenant";
|
||||
|
@ -318,4 +320,12 @@ public class Plan {
|
|||
public void setOtherPlanVersions(List<Plan> otherPlanVersions) {
|
||||
this.otherPlanVersions = otherPlanVersions;
|
||||
}
|
||||
|
||||
public List<PlanStatus> getAvailableTransitions() {
|
||||
return availableTransitions;
|
||||
}
|
||||
|
||||
public void setAvailableTransitions(List<PlanStatus> availableTransitions) {
|
||||
this.availableTransitions = availableTransitions;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -425,6 +425,8 @@ public class PlanQuery extends QueryBase<PlanEntity> {
|
|||
return PlanEntity._version;
|
||||
else if (item.match(Plan._status))
|
||||
return PlanEntity._status;
|
||||
else if (item.prefix(Plan._status))
|
||||
return PlanEntity._statusId;
|
||||
else if (item.match(Plan._properties))
|
||||
return PlanEntity._properties;
|
||||
else if (item.prefix(Plan._properties))
|
||||
|
@ -475,6 +477,7 @@ public class PlanQuery extends QueryBase<PlanEntity> {
|
|||
item.setLabel(QueryBase.convertSafe(tuple, columns, PlanEntity._label, String.class));
|
||||
item.setVersion(QueryBase.convertSafe(tuple, columns, PlanEntity._version, Short.class));
|
||||
item.setStatus(QueryBase.convertSafe(tuple, columns, PlanEntity._status, PlanStatus.class));
|
||||
item.setStatusId(QueryBase.convertSafe(tuple, columns, PlanEntity._statusId, UUID.class));
|
||||
item.setVersionStatus(QueryBase.convertSafe(tuple, columns, PlanEntity._versionStatus, PlanVersionStatus.class));
|
||||
item.setProperties(QueryBase.convertSafe(tuple, columns, PlanEntity._properties, String.class));
|
||||
item.setGroupId(QueryBase.convertSafe(tuple, columns, PlanEntity._groupId, UUID.class));
|
||||
|
|
|
@ -266,8 +266,10 @@ public class DescriptionServiceImpl implements DescriptionService {
|
|||
|
||||
PlanEntity plan = this.entityManager.find(PlanEntity.class, data.getPlanId(), true);
|
||||
if (plan == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{data.getPlanId(), Plan.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||
PlanStatusEntity planStatusEntity = this.entityManager.find(PlanStatusEntity.class, plan.getStatusId(), true);
|
||||
if (planStatusEntity == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{plan.getStatusId(), PlanStatusEntity.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||
|
||||
if (plan.getStatus().equals(PlanStatus.Finalized) && isUpdate) throw new MyValidationException(this.errors.getPlanIsFinalized().getCode(), this.errors.getPlanIsFinalized().getMessage());
|
||||
if (planStatusEntity.getInternalStatus() != null && planStatusEntity.getInternalStatus().equals(PlanStatus.Finalized) && isUpdate) throw new MyValidationException(this.errors.getPlanIsFinalized().getCode(), this.errors.getPlanIsFinalized().getMessage());
|
||||
|
||||
data.setLabel(model.getLabel());
|
||||
data.setDescription(model.getDescription());
|
||||
|
@ -504,7 +506,9 @@ public class DescriptionServiceImpl implements DescriptionService {
|
|||
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());
|
||||
PlanStatusEntity planStatusEntity = this.entityManager.find(PlanStatusEntity.class, planEntity.getStatusId(), true);
|
||||
if (planStatusEntity == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{planEntity.getStatusId(), PlanStatusEntity.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||
if (planStatusEntity.getInternalStatus() != null && planStatusEntity.getInternalStatus().equals(PlanStatus.Finalized)) throw new MyValidationException(this.errors.getPlanIsFinalized().getCode(), this.errors.getPlanIsFinalized().getMessage());
|
||||
}
|
||||
|
||||
data.setStatusId(model.getStatusId());
|
||||
|
@ -533,11 +537,14 @@ public class DescriptionServiceImpl implements DescriptionService {
|
|||
return null;
|
||||
}
|
||||
|
||||
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) throw new MyApplicationException("finalized status not found");
|
||||
|
||||
for (DescriptionEntity description: descriptions) {
|
||||
DescriptionValidationResult descriptionValidationResult = new DescriptionValidationResult(description.getId(), DescriptionValidationOutput.Invalid);
|
||||
|
||||
DescriptionPersist.DescriptionPersistValidator validator = this.validatorFactory.validator(DescriptionPersist.DescriptionPersistValidator.class);
|
||||
validator.validate(this.buildDescriptionPersist(description));
|
||||
validator.validate(this.buildDescriptionPersist(description, statusEntity.getId()));
|
||||
if (validator.result().isValid()) descriptionValidationResult.setResult(DescriptionValidationOutput.Valid);
|
||||
|
||||
descriptionValidationResults.add(descriptionValidationResult);
|
||||
|
@ -1013,7 +1020,7 @@ public class DescriptionServiceImpl implements DescriptionService {
|
|||
|
||||
//region build persist
|
||||
|
||||
private @NotNull DescriptionPersist buildDescriptionPersist(DescriptionEntity data) throws InvalidApplicationException {
|
||||
private @NotNull DescriptionPersist buildDescriptionPersist(DescriptionEntity data, UUID statusId) throws InvalidApplicationException {
|
||||
DescriptionPersist persist = new DescriptionPersist();
|
||||
if (data == null) return persist;
|
||||
|
||||
|
@ -1022,8 +1029,7 @@ public class DescriptionServiceImpl implements DescriptionService {
|
|||
|
||||
persist.setId(data.getId());
|
||||
persist.setLabel(data.getLabel());
|
||||
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.setStatusId(statusId);
|
||||
persist.setDescription(data.getDescription());
|
||||
persist.setDescriptionTemplateId(data.getDescriptionTemplateId());
|
||||
persist.setPlanId(data.getPlanId());
|
||||
|
|
|
@ -34,9 +34,7 @@ public interface PlanService {
|
|||
|
||||
void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException, IOException;
|
||||
|
||||
void finalize(UUID id, List<UUID> descriptionIds) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, IOException;
|
||||
|
||||
void undoFinalize(UUID id, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, IOException;
|
||||
void setStatus(UUID id, UUID newStatusId, List<UUID> descriptionIds) throws InvalidApplicationException, IOException;
|
||||
|
||||
PlanValidationResult validate(UUID id) throws InvalidApplicationException;
|
||||
|
||||
|
|
|
@ -386,7 +386,10 @@ public class PlanServiceImpl implements PlanService {
|
|||
planQuery.setOrder(new Ordering().addDescending(Plan._version));
|
||||
previousPlan = planQuery.count() > 0 ? planQuery.collect().getFirst() : null;
|
||||
if (previousPlan != null){
|
||||
if (previousPlan.getStatus().equals(PlanStatus.Finalized)) previousPlan.setVersionStatus(PlanVersionStatus.Current);
|
||||
PlanStatusEntity previousPlanStatusEntity = this.entityManager.find(PlanStatusEntity.class, previousPlan.getStatusId(), true);
|
||||
if (previousPlanStatusEntity == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{previousPlan.getStatusId(), PlanStatusEntity.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||
|
||||
if (previousPlanStatusEntity.getInternalStatus() != null && previousPlanStatusEntity.getInternalStatus().equals(PlanStatus.Finalized)) previousPlan.setVersionStatus(PlanVersionStatus.Current);
|
||||
else previousPlan.setVersionStatus(PlanVersionStatus.NotFinalized);
|
||||
this.entityManager.merge(previousPlan);
|
||||
}
|
||||
|
@ -428,6 +431,9 @@ public class PlanServiceImpl implements PlanService {
|
|||
.count();
|
||||
if (notFinalizedCount > 0) throw new MyValidationException(this.errors.getPlanNewVersionAlreadyCreatedDraft().getCode(), this.errors.getPlanNewVersionAlreadyCreatedDraft().getMessage());
|
||||
|
||||
PlanStatusEntity startingPlanStatusEntity = this.entityManager.find(PlanStatusEntity.class, this.planWorkflowService.getWorkFlowDefinition().getStartingStatusId(), true);
|
||||
if (startingPlanStatusEntity == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{this.planWorkflowService.getWorkFlowDefinition().getStartingStatusId(), PlanStatusEntity.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||
|
||||
PlanEntity newPlan = new PlanEntity();
|
||||
newPlan.setId(UUID.randomUUID());
|
||||
newPlan.setIsActive(IsActive.Active);
|
||||
|
@ -440,6 +446,7 @@ public class PlanServiceImpl implements PlanService {
|
|||
newPlan.setLabel(model.getLabel());
|
||||
newPlan.setLanguage(oldPlanEntity.getLanguage());
|
||||
newPlan.setStatus(PlanStatus.Draft);
|
||||
newPlan.setStatusId(startingPlanStatusEntity.getId());
|
||||
newPlan.setProperties(oldPlanEntity.getProperties());
|
||||
newPlan.setBlueprintId(model.getBlueprintId());
|
||||
newPlan.setAccessType(oldPlanEntity.getAccessType());
|
||||
|
@ -591,7 +598,7 @@ public class PlanServiceImpl implements PlanService {
|
|||
|
||||
this.entityManager.flush();
|
||||
|
||||
this.updateVersionStatusAndSave(newPlan, PlanStatus.Draft, newPlan.getStatus());
|
||||
this.updateVersionStatusAndSave(newPlan, PlanStatus.Draft, startingPlanStatusEntity.getInternalStatus());
|
||||
|
||||
this.entityManager.flush();
|
||||
|
||||
|
@ -855,9 +862,11 @@ public class PlanServiceImpl implements PlanService {
|
|||
|
||||
|
||||
private void updateVersionStatusAndSave(PlanEntity data, PlanStatus previousStatus, PlanStatus newStatus) throws InvalidApplicationException {
|
||||
if (previousStatus.equals(newStatus))
|
||||
if (previousStatus == null && newStatus == null)
|
||||
return;
|
||||
if (previousStatus.equals(PlanStatus.Finalized) && newStatus.equals(PlanStatus.Draft)){
|
||||
if (previousStatus != null && previousStatus.equals(newStatus))
|
||||
return;
|
||||
if (previousStatus != null && previousStatus.equals(PlanStatus.Finalized) && (newStatus == null || newStatus.equals(PlanStatus.Draft))){
|
||||
boolean alreadyCreatedNewVersion = this.queryFactory.query(PlanQuery.class).disableTracking()
|
||||
.versionStatuses(PlanVersionStatus.NotFinalized, PlanVersionStatus.Current)
|
||||
.excludedIds(data.getId())
|
||||
|
@ -870,7 +879,7 @@ public class PlanServiceImpl implements PlanService {
|
|||
this.entityManager.merge(data);
|
||||
}
|
||||
|
||||
if (newStatus.equals(PlanStatus.Finalized)) {
|
||||
if (newStatus != null && newStatus.equals(PlanStatus.Finalized)) {
|
||||
List<PlanEntity> latestVersionPlans = this.queryFactory.query(PlanQuery.class)
|
||||
.versionStatuses(PlanVersionStatus.Current).excludedIds(data.getId())
|
||||
.isActive(IsActive.Active).groupIds(data.getGroupId()).collect();
|
||||
|
@ -915,6 +924,7 @@ public class PlanServiceImpl implements PlanService {
|
|||
newPlan.setLabel(model.getLabel());
|
||||
newPlan.setLanguage(existingPlanEntity.getLanguage());
|
||||
newPlan.setStatus(PlanStatus.Draft);
|
||||
newPlan.setStatusId(this.planWorkflowService.getWorkFlowDefinition().getStartingStatusId());
|
||||
newPlan.setProperties(existingPlanEntity.getProperties());
|
||||
newPlan.setBlueprintId(existingPlanEntity.getBlueprintId());
|
||||
newPlan.setAccessType(existingPlanEntity.getAccessType());
|
||||
|
@ -1103,6 +1113,7 @@ public class PlanServiceImpl implements PlanService {
|
|||
newPlan.setLabel(model.getLabel());
|
||||
newPlan.setLanguage(existingPlanEntity.getLanguage());
|
||||
newPlan.setStatus(PlanStatus.Draft);
|
||||
newPlan.setStatusId(this.planWorkflowService.getWorkFlowDefinition().getStartingStatusId());
|
||||
newPlan.setProperties(existingPlanEntity.getProperties());
|
||||
newPlan.setBlueprintId(blueprintEntityByTenant.getId());
|
||||
newPlan.setAccessType(existingPlanEntity.getAccessType());
|
||||
|
@ -1339,7 +1350,7 @@ public class PlanServiceImpl implements PlanService {
|
|||
data.setIsActive(IsActive.Active);
|
||||
data.setCreatedAt(Instant.now());
|
||||
}
|
||||
PlanStatus previousStatus = data.getStatus();
|
||||
// PlanStatus previousStatus = data.getStatus();
|
||||
|
||||
PlanBlueprintEntity planBlueprintEntity = this.entityManager.find(PlanBlueprintEntity.class, model.getBlueprint(), true);
|
||||
if (planBlueprintEntity == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{model.getBlueprint(), PlanBlueprint.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||
|
@ -1361,7 +1372,7 @@ public class PlanServiceImpl implements PlanService {
|
|||
|
||||
this.entityManager.flush();
|
||||
|
||||
this.updateVersionStatusAndSave(data, previousStatus, data.getStatus());
|
||||
// this.updateVersionStatusAndSave(data, previousStatus, data.getStatus());
|
||||
|
||||
this.entityManager.flush();
|
||||
|
||||
|
@ -1587,31 +1598,50 @@ public class PlanServiceImpl implements PlanService {
|
|||
return data;
|
||||
}
|
||||
|
||||
public void finalize(UUID id, List<UUID> descriptionIds) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, IOException {
|
||||
this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.planAffiliation(id)), Permission.FinalizePlan);
|
||||
public void setStatus(UUID id, UUID newStatusId, List<UUID> descriptionIds) throws InvalidApplicationException, IOException {
|
||||
PlanEntity plan = this.queryFactory.query(PlanQuery.class).authorize(AuthorizationFlags.AllExceptPublic).ids(id).isActive(IsActive.Active).first();
|
||||
if (plan == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{id, Plan.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||
if (plan.getStatusId().equals(newStatusId)) throw new MyApplicationException("Old status equals with new");
|
||||
|
||||
if (plan == null){
|
||||
throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{id, Plan.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||
PlanStatusEntity oldPlanStatusEntity = this.entityManager.find(PlanStatusEntity.class, plan.getStatusId(), true);
|
||||
if (oldPlanStatusEntity == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{plan.getStatusId(), PlanStatusEntity.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||
|
||||
PlanStatusEntity newPlanStatusEntity = this.entityManager.find(PlanStatusEntity.class, newStatusId, true);
|
||||
if (newPlanStatusEntity == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{newStatusId, PlanStatusEntity.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||
|
||||
if (oldPlanStatusEntity.getInternalStatus() != null && oldPlanStatusEntity.getInternalStatus().equals(PlanStatus.Finalized)) {
|
||||
this.undoFinalize(plan, oldPlanStatusEntity, newPlanStatusEntity);
|
||||
} else if (newPlanStatusEntity.getInternalStatus() != null && newPlanStatusEntity.getInternalStatus().equals(PlanStatus.Finalized)) {
|
||||
this.finalize(plan, descriptionIds ,oldPlanStatusEntity, newPlanStatusEntity);
|
||||
} else {
|
||||
plan.setStatusId(newPlanStatusEntity.getId());
|
||||
plan.setUpdatedAt(Instant.now());
|
||||
|
||||
this.entityManager.merge(plan);
|
||||
this.entityManager.flush();
|
||||
}
|
||||
}
|
||||
|
||||
if (plan.getStatus().equals(PlanStatus.Finalized)){
|
||||
private void finalize(PlanEntity plan, List<UUID> descriptionIds, PlanStatusEntity oldPlanStatusEntity, PlanStatusEntity newPlanStatusEntity) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, IOException {
|
||||
this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.planAffiliation(plan.getId())), Permission.FinalizePlan);
|
||||
|
||||
if (oldPlanStatusEntity.getInternalStatus() != null && oldPlanStatusEntity.getInternalStatus().equals(PlanStatus.Finalized)){
|
||||
throw new MyApplicationException("Plan is already finalized");
|
||||
}
|
||||
|
||||
if (this.validate(id).getResult().equals(PlanValidationOutput.Invalid)){
|
||||
if (this.validate(plan.getId()).getResult().equals(PlanValidationOutput.Invalid)){
|
||||
throw new MyApplicationException("Plan is invalid");
|
||||
}
|
||||
|
||||
List<DescriptionEntity> descriptions = this.queryFactory.query(DescriptionQuery.class)
|
||||
.authorize(AuthorizationFlags.AllExceptPublic).planIds(id).isActive(IsActive.Active).collect();
|
||||
.authorize(AuthorizationFlags.AllExceptPublic).planIds(plan.getId()).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 descriptionFinalizedStatusEntity = this.queryFactory.query(DescriptionStatusQuery.class).disableTracking().internalStatuses(DescriptionStatus.Finalized).isActive(IsActive.Active).firstAs(new BaseFieldSet().ensure(org.opencdmp.model.descriptionstatus.DescriptionStatus._id));
|
||||
if (descriptionFinalizedStatusEntity == 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");
|
||||
|
@ -1625,7 +1655,7 @@ public class PlanServiceImpl implements PlanService {
|
|||
if (this.descriptionService.validate(List.of(description.getId())).getFirst().getResult().equals(DescriptionValidationOutput.Invalid)){
|
||||
throw new MyApplicationException("Description is invalid");
|
||||
}
|
||||
if (finalizedStatusEntity != null) description.setStatusId(finalizedStatusEntity.getId());
|
||||
description.setStatusId(descriptionFinalizedStatusEntity.getId());
|
||||
description.setUpdatedAt(Instant.now());
|
||||
description.setFinalizedAt(Instant.now());
|
||||
this.entityManager.merge(description);
|
||||
|
@ -1636,13 +1666,12 @@ public class PlanServiceImpl implements PlanService {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
PlanStatus previousStatus = plan.getStatus();
|
||||
plan.setStatus(PlanStatus.Finalized);
|
||||
|
||||
plan.setStatusId(newPlanStatusEntity.getId());
|
||||
plan.setUpdatedAt(Instant.now());
|
||||
plan.setFinalizedAt(Instant.now());
|
||||
|
||||
this.updateVersionStatusAndSave(plan, previousStatus, plan.getStatus());
|
||||
this.updateVersionStatusAndSave(plan, oldPlanStatusEntity.getInternalStatus(), newPlanStatusEntity.getInternalStatus());
|
||||
plan.setVersionStatus(PlanVersionStatus.Current);
|
||||
|
||||
this.entityManager.merge(plan);
|
||||
|
@ -1654,24 +1683,21 @@ public class PlanServiceImpl implements PlanService {
|
|||
this.sendNotification(plan);
|
||||
}
|
||||
|
||||
public void undoFinalize(UUID id, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException {
|
||||
this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.planAffiliation(id)), Permission.UndoFinalizePlan);
|
||||
PlanEntity plan = this.queryFactory.query(PlanQuery.class).authorize(AuthorizationFlags.AllExceptPublic).ids(id).isActive(IsActive.Active).firstAs(fields);
|
||||
private void undoFinalize(PlanEntity plan, PlanStatusEntity oldPlanStatusEntity, PlanStatusEntity newPlanStatusEntity) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException {
|
||||
this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.planAffiliation(plan.getId())), Permission.UndoFinalizePlan);
|
||||
|
||||
if (plan == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{id, Plan.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||
|
||||
if (!plan.getStatus().equals(PlanStatus.Finalized)) throw new MyApplicationException("Plan is already drafted");
|
||||
if (oldPlanStatusEntity.getInternalStatus() == null && !oldPlanStatusEntity.getInternalStatus().equals(PlanStatus.Finalized)) throw new MyApplicationException("Plan is already non finalized");
|
||||
|
||||
EntityDoiQuery entityDoiQuery = this.queryFactory.query(EntityDoiQuery.class).types(EntityType.Plan).entityIds(plan.getId()).isActive(IsActive.Active);
|
||||
if (entityDoiQuery.count() > 0) throw new MyApplicationException("Plan is deposited");
|
||||
|
||||
plan.setStatus(PlanStatus.Draft);
|
||||
plan.setStatusId(newPlanStatusEntity.getId());
|
||||
plan.setUpdatedAt(Instant.now());
|
||||
|
||||
this.entityManager.merge(plan);
|
||||
this.entityManager.flush();
|
||||
|
||||
this.updateVersionStatusAndSave(plan, PlanStatus.Finalized, plan.getStatus());
|
||||
this.updateVersionStatusAndSave(plan, PlanStatus.Finalized, newPlanStatusEntity.getInternalStatus());
|
||||
this.entityManager.flush();
|
||||
|
||||
PlanQuery planQuery = this.queryFactory.query(PlanQuery.class).disableTracking()
|
||||
|
@ -1683,7 +1709,10 @@ public class PlanServiceImpl implements PlanService {
|
|||
planQuery.setOrder(new Ordering().addDescending(Plan._version));
|
||||
PlanEntity previousPlan = planQuery.count() > 0 ? planQuery.collect().getFirst() : null;
|
||||
if (previousPlan != null){
|
||||
if (previousPlan.getStatus().equals(PlanStatus.Finalized)) previousPlan.setVersionStatus(PlanVersionStatus.Current);
|
||||
PlanStatusEntity previousPlanStatusEntity = this.entityManager.find(PlanStatusEntity.class, previousPlan.getStatusId(), true);
|
||||
if (previousPlanStatusEntity == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{previousPlan.getStatusId(), PlanStatusEntity.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||
|
||||
if (previousPlanStatusEntity.getInternalStatus() != null && previousPlanStatusEntity.getInternalStatus().equals(PlanStatus.Finalized)) previousPlan.setVersionStatus(PlanVersionStatus.Current);
|
||||
else previousPlan.setVersionStatus(PlanVersionStatus.NotFinalized);
|
||||
this.entityManager.merge(previousPlan);
|
||||
}
|
||||
|
@ -1721,8 +1750,8 @@ public class PlanServiceImpl implements PlanService {
|
|||
persist.setId(data.getId());
|
||||
persist.setHash(data.getId().toString());
|
||||
persist.setLabel(data.getLabel());
|
||||
//TODO status PlanStatusEntity statusEntity = this.queryFactory.query(DescriptionStatusQuery.class).disableTracking().internalStatuses(DescriptionStatus.Finalized).isActive(IsActive.Active).firstAs(new BaseFieldSet().ensure(org.opencdmp.model.descriptionstatus.DescriptionStatus._id));
|
||||
// persist.setStatus(PlanStatus.Finalized);
|
||||
PlanStatusEntity statusEntity = this.queryFactory.query(PlanStatusQuery.class).disableTracking().internalStatuses(PlanStatus.Finalized).isActives(IsActive.Active).firstAs(new BaseFieldSet().ensure(org.opencdmp.model.planstatus.PlanStatus._id));
|
||||
if (statusEntity != null) persist.setStatusId(statusEntity.getId());
|
||||
persist.setDescription(data.getDescription());
|
||||
persist.setBlueprint(data.getBlueprintId());
|
||||
persist.setAccessType(data.getAccessType());
|
||||
|
@ -2316,7 +2345,6 @@ public class PlanServiceImpl implements PlanService {
|
|||
|
||||
PlanPersist persist = new PlanPersist();
|
||||
persist.setLabel(label);
|
||||
//TODO status persist.setStatus(PlanStatus.Draft);
|
||||
persist.setDescription(planXml.getDescription());
|
||||
persist.setAccessType(planXml.getAccess());
|
||||
persist.setLanguage(planXml.getLanguage());
|
||||
|
@ -2618,7 +2646,6 @@ public class PlanServiceImpl implements PlanService {
|
|||
PlanPersist persist = new PlanPersist();
|
||||
|
||||
persist.setLabel(planCommonModelConfig.getLabel());
|
||||
// TODO status persist.setStatus(PlanStatus.Draft);
|
||||
persist.setDescription(model.getDescription());
|
||||
switch (model.getAccessType()) {
|
||||
case Public -> persist.setAccessType(PlanAccessType.Public);
|
||||
|
|
|
@ -10,10 +10,13 @@ import org.opencdmp.model.persist.planstatus.PlanStatusPersist;
|
|||
import org.opencdmp.model.planstatus.PlanStatus;
|
||||
|
||||
import javax.management.InvalidApplicationException;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface PlanStatusService {
|
||||
PlanStatus persist(PlanStatusPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, JAXBException;
|
||||
|
||||
void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException;
|
||||
|
||||
List<PlanStatus> getAvailableTransitionStatuses(UUID planId) throws InvalidApplicationException;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package org.opencdmp.service.planstatus;
|
|||
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;
|
||||
|
@ -13,24 +14,31 @@ import gr.cite.tools.logging.LoggerService;
|
|||
import gr.cite.tools.logging.MapLogEntry;
|
||||
import jakarta.xml.bind.JAXBException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
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.planstatus.PlanStatusDefinitionAuthorizationEntity;
|
||||
import org.opencdmp.commons.types.planstatus.PlanStatusDefinitionAuthorizationItemEntity;
|
||||
import org.opencdmp.commons.types.planstatus.PlanStatusDefinitionEntity;
|
||||
import org.opencdmp.commons.types.planworkflow.PlanWorkflowDefinitionEntity;
|
||||
import org.opencdmp.commons.types.planworkflow.PlanWorkflowDefinitionTransitionEntity;
|
||||
import org.opencdmp.convention.ConventionService;
|
||||
import org.opencdmp.data.PlanEntity;
|
||||
import org.opencdmp.data.PlanStatusEntity;
|
||||
import org.opencdmp.data.TenantEntityManager;
|
||||
import org.opencdmp.errorcode.ErrorThesaurusProperties;
|
||||
import org.opencdmp.event.EventBroker;
|
||||
import org.opencdmp.model.builder.planstatus.PlanStatusBuilder;
|
||||
import org.opencdmp.model.deleter.PlanStatusDeleter;
|
||||
import org.opencdmp.model.descriptionstatus.DescriptionStatus;
|
||||
import org.opencdmp.model.persist.planstatus.PlanStatusDefinitionAuthorizationItemPersist;
|
||||
import org.opencdmp.model.persist.planstatus.PlanStatusDefinitionAuthorizationPersist;
|
||||
import org.opencdmp.model.persist.planstatus.PlanStatusDefinitionPersist;
|
||||
import org.opencdmp.model.persist.planstatus.PlanStatusPersist;
|
||||
import org.opencdmp.model.planstatus.PlanStatus;
|
||||
import org.opencdmp.query.PlanStatusQuery;
|
||||
import org.opencdmp.service.planworkflow.PlanWorkflowService;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
|
@ -38,8 +46,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 PlanStatusServiceImpl implements PlanStatusService {
|
||||
|
@ -55,8 +65,10 @@ public class PlanStatusServiceImpl implements PlanStatusService {
|
|||
private final TenantEntityManager entityManager;
|
||||
private final MessageSource messageSource;
|
||||
private final ErrorThesaurusProperties errors;
|
||||
private final QueryFactory queryFactory;
|
||||
private final PlanWorkflowService planWorkflowService;
|
||||
|
||||
public PlanStatusServiceImpl(BuilderFactory builderFactory, DeleterFactory deleterFactory, AuthorizationService authorizationService, ConventionService conventionService, XmlHandlingService xmlHandlingService, TenantEntityManager entityManager, MessageSource messageSource, ErrorThesaurusProperties errors, EventBroker eventBroker) {
|
||||
public PlanStatusServiceImpl(BuilderFactory builderFactory, DeleterFactory deleterFactory, AuthorizationService authorizationService, ConventionService conventionService, XmlHandlingService xmlHandlingService, TenantEntityManager entityManager, MessageSource messageSource, ErrorThesaurusProperties errors, EventBroker eventBroker, QueryFactory queryFactory, PlanWorkflowService planWorkflowService) {
|
||||
this.builderFactory = builderFactory;
|
||||
this.deleterFactory = deleterFactory;
|
||||
|
||||
|
@ -66,6 +78,8 @@ public class PlanStatusServiceImpl implements PlanStatusService {
|
|||
this.entityManager = entityManager;
|
||||
this.messageSource = messageSource;
|
||||
this.errors = errors;
|
||||
this.queryFactory = queryFactory;
|
||||
this.planWorkflowService = planWorkflowService;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -149,4 +163,20 @@ public class PlanStatusServiceImpl implements PlanStatusService {
|
|||
|
||||
return data;
|
||||
}
|
||||
|
||||
public List<PlanStatus> getAvailableTransitionStatuses(UUID planId) throws InvalidApplicationException {
|
||||
PlanWorkflowDefinitionEntity definition = this.planWorkflowService.getWorkFlowDefinition();
|
||||
|
||||
PlanEntity plan = this.entityManager.find(PlanEntity.class, planId);
|
||||
if (plan == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{planId, PlanEntity.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||
|
||||
List<PlanWorkflowDefinitionTransitionEntity> availableTransitions = definition.getStatusTransitions().stream().filter(x -> x.getFromStatusId().equals(plan.getStatusId())).collect(Collectors.toList());
|
||||
if (!this.conventionService.isListNullOrEmpty(availableTransitions)){
|
||||
PlanStatusQuery query = this.queryFactory.query(PlanStatusQuery.class).authorize(AuthorizationFlags.AllExceptPublic).isActives(IsActive.Active).ids(availableTransitions.stream().map(PlanWorkflowDefinitionTransitionEntity::getToStatusId).distinct().toList());
|
||||
FieldSet fieldSet = new BaseFieldSet().ensure(DescriptionStatus._id).ensure(DescriptionStatus._name).ensure(DescriptionStatus._internalStatus);
|
||||
return this.builderFactory.builder(PlanStatusBuilder.class).authorize(AuthorizationFlags.AllExceptPublic).build(fieldSet, query.collectAs(fieldSet));
|
||||
}
|
||||
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ 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;
|
||||
|
@ -28,15 +27,11 @@ 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;
|
||||
|
@ -191,11 +186,7 @@ public class DescriptionStatusController {
|
|||
|
||||
@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
|
||||
))
|
||||
))
|
||||
responses = @ApiResponse(description = "OK", responseCode = "200"))
|
||||
@Swagger404
|
||||
public List<DescriptionStatus> GetAvailableTransitions(
|
||||
@Parameter(name = "descriptionId", description = "The id of description", example = "c0c163dc-2965-45a5-9608-f76030578609", required = true) @PathVariable UUID descriptionId
|
||||
|
|
|
@ -245,49 +245,29 @@ public class PlanController {
|
|||
this.auditService.track(AuditableAction.Plan_Delete, "id", id);
|
||||
}
|
||||
|
||||
@PostMapping("finalize/{id}")
|
||||
@OperationWithTenantHeader(summary = "Finalize a plan by id", description = "",
|
||||
@PostMapping("set-status/{id}/{newStatusId}")
|
||||
@OperationWithTenantHeader(summary = "set status for a plan", description = "",
|
||||
responses = @ApiResponse(description = "OK", responseCode = "200"))
|
||||
@Swagger404
|
||||
@Transactional
|
||||
public boolean finalize(
|
||||
@Parameter(name = "id", description = "The id of a plan to finalize", example = "c0c163dc-2965-45a5-9608-f76030578609", required = true) @PathVariable("id") UUID id,
|
||||
public boolean SetStatus(
|
||||
@Parameter(name = "id", description = "The id of a plan", example = "c0c163dc-2965-45a5-9608-f76030578609", required = true) @PathVariable("id") UUID id,
|
||||
@Parameter(name = "newStatusId", description = "The new status of a plan", example = "f1a3da63-0bff-438f-8b46-1a81ca176115", required = true) @PathVariable("newStatusId") UUID newStatusId,
|
||||
@RequestBody DescriptionsToBeFinalized descriptions
|
||||
) throws MyApplicationException, MyForbiddenException, MyNotFoundException, InvalidApplicationException, IOException {
|
||||
logger.debug(new MapLogEntry("finalizing" + Plan.class.getSimpleName()).And("id", id).And("descriptionIds", descriptions.getDescriptionIds()));
|
||||
logger.debug(new MapLogEntry("set status" + Plan.class.getSimpleName()).And("id", id).And("newStatusId", newStatusId).And("descriptionIds", descriptions.getDescriptionIds()));
|
||||
|
||||
this.planService.finalize(id, descriptions.getDescriptionIds());
|
||||
this.planService.setStatus(id, newStatusId, descriptions.getDescriptionIds());
|
||||
|
||||
this.auditService.track(AuditableAction.Plan_Finalize, Map.ofEntries(
|
||||
this.auditService.track(AuditableAction.Plan_SetStatus, Map.ofEntries(
|
||||
new AbstractMap.SimpleEntry<String, Object>("id", id),
|
||||
new AbstractMap.SimpleEntry<String, Object>("newStatusId", newStatusId),
|
||||
new AbstractMap.SimpleEntry<String, Object>("descriptionIds", descriptions.getDescriptionIds())
|
||||
));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@GetMapping("undo-finalize/{id}")
|
||||
@OperationWithTenantHeader(summary = "Undo the finalization of a plan by id (only possible if it is not already deposited)", description = "",
|
||||
responses = @ApiResponse(description = "OK", responseCode = "200"))
|
||||
@Swagger404
|
||||
@Transactional
|
||||
public boolean undoFinalize(
|
||||
@Parameter(name = "id", description = "The id of a plan to revert the finalization", example = "c0c163dc-2965-45a5-9608-f76030578609", required = true) @PathVariable("id") UUID id,
|
||||
@Parameter(name = "fieldSet", description = SwaggerHelpers.Commons.fieldset_description, required = true) FieldSet fieldSet
|
||||
) throws MyApplicationException, MyForbiddenException, MyNotFoundException, InvalidApplicationException, IOException, JAXBException {
|
||||
logger.debug(new MapLogEntry("undo-finalizing" + Plan.class.getSimpleName()).And("id", id));
|
||||
|
||||
this.censorFactory.censor(PlanCensor.class).censor(fieldSet, null);
|
||||
|
||||
this.planService.undoFinalize(id, fieldSet);
|
||||
|
||||
this.auditService.track(AuditableAction.Plan_Undo_Finalize, Map.ofEntries(
|
||||
new AbstractMap.SimpleEntry<String, Object>("id", id)
|
||||
));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@GetMapping("validate/{id}")
|
||||
@OperationWithTenantHeader(summary = "Validate if a plan is ready for finalization by id")
|
||||
@Hidden
|
||||
|
|
|
@ -184,4 +184,19 @@ public class PlanStatusController {
|
|||
|
||||
this.auditService.track(AuditableAction.PlanStatus_Delete, "id", id);
|
||||
}
|
||||
|
||||
@GetMapping("available-transitions/{planId}")
|
||||
@OperationWithTenantHeader(summary = "Get available status transitions for plan", description = "",
|
||||
responses = @ApiResponse(description = "OK", responseCode = "200"))
|
||||
@Swagger404
|
||||
public List<PlanStatus> GetAvailableTransitions(
|
||||
@Parameter(name = "planId", description = "The id of plan", example = "c0c163dc-2965-45a5-9608-f76030578609", required = true) @PathVariable UUID planId
|
||||
) throws InvalidApplicationException {
|
||||
logger.debug(new MapLogEntry("retrieving available statuses" + PlanStatus.class.getSimpleName()));
|
||||
|
||||
List<PlanStatus> availableTransitionStatuses = this.planStatusService.getAvailableTransitionStatuses(planId);
|
||||
|
||||
this.auditService.track(AuditableAction.PlanStatus_Delete, "planId", planId);
|
||||
return availableTransitionStatuses;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import { PlanReference } from './plan-reference';
|
|||
import { IsActive } from '@app/core/common/enum/is-active.enum';
|
||||
import { AppPermission } from '@app/core/common/enum/permission.enum';
|
||||
import { EntityType } from '@app/core/common/enum/entity-type';
|
||||
import { PlanStatus } from '../plan-status/plan-status';
|
||||
|
||||
export interface BasePlan extends BaseEntity {
|
||||
label?: string;
|
||||
|
@ -26,7 +27,7 @@ export interface BasePlan extends BaseEntity {
|
|||
planReferences?: PlanReference[];
|
||||
entityDois?: EntityDoi[];
|
||||
tenantId?: Guid;
|
||||
status?: PlanStatusEnum;
|
||||
status?: PlanStatus;
|
||||
descriptions?: BaseDescription[];
|
||||
}
|
||||
export interface Plan extends BasePlan {
|
||||
|
@ -96,7 +97,7 @@ export interface PlanDescriptionTemplate extends BaseEntity {
|
|||
//
|
||||
export interface PlanPersist extends BaseEntityPersist {
|
||||
label: string;
|
||||
status: PlanStatusEnum;
|
||||
statusId: Guid;
|
||||
properties: PlanPropertiesPersist;
|
||||
description: String;
|
||||
language: String;
|
||||
|
|
|
@ -57,6 +57,15 @@ export class PlanStatusService {
|
|||
catchError((error: any) => throwError(() => error)));
|
||||
}
|
||||
|
||||
getAvailableTransitions(planId: Guid, reqFields: string[] = []): Observable<Array<PlanStatus>> {
|
||||
const url = `${this.apiBase}/available-transitions/${planId}`;
|
||||
const options = { params: { f: reqFields } };
|
||||
|
||||
return this.http
|
||||
.get<Array<PlanStatus>>(url, options).pipe(
|
||||
catchError((error: any) => throwError(() => error)));
|
||||
}
|
||||
|
||||
buildLookup(params: {
|
||||
like?: string,
|
||||
excludedIds?: Guid[],
|
||||
|
|
|
@ -94,24 +94,14 @@ export class PlanService {
|
|||
catchError((error: any) => throwError(error)));
|
||||
}
|
||||
|
||||
finalize(id: Guid, descriptionIds: Guid[] = []): Observable<Boolean> {
|
||||
const url = `${this.apiBase}/finalize/${id}`;
|
||||
setStatus(id: Guid, newStatusId: Guid, descriptionIds: Guid[] = []): Observable<Boolean> {
|
||||
const url = `${this.apiBase}/set-status/${id}/${newStatusId}`;
|
||||
|
||||
return this.http
|
||||
.post<Boolean>(url, {descriptionIds: descriptionIds}).pipe(
|
||||
catchError((error: any) => throwError(error)));
|
||||
}
|
||||
|
||||
undoFinalize(id: Guid, reqFields: string[] = []): Observable<Boolean> {
|
||||
const url = `${this.apiBase}/undo-finalize/${id}`;
|
||||
|
||||
const options = { params: { f: reqFields } };
|
||||
|
||||
return this.http
|
||||
.get<Boolean>(url, options).pipe(
|
||||
catchError((error: any) => throwError(error)));
|
||||
}
|
||||
|
||||
validate(id: Guid): Observable<PlanValidationResult> {
|
||||
const url = `${this.apiBase}/validate/${id}`;
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ 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';
|
||||
import { PlanStatus } from '@app/core/model/plan-status/plan-status';
|
||||
|
||||
@Component({
|
||||
selector: 'app-recent-edited-activity',
|
||||
|
@ -232,7 +233,7 @@ export class RecentEditedActivityComponent extends BaseComponent implements OnIn
|
|||
response.forEach(item => {
|
||||
if (item.plan){
|
||||
if (item.plan.descriptions) {
|
||||
if (item.plan.status == PlanStatusEnum.Finalized) {
|
||||
if (item.plan.status.internalStatus == PlanStatusEnum.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?.internalStatus != DescriptionStatusEnum.Canceled);
|
||||
|
@ -271,7 +272,9 @@ export class RecentEditedActivityComponent extends BaseComponent implements OnIn
|
|||
[nameof<RecentActivityItem>(x => x.plan), nameof<Plan>(x => x.id)].join('.'),
|
||||
[nameof<RecentActivityItem>(x => x.plan), nameof<Plan>(x => x.label)].join('.'),
|
||||
[nameof<RecentActivityItem>(x => x.plan), nameof<Plan>(x => x.description)].join('.'),
|
||||
[nameof<RecentActivityItem>(x => x.plan), nameof<Plan>(x => x.status)].join('.'),
|
||||
[nameof<RecentActivityItem>(x => x.plan), nameof<Plan>(x => x.status), nameof<PlanStatus>(x => x.id)].join('.'),
|
||||
[nameof<RecentActivityItem>(x => x.plan), nameof<Plan>(x => x.status), nameof<PlanStatus>(x => x.name)].join('.'),
|
||||
[nameof<RecentActivityItem>(x => x.plan), nameof<Plan>(x => x.status), nameof<PlanStatus>(x => x.internalStatus)].join('.'),
|
||||
[nameof<RecentActivityItem>(x => x.plan), nameof<Plan>(x => x.accessType)].join('.'),
|
||||
[nameof<RecentActivityItem>(x => x.plan), nameof<Plan>(x => x.version)].join('.'),
|
||||
[nameof<RecentActivityItem>(x => x.plan), nameof<Plan>(x => x.versionStatus)].join('.'),
|
||||
|
|
|
@ -70,9 +70,7 @@
|
|||
<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> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -572,7 +572,7 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
|
|||
|
||||
hasReversableStatus(): boolean {
|
||||
if (this.item?.plan) {
|
||||
return (this.item.plan.status == PlanStatusEnum.Draft && this.isFinalized);
|
||||
return (this.item.plan.status?.internalStatus == PlanStatusEnum.Draft && this.isFinalized);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import { DescriptionStatus } from '@app/core/model/description-status/descriptio
|
|||
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';
|
||||
import { PlanStatus } from '@app/core/model/plan-status/plan-status';
|
||||
import { Plan, PlanDescriptionTemplate, PlanUser } from '@app/core/model/plan/plan';
|
||||
import { PrefillingSource } from '@app/core/model/prefilling-source/prefilling-source';
|
||||
import { ReferenceType } from '@app/core/model/reference-type/reference-type';
|
||||
|
@ -121,7 +122,11 @@ export class DescriptionEditorEntityResolver extends BaseEditorResolver {
|
|||
return [
|
||||
(prefix ? prefix + '.' : '') + [nameof<Plan>(x => x.id)].join('.'),
|
||||
(prefix ? prefix + '.' : '') + [nameof<Plan>(x => x.label)].join('.'),
|
||||
(prefix ? prefix + '.' : '') + [nameof<Plan>(x => x.status)].join('.'),
|
||||
|
||||
(prefix ? prefix + '.' : '') + [nameof<Plan>(x => x.status), nameof<PlanStatus>(x => x.id)].join('.'),
|
||||
(prefix ? prefix + '.' : '') + [nameof<Plan>(x => x.status), nameof<PlanStatus>(x => x.name)].join('.'),
|
||||
(prefix ? prefix + '.' : '') + [nameof<Plan>(x => x.status), nameof<PlanStatus>(x => x.internalStatus)].join('.'),
|
||||
|
||||
(prefix ? prefix + '.' : '') + [nameof<Plan>(x => x.isActive)].join('.'),
|
||||
|
||||
(prefix ? prefix + '.' : '') + [nameof<Plan>(x => x.authorizationFlags), AppPermission.EditPlan].join('.'),
|
||||
|
|
|
@ -44,6 +44,7 @@ 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';
|
||||
import { PlanStatus } from '@app/core/model/plan-status/plan-status';
|
||||
|
||||
@Component({
|
||||
selector: 'app-description-listing-component',
|
||||
|
@ -507,7 +508,9 @@ export class DescriptionListingComponent extends BaseListingComponent<BaseDescri
|
|||
[nameof<Description>(x => x.descriptionTemplate), nameof<DescriptionTemplate>(x => x.groupId)].join('.'),
|
||||
[nameof<Description>(x => x.plan), nameof<Plan>(x => x.id)].join('.'),
|
||||
[nameof<Description>(x => x.plan), nameof<Plan>(x => x.label)].join('.'),
|
||||
[nameof<Description>(x => x.plan), nameof<Plan>(x => x.status)].join('.'),
|
||||
[nameof<Description>(x => x.plan), nameof<Plan>(x => x.status), nameof<PlanStatus>(x => x.id)].join('.'),
|
||||
[nameof<Description>(x => x.plan), nameof<Plan>(x => x.status), nameof<PlanStatus>(x => x.name)].join('.'),
|
||||
[nameof<Description>(x => x.plan), nameof<Plan>(x => x.status), nameof<PlanStatus>(x => x.internalStatus)].join('.'),
|
||||
[nameof<Description>(x => x.plan), nameof<Plan>(x => x.accessType)].join('.'),
|
||||
[nameof<Description>(x => x.plan), nameof<Plan>(x => x.finalizedAt)].join('.'),
|
||||
[nameof<Description>(x => x.plan), nameof<Plan>(x => x.blueprint), nameof<PlanBlueprint>(x => x.id)].join('.'),
|
||||
|
|
|
@ -46,6 +46,7 @@ import { DescriptionCopyDialogComponent } from '../description-copy-dialog/descr
|
|||
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';
|
||||
import { PlanStatus } from '@app/core/model/plan-status/plan-status';
|
||||
|
||||
|
||||
@Component({
|
||||
|
@ -79,7 +80,7 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
|
|||
canAssignPlanUsers(): boolean {
|
||||
const authorizationFlags = !this.isPublicView ? (this.description?.plan as Plan)?.authorizationFlags : [];
|
||||
return (authorizationFlags?.some(x => x === AppPermission.AssignPlanUsers) || this.authentication.hasPermission(AppPermission.AssignPlanUsers)) &&
|
||||
!this.isPublicView && this.description?.belongsToCurrentTenant && this.description?.plan?.status === PlanStatusEnum.Draft;
|
||||
!this.isPublicView && this.description?.belongsToCurrentTenant && this.description?.plan?.status?.internalStatus != PlanStatusEnum.Finalized;
|
||||
}
|
||||
|
||||
authorFocus: string;
|
||||
|
@ -513,7 +514,7 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
|
|||
}
|
||||
|
||||
hasReversableStatus(description: Description): boolean {
|
||||
return description.plan.status == PlanStatusEnum.Draft && description?.status?.internalStatus == DescriptionStatusEnum.Finalized && this.canFinalize && this.availableStatusesTransitions?.find(x => x.internalStatus === DescriptionStatusEnum.Draft) != null
|
||||
return description.plan.status.internalStatus == PlanStatusEnum.Draft && description?.status?.internalStatus == DescriptionStatusEnum.Finalized && this.canFinalize && this.availableStatusesTransitions?.find(x => x.internalStatus === DescriptionStatusEnum.Draft) != null
|
||||
}
|
||||
|
||||
reverseFinalization(description: Description, statusId: Guid) {
|
||||
|
@ -575,7 +576,9 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
|
|||
[nameof<Description>(x => x.plan), nameof<Plan>(x => x.id)].join('.'),
|
||||
[nameof<Description>(x => x.plan), nameof<Plan>(x => x.label)].join('.'),
|
||||
[nameof<Description>(x => x.plan), nameof<Plan>(x => x.accessType)].join('.'),
|
||||
[nameof<Description>(x => x.plan), nameof<Plan>(x => x.status)].join('.'),
|
||||
[nameof<Description>(x => x.plan), nameof<Plan>(x => x.status), nameof<PlanStatus>(x => x.id)].join('.'),
|
||||
[nameof<Description>(x => x.plan), nameof<Plan>(x => x.status), nameof<PlanStatus>(x => x.name)].join('.'),
|
||||
[nameof<Description>(x => x.plan), nameof<Plan>(x => x.status), nameof<PlanStatus>(x => x.internalStatus)].join('.'),
|
||||
[nameof<Description>(x => x.plan), nameof<Plan>(x => x.authorizationFlags), AppPermission.InvitePlanUsers].join('.'),
|
||||
[nameof<Description>(x => x.plan), nameof<Plan>(x => x.blueprint), nameof<PlanBlueprint>(x => x.id)].join('.'),
|
||||
[nameof<Description>(x => x.plan), nameof<Plan>(x => x.blueprint), nameof<PlanBlueprint>(x => x.label)].join('.'),
|
||||
|
|
|
@ -17,13 +17,16 @@
|
|||
<div class="plan-subtitle">
|
||||
<span *ngIf="isUserPlanRelated()" class="col-auto">{{ enumUtils.toPlanUserRolesString(planService.getCurrentUserRolesInPlan(plan?.planUsers)) }}</span>
|
||||
<span *ngIf="isUserPlanRelated()">.</span>
|
||||
<span class="col-auto" *ngIf="plan.status === planStatusEnum.Finalized && isPublic"><span class="material-icons icon-align">public</span>{{'TYPES.PLAN-VISIBILITY.PUBLIC' | translate}}</span>
|
||||
<span *ngIf="plan.status === planStatusEnum.Finalized && !isPublic; else draft" class="col-auto"><span class="material-icons icon-align">done</span>{{ enumUtils.toPlanStatusString(plan.status) }}</span>
|
||||
<span class="col-auto" *ngIf="plan.status.internalStatus === planStatusEnum.Finalized && isPublic"><span class="material-icons icon-align">public</span>{{'TYPES.PLAN-VISIBILITY.PUBLIC' | translate}}</span>
|
||||
<span *ngIf="plan.status.internalStatus === planStatusEnum.Finalized && !isPublic; else draft" class="col-auto"><span class="material-icons icon-align">done</span>{{ plan.status.name }}</span>
|
||||
<ng-template #draft>
|
||||
<span *ngIf="plan.status === planStatusEnum.Draft && canEditPlan; else preview" class=" col-auto draft"><span class="material-icons icon-align">create</span>{{ enumUtils.toPlanStatusString(plan.status) }}</span>
|
||||
<span *ngIf="plan.status.internalStatus === planStatusEnum.Draft && canEditPlan; else preview" class=" col-auto draft"><span class="material-icons icon-align">create</span>{{ plan.status.name }}</span>
|
||||
</ng-template>
|
||||
<ng-template #preview>
|
||||
<span *ngIf="plan.status === planStatusEnum.Draft && !canEditPlan" class=" col-auto draft"><span class="material-icons-outlined mr-1 icon-align">visibility</span>{{ enumUtils.toPlanStatusString(plan.status) }}</span>
|
||||
<span *ngIf="plan.status.internalStatus === planStatusEnum.Draft && !canEditPlan; else otherStatus" class=" col-auto draft"><span class="material-icons-outlined mr-1 icon-align">visibility</span>{{ plan.status.name }}</span>
|
||||
</ng-template>
|
||||
<ng-template #otherStatus>
|
||||
<span *ngIf="!isPublic" class=" col-auto draft">{{ plan.status.name }}</span>
|
||||
</ng-template>
|
||||
<span>.</span>
|
||||
<span class="col-auto">{{'PLAN-LISTING.VERSION' | translate}} {{plan.version}}</span>
|
||||
|
|
|
@ -87,7 +87,7 @@ export class PlanListingItemComponent extends BaseComponent implements OnInit {
|
|||
}
|
||||
|
||||
get isDraftPlan(): boolean {
|
||||
return this.plan.status == PlanStatusEnum.Draft;
|
||||
return this.plan.status?.internalStatus == PlanStatusEnum.Draft;
|
||||
}
|
||||
|
||||
constructor(
|
||||
|
@ -112,16 +112,16 @@ export class PlanListingItemComponent extends BaseComponent implements OnInit {
|
|||
|
||||
ngOnInit() {
|
||||
this.analyticsService.trackPageView(AnalyticsService.PlanListingItem);
|
||||
if (this.plan.status == PlanStatusEnum.Draft) {
|
||||
if (this.plan.status?.internalStatus == PlanStatusEnum.Draft) {
|
||||
this.isDraft = true;
|
||||
this.isFinalized = false;
|
||||
this.isPublished = false;
|
||||
}
|
||||
else if (this.plan.status == PlanStatusEnum.Finalized) {
|
||||
else if (this.plan.status?.internalStatus == PlanStatusEnum.Finalized) {
|
||||
this.isDraft = false;
|
||||
this.isFinalized = true;
|
||||
this.isPublished = false;
|
||||
if (this.plan.status === PlanStatusEnum.Finalized && this.plan.accessType === PlanAccessType.Public) { this.isPublished = true }
|
||||
if (this.plan.status.internalStatus === PlanStatusEnum.Finalized && this.plan.accessType === PlanAccessType.Public) { this.isPublished = true }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -147,7 +147,7 @@ export class PlanListingItemComponent extends BaseComponent implements OnInit {
|
|||
}
|
||||
|
||||
viewVersions(plan: Plan) {
|
||||
if (plan.accessType == PlanAccessType.Public && plan.status == PlanStatusEnum.Finalized && !this.plan.authorizationFlags?.some(x => x === AppPermission.EditPlan)) {
|
||||
if (plan.accessType == PlanAccessType.Public && plan.status?.internalStatus == PlanStatusEnum.Finalized && !this.plan.authorizationFlags?.some(x => x === AppPermission.EditPlan)) {
|
||||
let url = this.router.createUrlTree(['/explore-plans/versions/', plan.groupId]);
|
||||
window.open(url.toString(), '_blank');
|
||||
} else {
|
||||
|
@ -157,7 +157,7 @@ export class PlanListingItemComponent extends BaseComponent implements OnInit {
|
|||
}
|
||||
|
||||
viewVersionsUrl(plan: Plan): string {
|
||||
if (plan.accessType == PlanAccessType.Public && plan.status == PlanStatusEnum.Finalized && !this.plan.authorizationFlags?.some(x => x === AppPermission.EditPlan)) {
|
||||
if (plan.accessType == PlanAccessType.Public && plan.status?.internalStatus == PlanStatusEnum.Finalized && !this.plan.authorizationFlags?.some(x => x === AppPermission.EditPlan)) {
|
||||
let url = this.router.createUrlTree(['/explore-plans/versions/', plan.groupId]);
|
||||
return url.toString();
|
||||
} else {
|
||||
|
|
|
@ -45,6 +45,7 @@ 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';
|
||||
import { PlanStatus } from '@app/core/model/plan-status/plan-status';
|
||||
|
||||
@Component({
|
||||
selector: 'app-plan-listing-component',
|
||||
|
@ -493,7 +494,11 @@ export class PlanListingComponent extends BaseListingComponent<BasePlan, PlanLoo
|
|||
nameof<Plan>(x => x.id),
|
||||
nameof<Plan>(x => x.label),
|
||||
nameof<Plan>(x => x.description),
|
||||
nameof<Plan>(x => x.status),
|
||||
|
||||
[nameof<Plan>(x => x.status), nameof<PlanStatus>(x => x.id)].join('.'),
|
||||
[nameof<Plan>(x => x.status), nameof<PlanStatus>(x => x.name)].join('.'),
|
||||
[nameof<Plan>(x => x.status), nameof<PlanStatus>(x => x.internalStatus)].join('.'),
|
||||
|
||||
nameof<Plan>(x => x.accessType),
|
||||
nameof<Plan>(x => x.version),
|
||||
nameof<Plan>(x => x.versionStatus),
|
||||
|
|
|
@ -51,7 +51,7 @@
|
|||
{{plan.updatedAt | dateTimeFormatter: "d MMMM y"}}
|
||||
</div>
|
||||
<div class="col-auto d-flex">
|
||||
<div *ngIf="plan.status== planStatusEnum.Finalized" class="d-flex flex-row uppercase">
|
||||
<div *ngIf="plan.status.internalStatus== planStatusEnum.Finalized" class="d-flex flex-row uppercase">
|
||||
<mat-icon class="status-icon">check</mat-icon>
|
||||
{{'PLAN-OVERVIEW.FINALISED' | translate}}
|
||||
</div>
|
||||
|
@ -125,7 +125,7 @@
|
|||
<ng-container *ngFor="let description of plan.descriptions">
|
||||
<div class="col-12 col-lg-7 mt-1">
|
||||
<a class="w-100 description" [routerLink]="isPublicView ? this.routerUtils.generateUrl(['/explore-descriptions/overview/public/', description.id]) : this.routerUtils.generateUrl(['/descriptions/overview/' + description.id])" target="_blank">
|
||||
<button class="w-100" [ngClass]="{'plan-btn': description.status === descriptionStatusEnum.Draft, 'plan-finalized-btn': description.status === descriptionStatusEnum.Finalized}">
|
||||
<button class="w-100" [ngClass]="{'plan-btn': description.status.internalStatus === descriptionStatusEnum.Draft, 'plan-finalized-btn': description.status.internalStatus === descriptionStatusEnum.Finalized}">
|
||||
<div matTooltip="{{ description.label }}" class="d-flex align-items-center justify-content-between">
|
||||
<div class="description-btn-label">{{ description.label }}</div>
|
||||
<mat-icon>launch</mat-icon>
|
||||
|
@ -181,7 +181,29 @@
|
|||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="frame mb-3 pt-4 pl-4 pr-5 pb-3">
|
||||
<ng-container *ngIf="isDraftPlan() && canFinalizePlan() && !isLocked">
|
||||
<ng-container *ngIf="availableStatusesTransitions && availableStatusesTransitions.length > 0 && !isLocked && hasDoi(plan) && plan.belongsToCurrentTenant != false">
|
||||
<div *ngFor='let status of availableStatusesTransitions'>
|
||||
<div class="row align-items-center" (click)="persistStatus(status)">
|
||||
<div class="col-auto pr-0">
|
||||
<button *ngIf="status.internalStatus === descriptionStatusEnum.Finalized && plan.status?.internalStatus != planStatusEnum.Finalized" mat-mini-fab class="finalize-btn">
|
||||
<mat-icon class="mat-mini-fab-icon check-icon">check</mat-icon>
|
||||
</button>
|
||||
<button *ngIf="plan.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">{{ status.name }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row align-items-center">
|
||||
<div class="col-12">
|
||||
<hr class="hr-line">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<!-- <ng-container *ngIf="isDraftPlan() && canFinalizePlan() && !isLocked">
|
||||
<div class="row align-items-center" (click)="finalize(plan)">
|
||||
<div class="col-auto pr-0">
|
||||
<button mat-mini-fab class="finalize-btn">
|
||||
|
@ -192,14 +214,14 @@
|
|||
<p class="mb-0 pl-2 finalize-txt">{{ 'PLAN-OVERVIEW.ACTIONS.FINALIZE' | translate }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row align-items-center">
|
||||
<div class="row align-items-center">0
|
||||
<div class="col-12">
|
||||
<hr class="hr-line">
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-container> -->
|
||||
<app-plan-deposit-dropdown *ngIf="(hasDoi(plan) || moreDeposit()) && isFinalizedPlan(plan) && !this.isPublicView && canDepositPlan(plan) && inputRepos.length > 0" [inputRepos]="inputRepos" [plan]="plan" (outputReposEmitter)="afterDeposit($event)"></app-plan-deposit-dropdown>
|
||||
<ng-container *ngIf="isFinalizedPlan(plan) && hasDoi(plan) && !isPublishedPlan(plan) && canFinalizePlan(plan)">
|
||||
<!-- <ng-container *ngIf="isFinalizedPlan(plan) && hasDoi(plan) && !isPublishedPlan(plan) && canFinalizePlan(plan)">
|
||||
<div (click)="reverseFinalization()" class="row mb-3 align-items-center">
|
||||
<div class="col-auto pr-0">
|
||||
<button mat-mini-fab class="frame-btn">
|
||||
|
@ -210,7 +232,7 @@
|
|||
<p class="mb-0 pl-2 frame-txt">{{ 'PLAN-OVERVIEW.ACTIONS.REVERSE' | translate }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-container> -->
|
||||
<ng-container *ngIf="canExportPlan() && fileTransformerService.availableFormatsFor(fileTransformerEntityTypeEnum.Plan).length > 0">
|
||||
<div class="row mb-3 align-items-center">
|
||||
<div class="col-auto pr-0">
|
||||
|
@ -254,7 +276,7 @@
|
|||
<app-plan-authors
|
||||
[planUsers]="plan.planUsers"
|
||||
[username]="userName"
|
||||
[removeUser]="canAssignPlanUsers(plan) && plan.status === planStatusEnum.Draft"
|
||||
[removeUser]="canAssignPlanUsers(plan) && (plan.status.internalStatus == null || plan.status.internalStatus === planStatusEnum.Finalized)"
|
||||
(deleteAuthor)="removeUserFromPlan($event)"
|
||||
/>
|
||||
<div *ngIf="canInvitePlanUsers()" class="col-12 d-flex align-items-center justify-content-center mt-2">
|
||||
|
|
|
@ -51,6 +51,8 @@ import { PlanInvitationDialogComponent } from '../invitation/dialog/plan-invitat
|
|||
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';
|
||||
import { PlanStatus } from '@app/core/model/plan-status/plan-status';
|
||||
import { PlanStatusService } from '@app/core/services/plan/plan-status.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-plan-overview',
|
||||
|
@ -83,6 +85,7 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
|
|||
|
||||
authorFocus: string;
|
||||
userName: string;
|
||||
availableStatusesTransitions: PlanStatus[];
|
||||
|
||||
constructor(
|
||||
public routerUtils: RouterUtilsService,
|
||||
|
@ -108,6 +111,7 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
|
|||
private breadcrumbService: BreadcrumbService,
|
||||
private httpErrorHandlingService: HttpErrorHandlingService,
|
||||
private userService: UserService,
|
||||
private pLanStatusService: PlanStatusService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
@ -130,10 +134,11 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
|
|||
this.breadcrumbService.addIdResolvedValue(data.id?.toString(), data.label);
|
||||
|
||||
this.plan = data;
|
||||
this.getAvailableStatuses(this.plan.id);
|
||||
this.plan.planUsers = this.isActive ? data?.planUsers?.filter((x) => x.isActive === IsActive.Active) : data?.planUsers;
|
||||
this.plan.otherPlanVersions = data.otherPlanVersions?.filter(x => x.isActive === IsActive.Active) || null;
|
||||
if (this.plan.descriptions) {
|
||||
if (this.plan.status == PlanStatusEnum.Finalized) {
|
||||
if (this.plan.status?.internalStatus == PlanStatusEnum.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?.internalStatus !== DescriptionStatusEnum.Canceled);
|
||||
|
@ -230,6 +235,15 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
|
|||
return this.language.instant('PLAN-OVERVIEW.INFOS.UNAUTHORIZED-ORCID');
|
||||
}
|
||||
|
||||
getAvailableStatuses(id: Guid){
|
||||
this.pLanStatusService.getAvailableTransitions(id).pipe(takeUntil(this._destroyed))
|
||||
.subscribe(
|
||||
(statuses) => {
|
||||
this.availableStatusesTransitions = statuses;
|
||||
},
|
||||
(error) => this.httpErrorHandlingService.handleBackedRequestError(error)
|
||||
); }
|
||||
|
||||
onFetchingDeletedCallbackError(redirectRoot: string) {
|
||||
this.router.navigate([this.routerUtils.generateUrl(redirectRoot)]);
|
||||
}
|
||||
|
@ -412,15 +426,15 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
|
|||
}
|
||||
|
||||
isDraftPlan() {
|
||||
return this.plan.status == PlanStatusEnum.Draft;
|
||||
return this.plan.status?.internalStatus == PlanStatusEnum.Draft;
|
||||
}
|
||||
|
||||
isFinalizedPlan(plan: Plan) {
|
||||
return plan.status == PlanStatusEnum.Finalized;
|
||||
return plan.status?.internalStatus == PlanStatusEnum.Finalized;
|
||||
}
|
||||
|
||||
isPublishedPlan() {
|
||||
return (this.plan.status == PlanStatusEnum.Finalized && this.plan.accessType === PlanAccessType.Public);
|
||||
return (this.plan.status?.internalStatus == PlanStatusEnum.Finalized && this.plan.accessType === PlanAccessType.Public);
|
||||
}
|
||||
|
||||
hasDoi() {
|
||||
|
@ -442,7 +456,24 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
|
|||
return (this.plan.entityDois.length < this.depositRepos.length);
|
||||
}
|
||||
|
||||
finalize() {
|
||||
persistStatus(status: PlanStatus) {
|
||||
if (status.internalStatus != null && status.internalStatus === PlanStatusEnum.Finalized) {
|
||||
this.finalize(status.id);
|
||||
} else if (this.plan.status.internalStatus === PlanStatusEnum.Finalized){
|
||||
this.reverseFinalization(status.id);
|
||||
} else {
|
||||
// other statuses
|
||||
this.planService.setStatus(this.plan.id, status.id).pipe(takeUntil(this._destroyed))
|
||||
.subscribe({
|
||||
complete: () => {this.reloadPage(); this.onUpdateCallbackSuccess()},
|
||||
error:(error: any) => {
|
||||
this.onUpdateCallbackError(error)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
finalize(newStatusId) {
|
||||
const dialogRef = this.dialog.open(PlanFinalizeDialogComponent, {
|
||||
maxWidth: '500px',
|
||||
restoreFocus: false,
|
||||
|
@ -454,7 +485,7 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
|
|||
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe((result: PlanFinalizeDialogOutput) => {
|
||||
if (result && !result.cancelled) {
|
||||
|
||||
this.planService.finalize(this.plan.id, result.descriptionsToBeFinalized)
|
||||
this.planService.setStatus(this.plan.id, newStatusId, result.descriptionsToBeFinalized)
|
||||
.pipe(takeUntil(this._destroyed))
|
||||
.subscribe({
|
||||
complete: () => {
|
||||
|
@ -512,7 +543,7 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
|
|||
}
|
||||
}
|
||||
|
||||
reverseFinalization() {
|
||||
reverseFinalization(newStatusId: Guid) {
|
||||
const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
|
||||
restoreFocus: false,
|
||||
data: {
|
||||
|
@ -524,7 +555,7 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
|
|||
});
|
||||
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => {
|
||||
if (result) {
|
||||
this.planService.undoFinalize(this.plan.id, PlanEditorEntityResolver.lookupFields()).pipe(takeUntil(this._destroyed))
|
||||
this.planService.setStatus(this.plan.id, newStatusId).pipe(takeUntil(this._destroyed))
|
||||
.subscribe({
|
||||
complete: () => {this.reloadPage(); this.onUpdateCallbackSuccess()},
|
||||
error:(error: any) => {
|
||||
|
@ -637,7 +668,9 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
|
|||
nameof<Plan>(x => x.id),
|
||||
nameof<Plan>(x => x.label),
|
||||
nameof<Plan>(x => x.description),
|
||||
nameof<Plan>(x => x.status),
|
||||
[nameof<Plan>(x => x.status), nameof<PlanStatus>(x => x.id)].join('.'),
|
||||
[nameof<Plan>(x => x.status), nameof<PlanStatus>(x => x.name)].join('.'),
|
||||
[nameof<Plan>(x => x.status), nameof<PlanStatus>(x => x.internalStatus)].join('.'),
|
||||
nameof<Plan>(x => x.accessType),
|
||||
nameof<Plan>(x => x.version),
|
||||
nameof<Plan>(x => x.versionStatus),
|
||||
|
|
|
@ -52,12 +52,11 @@
|
|||
<button [disabled]="saving" mat-menu-item (click)="formSubmit()" type="button">{{ 'PLAN-EDITOR.ACTIONS.SAVE-AND-CONTINUE' | translate }}</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
<div class="col-auto d-flex align-items-center" *ngIf="canFinalize && this.hasNotDoi() && formGroup.enabled" [matTooltipDisabled]="canFinalize && formGroup.pristine" matTooltip="{{'PLAN-EDITOR.ACTIONS.FINALIZE.CAN-NOT-FINALIZE' | translate}}">
|
||||
<button [disabled]="!formGroup.pristine" mat-button class="rounded-btn primary-inverted mr-2" type="button" (click)="finalize()">{{ 'PLAN-EDITOR.ACTIONS.FINALIZE.FINALIZE' | translate }}</button>
|
||||
<div *ngIf="availableStatusesTransitions && availableStatusesTransitions.length > 0 && !isLocked && !isNew && hasNotDoi()" class="col-auto d-flex align-items-center" [matTooltipDisabled]="formGroup.pristine" matTooltip="{{'PLAN-EDITOR.ACTIONS.FINALIZE.CAN-NOT-FINALIZE' | translate}}">
|
||||
<button *ngFor='let status of availableStatusesTransitions' [disabled]="saving || !formGroup.pristine" mat-button class="rounded-btn primary-inverted mr-2" type="button" (click)="persistStatus(status)">{{ status.name}}</button>
|
||||
</div>
|
||||
<div *ngIf="formGroup.pristine" class="col-auto d-flex align-items-center">
|
||||
<button [disabled]="saving" *ngIf="isLocked" mat-button class="rounded-btn primary-inverted mr-2" type="button">{{ 'PLAN-EDITOR.ACTIONS.LOCKED' | translate}}</button>
|
||||
<button [disabled]="saving" *ngIf="canReverseFinalize && this.hasNotDoi()" mat-button class="rounded-btn primary-inverted mr-2" type="button" (click)="reverseFinalization()">{{ 'PLAN-EDITOR.ACTIONS.REVERSE' | translate }}</button>
|
||||
<div *ngIf="isLocked" class="col-auto d-flex align-items-center">
|
||||
<button class="col-auto d-flex align-items-center" [disabled]="saving" mat-button class="rounded-btn primary-inverted mr-2" type="button">{{ 'PLAN-EDITOR.ACTIONS.LOCKED' | translate}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -60,6 +60,8 @@ import { PlanEditorService } from './plan-editor.service';
|
|||
import { PlanEditorEntityResolver } from './resolvers/plan-editor-enitity.resolver';
|
||||
import { FormAnnotationService } from '@app/ui/annotations/annotation-dialog-component/form-annotation.service';
|
||||
import { PlanAssociatedUser } from '@app/core/model/user/user';
|
||||
import { PlanStatusService } from '@app/core/services/plan/plan-status.service';
|
||||
import { PlanStatus } from '@app/core/model/plan-status/plan-status';
|
||||
|
||||
@Component({
|
||||
selector: 'app-plan-editor',
|
||||
|
@ -97,6 +99,8 @@ export class PlanEditorComponent extends BaseEditor<PlanEditorModel, Plan> imple
|
|||
|
||||
hoveredContact: number = -1;
|
||||
|
||||
availableStatusesTransitions: PlanStatus[];
|
||||
|
||||
singleAutocompleteBlueprintConfiguration: SingleAutoCompleteConfiguration = {
|
||||
initialItems: (data?: any) => this.planBlueprintService.query(this.planBlueprintService.buildAutocompleteLookup(null, null, null, [PlanBlueprintStatus.Finalized])).pipe(map(x => x.items)),
|
||||
filterFn: (searchQuery: string, data?: any) => this.planBlueprintService.query(this.planBlueprintService.buildAutocompleteLookup(searchQuery, null, null, [PlanBlueprintStatus.Finalized])).pipe(map(x => x.items)),
|
||||
|
@ -136,7 +140,7 @@ export class PlanEditorComponent extends BaseEditor<PlanEditorModel, Plan> imple
|
|||
}
|
||||
|
||||
protected get canReverseFinalize(): boolean {
|
||||
return !this.isDeleted && !this.isNew && this.canEdit && this.isLockedByUser && this.item.status == PlanStatusEnum.Finalized && (this.hasPermission(this.authService.permissionEnum.EditPlan) || this.item?.authorizationFlags?.some(x => x === AppPermission.EditPlan));
|
||||
return !this.isDeleted && !this.isNew && this.canEdit && this.isLockedByUser && this.item.status?.internalStatus == PlanStatusEnum.Finalized && (this.hasPermission(this.authService.permissionEnum.EditPlan) || this.item?.authorizationFlags?.some(x => x === AppPermission.EditPlan));
|
||||
}
|
||||
|
||||
protected canEditSection(id: Guid): boolean {
|
||||
|
@ -193,6 +197,7 @@ export class PlanEditorComponent extends BaseEditor<PlanEditorModel, Plan> imple
|
|||
private breadcrumbService: BreadcrumbService,
|
||||
public fileTransformerService: FileTransformerService,
|
||||
private formAnnotationService: FormAnnotationService,
|
||||
private pLanStatusService: PlanStatusService
|
||||
) {
|
||||
const descriptionLabel: string = route.snapshot.data['entity']?.label;
|
||||
if (descriptionLabel) {
|
||||
|
@ -261,8 +266,9 @@ export class PlanEditorComponent extends BaseEditor<PlanEditorModel, Plan> imple
|
|||
}
|
||||
this.editorModel = data ? new PlanEditorModel().fromModel(data) : new PlanEditorModel();
|
||||
if (data) {
|
||||
if (data.id) this.getAvailableStatuses(data.id);
|
||||
if (data.descriptions) {
|
||||
if (data.status == PlanStatusEnum.Finalized) {
|
||||
if (data.status?.internalStatus == PlanStatusEnum.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.internalStatus !== DescriptionStatusEnum.Canceled);
|
||||
|
@ -278,7 +284,6 @@ export class PlanEditorComponent extends BaseEditor<PlanEditorModel, Plan> imple
|
|||
|
||||
this.selectedBlueprint = data?.blueprint;
|
||||
this.isDeleted = data ? data.isActive === IsActive.Inactive : false;
|
||||
this.isFinalized = data ? data.status === PlanStatusEnum.Finalized : false;
|
||||
|
||||
if (data && data.id) {
|
||||
const descriptionSectionPermissionResolverModel: DescriptionSectionPermissionResolver = {
|
||||
|
@ -291,6 +296,14 @@ export class PlanEditorComponent extends BaseEditor<PlanEditorModel, Plan> imple
|
|||
this.buildForm();
|
||||
}
|
||||
|
||||
if (data && data.status?.internalStatus == PlanStatusEnum.Finalized || this.isDeleted) {
|
||||
this.viewOnly = true;
|
||||
this.isFinalized = true;
|
||||
this.formGroup.disable();
|
||||
} else {
|
||||
this.viewOnly = false;
|
||||
}
|
||||
|
||||
if (this.item && this.item.id != null) {
|
||||
this.checkLock(this.item.id, LockTargetType.Plan, 'PLAN-EDITOR.LOCKED-DIALOG.TITLE', 'PLAN-EDITOR.LOCKED-DIALOG.MESSAGE');
|
||||
}
|
||||
|
@ -304,16 +317,17 @@ export class PlanEditorComponent extends BaseEditor<PlanEditorModel, Plan> imple
|
|||
this.formGroup = this.editorModel.buildForm(null, this.isDeleted || !this.canEdit);
|
||||
|
||||
this.sectionToFieldsMap = this.prepareErrorIndication();
|
||||
|
||||
if (this.editorModel.status == PlanStatusEnum.Finalized || this.isDeleted) {
|
||||
this.viewOnly = true;
|
||||
this.isFinalized = this.editorModel.status == PlanStatusEnum.Finalized;
|
||||
this.formGroup.disable();
|
||||
} else {
|
||||
this.viewOnly = false;
|
||||
}
|
||||
}
|
||||
|
||||
getAvailableStatuses(id: Guid){
|
||||
this.pLanStatusService.getAvailableTransitions(id).pipe(takeUntil(this._destroyed))
|
||||
.subscribe(
|
||||
(statuses) => {
|
||||
this.availableStatusesTransitions = statuses;
|
||||
},
|
||||
(error) => this.httpErrorHandlingService.handleBackedRequestError(error)
|
||||
); }
|
||||
|
||||
prepareErrorIndication(): Map<string, PlanFieldIndicator> {
|
||||
if (this.selectedBlueprint?.definition == null) return;
|
||||
|
||||
|
@ -449,7 +463,23 @@ export class PlanEditorComponent extends BaseEditor<PlanEditorModel, Plan> imple
|
|||
return (this.item.entityDois == null || this.item.entityDois.length == 0);
|
||||
}
|
||||
|
||||
finalize() {
|
||||
persistStatus(status: PlanStatus) {
|
||||
if (status.internalStatus != null && status.internalStatus === PlanStatusEnum.Finalized) {
|
||||
this.finalize(status.id);
|
||||
} else if (this.item.status.internalStatus === PlanStatusEnum.Finalized){
|
||||
this.reverseFinalization(status.id);
|
||||
} else {
|
||||
// other statuses
|
||||
this.planService.setStatus(this.item.id, status.id).pipe(takeUntil(this._destroyed))
|
||||
.subscribe(data => {
|
||||
this.onCallbackSuccess()
|
||||
}, (error: any) => {
|
||||
this.onCallbackError(error)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
finalize(newStatusId) {
|
||||
const dialogRef = this.dialog.open(PlanFinalizeDialogComponent, {
|
||||
maxWidth: '500px',
|
||||
restoreFocus: false,
|
||||
|
@ -461,7 +491,7 @@ export class PlanEditorComponent extends BaseEditor<PlanEditorModel, Plan> imple
|
|||
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe((result: PlanFinalizeDialogOutput) => {
|
||||
if (result && !result.cancelled) {
|
||||
|
||||
this.planService.finalize(this.item.id, result.descriptionsToBeFinalized)
|
||||
this.planService.setStatus(this.item.id, newStatusId, result.descriptionsToBeFinalized)
|
||||
.pipe(takeUntil(this._destroyed))
|
||||
.subscribe(data => {
|
||||
this.onCallbackSuccess()
|
||||
|
@ -474,7 +504,7 @@ export class PlanEditorComponent extends BaseEditor<PlanEditorModel, Plan> imple
|
|||
|
||||
}
|
||||
|
||||
reverseFinalization() {
|
||||
reverseFinalization(newStatusId: Guid) {
|
||||
const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
|
||||
restoreFocus: false,
|
||||
data: {
|
||||
|
@ -486,7 +516,7 @@ export class PlanEditorComponent extends BaseEditor<PlanEditorModel, Plan> imple
|
|||
});
|
||||
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => {
|
||||
if (result) {
|
||||
this.planService.undoFinalize(this.item.id, PlanEditorEntityResolver.lookupFields()).pipe(takeUntil(this._destroyed))
|
||||
this.planService.setStatus(this.item.id, newStatusId).pipe(takeUntil(this._destroyed))
|
||||
.subscribe(data => {
|
||||
this.onCallbackSuccess()
|
||||
}, (error: any) => {
|
||||
|
@ -594,7 +624,6 @@ export class PlanEditorComponent extends BaseEditor<PlanEditorModel, Plan> imple
|
|||
label: this.formGroup.get('label').value,
|
||||
description: this.formGroup.get('description').value,
|
||||
blueprint: this.selectedBlueprint,
|
||||
status: PlanStatusEnum.Draft
|
||||
}
|
||||
|
||||
this.prepareForm(plan);
|
||||
|
|
|
@ -18,7 +18,7 @@ import { Guid } from "@common/types/guid";
|
|||
|
||||
export class PlanEditorModel extends BaseEditorModel implements PlanPersist {
|
||||
label: string;
|
||||
status: PlanStatusEnum;
|
||||
statusId: Guid;
|
||||
properties: PlanPropertiesEditorModel = new PlanPropertiesEditorModel(this.validationErrorModel);
|
||||
description: String;
|
||||
language: String;
|
||||
|
@ -37,7 +37,7 @@ export class PlanEditorModel extends BaseEditorModel implements PlanPersist {
|
|||
if (item) {
|
||||
super.fromModel(item);
|
||||
this.label = item.label;
|
||||
this.status = item.status;
|
||||
this.statusId = item.status?.id;
|
||||
this.properties = new PlanPropertiesEditorModel(this.validationErrorModel).fromModel(item.properties, item.planReferences?.filter(x => x.isActive === IsActive.Active), item.blueprint);
|
||||
this.description = item.description;
|
||||
this.language = item.language;
|
||||
|
@ -85,7 +85,7 @@ export class PlanEditorModel extends BaseEditorModel implements PlanPersist {
|
|||
const formGroup = this.formBuilder.group({
|
||||
id: [{ value: this.id, disabled: disabled }, context.getValidation('id').validators],
|
||||
label: [{ value: this.label, disabled: disabled }, context.getValidation('label').validators],
|
||||
status: [{ value: this.status, disabled: disabled }, context.getValidation('status').validators],
|
||||
statusId: [{ value: this.statusId, disabled: disabled }, context.getValidation('statusId').validators],
|
||||
properties: this.properties.buildForm({
|
||||
rootPath: `properties.`,
|
||||
disabled: disabled
|
||||
|
@ -121,7 +121,7 @@ export class PlanEditorModel extends BaseEditorModel implements PlanPersist {
|
|||
const baseValidationArray: Validation[] = new Array<Validation>();
|
||||
baseValidationArray.push({ key: 'id', validators: [BackendErrorValidator(this.validationErrorModel, 'id')] });
|
||||
baseValidationArray.push({ key: 'label', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'label')] });
|
||||
baseValidationArray.push({ key: 'status', validators: [BackendErrorValidator(this.validationErrorModel, 'status')] });
|
||||
baseValidationArray.push({ key: 'statusId', validators: [BackendErrorValidator(this.validationErrorModel, 'status')] });
|
||||
baseValidationArray.push({ key: 'properties', validators: [BackendErrorValidator(this.validationErrorModel, 'properties')] });
|
||||
baseValidationArray.push({ key: 'description', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'description')] });
|
||||
baseValidationArray.push({ key: 'language', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'language')] });
|
||||
|
|
|
@ -16,6 +16,7 @@ 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';
|
||||
import { PlanStatus } from '@app/core/model/plan-status/plan-status';
|
||||
|
||||
@Injectable()
|
||||
export class PlanEditorEntityResolver extends BaseEditorResolver {
|
||||
|
@ -29,7 +30,13 @@ export class PlanEditorEntityResolver extends BaseEditorResolver {
|
|||
...BaseEditorResolver.lookupFields(),
|
||||
nameof<Plan>(x => x.id),
|
||||
nameof<Plan>(x => x.label),
|
||||
// TODO status remove later
|
||||
nameof<Plan>(x => x.status),
|
||||
|
||||
[nameof<Plan>(x => x.status), nameof<PlanStatus>(x => x.id)].join('.'),
|
||||
[nameof<Plan>(x => x.status), nameof<PlanStatus>(x => x.name)].join('.'),
|
||||
[nameof<Plan>(x => x.status), nameof<PlanStatus>(x => x.internalStatus)].join('.'),
|
||||
|
||||
nameof<Plan>(x => x.versionStatus),
|
||||
nameof<Plan>(x => x.groupId),
|
||||
nameof<Plan>(x => x.description),
|
||||
|
|
Loading…
Reference in New Issue