@@ -87,6 +87,8 @@ | |||
/src/plugins/NativeScenarios/Migrator3000/ | |||
/src/plugins/NativeWidgets/ServiceMenu/ | |||
/src/plugins/Payments/Cyberplat/ | |||
/src/plugins/ScenarioBackends/UCS/ | |||
/src/plugins/ScenarioBackends/Uniteller/ | |||
/src/plugins/Plugins.sln | |||
/src/qbs/libTemplate.qbs | |||
/src/runtimes/common/ | |||
@@ -100,12 +100,19 @@ void IntegratedDrivers::checkDriverPath(QString & aDriverPath, const QVariantMap | |||
auto parameterIt = std::find_if(parameters.begin(), parameters.end(), [&] (const SPluginParameter & aParameter) -> bool | |||
{ return aParameter.name == jt.key(); }); | |||
if ((parameterIt != parameters.end()) && !parameterIt->readOnly && !parameterIt->possibleValues.values().contains(jt.value()) && | |||
!parameterIt->possibleValues.keys().contains(CHardwareSDK::Mask)) | |||
if (parameterIt != parameters.end()) | |||
{ | |||
paths.removeAt(i--); | |||
SPluginParameter & parameter = *parameterIt; | |||
QList<QVariant> & possibleValueValues = parameter.possibleValues.values(); | |||
QList<QString> & possibleValueKeys = parameter.possibleValues.keys(); | |||
const QVariant & value = jt.value(); | |||
break; | |||
if (!parameter.readOnly && !possibleValueValues.contains(value) && (value != CHardwareSDK::Values::Auto) && !possibleValueKeys.contains(CHardwareSDK::Mask)) | |||
{ | |||
paths.removeAt(i--); | |||
break; | |||
} | |||
} | |||
} | |||
} | |||
@@ -941,7 +941,7 @@ void PaymentService::hangupProcessing() | |||
} | |||
//--------------------------------------------------------------------------- | |||
void PaymentService::doUpdatePaymentFields(quint64 aID, std::shared_ptr<PPSDK::IPayment> aPayment, const QList<SDK::PaymentProcessor::IPayment::SParameter> & aFields, bool aForce) | |||
void PaymentService::doUpdatePaymentFields(qint64 aID, std::shared_ptr<PPSDK::IPayment> aPayment, const QList<SDK::PaymentProcessor::IPayment::SParameter> & aFields, bool aForce) | |||
{ | |||
if (!aPayment) | |||
{ | |||
@@ -222,7 +222,7 @@ private: | |||
bool setChangeAmount(double aChange, std::shared_ptr<PPSDK::IPayment> aPaymentSource); | |||
/// Обновляет параметры платежа. | |||
void doUpdatePaymentFields(quint64 aID, std::shared_ptr<PPSDK::IPayment> aPayment, const QList<SDK::PaymentProcessor::IPayment::SParameter> & aFields, bool aForce = false); | |||
void doUpdatePaymentFields(qint64 aID, std::shared_ptr<PPSDK::IPayment> aPayment, const QList<SDK::PaymentProcessor::IPayment::SParameter> & aFields, bool aForce = false); | |||
signals: | |||
/// Сигнал о завершении обработки платёжной команды, добавленной методом registerPaymentCommand. | |||
@@ -18,7 +18,6 @@ | |||
// Project | |||
#include "PrintingCommands.h" | |||
#include "PrintingService.h" | |||
#include "PrintConstants.h" | |||
#include "PaymentService.h" | |||
namespace FiscalCommand = SDK::Driver::EFiscalPrinterCommand; | |||
@@ -31,6 +30,20 @@ void PrintCommand::setReceiptTemplate(const QString & aTemplateName) | |||
} | |||
//--------------------------------------------------------------------------- | |||
QVariantMap PrintCommand::getPrintingParameters(SDK::Driver::IPrinter * aPrinter) | |||
{ | |||
QVariantMap configuration = aPrinter->getDeviceConfiguration(); | |||
QVariantMap result; | |||
if (configuration.contains(CHardwareSDK::Printer::LineSize) && configuration[CHardwareSDK::Printer::LineSize].isValid()) | |||
{ | |||
result.insert(CHardwareSDK::Printer::LineSize, configuration[CHardwareSDK::Printer::LineSize]); | |||
} | |||
return result; | |||
} | |||
//--------------------------------------------------------------------------- | |||
PrintFiscalCommand::PrintFiscalCommand(const QString & aReceiptType, FiscalCommand::Enum aFiscalCommand, PrintingService * aService) : | |||
PrintCommand(aReceiptType), | |||
mFiscalCommand(aFiscalCommand), | |||
@@ -134,6 +147,11 @@ DSDK::SPaymentData PrintFiscalCommand::getPaymentData(const QVariantMap & aParam | |||
result.fiscalParameters[CHardwareSDK::FR::UserPhone] = QString(); | |||
result.fiscalParameters[CHardwareSDK::FR::UserMail] = QString(); | |||
foreach (auto FFData, CPrintCommands::FFDataList) | |||
{ | |||
result.fiscalParameters[FFData] = aParameters[FFData]; | |||
} | |||
#if 0 | |||
//TODO - решить как на верхнем уровне в интерфейсе мы будем давать возможность | |||
// вводить телефон/email для отправки чека ПЕРЕД печатью этого чека. | |||
@@ -178,37 +196,45 @@ bool PrintFiscalCommand::canFiscalPrint(DSDK::IPrinter * aPrinter, bool aRealChe | |||
} | |||
//--------------------------------------------------------------------------- | |||
bool PrintFiscalCommand::getFiscalInfo(QVariantMap & aParameters, QStringList & aReceiptLines) | |||
bool PrintFiscalCommand::getFiscalInfo(QVariantMap & aParameters, QStringList & aReceiptLines, bool aWaitResult) | |||
{ | |||
PPSDK::IFiscalRegister * fr = mService->getFiscalRegister(); | |||
if (fr && fr->haveCapability(PPSDK::IFiscalRegister::Receipt)) | |||
if (!fr || !fr->hasCapability(PPSDK::ERequestType::Receipt) || !fr->isReady(PPSDK::ERequestType::Receipt)) | |||
{ | |||
qint64 paymentId = aParameters.value(PPSDK::CPayment::Parameters::ID).toLongLong(); | |||
QStringList parameterNames = fr->getParameterNames(); | |||
return false; | |||
} | |||
// Если в параметрах платежа ещё нет информации о фискальном номере | |||
bool OK = !parameterNames.toSet().intersect(aParameters.keys().toSet()).isEmpty(); | |||
if (!OK) | |||
{ | |||
auto fiscalParameters = fr->createFiscalTicket(paymentId, aParameters, getPaymentData(aParameters)); | |||
OK = !fiscalParameters.isEmpty(); | |||
aParameters.unite(fiscalParameters); | |||
qint64 paymentId = aParameters.value(PPSDK::CPayment::Parameters::ID).toLongLong(); | |||
QStringList parameterNames = fr->getParameterNames(); | |||
mService->setFiscalNumber(paymentId, fiscalParameters); | |||
} | |||
// Если в параметрах платежа ещё нет информации о фискальном номере | |||
bool OK = !parameterNames.toSet().intersect(aParameters.keys().toSet()).isEmpty(); | |||
SDK::Driver::SPaymentData fiscalPaymentData; | |||
if (!OK) | |||
{ | |||
fiscalPaymentData = getPaymentData(aParameters); | |||
auto fiscalParameters = fr->createFiscalTicket(paymentId, aParameters, fiscalPaymentData, aWaitResult); | |||
if (OK) | |||
if (!aWaitResult) | |||
{ | |||
aReceiptLines = fr->getReceipt(paymentId, aParameters); | |||
return true; | |||
} | |||
return OK; | |||
OK = !fiscalParameters.isEmpty(); | |||
aParameters.unite(fiscalParameters); | |||
mService->setFiscalNumber(paymentId, fiscalParameters); | |||
} | |||
if (OK) | |||
{ | |||
aReceiptLines = fr->getReceipt(paymentId, aParameters, fiscalPaymentData); | |||
} | |||
return false; | |||
return OK; | |||
} | |||
//--------------------------------------------------------------------------- | |||
@@ -225,6 +251,16 @@ bool PrintPayment::canPrint(DSDK::IPrinter * aPrinter, bool aRealCheck) | |||
} | |||
//--------------------------------------------------------------------------- | |||
bool PrintPayment::makeFiscalByFR(const QVariantMap & aParameters) | |||
{ | |||
QStringList receipt = mService->getReceipt(mReceiptTemplate, aParameters); | |||
QVariantMap parameters(aParameters); | |||
QStringList fiscalPart; | |||
return getFiscalInfo(parameters, fiscalPart, false); | |||
} | |||
//--------------------------------------------------------------------------- | |||
bool PrintPayment::print(DSDK::IPrinter * aPrinter, const QVariantMap & aParameters) | |||
{ | |||
// Добавляем строки основного чека | |||
@@ -245,9 +281,11 @@ bool PrintPayment::print(DSDK::IPrinter * aPrinter, const QVariantMap & aParamet | |||
QStringList receipt = mService->getReceipt(mReceiptTemplate, actualParameters); | |||
QVariantMap parameters = aParameters; | |||
QVariantMap parameters = QVariantMap(aParameters).unite(getPrintingParameters(aPrinter)); | |||
QStringList fiscalPart; | |||
bool hasFiscalInfo = getFiscalInfo(parameters, fiscalPart); | |||
bool hasFiscalInfo = getFiscalInfo(parameters, fiscalPart, true); | |||
if (hasFiscalInfo) | |||
{ | |||
receipt.append(fiscalPart); | |||
@@ -339,16 +377,17 @@ bool PrintPayment::isFiscal(DSDK::IPrinter * aPrinter) | |||
bool PrintBalance::print(DSDK::IPrinter * aPrinter, const QVariantMap & aParameters) | |||
{ | |||
QStringList receipt = mService->getReceipt(mReceiptType, expandFields(aParameters)); | |||
auto fr = dynamic_cast<DSDK::IFiscalPrinter *>(aPrinter); | |||
auto virtualFR = mService->getFiscalRegister(); | |||
QStringList fiscalReceipt; | |||
if (mFiscalMode && virtualFR && virtualFR->haveCapability(PPSDK::IFiscalRegister::Balance)) | |||
if (mFiscalMode && virtualFR && virtualFR->hasCapability(PPSDK::ERequestType::XReport) && virtualFR->isReady(PPSDK::ERequestType::XReport) && | |||
virtualFR->serviceRequest(PPSDK::ERequestType::XReport, fiscalReceipt, getPrintingParameters(aPrinter))) | |||
{ | |||
receipt.append(virtualFR->balance()); | |||
receipt << fiscalReceipt; | |||
} | |||
mService->saveReceiptContent(QString("%1_balance").arg(QTime::currentTime().toString("hhmmsszzz")), receipt); | |||
auto fr = dynamic_cast<DSDK::IFiscalPrinter *>(aPrinter); | |||
return mFiscalMode && fr && fr->isFiscalReady(false, mFiscalCommand) ? fr->printXReport(receipt) : aPrinter->print(receipt); | |||
} | |||
@@ -406,17 +445,17 @@ bool PrintEncashment::print(DSDK::IPrinter * aPrinter, const QVariantMap & aPara | |||
{ | |||
// Сохраняем чек перед печатью. | |||
QStringList receipt = mService->getReceipt(mReceiptType, expandFields(aParameters)); | |||
auto virtualFR = mService->getFiscalRegister(); | |||
QStringList fiscalReceipt; | |||
// Производим выплату фискального регистратора. | |||
if (mFiscalMode && virtualFR && virtualFR->haveCapability(PPSDK::IFiscalRegister::Encashment)) | |||
if (mFiscalMode && virtualFR && virtualFR->hasCapability(PPSDK::ERequestType::Encashment) && virtualFR->isReady(PPSDK::ERequestType::Encashment) && | |||
virtualFR->serviceRequest(PPSDK::ERequestType::Encashment, fiscalReceipt, getPrintingParameters(aPrinter))) | |||
{ | |||
receipt.append(virtualFR->encashment()); | |||
receipt << fiscalReceipt; | |||
} | |||
mService->saveReceiptContent(QString("%1_%2_encashment").arg(QTime::currentTime().toString("hhmmsszzz")).arg(aParameters["ENCASHMENT_NUMBER"].toString()), receipt); | |||
auto fr = dynamic_cast<DSDK::IFiscalPrinter *>(aPrinter); | |||
if (!canPrint(aPrinter, false)) | |||
@@ -434,7 +473,7 @@ bool PrintZReport::canPrint(DSDK::IPrinter * aPrinter, bool aRealCheck) | |||
auto virtualFR = mService->getFiscalRegister(); | |||
return (virtualFR && virtualFR->haveCapability(PPSDK::IFiscalRegister::ZReport) && PrintCommand::canPrint(aPrinter, aRealCheck)) || | |||
return (virtualFR && virtualFR->hasCapability(PPSDK::ERequestType::ZReport) && virtualFR->isReady(PPSDK::ERequestType::ZReport) && PrintCommand::canPrint(aPrinter, aRealCheck)) || | |||
(fr && fr->isFiscalReady(aRealCheck, mFiscalCommand)); | |||
} | |||
@@ -449,18 +488,18 @@ bool PrintZReport::print(DSDK::IPrinter * aPrinter, const QVariantMap & /*aParam | |||
} | |||
auto virtualFR = mService->getFiscalRegister(); | |||
QStringList fiscalReport; | |||
if (virtualFR && virtualFR->haveCapability(PPSDK::IFiscalRegister::ZReport)) | |||
if (virtualFR && virtualFR->hasCapability(PPSDK::ERequestType::ZReport) && virtualFR->isReady(PPSDK::ERequestType::ZReport) && | |||
virtualFR->serviceRequest(PPSDK::ERequestType::ZReport, fiscalReport, getPrintingParameters(aPrinter))) | |||
{ | |||
QStringList report = virtualFR->getZreport(); | |||
if (!report.isEmpty()) | |||
if (!fiscalReport.isEmpty()) | |||
{ | |||
result = aPrinter && aPrinter->print(report); | |||
result = aPrinter && aPrinter->print(fiscalReport); | |||
mService->saveReceiptContent(QString("%1_Z_report%2") | |||
.arg(QTime::currentTime().toString("hhmmsszzz")) | |||
.arg(result ? "" : CPrintCommands::NotPrintedPostfix), report); | |||
.arg(result ? "" : CPrintCommands::NotPrintedPostfix), fiscalReport); | |||
} | |||
} | |||
@@ -13,6 +13,9 @@ | |||
#include <SDK/Drivers/FR/FiscalPrinterCommand.h> | |||
#include <SDK/Drivers/IFiscalPrinter.h> | |||
// Project | |||
#include "PrintConstants.h" | |||
namespace FiscalCommand = SDK::Driver::EFiscalPrinterCommand; | |||
namespace PPSDK = SDK::PaymentProcessor; | |||
@@ -24,6 +27,15 @@ namespace CPrintCommands | |||
/// Шаблон имени файла фискального чека. | |||
const char ReceiptNameTemplate[] = "hhmmsszzz"; | |||
/// Данные фискальных тегов. | |||
const QStringList FFDataList = QStringList() | |||
<< CPrintConstants::OpPhone | |||
<< CPrintConstants::DealerSupportPhone | |||
<< CPrintConstants::BankPhone | |||
<< CPrintConstants::BankAddress | |||
<< CPrintConstants::BankInn | |||
<< CPrintConstants::BankName; | |||
} | |||
//--------------------------------------------------------------------------- | |||
@@ -55,6 +67,9 @@ public: | |||
/// Возвращает тип чека. | |||
QString getReceiptType() const { return mReceiptType; } | |||
/// Получить параметры принтера для печати. | |||
QVariantMap getPrintingParameters(SDK::Driver::IPrinter * aPrinter); | |||
protected: | |||
QString mReceiptType; | |||
QString mReceiptTemplate; | |||
@@ -77,7 +92,7 @@ protected: | |||
bool canFiscalPrint(SDK::Driver::IPrinter * aPrinter, bool aRealCheck); | |||
/// Получить строки с фискальной информацией чека | |||
bool getFiscalInfo(QVariantMap & aParameters, QStringList & aReceiptLines); | |||
bool getFiscalInfo(QVariantMap & aParameters, QStringList & aReceiptLines, bool aWaitResult); | |||
FiscalCommand::Enum mFiscalCommand; | |||
@@ -102,6 +117,9 @@ public: | |||
/// Печать. | |||
virtual bool print(SDK::Driver::IPrinter * aPrinter, const QVariantMap & aParameters); | |||
/// Сформировать фискальный чек через фискальный сервер. | |||
bool makeFiscalByFR(const QVariantMap & aParameters); | |||
private: | |||
/// Добавить данные платежа. | |||
void addFiscalPaymentData(const SDK::Driver::TFiscalPaymentData & aFPData, QStringList & aData); | |||
@@ -36,6 +36,9 @@ | |||
#include <SDK/Drivers/Components.h> | |||
#include <SDK/Drivers/HardwareConstants.h> | |||
// Common | |||
#include "Common/ExitAction.h" | |||
// Project | |||
#include "System/IApplication.h" | |||
@@ -53,6 +56,10 @@ | |||
namespace PPSDK = SDK::PaymentProcessor; | |||
//------------------------------------------------------------------------------ | |||
const char * PPSDK::IFiscalRegister::OFDNotSentSignal = SIGNAL(OFDNotSent(bool)); | |||
//------------------------------------------------------------------------------ | |||
namespace CPrintingService | |||
{ | |||
const QString ReceiptDateTimeFormat = "dd.MM.yyyy hh:mm:ss"; | |||
@@ -165,8 +172,9 @@ bool PrintingService::shutdown() | |||
if (mFiscalRegister) | |||
{ | |||
SDK::Plugin::IPluginLoader * pluginLoader = PluginService::instance(mApplication)->getPluginLoader(); | |||
pluginLoader->destroyPlugin(dynamic_cast<SDK::Plugin::IPlugin *>(mFiscalRegister)); | |||
mFiscalRegister = nullptr; | |||
} | |||
return true; | |||
@@ -303,26 +311,39 @@ int PrintingService::performPrint(PrintCommand * aCommand, const QVariantMap & a | |||
mPrintingFunction = | |||
[this, aReceiptTemplate](int aJobIndex, PrintCommand * aCommand, QVariantMap aParameters) -> bool | |||
{ | |||
QVariantMap staticParameters; | |||
joinMap(staticParameters, mStaticParameters); | |||
QVariantMap paymentParameters = joinMap(aParameters, staticParameters); | |||
auto printer = takePrinter(aCommand->getReceiptType(), false); | |||
PrintPayment * paymentPrintingCommand = dynamic_cast<PrintPayment *>(aCommand); | |||
auto makeResult = [&] (bool result, const QString & aLog) -> bool { if (aCommand) delete aCommand; | |||
toLog(result ? LogLevel::Normal : LogLevel::Error, aLog); emit receiptPrinted(aJobIndex, !result); return result; }; | |||
if (!printer) | |||
{ | |||
delete aCommand; | |||
if (!getFiscalRegister()) | |||
{ | |||
return makeResult(false, "Failed to process receipt without printer due to no fiscal register"); | |||
} | |||
else if (!paymentPrintingCommand) | |||
{ | |||
return makeResult(false, "Failed to process receipt without printer due to no printing command"); | |||
} | |||
return false; | |||
} | |||
bool result = paymentPrintingCommand->makeFiscalByFR(paymentParameters); | |||
QVariantMap staticParameters; | |||
return makeResult(result, "Send receipt printed without printer"); | |||
} | |||
QVariantMap configuration; | |||
configuration.insert(CHardwareSDK::Printer::ContinuousMode, mContinuousMode); | |||
configuration.insert(CHardwareSDK::Printer::ServiceOperation, mServiceOperation); | |||
configuration.insert(CHardwareSDK::Printer::TemplateParameters, aParameters); | |||
configuration.insert(CHardwareSDK::Printer::ReceiptParameters, joinMap(staticParameters, mStaticParameters)); | |||
configuration.insert(CHardwareSDK::Printer::ReceiptTemplate, aReceiptTemplate); | |||
printer->setDeviceConfiguration(configuration); | |||
bool result = aCommand->print(printer, joinMap(aParameters, staticParameters)); | |||
bool result = aCommand->print(printer, paymentParameters); | |||
if (result) | |||
{ | |||
@@ -331,11 +352,7 @@ int PrintingService::performPrint(PrintCommand * aCommand, const QVariantMap & a | |||
giveBackPrinter(printer); | |||
delete aCommand; | |||
emit receiptPrinted(aJobIndex, !result); | |||
return result; | |||
return makeResult(result, "Send receipt printed"); | |||
}; | |||
int taskIndex = mNextReceiptIndex.fetchAndAddOrdered(1); | |||
@@ -385,6 +402,12 @@ int PrintingService::printReport(const QString & aReceiptType, const QVariantMap | |||
} | |||
//--------------------------------------------------------------------------- | |||
bool PrintingService::hasFiscalRegister() | |||
{ | |||
return mFiscalRegister && mFiscalRegister->hasCapability(PPSDK::ERequestType::Receipt); | |||
} | |||
//--------------------------------------------------------------------------- | |||
void PrintingService::giveBackPrinter(DSDK::IPrinter * aPrinter) | |||
{ | |||
if (aPrinter) | |||
@@ -1192,56 +1215,58 @@ void PrintingService::expandTags(QStringList & aReceipt, const QVariantMap & aPa | |||
} | |||
//--------------------------------------------------------------------------- | |||
void PrintingService::loadReceiptTemplates() | |||
bool PrintingService::loadReceiptTemplate(const QFileInfo & aFileInfo) | |||
{ | |||
auto loadReceiptTemplate = [&](const QFileInfo & aFileInfo) | |||
if (aFileInfo.suffix().compare("xml", Qt::CaseInsensitive)) | |||
{ | |||
if (!aFileInfo.suffix().compare("xml", Qt::CaseInsensitive)) | |||
{ | |||
QDomDocument xmlFile(aFileInfo.fileName()); | |||
QFile file(aFileInfo.filePath()); | |||
if (!file.open(QIODevice::ReadOnly)) | |||
{ | |||
toLog(LogLevel::Error, QString("Failed to open file %1").arg(aFileInfo.filePath())); | |||
return; | |||
} | |||
toLog(LogLevel::Error, QString("Bad receipt template file extension : %1").arg(aFileInfo.fileName())); | |||
return false; | |||
} | |||
xmlFile.setContent(&file); | |||
QDomDocument xmlFile(aFileInfo.fileName()); | |||
QFile file(aFileInfo.filePath()); | |||
QDomElement body = xmlFile.documentElement(); | |||
if (!file.open(QIODevice::ReadOnly)) | |||
{ | |||
toLog(LogLevel::Error, QString("Failed to open file %1").arg(aFileInfo.filePath())); | |||
return false; | |||
} | |||
QStringList receiptContents; | |||
xmlFile.setContent(&file); | |||
for (QDomNode node = body.firstChild(); !node.isNull(); node = node.nextSibling()) | |||
{ | |||
QDomElement row = node.toElement(); | |||
if (row.tagName() == "string" || row.tagName() == "else") | |||
{ | |||
QString prefix = row.attribute("if").isEmpty() ? "" : QString("%1%2").arg(row.attribute("if")).arg(CPrintingService::ConditionTag); | |||
receiptContents.append(QString("%1%2").arg(prefix).arg(row.text())); | |||
} | |||
else if (row.tagName() == "hr") | |||
{ | |||
receiptContents.append("[hr]-[/hr]"); | |||
} | |||
} | |||
QDomElement body = xmlFile.documentElement(); | |||
if (!receiptContents.isEmpty()) | |||
{ | |||
mCachedReceipts.insert(aFileInfo.baseName().toLower(), receiptContents); | |||
} | |||
else | |||
{ | |||
toLog(LogLevel::Error, QString("Bad receipt template '%1': parse xml error").arg(aFileInfo.fileName())); | |||
} | |||
QStringList receiptContents; | |||
for (QDomNode node = body.firstChild(); !node.isNull(); node = node.nextSibling()) | |||
{ | |||
QDomElement row = node.toElement(); | |||
if (row.tagName() == "string" || row.tagName() == "else") | |||
{ | |||
QString prefix = row.attribute("if").isEmpty() ? "" : QString("%1%2").arg(row.attribute("if")).arg(CPrintingService::ConditionTag); | |||
receiptContents.append(QString("%1%2").arg(prefix).arg(row.text())); | |||
} | |||
else | |||
else if (row.tagName() == "hr") | |||
{ | |||
toLog(LogLevel::Error, QString("Bad receipt template file extension : %1").arg(aFileInfo.fileName())); | |||
receiptContents.append("[hr]-[/hr]"); | |||
} | |||
}; | |||
} | |||
if (receiptContents.isEmpty()) | |||
{ | |||
toLog(LogLevel::Error, QString("Bad receipt template '%1': parse xml error").arg(aFileInfo.fileName())); | |||
return false; | |||
} | |||
mCachedReceipts.insert(aFileInfo.baseName().toLower(), receiptContents); | |||
return true; | |||
} | |||
//--------------------------------------------------------------------------- | |||
void PrintingService::loadReceiptTemplates() | |||
{ | |||
// Загружаем все шаблоны чеков в папке ./receipts | |||
QDir receiptDirectory; | |||
@@ -1360,6 +1385,26 @@ QString PrintingService::loadReceipt(qint64 aPaymentId) | |||
} | |||
//--------------------------------------------------------------------------- | |||
void PrintingService::onOFDNotSent(bool aExist) | |||
{ | |||
auto service = SettingsService::instance(mApplication); | |||
auto adapter = service ? service->getAdapter<PPSDK::TerminalSettings>() : nullptr; | |||
bool block = adapter ? adapter->getCommonSettings().blockOn(PPSDK::SCommonSettings::PrinterError) : true; | |||
QVariantMap configuration; | |||
configuration.insert(CHardwareSDK::Printer::OFDNotSentError, aExist); | |||
configuration.insert(CHardwareSDK::Printer::BlockTerminalOnError, block); | |||
foreach (auto printer, mPrinterDevices) | |||
{ | |||
if (printer) | |||
{ | |||
printer->setDeviceConfiguration(configuration); | |||
} | |||
} | |||
} | |||
//--------------------------------------------------------------------------- | |||
void PrintingService::onStatusChanged(DSDK::EWarningLevel::Enum aWarningLevel, const QString & /*aTranslation*/, int /*aStatus*/) | |||
{ | |||
emit printerStatus(aWarningLevel == DSDK::EWarningLevel::OK); | |||
@@ -1384,36 +1429,38 @@ void PrintingService::onFRSessionClosed(const QVariantMap & aParameters) | |||
QFile file(fileName); | |||
// Сохраняем отчёт. | |||
if (file.open(QIODevice::WriteOnly)) | |||
if (!file.open(QIODevice::WriteOnly)) | |||
{ | |||
using namespace SDK::Driver; | |||
QXmlStreamWriter stream(&file); | |||
stream.setAutoFormatting(true); | |||
stream.writeStartDocument(); | |||
stream.writeStartElement(CFRReport::ZReport); | |||
stream.writeAttribute(CFRReport::Number, aParameters[CFiscalPrinter::ZReportNumber].toString()); | |||
stream.writeStartElement(CFRReport::FR); | |||
stream.writeAttribute(CFRReport::Serial, aParameters[CFiscalPrinter::Serial].toString()); | |||
stream.writeAttribute(CFRReport::RNM, aParameters[CFiscalPrinter::RNM].toString()); | |||
stream.writeEndElement(); // CFRReport::FR | |||
stream.writeTextElement(CFRReport::PaymentCount, aParameters[CFiscalPrinter::PaymentCount].toString()); | |||
double amount = aParameters[CFiscalPrinter::PaymentAmount].toDouble(); | |||
stream.writeTextElement(CFRReport::PaymentAmount, QString::number(amount, 'f', 2)); | |||
amount = aParameters[CFiscalPrinter::NonNullableAmount].toDouble(); | |||
stream.writeTextElement(CFRReport::NonNullableAmount, QString::number(amount, 'f', 2)); | |||
stream.writeTextElement(CFRReport::FRDateTime, aParameters[CFiscalPrinter::FRDateTime].toString()); | |||
stream.writeTextElement(CFRReport::SystemDateTime, aParameters[CFiscalPrinter::SystemDateTime].toString()); | |||
stream.writeEndElement(); // CFRReport::ZReport | |||
stream.writeEndDocument(); | |||
file.flush(); | |||
file.close(); | |||
return; | |||
} | |||
using namespace SDK::Driver; | |||
QXmlStreamWriter stream(&file); | |||
stream.setAutoFormatting(true); | |||
stream.writeStartDocument(); | |||
stream.writeStartElement(CFRReport::ZReport); | |||
stream.writeAttribute(CFRReport::Number, aParameters[CFiscalPrinter::ZReportNumber].toString()); | |||
stream.writeStartElement(CFRReport::FR); | |||
stream.writeAttribute(CFRReport::Serial, aParameters[CFiscalPrinter::Serial].toString()); | |||
stream.writeAttribute(CFRReport::RNM, aParameters[CFiscalPrinter::RNM].toString()); | |||
stream.writeEndElement(); // CFRReport::FR | |||
stream.writeTextElement(CFRReport::PaymentCount, aParameters[CFiscalPrinter::PaymentCount].toString()); | |||
double amount = aParameters[CFiscalPrinter::PaymentAmount].toDouble(); | |||
stream.writeTextElement(CFRReport::PaymentAmount, QString::number(amount, 'f', 2)); | |||
amount = aParameters[CFiscalPrinter::NonNullableAmount].toDouble(); | |||
stream.writeTextElement(CFRReport::NonNullableAmount, QString::number(amount, 'f', 2)); | |||
stream.writeTextElement(CFRReport::FRDateTime, aParameters[CFiscalPrinter::FRDateTime].toString()); | |||
stream.writeTextElement(CFRReport::SystemDateTime, aParameters[CFiscalPrinter::SystemDateTime].toString()); | |||
stream.writeEndElement(); // CFRReport::ZReport | |||
stream.writeEndDocument(); | |||
file.flush(); | |||
file.close(); | |||
} | |||
//--------------------------------------------------------------------------- | |||
@@ -1440,37 +1487,37 @@ void PrintingService::updateHardwareConfiguration() | |||
{ | |||
DSDK::IPrinter * device = dynamic_cast<DSDK::IPrinter *>(mDeviceService->acquireDevice(printerName)); | |||
if (device) | |||
if (!device) | |||
{ | |||
QVariantMap dealerSettings; | |||
if (mStaticParameters.contains(CPrintConstants::DealerTaxSystem)) dealerSettings.insert(CHardwareSDK::FR::DealerTaxSystem, mStaticParameters[CPrintConstants::DealerTaxSystem]); | |||
if (mStaticParameters.contains(CPrintConstants::DealerAgentFlag)) dealerSettings.insert(CHardwareSDK::FR::DealerAgentFlag, mStaticParameters[CPrintConstants::DealerAgentFlag]); | |||
if (mStaticParameters.contains(CPrintConstants::DealerVAT)) dealerSettings.insert(CHardwareSDK::FR::DealerVAT, mStaticParameters[CPrintConstants::DealerVAT]); | |||
toLog(LogLevel::Error, QString("Failed to acquire device %1 .").arg(printerName)); | |||
continue; | |||
} | |||
mPrinterDevices.append(device); | |||
QVariantMap dealerSettings; | |||
if (mStaticParameters.contains(CPrintConstants::DealerTaxSystem)) dealerSettings.insert(CHardwareSDK::FR::DealerTaxSystem, mStaticParameters[CPrintConstants::DealerTaxSystem]); | |||
if (mStaticParameters.contains(CPrintConstants::DealerAgentFlag)) dealerSettings.insert(CHardwareSDK::FR::DealerAgentFlag, mStaticParameters[CPrintConstants::DealerAgentFlag]); | |||
if (mStaticParameters.contains(CPrintConstants::DealerVAT)) dealerSettings.insert(CHardwareSDK::FR::DealerVAT, mStaticParameters[CPrintConstants::DealerVAT]); | |||
if (mStaticParameters.contains(CPrintConstants::DealerSupportPhone)) dealerSettings.insert(CHardwareSDK::FR::DealerSupportPhone, mStaticParameters[CPrintConstants::DealerSupportPhone]); | |||
// Подписываемся на события принтера. | |||
device->subscribe(SDK::Driver::IDevice::StatusSignal, this, SLOT(onStatusChanged(SDK::Driver::EWarningLevel::Enum, const QString &, int))); | |||
mPrinterDevices.append(device); | |||
// Подписываемся на события фискальника. | |||
if (dynamic_cast<DSDK::IFiscalPrinter *>(device)) | |||
{ | |||
device->subscribe(SDK::Driver::IFiscalPrinter::FRSessionClosedSignal, this, SLOT(onFRSessionClosed(const QVariantMap &))); | |||
// Подписываемся на события принтера. | |||
device->subscribe(SDK::Driver::IDevice::StatusSignal, this, SLOT(onStatusChanged(SDK::Driver::EWarningLevel::Enum, const QString &, int))); | |||
if (autoZReportTime.isValid() && !autoZReportTime.isNull()) | |||
{ | |||
toLog(LogLevel::Normal, QString("Setup auto z-report time: %1.").arg(autoZReportTime.toString("hh:mm:ss"))); | |||
// Подписываемся на события фискальника. | |||
if (dynamic_cast<DSDK::IFiscalPrinter *>(device)) | |||
{ | |||
device->subscribe(SDK::Driver::IFiscalPrinter::FRSessionClosedSignal, this, SLOT(onFRSessionClosed(const QVariantMap &))); | |||
dealerSettings.insert(CHardwareSDK::FR::ZReportTime, autoZReportTime); | |||
} | |||
} | |||
if (autoZReportTime.isValid() && !autoZReportTime.isNull()) | |||
{ | |||
toLog(LogLevel::Normal, QString("Setup auto z-report time: %1.").arg(autoZReportTime.toString("hh:mm:ss"))); | |||
device->setDeviceConfiguration(dealerSettings); | |||
} | |||
else | |||
{ | |||
toLog(LogLevel::Error, QString("Failed to acquire device %1 .").arg(printerName)); | |||
dealerSettings.insert(CHardwareSDK::FR::ZReportTime, autoZReportTime); | |||
} | |||
} | |||
device->setDeviceConfiguration(dealerSettings); | |||
} | |||
mAvailablePrinters = mPrinterDevices.toSet(); | |||
@@ -1479,51 +1526,59 @@ void PrintingService::updateHardwareConfiguration() | |||
//--------------------------------------------------------------------------- | |||
void PrintingService::createFiscalRegister() | |||
{ | |||
if (!mFiscalRegister) | |||
if (mFiscalRegister) | |||
{ | |||
// Получаем информацию о фискальных регистраторах. | |||
PPSDK::ExtensionsSettings * extSettings = SettingsService::instance(mApplication)->getAdapter<PPSDK::ExtensionsSettings>(); | |||
SDK::Plugin::IPluginLoader * pluginLoader = PluginService::instance(mApplication)->getPluginLoader(); | |||
return; | |||
} | |||
// Получаем информацию о фискальных регистраторах. | |||
PPSDK::ExtensionsSettings * extSettings = SettingsService::instance(mApplication)->getAdapter<PPSDK::ExtensionsSettings>(); | |||
SDK::Plugin::IPluginLoader * pluginLoader = PluginService::instance(mApplication)->getPluginLoader(); | |||
QStringList frPlugins = pluginLoader->getPluginList(QRegExp(PPSDK::CComponents::FiscalRegister)); | |||
foreach (auto fr, frPlugins) | |||
{ | |||
auto plugin = pluginLoader->createPlugin(fr); | |||
foreach(auto fr, pluginLoader->getPluginList(QRegExp(PPSDK::CComponents::FiscalRegister))) | |||
if (!plugin) | |||
{ | |||
auto plugin = pluginLoader->createPlugin(fr); | |||
if (!plugin) | |||
{ | |||
continue; | |||
} | |||
continue; | |||
} | |||
PPSDK::IFiscalRegister * frPlugin = dynamic_cast<PPSDK::IFiscalRegister *>(plugin); | |||
PPSDK::IFiscalRegister * frPlugin = dynamic_cast<PPSDK::IFiscalRegister *>(plugin); | |||
if (!frPlugin) | |||
{ | |||
toLog(LogLevel::Error, QString("FR %1 not have IFiscalRegister interface.").arg(fr)); | |||
pluginLoader->destroyPlugin(plugin); | |||
continue; | |||
} | |||
if (!frPlugin) | |||
{ | |||
toLog(LogLevel::Error, QString("FR %1 not have IFiscalRegister interface.").arg(fr)); | |||
pluginLoader->destroyPlugin(plugin); | |||
continue; | |||
} | |||
auto parameters = extSettings->getSettings(plugin->getPluginName()); | |||
auto parameters = extSettings->getSettings(plugin->getPluginName()); | |||
if (parameters.isEmpty()) | |||
{ | |||
toLog(LogLevel::Warning, QString("FR %1 not have extensions settings. Skip it. (check config.xml).").arg(plugin->getPluginName())); | |||
pluginLoader->destroyPlugin(plugin); | |||
continue; | |||
} | |||
if (parameters.isEmpty()) | |||
{ | |||
toLog(LogLevel::Warning, QString("FR %1 not have extensions settings. Skip it. (check config.xml).").arg(plugin->getPluginName())); | |||
pluginLoader->destroyPlugin(plugin); | |||
continue; | |||
} | |||
connect(dynamic_cast<QObject *>(frPlugin), SDK::Driver::IFiscalPrinter::FRSessionClosedSignal, this, SLOT(onFRSessionClosed(const QVariantMap &))); | |||
frPlugin->subscribe(PPSDK::IFiscalRegister::OFDNotSentSignal, this, SLOT(onOFDNotSent(bool))); | |||
if (!frPlugin->initialize(parameters)) | |||
{ | |||
toLog(LogLevel::Warning, QString("FR %1 error initialize. Skip it.").arg(plugin->getPluginName())); | |||
pluginLoader->destroyPlugin(plugin); | |||
continue; | |||
} | |||
if (!frPlugin->initialize(parameters)) | |||
{ | |||
frPlugin->unsubscribe(PPSDK::IFiscalRegister::OFDNotSentSignal, this); | |||
mFiscalRegister = frPlugin; | |||
toLog(LogLevel::Normal, QString("FR %1 loaded successful.").arg(plugin->getPluginName())); | |||
break; | |||
toLog(LogLevel::Warning, QString("FR %1 error initialize. Skip it.").arg(plugin->getPluginName())); | |||
pluginLoader->destroyPlugin(plugin); | |||
continue; | |||
} | |||
mFiscalRegister = frPlugin; | |||
toLog(LogLevel::Normal, QString("FR %1 loaded successful.").arg(plugin->getPluginName())); | |||
break; | |||
} | |||
} | |||
@@ -17,6 +17,7 @@ | |||
#include <QtCore/QSignalMapper> | |||
#include <QtCore/QWaitCondition> | |||
#include <QtCore/QAtomicInt> | |||
#include <QtCore/QFileInfo> | |||
#include <Common/QtHeadersEnd.h> | |||
// SDK | |||
@@ -107,6 +108,9 @@ public: | |||
/// Печать отчета. | |||
virtual int printReport(const QString & aReceiptType, const QVariantMap & aParameters); | |||
/// Может ли работать с фискальным сервером? | |||
virtual bool hasFiscalRegister(); | |||
#pragma endregion | |||
bool enableBlankFiscalData() const { return mEnableBlankFiscalData; } | |||
@@ -147,9 +151,12 @@ private: | |||
/// Первоначальная загрузка значений тегов. | |||
bool loadTags(); | |||
/// Загрузка чеков. | |||
/// Загрузка шаблонов чеков. | |||
void loadReceiptTemplates(); | |||
/// Загрузка шаблона чека из файла. | |||
bool loadReceiptTemplate(const QFileInfo & aFileInfo); | |||
/// Печать чека, возвращает индекс задания, поставленного в очередь. | |||
int performPrint(PrintCommand * aCommand, const QVariantMap & aParameters, QStringList aReceiptTemplate = QStringList()); | |||
@@ -195,6 +202,9 @@ private slots: | |||
/// Обработчик печати с ошибкой | |||
void printEmptyReceipt(int aJobIndex, bool aError); | |||
/// Обработчик сигнала о наличии неотправленных чеков в фисклаьном регистраторе. | |||
void onOFDNotSent(bool aExist); | |||
private: | |||
typedef QMap<QString, QString> TStaticParameters; | |||
typedef QMap<QString, QStringList> TCachedReceipts; | |||
@@ -436,9 +436,26 @@ CUpdaterApp::ExitCode::Enum UpdaterApp::bitsCheckStatus(bool aAlreadyRunning) | |||
// Restart for download w/o bits | |||
parameters << "--no-bits" << "true"; | |||
if (!QProcess::startDetached(qApp->applicationFilePath(), parameters)) | |||
QString commanLine = QDir::toNativeSeparators(qApp->applicationFilePath()); | |||
QString arguments = parameters.join(" "); | |||
QString workDir = QDir::toNativeSeparators(mUpdater->getWorkingDir()); | |||
try | |||
{ | |||
// Shedule restart myself | |||
PhishMe::AddScheduledTask(L"TC_Updater", | |||
commanLine.toStdWString(), | |||
arguments.toStdWString(), | |||
workDir.toStdWString(), | |||
CUpdaterApp::BITSErrorRestartTimeout); | |||
getLog()->write(LogLevel::Normal, QString("Do create updater restart scheduler task OK. Timeout: %1 min.").arg(CUpdaterApp::BITSErrorRestartTimeout / 60)); | |||
} | |||
catch (std::exception & ex) | |||
{ | |||
getLog()->write(LogLevel::Fatal, QString("Couldn't start updater from '%1'.").arg(qApp->applicationFilePath())); | |||
// Something seriously went wrong inside ScheduleTask | |||
getLog()->write(LogLevel::Fatal, QString("Didn't create updater restart scheduler task '%1'. Reason: %2.") | |||
.arg(commanLine).arg(QString::fromLocal8Bit(ex.what()))); | |||
} | |||
return CUpdaterApp::ExitCode::NetworkError; | |||
@@ -29,6 +29,8 @@ namespace CUpdaterApp | |||
/// Таймаут попыток повторнго запуска по финишу BITS | |||
const long BITSCompleteTimeout = 15 * 60; // 15 минут | |||
const long BITSErrorRestartTimeout = 3 * 60; // 3 минуты | |||
typedef enum | |||
{ | |||
Download, // закачиваем обновления | |||
@@ -0,0 +1,24 @@ | |||
/* @file Класс для выполнения функционала при выходе из области видимости. */ | |||
/* @brief Если отпадает необходимость в его вызове - вызвать reset(). */ | |||
#pragma once | |||
// STL | |||
#include <functional> | |||
typedef std::function<void()> TVoidMethod; | |||
//--------------------------------------------------------------------------- | |||
class ExitAction | |||
{ | |||
public: | |||
ExitAction(const TVoidMethod & aAction) : mAction(aAction) {} | |||
~ExitAction() { if (mAction) mAction(); } | |||
bool reset(const TVoidMethod & aAction = TVoidMethod()) { mAction = aAction; return true; } | |||
private: | |||
TVoidMethod mAction; | |||
}; | |||
//--------------------------------------------------------------------------- |
@@ -194,6 +194,7 @@ namespace CHardware | |||
const char BackFeed[] = "back_feed"; | |||
const char PrintPageNumber[] = "print_page_number"; | |||
const char LeftMargin[] = "left_margin"; | |||
const char RightMargin[] = "right_margin"; | |||
} | |||
/// Параметры обработки чека после отрезки. | |||
@@ -43,7 +43,7 @@ namespace CFR { namespace FiscalFields | |||
} | |||
//--------------------------------------------------------------------------- | |||
// Обязательность параметра | |||
// Обязательность тега. | |||
namespace ERequired | |||
{ | |||
enum Enum | |||
@@ -55,26 +55,52 @@ namespace CFR { namespace FiscalFields | |||
} | |||
//--------------------------------------------------------------------------- | |||
// Структура описателя. | |||
// Обобщенный тип (класс) тега. | |||
namespace EClassType | |||
{ | |||
enum Enum | |||
{ | |||
Default = 0, | |||
Money, | |||
INN | |||
}; | |||
} | |||
//--------------------------------------------------------------------------- | |||
// Структура описателя тега. | |||
struct SData | |||
{ | |||
ETypes::Enum type; | |||
QString textKey; | |||
QString translationPF; | |||
ERequired::Enum required; | |||
bool isMoney; | |||
ETypes::Enum type; /// Тип. | |||
QString textKey; /// Текстовый ключ. | |||
QString translationPF; /// Перевод для печатной формы (ПФ). | |||
ERequired::Enum required; /// Обязательность тега. | |||
EClassType::Enum classType; /// Обобщенный тип (класс) тега. | |||
SData(): type(ETypes::None), required(ERequired::No), classType(EClassType::Default) {} | |||
SData(): type(ETypes::None), required(ERequired::No), isMoney(false) {} | |||
SData(ETypes::Enum aType, const QString & aTextKey, ERequired::Enum aRequired): | |||
type(aType), textKey(aTextKey), translationPF(""), required(aRequired), isMoney(false) {} | |||
type(aType), textKey(aTextKey), translationPF(""), required(aRequired), classType(EClassType::Default) {} | |||
SData(ETypes::Enum aType, const QString & aTextKey, EClassType::Enum aClassType): | |||
type(aType), textKey(aTextKey), translationPF(""), required(ERequired::No), classType(aClassType) {} | |||
SData(ETypes::Enum aType, const QString & aTextKey, const QString & aTranslationPF = ""): | |||
type(aType), textKey(aTextKey), translationPF(aTranslationPF), required(ERequired::No), isMoney(false) {} | |||
SData(ETypes::Enum aType, const QString & aTextKey, const QString & aTranslationPF, bool aIsMoney): | |||
type(aType), textKey(aTextKey), translationPF(aTranslationPF), required(ERequired::No), isMoney(aIsMoney) {} | |||
SData(ETypes::Enum aType, const QString & aTextKey, const QString & aTranslationPF, ERequired::Enum aRequired): | |||
type(aType), textKey(aTextKey), translationPF(aTranslationPF), required(aRequired), isMoney(false) {} | |||
type(aType), textKey(aTextKey), translationPF(aTranslationPF), required(ERequired::No), classType(EClassType::Default) {} | |||
SData(ETypes::Enum aType, const QString & aTextKey, const QString & aTranslationPF, EClassType::Enum aClassType): | |||
type(aType), textKey(aTextKey), translationPF(aTranslationPF), required(ERequired::No), classType(aClassType) {} | |||
SData(ETypes::Enum aType, const QString & aTextKey, const QString & aTranslationPF, ERequired::Enum aRequired, EClassType::Enum aClassType = EClassType::Default): | |||
type(aType), textKey(aTextKey), translationPF(aTranslationPF), required(aRequired), classType(aClassType) {} | |||
bool isString() const { return type == ETypes::String; } | |||
bool isSTLV() const { return type == ETypes::STLV; } | |||
bool isTime() const { return type == ETypes::UnixTime; } | |||
bool isMoney() const { return classType == EClassType::Money; } | |||
bool isINN() const { return classType == EClassType::INN; } | |||
}; | |||
}} // namespace CFR::FiscalFields | |||
}} // namespace CFR::FiscalFields | |||
//--------------------------------------------------------------------------- |
@@ -62,8 +62,8 @@ namespace CFR | |||
/// Формальная дата окончания ФН. | |||
inline QString FSValidityDateOff(const QDate & aDate) { return aDate.addDays(-3).toString(CFR::DateLogFormat); } | |||
/// Срок годности обычной (на 13 месяцев) ФН в днях - так фактически, у всех производителей. | |||
const int SimpleFSValidityDays = 410; | |||
/// Срок годности ФН на 15 месяцев (Автоматика/Прагматик) в днях. | |||
const int FS15ValidityDays = 470; | |||
/// Результаты запроса статуса. | |||
namespace Result | |||
@@ -27,6 +27,7 @@ namespace FRStatusCode | |||
const int WrongTaxOnPayment = 264; /// Неверная налоговая ставка на платеже. | |||
const int NeedTimeSynchronization = 265; /// Необходима синхронизация с системным временем. | |||
const int FSVirtualEnd = 266; /// Срок действия ФН должен был закончиться. | |||
const int DealerSupportPhone = 267; /// Телефон техподдержки дилера некорректен. | |||
} | |||
/// Ошибки. | |||
@@ -39,6 +39,7 @@ namespace FRStatusCode | |||
ADD_FR_WARNING(WrongTaxOnPayment, QCoreApplication::translate("FRStatuses", "#wrong_tax_on_payment")); | |||
ADD_FR_WARNING(NeedTimeSynchronization, QCoreApplication::translate("FRStatuses", "#need_time_synchronization")); | |||
ADD_FR_WARNING(FSVirtualEnd, QCoreApplication::translate("FRStatuses", "#fs_virtual_end")); | |||
ADD_FR_WARNING(DealerSupportPhone, QCoreApplication::translate("FRStatuses", "#dealer_support_phone")); | |||
/// Ошибки. | |||
ADD_FR_ERROR(FR, QCoreApplication::translate("FRStatuses", "#fiscal_add_on_error")); | |||
@@ -29,7 +29,7 @@ namespace CPrinters | |||
const int DefaultHRSize = 35; | |||
/// Белый цвет. | |||
const QRgb White = QColor(Qt::white).rgb(); | |||
const QRgb White = QColor(Qt::transparent).rgb(); | |||
/// Действие с незабранным чеком. | |||
namespace ELeftReceiptAction | |||
@@ -22,6 +22,7 @@ namespace PrinterStatusCode | |||
const int TonerNearEnd = 212; /// Тонер заканчивается. | |||
const int PaperEndVirtual = 213; /// Бумага закончилась по показания датчика скорого окончания бумаги. | |||
const int WrongFWConfiguration = 214; /// Неверная конфигурация прошивки. | |||
const int OFDNotSent = 215; /// Нет связи с ОФД сервером (виртуальный статус фискального сервера). | |||
} | |||
/// Ошибки. | |||
@@ -44,6 +45,7 @@ namespace PrinterStatusCode | |||
const int OutletFull = 234; /// Выходной лоток полон. | |||
const int NeedPaperTakeOut = 235; /// Необходимо извлечь бумагу из презентера. | |||
const int MemoryEnd = 236; /// Не хватает памяти. | |||
const int OFDNotSent = 237; /// Нет связи с ОФД сервером (виртуальный статус фискального сервера). | |||
} | |||
} | |||
@@ -27,6 +27,7 @@ namespace PrinterStatusCode | |||
ADD_PRINTER_WARNING(TonerNearEnd, QCoreApplication::translate("PrinterStatuses", "#toner_near_end")); | |||
ADD_PRINTER_WARNING(PaperEndVirtual, QCoreApplication::translate("PrinterStatuses", "#no_paper_virtual")); | |||
ADD_PRINTER_WARNING(WrongFWConfiguration, QCoreApplication::translate("PrinterStatuses", "#wrong_firmware_configuration")); | |||
ADD_PRINTER_WARNING(OFDNotSent, QCoreApplication::translate("PrinterStatuses", "#ofd_not_sent")); | |||
/// Ошибки. | |||
ADD_PRINTER_ERROR(PaperEnd, QCoreApplication::translate("PrinterStatuses", "#no_paper")); | |||
@@ -46,6 +47,7 @@ namespace PrinterStatusCode | |||
ADD_PRINTER_ERROR(OutletFull, QCoreApplication::translate("PrinterStatuses", "#outlet_full")); | |||
ADD_PRINTER_ERROR(NeedPaperTakeOut, QCoreApplication::translate("PrinterStatuses", "#need_paper_take_out")); | |||
ADD_PRINTER_ERROR(MemoryEnd, QCoreApplication::translate("PrinterStatuses", "#memory_end")); | |||
ADD_PRINTER_ERROR(OFDNotSent, QCoreApplication::translate("PrinterStatuses", "#ofd_not_sent")); | |||
} | |||
TStatusCodes getAvailableErrors() | |||
@@ -14,6 +14,19 @@ namespace SDK { | |||
namespace Driver { | |||
//-------------------------------------------------------------------------------- | |||
/// Состояние сессии. | |||
namespace ESessionState | |||
{ | |||
enum Enum | |||
{ | |||
Error, /// Ошибка определения. | |||
Opened, /// Открыта. | |||
Closed, /// Закрыта. | |||
Expired /// Истекла. | |||
}; | |||
} | |||
//-------------------------------------------------------------------------------- | |||
// Структура описателя фискальных тегов. | |||
struct SFiscalFieldData | |||
{ | |||
@@ -176,6 +189,9 @@ typedef int TVAT; | |||
typedef QSet<TVAT> TVATs; | |||
typedef double TSum; | |||
/* !!!!!!!!!!!!!! НЕ забываем обновить метод FiscalProtocol::createRequest и parseRequestJsonDoc */ | |||
struct SUnitData | |||
{ | |||
TSum sum; /// Сумма платежа. | |||
@@ -194,7 +210,8 @@ struct SUnitData | |||
typedef QList<SUnitData> TUnitDataList; | |||
/// Фискальные данные платежа | |||
/// Фискальные данные платежа !!!!!!!!!!!!!! НЕ забываем обновить метод FiscalProtocol::createRequest и parseRequestJsonDoc | |||
struct SPaymentData | |||
{ | |||
TUnitDataList unitDataList; /// Список данных товара | |||
@@ -101,6 +101,13 @@ namespace SDK { namespace Driver { namespace CAllHardware { namespace FiscalFiel | |||
const char TaxAmount06[] = "tax_amount_06"; // 1106 (Сумма НДС чека по расчетной ставке 18/118 (20/120)). | |||
const char TaxAmount07[] = "tax_amount_07"; // 1107 (Сумма НДС чека по расчетной ставке 10/110). | |||
// Значения. | |||
namespace Values | |||
{ | |||
/// ФФД маркер отсутствия данных. | |||
const char NoData[] = "none"; | |||
} | |||
}}}} | |||
namespace CFiscalSDK = SDK::Driver::CAllHardware::FiscalFields; | |||
@@ -68,6 +68,7 @@ namespace CAllHardware | |||
const char DealerTaxSystem[] = "dealer_tax_system"; | |||
const char DealerAgentFlag[] = "dealer_agent_flag"; | |||
const char DealerVAT[] = "dealer_vat"; | |||
const char DealerSupportPhone[] = "dealer_support_phone"; | |||
const char UserPhone[] = "user_phone"; | |||
const char UserMail[] = "user_mail"; | |||
const char ZReportTime[] = "z_report_time"; | |||
@@ -80,11 +81,12 @@ namespace CAllHardware | |||
/// Константы принтера. | |||
namespace Printer | |||
{ | |||
const char TemplateParameters[] = "template_parameters"; | |||
const char ReceiptParameters[] = "receipt_parameters"; | |||
const char LineSize[] = "line_size"; | |||
const char ReceiptTemplate[] = "receipt_template"; | |||
const char ContinuousMode[] = "continuous_mode"; | |||
const char ServiceOperation[] = "service_operation"; | |||
const char BlockTerminalOnError[] = "block_terminal_on_error"; | |||
const char OFDNotSentError[] = "ofd_not_sent_error"; | |||
} | |||
/// Константы HID-устройств. | |||
@@ -15,18 +15,6 @@ | |||
namespace SDK { | |||
namespace Driver { | |||
/// Состояние сессии. | |||
namespace ESessionState | |||
{ | |||
enum Enum | |||
{ | |||
Error, /// Ошибка определения. | |||
Opened, /// Открыта. | |||
Closed, /// Закрыта. | |||
Expired /// Истекла. | |||
}; | |||
} | |||
/// Состояние документа. | |||
namespace EDocumentState | |||
{ | |||
@@ -82,6 +82,9 @@ public: // методы | |||
/// Подключено новое устройство? | |||
virtual bool deviceConnected() = 0; | |||
/// Открыт? | |||
virtual bool opened() = 0; | |||
protected: | |||
virtual ~IIOPort() {} | |||
}; | |||
@@ -46,6 +46,9 @@ public: | |||
/// Печать отчета. | |||
virtual int printReport(const QString & aReceiptType, const QVariantMap & aParameters) = 0; | |||
/// Может ли работать с фискальным сервером? | |||
virtual bool hasFiscalRegister() = 0; | |||
signals: | |||
/// Срабатывает после печати произвольного чека. Успешность операции передаётся в поле aError. | |||
void receiptPrinted(int aJobIndex, bool aErrorHappened); | |||
@@ -5,7 +5,7 @@ | |||
// Qt | |||
#include <Common/QtHeadersBegin.h> | |||
#include <QtCore/QMap> | |||
#include <QtCore/QString> | |||
#include <QtCore/QStringList> | |||
#include <Common/QtHeadersEnd.h> | |||
// SDK | |||
@@ -15,21 +15,34 @@ | |||
namespace SDK { | |||
namespace PaymentProcessor { | |||
//------------------------------------------------------------------------------ | |||
class IFiscalRegister | |||
namespace ERequestType | |||
{ | |||
public: | |||
enum | |||
enum Enum | |||
{ | |||
Receipt = 1, | |||
Balance = 2, | |||
XReport = 2, | |||
Encashment = 4, | |||
ZReport = 8 | |||
ZReport = 8, | |||
SessionData | |||
}; | |||
} | |||
//------------------------------------------------------------------------------ | |||
class IFiscalRegister | |||
{ | |||
public: | |||
/// Соединение открыто. | |||
static const char * OFDNotSentSignal; // SIGNAL(OFDNotSent(bool)); | |||
public: | |||
virtual ~IFiscalRegister() {} | |||
/// Соединяет сигнал данного класса со слотом приёмника. | |||
virtual bool subscribe(const char * aSignal, QObject * aReceiver, const char * aSlot) = 0; | |||
/// Отсоединяет сигнал данного класса от слота приёмника. | |||
virtual bool unsubscribe(const char * aSignal, QObject * aReceiver) = 0; | |||
/// Инициализация регистратора | |||
virtual bool initialize(const QMap<QString, QString> & aParameters) = 0; | |||
@@ -37,24 +50,24 @@ public: | |||
virtual QStringList getParameterNames() = 0; | |||
/// Получить список возможностей ФР | |||
virtual bool haveCapability(quint32 aCapabilityFlags) = 0; | |||
virtual bool hasCapability(quint32 aCapabilityFlags) = 0; | |||
/// Проверить готовность к выполнению операции. | |||
virtual bool isReady(ERequestType::Enum aType) = 0; | |||
/// Зарегистрировать платёж и вернуть набор параметров | |||
virtual QVariantMap createFiscalTicket(qint64 aPaymentId, const QVariantMap & aPaymentParameters, const SDK::Driver::SPaymentData & aPaymentData) = 0; | |||
virtual QVariantMap createFiscalTicket(qint64 aPaymentId, const QVariantMap & aPaymentParameters, const SDK::Driver::SPaymentData & aPaymentData, bool aWaitResult) = 0; | |||
/// Получить строки для чека с фискальной информацией, список параметров может модифицироваться! | |||
virtual QStringList getReceipt(qint64 paymentId, const QVariantMap & aPaymentParameters) = 0; | |||
/// Получить отчёт по балансу ФР | |||
virtual QStringList balance() = 0; | |||
virtual QStringList getReceipt(qint64 paymentId, const QVariantMap & aPaymentParameters, const SDK::Driver::SPaymentData & aPaymentData) = 0; | |||
/// Инкассировать и получить текст для печати на чеке | |||
virtual QStringList encashment() = 0; | |||
/// Получить Z отчёт | |||
virtual QStringList getZreport() = 0; | |||
/// Выполнить сервисную операцию (инкассация, Z-отчет, X-отчет). | |||
virtual bool serviceRequest(ERequestType::Enum aType, QStringList & aReceipt, const QVariantMap & aParameters) = 0; | |||
}; | |||
//------------------------------------------------------------------------------ | |||
}} // SDK::PaymentProcessor | |||
namespace PPSDK = SDK::PaymentProcessor; | |||
//------------------------------------------------------------------------------ |
@@ -368,6 +368,12 @@ public slots: | |||
/// Признак, что текущий шаг - последний | |||
bool isFinalStep(); | |||
/// | |||
void setExternalCommissions(const QVariantList & aCommissions); | |||
/// | |||
void resetExternalCommissions(); | |||
public: | |||
/// Получение списка полей для интерфейса в многошаговом шлюзе | |||
bool loadFieldsForStep(TProviderFields & aFields); | |||
@@ -30,6 +30,9 @@ public slots: | |||
/// Проверка принтера на возможность печати чека. Если aRealCheck - true, то результат придёт в сигнале printerChecked. | |||
bool checkPrinter(bool aRealCheck); | |||
/// Проверка возможности использования фискального регистратора | |||
bool checkFiscalRegister(); | |||
/// Печать типизированного чека с параметрами aParameters. | |||
void printReceipt(const QString & aReceiptType, const QVariantMap & aParameters, const QString & aTemplate, bool aContinuousMode = false); | |||
@@ -0,0 +1,69 @@ | |||
/* @file Базовый плагин. */ | |||
// Qt | |||
#include <Common/QtHeadersBegin.h> | |||
#include <QtCore/QString> | |||
#include <QtCore/QVariantMap> | |||
#include <Common/QtHeadersEnd.h> | |||
// SDK | |||
#include <SDK/Plugins/IPlugin.h> | |||
#include <SDK/Plugins/IPluginEnvironment.h> | |||
#include <SDK/Plugins/IExternalInterface.h> | |||
#include <SDK/Plugins/PluginInitializer.h> | |||
//------------------------------------------------------------------------------ | |||
template <class T> | |||
class PluginBase : public SDK::Plugin::IPlugin, public T | |||
{ | |||
public: | |||
PluginBase(const QString & aName, SDK::Plugin::IEnvironment * aEnvironment, const QString & aInstancePath) : | |||
mInstanceName(aInstancePath), | |||
mEnvironment(aEnvironment), | |||
mPluginName(aName) | |||
{ | |||
setLog(aEnvironment->getLog(aName)); | |||
} | |||
virtual ~PluginBase() {} | |||
/// Возвращает название плагина. | |||
virtual QString getPluginName() const | |||
{ | |||
return mPluginName; | |||
} | |||
/// Возвращает параметры плагина. | |||
virtual QVariantMap getConfiguration() const | |||
{ | |||
return QVariantMap(); | |||
} | |||
/// Настраивает плагин. | |||
virtual void setConfiguration(const QVariantMap & /*aParameters*/) {} | |||
/// Сохраняет конфигурацию плагина в постоянное хранилище (.ini файл или хранилище прикладной программы). | |||
virtual bool saveConfiguration() | |||
{ | |||
return true; | |||
} | |||
/// Возвращает имя файла конфигурации без расширения (ключ + идентификатор). | |||
virtual QString getConfigurationName() const | |||
{ | |||
return mInstanceName; | |||
} | |||
/// Проверяет успешно ли инициализировался плагин при создании. | |||
virtual bool isReady() const | |||
{ | |||
return true; | |||
} | |||
private: | |||
QString mPluginName; | |||
QString mInstanceName; | |||
SDK::Plugin::IEnvironment * mEnvironment; | |||
}; | |||
//-------------------------------------------------------------------------------- |
@@ -55,6 +55,7 @@ Widgets.SceneBase2 { | |||
for (var i in global.provider.fields) { | |||
if (!global.handlerParameters.fields.hasOwnProperty(global.provider.fields[i].id)) continue; | |||
if (!global.provider.fields[i].title || !global.handlerParameters.fields[global.provider.fields[i].id].value) continue; | |||
if (global.provider.fields[i]["behavior"] === "hidden") continue; | |||
table += "<tr><td width='40%'>"; | |||
table += String(global.provider.fields[i].title).toUpperCase(); | |||
@@ -338,19 +338,20 @@ function checkEnterHandler(aParameters) { | |||
for (var i in aProvider.fields) { | |||
if (aProvider.fields[i].isRequired) { | |||
if (aProvider.fields[i].dependency && Core.userProperties.get("operator_dependency_fields").hasOwnProperty(aProvider.fields[i].id) | |||
&& !Core.userProperties.get("operator_dependency_fields")[aProvider.fields[i].id]) { continue; } | |||
&& !Core.userProperties.get("operator_dependency_fields")[aProvider.fields[i].id]) { GUI.log("SKIP field %1 by dependency".arg(aProvider.fields[i].id)); continue; } | |||
if(aProvider.fields[i].type == "html") { continue; } | |||
if(aProvider.fields[i].type == "html") { GUI.log("SKIP field %1 by HTML type".arg(aProvider.fields[i].id)); continue; } | |||
if (!aFields.hasOwnProperty(aProvider.fields[i].id)) { return false; } | |||
if (!aFields.hasOwnProperty(aProvider.fields[i].id)) { GUI.log("SKIP field %1 by field not found".arg(aProvider.fields[i].id)); return false; } | |||
if(!aFields[aProvider.fields[i].id].rawValue) { return false; } | |||
if(!aFields[aProvider.fields[i].id].rawValue) { GUI.log("SKIP field %1 by empty value".arg(aProvider.fields[i].id)); return false; } | |||
if (aProvider.fields[i].type == "number" || aProvider.fields[i].type == "number:float") { | |||
if (Number(aFields[aProvider.fields[i].id].rawValue).toString() == "NaN") { return false; } | |||
if (Number(aFields[aProvider.fields[i].id].rawValue).toString() == "NaN") { GUI.log("SKIP field %1 by NaN number to string".arg(aProvider.fields[i].id)); return false; } | |||
} | |||
} | |||
} | |||
return true; | |||
} | |||
@@ -701,7 +702,8 @@ function finishEnterHandler(aParameters) { | |||
} | |||
} | |||
else { | |||
printReceipt(Scenario.Payment.ReceiptType.Payment, RECEIPT_STATE, false, true); | |||
// Если есть фисальный регистратор, то печатаем, иначе - только сохраняем копию чека | |||
printReceipt(Scenario.Payment.ReceiptType.Payment, RECEIPT_STATE, false, !Core.printer.checkFiscalRegister()); | |||
GUI.notify(Scenario.Payment.Event.ReceiptPrinted, {receipt_printed: false}); | |||
} | |||
} | |||
@@ -24,7 +24,7 @@ namespace CCCNet | |||
{ | |||
data()[Models::CashcodeGX ][Currency::RUB][true] = TFimwareVersionSet() << 1208; | |||
data()[Models::CashcodeSM ][Currency::RUB][true] = TFimwareVersionSet() << 1359; | |||
data()[Models::CashcodeSM ][Currency::RUB][true] = TFimwareVersionSet() << 1361; | |||
data()[Models::CashcodeSM ][Currency::RUB][false] = TFimwareVersionSet() << 1387 << 1434; | |||
data()[Models::CashcodeMSM][Currency::RUB][false] = TFimwareVersionSet() << 1115; | |||
@@ -291,6 +291,7 @@ | |||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\GeneratedFiles\$(ProjectName)\$(ConfigurationName)\moc_%(Filename).cpp</Outputs> | |||
<Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ProjectName)\$(ConfigurationName)\moc_%(Filename).cpp" -DWINVER=0x0502 -D_WIN32_WINNT=0x0502 -DUNICODE -DWIN32 -DQT_THREAD_SUPPORT -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQCOMPORTWIN_LIB -DCYBERPLAT_DRIVER "-I$(TC_DIR)\." "-I$(TC_INCLUDE_DIR)\." "-I$(THIRDPARTY_DIR)\." "-I.\src" "-I$(QTDIR)\include"</Command> | |||
</CustomBuild> | |||
<ClInclude Include="..\..\..\..\includes\Common\ExitAction.h" /> | |||
<ClInclude Include="..\..\..\..\includes\Hardware\Common\BaseStatusTypes.h" /> | |||
<ClInclude Include="..\..\..\..\includes\Hardware\Common\ConfigCleaner.h" /> | |||
<ClInclude Include="..\..\..\..\includes\Hardware\Common\ContainerProxy.h" /> | |||
@@ -378,6 +378,9 @@ | |||
<ClInclude Include="..\..\..\..\includes\Hardware\Common\ContainerProxy.h"> | |||
<Filter>Header Files</Filter> | |||
</ClInclude> | |||
<ClInclude Include="..\..\..\..\includes\Common\ExitAction.h"> | |||
<Filter>Utils</Filter> | |||
</ClInclude> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<None Include="..\src\locale\common_ru.ts"> | |||
@@ -7,6 +7,9 @@ | |||
#include <QtCore/QSet> | |||
#include <Common/QtHeadersEnd.h> | |||
// Common | |||
#include "Common/ExitAction.h" | |||
// Project | |||
#include "Hardware/Common/PollingExpector.h" | |||
#include "Hardware/Common/BaseStatusDescriptions.h" | |||
@@ -139,9 +139,9 @@ void PollingDeviceBase<T>::postPollingAction(const TStatusCollection & aNewStatu | |||
{ | |||
for (int i = 0; i < mPPTaskList.size(); ++i) | |||
{ | |||
mPPTaskList[i](); | |||
mPPTaskList[i](); | |||
mPPTaskList.removeAt(i--); | |||
mPPTaskList.removeAt(i--); | |||
} | |||
} | |||
@@ -29,7 +29,7 @@ public: | |||
/// Освобождает ресурсы, связанные с устройством, возвращается в состояние до вызова initialize(). | |||
virtual bool release(); | |||
protected: | |||
/// Завершение инициализации. | |||
virtual void finaliseInitialization(); | |||
@@ -18,9 +18,8 @@ | |||
using namespace SDK::Driver; | |||
//------------------------------------------------------------------------------- | |||
#define INSTANCE_LIB_USB_DEVICE(aClass) template class aClass; aClass::TUsageData aClass::mUsageData; QMutex aClass::mUsageDataGuard(QMutex::Recursive); | |||
INSTANCE_LIB_USB_DEVICE(LibUSBDeviceBase<PortPollingDeviceBase<ProtoPrinter>>) | |||
LibUSBDeviceBase<PortPollingDeviceBase<ProtoPrinter>>::TUsageData LibUSBDeviceBase<PortPollingDeviceBase<ProtoPrinter>>::mUsageData; | |||
QMutex LibUSBDeviceBase<PortPollingDeviceBase<ProtoPrinter>>::mUsageDataGuard(QMutex::Recursive); | |||
//-------------------------------------------------------------------------------- | |||
template <class T> | |||
@@ -12,20 +12,6 @@ | |||
#include "Hardware/Common/FunctionTypes.h" | |||
//--------------------------------------------------------------------------- | |||
// Класс для выполнения функционала при выходе из области видимости. | |||
// Если отпадает необъходимость в его вызове - вызвать reset(). | |||
class ExitAction | |||
{ | |||
public: | |||
ExitAction(const TVoidMethod & aAction) : mAction(aAction) {} | |||
~ExitAction() { if (mAction) mAction(); } | |||
bool reset() { mAction = TVoidMethod(); return true; } | |||
private: | |||
TVoidMethod mAction; | |||
}; | |||
/// Данные устройств. | |||
typedef QMap<QString, QString> TDeviceData; | |||
@@ -96,6 +96,34 @@ bool ProtocolUtils::getBit(const QByteArray & aBuffer, int aShift, bool invert) | |||
} | |||
//-------------------------------------------------------------------------------- | |||
bool ProtocolUtils::checkBufferString(QString aData, QString * aLog) | |||
{ | |||
aData = aData.replace("0x", "").replace(" ", ""); | |||
auto makeResult = [&aLog] (const QString & aLogData) -> bool { if (aLog) *aLog = "Failed to check buffer string due to " + aLogData; return false; }; | |||
int size = aData.size(); | |||
if (size % 2) | |||
{ | |||
return makeResult("size = " + QString::number(size)); | |||
} | |||
for (int i = 0; i < size / 2; ++i) | |||
{ | |||
bool OK; | |||
QString data = aData.mid(i * 2, 2); | |||
data.toUShort(&OK, 16); | |||
if (!OK) | |||
{ | |||
return makeResult(QString("data #%1 = %2").arg(i).arg(data)); | |||
} | |||
} | |||
return true; | |||
} | |||
//-------------------------------------------------------------------------------- | |||
QByteArray ProtocolUtils::getBufferFromString(QString aData) | |||
{ | |||
aData = aData.replace("0x", "").replace(" ", ""); | |||
@@ -8,6 +8,9 @@ | |||
#include <QtCore/QByteArray> | |||
#include <Common/QtHeadersEnd.h> | |||
// Common | |||
#include <Common/ILog.h> | |||
// Modules | |||
#include "Hardware/Common/ASCII.h" | |||
@@ -34,6 +37,9 @@ namespace ProtocolUtils | |||
/// Получить массив байтов по строке лога. | |||
QByteArray getBufferFromString(QString aData); | |||
/// Проверить массив байтов в текстовом представлении. | |||
bool checkBufferString(QString aData, QString * aLog = nullptr); | |||
/// Получить список из массивов байтов по массиву строк. | |||
typedef QList<QByteArray> TBufferList; | |||
TBufferList getBufferListFromStrings(QStringList aDataList); | |||
@@ -49,6 +49,7 @@ CModelData::CModelData() | |||
addOnlineTrade(64, 36, Models::Atol52F, true, 6, CAtolFR::OnlineTradeBuild, false); | |||
addOnlineTrade(62, 36, Models::Atol55F, true, 6); | |||
addOnlineTrade(69, 48, Models::Atol77F, true, 6, CAtolFR::OnlineTradeBuild, false); | |||
addOnlineTrade(82, 32, Models::Atol91F, true, 3, CAtolFR::OnlineTradeBuild, false); | |||
addOnlineTrade(63, 48, Models::FPrint22PTK, true, 6, CAtolFR::OnlineTradeBuild, false); | |||
@@ -68,6 +68,7 @@ namespace CAtolFR | |||
const char Atol52F[] = "ATOL-52F"; | |||
const char Atol55F[] = "ATOL-55F"; | |||
const char Atol77F[] = "ATOL-77F"; | |||
const char Atol91F[] = "ATOL-91F"; | |||
const char Paymaster[] = "Sensis Kaznachej"; | |||
const char FPrint22PTK[] = "ATOL FPrint-22PTK"; | |||
@@ -35,6 +35,7 @@ AtolFRBase::AtolFRBase() | |||
mSubmode = CAtolFR::InnerSubmodes::NoSubmode; | |||
mLocked = false; | |||
mNonNullableAmount = 0; | |||
mFDExecutionMode = CAtolFR::FiscalFlags::ExecutionMode; | |||
// количество строк шапки (по умолчанию 4) - изменено для корректной печати на фискализированных аппаратах (как и было раньше) | |||
mDocumentCapStrings = 6; | |||
@@ -802,7 +803,7 @@ bool AtolFRBase::performZReport(bool aPrintDeferredReports) | |||
bool AtolFRBase::openDocument(EPayOffTypes::Enum aPayOffType) | |||
{ | |||
QByteArray commandData; | |||
commandData.append(CAtolFR::FiscalFlags::ExecutionMode); | |||
commandData.append(mFDExecutionMode); | |||
commandData.append(CAtolFR::PayOffTypeData[aPayOffType]); | |||
if (processCommand(CAtolFR::Commands::OpenDocument, commandData)) | |||
@@ -212,6 +212,9 @@ protected: | |||
/// Данные регистров. | |||
CAtolFR::Registers::CData mRegisterData; | |||
/// Режим выполнения фискального документа. | |||
char mFDExecutionMode; | |||
}; | |||
//-------------------------------------------------------------------------------- |
@@ -327,8 +327,38 @@ bool AtolOnlineFRBase<T>::getStatus(TStatusCodes & aStatusCodes) | |||
//-------------------------------------------------------------------------------- | |||
template<class T> | |||
bool AtolOnlineFRBase<T>::isTradeWithoutPrinting() | |||
{ | |||
return getModelList().contains(mDeviceName) && !canNotPrinting() && isNotPrinting(); | |||
} | |||
//-------------------------------------------------------------------------------- | |||
template <class T> | |||
bool AtolOnlineFRBase<T>::isPrintingNeed(const QStringList & aReceipt) | |||
{ | |||
if (!T::isPrintingNeed(aReceipt)) | |||
{ | |||
return false; | |||
} | |||
bool OK = false; | |||
mFFEngine.checkFiscalField(CFR::FiscalFields::UserContact, OK); | |||
if (isTradeWithoutPrinting() && OK) | |||
{ | |||
toLog(LogLevel::Normal, mDeviceName + ": Receipt has not been printed by trade ATOL:\n" + aReceipt.join("\n")); | |||
return false; | |||
} | |||
return true; | |||
} | |||
//-------------------------------------------------------------------------------- | |||
template<class T> | |||
bool AtolOnlineFRBase<T>::performFiscal(const QStringList & aReceipt, const SPaymentData & aPaymentData, quint32 * aFDNumber) | |||
{ | |||
mFDExecutionMode = isTradeWithoutPrinting() ? CAtolOnlineFR::FiscalFlags::NotPrinting : CAtolFR::FiscalFlags::ExecutionMode; | |||
if (!T::performFiscal(aReceipt, aPaymentData)) | |||
{ | |||
return false; | |||
@@ -494,7 +524,12 @@ bool AtolOnlineFRBase<T>::checkTax(TVAT aVAT, CFR::Taxes::SData & aData) | |||
template<class T> | |||
bool AtolOnlineFRBase<T>::sale(const SUnitData & aUnitData) | |||
{ | |||
if (!processCommand(CAtolOnlineFR::Commands::StartSale, CAtolOnlineFR::StartSailingData)) | |||
if (isTradeWithoutPrinting() && !setTLV(CFR::FiscalFields::UserContact)) | |||
{ | |||
toLog(LogLevel::Error, mDeviceName + ": Failed to make fiscal document without printing"); | |||
} | |||
if (!processCommand(CAtolOnlineFR::Commands::StartSale, CAtolOnlineFR::FiscalFlags::StartSailingData)) | |||
{ | |||
return false; | |||
} | |||
@@ -511,7 +546,7 @@ bool AtolOnlineFRBase<T>::sale(const SUnitData & aUnitData) | |||
QByteArray payOffSubjectMethodType = getBCD(aUnitData.payOffSubjectMethodType, 1); | |||
QByteArray commandData; | |||
commandData.append(CAtolOnlineFR::SaleFlags); // флаги | |||
commandData.append(CAtolOnlineFR::FiscalFlags::Saling); // флаги | |||
commandData.append(sum); // сумма | |||
commandData.append(getBCD(10, 5, 2)); // количество = 1 штука | |||
commandData.append(sum); // стоимость | |||
@@ -65,6 +65,12 @@ protected: | |||
/// Проверить параметры налога. | |||
virtual bool checkTax(SDK::Driver::TVAT aVAT, CFR::Taxes::SData & aData); | |||
/// Торговый фискальник без печати чека? | |||
bool isTradeWithoutPrinting(); | |||
/// Проверить необходимость печати. | |||
virtual bool isPrintingNeed(const QStringList & aReceipt); | |||
/// Продажа. | |||
virtual bool sale(const SDK::Driver::SUnitData & aUnitData); | |||
@@ -79,11 +79,18 @@ namespace CAtolOnlineFR | |||
const SData SetAutoZReportTiming = SData(22, 1, 2); | |||
} | |||
/// Данные команды начала формирования позиции продажи - выполнить операцию + 2 магических числа | |||
const QByteArray StartSailingData = QByteArray::fromRawData("\x00\x01\x00", 3); | |||
/// Флаги выполнения фискальных операций. | |||
namespace FiscalFlags | |||
{ | |||
/// Не печатать чек - на открытии чека. | |||
const char NotPrinting = '\x04'; | |||
/// Данные команды начала формирования позиции продажи - выполнить операцию + 2 магических числа | |||
const QByteArray StartSailingData = QByteArray::fromRawData("\x00\x01\x00", 3); | |||
/// Флаги выполнения продажи - Выполнить операцию + проверить денежную наличность + налог на конкретную позицию. | |||
const char SaleFlags = CAtolFR::FiscalFlags::ExecutionMode | CAtolFR::FiscalFlags::CashChecking | CAtolFR::FiscalFlags::TaxForPosition; | |||
/// Флаги выполнения продажи - Выполнить операцию + проверить денежную наличность + налог на конкретную позицию. | |||
const char Saling = CAtolFR::FiscalFlags::ExecutionMode | CAtolFR::FiscalFlags::CashChecking | CAtolFR::FiscalFlags::TaxForPosition; | |||
} | |||
/// Регистры | |||
namespace Registers | |||
@@ -32,10 +32,9 @@ void Paymaster<T>::setDeviceConfiguration(const QVariantMap & aConfiguration) | |||
{ | |||
AtolVKP80BasedFR<T>::setDeviceConfiguration(aConfiguration); | |||
bool notPrinting = getConfigParameter(CHardwareSDK::FR::WithoutPrinting) == CHardwareSDK::Values::Use; | |||
QString printerModel = getConfigParameter(CHardware::FR::PrinterModel, CAtolOnlinePrinters::Default).toString(); | |||
if (aConfiguration.contains(CHardware::FR::PrinterModel) && (printerModel != CAtolOnlinePrinters::Default) && !notPrinting) | |||
if (aConfiguration.contains(CHardware::FR::PrinterModel) && (printerModel != CAtolOnlinePrinters::Default) && !isNotPrinting()) | |||
{ | |||
mPPTaskList.append([&] () { mNotPrintingError = !setNotPrintDocument(false); }); | |||
} | |||
@@ -26,7 +26,25 @@ FFEngine::FFEngine(ILog * aLog): DeviceLogManager(aLog), mOperatorPresence(false | |||
//-------------------------------------------------------------------------------- | |||
void FFEngine::setConfigParameter(const QString & aName, const QVariant & aValue) | |||
{ | |||
DeviceConfigManager::setConfigParameter(aName, aValue); | |||
int field = mFFData.getKey(aName); | |||
QVariant value(aValue); | |||
if (value.isValid() && field) | |||
{ | |||
CFR::FiscalFields::SData & data = mFFData.data()[field]; | |||
if (data.isString()) | |||
{ | |||
value = clean(value.toString()); | |||
} | |||
if (data.isINN()) | |||
{ | |||
value = value.toString().simplified().leftJustified(CFR::INN::Person::Natural, QChar(ASCII::Space)); | |||
} | |||
} | |||
DeviceConfigManager::setConfigParameter(aName, value); | |||
mOperatorPresence = getConfigParameter(CHardwareSDK::OperatorPresence, mOperatorPresence).toBool(); | |||
} | |||
@@ -107,6 +125,15 @@ CFR::TTLVList FFEngine::parseSTLV(const QByteArray & aData) | |||
} | |||
//-------------------------------------------------------------------------------- | |||