Терминальный проект КиберПлат [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.

801 lines
26KB

  1. /* @file Онлайн ФР семейства Штрих. */
  2. #include "ShtrihOnlineFRBase.h"
  3. //--------------------------------------------------------------------------------
  4. template class ShtrihOnlineFRBase<ShtrihTCPFRBase>;
  5. template class ShtrihOnlineFRBase<ShtrihSerialFRBase>;
  6. using namespace SDK::Driver;
  7. using namespace ProtocolUtils;
  8. //--------------------------------------------------------------------------------
  9. template<class T>
  10. ShtrihOnlineFRBase<T>::ShtrihOnlineFRBase()
  11. {
  12. // данные семейства ФР
  13. mSupportedModels = getModelList();
  14. mDeviceName = CShtrihFR::Models::OnlineDefault;
  15. setConfigParameter(CHardwareSDK::CanOnline, true);
  16. mNotEnableFirmwareUpdating = false;
  17. mPrinterStatusEnabled = true;
  18. mIsOnline = true;
  19. mOFDFiscalParameters.remove(CFR::FiscalFields::Cashier);
  20. mOFDFiscalParameters.remove(CFR::FiscalFields::TaxSystem);
  21. mNeedReceiptProcessingOnCancel = true;
  22. mSetCustomFields = ASCII::NUL;
  23. mSetCustomFieldsCorrect = false;
  24. setConfigParameter(CHardwareSDK::FR::CanWithoutPrinting, true);
  25. // типы оплаты
  26. mPayTypeData.add(EPayTypes::Cash, 1);
  27. mPayTypeData.add(EPayTypes::EMoney, 2);
  28. mPayTypeData.add(EPayTypes::PrePayment, 14);
  29. mPayTypeData.add(EPayTypes::PostPayment, 15);
  30. mPayTypeData.add(EPayTypes::CounterOffer, 16);
  31. // данные команд
  32. mCommandData.add(CShtrihOnlineFR::Commands::Service, CShtrihFR::Timeouts::Default, false);
  33. // ошибки
  34. mErrorData = PErrorData(new CShtrihOnlineFR::Errors::Data);
  35. mUnprocessedErrorData.add(CShtrihOnlineFR::Commands::FS::StartFiscalTLVData, CShtrihOnlineFR::Errors::NoRequiedDataInFS);
  36. mUnprocessedErrorData.add(CShtrihOnlineFR::Commands::FS::GetFiscalTLVData, CShtrihFR::Errors::BadModeForCommand);
  37. }
  38. //--------------------------------------------------------------------------------
  39. template<class T>
  40. QStringList ShtrihOnlineFRBase<T>::getModelList()
  41. {
  42. return CShtrihFR::Models::CData().getNonEjectorModels(true);
  43. }
  44. //--------------------------------------------------------------------------------
  45. template<class T>
  46. bool ShtrihOnlineFRBase<T>::setNotPrintDocument(bool aEnabled, bool /*aZReport*/)
  47. {
  48. return !aEnabled || setFRParameter(CShtrihOnlineFR::FRParameters::NotPrintDocument, true);
  49. }
  50. //--------------------------------------------------------------------------------
  51. template<class T>
  52. bool ShtrihOnlineFRBase<T>::updateParameters()
  53. {
  54. if (!ShtrihFRBase<T>::updateParameters())
  55. {
  56. return false;
  57. }
  58. mNotEnableFirmwareUpdating = containsDeviceParameter(CDeviceData::SDCard) && !enableFirmwareUpdating();
  59. QByteArray addressData;
  60. QByteArray portData;
  61. getFRParameter(CShtrihOnlineFR::FRParameters::OFDAddress, addressData);
  62. getFRParameter(CShtrihOnlineFR::FRParameters::OFDPort, portData);
  63. mOFDDataError = !checkOFDData(addressData, revert(portData));
  64. // Формировать QR-код средствами ФР
  65. setFRParameter(CShtrihOnlineFR::FRParameters::QRCode, true);
  66. // Печатать сквозной номер документа
  67. setFRParameter(CShtrihOnlineFR::FRParameters::PrintEndToEndNumber, true);
  68. // Печатать данные ОФД в чеках
  69. setFRParameter(CShtrihOnlineFR::FRParameters::PrintOFDData, true);
  70. // Печатать все реквизиты пользователя в чеках
  71. setFRParameter(CShtrihOnlineFR::FRParameters::PrintUserData, CShtrihOnlineFR::PrintFullUserData);
  72. // Печатать фискальные теги, вводимые на платеже
  73. setFRParameter(CShtrihOnlineFR::FRParameters::PrintCustomFields, true);
  74. if (!isFiscal())
  75. {
  76. return true;
  77. }
  78. if (mOperatorPresence && !setCashier())
  79. {
  80. QByteArray data;
  81. if (!getFRParameter(CShtrihOnlineFR::FRParameters::Cashier, data, CShtrihOnlineFR::CashierSeries) || data.simplified().isEmpty())
  82. {
  83. toLog(LogLevel::Error, mDeviceName + ": Cannot work with invalid cashier");
  84. return false;
  85. }
  86. }
  87. QByteArray data;
  88. mSetCustomFieldsCorrect = getFRParameter(CShtrihOnlineFR::FRParameters::SetCustomFields, data) && !data.isEmpty();
  89. if (!mSetCustomFieldsCorrect)
  90. {
  91. toLog(LogLevel::Error, mDeviceName + ": Cannot get custom fields data");
  92. }
  93. else
  94. {
  95. mSetCustomFields = data[0];
  96. }
  97. if (getFRParameter(CShtrihOnlineFR::FRParameters::AutomaticNumber, data) && !clean(data).isEmpty())
  98. {
  99. setDeviceParameter(CDeviceData::FR::AutomaticNumber, clean(data));
  100. }
  101. if (!processCommand(CShtrihOnlineFR::Commands::FS::GetFiscalizationTotal, &data) || (data.size() <= 46))
  102. {
  103. toLog(LogLevel::Error, mDeviceName + ": Failed to get fiscalization total");
  104. return false;
  105. }
  106. if (!checkTaxSystems(data[40]) || !checkOperationModes(data[41]))
  107. {
  108. return false;
  109. }
  110. if (processCommand(CShtrihOnlineFR::Commands::FS::StartFiscalTLVData, data.mid(43, 4)))
  111. {
  112. bool needWaitReady = getLongStatus() && (mMode == CShtrihFR::InnerModes::SessionClosed) || (mMode == CShtrihFR::InnerModes::DataEjecting);
  113. if (needWaitReady)
  114. {
  115. waitReady(CShtrihOnlineFR::ReadyWaiting);
  116. }
  117. TGetFiscalTLVData getFiscalTLVData = [&] (QByteArray & aData) -> TResult { TResult result = processCommand(CShtrihOnlineFR::Commands::FS::GetFiscalTLVData, &aData);
  118. aData = aData.mid(3); return result; };
  119. TProcessTLVAction checkingAgentFlags = [&] (const CFR::STLV & aTLV) -> bool { return (aTLV.field != CFR::FiscalFields::AgentFlagsReg) || checkAgentFlags(aTLV.data[0]); };
  120. processTLVData(getFiscalTLVData, checkingAgentFlags);
  121. }
  122. else if (isErrorUnprocessed(mLastCommand, mLastError))
  123. {
  124. mProcessingErrors.pop_back();
  125. mLastError = 0;
  126. }
  127. return true;
  128. }
  129. //---------------------------------------------------------------------------
  130. template <class T>
  131. bool ShtrihOnlineFRBase<T>::setCashier()
  132. {
  133. QString cashier;
  134. if (!mFFEngine.checkCashier(cashier))
  135. {
  136. return false;
  137. }
  138. if (!setFRParameter(CShtrihOnlineFR::FRParameters::Cashier, cashier, CShtrihOnlineFR::CashierSeries))
  139. {
  140. toLog(LogLevel::Warning, mDeviceName + QString(": Failed to set cashier fiscal field (%2)").arg(cashier));
  141. return false;
  142. }
  143. return true;
  144. }
  145. //---------------------------------------------------------------------------
  146. template <class T>
  147. int ShtrihOnlineFRBase<T>::getSessionNumber()
  148. {
  149. QByteArray data;
  150. if (processCommand(CShtrihOnlineFR::Commands::FS::GetSessionParameters, &data) && (data.size() > 5))
  151. {
  152. return int(uchar(data[4])) | (int(uchar(data[5])) << 8);
  153. }
  154. return 0;
  155. }
  156. //--------------------------------------------------------------------------------
  157. template<class T>
  158. bool ShtrihOnlineFRBase<T>::setTaxValue(TVAT /*aVAT*/, int /*aGroup*/)
  159. {
  160. return false;
  161. }
  162. //--------------------------------------------------------------------------------
  163. template<class T>
  164. bool ShtrihOnlineFRBase<T>::getStatus(TStatusCodes & aStatusCodes)
  165. {
  166. if (!ShtrihFRBase<T>::getStatus(aStatusCodes))
  167. {
  168. return false;
  169. }
  170. uint bootFirmware = getDeviceParameter(CDeviceData::BootFirmware).toUInt();
  171. if ((mModel != CShtrihFR::Models::ID::MStarTK2) && (bootFirmware < CShtrihOnlineFR::MinBootFirmware))
  172. {
  173. aStatusCodes.insert(DeviceStatusCode::Warning::BootFirmware);
  174. }
  175. if (mPrinterStatusEnabled)
  176. {
  177. TResult result = processCommand(CShtrihOnlineFR::Commands::GetPrinterStatus);
  178. if (result == CommandResult::Device) aStatusCodes.insert(PrinterStatusCode::Error::PrinterFR);
  179. else if (result == CommandResult::Answer) aStatusCodes.insert(DeviceStatusCode::Warning::OperationError);
  180. else if (!result)
  181. {
  182. return false;
  183. }
  184. }
  185. if (mNotEnableFirmwareUpdating)
  186. {
  187. aStatusCodes.insert(FRStatusCode::Warning::FirmwareUpdating);
  188. }
  189. QByteArray data = performStatus(aStatusCodes, CShtrihOnlineFR::Commands::FS::GetOFDInterchangeStatus, 6);
  190. if (data == CFR::Result::Fail)
  191. {
  192. return false;
  193. }
  194. else if (data != CFR::Result::Error)
  195. {
  196. int OFDNotSentCount = (data.size() < 7) ? -1 : revert(data.mid(5, 2)).toHex().toUShort(0, 16);
  197. checkOFDNotSentCount(OFDNotSentCount, aStatusCodes);
  198. }
  199. data = performStatus(aStatusCodes, CShtrihOnlineFR::Commands::FS::GetStatus, 7);
  200. if (data == CFR::Result::Fail)
  201. {
  202. return false;
  203. }
  204. else if (data != CFR::Result::Error)
  205. {
  206. checkFSFlags(data[7], aStatusCodes);
  207. }
  208. return true;
  209. }
  210. //--------------------------------------------------------------------------------
  211. template<class T>
  212. void ShtrihOnlineFRBase<T>::processDeviceData()
  213. {
  214. ShtrihFRBase<T>::processDeviceData();
  215. QByteArray data;
  216. if (processCommand(CShtrihOnlineFR::Commands::FS::GetStatus, &data))
  217. {
  218. mFSSerialNumber = CFR::FSSerialToString(data.mid(13, 16));
  219. int FDCount = revert(data.right(4)).toHex().toUInt(0, 16);
  220. setDeviceParameter(CDeviceData::FR::FiscalDocuments, FDCount);
  221. }
  222. if ((mModel != CShtrihFR::Models::ID::MStarTK2) && processCommand(CShtrihOnlineFR::Commands::Service, CShtrihOnlineFR::Service::BootFirmware, &data))
  223. {
  224. if (data == QByteArray(data.size(), ASCII::NUL))
  225. {
  226. toLog(LogLevel::Warning, mDeviceName + ": Failed to get boot firmware version due to the answer is NUL.");
  227. }
  228. else
  229. {
  230. uint bootFirmware = revert(data.mid(2)).toHex().toUInt(0, 16);
  231. setDeviceParameter(CDeviceData::BootFirmware, bootFirmware);
  232. }
  233. }
  234. if (processCommand(CShtrihOnlineFR::Commands::FS::GetValidity, &data))
  235. {
  236. QDate date = QDate::fromString(hexToBCD(data.mid(3, 3)).prepend("20"), CShtrihOnlineFR::DateFormat);
  237. if (date.isValid())
  238. {
  239. setDeviceParameter(CDeviceData::FS::ValidityData, CFR::FSValidityDateOff(date));
  240. }
  241. // признак фискализированности ККМ
  242. mFiscalized = data[7];
  243. }
  244. if (processCommand(CShtrihOnlineFR::Commands::FS::GetVersion, &data) && (data.size() > 19))
  245. {
  246. setDeviceParameter(CDeviceData::FS::Version, QString("%1, type %2").arg(clean(data.mid(3, 16)).data()).arg(data[19] ? "serial" : "debug"));
  247. }
  248. using namespace CShtrihOnlineFR::FRParameters;
  249. if (getFRParameter(FFDFR, data))
  250. {
  251. mFFDFR = EFFD::Enum(int(data[0]));
  252. mFFDFS = mFFDFR;
  253. }
  254. if (getFRParameter(SerialNumber, data))
  255. {
  256. mSerial = CFR::serialToString(data);
  257. }
  258. if (getFRParameter(INN, data)) mINN = CFR::INNToString(data);
  259. if (getFRParameter(RNM, data)) mRNM = CFR::RNMToString(data);
  260. #define SET_LCONFIG_FISCAL_FIELD(aName) QString aName##Log = mFFData.getTextLog(CFR::FiscalFields::aName); \
  261. if (getFRParameter(aName, data)) { mFFEngine.setLConfigParameter(CFiscalSDK::aName, data); \
  262. QString value = mFFEngine.getConfigParameter(CFiscalSDK::aName, data).toString(); \
  263. toLog(LogLevel::Normal, mDeviceName + QString(": Add %1 = \"%2\" to config data").arg(aName##Log).arg(value)); } \
  264. else toLog(LogLevel::Error, mDeviceName + QString(": Failed to add %1 to config data").arg(aName##Log));
  265. #define SET_BCONFIG_FISCAL_FIELD(aName) if (value & CShtrihOnlineFR::OperationModeMask::aName) { mFFEngine.setConfigParameter(CFiscalSDK::aName, 1); \
  266. toLog(LogLevel::Normal, mDeviceName + QString(": Add %1 = 1 to config data").arg(mFFData.getTextLog(CFR::FiscalFields::aName))); }
  267. SET_LCONFIG_FISCAL_FIELD(FTSURL);
  268. SET_LCONFIG_FISCAL_FIELD(OFDURL);
  269. SET_LCONFIG_FISCAL_FIELD(OFDName);
  270. SET_LCONFIG_FISCAL_FIELD(LegalOwner);
  271. SET_LCONFIG_FISCAL_FIELD(PayOffAddress);
  272. SET_LCONFIG_FISCAL_FIELD(PayOffPlace);
  273. if (getFRParameter(OperationModes, data) && !data.isEmpty())
  274. {
  275. char value = data[0];
  276. SET_BCONFIG_FISCAL_FIELD(LotteryMode);
  277. SET_BCONFIG_FISCAL_FIELD(GamblingMode);
  278. SET_BCONFIG_FISCAL_FIELD(ExcisableUnitMode);
  279. SET_BCONFIG_FISCAL_FIELD(InAutomateMode);
  280. }
  281. if (mFFEngine.getConfigParameter(CFiscalSDK::InAutomateMode).toInt() && mOperatorPresence)
  282. {
  283. mWrongFiscalizationSettings = true;
  284. toLog(LogLevel::Error, mDeviceName + ": There is \"In automate mode\" flag and operator is present.");
  285. }
  286. removeDeviceParameter(CDeviceData::SDCard);
  287. if ((mModel != CShtrihFR::Models::ID::MStarTK2) && getFRParameter(SD::Status, data))
  288. {
  289. if (data[0] == CShtrihOnlineFR::SDNotConnected)
  290. {
  291. setDeviceParameter(CDeviceData::SDCard, CDeviceData::NotConnected);
  292. }
  293. else if (data[0])
  294. {
  295. setDeviceParameter(CDeviceData::Error, uchar(data[0]), CDeviceData::SDCard);
  296. }
  297. else
  298. {
  299. auto getSDData = [&] (const CShtrihFR::FRParameters::SData & aData) -> uint { if (!getFRParameter(aData, data)) return 0;
  300. return revert(data).toHex().toUInt(0, 16); };
  301. QString SDCardData = QString("cluster %1 KB, space %2 MB (%3 MB free), io errors %4, retry count %5")
  302. .arg(getSDData(SD::ClusterSize) / 1024)
  303. .arg(getSDData(SD::TotalSize) / CShtrihOnlineFR::SectorsInMB)
  304. .arg(getSDData(SD::FreeSize) / CShtrihOnlineFR::SectorsInMB)
  305. .arg(getSDData(SD::IOErrors))
  306. .arg(getSDData(SD::RetryCount));
  307. setDeviceParameter(CDeviceData::SDCard, SDCardData);
  308. }
  309. }
  310. mCanProcessZBuffer = mModelData.ZBufferSize && containsDeviceParameter(CDeviceData::SDCard);
  311. checkDateTime();
  312. }
  313. //--------------------------------------------------------------------------------
  314. template<class T>
  315. bool ShtrihOnlineFRBase<T>::checkFirmwareUpdatingData(const CShtrihFR::FRParameters::SData & aData, int aValue, const QString & aLogData, bool & aNeedReboot)
  316. {
  317. QByteArray data;
  318. if (!getFRParameter(aData, data))
  319. {
  320. toLog(LogLevel::Error, QString("%1: Failed to get %2 updating data").arg(mDeviceName).arg(aLogData));
  321. return false;
  322. }
  323. int value = revert(data).toHex().toUShort(0, 16);
  324. if (value != aValue)
  325. {
  326. toLog(LogLevel::Normal, QString("%1: Need reboot due to %2 updating data").arg(mDeviceName).arg(aLogData));
  327. aNeedReboot = true;
  328. if (!setFRParameter(aData, aValue))
  329. {
  330. toLog(LogLevel::Error, QString("%1: Failed to set %2 updating data").arg(mDeviceName).arg(aLogData));
  331. return false;
  332. }
  333. }
  334. return true;
  335. }
  336. //--------------------------------------------------------------------------------
  337. template<class T>
  338. bool ShtrihOnlineFRBase<T>::enableFirmwareUpdating()
  339. {
  340. using namespace CShtrihOnlineFR::FRParameters;
  341. using namespace CShtrihOnlineFR::FirmwareUpdating;
  342. bool needReboot = false;
  343. if (!checkFirmwareUpdatingData(FirmwareUpdating::Working, Working, "working", needReboot) ||
  344. !checkFirmwareUpdatingData(FirmwareUpdating::Interval, Interval, "interval", needReboot) ||
  345. !checkFirmwareUpdatingData(FirmwareUpdating::Enabling, Enabling, "enabling", needReboot) ||
  346. !checkFirmwareUpdatingData(FirmwareUpdating::Single, Single, "single", needReboot))
  347. {
  348. return false;
  349. }
  350. return true;
  351. // до выяснения причин невозможности установки параметров обновления прошивки
  352. /*
  353. mNeedReboot = needReboot && (mModel == CShtrihFR::Models::ID::PayVKP80KFA);
  354. return !needReboot || mNeedReboot || reboot();
  355. */
  356. }
  357. //--------------------------------------------------------------------------------
  358. template<class T>
  359. bool ShtrihOnlineFRBase<T>::reboot()
  360. {
  361. QVariantMap portConfig = mIOPort->getDeviceConfiguration();
  362. TResult result = processCommand(CShtrihOnlineFR::Commands::Service, CShtrihOnlineFR::Service::Reboot);
  363. CommandResult::TResults errors = CommandResult::TResults()
  364. << CommandResult::Port
  365. << CommandResult::Transport
  366. << CommandResult::Protocol
  367. << CommandResult::Driver
  368. << CommandResult::Device;
  369. if (!errors.contains(result))
  370. {
  371. mIOPort->close();
  372. SleepHelper::msleep(CShtrihOnlineFR::RebootPause);
  373. if (mIOPort->getType() == EPortTypes::TCP)
  374. {
  375. portConfig.insert(CHardware::Port::OpeningTimeout, CShtrihOnlineFR::TCPReopeningTimeout);
  376. mIOPort->setDeviceConfiguration(portConfig);
  377. }
  378. result = mIOPort->open();
  379. mIOPort->setDeviceConfiguration(portConfig);
  380. }
  381. return result;
  382. }
  383. //--------------------------------------------------------------------------------
  384. template<class T>
  385. void ShtrihOnlineFRBase<T>::setErrorFlags()
  386. {
  387. if (mErrorData->value(mLastError).type == FRError::EType::FS)
  388. {
  389. mFSError = true;
  390. }
  391. }
  392. //--------------------------------------------------------------------------------
  393. template<class T>
  394. void ShtrihOnlineFRBase<T>::checkSalesName(QString & aName)
  395. {
  396. aName = aName.leftJustified(CShtrihFR::FixedStringSize, QChar(ASCII::Space), false);
  397. }
  398. //--------------------------------------------------------------------------------
  399. template<class T>
  400. bool ShtrihOnlineFRBase<T>::sale(const SUnitData & aUnitData, EPayOffTypes::Enum aPayOffType)
  401. {
  402. if (mModelData.date < CShtrihOnlineFR::MinFWDate::V2)
  403. {
  404. return ShtrihFRBase<T>::sale(aUnitData, aPayOffType);
  405. }
  406. char taxIndex = char(mTaxData[aUnitData.VAT].group);
  407. char section = (aUnitData.section == -1) ? CShtrihFR::SectionNumber : char(aUnitData.section);
  408. QByteArray sum = getHexReverted(aUnitData.sum, 5, 2);
  409. QString name = aUnitData.name;
  410. QByteArray commandData;
  411. commandData.append(char(aPayOffType)); // тип операции (1054)
  412. commandData.append(getHexReverted(1, 6, 6)); // количество (1023)
  413. commandData.append(sum); // цена (1079)
  414. commandData.append(sum); // сумма операций (1023)
  415. commandData.append(CShtrihOnlineFR::FiscalTaxData); // налог (1102..1107)
  416. commandData.append(taxIndex); // налоговая ставка
  417. commandData.append(section); // отдел
  418. commandData.append(char(aUnitData.payOffSubjectMethodType)); // признак способа расчета (1214)
  419. commandData.append(char(aUnitData.payOffSubjectType)); // признак предмета расчета (1212)
  420. commandData.append(mCodec->fromUnicode(name)); // наименование товара (1030)
  421. if (!processCommand(CShtrihOnlineFR::Commands::FS::Sale, commandData))
  422. {
  423. toLog(LogLevel::Error, QString("%1: Failed to sale for %2 (%3, VAT = %4), feed, cut and exit").arg(mDeviceName).arg(aUnitData.sum, 0, 'f', 2).arg(name).arg(aUnitData.VAT));
  424. return false;
  425. }
  426. return setOFDParametersOnSale(aUnitData);
  427. }
  428. //--------------------------------------------------------------------------------
  429. template<class T>
  430. bool ShtrihOnlineFRBase<T>::closeDocument(double aSum, EPayTypes::Enum aPayType)
  431. {
  432. if (mModelData.date < CShtrihOnlineFR::MinFWDate::V2)
  433. {
  434. return ShtrihFRBase<T>::closeDocument(aSum, aPayType);
  435. }
  436. QByteArray commandData;
  437. for (int i = 1; i <= CShtrihOnlineFR::PayTypeQuantity; ++i)
  438. {
  439. double sum = (i == mPayTypeData[aPayType].value) ? aSum : 0;
  440. commandData.append(getHexReverted(sum, 5, 2)); // сумма
  441. }
  442. char taxSystem = char(mFFEngine.getConfigParameter(CFiscalSDK::TaxSystem).toInt());
  443. commandData.append(ASCII::NUL); // Округление до рубля
  444. commandData.append(CShtrihOnlineFR::ClosingFiscalTaxes); // налоги
  445. commandData.append(taxSystem); // СНО
  446. if (!processCommand(CShtrihOnlineFR::Commands::FS::CloseDocument, commandData))
  447. {
  448. toLog(LogLevel::Error, "ShtrihFR: Failed to close document, feed, cut and exit");
  449. return false;
  450. }
  451. return true;
  452. }
  453. //--------------------------------------------------------------------------------
  454. template<class T>
  455. bool ShtrihOnlineFRBase<T>::performFiscal(const QStringList & aReceipt, const SPaymentData & aPaymentData, quint32 * aFDNumber)
  456. {
  457. if ((mModelData.date < CShtrihOnlineFR::MinFWDate::V2) || (mModel == CShtrihFR::Models::ID::MStarTK2))
  458. {
  459. char taxSystem = char(aPaymentData.taxSystem);
  460. if ((taxSystem != ETaxSystems::None) && (mTaxSystems.size() != 1) && !setFRParameter(CShtrihOnlineFR::FRParameters::TaxSystem, taxSystem))
  461. {
  462. toLog(LogLevel::Error, QString("%1: Failed to set taxation system %2 (%3)").arg(mDeviceName).arg(toHexLog(taxSystem)).arg(CFR::TaxSystems[taxSystem]));
  463. return false;
  464. }
  465. }
  466. if (mSetCustomFieldsCorrect && isFS36() && (aPaymentData.taxSystem == ETaxSystems::Main))
  467. {
  468. QStringList noPayLog;
  469. foreach (const SUnitData & unitData, aPaymentData.unitDataList)
  470. {
  471. if (CFR::PayOffSubjectTypesNo36.contains(unitData.payOffSubjectType))
  472. {
  473. noPayLog << QString("%1 (%2, %3)").arg(unitData.name).arg(unitData.sum).arg(CFR::PayOffSubjectTypes[char(unitData.payOffSubjectType)]);
  474. }
  475. }
  476. char newSetCustomFields = mSetCustomFields | CShtrihOnlineFR::FRParameters::DontSendPayOffSubjectType;
  477. if (!noPayLog.isEmpty() && (mSetCustomFields != newSetCustomFields))
  478. {
  479. QString log = mDeviceName + QString(": Failed to make fiscal document due to cannot sale unit(s): %1, because ").arg(noPayLog.join("; "));
  480. if (mFFDFS > EFFD::F105)
  481. {
  482. toLog(LogLevel::Error, log + QString("FFD FS = %1 > 1.05").arg(CFR::FFD[mFFDFS].description));
  483. return false;
  484. }
  485. else if (!mFiscalServerPresence)
  486. {
  487. toLog(LogLevel::Error, log + "it is not fiscal server");
  488. return false;
  489. }
  490. else if (!setFRParameter(CShtrihOnlineFR::FRParameters::SetCustomFields, newSetCustomFields))
  491. {
  492. toLog(LogLevel::Error, log + "impossible to set custom fields data");
  493. return false;
  494. }
  495. }
  496. }
  497. if (!ShtrihFRBase<T>::performFiscal(aReceipt, aPaymentData))
  498. {
  499. return false;
  500. }
  501. QByteArray data;
  502. if (aFDNumber && processCommand(CShtrihOnlineFR::Commands::FS::GetStatus, &data) && (data.size() >= 33))
  503. {
  504. *aFDNumber = revert(data.mid(29, 4)).toHex().toUInt(0, 16);
  505. }
  506. return true;
  507. }
  508. //--------------------------------------------------------------------------------
  509. template<class T>
  510. bool ShtrihOnlineFRBase<T>::getFiscalFields(quint32 aFDNumber, TFiscalPaymentData & aFPData, TComplexFiscalPaymentData & aPSData)
  511. {
  512. if (!processCommand(CShtrihOnlineFR::Commands::FS::StartFiscalTLVData, getHexReverted(aFDNumber, 4)))
  513. {
  514. return false;
  515. }
  516. TGetFiscalTLVData getFiscalTLVData = [&] (QByteArray & aData) -> TResult { TResult result = processCommand(CShtrihOnlineFR::Commands::FS::GetFiscalTLVData, &aData);
  517. aData = aData.mid(3); return result; };
  518. return processFiscalTLVData(getFiscalTLVData, &aFPData, &aPSData);
  519. }
  520. //--------------------------------------------------------------------------------
  521. template<class T>
  522. bool ShtrihOnlineFRBase<T>::canForceStatusBufferEnable()
  523. {
  524. return (mModel == CShtrihFR::Models::ID::ShtrihM01F) ||
  525. (mModel == CShtrihFR::Models::ID::ShtrihM02F) ||
  526. (mModel == CShtrihFR::Models::ID::ShtrihMini01F) ||
  527. (mModel == CShtrihFR::Models::ID::ShtrihLight02F);
  528. }
  529. //--------------------------------------------------------------------------------
  530. template<class T>
  531. bool ShtrihOnlineFRBase<T>::execZReport(bool aAuto)
  532. {
  533. QVariantMap outData;
  534. if (!prepareZReport(aAuto, outData))
  535. {
  536. return false;
  537. }
  538. bool result = checkNotPrinting(aAuto, true);
  539. if (!result && aAuto)
  540. {
  541. mNeedCloseSession = mNeedCloseSession || (mMode == CShtrihFR::InnerModes::SessionExpired);
  542. return false;
  543. }
  544. if (mOperatorPresence)
  545. {
  546. processCommand(CShtrihFR::Commands::SectionReport); waitForPrintingEnd(true, CShtrihOnlineFR::MaxWaitForPrintingSectionReport);
  547. processCommand(CShtrihFR::Commands::TaxReport); waitForPrintingEnd(true, CShtrihOnlineFR::MaxWaitForPrintingTaxReport);
  548. processCommand(CShtrihFR::Commands::CashierReport); waitForPrintingEnd(true, CShtrihOnlineFR::MaxWaitForPrintingCashierReport);
  549. }
  550. mNeedCloseSession = false;
  551. result = processCommand(CShtrihFR::Commands::ZReport);
  552. if (result)
  553. {
  554. mZBufferOverflow = false;
  555. SleepHelper::msleep(CShtrihFR::Pause::ZReportPrintingEnd);
  556. result = waitForChangeZReportMode();
  557. }
  558. if (getLongStatus())
  559. {
  560. mNeedCloseSession = mMode == CShtrihFR::InnerModes::SessionExpired;
  561. }
  562. if (result)
  563. {
  564. emit FRSessionClosed(outData);
  565. }
  566. toLog(result ? LogLevel::Normal : LogLevel::Error, result ?
  567. "ShtrihFR: Z-report is successfully processed" :
  568. "ShtrihFR: error in processing Z-report");
  569. checkNotPrinting(false, true);
  570. return result;
  571. }
  572. //--------------------------------------------------------------------------------
  573. template<class T>
  574. bool ShtrihOnlineFRBase<T>::processAnswer(const QByteArray & aCommand, char aError)
  575. {
  576. switch (aError)
  577. {
  578. case CShtrihOnlineFR::Errors::FSOfflineEnd:
  579. {
  580. mProcessingErrors.push_back(aError);
  581. mFSOfflineEnd = true;
  582. break;
  583. }
  584. case CShtrihOnlineFR::Errors::NeedZReport:
  585. {
  586. mProcessingErrors.push_back(aError);
  587. return execZReport(true);
  588. }
  589. case CShtrihOnlineFR::Errors::WrongFSState:
  590. {
  591. mProcessingErrors.push_back(aError);
  592. if (aCommand[0] != CShtrihFR::Commands::OpenFRSession)
  593. {
  594. return (getSessionState() == ESessionState::Closed) && openFRSession();
  595. }
  596. break;
  597. }
  598. }
  599. bool result = ShtrihFRBase<T>::processAnswer(aCommand, aError);
  600. if (!mProcessingErrors.isEmpty() && (mProcessingErrors.last() == CShtrihFR::Errors::BadModeForCommand) && getLongStatus() && (mMode == CShtrihFR::InnerModes::DataEjecting))
  601. {
  602. TGetFiscalTLVData getFiscalTLVData = [&] (QByteArray & aData) -> TResult { TResult result = processCommand(CShtrihOnlineFR::Commands::FS::GetFiscalTLVData, &aData);
  603. aData = aData.mid(3); return result; };
  604. result = processTLVData(getFiscalTLVData);
  605. }
  606. return result;
  607. }
  608. //--------------------------------------------------------------------------------
  609. template<class T>
  610. bool ShtrihOnlineFRBase<T>::setTLV(int aField, bool aForSale)
  611. {
  612. bool result;
  613. if (!mFFEngine.checkFiscalField(aField, result))
  614. {
  615. return result;
  616. }
  617. QString fieldLog;
  618. QByteArray commandData = mFFEngine.getTLVData(aField, &fieldLog);
  619. QByteArray command = aForSale ? CShtrihOnlineFR::Commands::FS::SetOFDParameterLinked : CShtrihOnlineFR::Commands::FS::SetOFDParameter;
  620. if (!processCommand(command, commandData))
  621. {
  622. toLog(LogLevel::Error, mDeviceName + ": Failed to set " + fieldLog);
  623. return false;
  624. }
  625. return true;
  626. }
  627. //--------------------------------------------------------------------------------
  628. template<class T>
  629. bool ShtrihOnlineFRBase<T>::openSession()
  630. {
  631. TStatusCodes statusCodes;
  632. if (!ProtoShtrihFR<T>::getStatus(statusCodes) || statusCodes.contains(PrinterStatusCode::OK::PaperInPresenter))
  633. {
  634. waitForPrintingEnd();
  635. SleepHelper::msleep(CShtrihFR::Pause::Cutting);
  636. }
  637. checkNotPrinting(true);
  638. bool result = processCommand(CShtrihFR::Commands::OpenFRSession);
  639. waitForPrintingEnd();
  640. checkNotPrinting();
  641. return result;
  642. }
  643. //--------------------------------------------------------------------------------