Терминальный проект КиберПлат [open source]
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

612 lines
20KB

  1. /* @file Базовый фискальный регистратор. */
  2. #pragma once
  3. // Qt
  4. #include <Common/QtHeadersBegin.h>
  5. #include <QtCore/QRegExp>
  6. #include <Common/QtHeadersEnd.h>
  7. // STL
  8. #include <numeric>
  9. #include <functional>
  10. // SDK
  11. #include <SDK/Drivers/FR/FiscalFields.h>
  12. #include <SDK/Drivers/FR/FiscalDataTypes.h>
  13. // Modules
  14. #include "Hardware/Common/ASCII.h"
  15. #include "Hardware/Common/HardwareConstants.h"
  16. #include "Hardware/Common/DeviceConfigManager.h"
  17. // Project
  18. #include "Hardware/FR/FRStatusesDescriptions.h"
  19. #include "Hardware/FR/FiscalFieldDescriptions.h"
  20. //--------------------------------------------------------------------------------
  21. /// Режим работы ФР
  22. namespace EFRMode
  23. {
  24. enum Enum
  25. {
  26. Fiscal,
  27. Printer
  28. };
  29. }
  30. //--------------------------------------------------------------------------------
  31. /// Регион (для налоговых ставок).
  32. namespace ERegion
  33. {
  34. enum Enum
  35. {
  36. RF,
  37. KZ
  38. };
  39. }
  40. //--------------------------------------------------------------------------------
  41. /// Типы ФР.
  42. namespace EFRType
  43. {
  44. enum Enum
  45. {
  46. EKLZ, // Старый ФР с ЭКЛЗ
  47. NoEKLZ, // Старый ФР без ЭКЛЗ (для ЕНВД)
  48. FS // Онлайновый ФР с ФН
  49. };
  50. }
  51. //--------------------------------------------------------------------------------
  52. /// Форматы ФФД.
  53. namespace EFFD
  54. {
  55. enum Enum
  56. {
  57. Unknown = -1, // Неизвестная
  58. F10Beta, // 1.0 Beta
  59. F10, // 1.0
  60. F105, // 1.05
  61. F11 // 1.1
  62. };
  63. }
  64. //--------------------------------------------------------------------------------
  65. /// Общие константы ФР.
  66. namespace CFR
  67. {
  68. /// Актуальный ФФД.
  69. const EFFD::Enum ActualFFD = EFFD::F105;
  70. /// Формат представления даты.
  71. const char DateFormat[] = "ddMMyyyy";
  72. /// Формат представления даты-времени для парсинга даты bp TLV-параметра фискального тега.
  73. const char TLVDateTimeFormat[] = "yyyyMMddhhmm";
  74. /// Формат представления даты для вывода в лог.
  75. const char DateLogFormat[] = "dd.MM.yyyy";
  76. /// Формат представления даты для вывода в лог.
  77. const char TimeLogFormat[] = "hh:mm:ss";
  78. /// Формат представления даты для вывода в лог даты и времени.
  79. const char DateTimeLogFormat[] = "dd.MM.yyyy hh:mm:ss";
  80. /// Формат представления даты для вывода в лог даты и времени.
  81. const char DateTimeShortLogFormat[] = "dd.MM.yyyy hh:mm";
  82. /// Количество секунд в сутках.
  83. const int SecsInDay = 24 * 60 * 60;
  84. /// Количество миллисекунд в сутках.
  85. const int MSecsInDay = SecsInDay * 1000;
  86. /// Дата и время одноразового закрытия смены перед применением НДС 20% в 2019 году.
  87. const QDateTime ClosingSessionDTVAT20 = QDateTime(QDate(2018, 12, 31), QTime(23, 57));
  88. /// Формальная дата окончания ФН.
  89. inline QString FSValidityDateOff(const QDate & aDate) { return aDate.addDays(-3).toString(CFR::DateLogFormat); }
  90. /// Срок годности обычной (на 13 месяцев) ФН в днях.
  91. const int SimpleFSValidityDays = 365 + 30;
  92. /// Результаты запроса статуса.
  93. namespace Result
  94. {
  95. const char Error[] = "__ERROR__"; /// Ошибка устройства, либо ответ неверно скомпонован.
  96. const char Fail[] = "__FAIL__"; /// Транспортная/протокольная ошибка.
  97. }
  98. /// ИНН.
  99. namespace INN
  100. {
  101. /// Лицо и его размер.
  102. namespace Person
  103. {
  104. const int Unknown = 0; /// Неизвестное.
  105. const int Legal = 10; /// Юридическое.
  106. const int Natural = 12; /// Физическое.
  107. }
  108. /// Коэффициенты контрольных чисел (КЧ)
  109. namespace Factors
  110. {
  111. const int Legal[] = {2, 4, 10, 3, 5, 9, 4, 6, 8}; /// Коэффициенты КЧ для ИНН юр. лица.
  112. const int Natural1[] = {7, 2, 4, 10, 3, 5, 9, 4, 6, 8}; /// Коэффициенты 1-го КЧ для ИНН физ. лица.
  113. const int Natural2[] = {3, 7, 2, 4, 10, 3, 5, 9, 4, 6, 8}; /// Коэффициенты 2-го КЧ для ИНН физ. лица.
  114. }
  115. /// Делитель для вычисления проверочного числа.
  116. const int Divider = 11;
  117. }
  118. /// Константные данные ФФД.
  119. struct SFFDData
  120. {
  121. int maxUnitNameSize;
  122. QString description;
  123. SFFDData() : maxUnitNameSize(0), description("unknown") {}
  124. SFFDData(int aMaxUnitNameSize, const QString & aDescription) : maxUnitNameSize(aMaxUnitNameSize), description(aDescription) {}
  125. };
  126. class CFFD : public CSpecification<EFFD::Enum, SFFDData>
  127. {
  128. public:
  129. CFFD()
  130. {
  131. add(EFFD::F10Beta, 64, "1.0 Beta");
  132. add(EFFD::F10, 100, "1.0");
  133. add(EFFD::F105, 128, "1.05");
  134. add(EFFD::F11, 128, "1.1");
  135. }
  136. private:
  137. void add(EFFD::Enum aFFD, int aMaxUnitNameSize, const QString & aDescription)
  138. {
  139. append(aFFD, SFFDData(aMaxUnitNameSize, aDescription));
  140. }
  141. };
  142. static CFFD FFD;
  143. /// Фискальные ошибки при выполнении X-отчета.
  144. const TStatusCodes XReportFiscalErrors = getFiscalStatusCodes(SDK::Driver::EWarningLevel::Error)
  145. << DeviceStatusCode::Error::Unknown
  146. << FRStatusCode::Error::EKLZ
  147. << FRStatusCode::Error::FM;
  148. /// Актуальные ставки НДС в России.
  149. const SDK::Driver::TVATs RFActualVATs = SDK::Driver::TVATs() << 18 << 10 << 0;
  150. /// Актуальные ставки НДС в России начиная с 2019 года.
  151. const SDK::Driver::TVATs RFActualVATs20 = SDK::Driver::TVATs() << 20 << 10 << 0;
  152. /// Актуальные ставки НДС в Казахстане.
  153. const SDK::Driver::TVATs KZActualVATs = SDK::Driver::TVATs() << 12 << 0;
  154. /// Таймаут соединения с ОФД, [с].
  155. const int OFDConnectionTimeout = 3 * 60;
  156. /// Смержить данные (СНО, флаги агента).
  157. inline char joinData(const QList<char> & aData)
  158. {
  159. return std::accumulate(aData.begin(), aData.end(), ASCII::NUL, [] (char aResult, char aLocalData) -> char { return aResult | aLocalData; });
  160. }
  161. /// Преобразование байт-массива данных в формат ФФД
  162. inline QString dataToString(const QByteArray & aData, int aBase, int aSize)
  163. {
  164. int index = -1;
  165. while ((aData.size() > ++index) && !aData[index]) {}
  166. int lastIndex = aData.indexOf(ASCII::NUL, index);
  167. QString data = QString(aData.mid(index, lastIndex - index)).simplified();
  168. if (aBase != 10)
  169. {
  170. bool OK;
  171. qulonglong result = data.toULongLong(&OK, aBase);
  172. return OK ? QString("%1").arg(result, aSize, 10, QChar(ASCII::Zero)) : "";
  173. }
  174. else if (QRegExp("^[0-9]+$").indexIn(data) == -1)
  175. {
  176. return "";
  177. }
  178. else if (data.size() <= aSize)
  179. {
  180. return data.rightJustified(aSize, QChar(ASCII::Zero));
  181. }
  182. index = data.indexOf(QRegExp("[1-9]"));
  183. return data.mid(index);
  184. }
  185. inline QString serialToString(const QByteArray & aData, int aBase = 10) { return dataToString(aData, aBase, 16); } /// Серийный номер ККТ (1013)
  186. inline QString FSSerialToString(const QByteArray & aData, int aBase = 10) { return dataToString(aData, aBase, 16); } /// Серийный номер ФН (1041)
  187. inline QString RNMToString(const QByteArray & aData, int aBase = 10) { return dataToString(aData, aBase, 16); } /// РНМ (1037)
  188. inline QString INNToString(const QByteArray & aData, int aBase = 10) { return dataToString(aData, aBase, 10); } /// ИНН оператора перевода (1016)
  189. /// Размеры номеров
  190. const int FDSerialNumberSize = 4; /// Чек в смене (1042)
  191. const int SessionNumberSize = 4; /// Смена (1038)
  192. const int FDNumberSize = 4; /// ФД (1040)
  193. const int FDSignSize = 10; /// ФП (1077)
  194. const int AutomaticNumber = 20; /// Номер автомата (1036)
  195. //--------------------------------------------------------------------------------
  196. /// Минимальный размер TLV-структуры, нет данных.
  197. const int MinTLVSize = 4;
  198. /// TLV-структура
  199. struct STLV
  200. {
  201. int field;
  202. QByteArray data;
  203. STLV(): field(0) {}
  204. STLV(int aField, const QByteArray & aData): field(aField), data(aData) {}
  205. };
  206. /// TLV-пакет
  207. typedef QMap<int, QByteArray> TTLVList;
  208. //--------------------------------------------------------------------------------
  209. /// Налоги.
  210. namespace Taxes
  211. {
  212. struct SData
  213. {
  214. int group;
  215. SDK::Driver::TVAT deviceVAT;
  216. QString description;
  217. SData() : group(0) {}
  218. SData(int aGroup, SDK::Driver::TVAT aDeviceVAT, const QString & aDescription = "") : group(aGroup), deviceVAT(aDeviceVAT), description(aDescription) {}
  219. };
  220. class Data : public CSpecification<SDK::Driver::TVAT, SData>
  221. {
  222. public:
  223. void add(SDK::Driver::TVAT aVAT, int aGroup)
  224. {
  225. append(aVAT, SData(aGroup, aVAT));
  226. }
  227. };
  228. typedef QMap<SDK::Driver::TVAT, SData> TData;
  229. }
  230. /// Скорректировать ставку НДС с 18% на 20% в РФ.
  231. inline void adjustRFVAT(Taxes::TData & aData) { if (aData.contains(18) && isRFVAT20()) { aData.insert(20, aData[18]); aData[20].description.replace("18", "20"); aData.remove(18); }}
  232. //--------------------------------------------------------------------------------
  233. /// Типы оплаты
  234. struct SPayTypeData
  235. {
  236. char value;
  237. QString description;
  238. SPayTypeData() : value(0) {}
  239. SPayTypeData(int aValue, const QString & aDescription) : value(char(aValue)), description(aDescription) {}
  240. };
  241. class CPayTypeDescription : public CDescription<SDK::Driver::EPayTypes::Enum>
  242. {
  243. public:
  244. CPayTypeDescription()
  245. {
  246. using namespace SDK::Driver::EPayTypes;
  247. append(Cash, QString::fromUtf8("НАЛИЧНЫМИ"));
  248. append(EMoney, QString::fromUtf8("КАРТОЙ"));
  249. append(PrePayment, QString::fromUtf8("АВАНС"));
  250. append(PostPayment, QString::fromUtf8("КРЕДИТ"));
  251. append(CounterOffer, QString::fromUtf8("ВСТРЕЧНОЕ ПРЕДОСТАВЛЕНИЕ"));
  252. }
  253. };
  254. static CPayTypeDescription PayTypeDescription;
  255. class PayTypeData : public CSpecification<SDK::Driver::EPayTypes::Enum, SPayTypeData>
  256. {
  257. public:
  258. void add(SDK::Driver::EPayTypes::Enum aPayType, int aValue)
  259. {
  260. append(aPayType, SPayTypeData(aValue, PayTypeDescription[aPayType]));
  261. }
  262. };
  263. //--------------------------------------------------------------------------------
  264. /// Признак способа расчета (1214).
  265. class CPayOffSubjectMethodTypes : public CDescription<char>
  266. {
  267. public:
  268. CPayOffSubjectMethodTypes()
  269. {
  270. using namespace SDK::Driver::EPayOffSubjectMethodTypes;
  271. append(Prepayment100, "ПРЕДОПЛАТА 100%");
  272. append(Prepayment, "ПРЕДОПЛАТА");
  273. append(PostPayment, "АВАНС");
  274. append(Full, "ПОЛНЫЙ РАСЧЕТ");
  275. append(Part, "ЧАСТИЧНЫЙ РАСЧЕТ И КРЕДИТ");
  276. append(CreditTransfer, "ПЕРЕДАЧА В КРЕДИТ");
  277. append(CreditPayment, "ОПЛАТА КРЕДИТА");
  278. }
  279. };
  280. static CPayOffSubjectMethodTypes PayOffSubjectMethodTypes;
  281. //--------------------------------------------------------------------------------
  282. /// Признак предмета расчета (1212).
  283. class CPayOffSubjectTypes : public CDescription<char>
  284. {
  285. public:
  286. CPayOffSubjectTypes()
  287. {
  288. using namespace SDK::Driver::EPayOffSubjectTypes;
  289. append(Unit, "ТОВАР");
  290. append(ExciseUnit, "ПОДАКЦИЗНЫЙ ТОВАР");
  291. append(Job, "РАБОТА");
  292. append(Service, "УСЛУГА");
  293. append(GamblingBet, "СТАВКА АЗАРТНОЙ ИГРЫ");
  294. append(GamblingWin, "ВЫИГРЫШ АЗАРТНОЙ ИГРЫ");
  295. append(LotteryTicket, "ЛОТЕРЕЙНЫЙ БИЛЕТ");
  296. append(LotteryWin, "ВЫИГРЫШ ЛОТЕРЕИ");
  297. append(RIARightsProvision, "ПРЕДОСТАВЛЕНИЕ РИД");
  298. append(Payment, "ПЛАТЕЖ");
  299. append(AgentFee, "АГЕНТСКОЕ ВОЗНАГРАЖДЕНИЕ");
  300. append(Composite, "СОСТАВНОЙ ПРЕДМЕТ РАСЧЕТА");
  301. append(Other, "ИНОЙ ПРЕДМЕТ РАСЧЕТА");
  302. append(PropertyRight, "ИМУЩЕСТВЕННОЕ ПРАВО");
  303. append(NonSalesIncome, "ВНЕРЕАЛИЗАЦИОННЫЙ ДОХОД");
  304. append(InsuranceСontribution, "СТРАХОВЫЕ ВЗНОСЫ");
  305. append(TradeTax, "ТОРГОВЫЙ СБОР");
  306. append(ResortTax, "КУРОРТНЫЙ СБОР");
  307. append(Deposit, "ЗАЛОГ");
  308. }
  309. };
  310. static CPayOffSubjectTypes PayOffSubjectTypes;
  311. typedef QSet<SDK::Driver::EPayOffSubjectTypes::Enum> TPayOffSubjectTypes;
  312. /// Признаки предмета расчета, работа с которыми невозможна с ФН 36 месяцев и СНО == ОСН.
  313. const TPayOffSubjectTypes PayOffSubjectTypesNo36 = TPayOffSubjectTypes()
  314. << SDK::Driver::EPayOffSubjectTypes::Unit
  315. << SDK::Driver::EPayOffSubjectTypes::AgentFee;
  316. //--------------------------------------------------------------------------------
  317. /// Типы систем налогообложения (1062, 1055)
  318. class CTaxSystems : public CBitmapDescription<char>
  319. {
  320. public:
  321. CTaxSystems()
  322. {
  323. using namespace SDK::Driver::ETaxSystems;
  324. append(Main, "ОСН");
  325. append(SimplifiedIncome, "УСН доход");
  326. append(SimplifiedIncomeMinusExpense, "УСН доход - расход");
  327. append(SingleImputedIncome, "ЕНВД");
  328. append(SingleAgricultural, "ЕСН");
  329. append(Patent, "Патент");
  330. }
  331. };
  332. static CTaxSystems TaxSystems;
  333. //--------------------------------------------------------------------------------
  334. /// Признаки платежного агента (1057, 1222).
  335. class CAgentFlags : public CBitmapDescription<char>
  336. {
  337. public:
  338. CAgentFlags()
  339. {
  340. using namespace SDK::Driver::EAgentFlags;
  341. append(BankAgent, "БАНК. ПЛ. АГЕНТ");
  342. append(BankSubagent, "БАНК. ПЛ. СУБАГЕНТ");
  343. append(PaymentAgent, "ПЛ. АГЕНТ");
  344. append(PaymentSubagent, "ПЛ. СУБАГЕНТ");
  345. append(Attorney, "ПОВЕРЕННЫЙ");
  346. append(CommissionAgent, "КОМИССИОНЕР");
  347. append(Agent, "АГЕНТ");
  348. }
  349. };
  350. static CAgentFlags AgentFlags;
  351. //--------------------------------------------------------------------------------
  352. /// Причины перерегистрации (1101).
  353. class CReregistrationCauses : public CBitmapDescription<char>
  354. {
  355. public:
  356. CReregistrationCauses()
  357. {
  358. append(1, "Замена ФН");
  359. append(2, "Замена ОФД");
  360. append(3, "Изменение реквизитов");
  361. append(4, "Изменение настроек ККТ");
  362. }
  363. };
  364. static CReregistrationCauses ReregistrationCauses;
  365. //--------------------------------------------------------------------------------
  366. /// Спецификация флагов ФН.
  367. class CFSFlagData : public CSpecification<char, int>
  368. {
  369. public:
  370. CFSFlagData()
  371. {
  372. append('\x01', FRStatusCode::Error::FSEnd);
  373. append('\x02', FRStatusCode::Warning::FSNearEnd);
  374. append('\x04', FRStatusCode::Error::NeedOFDConnection); // память ФН переполнена (это не таймаут 30 суток)
  375. // append('\x08', FRStatusCode::Warning::OFDNoConnection); // не работает либо имеет другое значение
  376. append('\x80', FRStatusCode::Error::FS);
  377. }
  378. };
  379. static CFSFlagData FSFlagData;
  380. //--------------------------------------------------------------------------------
  381. /// Признаки расчета (1054).
  382. class CPayOffTypes : public CDescription<char>
  383. {
  384. public:
  385. CPayOffTypes()
  386. {
  387. using namespace SDK::Driver::EPayOffTypes;
  388. append(Debit, "ПРИХОД");
  389. append(DebitBack, "ВОЗВРАТ ПРИХОДА");
  390. append(Credit, "РАСХОД");
  391. append(CreditBack, "ВОЗВРАТ РАСХОДА");
  392. }
  393. };
  394. static CPayOffTypes PayOffTypes;
  395. //--------------------------------------------------------------------------------
  396. /// Ставка НДС (1199).
  397. class CVATRates: public CDescription<char>
  398. {
  399. public:
  400. CVATRates::CVATRates()
  401. {
  402. if (isRFVAT20())
  403. {
  404. append(1, "НДС 20%");
  405. append(3, "НДС 20/120");
  406. }
  407. else
  408. {
  409. append(1, "НДС 18%");
  410. append(3, "НДС 18/118");
  411. }
  412. append(2, "НДС 10%");
  413. append(4, "НДС 10/110");
  414. append(5, "НДС 0%");
  415. append(6, "БЕЗ НАЛОГА");
  416. }
  417. };
  418. static CVATRates VATRates;
  419. //--------------------------------------------------------------------------------
  420. /// ПФ ставок НДС.
  421. class CVATTr: public CDescription<SDK::Driver::TVAT>
  422. {
  423. public:
  424. CVATTr::CVATTr()
  425. {
  426. append(20, "НДС 20%");
  427. append(18, "НДС 18%");
  428. append(12, "НДС 12%");
  429. append(10, "НДС 10%");
  430. append( 0, "БЕЗ НАЛОГА");
  431. }
  432. };
  433. static CVATTr VATTr;
  434. //--------------------------------------------------------------------------------
  435. /// Режимы работы.
  436. class COperationModeData: public CSpecification<char, int>
  437. {
  438. public:
  439. COperationModeData() : TrashMask('\x40')
  440. {
  441. #define ADD_OPERATION_MODE(aName) append(SDK::Driver::EOperationModes::aName, CFR::FiscalFields::aName##Mode)
  442. ADD_OPERATION_MODE(Encryption);
  443. ADD_OPERATION_MODE(Autonomous);
  444. ADD_OPERATION_MODE(Automatic);
  445. ADD_OPERATION_MODE(ServiceArea);
  446. ADD_OPERATION_MODE(FixedReporting);
  447. ADD_OPERATION_MODE(Internet);
  448. }
  449. /// Константа для очистки режимов работы от мусора из ФН (ранее это был призак банковского агента).
  450. const char TrashMask;
  451. };
  452. static COperationModeData OperationModeData;
  453. //---------------------------------------------------------------------------
  454. class ConfigCleaner
  455. {
  456. public:
  457. ConfigCleaner(DeviceConfigManager * aConfigManager) : mPerformer(aConfigManager) {}
  458. ~ConfigCleaner()
  459. {
  460. if (mPerformer)
  461. {
  462. QStringList fieldNames = QStringList()
  463. << CFiscalSDK::Cashier
  464. << CFiscalSDK::CashierINN
  465. << CFiscalSDK::UserContact
  466. << CFiscalSDK::TaxSystem
  467. << CFiscalSDK::AgentFlag;
  468. foreach (const QString & fieldName, fieldNames)
  469. {
  470. mPerformer->removeConfigParameter(fieldName);
  471. }
  472. }
  473. }
  474. private:
  475. DeviceConfigManager * mPerformer;
  476. };
  477. //---------------------------------------------------------------------------
  478. class DealerDataManager
  479. {
  480. public:
  481. DealerDataManager(DeviceConfigManager * aConfigManager, const QString & aKey) : mPerformer(aConfigManager), mKey(aKey)
  482. {
  483. if (mPerformer)
  484. {
  485. QVariantMap configData = mPerformer->getConfigParameter(CHardware::ConfigData).toMap();
  486. QString value = mPerformer->getConfigParameter(mKey).toString().simplified();
  487. if (value.isEmpty())
  488. {
  489. mData = value;
  490. }
  491. }
  492. }
  493. ~DealerDataManager()
  494. {
  495. if (mPerformer && mData.isValid())
  496. {
  497. QVariantMap configData = mPerformer->getConfigParameter(CHardware::ConfigData).toMap();
  498. configData.insert(mKey, mData);
  499. mPerformer->setConfigParameter(CHardware::ConfigData, configData);
  500. }
  501. }
  502. void setValue(const QString & aValue)
  503. {
  504. mData = aValue;
  505. }
  506. private:
  507. DeviceConfigManager * mPerformer;
  508. QVariant mData;
  509. QString mKey;
  510. };
  511. }
  512. //--------------------------------------------------------------------------------