Терминальный проект КиберПлат [open source]
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

1520 строки
47KB

  1. /* @file Сервис печати и формирования чеков. */
  2. // Qt
  3. #include <Common/QtHeadersBegin.h>
  4. #include <QtCore/QRegExp>
  5. #include <QtConcurrent/QtConcurrentRun>
  6. #include <QtCore/QFuture>
  7. #include <QtCore/QMutexLocker>
  8. #include <QtCore/QDir>
  9. #include <QtCore/QDate>
  10. #include <QtCore/QBuffer>
  11. #include <QtCore/QTextStream>
  12. #include <QtQml/QJSEngine>
  13. #include <QtQml/QJSValue>
  14. #include <QtCore/QTextCodec>
  15. #include <QtXML/QDomDocument>
  16. #include <QtCore/QXmlStreamWriter>
  17. #include <Common/QtHeadersEnd.h>
  18. // Thirdparty
  19. #include <Common/QtHeadersBegin.h>
  20. #include <qzint.h>
  21. #include <Common/QtHeadersEnd.h>
  22. // PaymentProcessor SDK
  23. #include <SDK/PaymentProcessor/Components.h>
  24. #include <SDK/PaymentProcessor/Core/IDeviceService.h>
  25. #include <SDK/PaymentProcessor/Core/IEventService.h>
  26. #include <SDK/PaymentProcessor/Core/ReceiptTypes.h>
  27. #include <SDK/PaymentProcessor/Core/ServiceParameters.h>
  28. #include <SDK/PaymentProcessor/Settings/DealerSettings.h>
  29. #include <SDK/PaymentProcessor/Settings/ExtensionsSettings.h>
  30. #include <SDK/PaymentProcessor/Payment/Parameters.h>
  31. #include <SDK/PaymentProcessor/Payment/Security.h>
  32. // Driver SDK
  33. #include <SDK/Drivers/IFiscalPrinter.h>
  34. #include <SDK/Drivers/FR/FiscalPrinterConstants.h>
  35. #include <SDK/Drivers/Components.h>
  36. #include <SDK/Drivers/HardwareConstants.h>
  37. // Project
  38. #include "System/IApplication.h"
  39. #include "Services/ServiceNames.h"
  40. #include "Services/PrintConstants.h"
  41. #include "Services/PrintingService.h"
  42. #include "Services/SettingsService.h"
  43. #include "Services/DatabaseService.h"
  44. #include "Services/EventService.h"
  45. #include "Services/PluginService.h"
  46. #include "Services/PaymentService.h"
  47. #include "PrintingCommands.h"
  48. #include "FRReportConstants.h"
  49. namespace PPSDK = SDK::PaymentProcessor;
  50. namespace CPrintingService
  51. {
  52. const QString ReceiptDateTimeFormat = "dd.MM.yyyy hh:mm:ss";
  53. const QString ConditionTag = "@@";
  54. const QString MaskedFieldPostfix = "_MASKED";
  55. const QString DislayPostfix = "_DISPLAY";
  56. }
  57. //---------------------------------------------------------------------------
  58. PrintingService::PrintingService(IApplication * aApplication) :
  59. mApplication(aApplication),
  60. mContinuousMode(false),
  61. mServiceOperation(false),
  62. mRandomReceiptsID(false),
  63. mNextReceiptIndex(1),
  64. mFiscalRegister(nullptr),
  65. mRandomGenerator(static_cast<unsigned>(QDateTime::currentDateTime().currentMSecsSinceEpoch()))
  66. {
  67. setLog(mApplication->getLog());
  68. }
  69. //---------------------------------------------------------------------------
  70. PrintingService::~PrintingService()
  71. {
  72. }
  73. //---------------------------------------------------------------------------
  74. QString PrintingService::getName() const
  75. {
  76. return CServices::PrintingService;
  77. }
  78. //---------------------------------------------------------------------------
  79. const QSet<QString> & PrintingService::getRequiredServices() const
  80. {
  81. static QSet<QString> requiredServices = QSet<QString>()
  82. << CServices::DeviceService
  83. << CServices::DatabaseService
  84. << CServices::SettingsService
  85. << CServices::PluginService
  86. << CServices::PaymentService;
  87. return requiredServices;
  88. }
  89. //---------------------------------------------------------------------------
  90. QVariantMap PrintingService::getParameters() const
  91. {
  92. QVariantMap parameters;
  93. parameters[PPSDK::CServiceParameters::Printing::ReceiptCount] = getReceiptCount();
  94. return parameters;
  95. }
  96. //---------------------------------------------------------------------------
  97. void PrintingService::resetParameters(const QSet<QString> & aParameters)
  98. {
  99. if (aParameters.contains(PPSDK::CServiceParameters::Printing::ReceiptCount))
  100. {
  101. mDatabaseUtils->setDeviceParam(PPSDK::CDatabaseConstants::Devices::Terminal, PPSDK::CDatabaseConstants::Parameters::ReceiptCount, 0);
  102. }
  103. }
  104. //---------------------------------------------------------------------------
  105. bool PrintingService::initialize()
  106. {
  107. // Запрашиваем доступные устройства.
  108. mDeviceService = mApplication->getCore()->getDeviceService();
  109. loadReceiptTemplates();
  110. loadTags();
  111. updateHardwareConfiguration();
  112. createFiscalRegister();
  113. mDatabaseUtils = DatabaseService::instance(mApplication)->getDatabaseUtils<IHardwareDatabaseUtils>();
  114. connect(mDeviceService, SIGNAL(configurationUpdated()), SLOT(updateHardwareConfiguration()));
  115. connect(&mFutureWatcher, SIGNAL(finished()), this, SLOT(taskFinished()));
  116. return true;
  117. }
  118. //------------------------------------------------------------------------------
  119. void PrintingService::finishInitialize()
  120. {
  121. auto settings = SettingsService::instance(mApplication)->getAdapter<SDK::PaymentProcessor::TerminalSettings>();
  122. mRandomReceiptsID = settings->getCommonSettings().randomReceiptsID;
  123. }
  124. //---------------------------------------------------------------------------
  125. bool PrintingService::canShutdown()
  126. {
  127. return true;
  128. }
  129. //---------------------------------------------------------------------------
  130. bool PrintingService::shutdown()
  131. {
  132. mFutureWatcher.waitForFinished();
  133. foreach (DSDK::IPrinter * printer, mPrinterDevices)
  134. {
  135. mDeviceService->releaseDevice(printer);
  136. }
  137. if (mFiscalRegister)
  138. {
  139. SDK::Plugin::IPluginLoader * pluginLoader = PluginService::instance(mApplication)->getPluginLoader();
  140. pluginLoader->destroyPlugin(dynamic_cast<SDK::Plugin::IPlugin *>(mFiscalRegister));
  141. }
  142. return true;
  143. }
  144. //---------------------------------------------------------------------------
  145. PrintCommand * PrintingService::getPrintCommand(const QString & aReceiptType)
  146. {
  147. if (aReceiptType == PPSDK::CReceiptType::Payment)
  148. {
  149. return new PrintPayment(aReceiptType, this);
  150. }
  151. else if (aReceiptType == PPSDK::CReceiptType::Balance || aReceiptType == PPSDK::CReceiptType::XReport)
  152. {
  153. return new PrintBalance(aReceiptType, this);
  154. }
  155. else if (aReceiptType == PPSDK::CReceiptType::DispenserBalance || aReceiptType == PPSDK::CReceiptType::DispenserEncashment)
  156. {
  157. auto command = new PrintBalance(aReceiptType, this);
  158. command->setFiscal(false);
  159. return command;
  160. }
  161. else if (aReceiptType == PPSDK::CReceiptType::Encashment)
  162. {
  163. return new PrintEncashment(aReceiptType, this);
  164. }
  165. else if (aReceiptType == PPSDK::CReceiptType::ZReport)
  166. {
  167. return new PrintZReport(aReceiptType, this, false);
  168. }
  169. else if (aReceiptType == PPSDK::CReceiptType::ZReportFull)
  170. {
  171. return new PrintZReport(aReceiptType, this, true);
  172. }
  173. else
  174. {
  175. return new PrintReceipt(aReceiptType, this);
  176. }
  177. }
  178. //---------------------------------------------------------------------------
  179. bool PrintingService::canPrintReceipt(const QString & aReceiptType, bool aRealCheck)
  180. {
  181. DSDK::IPrinter * printer = takePrinter(aReceiptType, aRealCheck);
  182. giveBackPrinter(printer);
  183. return printer != nullptr;
  184. }
  185. //---------------------------------------------------------------------------
  186. int PrintingService::printReceipt(const QString & aReceiptType, const QVariantMap & aParameters, const QString & aReceiptTemplate, bool aContinuousMode, bool aServiceOperation)
  187. {
  188. mContinuousMode = aContinuousMode;
  189. mServiceOperation = aServiceOperation;
  190. QString receiptTemplate = (QString("%1_%2").arg(aParameters[PPSDK::CPayment::Parameters::Type].toString()).arg(aReceiptTemplate)).toLower();
  191. // Извлекаем шаблон 'PROCESSING_TYPE'_aReceiptTemplate
  192. if (!mCachedReceipts.contains(receiptTemplate))
  193. {
  194. receiptTemplate = aReceiptTemplate.toLower();
  195. // Извлекаем обычный шаблон для чека нужного типа.
  196. if (!mCachedReceipts.contains(receiptTemplate))
  197. {
  198. toLog(LogLevel::Error, QString("Failed to print receipt. Missing receipt template : %1.").arg(receiptTemplate));
  199. QMetaObject::invokeMethod(this, "printEmptyReceipt", Qt::QueuedConnection, Q_ARG(int, 0), Q_ARG(bool, true));
  200. return 0;
  201. }
  202. }
  203. auto printCommand = getPrintCommand(aReceiptType);
  204. printCommand->setReceiptTemplate(aReceiptTemplate);
  205. return performPrint(printCommand, aParameters, mCachedReceipts[receiptTemplate]);
  206. }
  207. //---------------------------------------------------------------------------
  208. void PrintingService::printEmptyReceipt(int aJobIndex, bool aError)
  209. {
  210. emit receiptPrinted(aJobIndex, aError);
  211. }
  212. //---------------------------------------------------------------------------
  213. bool PrintingService::printReceiptDirected(DSDK::IPrinter * aPrinter, const QString & aReceiptTemplate, const QVariantMap & aParameters)
  214. {
  215. mContinuousMode = false;
  216. // Извлекаем шаблон для чека нужного типа.
  217. if (!mCachedReceipts.contains(aReceiptTemplate.toLower()))
  218. {
  219. toLog(LogLevel::Error, QString("Missing receipt template : %1.").arg(aReceiptTemplate));
  220. return false;
  221. }
  222. QScopedPointer<PrintCommand> printCommand(getPrintCommand(QString()));
  223. printCommand->setReceiptTemplate(aReceiptTemplate);
  224. QVariantMap configuration;
  225. configuration.insert(CHardwareSDK::Printer::ContinuousMode, mContinuousMode);
  226. configuration.insert(CHardwareSDK::Printer::ServiceOperation, mServiceOperation);
  227. aPrinter->setDeviceConfiguration(configuration);
  228. // TODO: нужно ли увеличивать счетчик чеков?
  229. return printCommand->print(aPrinter, aParameters);
  230. }
  231. //---------------------------------------------------------------------------
  232. template <class ResultT, class T>
  233. ResultT & joinMap(ResultT & aResult, const T & aParameters2)
  234. {
  235. for (auto it = aParameters2.begin(); it != aParameters2.end(); ++it)
  236. {
  237. aResult.insert(it.key(), it.value());
  238. }
  239. return aResult;
  240. }
  241. //---------------------------------------------------------------------------
  242. int PrintingService::performPrint(PrintCommand * aCommand, const QVariantMap & aParameters, QStringList aReceiptTemplate)
  243. {
  244. // Функция, в которой прозводится печать. Должно быть исключено обращение к общим для разных принтеров данным.
  245. mPrintingFunction =
  246. [this, aReceiptTemplate](int aJobIndex, PrintCommand * aCommand, QVariantMap aParameters) -> bool
  247. {
  248. auto printer = takePrinter(aCommand->getReceiptType(), false);
  249. if (!printer)
  250. {
  251. delete aCommand;
  252. return false;
  253. }
  254. QVariantMap staticParameters;
  255. QVariantMap configuration;
  256. configuration.insert(CHardwareSDK::Printer::ContinuousMode, mContinuousMode);
  257. configuration.insert(CHardwareSDK::Printer::ServiceOperation, mServiceOperation);
  258. configuration.insert(CHardwareSDK::Printer::TemplateParameters, aParameters);
  259. configuration.insert(CHardwareSDK::Printer::ReceiptParameters, joinMap(staticParameters, mStaticParameters));
  260. configuration.insert(CHardwareSDK::Printer::ReceiptTemplate, aReceiptTemplate);
  261. printer->setDeviceConfiguration(configuration);
  262. bool result = aCommand->print(printer, joinMap(aParameters, staticParameters));
  263. if (result)
  264. {
  265. incrementReceiptCount(printer);
  266. }
  267. giveBackPrinter(printer);
  268. delete aCommand;
  269. emit receiptPrinted(aJobIndex, !result);
  270. return result;
  271. };
  272. int taskIndex = mNextReceiptIndex.fetchAndAddOrdered(1);
  273. if (taskIndex != 1 && !mFutureWatcher.isFinished())
  274. {
  275. Task task = {taskIndex, aCommand, aParameters};
  276. mQueue.enqueue(task);
  277. }
  278. else
  279. {
  280. mFutureWatcher.setFuture(QtConcurrent::run(mPrintingFunction, taskIndex, aCommand, aParameters));
  281. }
  282. return taskIndex;
  283. }
  284. //---------------------------------------------------------------------------
  285. void PrintingService::taskFinished()
  286. {
  287. if (!mQueue.isEmpty())
  288. {
  289. Task task = mQueue.dequeue();
  290. mFutureWatcher.setFuture(QtConcurrent::run(mPrintingFunction, task.index, task.command, task.parameters));
  291. }
  292. }
  293. //---------------------------------------------------------------------------
  294. int PrintingService::printReport(const QString & aReceiptType, const QVariantMap & aParameters)
  295. {
  296. auto printCommand = getPrintCommand(aReceiptType);
  297. PrintBalance * balanceCommand = dynamic_cast<PrintBalance *>(printCommand);
  298. if (balanceCommand && aParameters.contains(CPrintConstants::NoFiscal))
  299. {
  300. // Включаем нефискальный режим
  301. balanceCommand->setFiscal(false);
  302. }
  303. printCommand->setReceiptTemplate(aReceiptType);
  304. QVariantMap parameters;
  305. return performPrint(printCommand, joinMap(joinMap(parameters, mStaticParameters), aParameters));
  306. }
  307. //---------------------------------------------------------------------------
  308. void PrintingService::giveBackPrinter(DSDK::IPrinter * aPrinter)
  309. {
  310. if (aPrinter)
  311. {
  312. QMutexLocker lock(&mAvailablePrintersMutex);
  313. mAvailablePrinters.insert(aPrinter);
  314. mPrintersAvailable.wakeOne();
  315. }
  316. }
  317. //---------------------------------------------------------------------------
  318. DSDK::IPrinter * PrintingService::takePrinter(const QString & aReceiptType, bool aCheckOnline)
  319. {
  320. if (mPrinterDevices.empty())
  321. {
  322. toLog(LogLevel::Error, "Printers are not found in current configuration.");
  323. return 0;
  324. }
  325. // Пытаемся найти предпочтительный принтер.
  326. auto settings = SettingsService::instance(mApplication)->getAdapter<SDK::PaymentProcessor::TerminalSettings>();
  327. QString preferredName = settings->getPrinterForReceipt(aReceiptType);
  328. QList<DSDK::IPrinter *> printers;
  329. auto printCommand = getPrintCommand(aReceiptType);
  330. DSDK::IPrinter * preferred = nullptr;
  331. if (!preferredName.isEmpty())
  332. {
  333. preferred = dynamic_cast<DSDK::IPrinter *>(mDeviceService->acquireDevice(preferredName));
  334. if (preferred && mPrinterDevices.contains(preferred) && printCommand->canPrint(preferred, aCheckOnline))
  335. {
  336. printers << preferred;
  337. }
  338. }
  339. foreach(auto printer, mPrinterDevices)
  340. {
  341. if (printer && (printer != preferred) && printCommand->canPrint(printer, aCheckOnline))
  342. {
  343. printers << printer;
  344. }
  345. }
  346. delete printCommand;
  347. if (printers.isEmpty())
  348. {
  349. toLog(LogLevel::Error, QString("No any available printer for type %1 with %2 checking.").arg(aReceiptType).arg(aCheckOnline ? "online" : "offline"));
  350. return nullptr;
  351. }
  352. DSDK::IPrinter * printer = printers.first();
  353. // Ждем, пока принтер не появится в списке доступных.
  354. QMutexLocker lock(&mAvailablePrintersMutex);
  355. if (!mAvailablePrinters.contains(printer))
  356. {
  357. mPrintersAvailable.wait(&mAvailablePrintersMutex);
  358. }
  359. mAvailablePrinters.remove(printer);
  360. return printer;
  361. }
  362. //---------------------------------------------------------------------------
  363. void PrintingService::incrementReceiptCount(DSDK::IPrinter * aPrinter) const
  364. {
  365. QString deviceConfigName = mDeviceService->getDeviceConfigName(aPrinter);
  366. // Увеличиваем количество напечатанных чеков для конкртетного принтера.
  367. QVariant receiptCount = mDatabaseUtils->getDeviceParam(deviceConfigName, PPSDK::CDatabaseConstants::Parameters::ReceiptCount);
  368. receiptCount = QVariant::fromValue(receiptCount.toInt() + 1);
  369. mDatabaseUtils->setDeviceParam(deviceConfigName, PPSDK::CDatabaseConstants::Parameters::ReceiptCount, receiptCount);
  370. // Увеличиваем количество напечатанных чеков для терминала.
  371. receiptCount = mDatabaseUtils->getDeviceParam(PPSDK::CDatabaseConstants::Devices::Terminal, PPSDK::CDatabaseConstants::Parameters::ReceiptCount);
  372. receiptCount = QVariant::fromValue(receiptCount.toInt() + 1);
  373. mDatabaseUtils->setDeviceParam(PPSDK::CDatabaseConstants::Devices::Terminal, PPSDK::CDatabaseConstants::Parameters::ReceiptCount, receiptCount);
  374. }
  375. //---------------------------------------------------------------------------
  376. unsigned PrintingService::getReceiptID() const
  377. {
  378. if (mRandomReceiptsID)
  379. {
  380. return mRandomGenerator();
  381. }
  382. else
  383. {
  384. return getReceiptCount() + 1;
  385. }
  386. }
  387. //---------------------------------------------------------------------------
  388. int PrintingService::getReceiptCount() const
  389. {
  390. QVariant receiptCount = mDatabaseUtils->getDeviceParam(PPSDK::CDatabaseConstants::Devices::Terminal, PPSDK::CDatabaseConstants::Parameters::ReceiptCount);
  391. return receiptCount.isNull() ? 0 : receiptCount.toInt();
  392. }
  393. //---------------------------------------------------------------------------
  394. bool PrintingService::loadTags()
  395. {
  396. mStaticParameters.clear();
  397. SettingsService * settingsService = SettingsService::instance(mApplication);
  398. PPSDK::TerminalSettings * terminalSettings = settingsService->getAdapter<PPSDK::TerminalSettings>();
  399. PPSDK::SKeySettings key0 = terminalSettings->getKeys().value(0);
  400. if (key0.ap.isEmpty())
  401. {
  402. toLog(LogLevel::Error, "Failed to retrieve terminal number from configs.");
  403. return false;
  404. }
  405. else
  406. {
  407. mStaticParameters.insert(CPrintConstants::TermNumber, key0.ap);
  408. }
  409. PPSDK::DealerSettings * dealerSettings = settingsService->getAdapter<PPSDK::DealerSettings>();
  410. // TODO: проверить на валидность.
  411. PPSDK::SPersonalSettings dealer = dealerSettings->getPersonalSettings();
  412. joinMap(mStaticParameters, dealer.mPrintingParameters);
  413. mStaticParameters.insert(CPrintConstants::DealerName, dealer.name);
  414. mStaticParameters.insert(CPrintConstants::DealerPhone, dealer.phone);
  415. mStaticParameters.insert(CPrintConstants::DealerSupportPhone, dealer.phone);
  416. mStaticParameters.insert(CPrintConstants::DealerAddress, dealer.address);
  417. mStaticParameters.insert(CPrintConstants::DealerBusinessAddress, dealer.businessAddress.isEmpty() ? dealer.address : dealer.businessAddress);
  418. mStaticParameters.insert(CPrintConstants::DealerInn, dealer.inn);
  419. mStaticParameters.insert(CPrintConstants::DealerKbk, dealer.kbk);
  420. mStaticParameters.insert(CPrintConstants::DealerIsBank, dealer.isBank);
  421. mStaticParameters.insert(CPrintConstants::PointAddress, dealer.pointAddress);
  422. mStaticParameters.insert(CPrintConstants::PointName, dealer.pointName);
  423. mStaticParameters.insert(CPrintConstants::PointExternalID, dealer.pointExternalID);
  424. mStaticParameters.insert(CPrintConstants::BankName, dealer.bankName);
  425. mStaticParameters.insert(CPrintConstants::BankBik, dealer.bankBik);
  426. mStaticParameters.insert(CPrintConstants::BankPhone, dealer.bankPhone);
  427. mStaticParameters.insert(CPrintConstants::BankAddress, dealer.bankAddress);
  428. mStaticParameters.insert(CPrintConstants::BankInn, dealer.bankInn);
  429. QString currency = terminalSettings->getCurrencySettings().code;
  430. if (!currency.isEmpty())
  431. {
  432. mStaticParameters.insert(CPrintConstants::Currency, terminalSettings->getCurrencySettings().name);
  433. }
  434. else
  435. {
  436. toLog(LogLevel::Warning, "Failed to retrieve currency settings from configs. Tag <CURRENCY> will be printed empty on all receipts!");
  437. return false;
  438. }
  439. return true;
  440. }
  441. //---------------------------------------------------------------------------
  442. QStringList PrintingService::getReceipt(const QString & aReceiptTemplate, const QVariantMap & aParameters)
  443. {
  444. if (!mCachedReceipts.contains(aReceiptTemplate.toLower()))
  445. {
  446. toLog(LogLevel::Error, QString("Missing receipt template %1.").arg(aReceiptTemplate));
  447. }
  448. QStringList receipt = mCachedReceipts.value(aReceiptTemplate.toLower());
  449. expandTags(receipt, aParameters);
  450. return receipt;
  451. }
  452. //---------------------------------------------------------------------------
  453. QString maskedString(const QString & aString, bool aNeedMask)
  454. {
  455. if (aNeedMask)
  456. {
  457. int oneFourthLen = qMin(aString.size() / 4, 4);
  458. return aString.left(oneFourthLen) + QString("*").repeated(aString.size() - oneFourthLen * 2) + aString.right(oneFourthLen);
  459. }
  460. return aString;
  461. }
  462. //---------------------------------------------------------------------------
  463. QString PrintingService::convertImage2base64(const QString & aString)
  464. {
  465. QString result = aString;
  466. for (int extLen = 3; extLen <= 4; extLen++)
  467. {
  468. // \[img\s*\].*((?:[\w]\:|\\)?((\\|/)?[a-z_\-\s0-9\.]+)+\.[a-z]{3,4})
  469. QRegExp imgPattern = QRegExp(QString("\\[img\\s*\\].*((?:[\\w]\\:|\\\\)?((\\\\|/)?[a-z_\\-\\s0-9\\.]+)+\\.[a-z]{%1})").arg(extLen), Qt::CaseInsensitive);
  470. imgPattern.setMinimal(true);
  471. int offset = 0;
  472. while ((offset = imgPattern.indexIn(result, offset)) != -1)
  473. {
  474. QString img = "<image>";
  475. QFile file(imgPattern.cap(1));
  476. if (file.open(QIODevice::ReadOnly))
  477. {
  478. img = QString::fromLatin1(file.readAll().toBase64());
  479. }
  480. else
  481. {
  482. toLog(LogLevel::Error, QString("Error load image '%1': %2").arg(imgPattern.cap(1)).arg(file.errorString()));
  483. }
  484. result.replace(imgPattern.pos(1), imgPattern.cap(1).length(), img);
  485. offset = imgPattern.pos(1) + img.size();
  486. }
  487. }
  488. return result;
  489. }
  490. //---------------------------------------------------------------------------
  491. QString PrintingService::generateQR(const QString & aString)
  492. {
  493. QString result = aString;
  494. auto generateQRCode = [=](const QString & aText, int aSize, int aLeftMargin) -> QString
  495. {
  496. QImage image(QSize(aSize + aLeftMargin, aSize), QImage::Format_ARGB32);
  497. image.fill(QColor("transparent"));
  498. Zint::QZint zint;
  499. zint.setWidth(0);
  500. zint.setHeight(aSize);
  501. zint.setText(aText);
  502. zint.setWhitespace(0);
  503. zint.setBorderType(Zint::QZint::NO_BORDER);
  504. zint.setInputMode(DATA_MODE);
  505. zint.setHideText(true);
  506. zint.setSymbol(BARCODE_QRCODE);
  507. zint.setFgColor(QColor("black"));
  508. zint.setBgColor(QColor("white"));
  509. {
  510. QPainter painter(&image);
  511. painter.fillRect(QRectF(0, 0, image.width(), image.height()), QColor("white"));
  512. zint.render(painter, QRectF(aLeftMargin, 0, image.width() - aLeftMargin, image.height()), Zint::QZint::KeepAspectRatio);
  513. painter.end();
  514. }
  515. if (zint.hasErrors())
  516. {
  517. toLog(LogLevel::Error, QString("Failed render QR code: %1.").arg(zint.lastError()));
  518. }
  519. else
  520. {
  521. QBuffer buffer;
  522. if (buffer.open(QIODevice::WriteOnly))
  523. {
  524. image.save(&buffer, "png");
  525. return buffer.data().toBase64();
  526. }
  527. }
  528. return "";
  529. };
  530. QRegExp qrPattern("\\[qr(\\s*(\\w+)\\s*=\\s*(\\d+)\\s*)?(\\s*(\\w+)\\s*=\\s*(\\d+)\\s*)?\\](.*)\\[/qr\\]", Qt::CaseInsensitive);
  531. qrPattern.setMinimal(true);
  532. int offset = 0;
  533. while ((offset = qrPattern.indexIn(result, offset)) != -1)
  534. {
  535. int size = 200;
  536. int left_margin = 0;
  537. for (int i = 2; i < 6; i += 3)
  538. {
  539. if (qrPattern.cap(i).toLower() == "size")
  540. {
  541. size = qrPattern.cap(i + 1).toInt() ? qrPattern.cap(i + 1).toInt() : size;
  542. }
  543. else if (qrPattern.cap(i).toLower() == "left_margin")
  544. {
  545. left_margin = qrPattern.cap(i + 1).toInt() ? qrPattern.cap(i + 1).toInt() : left_margin;
  546. }
  547. }
  548. QString content = qrPattern.cap(7);
  549. QString qrImage = generateQRCode(content, size, left_margin);
  550. QString img = qrImage.isEmpty() ? "<qr-code>" : QString("[img]%1[/img]").arg(qrImage);
  551. result.replace(offset, qrPattern.cap(0).length(), img);
  552. offset += img.size();
  553. }
  554. return result;
  555. }
  556. //---------------------------------------------------------------------------
  557. QString PrintingService::generatePDF417(const QString & aString)
  558. {
  559. QString result = aString;
  560. auto generatePDFCode = [=](const QString & aText, int aSize, int aLeftMargin) -> QString
  561. {
  562. int divider = aText.size() > 100 ? 2 : 3;
  563. QImage image(QSize(aSize + aLeftMargin, aSize / divider), QImage::Format_ARGB32);
  564. image.fill(QColor("transparent"));
  565. Zint::QZint zint;
  566. zint.setWidth(aSize);
  567. zint.setHeight(aSize / divider);
  568. zint.setText(aText);
  569. zint.setWhitespace(0);
  570. zint.setBorderType(Zint::QZint::NO_BORDER);
  571. zint.setInputMode(UNICODE_MODE);
  572. zint.setHideText(true);
  573. zint.setSymbol(BARCODE_PDF417);
  574. zint.setFgColor(QColor("black"));
  575. zint.setBgColor(QColor("white"));
  576. QPainter painter(&image);
  577. painter.fillRect(QRectF(0, 0, image.width(), image.height()), QColor("white"));
  578. zint.render(painter, QRectF(aLeftMargin, 0, image.width() - aLeftMargin, image.height()));
  579. painter.end();
  580. if (zint.hasErrors())
  581. {
  582. toLog(LogLevel::Error, QString("Failed render PDF-417 code: %1.").arg(zint.lastError()));
  583. }
  584. else
  585. {
  586. QBuffer buffer;
  587. if (buffer.open(QIODevice::WriteOnly))
  588. {
  589. image.save(&buffer, "png");
  590. return buffer.data().toBase64();
  591. }
  592. }
  593. return "";
  594. };
  595. QRegExp qrPattern("\\[pdf417(\\s*(\\w+)\\s*=\\s*(\\d+)\\s*)?(\\s*(\\w+)\\s*=\\s*(\\d+)\\s*)?\\](.*)\\[/pdf417\\]", Qt::CaseInsensitive);
  596. qrPattern.setMinimal(true);
  597. int offset = 0;
  598. while ((offset = qrPattern.indexIn(result, offset)) != -1)
  599. {
  600. int size = 200;
  601. int left_margin = 0;
  602. for (int i = 2; i < 6; i += 3)
  603. {
  604. if (qrPattern.cap(i).toLower() == "size")
  605. {
  606. size = qrPattern.cap(i + 1).toInt() ? qrPattern.cap(i + 1).toInt() : size;
  607. }
  608. else if (qrPattern.cap(i).toLower() == "left_margin")
  609. {
  610. left_margin = qrPattern.cap(i + 1).toInt() ? qrPattern.cap(i + 1).toInt() : left_margin;
  611. }
  612. }
  613. QString content = qrPattern.cap(7);
  614. QString qrImage = generatePDFCode(content, size, left_margin);
  615. QString img = qrImage.isEmpty() ? "<pdf417-code>" : QString("[img]%1[/img]").arg(qrImage);
  616. result.replace(offset, qrPattern.cap(0).length(), img);
  617. offset += img.size();
  618. }
  619. return result;
  620. }
  621. //---------------------------------------------------------------------------
  622. QString PrintingService::generate1D(const QString & aString)
  623. {
  624. QString result = aString;
  625. auto generatePDFCode = [=](const QString & aText, int aSize, int aLeftMargin) -> QString
  626. {
  627. int divider = aText.size() > 100 ? 2 : 3;
  628. QImage image(QSize(aSize + aLeftMargin, aSize / divider), QImage::Format_ARGB32);
  629. image.fill(QColor("transparent"));
  630. Zint::QZint zint;
  631. zint.setWidth(aSize);
  632. zint.setHeight(aSize / divider);
  633. zint.setText(aText);
  634. zint.setWhitespace(0);
  635. zint.setBorderType(Zint::QZint::NO_BORDER);
  636. zint.setInputMode(UNICODE_MODE);
  637. zint.setHideText(true);
  638. zint.setSymbol(BARCODE_CODE128);
  639. zint.setFgColor(QColor("black"));
  640. zint.setBgColor(QColor("white"));
  641. QPainter painter(&image);
  642. painter.fillRect(QRectF(0, 0, image.width(), image.height()), QColor("white"));
  643. zint.render(painter, QRectF(aLeftMargin, 0, image.width() - aLeftMargin, image.height()));
  644. painter.end();
  645. if (zint.hasErrors())
  646. {
  647. toLog(LogLevel::Error, QString("Failed render PDF-417 code: %1.").arg(zint.lastError()));
  648. }
  649. else
  650. {
  651. QBuffer buffer;
  652. if (buffer.open(QIODevice::WriteOnly))
  653. {
  654. image.save(&buffer, "png");
  655. return buffer.data().toBase64();
  656. }
  657. }
  658. return "";
  659. };
  660. QRegExp qrPattern("\\[1d(\\s*(\\w+)\\s*=\\s*(\\d+)\\s*)?(\\s*(\\w+)\\s*=\\s*(\\d+)\\s*)?\\](.*)\\[/1d\\]", Qt::CaseInsensitive);
  661. qrPattern.setMinimal(true);
  662. int offset = 0;
  663. while ((offset = qrPattern.indexIn(result, offset)) != -1)
  664. {
  665. int size = 200;
  666. int left_margin = 0;
  667. for (int i = 2; i < 6; i += 3)
  668. {
  669. if (qrPattern.cap(i).toLower() == "size")
  670. {
  671. size = qrPattern.cap(i + 1).toInt() ? qrPattern.cap(i + 1).toInt() : size;
  672. }
  673. else if (qrPattern.cap(i).toLower() == "left_margin")
  674. {
  675. left_margin = qrPattern.cap(i + 1).toInt() ? qrPattern.cap(i + 1).toInt() : left_margin;
  676. }
  677. }
  678. QString content = qrPattern.cap(7);
  679. QString qrImage = generatePDFCode(content, size, left_margin);
  680. QString img = qrImage.isEmpty() ? "<pdf417-code>" : QString("[img]%1[/img]").arg(qrImage);
  681. result.replace(offset, qrPattern.cap(0).length(), img);
  682. offset += img.size();
  683. }
  684. return result;
  685. }
  686. //---------------------------------------------------------------------------
  687. QString PrintingService::generateLine(const QString & aString)
  688. {
  689. QString result = aString;
  690. auto generateLine = [](int aSize, int aHeight, int aDense) -> QString
  691. {
  692. QImage image(QSize(aSize, aHeight), QImage::Format_ARGB32);
  693. image.fill(QColor("transparent"));
  694. Qt::BrushStyle style;
  695. if (aDense <= 0) style = Qt::SolidPattern;
  696. else if (aDense >= 6) style = Qt::Dense7Pattern;
  697. else style = static_cast<Qt::BrushStyle>(aDense + 1);
  698. QPainter painter(&image);
  699. painter.setBrush(style);
  700. painter.setPen(QColor("transparent"));
  701. painter.drawRect(0, 0, image.width(), image.height());
  702. painter.end();
  703. QBuffer buffer;
  704. if (buffer.open(QIODevice::WriteOnly))
  705. {
  706. image.save(&buffer, "png");
  707. return buffer.data().toBase64();
  708. }
  709. return QString();
  710. };
  711. QRegExp qrPattern("\\[hr(\\s*(\\w+)\\s*=\\s*(\\d+)\\s*)?(\\s*(\\w+)\\s*=\\s*(\\d+)\\s*)?\\](.*)\\[/hr\\]", Qt::CaseInsensitive);
  712. qrPattern.setMinimal(true);
  713. int offset = 0;
  714. while ((offset = qrPattern.indexIn(result, offset)) != -1)
  715. {
  716. int size = 220;
  717. int height = 1;
  718. int dense = 0;
  719. for (int i = 2; i < 6; i += 3)
  720. {
  721. if (qrPattern.cap(i).toLower() == "size")
  722. {
  723. size = qrPattern.cap(i + 1).toInt() ? qrPattern.cap(i + 1).toInt() : size;
  724. }
  725. else if (qrPattern.cap(i).toLower() == "height")
  726. {
  727. height = qrPattern.cap(i + 1).toInt() ? qrPattern.cap(i + 1).toInt() : height;
  728. }
  729. else if (qrPattern.cap(i).toLower() == "ds")
  730. {
  731. dense = qrPattern.cap(i + 1).toInt() ? qrPattern.cap(i + 1).toInt() : dense;
  732. }
  733. }
  734. QString qrImage = generateLine(size, height, dense);
  735. QString img = qrImage.isEmpty() ? "<hr-code>" : QString("[img]%1[/img]").arg(qrImage);
  736. result.replace(offset, qrPattern.cap(0).length(), img);
  737. offset += img.size();
  738. }
  739. return result;
  740. }
  741. //---------------------------------------------------------------------------
  742. void PrintingService::expandTags(QStringList & aReceipt, const QVariantMap & aParameters)
  743. {
  744. int operatorFieldIndex = 0;
  745. QVariantMap userParameters = aParameters;
  746. // Если есть дата создания платежа и она валидна, то подставляем на чек дату платежа
  747. userParameters[CPrintConstants::DateTime] = aParameters.contains(PPSDK::CPayment::Parameters::CreationDate) &&
  748. aParameters.value(PPSDK::CPayment::Parameters::CreationDate, "").toDateTime().isValid() ?
  749. aParameters.value(PPSDK::CPayment::Parameters::CreationDate, "").toDateTime().toString(CPrintingService::ReceiptDateTimeFormat) :
  750. QDateTime::currentDateTime().toLocalTime().toString(CPrintingService::ReceiptDateTimeFormat);
  751. // Добавляем поля, зависящие от конкретного вызова.
  752. userParameters[CPrintConstants::ReceiptNumber] = getReceiptID();
  753. int providerId = aParameters.value(PPSDK::CPayment::Parameters::Provider, -1).toInt();
  754. PPSDK::SProvider provider = SettingsService::instance(mApplication)->getAdapter<PPSDK::DealerSettings>()->getProvider(providerId);
  755. PPSDK::SProvider mnpProvider = SettingsService::instance(mApplication)->getAdapter<PPSDK::DealerSettings>()->getMNPProvider(providerId,
  756. aParameters.value(PPSDK::CPayment::Parameters::MNPGetewayIn, 0).toLongLong(),
  757. aParameters.value(PPSDK::CPayment::Parameters::MNPGetewayOut, 0).toLongLong());
  758. if (!mnpProvider.isNull())
  759. {
  760. userParameters[CPrintConstants::OpBrand] = mnpProvider.name;
  761. }
  762. PPSDK::SecurityFilter filter(mnpProvider, PPSDK::SProviderField::SecuritySubsystem::Printer);
  763. // Набор строк, полученных в результате применения всех подстановок.
  764. QStringList result;
  765. // Для каждого тега в шаблоне чека, заменяем его значением из параметров.
  766. for (auto it = aReceipt.begin(); it != aReceipt.end(); ++it)
  767. {
  768. QRegExp tagPattern("%(.*)%", Qt::CaseInsensitive);
  769. tagPattern.setMinimal(true);
  770. int offset = 0;
  771. while ((offset = tagPattern.indexIn(*it, offset)) != -1)
  772. {
  773. QString tag = tagPattern.cap(1);
  774. // %% заменяем на %
  775. if (tag.isEmpty())
  776. {
  777. it->replace(offset, tagPattern.cap(0).length(), "%");
  778. offset += 1;
  779. continue;
  780. }
  781. bool isMasked = tag.endsWith(CPrintingService::MaskedFieldPostfix);
  782. if (isMasked)
  783. {
  784. tag.remove(CPrintingService::MaskedFieldPostfix);
  785. }
  786. // Параметр пользователя?
  787. auto userParameter = userParameters.find(tag);
  788. if (userParameter != userParameters.end())
  789. {
  790. // Параметр список?
  791. if (tag.startsWith("[") && tag.endsWith("]"))
  792. {
  793. // удаляем строку с параметром-списком
  794. aReceipt.erase(it, it);
  795. it++;
  796. QStringList listItems = userParameter.value().toStringList();
  797. QStringList::const_iterator listIt;
  798. for (listIt = listItems.constBegin(); listIt != listItems.constEnd(); listIt++)
  799. {
  800. result.append(filter.apply(tag, *listIt));
  801. }
  802. }
  803. else
  804. {
  805. QString masked = isMasked ?
  806. maskedString(userParameter.value().toString(), isMasked) :
  807. filter.apply(userParameter.key(), userParameter.value().toString());
  808. it->replace(offset, tagPattern.cap(0).length(), masked);
  809. offset += masked.length();
  810. }
  811. continue;
  812. }
  813. // Статический параметр?
  814. auto staticParameter = mStaticParameters.find(tag);
  815. if (staticParameter != mStaticParameters.end())
  816. {
  817. QString masked = isMasked ?
  818. maskedString(staticParameter.value(), isMasked) :
  819. filter.apply(staticParameter.key(), staticParameter.value());
  820. it->replace(offset, tagPattern.cap(0).length(), masked);
  821. offset += masked.length();
  822. continue;
  823. }
  824. // Название или значение поля оператора?
  825. QString targetParameter;
  826. QRegExp operatorFieldPattern("FIELD_(.+)", Qt::CaseInsensitive);
  827. operatorFieldPattern.setMinimal(true);
  828. if (operatorFieldPattern.indexIn(tag) != -1)
  829. {
  830. targetParameter = operatorFieldPattern.cap(1);
  831. if (!filter.haveFilter(targetParameter))
  832. {
  833. targetParameter += CPrintingService::DislayPostfix;
  834. }
  835. }
  836. else
  837. {
  838. operatorFieldPattern.setPattern("RAWFIELD_(.+)");
  839. if (operatorFieldPattern.indexIn(tag) != -1)
  840. {
  841. targetParameter = operatorFieldPattern.cap(1);
  842. }
  843. }
  844. if (!targetParameter.isEmpty())
  845. {
  846. if (!aParameters.contains(targetParameter))
  847. {
  848. toLog(LogLevel::Error, QString("Operator parameter %1 required in receipt but missing in parameters list.").arg(targetParameter));
  849. }
  850. else
  851. {
  852. QString masked = isMasked ?
  853. maskedString(aParameters[targetParameter].toString(), isMasked) :
  854. filter.apply(targetParameter, aParameters[targetParameter].toString());
  855. it->replace(offset, tagPattern.cap(0).length(), masked);
  856. offset += masked.length();
  857. continue;
  858. }
  859. }
  860. // Тег OPERATOR_FIELD
  861. if (tag == "OPERATOR_FIELD")
  862. {
  863. if (provider.isNull())
  864. {
  865. toLog(LogLevel::Error, QString("Failed to expand operator field. Provider id %1 is not valid.").arg(providerId));
  866. }
  867. // Вставляем поля оператора.
  868. if (operatorFieldIndex < provider.fields.size())
  869. {
  870. PPSDK::SProviderField field = provider.fields.at(operatorFieldIndex);
  871. QString fieldValue = aParameters.value(field.id, QString()).toString();
  872. bool findNextField = true;
  873. while (fieldValue.isEmpty())
  874. {
  875. if (operatorFieldIndex >= provider.fields.size())
  876. {
  877. findNextField = false;
  878. break;
  879. }
  880. field = provider.fields.at(operatorFieldIndex);
  881. fieldValue = aParameters.value(field.id, QString()).toString();
  882. if (fieldValue.isEmpty())
  883. {
  884. ++operatorFieldIndex;
  885. }
  886. }
  887. if (findNextField)
  888. {
  889. QString masked;
  890. if (filter.haveFilter(field.id))
  891. {
  892. masked = filter.apply(field.id, aParameters.value(field.id, QString()).toString());
  893. }
  894. else
  895. {
  896. QString value = aParameters.value(field.id + CPrintingService::DislayPostfix, QString()).toString();
  897. masked = isMasked ? maskedString(value, isMasked) : value;
  898. }
  899. QString replaceString = QString("%1: %2").arg(field.title).arg(masked);
  900. it->replace(offset, tagPattern.cap(0).size(), replaceString);
  901. offset = replaceString.length();
  902. ++operatorFieldIndex;
  903. }
  904. }
  905. else
  906. {
  907. it->replace(tagPattern.cap(0), QString());
  908. }
  909. }
  910. // Оставляем поле пустым.
  911. it->replace(tagPattern.cap(0), QString());
  912. }
  913. // Предзагружаем содержимое тегов [IMG]
  914. *it = convertImage2base64(*it);
  915. // Преобразуем [QR] теги в [IMG]
  916. *it = generateQR(*it);
  917. // Преобразуем [PDF417] теги в [IMG]
  918. *it = generatePDF417(*it);
  919. // Преобразуем [1d] теги в [IMG]
  920. *it = generate1D(*it);
  921. // Преобразуем [hr] тег в [IMG]
  922. *it = generateLine(*it);
  923. // Обработаем тег с условием
  924. if (it->contains(CPrintingService::ConditionTag))
  925. {
  926. QStringList l = it->split(CPrintingService::ConditionTag);
  927. if (QJSEngine().evaluate(l.first()).toBool())
  928. {
  929. result.append(l.last());
  930. }
  931. continue;
  932. }
  933. if (it->length() > 0)
  934. {
  935. result.append(*it);
  936. }
  937. }
  938. aReceipt = result;
  939. }
  940. //---------------------------------------------------------------------------
  941. void PrintingService::loadReceiptTemplates()
  942. {
  943. auto loadReceiptTemplate = [&](const QFileInfo & aFileInfo)
  944. {
  945. if (!aFileInfo.suffix().compare("xml", Qt::CaseInsensitive))
  946. {
  947. QDomDocument xmlFile(aFileInfo.fileName());
  948. QFile file(aFileInfo.filePath());
  949. if (!file.open(QIODevice::ReadOnly))
  950. {
  951. toLog(LogLevel::Error, QString("Failed to open file %1").arg(aFileInfo.filePath()));
  952. return;
  953. }
  954. xmlFile.setContent(&file);
  955. QDomElement body = xmlFile.documentElement();
  956. QStringList receiptContents;
  957. for (QDomNode node = body.firstChild(); !node.isNull(); node = node.nextSibling())
  958. {
  959. QDomElement row = node.toElement();
  960. if (row.tagName() == "string" || row.tagName() == "else")
  961. {
  962. QString prefix = row.attribute("if").isEmpty() ? "" : QString("%1%2").arg(row.attribute("if")).arg(CPrintingService::ConditionTag);
  963. receiptContents.append(QString("%1%2").arg(prefix).arg(row.text()));
  964. }
  965. else if (row.tagName() == "hr")
  966. {
  967. receiptContents.append("[hr]-[/hr]");
  968. }
  969. }
  970. if (!receiptContents.isEmpty())
  971. {
  972. mCachedReceipts.insert(aFileInfo.baseName().toLower(), receiptContents);
  973. }
  974. else
  975. {
  976. toLog(LogLevel::Error, QString("Bad receipt template '%1': parse xml error").arg(aFileInfo.fileName()));
  977. }
  978. }
  979. else
  980. {
  981. toLog(LogLevel::Error, QString("Bad receipt template file extension : %1").arg(aFileInfo.fileName()));
  982. }
  983. };
  984. // Загружаем все шаблоны чеков в папке ./receipts
  985. QDir receiptDirectory;
  986. receiptDirectory.setPath(mApplication->getWorkingDirectory() + "/data/receipts");
  987. // Загружаем все файлы, которые есть в каталоге.
  988. foreach (const QFileInfo & fileInfo, receiptDirectory.entryInfoList(QDir::Files))
  989. {
  990. loadReceiptTemplate(fileInfo);
  991. }
  992. // загружаем все шаблоны из папки пользовательских шаблонов чеков
  993. receiptDirectory.setPath(mApplication->getWorkingDirectory() + "/user/receipts");
  994. // Загружаем все файлы, которые есть в каталоге.
  995. foreach (const QFileInfo & fileInfo, receiptDirectory.entryInfoList(QDir::Files))
  996. {
  997. loadReceiptTemplate(fileInfo);
  998. }
  999. }
  1000. //---------------------------------------------------------------------------
  1001. void PrintingService::saveReceiptContent(const QString & aReceiptName, const QStringList & aContents)
  1002. {
  1003. // Получаем имя папки с чеками.
  1004. QString suffix = QDate::currentDate().toString("yyyy.MM.dd");
  1005. QDir path(mApplication->getWorkingDirectory() + "/receipts/" + suffix);
  1006. if (!path.exists())
  1007. {
  1008. if (!QDir().mkpath(path.path()))
  1009. {
  1010. toLog(LogLevel::Error, "Failed to create printed receipts folder.");
  1011. return;
  1012. }
  1013. }
  1014. auto fileName = path.path() + QDir::separator() + aReceiptName + ".txt";
  1015. QFile file(fileName);
  1016. // Сохраняем чек.
  1017. if (file.open(QIODevice::WriteOnly))
  1018. {
  1019. QTextStream ostream(&file);
  1020. ostream << aContents.join("\r\n");
  1021. ostream.flush();
  1022. }
  1023. else
  1024. {
  1025. toLog(LogLevel::Error, QString("Failed to open file %1 for receipt.").arg(fileName));
  1026. }
  1027. }
  1028. //---------------------------------------------------------------------------
  1029. void PrintingService::saveReceipt(const QVariantMap & aParameters, const QString & aReceiptTemplate)
  1030. {
  1031. QStringList receipt = getReceipt(aReceiptTemplate, aParameters);
  1032. QString fileName = QTime::currentTime().toString("hhmmsszzz");
  1033. if (aParameters.contains(PPSDK::CPayment::Parameters::ID))
  1034. {
  1035. fileName += QString("_%1").arg(aParameters[PPSDK::CPayment::Parameters::ID].toString());
  1036. }
  1037. fileName += "_not_printed";
  1038. saveReceiptContent(fileName, receipt);
  1039. }
  1040. //---------------------------------------------------------------------------
  1041. QString PrintingService::loadReceipt(qint64 aPaymentId)
  1042. {
  1043. // Получаем имя папки с чеками.
  1044. QString suffix = QDate::currentDate().toString("yyyy.MM.dd");
  1045. QDir path(mApplication->getWorkingDirectory() + "/receipts/" + suffix);
  1046. if (!path.exists())
  1047. {
  1048. toLog(LogLevel::Error, "Failed to find printed receipts folder.");
  1049. return QString();
  1050. }
  1051. QDir dir(path.path() + QDir::separator());
  1052. QStringList receipts = dir.entryList(QStringList() << QString("*%1.txt").arg(aPaymentId) << QString("*%1_not_printed.txt").arg(aPaymentId));
  1053. if (!receipts.isEmpty())
  1054. {
  1055. QFile f(dir.absolutePath() + QDir::separator() + receipts.takeFirst());
  1056. if (f.open(QIODevice::ReadOnly))
  1057. {
  1058. return QTextCodec::codecForName("Windows-1251")->toUnicode(f.readAll());
  1059. }
  1060. }
  1061. return QString();
  1062. }
  1063. //---------------------------------------------------------------------------
  1064. void PrintingService::onStatusChanged(DSDK::EWarningLevel::Enum aWarningLevel, const QString & /*aTranslation*/, int /*aStatus*/)
  1065. {
  1066. emit printerStatus(aWarningLevel == DSDK::EWarningLevel::OK);
  1067. }
  1068. //---------------------------------------------------------------------------
  1069. void PrintingService::onFRSessionClosed(const QVariantMap & aParameters)
  1070. {
  1071. // Получаем имя папки с отчётами.
  1072. QDir path(mApplication->getWorkingDirectory() + "/receipts/reports");
  1073. if (!path.exists())
  1074. {
  1075. if (!QDir().mkpath(path.path()))
  1076. {
  1077. toLog(LogLevel::Error, "Failed to create printed reports folder.");
  1078. return;
  1079. }
  1080. }
  1081. auto fileName = path.path() + QDir::separator() + QDateTime::currentDateTime().toString("yyyy.MM.dd hhmmsszzz") + ".xml";
  1082. QFile file(fileName);
  1083. // Сохраняем отчёт.
  1084. if (file.open(QIODevice::WriteOnly))
  1085. {
  1086. using namespace SDK::Driver;
  1087. QXmlStreamWriter stream(&file);
  1088. stream.setAutoFormatting(true);
  1089. stream.writeStartDocument();
  1090. stream.writeStartElement(CFRReport::ZReport);
  1091. stream.writeAttribute(CFRReport::Number, aParameters[CFiscalPrinter::ZReportNumber].toString());
  1092. stream.writeStartElement(CFRReport::FR);
  1093. stream.writeAttribute(CFRReport::Serial, aParameters[CFiscalPrinter::Serial].toString());
  1094. stream.writeAttribute(CFRReport::RNM, aParameters[CFiscalPrinter::RNM].toString());
  1095. stream.writeEndElement(); // CFRReport::FR
  1096. stream.writeTextElement(CFRReport::PaymentCount, aParameters[CFiscalPrinter::PaymentCount].toString());
  1097. double amount = aParameters[CFiscalPrinter::PaymentAmount].toDouble();
  1098. stream.writeTextElement(CFRReport::PaymentAmount, QString::number(amount, 'f', 2));
  1099. amount = aParameters[CFiscalPrinter::NonNullableAmount].toDouble();
  1100. stream.writeTextElement(CFRReport::NonNullableAmount, QString::number(amount, 'f', 2));
  1101. stream.writeTextElement(CFRReport::FRDateTime, aParameters[CFiscalPrinter::FRDateTime].toString());
  1102. stream.writeTextElement(CFRReport::SystemDateTime, aParameters[CFiscalPrinter::SystemDateTime].toString());
  1103. stream.writeEndElement(); // CFRReport::ZReport
  1104. stream.writeEndDocument();
  1105. file.flush();
  1106. file.close();
  1107. }
  1108. }
  1109. //---------------------------------------------------------------------------
  1110. void PrintingService::updateHardwareConfiguration()
  1111. {
  1112. PPSDK::TerminalSettings * settings = SettingsService::instance(mApplication)->getAdapter<PPSDK::TerminalSettings>();
  1113. // Получаем информацию о принтерах из конфигов.
  1114. QString regExpData = QString("(%1|%2|%3)")
  1115. .arg(DSDK::CComponents::Printer).arg(DSDK::CComponents::DocumentPrinter).arg(DSDK::CComponents::FiscalRegistrator);
  1116. QStringList printerNames = settings->getDeviceList().filter(QRegExp(regExpData));
  1117. mPrinterDevices.clear();
  1118. mAvailablePrinters.clear();
  1119. QVariantMap delalerSettings;
  1120. delalerSettings.insert(CHardwareSDK::FR::DealerTaxation, mStaticParameters.value(CPrintConstants::DealerTaxation));
  1121. delalerSettings.insert(CHardwareSDK::FR::DealerAgentFlag, mStaticParameters.value(CPrintConstants::DealerAgentFlag));
  1122. // Запрашиваем устройства.
  1123. foreach (const QString & printerName, printerNames)
  1124. {
  1125. DSDK::IPrinter * device = dynamic_cast<DSDK::IPrinter *>(mDeviceService->acquireDevice(printerName));
  1126. if (device)
  1127. {
  1128. mPrinterDevices.append(device);
  1129. // Подписываемся на события принтера.
  1130. device->subscribe(SDK::Driver::IDevice::StatusSignal, this, SLOT(onStatusChanged(SDK::Driver::EWarningLevel::Enum, const QString &, int)));
  1131. // Подписываемся на события фискальника.
  1132. if (dynamic_cast<DSDK::IFiscalPrinter *>(device))
  1133. {
  1134. device->subscribe(SDK::Driver::IFiscalPrinter::FRSessionClosedSignal, this, SLOT(onFRSessionClosed(const QVariantMap &)));
  1135. }
  1136. device->setDeviceConfiguration(delalerSettings);
  1137. }
  1138. else
  1139. {
  1140. toLog(LogLevel::Error, QString("Failed to acquire device %1 .").arg(printerName));
  1141. }
  1142. }
  1143. mAvailablePrinters = mPrinterDevices.toSet();
  1144. }
  1145. //---------------------------------------------------------------------------
  1146. void PrintingService::createFiscalRegister()
  1147. {
  1148. if (!mFiscalRegister)
  1149. {
  1150. // Получаем информацию о фискальных регистраторах.
  1151. PPSDK::ExtensionsSettings * extSettings = SettingsService::instance(mApplication)->getAdapter<PPSDK::ExtensionsSettings>();
  1152. SDK::Plugin::IPluginLoader * pluginLoader = PluginService::instance(mApplication)->getPluginLoader();
  1153. foreach(auto fr, pluginLoader->getPluginList(QRegExp(PPSDK::CComponents::FiscalRegister)))
  1154. {
  1155. auto plugin = pluginLoader->createPlugin(fr);
  1156. if (!plugin)
  1157. {
  1158. continue;
  1159. }
  1160. PPSDK::IFiscalRegister * frPlugin = dynamic_cast<PPSDK::IFiscalRegister *>(plugin);
  1161. if (!frPlugin)
  1162. {
  1163. toLog(LogLevel::Error, QString("FR %1 not have IFiscalRegister interface.").arg(fr));
  1164. pluginLoader->destroyPlugin(plugin);
  1165. continue;
  1166. }
  1167. auto parameters = extSettings->getSettings(plugin->getPluginName());
  1168. if (parameters.isEmpty())
  1169. {
  1170. toLog(LogLevel::Warning, QString("FR %1 not have extensions settings. Skip it. (check config.xml).").arg(plugin->getPluginName()));
  1171. pluginLoader->destroyPlugin(plugin);
  1172. continue;
  1173. }
  1174. connect(dynamic_cast<QObject *>(frPlugin), SDK::Driver::IFiscalPrinter::FRSessionClosedSignal, this, SLOT(onFRSessionClosed(const QVariantMap &)));
  1175. if (!frPlugin->initialize(parameters))
  1176. {
  1177. toLog(LogLevel::Warning, QString("FR %1 error initialize. Skip it.").arg(plugin->getPluginName()));
  1178. pluginLoader->destroyPlugin(plugin);
  1179. continue;
  1180. }
  1181. mFiscalRegister = frPlugin;
  1182. toLog(LogLevel::Normal, QString("FR %1 loaded successful.").arg(plugin->getPluginName()));
  1183. break;
  1184. }
  1185. }
  1186. }
  1187. //---------------------------------------------------------------------------
  1188. SDK::PaymentProcessor::IFiscalRegister * PrintingService::getFiscalRegister() const
  1189. {
  1190. return mFiscalRegister;
  1191. }
  1192. //---------------------------------------------------------------------------
  1193. void PrintingService::setFiscalNumber(qint64 aPaymentId, const QVariantMap & aParameters)
  1194. {
  1195. QList<PPSDK::IPayment::SParameter> parameters;
  1196. foreach (auto name, aParameters.keys())
  1197. {
  1198. parameters.push_back(PPSDK::IPayment::SParameter(name, aParameters.value(name), true));
  1199. }
  1200. if (!PaymentService::instance(mApplication)->updatePaymentFields(aPaymentId, parameters))
  1201. {
  1202. toLog(LogLevel::Error, QString("Payment %1: Error update fiscal parameters.").arg(aPaymentId));
  1203. }
  1204. }
  1205. //---------------------------------------------------------------------------
  1206. SDK::PaymentProcessor::SCurrencySettings PrintingService::getCurrencySettings() const
  1207. {
  1208. SettingsService * settingsService = SettingsService::instance(mApplication);
  1209. PPSDK::TerminalSettings * terminalSettings = settingsService->getAdapter<PPSDK::TerminalSettings>();
  1210. return terminalSettings->getCurrencySettings();
  1211. }
  1212. //---------------------------------------------------------------------------