Compare commits
5
Commits
e3a5e44c33
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a5612b2eac | ||
|
|
99f98b0451 | ||
|
|
464216d6c3 | ||
|
|
3a4c517a75 | ||
|
|
a0a745df0c |
@@ -1,2 +1,3 @@
|
|||||||
cmake*
|
cmake*
|
||||||
|
!CMakePresets.json
|
||||||
build
|
build
|
||||||
+5
-2
@@ -3,7 +3,10 @@
|
|||||||
url = https://github.com/semihc/qdecimal.git
|
url = https://github.com/semihc/qdecimal.git
|
||||||
[submodule "3rdparty/QxOrm"]
|
[submodule "3rdparty/QxOrm"]
|
||||||
path = 3rdparty/QxOrm
|
path = 3rdparty/QxOrm
|
||||||
url = https://github.com/PepaRokos/QxOrm.git
|
url = https://github.com/QxOrm/QxOrm.git
|
||||||
[submodule "3rdparty/LimeReport"]
|
[submodule "3rdparty/LimeReport"]
|
||||||
path = 3rdparty/LimeReport
|
path = 3rdparty/LimeReport
|
||||||
url = https://github.com/PepaRokos/LimeReport.git
|
url = https://github.com/fralx/LimeReport.git
|
||||||
|
[submodule "3rdparty/EetCpp"]
|
||||||
|
path = 3rdparty/EetCpp
|
||||||
|
url = https://gitea.itsolved.cz/ITsolved/EetCpp.git
|
||||||
|
|||||||
+1
Submodule 3rdparty/EetCpp added at 3f8e4796b3
Vendored
+1
-1
Submodule 3rdparty/LimeReport updated: b1bfd131ad...e6210b58a4
Vendored
+1
-1
Submodule 3rdparty/QxOrm updated: 975c3b5ad3...44fd7f1617
@@ -1,8 +1,40 @@
|
|||||||
cmake_minimum_required(VERSION 3.24)
|
cmake_minimum_required(VERSION 3.24)
|
||||||
project(prodejna-root)
|
project(prodejna-root)
|
||||||
|
|
||||||
|
include(CTest)
|
||||||
|
option(ENABLE_ZINT "Enable LimeReport barcode support" ON)
|
||||||
|
option(ENABLE_EET_V4 "Enable EET v4 integration" OFF)
|
||||||
|
|
||||||
|
set(EETCPP_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/EetCpp" CACHE PATH
|
||||||
|
"Path to the EetCpp source tree when EET v4 integration is enabled")
|
||||||
|
|
||||||
|
find_package(Qt6 6.12 REQUIRED COMPONENTS Core Core5Compat Test Gui Widgets PrintSupport Sql Qml Network Xml Svg)
|
||||||
|
|
||||||
|
set(CMAKE_POLICY_VERSION_MINIMUM 3.10)
|
||||||
|
|
||||||
|
if(ENABLE_EET_V4)
|
||||||
|
if(NOT EETCPP_SOURCE_DIR)
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"ENABLE_EET_V4 requires EETCPP_SOURCE_DIR pointing to the EetCpp source tree")
|
||||||
|
endif()
|
||||||
|
if(NOT EXISTS "${EETCPP_SOURCE_DIR}/CMakeLists.txt")
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"EETCPP_SOURCE_DIR does not contain a CMakeLists.txt: ${EETCPP_SOURCE_DIR}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(EETCPP_BUILD_PLAYGROUND OFF CACHE BOOL
|
||||||
|
"Build the EetCpp Playground CLI" FORCE)
|
||||||
|
set(_prodejna_build_testing "${BUILD_TESTING}")
|
||||||
|
set(BUILD_TESTING OFF)
|
||||||
|
add_subdirectory("${EETCPP_SOURCE_DIR}"
|
||||||
|
"${CMAKE_BINARY_DIR}/_deps/eetcpp-build"
|
||||||
|
EXCLUDE_FROM_ALL)
|
||||||
|
set(BUILD_TESTING "${_prodejna_build_testing}")
|
||||||
|
endif()
|
||||||
|
|
||||||
add_subdirectory(3rdparty/qdecimal)
|
add_subdirectory(3rdparty/qdecimal)
|
||||||
add_subdirectory(3rdparty/QxOrm)
|
add_subdirectory(3rdparty/QxOrm)
|
||||||
|
set(USE_QT6 ON CACHE BOOL "Build LimeReport with Qt6" FORCE)
|
||||||
add_subdirectory(3rdparty/LimeReport)
|
add_subdirectory(3rdparty/LimeReport)
|
||||||
|
|
||||||
add_subdirectory(core)
|
add_subdirectory(core)
|
||||||
@@ -15,3 +47,6 @@ add_subdirectory(shop)
|
|||||||
add_subdirectory(commodity)
|
add_subdirectory(commodity)
|
||||||
add_subdirectory(camp)
|
add_subdirectory(camp)
|
||||||
|
|
||||||
|
if(BUILD_TESTING)
|
||||||
|
add_subdirectory(tests)
|
||||||
|
endif()
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"version": 5,
|
||||||
|
"cmakeMinimumRequired": {
|
||||||
|
"major": 3,
|
||||||
|
"minor": 24,
|
||||||
|
"patch": 0
|
||||||
|
},
|
||||||
|
"configurePresets": [
|
||||||
|
{
|
||||||
|
"name": "qt6-debug",
|
||||||
|
"displayName": "Qt 6.12+ Debug",
|
||||||
|
"generator": "Ninja",
|
||||||
|
"binaryDir": "${sourceDir}/build/presets/${presetName}",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_BUILD_TYPE": "Debug"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"buildPresets": [
|
||||||
|
{
|
||||||
|
"name": "qt6-debug",
|
||||||
|
"configurePreset": "qt6-debug"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"testPresets": [
|
||||||
|
{
|
||||||
|
"name": "qt6-debug",
|
||||||
|
"configurePreset": "qt6-debug",
|
||||||
|
"output": {
|
||||||
|
"outputOnFailure": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -84,7 +84,7 @@ QDecDouble CommodityData::price() const
|
|||||||
|
|
||||||
void CommodityData::setPrice(const QDecDouble &price)
|
void CommodityData::setPrice(const QDecDouble &price)
|
||||||
{
|
{
|
||||||
m_price = price.toDouble() * DEC_MULTIPLE;
|
m_price = qRound(price.toDouble() * DEC_MULTIPLE);
|
||||||
}
|
}
|
||||||
Enums::VatType CommodityData::vat() const
|
Enums::VatType CommodityData::vat() const
|
||||||
{
|
{
|
||||||
|
|||||||
+3
-1
@@ -1,6 +1,8 @@
|
|||||||
#ifndef DEFINE_H
|
#ifndef DEFINE_H
|
||||||
#define DEFINE_H
|
#define DEFINE_H
|
||||||
|
|
||||||
|
#include <QtGlobal>
|
||||||
|
|
||||||
#define ALL(arr) arr.begin(), arr.end()
|
#define ALL(arr) arr.begin(), arr.end()
|
||||||
|
|
||||||
// default rights
|
// default rights
|
||||||
@@ -11,7 +13,7 @@
|
|||||||
|
|
||||||
#define DEC_MULTIPLE 100
|
#define DEC_MULTIPLE 100
|
||||||
#define TO_DEC(num) (QDecDouble(num) / QDecDouble(DEC_MULTIPLE))
|
#define TO_DEC(num) (QDecDouble(num) / QDecDouble(DEC_MULTIPLE))
|
||||||
#define FROM_DEC(num) (num * QDecDouble(DEC_MULTIPLE)).toDouble()
|
#define FROM_DEC(num) qRound((num * QDecDouble(DEC_MULTIPLE)).toDouble())
|
||||||
|
|
||||||
#ifndef PLUGIN_ROOT
|
#ifndef PLUGIN_ROOT
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|||||||
@@ -237,7 +237,10 @@ protected:
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (!Context::instance().currentUser().isNull()) {
|
||||||
obj->setProperty("updatedBy", Context::instance().currentUser()->login());
|
obj->setProperty("updatedBy", Context::instance().currentUser()->login());
|
||||||
|
}
|
||||||
|
|
||||||
obj->setProperty("updated", QDateTime::currentDateTime());
|
obj->setProperty("updated", QDateTime::currentDateTime());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ QDecDouble AccService::price() const
|
|||||||
|
|
||||||
void AccService::setPrice(QDecDouble price)
|
void AccService::setPrice(QDecDouble price)
|
||||||
{
|
{
|
||||||
m_price = price.toDouble() * DEC_MULTIPLE;
|
m_price = qRound(price.toDouble() * DEC_MULTIPLE);
|
||||||
}
|
}
|
||||||
bool AccService::active() const
|
bool AccService::active() const
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -93,3 +93,12 @@ target_link_libraries(shop
|
|||||||
core
|
core
|
||||||
addressbook
|
addressbook
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(ENABLE_EET_V4)
|
||||||
|
target_sources(shop PRIVATE
|
||||||
|
eet/eetv4adapter.cpp
|
||||||
|
eet/eetv4adapter.h
|
||||||
|
)
|
||||||
|
target_link_libraries(shop EetCpp::EetCpp)
|
||||||
|
target_compile_definitions(shop PRIVATE PRODEJNA_EET_V4)
|
||||||
|
endif()
|
||||||
@@ -30,6 +30,7 @@ namespace qx {
|
|||||||
t.data(&Voucher::m_eetPkp, "eetPkp");
|
t.data(&Voucher::m_eetPkp, "eetPkp");
|
||||||
t.data(&Voucher::m_eetBkp, "eetBkp");
|
t.data(&Voucher::m_eetBkp, "eetBkp");
|
||||||
t.data(&Voucher::m_eetFik, "eetFik");
|
t.data(&Voucher::m_eetFik, "eetFik");
|
||||||
|
t.data(&Voucher::m_eetPok, "eetPok");
|
||||||
t.data(&Voucher::m_saveDateTime, "saveDateTime");
|
t.data(&Voucher::m_saveDateTime, "saveDateTime");
|
||||||
t.data(&Voucher::m_createdBy, "createdBy");
|
t.data(&Voucher::m_createdBy, "createdBy");
|
||||||
t.data(&Voucher::m_created, "created");
|
t.data(&Voucher::m_created, "created");
|
||||||
@@ -329,6 +330,16 @@ void Voucher::setEetFik(const QString &eetFik)
|
|||||||
m_eetFik = eetFik;
|
m_eetFik = eetFik;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Voucher::eetPok() const
|
||||||
|
{
|
||||||
|
return m_eetPok;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Voucher::setEetPok(const QString &eetPok)
|
||||||
|
{
|
||||||
|
m_eetPok = eetPok;
|
||||||
|
}
|
||||||
|
|
||||||
QDateTime Voucher::saveDateTime() const
|
QDateTime Voucher::saveDateTime() const
|
||||||
{
|
{
|
||||||
return m_saveDateTime;
|
return m_saveDateTime;
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ class SHOPSHARED_EXPORT Voucher : public QObject
|
|||||||
Q_PROPERTY(QString eetBkp READ eetBkp WRITE setEetBkp)
|
Q_PROPERTY(QString eetBkp READ eetBkp WRITE setEetBkp)
|
||||||
Q_PROPERTY(QString eetPkp READ eetPkp WRITE setEetPkp)
|
Q_PROPERTY(QString eetPkp READ eetPkp WRITE setEetPkp)
|
||||||
Q_PROPERTY(QString eetFik READ eetFik WRITE setEetFik)
|
Q_PROPERTY(QString eetFik READ eetFik WRITE setEetFik)
|
||||||
|
Q_PROPERTY(QString eetPok READ eetPok WRITE setEetPok)
|
||||||
Q_PROPERTY(VoucherStatus status READ status WRITE setStatus)
|
Q_PROPERTY(VoucherStatus status READ status WRITE setStatus)
|
||||||
Q_PROPERTY(QString createdBy READ createdBy WRITE setCreatedBy)
|
Q_PROPERTY(QString createdBy READ createdBy WRITE setCreatedBy)
|
||||||
Q_PROPERTY(QString updatedBy READ updatedBy WRITE setUpdatedBy)
|
Q_PROPERTY(QString updatedBy READ updatedBy WRITE setUpdatedBy)
|
||||||
@@ -147,6 +148,9 @@ public:
|
|||||||
QString eetFik() const;
|
QString eetFik() const;
|
||||||
void setEetFik(const QString &eetFik);
|
void setEetFik(const QString &eetFik);
|
||||||
|
|
||||||
|
QString eetPok() const;
|
||||||
|
void setEetPok(const QString &eetPok);
|
||||||
|
|
||||||
QDateTime saveDateTime() const;
|
QDateTime saveDateTime() const;
|
||||||
void setSaveDateTime(const QDateTime &saveDateTime);
|
void setSaveDateTime(const QDateTime &saveDateTime);
|
||||||
|
|
||||||
@@ -192,6 +196,7 @@ private:
|
|||||||
QString m_eetPkp;
|
QString m_eetPkp;
|
||||||
QString m_eetBkp;
|
QString m_eetBkp;
|
||||||
QString m_eetFik;
|
QString m_eetFik;
|
||||||
|
QString m_eetPok;
|
||||||
QList<QSharedPointer<VoucherItem>> m_items;
|
QList<QSharedPointer<VoucherItem>> m_items;
|
||||||
VoucherStatus m_status;
|
VoucherStatus m_status;
|
||||||
SeasonPtr m_season;
|
SeasonPtr m_season;
|
||||||
|
|||||||
@@ -0,0 +1,152 @@
|
|||||||
|
#include "eetv4adapter.h"
|
||||||
|
|
||||||
|
#include <QRegularExpression>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
void setError(QString *error, const QString &message)
|
||||||
|
{
|
||||||
|
if (error != nullptr)
|
||||||
|
{
|
||||||
|
*error = message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QDateTime withExplicitOffset(const QDateTime &dateTime)
|
||||||
|
{
|
||||||
|
if (!dateTime.isValid())
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return dateTime.timeSpec() == Qt::LocalTime
|
||||||
|
? dateTime.toOffsetFromUtc(dateTime.offsetFromUtc())
|
||||||
|
: dateTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EetV4ShopConfig::validate(QString *error) const
|
||||||
|
{
|
||||||
|
if (!endpoint.isValid() || endpoint.scheme().compare(QStringLiteral("https"),
|
||||||
|
Qt::CaseInsensitive) != 0)
|
||||||
|
{
|
||||||
|
setError(error, QStringLiteral("EET v4 endpoint must be a valid HTTPS URL."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (taxpayerEic.isEmpty())
|
||||||
|
{
|
||||||
|
setError(error, QStringLiteral("EET v4 taxpayer EIC must not be empty."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (unitId < 1)
|
||||||
|
{
|
||||||
|
setError(error, QStringLiteral("EET v4 unit ID must be positive."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (cashRegisterId.isEmpty())
|
||||||
|
{
|
||||||
|
setError(error, QStringLiteral("EET v4 cash-register ID must not be empty."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (timeoutMilliseconds < 1)
|
||||||
|
{
|
||||||
|
setError(error, QStringLiteral("EET v4 timeout must be positive."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EetV4ShopAdapter::amountText(const QDecDouble &amount,
|
||||||
|
QString *text,
|
||||||
|
QString *error)
|
||||||
|
{
|
||||||
|
QString value = QString::fromLatin1(amount.toString());
|
||||||
|
if (value.contains(QRegularExpression(QStringLiteral("[eE]"))))
|
||||||
|
{
|
||||||
|
setError(error, QStringLiteral("Voucher amount must not use exponential notation."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!value.contains(QLatin1Char('.')))
|
||||||
|
{
|
||||||
|
value += QStringLiteral(".00");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const int decimals = value.size() - value.indexOf(QLatin1Char('.')) - 1;
|
||||||
|
if (decimals > 2)
|
||||||
|
{
|
||||||
|
setError(error, QStringLiteral("Voucher amount has more than two decimal places."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
value = value.leftJustified(value.size() + (2 - decimals), QLatin1Char('0'));
|
||||||
|
}
|
||||||
|
|
||||||
|
EetV4Amount parsed;
|
||||||
|
if (!EetV4Amount::fromString(value, &parsed, error))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text != nullptr)
|
||||||
|
{
|
||||||
|
*text = parsed.toString();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EetV4ShopAdapter::createRequest(const Voucher &voucher,
|
||||||
|
const EetV4ShopConfig &config,
|
||||||
|
EetV4SaleRequest *request,
|
||||||
|
QString *error)
|
||||||
|
{
|
||||||
|
if (request == nullptr)
|
||||||
|
{
|
||||||
|
setError(error, QStringLiteral("EET v4 request output must not be null."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!config.validate(error))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QDateTime saleAt = withExplicitOffset(voucher.payDateTime());
|
||||||
|
if (!saleAt.isValid())
|
||||||
|
{
|
||||||
|
setError(error, QStringLiteral("Voucher payment date must be valid."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString totalText;
|
||||||
|
if (!amountText(voucher.totalPrice(), &totalText, error))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
EetV4Amount total;
|
||||||
|
if (!EetV4Amount::fromString(totalText, &total, error))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
EetV4SaleRequest result;
|
||||||
|
result.sentAt = withExplicitOffset(QDateTime::currentDateTime());
|
||||||
|
result.saleAt = saleAt;
|
||||||
|
result.firstSubmission = voucher.eetStatus() == Voucher::EET_FOR_SEND;
|
||||||
|
result.verificationMode = config.verificationMode;
|
||||||
|
result.taxpayerEic = config.taxpayerEic;
|
||||||
|
result.unitId = config.unitId;
|
||||||
|
result.cashRegisterId = config.cashRegisterId;
|
||||||
|
result.receiptNumber = voucher.numSer();
|
||||||
|
result.totalSale = total;
|
||||||
|
|
||||||
|
QStringList validationErrors;
|
||||||
|
if (!result.validate(&validationErrors))
|
||||||
|
{
|
||||||
|
setError(error, validationErrors.join(QStringLiteral("; ")));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*request = result;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
#ifndef SHOP_EETV4ADAPTER_H
|
||||||
|
#define SHOP_EETV4ADAPTER_H
|
||||||
|
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
#include <eetv4request.h>
|
||||||
|
|
||||||
|
#include "../data/voucher.h"
|
||||||
|
|
||||||
|
struct EetV4ShopConfig
|
||||||
|
{
|
||||||
|
QUrl endpoint;
|
||||||
|
QString taxpayerEic;
|
||||||
|
qint32 unitId{0};
|
||||||
|
QString cashRegisterId;
|
||||||
|
int timeoutMilliseconds{30000};
|
||||||
|
bool verificationMode{true};
|
||||||
|
|
||||||
|
bool validate(QString *error = nullptr) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class EetV4ShopAdapter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static bool createRequest(const Voucher &voucher,
|
||||||
|
const EetV4ShopConfig &config,
|
||||||
|
EetV4SaleRequest *request,
|
||||||
|
QString *error = nullptr);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static bool amountText(const QDecDouble &amount, QString *text, QString *error);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SHOP_EETV4ADAPTER_H
|
||||||
@@ -47,7 +47,7 @@ PaydVouchersDialog::PaydVouchersDialog(QWidget *parent) :
|
|||||||
ui->tableItems->setColumnHidden(0, true);
|
ui->tableItems->setColumnHidden(0, true);
|
||||||
ui->tableItems->verticalScrollBar()->setStyleSheet("QScrollBar:vertical { width: 30px; }");
|
ui->tableItems->verticalScrollBar()->setStyleSheet("QScrollBar:vertical { width: 30px; }");
|
||||||
|
|
||||||
#ifndef EET
|
#ifndef PRODEJNA_EET_V4
|
||||||
ui->btnEetNotSen->setVisible(false);
|
ui->btnEetNotSen->setVisible(false);
|
||||||
ui->btnSendEet->setVisible(false);
|
ui->btnSendEet->setVisible(false);
|
||||||
ui->label_2->setVisible(false);
|
ui->label_2->setVisible(false);
|
||||||
|
|||||||
@@ -305,7 +305,17 @@ QByteArray ReceiptGenerator::generate()
|
|||||||
out.append("Datum:");
|
out.append("Datum:");
|
||||||
out.append(prepareString(m_voucher->payDateTime().toString()));
|
out.append(prepareString(m_voucher->payDateTime().toString()));
|
||||||
out.append("\x0a");
|
out.append("\x0a");
|
||||||
if (!m_voucher->eetBkp().isEmpty())
|
if (!m_voucher->eetPok().isEmpty())
|
||||||
|
{
|
||||||
|
out.append("Rezim EET: v4");
|
||||||
|
out.append("\x0a");
|
||||||
|
out.append("POK:");
|
||||||
|
out.append("\x0a");
|
||||||
|
out.append(prepareString(m_voucher->eetPok()));
|
||||||
|
out.append("\x0a");
|
||||||
|
out.append("\x0a");
|
||||||
|
}
|
||||||
|
else if (!m_voucher->eetBkp().isEmpty())
|
||||||
{
|
{
|
||||||
out.append("Rezim EET: bezny");
|
out.append("Rezim EET: bezny");
|
||||||
out.append("\x0a");
|
out.append("\x0a");
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ ShopSettings::ShopSettings(QObject *parent) : QObject(parent)
|
|||||||
m_lettersPerLine = 48;
|
m_lettersPerLine = 48;
|
||||||
|
|
||||||
m_eetActive = false;
|
m_eetActive = false;
|
||||||
|
m_eetKeyPassword = QStringLiteral("EET_PKCS12_PASSWORD");
|
||||||
|
m_eetUnitId = 1;
|
||||||
|
m_eetTimeout = 30000;
|
||||||
m_eetMode = 1;
|
m_eetMode = 1;
|
||||||
m_eetTest = 0;
|
m_eetTest = 0;
|
||||||
m_eetPlayground = 0;
|
m_eetPlayground = 0;
|
||||||
@@ -94,6 +97,36 @@ void ShopSettings::setEetRegisterId(const QString &eetRegisterId)
|
|||||||
m_eetRegisterId = eetRegisterId;
|
m_eetRegisterId = eetRegisterId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString ShopSettings::eetEndpoint() const
|
||||||
|
{
|
||||||
|
return m_eetEndpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShopSettings::setEetEndpoint(const QString &eetEndpoint)
|
||||||
|
{
|
||||||
|
m_eetEndpoint = eetEndpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ShopSettings::eetUnitId() const
|
||||||
|
{
|
||||||
|
return m_eetUnitId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShopSettings::setEetUnitId(int eetUnitId)
|
||||||
|
{
|
||||||
|
m_eetUnitId = eetUnitId;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ShopSettings::eetTimeout() const
|
||||||
|
{
|
||||||
|
return m_eetTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShopSettings::setEetTimeout(int eetTimeout)
|
||||||
|
{
|
||||||
|
m_eetTimeout = eetTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
int ShopSettings::eetMode() const
|
int ShopSettings::eetMode() const
|
||||||
{
|
{
|
||||||
return m_eetMode;
|
return m_eetMode;
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ class ShopSettings : public QObject
|
|||||||
Q_PROPERTY(bool eetActive READ eetActive WRITE setEetActive)
|
Q_PROPERTY(bool eetActive READ eetActive WRITE setEetActive)
|
||||||
Q_PROPERTY(QString eetShopId READ eetShopId WRITE setEetShopId)
|
Q_PROPERTY(QString eetShopId READ eetShopId WRITE setEetShopId)
|
||||||
Q_PROPERTY(QString eetRegisterId READ eetRegisterId WRITE setEetRegisterId)
|
Q_PROPERTY(QString eetRegisterId READ eetRegisterId WRITE setEetRegisterId)
|
||||||
|
Q_PROPERTY(QString eetEndpoint READ eetEndpoint WRITE setEetEndpoint)
|
||||||
|
Q_PROPERTY(int eetUnitId READ eetUnitId WRITE setEetUnitId)
|
||||||
|
Q_PROPERTY(int eetTimeout READ eetTimeout WRITE setEetTimeout)
|
||||||
Q_PROPERTY(int eetMode READ eetMode WRITE setEetMode)
|
Q_PROPERTY(int eetMode READ eetMode WRITE setEetMode)
|
||||||
Q_PROPERTY(QString eetCertificate READ eetCertificate WRITE setEetCertificate)
|
Q_PROPERTY(QString eetCertificate READ eetCertificate WRITE setEetCertificate)
|
||||||
Q_PROPERTY(QString eetKeyPassword READ eetKeyPassword WRITE setEetKeyPassword)
|
Q_PROPERTY(QString eetKeyPassword READ eetKeyPassword WRITE setEetKeyPassword)
|
||||||
@@ -65,6 +68,15 @@ public:
|
|||||||
QString eetRegisterId() const;
|
QString eetRegisterId() const;
|
||||||
void setEetRegisterId(const QString &eetRegisterId);
|
void setEetRegisterId(const QString &eetRegisterId);
|
||||||
|
|
||||||
|
QString eetEndpoint() const;
|
||||||
|
void setEetEndpoint(const QString &eetEndpoint);
|
||||||
|
|
||||||
|
int eetUnitId() const;
|
||||||
|
void setEetUnitId(int eetUnitId);
|
||||||
|
|
||||||
|
int eetTimeout() const;
|
||||||
|
void setEetTimeout(int eetTimeout);
|
||||||
|
|
||||||
int eetMode() const;
|
int eetMode() const;
|
||||||
void setEetMode(int eetMode);
|
void setEetMode(int eetMode);
|
||||||
|
|
||||||
@@ -119,6 +131,9 @@ private:
|
|||||||
bool m_eetActive;
|
bool m_eetActive;
|
||||||
QString m_eetShopId;
|
QString m_eetShopId;
|
||||||
QString m_eetRegisterId;
|
QString m_eetRegisterId;
|
||||||
|
QString m_eetEndpoint;
|
||||||
|
int m_eetUnitId;
|
||||||
|
int m_eetTimeout;
|
||||||
int m_eetMode;
|
int m_eetMode;
|
||||||
QString m_eetCertificate;
|
QString m_eetCertificate;
|
||||||
QString m_eetKeyPassword;
|
QString m_eetKeyPassword;
|
||||||
|
|||||||
@@ -16,10 +16,13 @@ ShopSettingsForm::ShopSettingsForm(QWidget *parent) :
|
|||||||
registerBinding(ui->lettersPerLine);
|
registerBinding(ui->lettersPerLine);
|
||||||
registerBinding(ui->byMessage);
|
registerBinding(ui->byMessage);
|
||||||
|
|
||||||
#ifdef EET
|
#ifdef PRODEJNA_EET_V4
|
||||||
registerBinding(ui->eetActive);
|
registerBinding(ui->eetActive);
|
||||||
registerBinding(ui->eetShopId);
|
registerBinding(ui->eetShopId);
|
||||||
registerBinding(ui->eetRegisterId);
|
registerBinding(ui->eetRegisterId);
|
||||||
|
registerBinding(ui->eetEndpoint);
|
||||||
|
registerBinding(ui->eetUnitId);
|
||||||
|
registerBinding(ui->eetTimeout);
|
||||||
QList<ComboData> listModes;
|
QList<ComboData> listModes;
|
||||||
listModes << ComboData(0, tr("Simplifyed")) << ComboData(1, tr("Standard"));
|
listModes << ComboData(0, tr("Simplifyed")) << ComboData(1, tr("Standard"));
|
||||||
registerBinding(ui->eetMode, listModes);
|
registerBinding(ui->eetMode, listModes);
|
||||||
|
|||||||
@@ -187,7 +187,7 @@
|
|||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
<widget class="QLabel" name="label_4">
|
<widget class="QLabel" name="label_4">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Shop ID</string>
|
<string>Taxpayer EIC</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@@ -204,24 +204,54 @@
|
|||||||
<item row="1" column="1">
|
<item row="1" column="1">
|
||||||
<widget class="QLineEdit" name="eetRegisterId"/>
|
<widget class="QLineEdit" name="eetRegisterId"/>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label_17">
|
||||||
|
<property name="text">
|
||||||
|
<string>EET endpoint</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QLineEdit" name="eetEndpoint"/>
|
||||||
|
</item>
|
||||||
<item row="3" column="0">
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="label_18">
|
||||||
|
<property name="text">
|
||||||
|
<string>Unit ID</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QSpinBox" name="eetUnitId">
|
||||||
|
<property name="minimum">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>999999999</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
<widget class="QLabel" name="label_8">
|
<widget class="QLabel" name="label_8">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>EET mode</string>
|
<string>EET mode</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="1">
|
<item row="4" column="1">
|
||||||
<widget class="QComboBox" name="eetMode"/>
|
<widget class="QComboBox" name="eetMode"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="0">
|
<item row="5" column="0">
|
||||||
<widget class="QLabel" name="label_6">
|
<widget class="QLabel" name="label_6">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Certificate file</string>
|
<string>Certificate file</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="1">
|
<item row="5" column="1">
|
||||||
<widget class="QWidget" name="widget_2" native="true">
|
<widget class="QWidget" name="widget_2" native="true">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||||
<property name="leftMargin">
|
<property name="leftMargin">
|
||||||
@@ -249,28 +279,48 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="0">
|
<item row="6" column="0">
|
||||||
<widget class="QLabel" name="label_7">
|
<widget class="QLabel" name="label_7">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Private key password</string>
|
<string>Certificate password environment variable</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="1">
|
<item row="6" column="1">
|
||||||
<widget class="QLineEdit" name="eetKeyPassword">
|
<widget class="QLineEdit" name="eetKeyPassword">
|
||||||
<property name="echoMode">
|
<property name="text">
|
||||||
<enum>QLineEdit::Password</enum>
|
<string>EET_PKCS12_PASSWORD</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="6" column="0">
|
<item row="7" column="0">
|
||||||
|
<widget class="QLabel" name="label_19">
|
||||||
|
<property name="text">
|
||||||
|
<string>Request timeout (ms)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="1">
|
||||||
|
<widget class="QSpinBox" name="eetTimeout">
|
||||||
|
<property name="minimum">
|
||||||
|
<number>1000</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>600000</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>30000</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="8" column="0">
|
||||||
<widget class="QCheckBox" name="eetTest">
|
<widget class="QCheckBox" name="eetTest">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Test mode</string>
|
<string>Test mode</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="7" column="0" colspan="2">
|
<item row="9" column="0" colspan="2">
|
||||||
<widget class="QCheckBox" name="eetPlayground">
|
<widget class="QCheckBox" name="eetPlayground">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Communication with playground </string>
|
<string>Communication with playground </string>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ ShopOverview::ShopOverview(QWidget *parent) :
|
|||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
#ifndef EET
|
#ifndef PRODEJNA_EET_V4
|
||||||
ui->labelUnsendEET->setVisible(false);
|
ui->labelUnsendEET->setVisible(false);
|
||||||
ui->label_6->setVisible(false);
|
ui->label_6->setVisible(false);
|
||||||
#endif
|
#endif
|
||||||
@@ -32,7 +32,7 @@ void ShopOverview::refresh()
|
|||||||
ui->labelSales->setText(QString::number(today.totalPrice().toDouble()));
|
ui->labelSales->setText(QString::number(today.totalPrice().toDouble()));
|
||||||
ui->labelNumSales->setText(QString::number(today.count()));
|
ui->labelNumSales->setText(QString::number(today.count()));
|
||||||
|
|
||||||
#ifdef EET
|
#ifdef PRODEJNA_EET_V4
|
||||||
VoucherSum unsend = srv.unsendEET();
|
VoucherSum unsend = srv.unsendEET();
|
||||||
ui->labelUnsendEET->setText(QString::number(unsend.count()));
|
ui->labelUnsendEET->setText(QString::number(unsend.count()));
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+85
-77
@@ -4,10 +4,11 @@
|
|||||||
|
|
||||||
#include "settings/shopsettings.h"
|
#include "settings/shopsettings.h"
|
||||||
|
|
||||||
#ifdef EET
|
#ifdef PRODEJNA_EET_V4
|
||||||
#include <eetcpp.h>
|
#include "eet/eetv4adapter.h"
|
||||||
|
#include <eetv4client.h>
|
||||||
#endif
|
#endif
|
||||||
#include <QEventLoop>
|
#include <QProcessEnvironment>
|
||||||
#include <seasonservice.h>
|
#include <seasonservice.h>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
@@ -15,6 +16,15 @@
|
|||||||
inline double round(double value) { return value < 0 ? -std::floor(0.5 - value) : std::floor(0.5 + value); }
|
inline double round(double value) { return value < 0 ? -std::floor(0.5 - value) : std::floor(0.5 + value); }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
bool &eetOnlineState()
|
||||||
|
{
|
||||||
|
static bool online = true;
|
||||||
|
return online;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
VoucherPtr ShopService::createVoucher()
|
VoucherPtr ShopService::createVoucher()
|
||||||
{
|
{
|
||||||
QSharedPointer<Voucher> voucher(new Voucher);
|
QSharedPointer<Voucher> voucher(new Voucher);
|
||||||
@@ -189,96 +199,95 @@ void ShopService::updateRelatedItem(VoucherItem* item, int countAdded)
|
|||||||
|
|
||||||
bool ShopService::processEet(VoucherPtr voucher, QString &message)
|
bool ShopService::processEet(VoucherPtr voucher, QString &message)
|
||||||
{
|
{
|
||||||
#ifdef EET
|
#ifdef PRODEJNA_EET_V4
|
||||||
if (voucher->eetStatus() == Voucher::EET_NOT_ENTERING)
|
if (voucher->eetStatus() == Voucher::EET_NOT_ENTERING)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (!eetOnlineState())
|
||||||
|
{
|
||||||
|
message = QStringLiteral("EET is offline; the voucher remains queued for sending.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
SettingsService srvSettings("SHOP");
|
SettingsService srvSettings("SHOP");
|
||||||
ShopSettingsPtr settings = srvSettings.loadSettings<ShopSettings>();
|
ShopSettingsPtr settings = srvSettings.loadSettings<ShopSettings>();
|
||||||
|
|
||||||
loadSettings();
|
EetV4ShopConfig adapterConfig;
|
||||||
EetRequest request;
|
adapterConfig.endpoint = QUrl(settings->eetEndpoint());
|
||||||
request.setCelkTrzba(voucher->totalPrice().toDouble());
|
adapterConfig.taxpayerEic = settings->eetShopId();
|
||||||
request.setDatTrzby(voucher->payDateTime());
|
adapterConfig.unitId = settings->eetUnitId();
|
||||||
request.setIdPokl(settings->eetRegisterId());
|
adapterConfig.cashRegisterId = settings->eetRegisterId();
|
||||||
request.setIdProvoz(settings->eetShopId());
|
adapterConfig.timeoutMilliseconds = settings->eetTimeout();
|
||||||
request.setPrvniZaslani(voucher->eetStatus() == Voucher::EET_FOR_SEND);
|
adapterConfig.verificationMode = settings->eetTest();
|
||||||
request.setDicPopl(m_gs->dic());
|
|
||||||
request.setPoradCis(voucher->numSer());
|
|
||||||
request.setDatOdesl(QDateTime::currentDateTime());
|
|
||||||
request.setRezim((EetRequest::EetRezim)settings->eetMode());
|
|
||||||
|
|
||||||
if (m_gs->vatPayer())
|
EetV4SaleRequest request;
|
||||||
|
QString adapterError;
|
||||||
|
if (!EetV4ShopAdapter::createRequest(*voucher, adapterConfig, &request, &adapterError))
|
||||||
{
|
{
|
||||||
request.setDan1(voucher->vatAmountHigh().toDouble());
|
message = adapterError;
|
||||||
request.setDan2(voucher->vatAmountFirstLower().toDouble());
|
voucher->setEetStatus(Voucher::EET_ERROR);
|
||||||
request.setDan3(voucher->VatAmountSecondLower().toDouble());
|
update(voucher);
|
||||||
|
return false;
|
||||||
request.setZaklDan1(voucher->priceVatHigh().toDouble());
|
|
||||||
request.setZaklDan2(voucher->priceVatFirstLower().toDouble());
|
|
||||||
request.setZaklDan3(voucher->priceVatSecondLower().toDouble());
|
|
||||||
request.setZaklNepodlDph(voucher->priceNoVat().toDouble());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EetSender *sender = new EetSender(this);
|
const QString passwordVariable = settings->eetKeyPassword().trimmed();
|
||||||
sender->setupSigner(settings->eetCertificate(), settings->eetKeyPassword());
|
const QByteArray password = QProcessEnvironment::systemEnvironment()
|
||||||
sender->setPlayground(settings->eetPlayground());
|
.value(passwordVariable)
|
||||||
|
.toUtf8();
|
||||||
QEventLoop loop;
|
if (passwordVariable.isEmpty() || password.isEmpty())
|
||||||
bool replyFinished = false;
|
|
||||||
|
|
||||||
connect(sender, &EetSender::sendFinished, [this, voucher, sender, &loop, &replyFinished](){
|
|
||||||
Transaction tx;
|
|
||||||
|
|
||||||
voucher->setEetBkp(sender->resut()->bkp());
|
|
||||||
voucher->setEetPkp(sender->resut()->pkp());
|
|
||||||
voucher->setEetFik(sender->resut()->fik());
|
|
||||||
|
|
||||||
if (sender->resut()->status() == EetResult::RESPONSE_OK)
|
|
||||||
{
|
{
|
||||||
|
message = QStringLiteral("EET certificate password environment variable is missing.");
|
||||||
|
voucher->setEetStatus(Voucher::EET_ERROR);
|
||||||
|
update(voucher);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
EetV4ClientConfig clientConfig;
|
||||||
|
clientConfig.endpoint = adapterConfig.endpoint;
|
||||||
|
clientConfig.timeoutMilliseconds = adapterConfig.timeoutMilliseconds;
|
||||||
|
EetV4Client client(clientConfig);
|
||||||
|
EetV4Response response;
|
||||||
|
QString clientError;
|
||||||
|
if (!client.sendSale(request,
|
||||||
|
settings->eetCertificate(),
|
||||||
|
password,
|
||||||
|
&response,
|
||||||
|
&clientError))
|
||||||
|
{
|
||||||
|
message = clientError;
|
||||||
|
voucher->setEetStatus(Voucher::EET_ERROR);
|
||||||
|
update(voucher);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response.isSuccess() || response.pok.isEmpty())
|
||||||
|
{
|
||||||
|
message = response.message.isEmpty()
|
||||||
|
? QStringLiteral("EET returned an unsuccessful response.")
|
||||||
|
: response.message;
|
||||||
|
voucher->setEetStatus(Voucher::EET_ERROR);
|
||||||
|
update(voucher);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
voucher->setEetPok(response.pok);
|
||||||
voucher->setEetSendDateTime(QDateTime::currentDateTime());
|
voucher->setEetSendDateTime(QDateTime::currentDateTime());
|
||||||
voucher->setEetStatus(Voucher::EET_SENT);
|
voucher->setEetStatus(Voucher::EET_SENT);
|
||||||
}
|
update(voucher);
|
||||||
else
|
if (!response.warningCodes.isEmpty())
|
||||||
{
|
{
|
||||||
voucher->setEetStatus(Voucher::EET_ERROR);
|
message = QStringLiteral("EET warning codes: ");
|
||||||
}
|
for (int i = 0; i < response.warningCodes.size(); ++i)
|
||||||
|
|
||||||
this->update(voucher);
|
|
||||||
tx.commit();
|
|
||||||
sender->deleteLater();
|
|
||||||
|
|
||||||
loop.quit();
|
|
||||||
replyFinished = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
sender->sendRequest(&request);
|
|
||||||
|
|
||||||
if (!replyFinished)
|
|
||||||
{
|
{
|
||||||
loop.exec();
|
if (i > 0)
|
||||||
}
|
|
||||||
|
|
||||||
auto addMessage = [&message](EetMessage *msg, const QString &label){
|
|
||||||
if (message.isEmpty())
|
|
||||||
{
|
{
|
||||||
message = label + "\n";
|
message += QStringLiteral(", ");
|
||||||
}
|
}
|
||||||
|
message += QString::number(response.warningCodes.at(i));
|
||||||
message += QString::number(msg->code()) + ": " + msg->message();
|
|
||||||
};
|
|
||||||
|
|
||||||
foreach (EetMessage *msg, sender->resut()->errors()) {
|
|
||||||
addMessage(msg, "Errors:");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (EetMessage *msg, sender->resut()->warnings()) {
|
|
||||||
addMessage(msg, "Warnings:");
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
return voucher->eetStatus() == Voucher::EET_SENT;
|
|
||||||
#else
|
#else
|
||||||
return true;
|
return true;
|
||||||
#endif
|
#endif
|
||||||
@@ -286,15 +295,15 @@ bool ShopService::processEet(VoucherPtr voucher, QString &message)
|
|||||||
|
|
||||||
void ShopService::setEetOnline(bool online)
|
void ShopService::setEetOnline(bool online)
|
||||||
{
|
{
|
||||||
#ifdef EET
|
#ifdef PRODEJNA_EET_V4
|
||||||
EetSender::m_online = online;
|
eetOnlineState() = online;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShopService::isEetOnline()
|
bool ShopService::isEetOnline()
|
||||||
{
|
{
|
||||||
#ifdef EET
|
#ifdef PRODEJNA_EET_V4
|
||||||
return EetSender::m_online;
|
return eetOnlineState();
|
||||||
#else
|
#else
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
@@ -562,4 +571,3 @@ VoucherSum ShopService::summary(const QString& where) {
|
|||||||
|
|
||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QtTest>
|
||||||
|
|
||||||
|
#include "data/campdata.h"
|
||||||
|
#include "data/serviceitem.h"
|
||||||
|
#include "data/addressitem.h"
|
||||||
|
#include "data/personprice.h"
|
||||||
|
|
||||||
|
class CampTests : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void campDataDefaultsAreZeroed()
|
||||||
|
{
|
||||||
|
CampData camp;
|
||||||
|
|
||||||
|
QCOMPARE(camp.totalPrice().toDouble(), 0.0);
|
||||||
|
QCOMPARE(camp.sale().toDouble(), 0.0);
|
||||||
|
QCOMPARE(camp.totalSale().toDouble(), 0.0);
|
||||||
|
QCOMPARE(camp.fullPrice().toDouble(), 0.0);
|
||||||
|
QVERIFY(!camp.fixedSale());
|
||||||
|
QVERIFY(!camp.onVoucher());
|
||||||
|
}
|
||||||
|
|
||||||
|
void campPricesRoundTripWithoutLoss()
|
||||||
|
{
|
||||||
|
CampData camp;
|
||||||
|
|
||||||
|
camp.setTotalPrice(QDecDouble(19.9));
|
||||||
|
camp.setSale(QDecDouble(4.9));
|
||||||
|
camp.setFullPrice(QDecDouble(24.8));
|
||||||
|
|
||||||
|
QVERIFY(qAbs(camp.totalPrice().toDouble() - 19.9) < 0.0001);
|
||||||
|
QVERIFY(qAbs(camp.sale().toDouble() - 4.9) < 0.0001);
|
||||||
|
QVERIFY(qAbs(camp.fullPrice().toDouble() - 24.8) < 0.0001);
|
||||||
|
}
|
||||||
|
|
||||||
|
void campCollectsPeopleAndServices()
|
||||||
|
{
|
||||||
|
CampData camp;
|
||||||
|
auto person = QSharedPointer<AddressItem>::create();
|
||||||
|
auto service = QSharedPointer<ServiceItem>::create();
|
||||||
|
|
||||||
|
camp.addPerson(person);
|
||||||
|
camp.addServiceItem(service);
|
||||||
|
QCOMPARE(camp.people().size(), 1);
|
||||||
|
QCOMPARE(camp.services().size(), 1);
|
||||||
|
|
||||||
|
camp.removePerson(person);
|
||||||
|
camp.removeServiceItem(service);
|
||||||
|
QVERIFY(camp.people().isEmpty());
|
||||||
|
QVERIFY(camp.services().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
void serviceItemPricesRoundTrip()
|
||||||
|
{
|
||||||
|
ServiceItem item;
|
||||||
|
|
||||||
|
item.setPrice(QDecDouble(33.3));
|
||||||
|
item.setTotalPrice(QDecDouble(66.6));
|
||||||
|
item.setSale(QDecDouble(3.3));
|
||||||
|
|
||||||
|
QVERIFY(qAbs(item.price().toDouble() - 33.3) < 0.0001);
|
||||||
|
QVERIFY(qAbs(item.totalPrice().toDouble() - 66.6) < 0.0001);
|
||||||
|
QVERIFY(qAbs(item.sale().toDouble() - 3.3) < 0.0001);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addressItemOwnerFlagAndPrice()
|
||||||
|
{
|
||||||
|
AddressItem person;
|
||||||
|
|
||||||
|
person.setOwner(true);
|
||||||
|
person.setPrice(QDecDouble(12.5));
|
||||||
|
|
||||||
|
QVERIFY(person.owner());
|
||||||
|
QVERIFY(qAbs(person.price().toDouble() - 12.5) < 0.0001);
|
||||||
|
}
|
||||||
|
|
||||||
|
void personPriceAgeRangeAndPrice()
|
||||||
|
{
|
||||||
|
PersonPrice price;
|
||||||
|
|
||||||
|
price.setFromAge(3);
|
||||||
|
price.setToAge(12);
|
||||||
|
price.setPrice(QDecDouble(7.5));
|
||||||
|
price.setActive(true);
|
||||||
|
|
||||||
|
QCOMPARE(price.fromAge(), 3);
|
||||||
|
QCOMPARE(price.toAge(), 12);
|
||||||
|
QVERIFY(qAbs(price.price().toDouble() - 7.5) < 0.0001);
|
||||||
|
QVERIFY(price.active());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QCoreApplication app(argc, argv);
|
||||||
|
CampTests tests;
|
||||||
|
return QTest::qExec(&tests, argc, argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "camp_tests.moc"
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QtTest>
|
||||||
|
|
||||||
|
#include "data/commoditydata.h"
|
||||||
|
|
||||||
|
class CommodityTests : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void defaultsAreZero()
|
||||||
|
{
|
||||||
|
CommodityData commodity;
|
||||||
|
|
||||||
|
QCOMPARE(commodity.price().toDouble(), 0.0);
|
||||||
|
QCOMPARE(commodity.count(), 0);
|
||||||
|
QCOMPARE(commodity.vat(), Enums::NONE);
|
||||||
|
QVERIFY(!commodity.favorite());
|
||||||
|
}
|
||||||
|
|
||||||
|
void priceRoundTripUsesFixedPointStorage()
|
||||||
|
{
|
||||||
|
CommodityData commodity;
|
||||||
|
|
||||||
|
commodity.setPrice(QDecDouble(19.9));
|
||||||
|
|
||||||
|
QVERIFY(qAbs(commodity.price().toDouble() - 19.9) < 0.0001);
|
||||||
|
QCOMPARE(commodity.price(), commodity.unitPrice());
|
||||||
|
}
|
||||||
|
|
||||||
|
void priceRoundTripHandlesWholeAmounts()
|
||||||
|
{
|
||||||
|
CommodityData commodity;
|
||||||
|
|
||||||
|
commodity.setPrice(QDecDouble(125));
|
||||||
|
|
||||||
|
QCOMPARE(commodity.price().toDouble(), 125.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vatMirrorsShopItemInterface()
|
||||||
|
{
|
||||||
|
CommodityData commodity;
|
||||||
|
|
||||||
|
commodity.setVat(Enums::HIGH);
|
||||||
|
|
||||||
|
QCOMPARE(commodity.vat(), Enums::HIGH);
|
||||||
|
QCOMPARE(commodity.vatType(), Enums::HIGH);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pluginIdMatchesEmbeddedMetadata()
|
||||||
|
{
|
||||||
|
CommodityData commodity;
|
||||||
|
|
||||||
|
QCOMPARE(commodity.pluginId(), QStringLiteral("COMMODITY"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void basicPropertiesRoundTrip()
|
||||||
|
{
|
||||||
|
CommodityData commodity;
|
||||||
|
|
||||||
|
commodity.setName(QStringLiteral("Položka"));
|
||||||
|
commodity.setShortName(QStringLiteral("Pol."));
|
||||||
|
commodity.setCode(QStringLiteral("A-01"));
|
||||||
|
commodity.setCount(7);
|
||||||
|
commodity.setFavorite(true);
|
||||||
|
|
||||||
|
QCOMPARE(commodity.name(), QStringLiteral("Položka"));
|
||||||
|
QCOMPARE(commodity.shortName(), QStringLiteral("Pol."));
|
||||||
|
QCOMPARE(commodity.code(), QStringLiteral("A-01"));
|
||||||
|
QCOMPARE(commodity.count(), 7);
|
||||||
|
QVERIFY(commodity.favorite());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QCoreApplication app(argc, argv);
|
||||||
|
CommodityTests tests;
|
||||||
|
return QTest::qExec(&tests, argc, argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "commodity_tests.moc"
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QLocale>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QSignalSpy>
|
||||||
|
#include <QTemporaryDir>
|
||||||
|
#include <QtTest>
|
||||||
|
|
||||||
|
#include "context.h"
|
||||||
|
#include "data/permission.h"
|
||||||
|
#include "data/role.h"
|
||||||
|
#include "data/user.h"
|
||||||
|
#include "imetadataplugin.h"
|
||||||
|
#include "permissionevaluator.h"
|
||||||
|
|
||||||
|
class CoreTests : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void init()
|
||||||
|
{
|
||||||
|
Context::instance().setCurrentUser({});
|
||||||
|
}
|
||||||
|
|
||||||
|
void cleanup()
|
||||||
|
{
|
||||||
|
Context::instance().setCurrentUser({});
|
||||||
|
}
|
||||||
|
|
||||||
|
void metadataRejectsMissingObject()
|
||||||
|
{
|
||||||
|
QVERIFY(!IMetaDataPlugin::loadBaseMetaData({}));
|
||||||
|
QVERIFY(!IMetaDataPlugin::loadBaseMetaData(QJsonObject{{"MetaData", QStringLiteral("invalid")}}));
|
||||||
|
}
|
||||||
|
|
||||||
|
void metadataUsesDefaultLocaleAndExtractsSchema()
|
||||||
|
{
|
||||||
|
const QJsonObject metadata{
|
||||||
|
{"id", QStringLiteral("EXAMPLE")},
|
||||||
|
{"schemaVersion", 2},
|
||||||
|
{"name", QJsonObject{{"default", QStringLiteral("Example plugin")}}},
|
||||||
|
{"description", QJsonObject{{"default", QStringLiteral("Example description")}}},
|
||||||
|
{"sql", QJsonArray{QStringLiteral("CREATE TABLE example (id INTEGER)")}},
|
||||||
|
{"dependencies", QJsonArray{QStringLiteral("CORE")}}
|
||||||
|
};
|
||||||
|
|
||||||
|
const MetaDataPtr result = IMetaDataPlugin::loadBaseMetaData(QJsonObject{{"MetaData", metadata}});
|
||||||
|
|
||||||
|
QVERIFY(result);
|
||||||
|
QCOMPARE(result->getId(), QStringLiteral("EXAMPLE"));
|
||||||
|
QCOMPARE(result->getName(), QStringLiteral("Example plugin"));
|
||||||
|
QCOMPARE(result->getDescription(), QStringLiteral("Example description"));
|
||||||
|
QCOMPARE(result->getSchemaVersion(), 2);
|
||||||
|
QCOMPARE(result->getSchemas(), QStringList{QStringLiteral("CREATE TABLE example (id INTEGER)")});
|
||||||
|
QCOMPARE(result->getDependsOn(), QStringList{QStringLiteral("CORE")});
|
||||||
|
}
|
||||||
|
|
||||||
|
void metadataPrefersSystemLocale()
|
||||||
|
{
|
||||||
|
const QString locale = QLocale::system().name();
|
||||||
|
const QJsonObject metadata{
|
||||||
|
{"id", QStringLiteral("LOCALIZED")},
|
||||||
|
{"name", QJsonObject{{"default", QStringLiteral("Fallback")}, {locale, QStringLiteral("Localized")}}},
|
||||||
|
{"description", QJsonObject{{"default", QStringLiteral("Fallback description")}}}
|
||||||
|
};
|
||||||
|
|
||||||
|
const MetaDataPtr result = IMetaDataPlugin::loadBaseMetaData(QJsonObject{{"MetaData", metadata}});
|
||||||
|
|
||||||
|
QVERIFY(result);
|
||||||
|
QCOMPARE(result->getName(), QStringLiteral("Localized"));
|
||||||
|
QCOMPARE(result->getDescription(), QStringLiteral("Fallback description"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void permissionDeniedWithoutUser()
|
||||||
|
{
|
||||||
|
PermissionEvaluator evaluator;
|
||||||
|
QSignalSpy denied(&evaluator, &PermissionEvaluator::permissionDenied);
|
||||||
|
|
||||||
|
QVERIFY(!evaluator.hasPermission(QStringLiteral("SHOP"), QStringLiteral("READ")));
|
||||||
|
QCOMPARE(denied.count(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void administratorBypassesPermissionLookup()
|
||||||
|
{
|
||||||
|
auto user = QSharedPointer<User>::create();
|
||||||
|
user->setIsAdmin(true);
|
||||||
|
Context::instance().setCurrentUser(user);
|
||||||
|
|
||||||
|
PermissionEvaluator evaluator;
|
||||||
|
QSignalSpy denied(&evaluator, &PermissionEvaluator::permissionDenied);
|
||||||
|
|
||||||
|
QVERIFY(evaluator.hasPermission(QStringLiteral("SHOP"), QStringLiteral("DELETE")));
|
||||||
|
QCOMPARE(denied.count(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void matchingRolePermissionIsGranted()
|
||||||
|
{
|
||||||
|
auto permission = QSharedPointer<Permission>::create();
|
||||||
|
permission->setPluginId(QStringLiteral("SHOP"));
|
||||||
|
permission->setPermissionName(QStringLiteral("READ"));
|
||||||
|
|
||||||
|
auto role = QSharedPointer<Role>::create();
|
||||||
|
role->addPermission(permission);
|
||||||
|
|
||||||
|
auto user = QSharedPointer<User>::create();
|
||||||
|
user->setIsAdmin(false);
|
||||||
|
user->addRole(role);
|
||||||
|
Context::instance().setCurrentUser(user);
|
||||||
|
|
||||||
|
PermissionEvaluator evaluator;
|
||||||
|
QSignalSpy denied(&evaluator, &PermissionEvaluator::permissionDenied);
|
||||||
|
|
||||||
|
QVERIFY(evaluator.hasPermission(QStringLiteral("SHOP"), QStringLiteral("READ")));
|
||||||
|
QCOMPARE(denied.count(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mismatchedPermissionIsDeniedAndEmitsSignal()
|
||||||
|
{
|
||||||
|
auto permission = QSharedPointer<Permission>::create();
|
||||||
|
permission->setPluginId(QStringLiteral("SHOP"));
|
||||||
|
permission->setPermissionName(QStringLiteral("READ"));
|
||||||
|
|
||||||
|
auto role = QSharedPointer<Role>::create();
|
||||||
|
role->addPermission(permission);
|
||||||
|
|
||||||
|
auto user = QSharedPointer<User>::create();
|
||||||
|
user->setIsAdmin(false);
|
||||||
|
user->addRole(role);
|
||||||
|
Context::instance().setCurrentUser(user);
|
||||||
|
|
||||||
|
PermissionEvaluator evaluator;
|
||||||
|
QSignalSpy denied(&evaluator, &PermissionEvaluator::permissionDenied);
|
||||||
|
|
||||||
|
QVERIFY(!evaluator.hasPermission(QStringLiteral("SHOP"), QStringLiteral("DELETE")));
|
||||||
|
QCOMPARE(denied.count(), 1);
|
||||||
|
QCOMPARE(denied.takeFirst().at(0).toString(), QStringLiteral("DELETE"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QCoreApplication app(argc, argv);
|
||||||
|
QTemporaryDir settingsDirectory;
|
||||||
|
if (!settingsDirectory.isValid()) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSettings::setDefaultFormat(QSettings::IniFormat);
|
||||||
|
QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, settingsDirectory.path());
|
||||||
|
|
||||||
|
CoreTests tests;
|
||||||
|
return QTest::qExec(&tests, argc, argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "core_tests.moc"
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QTemporaryDir>
|
||||||
|
#include <QtTest>
|
||||||
|
|
||||||
|
#include <QxOrm.h>
|
||||||
|
|
||||||
|
#include "context.h"
|
||||||
|
#include "service.h"
|
||||||
|
#include "../countryregister/data/countrydata.h"
|
||||||
|
|
||||||
|
class PersistenceTests : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void crudCycleOnTemporaryDatabase()
|
||||||
|
{
|
||||||
|
QTemporaryDir dir;
|
||||||
|
QVERIFY(dir.isValid());
|
||||||
|
const QString dbPath = dir.filePath(QStringLiteral("countries.db"));
|
||||||
|
|
||||||
|
qx::QxSqlDatabase::getSingleton()->setDriverName(QStringLiteral("QSQLITE"));
|
||||||
|
qx::QxSqlDatabase::getSingleton()->setDatabaseName(dbPath);
|
||||||
|
|
||||||
|
const QSqlError createError = qx::dao::create_table<CountryData>();
|
||||||
|
QVERIFY2(!createError.isValid(), qPrintable(createError.text()));
|
||||||
|
QVERIFY(QFileInfo::exists(dbPath));
|
||||||
|
|
||||||
|
Service<CountryData> service;
|
||||||
|
|
||||||
|
auto country = QSharedPointer<CountryData>::create();
|
||||||
|
country->setCode2(QStringLiteral("CZ"));
|
||||||
|
country->setCode3(QStringLiteral("CZE"));
|
||||||
|
country->setCzechName(QStringLiteral("Česko"));
|
||||||
|
country->setEnglishName(QStringLiteral("Czechia"));
|
||||||
|
|
||||||
|
QSignalSpy errorSpy(&service, &Service<CountryData>::dbError);
|
||||||
|
service.save(country);
|
||||||
|
QCOMPARE(errorSpy.count(), 0);
|
||||||
|
QVERIFY(country->id() > 0);
|
||||||
|
|
||||||
|
const QList<QSharedPointer<CountryData>> all = service.all();
|
||||||
|
QCOMPARE(all.size(), 1);
|
||||||
|
QCOMPARE(all.first()->code2(), QStringLiteral("CZ"));
|
||||||
|
QCOMPARE(all.first()->czechName(), QStringLiteral("Česko"));
|
||||||
|
|
||||||
|
auto loaded = service.loadById(country->id());
|
||||||
|
QVERIFY(!loaded.isNull());
|
||||||
|
QCOMPARE(loaded->code3(), QStringLiteral("CZE"));
|
||||||
|
|
||||||
|
loaded->setCzechName(QStringLiteral("Česká republika"));
|
||||||
|
service.update(loaded);
|
||||||
|
QCOMPARE(errorSpy.count(), 0);
|
||||||
|
|
||||||
|
auto reloaded = service.loadById(country->id());
|
||||||
|
QCOMPARE(reloaded->czechName(), QStringLiteral("Česká republika"));
|
||||||
|
|
||||||
|
service.erase(reloaded);
|
||||||
|
QCOMPARE(errorSpy.count(), 0);
|
||||||
|
QVERIFY(service.all().isEmpty());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QCoreApplication app(argc, argv);
|
||||||
|
QTemporaryDir settingsDirectory;
|
||||||
|
if (!settingsDirectory.isValid()) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSettings::setDefaultFormat(QSettings::IniFormat);
|
||||||
|
QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, settingsDirectory.path());
|
||||||
|
|
||||||
|
PersistenceTests tests;
|
||||||
|
return QTest::qExec(&tests, argc, argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "persistence_tests.moc"
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QPluginLoader>
|
||||||
|
#include <QtTest>
|
||||||
|
|
||||||
|
#include "imetadataplugin.h"
|
||||||
|
#include "iplugin.h"
|
||||||
|
|
||||||
|
struct PluginExpectation
|
||||||
|
{
|
||||||
|
QString path;
|
||||||
|
QString id;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PluginMetadataTests : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit PluginMetadataTests(QList<PluginExpectation> plugins)
|
||||||
|
: m_plugins(std::move(plugins))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void embeddedMetadataMatchesPluginContract()
|
||||||
|
{
|
||||||
|
for (const PluginExpectation &plugin : m_plugins) {
|
||||||
|
QVERIFY2(QFileInfo::exists(plugin.path), qPrintable(plugin.path));
|
||||||
|
|
||||||
|
QPluginLoader loader(plugin.path);
|
||||||
|
const QJsonObject metadata = loader.metaData();
|
||||||
|
QVERIFY2(!metadata.isEmpty(), qPrintable(loader.errorString()));
|
||||||
|
QCOMPARE(metadata.value(QStringLiteral("IID")).toString(), QString::fromLatin1(PluginInterface_iid));
|
||||||
|
|
||||||
|
const QJsonObject data = metadata.value(QStringLiteral("MetaData")).toObject();
|
||||||
|
QVERIFY2(!data.isEmpty(), qPrintable(plugin.path));
|
||||||
|
QCOMPARE(data.value(QStringLiteral("id")).toString(), plugin.id);
|
||||||
|
QVERIFY(data.value(QStringLiteral("schemaVersion")).toInt() > 0);
|
||||||
|
QVERIFY(data.value(QStringLiteral("sql")).isArray());
|
||||||
|
QVERIFY(data.value(QStringLiteral("dependencies")).isArray());
|
||||||
|
|
||||||
|
const MetaDataPtr parsed = IMetaDataPlugin::loadBaseMetaData(metadata);
|
||||||
|
QVERIFY(parsed);
|
||||||
|
QCOMPARE(parsed->getId(), plugin.id);
|
||||||
|
QCOMPARE(parsed->getSchemaVersion(), data.value(QStringLiteral("schemaVersion")).toInt());
|
||||||
|
QCOMPARE(parsed->getSchemas().size(), data.value(QStringLiteral("sql")).toArray().size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QList<PluginExpectation> m_plugins;
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QCoreApplication app(argc, argv);
|
||||||
|
if (argc < 3 || (argc - 1) % 2 != 0) {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<PluginExpectation> plugins;
|
||||||
|
for (int index = 1; index < argc; index += 2) {
|
||||||
|
plugins.append({QString::fromLocal8Bit(argv[index]), QString::fromLocal8Bit(argv[index + 1])});
|
||||||
|
}
|
||||||
|
|
||||||
|
PluginMetadataTests tests(std::move(plugins));
|
||||||
|
return QTest::qExec(&tests, 1, argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "plugin_metadata_tests.moc"
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QtTest>
|
||||||
|
|
||||||
|
#include "data/accservice.h"
|
||||||
|
|
||||||
|
class ServicesTests : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void defaultsMatchExpectedInitialState()
|
||||||
|
{
|
||||||
|
AccService service;
|
||||||
|
|
||||||
|
QCOMPARE(service.price().toDouble(), 0.0);
|
||||||
|
QVERIFY(service.active());
|
||||||
|
QVERIFY(!service.salePossible());
|
||||||
|
QCOMPARE(service.serviceType(), AccService::OTHER);
|
||||||
|
QCOMPARE(service.vatType(), Enums::NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void priceRoundTripUsesFixedPointStorage()
|
||||||
|
{
|
||||||
|
AccService service;
|
||||||
|
|
||||||
|
service.setPrice(QDecDouble(19.9));
|
||||||
|
|
||||||
|
QVERIFY(qAbs(service.price().toDouble() - 19.9) < 0.0001);
|
||||||
|
}
|
||||||
|
|
||||||
|
void priceRoundTripHandlesWholeAmounts()
|
||||||
|
{
|
||||||
|
AccService service;
|
||||||
|
|
||||||
|
service.setPrice(QDecDouble(250));
|
||||||
|
|
||||||
|
QCOMPARE(service.price().toDouble(), 250.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void basicPropertiesRoundTrip()
|
||||||
|
{
|
||||||
|
AccService service;
|
||||||
|
|
||||||
|
service.setAccServiceName(QStringLiteral("Ubytování"));
|
||||||
|
service.setAccServiceCode(QStringLiteral("UB-1"));
|
||||||
|
service.setActive(false);
|
||||||
|
service.setSalePossible(true);
|
||||||
|
service.setServiceType(AccService::TENT);
|
||||||
|
service.setVatType(Enums::FIRST_LOWER);
|
||||||
|
|
||||||
|
QCOMPARE(service.accServiceName(), QStringLiteral("Ubytování"));
|
||||||
|
QCOMPARE(service.accServiceCode(), QStringLiteral("UB-1"));
|
||||||
|
QVERIFY(!service.active());
|
||||||
|
QVERIFY(service.salePossible());
|
||||||
|
QCOMPARE(service.serviceType(), AccService::TENT);
|
||||||
|
QCOMPARE(service.vatType(), Enums::FIRST_LOWER);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QCoreApplication app(argc, argv);
|
||||||
|
ServicesTests tests;
|
||||||
|
return QTest::qExec(&tests, argc, argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "services_tests.moc"
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QtTest>
|
||||||
|
|
||||||
|
#include "data/voucher.h"
|
||||||
|
#ifdef PRODEJNA_EET_V4
|
||||||
|
#include "eet/eetv4adapter.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class ShopTests : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void defaultsMatchNewVoucherState()
|
||||||
|
{
|
||||||
|
Voucher voucher;
|
||||||
|
|
||||||
|
QCOMPARE(voucher.totalPrice().toDouble(), 0.0);
|
||||||
|
QCOMPARE(voucher.status(), Voucher::NEW);
|
||||||
|
QCOMPARE(voucher.eetStatus(), Voucher::EET_FOR_SEND);
|
||||||
|
}
|
||||||
|
|
||||||
|
void totalPriceRoundTripUsesFixedPointStorage()
|
||||||
|
{
|
||||||
|
Voucher voucher;
|
||||||
|
|
||||||
|
voucher.setTotalPrice(QDecDouble(19.9));
|
||||||
|
|
||||||
|
QVERIFY(qAbs(voucher.totalPrice().toDouble() - 19.9) < 0.0001);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vatAmountIsDifferenceBetweenTotalAndBase()
|
||||||
|
{
|
||||||
|
Voucher voucher;
|
||||||
|
|
||||||
|
voucher.setTotalPriceVatHigh(QDecDouble(121));
|
||||||
|
voucher.setPriceVatHigh(QDecDouble(100));
|
||||||
|
|
||||||
|
QVERIFY(qAbs(voucher.vatAmountHigh().toDouble() - 21.0) < 0.0001);
|
||||||
|
}
|
||||||
|
|
||||||
|
void voucherSumAccumulatesPricesWithoutTruncation()
|
||||||
|
{
|
||||||
|
VoucherSum sum(0, 0);
|
||||||
|
|
||||||
|
sum.addPrice(QDecDouble(19.9));
|
||||||
|
sum.addPrice(QDecDouble(0.1));
|
||||||
|
|
||||||
|
QVERIFY(qAbs(sum.totalPrice().toDouble() - 20.0) < 0.0001);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef PRODEJNA_EET_V4
|
||||||
|
void eetV4AdapterBuildsRequestFromVoucher()
|
||||||
|
{
|
||||||
|
Voucher voucher;
|
||||||
|
voucher.setNumSer(QStringLiteral("SHOP-42"));
|
||||||
|
voucher.setPayDateTime(QDateTime(QDate(2026, 10, 13),
|
||||||
|
QTime(20, 30),
|
||||||
|
Qt::OffsetFromUTC,
|
||||||
|
7200));
|
||||||
|
voucher.setTotalPrice(QDecDouble(QStringLiteral("19.90").toLatin1().constData()));
|
||||||
|
|
||||||
|
EetV4ShopConfig config;
|
||||||
|
config.endpoint = QUrl(QStringLiteral("https://eet.example.test/eet"));
|
||||||
|
config.taxpayerEic = QStringLiteral("CZ123456789");
|
||||||
|
config.unitId = 3;
|
||||||
|
config.cashRegisterId = QStringLiteral("KASA1");
|
||||||
|
|
||||||
|
EetV4SaleRequest request;
|
||||||
|
QString error;
|
||||||
|
QVERIFY2(EetV4ShopAdapter::createRequest(voucher, config, &request, &error),
|
||||||
|
qPrintable(error));
|
||||||
|
QCOMPARE(request.receiptNumber, QStringLiteral("SHOP-42"));
|
||||||
|
QCOMPARE(request.totalSale.toString(), QStringLiteral("19.90"));
|
||||||
|
QCOMPARE(request.unitId, 3);
|
||||||
|
QCOMPARE(request.cashRegisterId, QStringLiteral("KASA1"));
|
||||||
|
QVERIFY(request.verificationMode);
|
||||||
|
QVERIFY(request.firstSubmission);
|
||||||
|
}
|
||||||
|
|
||||||
|
void eetV4AdapterRejectsInvalidConfiguration()
|
||||||
|
{
|
||||||
|
Voucher voucher;
|
||||||
|
voucher.setNumSer(QStringLiteral("SHOP-42"));
|
||||||
|
voucher.setPayDateTime(QDateTime::currentDateTime());
|
||||||
|
voucher.setTotalPrice(QDecDouble(QStringLiteral("1.00").toLatin1().constData()));
|
||||||
|
|
||||||
|
EetV4ShopConfig config;
|
||||||
|
config.endpoint = QUrl(QStringLiteral("http://not-tls.example.test/eet"));
|
||||||
|
EetV4SaleRequest request;
|
||||||
|
QString error;
|
||||||
|
|
||||||
|
QVERIFY(!EetV4ShopAdapter::createRequest(voucher, config, &request, &error));
|
||||||
|
QVERIFY(error.contains(QStringLiteral("HTTPS")));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QCoreApplication app(argc, argv);
|
||||||
|
ShopTests tests;
|
||||||
|
return QTest::qExec(&tests, argc, argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "shop_tests.moc"
|
||||||
Reference in New Issue
Block a user