Как отправить счет-фактуру

Рассмотрим последовательность действий к функциям интеграторского интерфейса Диадока, которые требуется совершить продавцу при отправке счета-фактуры (СФ), корректировочного счета-фактуры (КСФ), исправления счета-фактуры (ИСФ).

Порядок согласно приказу N 14Н

Порядок документооборота со стороны продавца:

  1. Продавец формирует счет-фактуру, подписывает и направляет Покупателю.

  2. Диадок формирует подтверждение оператора о дате получения счета-фактуры, подписывает его и направляет Продавцу.

  3. Продавец получает подтверждение оператора.

  4. Продавец получает извещение о получении счета-фактуры от Покупателя.

Примечание

Более подробно о порядке обмена электронными счетами-фактурами между компаниями можно почитать в соответствующем разделе или на сайте

Формирование счета-фактуры

Если на стороне интеграционного решения не предусмотрено функциональности для формирования XML-документов, соответствующих утвержденным форматам, то продавец может сгенерировать СФ/ИСФ/КСФ/ИКСФ, используя метод GenerateTitleXml.

В теле запроса должен содержаться упрощенный XML-файл, соответствующий XSD-схеме контракта для генерации титула. XSD-схема контракта, необходимого для генерации титула, может быть получена с помощью ссылки, доступной в поле UserDataXsdUrl контракта DocumentTitle, который можно получить с помощью метода-справочника GetDocumentTypes.

В теле ответа содержится сгенерированный XML-файл титула, построенный на основании данных из запроса. Файл изготавливается в соответствии с XSD-схемой соответствующего титула документа.

Подробнее см. Обобщенный метод генерации GenerateTitleXml.

Отправка счета-фактуры

После того, как у вас есть XML-файл СФ/ИСФ/КСФ/ИКСФ, его нужно отправить с помощью метода PostMessage.

Для этого нужно подготовить структуру MessageToPost следующим образом:

  • в значение атрибута FromBoxId указываем идентификатор ящика отправителя,

  • в значение атрибута ToBoxId указываем идентификатор ящика получателя,

  • для передачи XML-файла СФ/ИСФ/КСФ/ИКСФ нужно использовать атрибут DocumentAttachments, описываемый структурой DocumentAttachment

    • внутри структуры DocumentAttachment находится вложенная структура SignedContent. Сам XML-файл нужно передать в атрибут Content, подпись продавца в атрибут Signature,

    • внутри структуры DocumentAttachment в атрибут TypeNamedId нужно передать строковой идентификатор типа документа.

Описание структур, используемых при отправке СФ/ИСФ/КСФ/ИКСФ:

message MessageToPost {
    required string FromBoxId = 1;
    optional string ToBoxId = 2;
    repeated DocumentAttachment DocumentAttachments = 34;
}

message DocumentAttachment {
    required SignedContent SignedContent = 1;
    optional string Comment = 3;
    required string TypeNamedId = 12;
}

message SignedContent {
    optional bytes Content = 1;
    optional bytes Signature = 2;
}

После отправки в теле ответа будет содержаться отправленное сообщение, сериализованное в протобуфер Message.

Получение подтверждения оператора

После успешной отправки СФ/ИСФ/КСФ/ИКСФ необходимо получить подтверждение оператора InvoiceConfirmation.

Подтверждение оператора представляется структурой Entity, где значение полей EntityType и AttachmentType должно быть Attachment/InvoiceConfirmation.

Чтобы получить подтверждение оператора нужно вызвать метод GetMessage и указать нужные GET-параметры boxId, messageId, entityId.

BoxId - это идентификатор ящика отправителя, messageId - идентификатор отправленного сообщения с СФ/ИСФ/КСФ/ИКСФ, entityId - идентификатор счета-фактуры. Их можно взять из структуры Message

Например, HTTP-запрос для получения сообщения выглядит следующим образом:

GET /V3/GetMessage?messageId=8971177a-8c38-49f7-97d3-0f51fbe134c5&entityId=736aa0c4-12f5-4412-bfea-1de59948b904&boxId=96339010-4c66-462d-a917-7f31bb8d80c4 HTTP/1.1
Host: diadoc-api.kontur.ru
Content-Type: application/json; charset=utf-8
Accept: application/json
Authorization: DiadocAuth ddauth_api_client_id=testClient-8ee1638deae84c86b8e2069955c2825a

Пример структуры подтверждения оператора InvoiceConfirmation в теле ответа:

{
    "EntityType": "Attachment",
    "EntityId": "9955dccd-82fd-4412-b953-7854e102f782",
    "ParentEntityId": "736aa0c4-12f5-4412-bfea-1de59948b904",
    "Content": "lores ipsum",
    "AttachmentType": "InvoiceConfirmation",
    "FileName": "DP_PDPOL_2BM-7750370234-4012052808304878702630000000000_2BM_20150927_324c290e-f049-4906-baac-1ddcd7f3c2ff.xml",
    "NeedRecipientSignature": false,
    "SignerBoxId": "",
    "NotDeliveredEventId": "",
    "RawCreationDate": 635789700936777240,
    "SignerDepartmentId": "",
    "NeedReceipt": false,
    "IsApprovementSignature": false,
    "IsEncryptedContent": false
}

Получение извещения о получении счета-фактуры

На отправленный счет-фактуру нужно получить извещение о получении счета-фактуры со стороны покупателя InvoiceReceipt.

Извещение о получении счета-фактуры представляется структурой Entity, где значение полей EntityType и AttachmentType должно быть Attachment/InvoiceReceipt.

Чтобы получить подтверждение оператора нужно вызвать метод GetMessage и указать нужные GET-параметры boxId, messageId, entityId.

BoxId - это идентификатор ящика отправителя, messageId - идентификатор отправленного сообщения с СФ/ИСФ/КСФ/ИКСФ, entityId - идентификатор счета-фактуры. Их можно взять из структуры Message

Например, HTTP-запрос для получения сообщения выглядит следующим образом:

GET /V3/GetMessage?messageId=8971177a-8c38-49f7-97d3-0f51fbe134c5&entityId=736aa0c4-12f5-4412-bfea-1de59948b904&boxId=96339010-4c66-462d-a917-7f31bb8d80c4 HTTP/1.1
Host: diadoc-api.kontur.ru
Content-Type: application/json; charset=utf-8
Accept: application/json
Authorization: DiadocAuth ddauth_api_client_id=testClient-8ee1638deae84c86b8e2069955c2825a

Пример структуры извещения о получении счета-фактуры InvoiceReceipt в теле ответа:

{
    "EntityType": "Attachment",
    "EntityId": "1d7b2e96-9945-41ab-aeea-2f310382bfad",
    "ParentEntityId": "45d16c54-8700-4882-afaf-97678d6ed135",
    "Content": "lores ipsum",
    "AttachmentType": "InvoiceReceipt",
    "FileName": "DP_IZVPOL_2BM-9610384428-961001000-201510080625090688235_2BM-9653544919-965301000-201508270726013081470_20151008_6bbfab54-4e9f-4ca1-99eb-37f34880a784.xml",
    "NeedRecipientSignature": false,
    "SignerBoxId": "",
    "NotDeliveredEventId": "",
    "RawCreationDate": 635798950114653648,
    "SignerDepartmentId": "",
    "NeedReceipt": false,
    "IsApprovementSignature": false,
    "IsEncryptedContent": false
}

SDK

Пример кода на C# для отправки счета-фактуры:

//Для работы с документами в Диадоке необходим авторизационный токен.
//Подробнее о получении авторизационного токена можно узнать в разделе "Как авторизоваться в системе".
public static string AuthTokenCert;

public static string BoxId = "идентификатор ящика отправителя";

//Формирование счета-фактуры
public static GeneratedFile GenerateInvoiceXml()
{
    var content = new InvoiceInfo();
    return Api.GenerateInvoiceXml(AuthTokenCert, content);
}

//Отправка счета-фактуры
public static Message SendInvoiceXml()
{
    var invoice = GenerateInvoiceXml();
    var messageAttachment = new XmlDocumentAttachment
    {
        SignedContent = new SignedContent
        {
            Content = invoice.Content,
            //Подпись отправителя, см. "Как авторизоваться в системе"
            Signature = Crypt.Sign(invoice.Content, ReadCertContent("путь к сертификату"))
        }
    };
    var messageToPost = new MessageToPost
    {
        FromBoxId = BoxId,
        ToBoxId = "идентификатор ящика получателя",
        Invoices =
        {
            messageAttachment
        }
    };
    return Api.PostMessage(AuthTokenCert, messageToPost);
}

//Получение извещения о получении счета-фактуры
public static byte[] GetInvoiceReceipt(Message invoiceMessage)
{
    var receiptEntityId = "";
    foreach (var entity in invoiceMessage.Entities)
    {
        if (entity.AttachmentType == AttachmentType.InvoiceReceipt && entity.ParentEntityId == invoiceMessage.Entities[0].EntityId)
        {
            receiptEntityId = entity.EntityId;
        }
    }

    return Api.GetEntityContent(AuthTokenCert, BoxId, invoiceMessage.MessageId, receiptEntityId);
}

public static void Main()
{
    var invoiceMessage = SendInvoiceXml();

    //Оператор формирует подтверждение в течение нескольких секунд.
    //Для получения сообщения с подтверждением необходимо вызвать метод GetMessage()
    var invoiceMessageWithConfirmation = Api.GetMessage(AuthTokenCert, BoxId, invoiceMessage.MessageId);

    //Технический документ можно получить в виде массива байтов.
    //Для получения сообщения с новыми вложениями необходимо снова вызвать метод GetMessage()
    var invoiceMessageWithReceipt = Api.GetMessage(AuthTokenCert, BoxId, invoiceMessage.MessageId);

    //Технический документ можно получить в виде массива байтов.
    var invoiceReceipt = GetInvoiceReceipt(invoiceMessageWithReceipt);
}

Порядок согласно приказу N 174Н (утратил силу с 01.07.2021)

Подробнее

Порядок документооборота со стороны продавца:

  1. Продавец формирует счет-фактуру, подписывает и направляет Покупателю.

  2. Диадок формирует подтверждение оператора о дате получения счета-фактуры, подписывает его и направляет Продавцу.

  3. Продавец получает подтверждение оператора и отправляет в ответ подписанное извещение о получении подтверждения.

  4. Продавец получает извещение о получении счета-фактуры от Покупателя.

Формирование счета-фактуры

Действия аналогичны инструкции для обмена СФ по 14Н (см. Формирование счета-фактуры).

Отправка счета-фактуры

Действия аналогичны инструкции для обмена СФ по 14Н (см. Отправка счета-фактуры).

Получение подтверждения оператора

Действия аналогичны инструкции для обмена СФ по 14Н (см. Получение подтверждения оператора).

Формирование извещения о получении подтверждения оператора

После того, как продавец получил подтверждение оператора, он должен отправить в ответ подписанное извещение InvoiceReceipt о получении подтверждения.

Извещение о получении подтверждения оператора представляется структурой Entity, где значение полей EntityType и AttachmentType должно быть Attachment/InvoiceReceipt.

В API Диадока есть метод, который позволяет сформировать извещение о получении подтверждения оператора - GenerateReceiptXml, при вызове этого метода нужно корректно указать GET-параметры boxId, messageId, attachmentId и передать в тело запроса данные о подписанте генерируемого извещения в виде сериализованной структуры Signer.

BoxId - это идентификатор ящика отправителя, messageId - идентификатор отправленного сообщения с СФ/ИСФ/КСФ/ИКСФ, attachmentId - идентификатор подтверждение оператора. Их можно взять из структуры Message.

Например, HTTP-запрос для формирования извещение о получении подтверждения оператора выглядит следующим образом:

POST /GenerateReceiptXml?boxId=db32772b-9256-49a8-a133-fda593fda38a&messageId=a9093c56-7c48-4ab1-bc87-efb04e7d4400&attachmentId=f80738a3-b0bc-426a-aadf-6967ec1b53df HTTP/1.1
Host: diadoc-api.kontur.ru
Content-Type: application/json charset=utf-8
Accept: application/json
Authorization: DiadocAuth ddauth_api_client_id=testClient-8ee1638deae84c86b8e2069955c2825a

Пример структуры в теле запроса, содержащей данные о подписанте генерируемого извещения Signer:

{
    "SignerCertificate": "",
    "SignerDetails ": {
     "Surname": "Иванов",
     "FirstName": "Иван",
     "Patronymic": "Иванович",
     "JobTitle": "QA",
     "Inn": "1234567",
     "SoleProprietorRegistrationCertificate": "",
    },
}

В теле ответа содержится XML-файл с извещением о получении документа attachmentId из сообщения messageId в ящике boxId.

Отправка извещения о получении подтверждения оператора

Полученное на предыдущем этапе извещение нужно подписать и отправить. Подписание извещения происходит на стороне клиента, после того как извещение подписано, его нужно отправить вместе с файлом подписи воспользовавшись методом PostMessagePatch.

Для этого нужно подготовить структуру MessagePatchToPost следующим образом:

  • в значение атрибута BoxId указываем идентификатор ящика отправителя,

  • в значение атрибута MessageId указываем идентификатор модифицируемого сообщения,

  • для передачи XML-файла извещения нужно использовать атрибут Receipts, описываемый структурой ReceiptAttachment

  • в поле ParentEntityId нужно указать идентификатор (EntityId) подтверждения оператора, полученный на предыдущем шаге,

  • внутри структуры ReceiptAttachment находится вложенная структура SignedContent,

  • сам XML-файл нужно передать в атрибут Content, подпись продавца в атрибут Signature

message MessagePatchToPost {
    required string BoxId = 1;
    required string MessageId = 2;
    repeated ReceiptAttachment Receipts = 3;
}

message ReceiptAttachment  {
    required string ParentEntityId  = 1;
    required SignedContent SignedContent = 2;

}

message SignedContent {
    optional bytes Content = 1;
    optional bytes Signature = 2;
}

Пример структуры в теле запроса, содержащей данные о передаваемом извещении MessagePatchToPost:

{
  "BoxId": "db32772b-9256-49a8-a133-fda593fda38a",
  "MessageId": "a9093c56-7c48-4ab1-bc87-efb04e7d4400",
  "Receipts":
  [
    {
      "ParentEntityId":"f80738a3-b0bc-426a-aadf-6967ec1b53df",
      "SignedContent":
        {
          "Content": "...",
          "Signature": "...",
        },
      "Comment": "Подписание извещения о получении подтверждения оператора",
    }
 ]
}

Получение извещения о получении счета-фактуры

Действия аналогичны инструкции для обмена СФ по 14Н (см. Получение извещения о получении счета-фактуры).

SDK

Пример кода на C# для отправки счета-фактуры:

//Для работы с документами в Диадоке необходим авторизационный токен.
//Подробнее о получении авторизационного токена можно узнать в разделе "Как авторизоваться в системе".
public static string AuthTokenCert;

public static string BoxId = "идентификатор ящика отправителя";

//Формирование счета-фактуры
public static GeneratedFile GenerateInvoiceXml()
{
        var content = new InvoiceInfo()
        {
                //Заполняется согласно структуре InvoiceInfo
        };
        return Api.GenerateInvoiceXml(AuthTokenCert, content);
}

//Отправка счета-фактуры
public static Message SendInvoiceXml()
{
        var invoice = GenerateInvoiceXml();
        var messageAttachment = new XmlDocumentAttachment
        {
                SignedContent = new SignedContent
                {
                        Content = invoice.Content,
                        //Подпись отправителя, см. "Как авторизоваться в системе"
                        Signature = Crypt.Sign(invoice.Content, ReadCertContent("путь к сертификату"))
                }
        };
        var messageToPost = new MessageToPost
        {
                FromBoxId = BoxId,
                ToBoxId = "идентификатор ящика получателя",
                Invoices =
                {
                        messageAttachment
                }
        };
        return Api.PostMessage(AuthTokenCert, messageToPost);
}

//Получение подтверждения оператора, формирование и отправка извещения о получении
public static void GetInvoiceConfirmationAndSendReceipt(Message invoiceMessage)
{
        var confirmationEntityId = "";

        foreach (var entity in invoiceMessage.Entities)
        {
                if (entity.AttachmentType == AttachmentType.InvoiceConfirmation)
                {
                        confirmationEntityId = entity.EntityId;
                        break;
                }
        }

        var receipt = Api.GenerateReceiptXml(AuthTokenCert, BoxId, invoiceMessage.MessageId, confirmationEntityId, new Signer()
        {
                //Подпись отправителя, см. "Как авторизоваться в системе"
                SignerCertificate = ReadCertContent("путь к сертификату"),
                SignerDetails = new SignerDetails()
                {
                        //Заполняется согласно структуре SignerDetails
                }
        });

        var receiptAttachment = new ReceiptAttachment()
        {
                ParentEntityId = confirmationEntityId,
                SignedContent = new SignedContent()
                {
                        Content = receipt.Content,
                        //Подпись отправителя, см. "Как авторизоваться в системе"
                        Signature = Crypt.Sign(receipt.Content, ReadCertContent("путь к сертификату"))
                }
        };

        var receiptPatch = new MessagePatchToPost()
        {
                BoxId = BoxId,
                MessageId = invoiceMessage.MessageId,
                Receipts =
                {
                        receiptAttachment
                }
        };

        Api.PostMessagePatch(AuthTokenCert, receiptPatch);
}

//Получение извещения о получении счета-фактуры
public static byte[] GetInvoiceReceipt(Message invoiceMessage)
{
        var receiptEntityId = "";
        foreach (var entity in invoiceMessage.Entities)
        {
                if (entity.AttachmentType == AttachmentType.InvoiceReceipt &&
                        entity.ParentEntityId == invoiceMessage.Entities[0].EntityId)
                        receiptEntityId = entity.EntityId;
        }
        return Api.GetEntityContent(AuthTokenCert, BoxId, invoiceMessage.MessageId, receiptEntityId);
}

public static void Main()
{
        var invoiceMessage = SendInvoiceXml();

        //Оператор формирует подтверждение в течение нескольких секунд.
        //Для получения сообщения с подтверждением необходимо вызвать метод GetMessage()
        var invoiceMessageWithConfirmation = Api.GetMessage(AuthTokenCert, BoxId, invoiceMessage.MessageId);

        GetInvoiceConfirmationAndSendReceipt(invoiceMessageWithConfirmation);

        //Технический документ можно получить в виде массива байтов.
        //Для получения сообщения с новыми вложениями необходимо снова вызвать метод GetMessage()
        var invoiceMessageWithReceipt = Api.GetMessage(AuthTokenCert, BoxId, invoiceMessage.MessageId);
        var invoiceMessageWithReceipt = Api.GetMessage(AuthTokenCert, BoxId, invoiceMessage.MessageId);

        //Технический документ можно получить в виде массива байтов.
        var invoiceReceipt = GetInvoiceReceipt(invoiceMessageWithReceipt);
}