Разработка компонента изменения данных карточки

Компонент изменения данных предоставляет модулю не зависящий от типа карточки слой изменения данных карточки, обеспечивающий возможность внесения изменения при получении ответа от контрагента.

Компонент должен реализовывать программный интерфейс IDocumentUpdater.

Рекомендуется создавать свой компонент на основе абстрактного класса BaseUpdater (сборка DocsVision.Edi.Runtime.UniversalDocument.dll). Далее приведена часть класса BaseDataReader с описанием.

public abstract class BaseUpdater : IDocumentUpdater
{

    public void Initialize(ObjectContext objectContext) (1)
    {
        ObjectContext = objectContext;
        UserSession = objectContext.GetService<UserSession>();

        documentService = objectContext.GetService<IDocumentService>();
        baseCardService = objectContext.GetService<IBaseCardService>();
        staffService = objectContext.GetService<IStaffService>();
    }

    public virtual Guid AddSignatureToDocument(Guid cardId, Guid fileId, MessageFile messageFile, string partnerName) (2)
    {
        Document document = GetDocument(cardId);

        DocumentFile documentFile = document.Files.FirstOrDefault(item => item.FileVersionRowId == fileId);
        if (documentFile == null)
        {
            return Guid.Empty;
        }

        byte[] signatureData = null;
        if (messageFile.SignatureData != null && messageFile.SignatureData.Length > 0) (3)
        {
            signatureData = messageFile.SignatureData;
        }
        else if (!string.IsNullOrEmpty(messageFile.SignatureFilePath))
        {
            signatureData = File.ReadAllBytes(messageFile.SignatureFilePath);
        }

        if (signatureData == null)
        {
            return Guid.Empty;
        }

        SignedCms signedCms = new SignedCms();
        signedCms.Decode(signatureData);

        if (signedCms.SignerInfos.Count == 0 || signedCms.Certificates.Count == 0)
        {
            return Guid.Empty;
        }

        BaseCardSignature cardSignature = document.MainInfo.SignatureList.Signatures.FirstOrDefault(item => item.SignedFromConfirmation == partnerName); (4)
        if (cardSignature == null)
        {
            cardSignature = baseCardService.AddSignature(document.MainInfo.SignatureList, signedCms.Certificates[0], document.Description, document.SystemInfo.State);
            cardSignature.Signer = staffService.GetCurrentEmployee();
            cardSignature.SignedFromConfirmation = partnerName;
            cardSignature.Certificate = signedCms.Certificates[0].Thumbprint;
            cardSignature.Imported = true;
        }

(5)
        BaseCardSignature newSignature = baseCardService.AddSignaturePart(cardSignature, MainFileSignaturePartType, signatureData,
            string.Format(Resources.MainFilePartnerSignature, documentFile.FileName), documentFile.FileId, documentFile.FileVersionId, document);

        ObjectContext.SaveObject(document);

        return ObjectContext.GetObjectRef(newSignature).Id;
    }

    public virtual Guid CreateNewSignatures(Guid cardId, X509Certificate2 certificate, bool signAdditionalFiles) (6)
    {
        Document document = GetDocument(cardId);

        if (document.MainInfo.SignatureList == null) (7)
        {
            SignatureList signatureList = documentService.CreateSignatureList();
            ObjectContext.SaveObject(signatureList);
            document.MainInfo.SignatureList = signatureList;
        }

        foreach (DocumentFile documentFile in document.Files)
        {
            UpdateInvoiceFile(document, documentFile, certificate);
            string validateResult = ValidateInvoiceFile(document, documentFile); (8)
            if (!string.IsNullOrEmpty(validateResult))
            {
                throw new IncompleteSignatureDataException(string.Format(Resources.UnableToValidateInvoice, documentFile.FileName, validateResult));
            }
        }

        DocumentSetting documentSetting = documentService.GetKindSettings(document.SystemInfo.CardKind);

        BaseCardSignature baseCardSignature = documentService.AddSignature(document, certificate, false, documentSetting.DocumentSignature.Fields); (9)

        ObjectContext.SaveObject(document);

        return ObjectContext.GetObjectRef(baseCardSignature).Id;
    }

    public virtual string CheckCertificate(Guid cardId, X509Certificate2 certificate) (10)
    {
        return CheckDocumentSignerForCertificate(GetDocument(cardId), certificate);
    }

    public virtual bool ChangeDocumentState(Guid cardId, Guid newStateId) (11)
    {
        if (cardId == Guid.Empty)
        {
            throw Error.ArgumentNull("cardId");
        }

        Document document = GetDocument(cardId);

        IStateService stateService = ObjectContext.GetService<IStateService>();
        StatesStateMachineBranch branch = stateService.FindLineBranchesByStartState(document.SystemInfo.State).FirstOrDefault(item => item.EndState.BuiltInState == newStateId);
        if (branch == null)
        {
            return false;
        }

        if (!stateService.IsOperationAllowedFull(branch.Operation, document))
        {
            return false;
        }

        stateService.ChangeState(document, branch, true, out string processErrors);

        return true;
    }

    public virtual Guid GetNewStateId(MessageFileType messageFileType) (12)
    {
        switch (messageFileType)
        {
            case MessageFileType.InvoiceReply:
            case MessageFileType.ReplySignature:
                return SignedStateId;

            case MessageFileType.SignatureRejection:
                return RejectedStateId;

            case MessageFileType.InvoiceCorrectionRequest:
                return CorrectionRequiredStateId;

            case MessageFileType.RevocationRequest:
                return RevocationRequestStateId;

            case MessageFileType.RevocationReply:
                return RevocationReplyStateId;

            case MessageFileType.RevocationRejection:
                return RevocationRejectionStateId;
        }

        return Guid.Empty;
    }

    public void UpdateDocumentDataFromFile(Guid cardId, Guid fileId) (13)
    {
        if (cardId == Guid.Empty)
        {
            throw Error.ArgumentNull("cardId");
        }

        if (fileId == Guid.Empty)
        {
            throw Error.ArgumentNull("fileId");
        }

        Document document = GetDocument(cardId);

        DocumentFile documentFile = document.Files.FirstOrDefault(item => item.FileVersionRowId == fileId);
        if (documentFile == null)
        {
            throw Error.InvalidOperation(Resources.FileNotExists, fileId, cardId);
        }

        UpdateDocumentDataFromFile(document, documentFile);
    }

    protected virtual void UpdateInvoiceFile(Document document, DocumentFile documentFile, X509Certificate2 certificate) (14)
    {
    }

    protected virtual string ValidateInvoiceFile(Document document, DocumentFile documentFile) (15)
    {
        return null;
    }

    protected virtual string CheckDocumentSignerForCertificate(Document document, X509Certificate2 certificate) (16)
    {
        CardData documentData = UserSession.CardManager.GetCardData(ObjectContext.GetObjectRef(document).Id);
        RowData signerRow = documentData.Sections[documentData.Type.AllSections[CardDefs.UniversalDocumentSignature.Alias].Id].FirstRow;
        Guid employeeId = signerRow.GetGuid(CardDefs.UniversalDocumentSignature.Signer).GetValueOrDefault();
        StaffEmployee employee = (employeeId != Guid.Empty) ? ObjectContext.GetObject<StaffEmployee>(employeeId) : null;

        if (employee == null)
        {
            return Resources.NoSignerData;
        }

        string certificateName = CertificateHelper.GetCertificateSignerName(certificate);
        if (string.IsNullOrEmpty(certificateName))
        {
            return Resources.NoCertificateSignerData;
        }

        if (string.Compare($"{employee.LastName} {employee.FirstName} {employee.MiddleName}", certificateName, StringComparison.InvariantCultureIgnoreCase) != 0)
        {
            return Resources.DifferentSignerData;
        }

        return null;
    }

(17)
    protected virtual void UpdateDocumentDataFromFile(Document document, DocumentFile documentFile)
    {
    }
}
1 Инициализируем компонент отправки.
При инициализации получаем сервисы API Docsvision, которые потребуются в дальнейшем.
2 Реализация метода добавления подписи к указанному файлу карточки.
3 Получаем данные подписи из сообщения.
Подпись может быть в сообщении или в файле, указанном в сообщении.
4 Получаем карточку подписей.
5 Добавляем подпись в документ.
6 Реализация метода подписания карточки.
7 Если в документе не списка подписей — создаём.
8 Обновляем информацию в титуле продавца.
9 Добавляем подпись в документ.
10 Реализация метода проверка сертификата подписи в карточке.
Для неформализованного документа реализация функциональности не требуется.
В стандартной реализации для УПД выполняется сравнение данных сотрудника, подписавшего УПД с данными сотрудника-владельца указанного сертификата.
11 Реализация метода изменения состояния карточки.
12 Реализация метода получения идентификатора состояния карточки.
Для неформализованного документа реализация функциональности не требуется.
В стандартной реализации для УПД метод возвращает идентификатор одного из встроенных состояний карточки УПД из Конструктора состояний.
13 Реализация метода обновления данных карточки данными из файла титула продавца.
Для неформализованного документа реализация функциональности не требуется.
В стандартной реализации для УПД метод получает содержимое файла (XML-формата) и устанавливает значения полей карточки.
14 В своей реализации нужно переопределить метод, добавив алгоритм обновления титула продавца.
Пример реализации в классе DocsVision.Edi.Runtime.UniversalDocument.SellerInvoiceUpdater (сборка DocsVision.Edi.Runtime.UniversalDocument.dll).
15 В своей реализации нужно переопределить метод, добавив алгоритм проверки файла титула продавца.
16 В своей реализации нужно переопределить метод, добавив алгоритм проверки подписанта.
17 В своей реализации нужно переопределить метод, добавив алгоритм загрузки данных в карточку из приложенного файла титула продавца.
Пример реализации в классе DocsVision.Edi.Runtime.UniversalDocument.BuyerInvoiceUpdater (сборка DocsVision.Edi.Runtime.UniversalDocument.dll).