#include "eetv4request.h" #include namespace { void addError(QStringList *errors, const QString &message) { if (errors != nullptr) { errors->append(message); } } bool isValidEic(const QString &value) { static const QRegularExpression expression(QStringLiteral("^CZ[0-9]{8,10}$")); return expression.match(value).hasMatch(); } bool isValidRestrictedString(const QString &value, int maxLength) { static const QRegularExpression expression(QStringLiteral("^[0-9A-Za-z.,:;/#_\\- ]+$")); return value.size() >= 1 && value.size() <= maxLength && expression.match(value).hasMatch(); } } EetV4Amount::EetV4Amount(qint64 cents) : m_cents(cents) { } bool EetV4Amount::fromString(const QString &value, EetV4Amount *amount, QString *error) { static const QRegularExpression expression( QStringLiteral("^(?:(?:0|-?[1-9][0-9]{0,7})\\.[0-9]{2}|-0\\.(?:0[1-9]|[1-9][0-9]))$")); if (!expression.match(value).hasMatch()) { if (error != nullptr) { *error = QStringLiteral("Amount must contain two decimal places and fit the EET range."); } return false; } const bool negative = value.startsWith('-'); const QString absolute = negative ? value.mid(1) : value; const QStringList parts = absolute.split('.'); const qint64 whole = parts.at(0).toLongLong(); const qint64 cents = whole * 100 + parts.at(1).toLongLong(); if (amount != nullptr) { *amount = EetV4Amount(negative ? -cents : cents); } return true; } qint64 EetV4Amount::cents() const { return m_cents; } QString EetV4Amount::toString() const { const bool negative = m_cents < 0; const qint64 absolute = negative ? -m_cents : m_cents; return QStringLiteral("%1%2.%3") .arg(negative ? QStringLiteral("-") : QString()) .arg(absolute / 100) .arg(absolute % 100, 2, 10, QLatin1Char('0')); } bool EetV4SaleRequest::validate(QStringList *errors) const { bool valid = true; const auto require = [&valid, errors](bool condition, const QString &message) { if (!condition) { valid = false; addError(errors, message); } }; require(!messageUuid.isNull(), QStringLiteral("messageUuid must not be null.")); require(sentAt.isValid() && sentAt.timeSpec() != Qt::LocalTime, QStringLiteral("sentAt must be a valid timestamp with an explicit offset.")); require(saleAt.isValid() && saleAt.timeSpec() != Qt::LocalTime, QStringLiteral("saleAt must be a valid timestamp with an explicit offset.")); require(isValidEic(taxpayerEic), QStringLiteral("taxpayerEic must match CZ followed by 8 to 10 digits.")); require(authorizingTaxpayerEic.isEmpty() || isValidEic(authorizingTaxpayerEic), QStringLiteral("authorizingTaxpayerEic has an invalid format.")); require(!multipleTaxpayerAuthorization || !authorizingTaxpayerEic.isEmpty(), QStringLiteral("authorizingTaxpayerEic is required for multiple taxpayer authorization.")); require(unitId >= 1 && unitId <= 999999999, QStringLiteral("unitId must be between 1 and 999999999.")); require(isValidRestrictedString(cashRegisterId, 20), QStringLiteral("cashRegisterId has an invalid format or length.")); require(isValidRestrictedString(receiptNumber, 25), QStringLiteral("receiptNumber has an invalid format or length.")); return valid; } QString EetV4SaleRequest::formatDateTime(const QDateTime &dateTime) { const int offset = dateTime.offsetFromUtc(); const QChar sign = offset < 0 ? QLatin1Char('-') : QLatin1Char('+'); const int absoluteOffset = qAbs(offset); return dateTime.toString(QStringLiteral("yyyy-MM-dd'T'HH:mm:ss")) + sign + QStringLiteral("%1:%2") .arg(absoluteOffset / 3600, 2, 10, QLatin1Char('0')) .arg((absoluteOffset % 3600) / 60, 2, 10, QLatin1Char('0')); }