Изменение состояния карточки, которое было добавлено в таблицу-перечисление KrDocState
Изменение состояния карточки, которое было добавлено в таблицу-перечисление KrDocState¶
Состояния карточки, которые использует типовое решение, содержатся в таблице KrDocState
. Чтобы добавить новые состояния карточки (например, “В архиве”, “На доработке” и пр.), нужно добавить записи в эту таблицу с опциональной локализацией. Для этого в редакторе схемы данных надо выбрать таблицу, перейти на узел “Записи” и вставить строки с состояниями. Тогда все представления и правила доступа типового решения получат возможность использовать это состояние для фильтрации и разделения прав.
Для того, чтобы перевести карточку в новое состояние в расширениях (например, в бизнес-процессах Workflow API), требуется написать и вызвать метод SetKrStateAsync
.
Задание состояния необходимо выполнять с помощью объекта IKrDocumentStateManager
. Это позволяет обеспечить единообразие при задании состояния. В типовой реализации интерфейса IKrDocumentStateManager
, объект KrDocumentStateManager
копирует информацию о состоянии в поля DocumentCommonInfo.StateID
и DocumentCommonInfo.StateName
, если сответствующие поля включены в пакет карточки. Это позволяет оптимизировать запросы. Например:
SELECT
[dci].[ID],
[aci].[StateID],
[aci].[StateName]
FROM [DocumentCommonInfo] AS [dci] WITH (NOLOCK)
LEFT JOIN [KrApprovalCommonInfo] AS [aci] WITH (NOLOCK)
ON [aci].[MainCardID] = [dci].[ID]
WHERE [dci].[CardTypeID] = '335F86A1-D009-012C-8B45-1F43C2382C2D'
AND [aci].[StateID] IN (0, 1, 2, 3);
Следующим образом:
SELECT
[dci].[ID],
[dci].[StateID],
[dci].[StateName]
FROM [DocumentCommonInfo] AS [dci] WITH (NOLOCK)
WHERE [dci].[CardTypeID] = '335F86A1-D009-012C-8B45-1F43C2382C2D'
AND [dci].[StateID] IN (0, 1, 2, 3);
Примеры изменения состояния карточки
using System;
using System.Threading;
using System.Threading.Tasks;
using Tessa.Cards;
using Tessa.Extensions.Default.Server.Cards;
using Tessa.Extensions.Default.Server.Workflow.KrProcess.Scope;
using Tessa.Extensions.Default.Shared;
using Tessa.Extensions.Default.Shared.Workflow.KrProcess;
using Tessa.Platform;
using Tessa.Platform.Validation;
namespace Tessa.Extensions.Server
{
public static class SetKrStateExamples
{
/// <summary>
/// Устанавливает состояние документа.
///
/// Данный способ следует использовать, если выполнение происходит вне транзакции на сохранение
/// (CardStoreExtension AfterBeginTransaction/BeforeCommitTransaction).
/// </summary>
/// <param name="cardRepository">
/// Репозиторий для управления карточками с расширениями
/// (<see cref="CardRepositoryNames.Extended"/> или <see cref="CardRepositoryNames.ExtendedWithoutTransaction"/>).
/// </param>
/// <param name="krDocumentStateManager">Объект управляющий состоянием карточки документа.</param>
/// <param name="mainCard">Карточка документа.</param>
/// <param name="state">Состояние документа.</param>
/// <param name="validationResult">Реультат валидации.</param>
/// <param name="cancellationToken">Объект, посредством которого можно отменить асинхронную задачу.</param>
/// <returns>Асинхронная задача.</returns>
public static async Task SetKrStateAsync(
ICardRepository cardRepository,
IKrDocumentStateManager krDocumentStateManager,
Card mainCard,
KrState state,
IValidationResultBuilder validationResult,
CancellationToken cancellationToken = default)
{
Check.ArgumentNotNull(cardRepository, nameof(cardRepository));
Check.ArgumentNotNull(krDocumentStateManager, nameof(krDocumentStateManager));
Check.ArgumentNotNull(mainCard, nameof(mainCard));
Check.ArgumentNotNull(validationResult, nameof(validationResult));
// Карточка-сателлит будет автоматически создана, если она отсутствовала при загрузке.
Card satellite = await GetKrSatelliteCardAsync(
cardRepository,
mainCard.ID,
validationResult,
cancellationToken);
if (satellite is null)
{
return;
}
(_, bool hasMainSatelliteChanges, _) = await krDocumentStateManager.SetStateAsync(
mainCard,
satellite,
state,
cancellationToken);
if (!hasMainSatelliteChanges)
{
return;
}
satellite.RemoveAllButChanged();
CardStoreRequest storeRequest = new CardStoreRequest { Card = satellite };
CardStoreResponse storeResponse = await cardRepository.StoreAsync(storeRequest, cancellationToken);
validationResult.Add(storeResponse.ValidationResult);
}
/// <summary>
/// Возвращает карточку основного сателлита карточки документа.
/// </summary>
/// <param name="cardRepository">Репозиторий для управления карточками.</param>
/// <param name="mainCardID">Идентификатор карточки документа.</param>
/// <param name="validationResult">Результат валидации.</param>
/// <param name="cancellationToken">Объект, посредством которого можно отменить асинхронную задачу.</param>
/// <returns>Асинхронная задача содержащая карточку основного сателлита
/// или значение <see langword="null"/>, если при выполнении произошла ошибка.</returns>
public static async Task<Card> GetKrSatelliteCardAsync(
ICardRepository cardRepository,
Guid mainCardID,
IValidationResultBuilder validationResult,
CancellationToken cancellationToken = default)
{
Check.ArgumentNotNull(cardRepository, nameof(cardRepository));
Check.ArgumentNotNull(validationResult, nameof(validationResult));
CardGetRequest getRequest = new CardGetRequest
{
CardID = mainCardID,
CardTypeID = DefaultCardTypes.KrSatelliteTypeID,
RestrictionFlags = CardGetRestrictionValues.Satellite,
};
// Если мы внутри Workflow API, то необходимо добавить строку,
// чтобы блокировка на основную карточку не бралась:
getRequest.SetNoLockingMainCard(true);
CardGetResponse response = await cardRepository.GetAsync(
getRequest,
cancellationToken);
validationResult.Add(response.ValidationResult);
return response.ValidationResult.IsSuccessful() ? response.Card : null;
}
/// <summary>
/// Устанавливает состояние документа.
///
/// Данный способ следует использовать, если выполнение происходит в транзакции на сохранение
/// (CardStoreExtension AfterBeginTransaction/BeforeCommitTransaction).
/// </summary>
/// <param name="krScope">Объект предоставляющий методы для работы с текущим контекстом расширений
/// типового решения и использования разделяемых объектов карточек.</param>
/// <param name="krDocumentStateManager">Объект управляющий состоянием карточки документа.</param>
/// <param name="mainCard">Карточка документа.</param>
/// <param name="state">Состояние документа.</param>
/// <param name="validationResult">Реультат валидации.</param>
/// <param name="cancellationToken">Объект, посредством которого можно отменить асинхронную задачу.</param>
/// <returns>Асинхронная задача.</return
public static async Task SetKrStateAsync(
IKrScope krScope,
IKrDocumentStateManager krDocumentStateManager,
Card mainCard,
KrState state,
IValidationResultBuilder validationResult,
CancellationToken cancellationToken = default)
{
Check.ArgumentNotNull(krScope, nameof(krScope));
Check.ArgumentNotNull(krDocumentStateManager, nameof(krDocumentStateManager));
Check.ArgumentNotNull(mainCard, nameof(mainCard));
Check.ArgumentNotNull(validationResult, nameof(validationResult));
// Сателлит будет сохранён автоматически при выходе из уровня контекста KrScopeContext
// в Tessa.Extensions.Default.Server.Workflow.KrProcess.Scope.KrLifecycleScopeStoreExtension.
Card satellite = await krScope.GetKrSatelliteAsync(
mainCard.ID,
validationResult,
cancellationToken);
await krDocumentStateManager.SetStateAsync(
mainCard,
satellite,
state,
cancellationToken);
}
}
}
Important
В сборке 2.2 и более ранних при использовании метода GetSatelliteCard внутри Workflow API карточка-сателлит уже должна быть создана. В противном случае будет отброшена транзакция Workflow, и будет создана транзакция на создание сателлита. Это можно сделать, вызвав метод GetSatelliteCard в запросе BeforeRequest перед запуском бизнес-процесса.
Чтобы состояние карточки отображалось в блоке KrBlockForDocStatus
даже в том случае, когда не используется типовой процесс согласования и регистрации, следует изменить расширение KrHideApprovalTabOrDocStateBlockUIExtension
в типовом решении в проекте Tessa.Extensions.Default.Client
, убрав вызов метода CollapseSpecialBlock
.
// Удалить строки ниже:
// скрываем специальный блок с состоянием документа, если нет согласования и регистрации
//if (usedComponents.HasNot(KrComponents.Routes)
// && usedComponents.HasNot(KrComponents.Registration))
//{
// CollapseSpecialBlock(model);
//}