В связи с очередным злоебучим (по моему мнению) законом 54-ФЗ пришлось клиентам резко обновлять кассы и выкидывать из своего кармана 40 штук, что бы поменять шило на полноценный анальный зонд.

Я бы да же этому был бы рад из-за возможности заработать на обновлении, но вот что-то радоваться чужому горю вообще не фонтан.

В общем, подключаем приблуду по микро USB (ком-порта у меня в ноуте нет), заходим на оф. сайт, качаем и драйвер «USB VCOM«.

В дальнейшем оттуда нас будет так же интересовать руководство программиста, почитайте.

С драйвером мы будем работать через ActiveX (старовато, ну а херли делать. Радует немного, что Qt поддерживает эту технологию).

Создаём новый проект (у меня консольное приложение), создаём в проекте папку activex, в неё кидаем саму либо. В неё же в дальнейшем сгенерируем заголовочник и исходник.

C:\Projects\KKT-PAYONLINE-01-FA\KKT-PAYONLINE-01-FA\activex
C:\Qt\5.9.2\mingw53_32\bin\dumpcpp DrvFR.dll

Пути замените на свои. Кстати, в путях не используйте кириллицу. Если есть пробелы, то возьмите в кавычки. 

В папке должно появится два файла: «drvfrlib.h» и «drvfrlib.cpp» — они то нам и нужны, добавляем их в проект.

В настройках проекта («*.pro» файле) подключаем пару модулей 

QT += axcontainer widgets

Они нам нужны для работы с ActiveX.

Наша цель сейчас подключиться к фискальнику, проверить кассовую смену (если истекла — печатаем z-отчёт, если закрыта — открываем, иначе нам ничего не сделать больше), делаем продажу, отключаемся.

#include <QCoreApplication>
#include <QApplication>
#include <QDebug>

#include "activex/drvfrlib.h" //-- Подключаем заголовочник либы
using namespace DrvFRLib; //-- Сразу задаём в каком пространстве имён будем работать

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    qDebug()<<"Started!";

    DrvFR * fr = new DrvFR(0, 0); //-- Создаём класс драйвера

    //-- Подключаемся к поебени
    fr->SetConnectionType(0); //-- Метод её подключения к нам, 0-локально
    fr->SetProtocolType(0); //-- Вервия протокола, 0-стандартная
    fr->SetComputerName("HCSTerminal"); //-- Задаём имя, под которым она нас будет знать
    fr->SetTimeout(0); //-- Сколько будет ждать ответа (рекомендую 0, иначе будут сюрпризы)
    fr->SetComNumber(3); //-- Устанавливаем на каком com-порту висит (COM3 - в моём случае)
    fr->SetBaudRate(115200); //-- Устанавливаем скорость подключения (115200 - дефолтная)
    fr->SetPassword(30); //-- Задаём пароль оператора (30 - админ по дефолту)
    qDebug()<<"Connect"<<fr->Connect();


    //-- Проверим состояние
    fr->SetPassword(30); //-- Задаём пароль оператора (30 - админ)
    qDebug()<<"EcrStatus"<< fr->GetECRStatus();
    qDebug()<<"EcrMode"<< fr->ECRMode(); //-- Подробнее на стр. 64. Сейчас нас интересует: 4-смена закрыта, 3-смена открыта, но кончилась (нужно напечатать Z-отчёт с гашением)
    qDebug()<<fr->ECRModeDescription();



    //-- Печатаем z-отчёт с гашением
    fr->SetPassword(30); //-- Задаём пароль оператора (30 - админ)
    fr->PrintReportWithCleaning();
  
      //-- Откроем новую смену
    fr->SetPassword(30); //-- Задаём пароль оператора (30 - админ)
    fr->OpenSession(); //-- открывает смену
    

    //-- Начинаем формирование нового чека
    fr->SetCheckType(0); //-- Устанавливаем тип нового чека, 0-продажа
    fr->SetPassword(30); //-- Задаём пароль оператора (30 - админ)
    qDebug()<<"OpenCheck"<<fr->OpenCheck();
    qDebug()<<"Result"<<fr->ResultCode()<<fr->ResultCodeDescription();
    
    //-- Добавляем позиции продажи
    fr->SetQuantity(1.0); //-- Устанавливаем количество
    fr->SetPrice(3); //-- Устанавливаем цену
    fr->SetStringForPrinting("Проверка печати"); //-- Описание товара
    fr->SetPassword(30); //-- Задаём пароль оператора (30 - админ)
    fr->SetDepartment(1); //-- Задаём отдел продажи (если хуёвин несколько, что бы отличать)
    qDebug()<<"Sale result"<<fr->Sale();


    //-- Завершаем формирование чека, после закрытия выйдет на печать
    fr->SetSumm1(3); //-- Сумма оплаты наличными, если несколько типов оплат, то соответственно fr->setSumm2(); Если сумма по всем позициям будет не совпадать, то на чеке автоматически будет выведена сдача.
    fr->SetStringForPrinting("==="); //-- В чеке будет двойная линия
    fr->SetPassword(30); //-- Задаём пароль оператора (30 - админ)
    qDebug()<<"Check status"<<fr->CloseCheck();
    
    //-- Отключаемся
    fr->Disconnect();

    delete fr;

    qDebug()<<"Ended!";
    return a.exec();
}

Вот как-то так выглядит алгоритм печати чека (продажи). Если какой-то метод вернёт что-то отличное от 0, значит произошла ошибка.

Разумеется каждый раз печатать z-отчёт и открывать смену не нужно, как и писал выше, нужно проверять сначала, но для примера не стал (лень). 

Если будет ругаться на «enum TBarcodePrintType«, то просто закомментите в двух местах.

А вот теперь попробуем  продать товар с ценой, где есть копейки:

....
fr->SetPrice(3.23);
....
fr->SetSumm1(3.23);
....

Облом, да? Дробная часть у цена откинулась наглухо. Дело в том, что в документации у цены стоит тип «Cyrrency» (Денежный).
Так как в C++ явно такого типа нет, то Qt преобразовал его в qlonglong.

Currency широко используется в Delphi для хранения денежных единиц. Представляет он из себя, по сути, тип с фиксированной точкой, а храниться как 8 байтное целое, где 4 младших разряда — неявная дробная часть. Подробнее.

Перевод из double (c++) в Currency (Delphi) прост:

double d = 12.3456;
qlonglong currency = (round(d*10000)/10000)*10000;

Пояснение:
(round(d*10000)/10000) так как у Currency значащими являются только 4 знака после запятой, то так мы округляем до них.
*10000 — тем самым приводим к виду Currency.

Кстати, здесь не используется банковское округление (когда 0.5 считается в меньшую сторону).

Что бы каждый раз не возиться, предлагаю создать простенький класс TCurrency, в котором заодно перегрузим основные операции сложение, вычитания:

class TCurrency
{
public:
    explicit TCurrency(double d) { _val = fromDouble(d); }
    explicit TCurrency(qlonglong ll) { _val = ll; }
    static inline qlonglong fromDouble(double d) { return (round(d*10000)/10000)*10000; }

    TCurrency& operator =(double d) { _val = fromDouble(d); return *this; }

    const TCurrency operator +(double d) { return TCurrency(_val+fromDouble(d)); }
    const TCurrency operator +(const TCurrency &tc) { return TCurrency(_val+tc._val); }
    TCurrency& operator +=(double d) { _val += fromDouble(d); return *this; }
    TCurrency& operator +=(const TCurrency &tc) { _val += tc._val; return *this; }

    const TCurrency operator -(double d) { return TCurrency(_val-fromDouble(d)); }
    const TCurrency operator -(const TCurrency &tc) { return TCurrency(_val-tc._val); }
    TCurrency& operator -=(double d) { _val -= fromDouble(d); return *this; }
    TCurrency& operator -=(const TCurrency &tc) { _val -= tc._val; return *this; }

    operator qlonglong() { return _val; }

private:
    qlonglong _val;

};

В итоге:

fr->SetPrice(TCurrency(3.23));
или 
TCurrency cr;
cr = 2.23;
cr += 0.1;
fr->SetPrice(cr);

Вот как-то так.