Skip to content

2. Печать чека

Ansar8 edited this page Mar 17, 2023 · 11 revisions

Доступность: с версии 8.18.0

Регистрировать чеки можно только в открытой смене. Если смена закрыта, попытка напечатать чек завершится с ошибкой: "Перед печатью чеков необходимо открыть смену".

Согласно требованиям законодательства, длительность смены составляет 24 часа. По истечении этого времени необходимо закрыть смену.

Печать чека через приложение МодульКассы включает в себя прием оплаты (в том числе используя внешний эквайринг) и фискализацию чека. В качестве результата выполнения операции получим чек с фискальной информацией от ККТ и данными по платежной транзакции. Обработка возникающих ошибок производится на стороне МодульКассы с показом диалогов, требующих разрешения конфликтных ситуаций пользователем. Таким образом, МодульКасса позволяет разрешить возникающие в процессе оплаты коллизии с возможностью полной отмены оплаты. Если ошибку не удается устранить, вызывающее приложение получает ответ с описанием и причиной ошибки.

Этап 1. Подготовка данных чека

Перед отправкой чека на печать подготовьте данные в соответствии со структурой Check:

val demoCheck = Check(
    id = UUID.randomUUID().toString(),
    docType = SALE,
    employee = "Иванов Иван Иванович",
    printReceipt = true,
    email = "some@email.ru",
    inventPositions = listOf(
        InventPosition(
            name = "Материнская плата AS—Rock H32M R3.0, Socket1150, iH81, 2DDR3, PCI-Ex16, 2SATA2, 2SATA3",
            price = BigDecimal("200"),
            barcode = "2880000023757",
            vatTag = TAG_1103,
            quantity = BigDecimal.ONE,
            measure = PCS,
            inventCode = "2880000023757",
            inventType = INVENTORY
        ),
        InventPosition(
            name = "Жесткий диск",
            price = BigDecimal("100"),
            barcode = "2880000023757",
            vatTag = TAG_1103,
            quantity = BigDecimal.ONE,
            measure = PCS,
            inventCode = "2880000023757"
        )
    ),
    moneyPositions = listOf(MoneyPosition(
        paymentType = CASH,
        sum = BigDecimal("1000")
    )),
    taxMode = COMMON,
    textToPrint = "Текст для дополнительной\nпечати на чеке"
)

Типы чеков

Приложение поддерживает два типа чека:

  • Чек продажи (DocumentType.SALE)
  • Чек возврата (DocumentType.RETURN)

Тип указывается в поле Check.docType.

В версии 8.66 появилась поддержка еще двух типов чека:

  • Чек коррекции прихода (DocumentType.SALE_CORRECTION)
  • Чек коррекции возврата прихода (DocumentType.SALE_RETURN_CORRECTION)

Для успешной регистрации чеков коррекции необходимо передавать дополнительную информацию о коррекции в структуре CorrectionInfo в поле чека correctionInfo.

Оплата

При формировании чека фактический тип оплаты нужно указать в MoneyPosition.

Поддерживается два типа оплаты:

  1. Наличными (PaymentType.CASH)

Укажите в MoneyPosition ту сумму, которую передал покупатель. Если указанная сумма больше, чем сумма по позициям чека, будет автоматически рассчитана сдача.

val demoReturnCheck = Check(
    ...
    moneyPositions = listOf(MoneyPosition(CASH, BigDecimal("1000")))
    ...
)
  1. Безналичными (PaymentType.CARD)

Укажите в MoneyPosition сумму, равную сумме товаров по позициям, иначе при регистрации чека вернется ошибка. При оплате с помощью карты указать сдачу невозможно.

val demoReturnCheck = Check(
    ...
    moneyPositions = listOf(MoneyPosition(CARD, BigDecimal("1000")))
    ...
)

С версии 8.31.0 в безналичной оплате можно указать идентификатор мерчанта, по которому на терминале будет проведена оплата. За эту возможность отвечает поле merchantId. Параметр является необязательным. В одном чеке может быть только одна безналичная оплата с одним идентификатором мерчанта. На стороне МодульКассы проверки на соответствие ID мерчанта и состава чека нет.

  1. Смешанная оплата

Создайте две MoneyPosition с разными типами оплат. Сумма денежных позиций должна совпадать с суммой товаров по позициям, иначе при регистрации чека вернется ошибка. В чеке можно указать не более двух оплат, причем оплата с типом PaymentType.CARD может быть только одна.

val demoReturnCheck = Check(
    ...
    moneyPositions = listOf(
        MoneyPosition(CASH, BigDecimal("300")),
        MoneyPosition(CARD, BigDecimal("700"))
    )
    ...
)

Возврат безналичными

При создании чека возврата с безналичной оплатой есть возможность создать транзакцию на возврат или отменить транзакцию оплаты. За эту возможность отвечает поле refundThroughCancel. В таком случае также желательно будет заполнить дополнительные реквизиты оригинальной транзакции (linkedId, paymentSystemId, extra). Также, если необходимо, чтобы возврат/отмена проходили по чеку продажи, необходимо указать идентификатор чека в рамках МодульКассы modulKassaId. Если реквизиты не заполнить, то их необходимо будет ввести вручную на внешнем платежном терминале.

Этап 2. Отправка чека на печать

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

  1. C помощью CheckManager сформируйте Intent для отправки в приложение МодульКасса. Запустите активити через startActivityForResult() для запуска процесса фискализации чека.
startActivityForResult(
    modulKassaClient.checkManager().createPrintCheckIntent(demoCheck),
    PRINT_CHECK_REQUEST_CODE
)
  1. Обработайте результат стандартным для Android способом.
  • Если операция прошла успешно, в ответ придет чек типа Check с заполненными фискальными данными и результатом платежной транзакции.
  • В случае ошибки, в ответ придет объект типа ResultError с описанием ошибки, ее типом и причиной. С версии 8.40.0 добавлена обработка нового типа ошибок - ANOTHER_REQUEST_IN_PROGRESS. Данная ошибка означает, что запрос на обработку следующего чека поступил во время обработки предыдущего, и нужно дождаться ответа на предыдущий запрос.
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        when (requestCode) {
            PRINT_CHECK_REQUEST_CODE -> handlePrintCheckAnswer(resultCode, data)
        }
    }

    private fun handlePrintCheckAnswer(resultCode: Int, data: Intent?) {
        if (resultCode == Activity.RESULT_OK) {
            val printedCheck = modulKassaClient.checkManager().parsePrintCheckSuccess(data ?: Intent())
        } else {
            val resultError = modulKassaClient.checkManager().parsePrintCheckError(data ?: Intent())
        }
    }