Compare commits

...

6 Commits

Author SHA1 Message Date
5a55ad6ef0 ComputeDurationFromCost():
brutto-time must be updated in case of an carry-over-section.
2024-10-02 15:21:21 +02:00
5a77958e8d getOutOfService():
Check for special days (holidays) with out-of-service-time-ranges.
	Holidays have higher priority than usual days ("default").
2024-10-02 15:19:32 +02:00
a1e7f4629a getService():
Check for special days (holidays) with service-time-ranges.
	Holidays have higher priority than usual days ("default").
2024-10-02 15:18:16 +02:00
efc2582c36 getCarryOver():
Check for special days (holidays) with carry-over-sections.
	Holidays have higher priority than usual days.
2024-10-02 15:17:00 +02:00
a72f5a5019 getPrepaid():
Check for special days (holidays) with prepaid sections.
	Holidays have higher priority than usual days.
2024-10-02 15:15:19 +02:00
28f0ea9fce Merge from kleipeda-experimental-snapshot-2024-09-27:
started new implementation of tariff-calculator.
2024-10-01 09:25:53 +02:00
7 changed files with 6131 additions and 35 deletions

1
\ Normal file
View File

@@ -0,0 +1 @@
return std::make_pair(CalcState(CalcState::State::OVERPAID), dt);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,762 @@
#include "calculate_price.h"
#include "configuration.h"
#include "calculator_functions.h"
#include "payment_option.h"
#include "utilities.h"
#include <QFile>
#include <QFileInfo>
#include <QDateTime>
#include <QDebug>
#include <QList>
QString const CalcState::SUCCESS = "SUCCESS";
QString const CalcState::ERROR_PARSING_ZONE_NR = "ERROR_PARSING_ZONE_NR";
QString const CalcState::ERROR_LOADING_TARIFF = "ERROR_LOADING_TARIFF";
QString const CalcState::ERROR_PARSING_TARIFF = "ERROR_PARSING_TARIFF";
QString const CalcState::NEGATIVE_PARKING_TIME = "NEGATIVE_PARKING_TIME";
QString const CalcState::INVALID_START_DATE = "INVALID_START_DATE";
QString const CalcState::WRONG_PARAM_VALUES = "WRONG_PARAM_VALUES";
QString const CalcState::WRONG_ISO_TIME_FORMAT = "WRONG_ISO_TIME_FORMAT";
QString const CalcState::ABOVE_MAX_PARKING_TIME = "ABOVE_MAX_PARKING_TIME";
QString const CalcState::BELOW_MIN_PARKING_TIME = "BELOW_MIN_PARKING_TIME";
QString const CalcState::BELOW_MIN_PARKING_PRICE = "BELOW_MIN_PARKING_PRICE";
QString const CalcState::ABOVE_MAX_PARKING_PRICE = "ABOVE_MAX_PARKING_PRICE";
QString const CalcState::OVERPAID = "OVERPAID";
QString const CalcState::OUTSIDE_ALLOWED_PARKING_TIME = "OUTSIDE_ALLOWED_PARKING_TIME";
QList<int> CALCULATE_LIBRARY_API get_time_steps(Configuration *cfg) {
return Calculator::GetInstance().GetTimeSteps(cfg);
}
int CALCULATE_LIBRARY_API get_minimal_parkingtime(Configuration const *cfg,
PERMIT_TYPE permitType,
int paymentOptionIndex) {
int minTime = 0;
switch(permitType) {
case PERMIT_TYPE::SHORT_TERM_PARKING: { // e.g. szeged (customer_281)
QList<int> const tsteps = Calculator::GetInstance().GetTimeSteps((Configuration *)cfg, paymentOptionIndex);
Q_UNUSED(tsteps);
minTime = cfg->getPaymentOptions(paymentOptionIndex).pop_min_time;
} break;
case PERMIT_TYPE::DAY_TICKET_ADULT: {
} break;
case PERMIT_TYPE::DAY_TICKET_TEEN: {
} break;
case PERMIT_TYPE::DAY_TICKET_CHILD: {
} break;
default:
// for each new sell-procedure, recomute the timesteps. implicitly, set
// the minimal parking time.
Calculator::GetInstance().ResetTimeSteps(paymentOptionIndex);
Calculator::GetInstance().GetTimeSteps((Configuration *)cfg, paymentOptionIndex);
minTime = qRound(cfg->getPaymentOptions(paymentOptionIndex).pop_min_time);
}
return minTime;
}
int CALCULATE_LIBRARY_API get_maximal_parkingtime(Configuration const *cfg,
PERMIT_TYPE permitType,
int paymentOptionIndex) {
int maxTime = 0;
switch(permitType) {
case PERMIT_TYPE::SHORT_TERM_PARKING: { // e.g. szeged (customer_281)
maxTime = cfg->getPaymentOptions(paymentOptionIndex).pop_max_time;
} break;
case PERMIT_TYPE::DAY_TICKET_ADULT: {
} break;
case PERMIT_TYPE::DAY_TICKET_TEEN: {
} break;
case PERMIT_TYPE::DAY_TICKET_CHILD: {
} break;
default: ;
}
return maxTime;
}
int CALCULATE_LIBRARY_API get_minimal_parkingprice(Configuration *cfg,
PERMIT_TYPE permitType,
int paymentOptionIndex,
QDateTime const &start) {
int minPrice = -1;
switch(permitType) {
case PERMIT_TYPE::SHORT_TERM_PARKING: { // e.g. szeged (customer_281)
minPrice = cfg->getPaymentOptions(paymentOptionIndex).pop_min_price;
} break;
case PERMIT_TYPE::DAY_TICKET_ADULT: {
} break;
case PERMIT_TYPE::DAY_TICKET_TEEN: {
} break;
case PERMIT_TYPE::DAY_TICKET_CHILD: {
} break;
case PERMIT_TYPE::DAY_TICKET: {
minPrice = compute_product_price(cfg, permitType, start);
} break;
default: ;
}
return minPrice;
}
int CALCULATE_LIBRARY_API compute_product_price(Configuration const *cfg,
PERMIT_TYPE permitType,
QDateTime const &start,
QDateTime *productStart,
QDateTime *productEnd) {
switch(permitType) {
case PERMIT_TYPE::SHORT_TERM_PARKING: { // e.g. szeged (customer_281)
} break;
case PERMIT_TYPE::DAY_TICKET_CHILD:
// [[fallthrough]];
case PERMIT_TYPE::DAY_TICKET_TEEN:
// [[fallthrough]];
case PERMIT_TYPE::FOOD_STAMP:
// [[fallthrough]];
case PERMIT_TYPE::DAY_TICKET_ADULT: {
std::optional<QVector<ATBTariffProduct>> products = cfg->getTariffProductForProductId(permitType);
if (products) {
QVector<ATBTariffProduct> product = products.value();
if (product.size() > 0) {
ATBTariffProduct const &p = product[0];
return p.m_tariff_product_price;
#if 0
// in case we do not have prepaid-option
QTime const &currentTime = QDateTime::currentDateTime().time();
if (p.m_tariff_product_start <= currentTime && currentTime <= p.m_tariff_product_end) {
return p.m_tariff_product_price;
} else {
qCritical() << "(" << __func__ << ":" << __LINE__ << ")"
<< "ERROR currentTime"
<< currentTime.toString(Qt::ISODate)
<< "INVALID ("
<< p.m_tariff_product_start.toString(Qt::ISODate)
<< p.m_tariff_product_end.toString(Qt::ISODate) << ")";
}
#endif
}
}
} break;
case PERMIT_TYPE::INVALID:
// [[fallthrough]];
case PERMIT_TYPE::DAY_TICKET: {
std::optional<QVector<ATBTariffProduct>> products = cfg->getTariffProductForProductId(permitType);
if (products) {
QVector<ATBTariffProduct> product = products.value();
int product_price = 0;
if (productStart && productEnd) {
*productStart = start;
*productEnd = start;
if (product.size() > 0) {
productStart->setTime(product[0].getTimeStart());
productEnd->setTime(product[0].getTimeEnd());
}
}
for (QVector<ATBTariffProduct>::size_type i=0; i<product.size(); ++i) {
ATBTariffProduct const &p = product[i];
QTime const &startTime = p.getTimeStart();
QTime const &endTime = p.getTimeEnd();
// qCritical() << __LINE__ << startTime.toString(Qt::ISODate);
// qCritical() << __LINE__ << endTime.toString(Qt::ISODate);
// qCritical() << __LINE__ << start.toString(Qt::ISODate);
if (start.time() >= startTime && start.time() < endTime) {
product_price = p.getProductPrice();
if (productStart && productEnd) {
productStart->setTime(startTime);
productEnd->setTime(endTime);
}
}
}
return product_price;
} else {
// SZEGED
int const pop_daily_card_price = cfg->getPaymentOptions().pop_daily_card_price;
qDebug() << QString("(%1:%2) no products defined in tariff-file").arg(__func__).arg(__LINE__);
qDebug() << QString("(%1:%2) pop_daily_card_price=%3").arg(__func__).arg(__LINE__).arg(pop_daily_card_price);
// static const PaymentMethod paymentMethodId = Utilities::getPaymentMethodId(cfg);
// return Utilities::getDailyTicketCardPrice(cfg, paymentMethodId);
return pop_daily_card_price;
}
} break;
case PERMIT_TYPE::TWENTY_FOUR_HOURS_TICKET: {
std::optional<QVector<ATBTariffProduct>> products = cfg->getTariffProductForProductId(permitType);
if (products) {
int product_price = 0;
QVector<ATBTariffProduct> product = products.value();
if (product.size() > 0) {
if (productStart && productEnd) {
int pop_min_time = get_minimal_parkingtime(cfg); // in minutes
int pop_max_time = get_maximal_parkingtime(cfg); // in minutes
if (pop_max_time >= pop_min_time) {
*productStart = start;
*productEnd = start.addSecs(pop_max_time*60);
product_price = product[0].getProductPrice();
}
}
}
return product_price;
}
} break;
default:
break;
}
return 0;
}
int CALCULATE_LIBRARY_API get_maximal_parkingprice(Configuration *cfg,
PERMIT_TYPE permitType,
int paymentOptionIndex) {
int maxPrice = -1;
static const PaymentMethod paymentMethodId = Utilities::getPaymentMethodId(cfg);
switch(permitType) {
case PERMIT_TYPE::SHORT_TERM_PARKING: { // e.g. szeged (customer_281)
if (paymentMethodId == PaymentMethod::Progressive || paymentMethodId == PaymentMethod::Steps) {
maxPrice = Utilities::getMaximalParkingPrice(cfg, paymentMethodId);
} else { // PaymentMethod::Linear -> e.g. szeged
int const key = cfg->getPaymentOptions(paymentOptionIndex).pop_id;
int const maxTime = cfg->getPaymentOptions(paymentOptionIndex).pop_max_time; // maxTime is given in minutes
std::optional<QVector<ATBPaymentRate>> const &pv = cfg->getPaymentRateForKey(key);
if (pv) {
QVector<ATBPaymentRate> const &paymentRate = pv.value();
if (paymentRate.size() > 0) {
int const price = paymentRate.last().pra_price; // price is given per hour
maxPrice = qRound((maxTime * price) / 60.0f);
}
}
}
} break;
case PERMIT_TYPE::DAY_TICKET_ADULT:
break;
case PERMIT_TYPE::DAY_TICKET_TEEN:
break;
case PERMIT_TYPE::DAY_TICKET_CHILD:
break;
default: ;
}
return maxPrice;
}
int CALCULATE_LIBRARY_API get_zone_nr(int zone)
{
if(zone > -1) return zone;
else
{
QFile zone("/etc/zone_nr");
if (zone.exists()) {
QFileInfo finfo(zone);
if (finfo.size() <= 4) { // decimal 000\n
if (zone.open(QIODevice::ReadOnly | QIODevice::Text)) {
QTextStream in(&zone);
return in.readLine(100).toInt();
}
}
}
return -1;
}
}
CalcState CALCULATE_LIBRARY_API init_tariff(parking_tariff_t **tariff, char const *config_file) {
*tariff = new Configuration();
CalcState calcState;
#if __linux__
int const zone = get_zone_nr();
// DEBUG
qCritical() << "init_tariff:";
qCritical() << " ... zone = " << zone;
if (zone <= 0) {
delete *tariff;
*tariff = nullptr;
return calcState.set(CalcState::State::ERROR_PARSING_ZONE_NR);
}
QString confFile(config_file);
if (!confFile.endsWith(QChar('/'))) {
confFile += "/";
}
char buffer[32];
memset(buffer, 0x00, sizeof(buffer));
snprintf(buffer, sizeof(buffer)-1, "tariff%02d.json", zone);
confFile += buffer;
#else // windows
QString confFile(config_file);
#endif
// DEBUG
qCritical() << " ... confFile = " << confFile;
QFile fname(confFile);
if (fname.exists() &&
fname.open(QIODevice::ReadOnly | QIODevice::Text)) {
// DEBUG
qCritical() << " ... confFile is open";
QString json = fname.readAll();
if (! (*tariff)->ParseJson(*tariff, json.toStdString().c_str())) {
delete *tariff;
*tariff = nullptr;
return calcState.set(CalcState::State::ERROR_PARSING_TARIFF);
}
} else {
delete *tariff;
*tariff = nullptr;
return calcState.set(CalcState::State::ERROR_LOADING_TARIFF);
}
qCritical() << "init_tariff: Parsing tariff config (" << confFile << ")";
return calcState;
}
void CALCULATE_LIBRARY_API free_tariff(parking_tariff_t *tariff) {
if (tariff != nullptr) {
delete tariff;
}
}
//
// UpDown 1 -> up; 0 -> down
int CALCULATE_LIBRARY_API compute_next_timestep(parking_tariff_t *tariff, int currentTimeMinutes, int UpDown)
{
qCritical() << " compute_next_timestep() currentTimeMinutes: " << currentTimeMinutes;
qCritical() << " compute_next_timestep() up/down (1=up, 0=down): " << UpDown;
Configuration const *cfg = tariff;
// compute payment method id (e.g. Linear=3, Steps=4)
PaymentMethod const paymentMethodId = Utilities::getPaymentMethodId(cfg);
switch (paymentMethodId) {
case PaymentMethod::Progressive:
qCritical() << " compute_next_timestep() paymentMethodId: Progressive";
break;
case PaymentMethod::Degressive:
qCritical() << " compute_next_timestep() paymentMethodId: Degressive";
break;
case PaymentMethod::Linear:
qCritical() << " compute_next_timestep() paymentMethodId: Linear";
break;
case PaymentMethod::Steps:
qCritical() << " compute_next_timestep() paymentMethodId: Steps";
break;
case PaymentMethod::Undefined:
qCritical() << " compute_next_timestep() paymentMethodId: Undefined";
break;
}
// use tariff with structure as for instance Schnau, Koenigsee:
// without given YearPeriod, SpecialDays and SpecialDaysWorktime
if ((paymentMethodId == PaymentMethod::Steps) ||
// progressive tariff: e.g. Neuhauser, Kirchdorf (743)
(paymentMethodId == PaymentMethod::Progressive))
{
const QList<int> stepList = Calculator::GetInstance().GetTimeSteps(tariff);
qCritical() << " compute_next_timestep() timeSteps:" << stepList;
int currentStepIndex = stepList.indexOf(currentTimeMinutes);
if (currentStepIndex == -1) {
qCritical() << "compute_next_timestep() *NO STEP* for currentTimeMinutes (" << currentTimeMinutes << ")";
return currentTimeMinutes;
}
if (UpDown == 1) { // UP
if (stepList[currentStepIndex] == stepList.last()) {
qCritical() << "compute_next_timestep() *NO NEXT STEP* for currentTimeMinutes (" << currentTimeMinutes << ")";
return currentTimeMinutes;
}
else {
return stepList[currentStepIndex + 1];
}
}
if (UpDown == 0) { // DOWN
if (stepList[currentStepIndex] == stepList.first()) {
qCritical() << "compute_next_timestep() *NO PREVIOUS STEP* for currentTimeMinutes (" << currentTimeMinutes << ")";
return currentTimeMinutes;
}
else {
return stepList[currentStepIndex - 1];
}
}
} else
if (paymentMethodId == PaymentMethod::Linear) {
// currentTimeMinutes is the number of minutes actually used. This
// value is an offset from the start time and cannot be used as a
// QDateTime.
qCritical() << "compute_next_timestep() up/down (1=up, 0=down):" << UpDown;
// get minimal and maximal parking times
int const minParkingTime = Utilities::getMinimalParkingTime(cfg, paymentMethodId);
int const maxParkingTime = Utilities::getMaximalParkingTime(cfg, paymentMethodId);
qCritical() << " compute_next_timestep() maxParkingTime:" << maxParkingTime;
qCritical() << " compute_next_timestep() minParkingTime:" << minParkingTime;
// use the first (i.e. main duration step contained in the tariff json-file)
int firstDurationStep = Utilities::getFirstDurationStep(cfg, paymentMethodId);
firstDurationStep = ((UpDown == 1) ? firstDurationStep : -firstDurationStep);
qCritical() << " compute_next_timestep() firstDurationStep:" << firstDurationStep;
int const nextTimeStep = currentTimeMinutes + firstDurationStep;
if (nextTimeStep >= minParkingTime && nextTimeStep <= maxParkingTime) {
qCritical() << " compute_next_timestep() nextTimeStep:" << nextTimeStep;
return nextTimeStep;
}
}
qCritical() << "compute_next_timestep() *CAN NOT COMPUTE* for currentTimeMinutes (" << currentTimeMinutes << ")";
return currentTimeMinutes;
}
// this is currently not used
CalcState CALCULATE_LIBRARY_API compute_price_for_parking_ticket(
parking_tariff_t *tariff,
time_t start_parking_time, // in minutes
time_t end_parking_time, // netto time in minutes
struct price_t *price) {
CalcState calcState;
double minMin = tariff->PaymentOption.find(tariff->getPaymentOptions().pop_payment_method_id)->second.pop_min_time;
double maxMin = tariff->PaymentOption.find(tariff->getPaymentOptions().pop_payment_method_id)->second.pop_max_time;
if (minMin < 0 || maxMin < 0 || maxMin < minMin) {
calcState.setDesc(QString("minMin=%1, maxMin=%2").arg(minMin).arg(maxMin));
return calcState.set(CalcState::State::WRONG_PARAM_VALUES);
}
int const duration = end_parking_time - start_parking_time;
if (duration < 0) {
calcState.setDesc(QString("end=%1, start=%2")
.arg(end_parking_time, start_parking_time));
return calcState.set(CalcState::State::NEGATIVE_PARKING_TIME);
}
if (duration > maxMin) {
calcState.setDesc(QString("duration=%1, maxMin=%2").arg(duration).arg(maxMin));
return calcState.set(CalcState::State::ABOVE_MAX_PARKING_TIME);
}
if (duration < minMin) {
calcState.setDesc(QString("duration=%1, minMin=%2").arg(duration).arg(minMin));
return calcState.set(CalcState::State::BELOW_MIN_PARKING_TIME);
}
if (duration == 0) {
return calcState.set(CalcState::State::SUCCESS);
}
QDate const d(1970, 1, 1);
QTime const t(0, 0, 0);
QDateTime start(d, t, Qt::UTC);
start = start.toLocalTime().addSecs(start_parking_time * 60);
QDateTime end(start);
if (start.isValid()) {
double cost = Calculator::GetInstance().GetCostFromDuration(
tariff,
tariff->getPaymentOptions().pop_payment_method_id,
start,
end,
duration, false, true);
double minCost = tariff->PaymentOption.find(tariff->getPaymentOptions().pop_payment_method_id)->second.pop_min_price;
if (cost < minCost) {
calcState.setDesc(QString("minCost=%1, cost=%2").arg(minCost).arg(cost));
return calcState.set(CalcState::State::BELOW_MIN_PARKING_PRICE);
}
price->units = cost;
price->netto = cost;
} else {
return calcState.set(CalcState::State::INVALID_START_DATE);
}
return calcState.set(CalcState::State::SUCCESS);
}
CalcState CALCULATE_LIBRARY_API compute_price_for_parking_ticket(
parking_tariff_t *tariff,
QDateTime &start_parking_time,
int netto_parking_time,
QDateTime &end_parking_time,
struct price_t *price,
bool prepaid)
{
CalcState calcState;
int paymentOptionIndex = tariff->getPaymentOptionIndex(start_parking_time);
double minMin = tariff->getPaymentOptions(paymentOptionIndex).pop_min_time;
double maxMin = tariff->getPaymentOptions(paymentOptionIndex).pop_max_time;
// DEBUG
qCritical() << "compute_price_for_parking_ticket() " << endl
<< " paymentOptionIndex: " << paymentOptionIndex << endl
<< " start_parking_time: " << start_parking_time << endl
<< " netto_parking_time: " << netto_parking_time << endl
<< " minMin: " << minMin << endl
<< " maxMin: " << maxMin;
if (netto_parking_time < 0) {
calcState.setDesc(QString("end=%1, start=%2")
.arg(end_parking_time.toString(Qt::ISODate),
start_parking_time.toString(Qt::ISODate)));
return calcState.set(CalcState::State::NEGATIVE_PARKING_TIME);
}
if (netto_parking_time > maxMin) {
calcState.setDesc(QString("duration=%1, maxMin=%2").arg(netto_parking_time).arg(maxMin));
return calcState.set(CalcState::State::ABOVE_MAX_PARKING_TIME);
}
if (netto_parking_time < minMin) {
calcState.setDesc(QString("duration=%1, minMin=%2").arg(netto_parking_time).arg(minMin));
return calcState.set(CalcState::State::BELOW_MIN_PARKING_TIME);
}
if (netto_parking_time == 0) {
return calcState.set(CalcState::State::SUCCESS);
}
double cost = -1;
if (start_parking_time.isValid()) {
if (tariff->getPaymentOptions(paymentOptionIndex).pop_payment_method_id == PaymentMethod::Steps) {
// hier muesste man unterscheiden: uebertrag oder nicht?
calcState = Calculator::GetInstance().isParkingAllowed(tariff, start_parking_time,
netto_parking_time, paymentOptionIndex);
if (calcState.getStatus() == CalcState::State::OUTSIDE_ALLOWED_PARKING_TIME) {
// qCritical() << "(" << __func__ << ":" << __LINE__ << ")"
// << calcState.toString();
return calcState;
}
cost = Calculator::GetInstance().GetCostFromDuration(tariff, start_parking_time, netto_parking_time, paymentOptionIndex);
end_parking_time = start_parking_time.addSecs(netto_parking_time*60);
// qCritical() << "(" << __func__ << ":" << __LINE__ << ")"
// << "end_parking_time" << end_parking_time.toString(Qt::ISODate);
} else {
cost = Calculator::GetInstance().GetCostFromDuration(
tariff,
tariff->getPaymentOptions().pop_payment_method_id,
start_parking_time, // starting time
end_parking_time, // return value: end time
netto_parking_time, // minutes, netto
false, prepaid);
}
double minCost = tariff->getPaymentOptions(paymentOptionIndex).pop_min_price;
if (cost < minCost) {
calcState.setDesc(QString("minCost=%1, cost=%2").arg(minCost, cost));
return calcState.set(CalcState::State::BELOW_MIN_PARKING_PRICE);
}
// DEBUG
qCritical() << " end_parking_time: " << end_parking_time;
qCritical() << " -> calculated cost (netto): " << cost;
price->brutto = price->vat = price->vat_percentage = 0;
price->units = cost;
price->netto = cost;
} else {
return calcState.set(CalcState::State::INVALID_START_DATE);
}
return calcState.set(CalcState::State::SUCCESS);
}
CalcState CALCULATE_LIBRARY_API compute_duration_for_parking_ticket(
parking_tariff_t *tariff,
time_t start_parking_time,
double price,
QString &duration) {
CalcState calcState;
QDate const d(1970, 1, 1);
QTime const t(0, 0, 0);
QDateTime start(d, t, Qt::UTC);
start = start.toLocalTime().addSecs(start_parking_time * 60);
if (start.isValid()) {
QString cs = start.toString(Qt::ISODate);
// DEBUG
qCritical() << "compute_duration_for_parking_ticket(): ";
qCritical() << " start (cs): " << cs;
qCritical() << " price: " << price;
duration = Calculator::GetInstance().GetDurationFromCost(tariff,
tariff->getPaymentOptions().pop_payment_method_id,
cs.toLocal8Bit().constData(),
price, false, true).c_str();
QDateTime d = QDateTime::fromString(duration, Qt::ISODate);
if (!d.isValid()) {
calcState.setDesc(QString("ticketEndTime=%1").arg(duration));
return calcState.set(CalcState::State::WRONG_ISO_TIME_FORMAT);
}
} else {
return calcState.set(CalcState::State::INVALID_START_DATE);
}
return calcState.set(CalcState::State::SUCCESS);
}
CalcState CALCULATE_LIBRARY_API compute_duration_for_parking_ticket(
parking_tariff_t *tariff,
QDateTime const &start_parking_time,
double price,
QDateTime &ticketEndTime)
{
CalcState calcState;
if (start_parking_time.isValid()) {
QString cs = start_parking_time.toString(Qt::ISODate);
QString endTime = Calculator::GetInstance().GetDurationFromCost(
tariff,
tariff->getPaymentOptions().pop_payment_method_id,
cs.toLocal8Bit().constData(),
price, false, true).c_str();
if (endTime == CalcState::SUCCESS) {
calcState.setDesc(QString("SUCCESS"));
calcState.setStatus(endTime);
} else
if (endTime == CalcState::ERROR_PARSING_ZONE_NR) {
calcState.setStatus(endTime);
return calcState;
} else
if (endTime == CalcState::ERROR_LOADING_TARIFF) {
calcState.setStatus(endTime);
return calcState;
} else
if (endTime == CalcState::ERROR_PARSING_TARIFF) {
calcState.setStatus(endTime);
return calcState;
} else
if (endTime == CalcState::NEGATIVE_PARKING_TIME) {
calcState.setStatus(endTime);
return calcState;
} else
if (endTime == CalcState::INVALID_START_DATE) {
calcState.setStatus(endTime);
return calcState;
} else
if (endTime == CalcState::WRONG_PARAM_VALUES) {
calcState.setStatus(endTime);
return calcState;
} else
if (endTime == CalcState::WRONG_ISO_TIME_FORMAT) {
calcState.setStatus(endTime);
return calcState;
} else
if (endTime == CalcState::ABOVE_MAX_PARKING_TIME) {
calcState.setStatus(endTime);
return calcState;
} else
if (endTime == CalcState::BELOW_MIN_PARKING_TIME) {
calcState.setStatus(endTime);
return calcState;
} else
if (endTime == CalcState::BELOW_MIN_PARKING_PRICE) {
calcState.setStatus(endTime);
return calcState;
} else
if (endTime == CalcState::ABOVE_MAX_PARKING_PRICE) {
calcState.setDesc(CalcState::ABOVE_MAX_PARKING_PRICE);
calcState.setStatus(CalcState::ABOVE_MAX_PARKING_PRICE);
return calcState;
} else
if (endTime == CalcState::OVERPAID) {
calcState.setDesc(CalcState::OVERPAID);
calcState.setStatus(CalcState::OVERPAID);
return calcState;
} else
if (endTime == CalcState::OUTSIDE_ALLOWED_PARKING_TIME) {
calcState.setStatus(endTime);
return calcState;
} else {
ticketEndTime = QDateTime::fromString(endTime,Qt::ISODate);
// DEBUG
//qCritical() << "compute_duration_for_parking_ticket(): ";
//qCritical() << " endTime: " << endTime;
//qCritical() << " ticketEndTime: " << ticketEndTime;
if (!ticketEndTime.isValid()) {
calcState.setDesc(QString("ticketEndTime=%1").arg(endTime));
return calcState.set(CalcState::State::WRONG_ISO_TIME_FORMAT);
}
}
} else {
return calcState.set(CalcState::State::INVALID_START_DATE);
}
return calcState.set(CalcState::State::SUCCESS);
}
CalcState CALCULATE_LIBRARY_API compute_duration_for_daily_ticket(parking_tariff_t *tariff, QDateTime const &start_parking_time, QDateTime &ticketEndTime)
{
CalcState calcState;
if (start_parking_time.isValid()) {
ticketEndTime = Calculator::GetInstance().GetDailyTicketDuration(tariff,
start_parking_time,
tariff->getPaymentOptions().pop_payment_method_id,
false); // carry over
// DEBUG
qCritical() << "compute_duration_for_daily_ticket(): ";
qCritical() << " ticketEndTime: " << ticketEndTime;
if (!ticketEndTime.isValid()) {
calcState.setDesc(QString("ticketEndTime=%1").arg(ticketEndTime.toString(Qt::ISODate)));
return calcState.set(CalcState::State::WRONG_ISO_TIME_FORMAT);
}
} else {
return calcState.set(CalcState::State::INVALID_START_DATE);
}
return calcState.set(CalcState::State::SUCCESS);
}
CalcState CALCULATE_LIBRARY_API compute_price_for_daily_ticket(
parking_tariff_t *tariff,
QDateTime const &startDatetime,
QDateTime &endDatetime,
PERMIT_TYPE permitType,
struct price_t *price) {// return value
CalcState calcState;
if (startDatetime.isValid()) {
if (std::optional<struct price_t> p =
Calculator::GetInstance().GetDailyTicketPrice(tariff,
startDatetime,
endDatetime,
permitType)) {
*price = p.value();
}
} else {
return calcState.set(CalcState::State::INVALID_START_DATE);
}
return calcState.set(CalcState::State::SUCCESS);
}

View File

@@ -131,17 +131,52 @@ std::optional<ATBTariffPrepaid> getPrepaid(Configuration const *cfg, QDateTime c
std::optional<ATBTariffPrepaid> value = std::nullopt;
int weekDay = dt.date().dayOfWeek();
// qCritical() << __func__ << ":" << __LINE__ << dt.toString(Qt::ISODate) << weekDay;
ATBTime inputTime(dt.time());
QDate d; // check if a special date is configured in tariff-file for this day
auto const &prepaidRange = cfg->TariffPrepaids.equal_range(weekDay);
for (auto i = prepaidRange.first; i != prepaidRange.second; ++i) {
ATBTariffPrepaid const &prepaid = i->second;
TimeRange const &prepaidTimeRange = prepaid.m_range;
if (inputTime >= prepaidTimeRange.m_start && inputTime < prepaidTimeRange.m_end) {
value = value.value_or(i->second);
if (!prepaid.m_date.isNull() && prepaid.m_date.isValid() && prepaid.m_date == dt.date()) {
d = dt.date();
// qCritical() << __func__ << ":" << __LINE__ << "found special day" << d.toString(Qt::ISODate);
break;
}
}
if (!d.isNull() && d.isValid()) {
for (auto i = prepaidRange.first; i != prepaidRange.second; ++i) {
ATBTariffPrepaid const &prepaid = i->second;
if (!prepaid.m_date.isNull() && prepaid.m_date.isValid() && prepaid.m_date == d) {
TimeRange const &prepaidTimeRange = prepaid.m_range;
if (inputTime >= prepaidTimeRange.m_start && inputTime < prepaidTimeRange.m_end) {
// qCritical() << __func__ << ":" << __LINE__ << prepaidTimeRange.m_start.toString(Qt::ISODate);
// qCritical() << __func__ << ":" << __LINE__ << prepaidTimeRange.m_end.toString(Qt::ISODate);
value = value.value_or(i->second);
break;
}
}
}
} else {
// qCritical() << __func__ << ":" << __LINE__ << "no special day" << dt.date().toString(Qt::ISODate);
for (auto i = prepaidRange.first; i != prepaidRange.second; ++i) {
ATBTariffPrepaid const &prepaid = i->second;
if (prepaid.m_date.isNull() || !prepaid.m_date.isValid()) {
qCritical() << __func__ << ":" << __LINE__ << "default";
TimeRange const &prepaidTimeRange = prepaid.m_range;
if (inputTime >= prepaidTimeRange.m_start && inputTime < prepaidTimeRange.m_end) {
// qCritical() << __func__ << ":" << __LINE__ << prepaidTimeRange.m_start.toString(Qt::ISODate);
// qCritical() << __func__ << ":" << __LINE__ << prepaidTimeRange.m_end.toString(Qt::ISODate);
value = value.value_or(i->second);
break;
}
}
}
}
return value;
}
@@ -161,17 +196,48 @@ std::optional<ATBTariffCarryOver> getCarryOver(Configuration const *cfg, QDateTi
ATBTime inputTime(dt.time());
auto const &carryOverRange = cfg->TariffCarryOvers.equal_range(weekDay);
QDate d; // check if a special date is configured in tariff-file for this day
for (auto i = carryOverRange.first; i != carryOverRange.second; ++i) {
ATBTariffCarryOver const &carryOver = i->second;
TimeRange const &carryOverTimeRange = carryOver.m_range;
if (inputTime >= carryOverTimeRange.m_start && inputTime < carryOverTimeRange.m_end) {
// qCritical() << __func__ << ":" << __LINE__ << carryOverTimeRange.m_start.toString(Qt::ISODate);
// qCritical() << __func__ << ":" << __LINE__ << carryOverTimeRange.m_end.toString(Qt::ISODate);
value = value.value_or(carryOver);
if (!carryOver.m_date.isNull() && carryOver.m_date.isValid() && carryOver.m_date == dt.date()) {
d = dt.date();
// qCritical() << __func__ << ":" << __LINE__ << "found special day" << d.toString(Qt::ISODate);
break;
}
}
if (!d.isNull() && d.isValid()) {
for (auto i = carryOverRange.first; i != carryOverRange.second; ++i) {
ATBTariffCarryOver const &carryOver = i->second;
if (!carryOver.m_date.isNull() && carryOver.m_date.isValid() && carryOver.m_date == d) {
TimeRange const &carryOverTimeRange = carryOver.m_range;
if (inputTime >= carryOverTimeRange.m_start && inputTime < carryOverTimeRange.m_end) {
// qCritical() << __func__ << ":" << __LINE__ << carryOverTimeRange.m_start.toString(Qt::ISODate);
// qCritical() << __func__ << ":" << __LINE__ << carryOverTimeRange.m_end.toString(Qt::ISODate);
value = value.value_or(carryOver);
break;
}
}
}
} else {
// qCritical() << __func__ << ":" << __LINE__ << "no special day" << dt.date().toString(Qt::ISODate);
for (auto i = carryOverRange.first; i != carryOverRange.second; ++i) {
ATBTariffCarryOver const &carryOver = i->second;
if (carryOver.m_date.isNull() || !carryOver.m_date.isValid()) {
// qCritical() << __func__ << ":" << __LINE__ << "default";
TimeRange const &carryOverTimeRange = carryOver.m_range;
if (inputTime >= carryOverTimeRange.m_start && inputTime < carryOverTimeRange.m_end) {
// qCritical() << __func__ << ":" << __LINE__ << carryOverTimeRange.m_start.toString(Qt::ISODate);
// qCritical() << __func__ << ":" << __LINE__ << carryOverTimeRange.m_end.toString(Qt::ISODate);
value = value.value_or(carryOver);
break;
}
}
}
}
return value;
}
@@ -186,17 +252,53 @@ std::optional<ATBTariffService> getService(Configuration const *cfg, QDateTime c
std::optional<ATBTariffService> value = std::nullopt;
int weekDay = dt.date().dayOfWeek();
// qCritical() << __func__ << ":" << __LINE__ << dt.toString(Qt::ISODate) << weekDay;
ATBTime inputTime(dt.time());
auto const &serviceRange = cfg->TariffServices.equal_range(weekDay);
QDate d; // check if a special date is configured in tariff-file for this day
for (auto i = serviceRange.first; i != serviceRange.second; ++i) {
ATBTariffService const &service = i->second;
TimeRange const &serviceTimeRange = service.m_range;
if (inputTime >= serviceTimeRange.m_start && inputTime < serviceTimeRange.m_end) {
value = value.value_or(i->second);
if (!service.m_date.isNull() && service.m_date.isValid() && service.m_date == dt.date()) {
d = dt.date();
// qCritical() << __func__ << ":" << __LINE__ << "found special day" << d.toString(Qt::ISODate);
break;
}
}
if (!d.isNull() && d.isValid()) {
for (auto i = serviceRange.first; i != serviceRange.second; ++i) {
ATBTariffService const &service = i->second;
if (!service.m_date.isNull() && service.m_date.isValid() && service.m_date == d) {
TimeRange const &serviceTimeRange = service.m_range;
if (inputTime >= serviceTimeRange.m_start && inputTime < serviceTimeRange.m_end) {
// qCritical() << __func__ << ":" << __LINE__ << serviceTimeRange.m_start.toString(Qt::ISODate);
// qCritical() << __func__ << ":" << __LINE__ << serviceTimeRange.m_end.toString(Qt::ISODate);
value = value.value_or(service);
break;
}
}
}
} else {
// qCritical() << __func__ << ":" << __LINE__ << "no special day" << dt.date().toString(Qt::ISODate);
for (auto i = serviceRange.first; i != serviceRange.second; ++i) {
ATBTariffService const &service = i->second;
if (service.m_date.isNull() || !service.m_date.isValid()) {
// qCritical() << __func__ << ":" << __LINE__ << "default";
TimeRange const &serviceTimeRange = service.m_range;
if (inputTime >= serviceTimeRange.m_start && inputTime < serviceTimeRange.m_end) {
// qCritical() << __func__ << ":" << __LINE__ << serviceTimeRange.m_start.toString(Qt::ISODate);
// qCritical() << __func__ << ":" << __LINE__ << serviceTimeRange.m_end.toString(Qt::ISODate);
value = value.value_or(service);
break;
}
}
}
}
return value;
}
@@ -210,29 +312,50 @@ std::optional<ATBTariffOutOfService> getOutOfService(Configuration const *cfg, Q
std::optional<ATBTariffOutOfService> value = std::nullopt;
int weekDay = dt.date().dayOfWeek();
// qCritical() << __func__ << ":" << __LINE__ << dt.toString(Qt::ISODate) << weekDay;
ATBTime inputTime(dt.time());
QDate date;
auto const &outOfServiceRange = cfg->TariffOutOfServices.equal_range(weekDay);
QDate d; // check if a special date is configured in tariff-file for this day
for (auto i = outOfServiceRange.first; i != outOfServiceRange.second; ++i) {
ATBTariffOutOfService const &outOfService = i->second;
TimeRange const &outOfServiceTimeRange = outOfService.m_range;
if (outOfService.m_date == dt.date()) {
date = dt.date();
if (inputTime >= outOfServiceTimeRange.m_start && inputTime < outOfServiceTimeRange.m_end) {
value = value.value_or(i->second);
return value;
}
if (!outOfService.m_date.isNull() && outOfService.m_date.isValid() && outOfService.m_date == dt.date()) {
d = dt.date();
// qCritical() << __func__ << ":" << __LINE__ << "found special day" << d.toString(Qt::ISODate);
break;
}
}
if (date.isNull() || !date.isValid()) {
if (!d.isNull() && d.isValid()) {
for (auto i = outOfServiceRange.first; i != outOfServiceRange.second; ++i) {
ATBTariffOutOfService const &outOfService = i->second;
TimeRange const &outOfServiceTimeRange = outOfService.m_range;
if (inputTime >= outOfServiceTimeRange.m_start && inputTime < outOfServiceTimeRange.m_end) {
value = value.value_or(i->second);
return value;
if (!outOfService.m_date.isNull() && outOfService.m_date.isValid() && outOfService.m_date == d) {
TimeRange const &outOfServiceTimeRange = outOfService.m_range;
if (inputTime >= outOfServiceTimeRange.m_start && inputTime < outOfServiceTimeRange.m_end) {
// qCritical() << __func__ << ":" << __LINE__ << outOfServiceTimeRange.m_start.toString(Qt::ISODate);
// qCritical() << __func__ << ":" << __LINE__ << outOfServiceTimeRange.m_end.toString(Qt::ISODate);
value = value.value_or(outOfService);
break;
}
}
}
} else {
// qCritical() << __func__ << ":" << __LINE__ << "no special day" << dt.date().toString(Qt::ISODate);
for (auto i = outOfServiceRange.first; i != outOfServiceRange.second; ++i) {
ATBTariffOutOfService const &outOfService = i->second;
if (outOfService.m_date.isNull() || !outOfService.m_date.isValid()) {
// qCritical() << __func__ << ":" << __LINE__ << "default";
TimeRange const &outOfServiceTimeRange = outOfService.m_range;
if (inputTime >= outOfServiceTimeRange.m_start && inputTime < outOfServiceTimeRange.m_end) {
// qCritical() << __func__ << ":" << __LINE__ << outOfServiceTimeRange.m_start.toString(Qt::ISODate);
// qCritical() << __func__ << ":" << __LINE__ << outOfServiceTimeRange.m_end.toString(Qt::ISODate);
value = value.value_or(outOfService);
break;
}
}
}
}
@@ -431,22 +554,22 @@ Calculator::ComputeDurationFromCost(Configuration *cfg,
.arg(netto_parking_time_in_minutes)
.arg(free_parking_time_in_minutes);
if (std::optional<ATBTariffCarryOver> co = getCarryOver(cfg, inputDate.addSecs(brutto_parking_time_in_minutes * 60))) {
if (std::optional<ATBTariffCarryOver> co = getCarryOver(cfg, dt)) {
TimeRange const &carryOverTimeRange = co.value().m_range;
free_parking_time_in_minutes += carryOverTimeRange.m_duration;
brutto_parking_time_in_minutes = free_parking_time_in_minutes + netto_parking_time_in_minutes;
dt = inputDate.addSecs(brutto_parking_time_in_minutes * 60);
weekDay = dt.date().dayOfWeek();
qCritical() << __func__ << ":" << __LINE__ << QString("%1 (%2): brutto: %3 = netto: %4 + free: %5")
.arg(dt.toString(Qt::ISODate))
.arg(weekDay)
.arg(brutto_parking_time_in_minutes)
.arg(netto_parking_time_in_minutes)
.arg(free_parking_time_in_minutes);
}
brutto_parking_time_in_minutes = free_parking_time_in_minutes + netto_parking_time_in_minutes;
dt = inputDate.addSecs(brutto_parking_time_in_minutes * 60);
weekDay = dt.date().dayOfWeek();
qCritical() << __func__ << ":" << __LINE__ << QString("%1 (%2): brutto: %3 = netto: %4 + free: %5")
.arg(dt.toString(Qt::ISODate))
.arg(weekDay)
.arg(brutto_parking_time_in_minutes)
.arg(netto_parking_time_in_minutes)
.arg(free_parking_time_in_minutes);
if (std::optional<ATBTariffService> serv = getService(cfg, dt)) {
TimeRange const &serviceTimeRange = serv.value().m_range;

1529
out.txt Normal file

File diff suppressed because it is too large Load Diff