Compare commits

...

10 Commits

Author SHA1 Message Date
d4363e71cd Keep bacjward compatibility before using payment-method Unified. 2024-09-27 14:24:10 +02:00
fd99c20bd9 Take into account new payment method 'Unified' to call new tariff-calculator. 2024-09-27 14:20:20 +02:00
dbccdba9fe isParkingAllowedForWeekDay():
Minor: adapt function call to changes parameter arity.
2024-09-27 14:18:31 +02:00
b035f4f887 Calculator::GetDurationFromCost():
Minor: move scope of some variables upward.
2024-09-27 14:17:35 +02:00
18f09fccb9 GetDurationFromCost():
Call ComputeDurationFromCost() of new tariff calculator for payment-method Unified.
2024-09-27 14:10:02 +02:00
1086e360e5 Start of implementing new tariff calculator:
Added serveral helper function for parsing tariff file:
	  * getPrepaid()
	  * getCarryOver()
          * getService()
          * getOutOfService()

	Added main functions of tariff calculator:

	  * ComputeDurationFromCost()
	  * ComputeCostFromDuration()
2024-09-27 14:04:39 +02:00
ada7bebd90 Minor: Add headers. 2024-09-27 13:57:30 +02:00
2b9ea67ef5 Added function to implement new tariff-calculator:
std::pair<CalcState, QDateTime> ComputeDurationFromCost(Configuration const* cfg, QDateTime const &startDatetimePassed,  int cost);
std::pair<CalcState, std::optional<int>> ComputeCostFromDuration(Configuration const* cfg, QDateTime const &startDatetime, QDateTime &endDatetime, int nettoParkingTime);
2024-09-27 13:48:30 +02:00
d117328bed Minor: removed default parameter in constructor 2024-09-27 13:47:26 +02:00
fd04531474 Added payment <unified> payment-methode 2024-09-27 13:45:45 +02:00
5 changed files with 585 additions and 115 deletions

View File

@ -90,9 +90,9 @@ struct CALCULATE_LIBRARY_API CalcState {
, m_desc(desc) { , m_desc(desc) {
} }
explicit CalcState(State state, QString desc = "", explicit CalcState(State state, QString desc,
QTime const &from = QTime(), QTime const &from,
QTime const &until = QTime()) QTime const &until)
: m_status(state) : m_status(state)
, m_desc(desc) , m_desc(desc)
, m_allowedTimeRange(from, until) { , m_allowedTimeRange(from, until) {

View File

@ -87,6 +87,9 @@ public:
double GetCostFromDuration(Configuration* cfg, uint8_t vehicle_type, QDateTime &start_datetime, QDateTime & end_datetime, int durationMin, double GetCostFromDuration(Configuration* cfg, uint8_t vehicle_type, QDateTime &start_datetime, QDateTime & end_datetime, int durationMin,
PermitType permitType, bool nextDay = false, bool prepaid = false); PermitType permitType, bool nextDay = false, bool prepaid = false);
std::pair<CalcState, QDateTime> ComputeDurationFromCost(Configuration const* cfg, QDateTime const &startDatetimePassed, int cost);
std::pair<CalcState, std::optional<int>> ComputeCostFromDuration(Configuration const* cfg, QDateTime const &startDatetime, QDateTime &endDatetime, int nettoParkingTime);
// Daily ticket // Daily ticket
QDateTime GetDailyTicketDuration(Configuration* cfg, const QDateTime start_datetime, uint8_t payment_option, bool carry_over); QDateTime GetDailyTicketDuration(Configuration* cfg, const QDateTime start_datetime, uint8_t payment_option, bool carry_over);
std::optional<struct price_t> GetDailyTicketPrice(Configuration* cfg, QDateTime const &startDatetime, QDateTime &endTime, PERMIT_TYPE permitType); std::optional<struct price_t> GetDailyTicketPrice(Configuration* cfg, QDateTime const &startDatetime, QDateTime &endTime, PERMIT_TYPE permitType);

View File

@ -6,7 +6,8 @@ enum PaymentMethod {
Progressive = 0x01, Progressive = 0x01,
Degressive = 0x02, Degressive = 0x02,
Linear = 0x03, Linear = 0x03,
Steps = 0x04 Steps = 0x04,
Unified = 0x05
}; };
#endif // PAYMENT_METHOD_H_INCLUDED #endif // PAYMENT_METHOD_H_INCLUDED

View File

@ -338,7 +338,9 @@ int CALCULATE_LIBRARY_API get_maximal_parkingprice(Configuration *cfg,
switch(permitType) { switch(permitType) {
case PERMIT_TYPE::SHORT_TERM_PARKING: { // e.g. szeged (customer_281) case PERMIT_TYPE::SHORT_TERM_PARKING: { // e.g. szeged (customer_281)
if (paymentMethodId == PaymentMethod::Progressive || paymentMethodId == PaymentMethod::Steps) { if (paymentMethodId == PaymentMethod::Progressive
|| paymentMethodId == PaymentMethod::Steps
|| paymentMethodId == PaymentMethod::Unified) {
//maxPrice = Utilities::getMaximalParkingPrice(cfg, paymentMethodId); //maxPrice = Utilities::getMaximalParkingPrice(cfg, paymentMethodId);
ATBPaymentOption const &po = cfg->getPaymentOptions(paymentOptionIndex); ATBPaymentOption const &po = cfg->getPaymentOptions(paymentOptionIndex);
maxPrice = po.pop_max_price; // maxTime is given in minutes maxPrice = po.pop_max_price; // maxTime is given in minutes
@ -518,6 +520,9 @@ int CALCULATE_LIBRARY_API compute_next_timestep(parking_tariff_t *tariff, int cu
case PaymentMethod::Steps: case PaymentMethod::Steps:
qCritical() << __LINE__ << "compute_next_timestep() paymentMethodId: Steps"; qCritical() << __LINE__ << "compute_next_timestep() paymentMethodId: Steps";
break; break;
case PaymentMethod::Unified:
qCritical() << __LINE__ << "compute_next_timestep() paymentMethodId: Unified";
break;
case PaymentMethod::Undefined: case PaymentMethod::Undefined:
qCritical() << __LINE__ << "compute_next_timestep() paymentMethodId: Undefined"; qCritical() << __LINE__ << "compute_next_timestep() paymentMethodId: Undefined";
break; break;
@ -528,6 +533,8 @@ int CALCULATE_LIBRARY_API compute_next_timestep(parking_tariff_t *tariff, int cu
if ((paymentMethodId == PaymentMethod::Steps) || if ((paymentMethodId == PaymentMethod::Steps) ||
// progressive tariff: e.g. Neuhauser, Kirchdorf (743) // progressive tariff: e.g. Neuhauser, Kirchdorf (743)
(paymentMethodId == PaymentMethod::Progressive) || (paymentMethodId == PaymentMethod::Progressive) ||
// unified tariff: starting with Bad Neuenahr (249), Tariff for Zone5
(paymentMethodId == PaymentMethod::Unified) ||
// degressive tariff: e.g. Fuchs Technik (500) // degressive tariff: e.g. Fuchs Technik (500)
(paymentMethodId == PaymentMethod::Degressive)) (paymentMethodId == PaymentMethod::Degressive))
{ {
@ -960,6 +967,19 @@ CalcState CALCULATE_LIBRARY_API compute_price_for_parking_ticket(
return calcState.set(CalcState::State::ABOVE_MAX_PARKING_TIME); return calcState.set(CalcState::State::ABOVE_MAX_PARKING_TIME);
} }
} }
} else
if (tariff->getPaymentOptions(0).pop_payment_method_id == PaymentMethod::Unified) {
std::pair<CalcState, std::optional<int>> p =
Calculator::GetInstance().ComputeCostFromDuration(tariff, start_parking_time, end_parking_time, netto_parking_time);
CalcState const cs = p.first;
if ((cs.getStatus() == CalcState::State::SUCCESS || cs.getStatus() == CalcState::State::SUCCESS_MAXPRICE)) {
if (p.second.has_value()) {
cost = p.second.value();
}
} else {
return cs;
}
} else { } else {
cost = Calculator::GetInstance().GetCostFromDuration( cost = Calculator::GetInstance().GetCostFromDuration(
tariff, tariff,
@ -969,7 +989,7 @@ CalcState CALCULATE_LIBRARY_API compute_price_for_parking_ticket(
netto_parking_time, // minutes, netto netto_parking_time, // minutes, netto
false, prepaid); false, prepaid);
} }
// qCritical() << __func__ << __LINE__;
double minCost = tariff->getPaymentOptions(paymentOptionIndex).pop_min_price; double minCost = tariff->getPaymentOptions(paymentOptionIndex).pop_min_price;
if (cost < minCost) { if (cost < minCost) {
calcState.setDesc(QString("line=%1 minCost=%2, cost=%3").arg(__LINE__).arg(minCost).arg(cost)); calcState.setDesc(QString("line=%1 minCost=%2, cost=%3").arg(__LINE__).arg(minCost).arg(cost));
@ -1125,6 +1145,9 @@ CalcState CALCULATE_LIBRARY_API compute_duration_for_parking_ticket(
QString endTime = p_endTime.first.c_str(); QString endTime = p_endTime.first.c_str();
ticketEndTime = p_endTime.second; ticketEndTime = p_endTime.second;
qCritical() << __func__ << ":" << __LINE__ << endTime;
qCritical() << __func__ << ":" << __LINE__ << ticketEndTime.toString(Qt::ISODate);
if (endTime == CalcState::SUCCESS) { if (endTime == CalcState::SUCCESS) {
calcState.setDesc(QString("SUCCESS")); calcState.setDesc(QString("SUCCESS"));
calcState.setStatus(endTime); calcState.setStatus(endTime);
@ -1205,154 +1228,157 @@ CalcState CALCULATE_LIBRARY_API compute_duration_for_parking_ticket(
return calcState.set(CalcState::State::WRONG_ISO_TIME_FORMAT); return calcState.set(CalcState::State::WRONG_ISO_TIME_FORMAT);
} }
if (pop_time_step_config == (int)ATBTimeStepConfig::TimeStepConfig::STATIC) { if (tariff->getPaymentOptions().pop_payment_method_id != PaymentMethod::Unified) {
// handle carry over for ticket-end-time
qCritical() << __func__ << ":" << __LINE__ << "ticketEndTime:" << ticketEndTime.toString(Qt::ISODate);
int weekDay = start_parking_time.date().dayOfWeek(); if (pop_time_step_config == (int)ATBTimeStepConfig::TimeStepConfig::STATIC) {
int pop_carry_over_option_id = tariff->getPaymentOptions(paymentOptionIndex).pop_carry_over_option_id; // handle carry over for ticket-end-time
qCritical() << __func__ << ":" << __LINE__ << "configured carry-over-id" << pop_carry_over_option_id; qCritical() << __func__ << ":" << __LINE__ << "ticketEndTime:" << ticketEndTime.toString(Qt::ISODate);
std::optional<ATBPeriodYear> yperiod = Utilities::GetYearPeriodActive(tariff, start_parking_time); int weekDay = start_parking_time.date().dayOfWeek();
if (yperiod.has_value()) { int pop_carry_over_option_id = tariff->getPaymentOptions(paymentOptionIndex).pop_carry_over_option_id;
ATBPeriodYear const &period = yperiod.value(); qCritical() << __func__ << ":" << __LINE__ << "configured carry-over-id" << pop_carry_over_option_id;
pop_carry_over_option_id = period.pye_id;
qCritical() << __func__ << ":" << __LINE__ << "re-computed carry-over-id" << pop_carry_over_option_id;
}
QTime carryOverStart; std::optional<ATBPeriodYear> yperiod = Utilities::GetYearPeriodActive(tariff, start_parking_time);
QTime carryOverEnd; if (yperiod.has_value()) {
int carryOverDuration = -1; ATBPeriodYear const &period = yperiod.value();
pop_carry_over_option_id = period.pye_id;
qCritical() << __func__ << ":" << __LINE__ << "re-computed carry-over-id" << pop_carry_over_option_id;
}
// using TariffCarryOverType = std::multimap<int, ATBCarryOver>; QTime carryOverStart;
std::multimap<int, ATBCarryOver>::const_iterator it; QTime carryOverEnd;
if ((it = tariff->TariffCarryOverOptions.find(pop_carry_over_option_id)) != int carryOverDuration = -1;
tariff->TariffCarryOverOptions.cend()) {
carryOverStart = it->second.carryover[weekDay].static_start;
carryOverEnd = it->second.carryover[weekDay].static_end;
carryOverDuration = it->second.carryover[weekDay].duration;
}
if (carryOverStart.isValid() && carryOverEnd.isValid()) { // using TariffCarryOverType = std::multimap<int, ATBCarryOver>;
qCritical() << __func__ << ":" << __LINE__ << "carryOverStart" << carryOverStart.toString(Qt::ISODate); std::multimap<int, ATBCarryOver>::const_iterator it;
qCritical() << __func__ << ":" << __LINE__ << "carryOverEnd" << carryOverEnd.toString(Qt::ISODate); if ((it = tariff->TariffCarryOverOptions.find(pop_carry_over_option_id)) !=
qCritical() << __func__ << ":" << __LINE__ << "carryOverDuration" << carryOverDuration; tariff->TariffCarryOverOptions.cend()) {
} carryOverStart = it->second.carryover[weekDay].static_start;
carryOverEnd = it->second.carryover[weekDay].static_end;
carryOverDuration = it->second.carryover[weekDay].duration;
}
if (carryOverStart.isValid() && carryOverEnd.isValid() && carryOverDuration != -1) { if (carryOverStart.isValid() && carryOverEnd.isValid()) {
qCritical() << __func__ << ":" << __LINE__ << "carryOverStart" << carryOverStart.toString(Qt::ISODate);
qCritical() << __func__ << ":" << __LINE__ << "carryOverEnd" << carryOverEnd.toString(Qt::ISODate);
qCritical() << __func__ << ":" << __LINE__ << "carryOverDuration" << carryOverDuration;
}
// note: in such a case (direct coins) carry-over has been handled if (carryOverStart.isValid() && carryOverEnd.isValid() && carryOverDuration != -1) {
// already in GetDurationFromCost()
netto_parking_time -= carryOverDuration; // note: in such a case (direct coins) carry-over has been handled
qCritical() << __func__ << ":" << __LINE__ << "netto-parking-time" << netto_parking_time; // already in GetDurationFromCost()
netto_parking_time -= carryOverDuration;
qCritical() << __func__ << ":" << __LINE__ << "netto-parking-time" << netto_parking_time;
// qCritical() << __func__ << __LINE__ << "ticketEndTime.time():" << ticketEndTime.time().toString(Qt::ISODate);
if (ticketEndTime.time() > carryOverStart) {
// qCritical() << __func__ << __LINE__ << "ticketEndTime.time():" << ticketEndTime.time().toString(Qt::ISODate); // qCritical() << __func__ << __LINE__ << "ticketEndTime.time():" << ticketEndTime.time().toString(Qt::ISODate);
ticketEndTime = ticketEndTime.addSecs(carryOverDuration * 60); if (ticketEndTime.time() > carryOverStart) {
} else
if (ticketEndTime.time() == carryOverStart) {
qCritical() << __func__ << __LINE__ << "ticketEndTime.time():" << ticketEndTime.time().toString(Qt::ISODate);
qCritical() << __func__ << ":" << __LINE__ << " carryOverStart" << carryOverStart.toString(Qt::ISODate);
ATBPaymentOption const &po = tariff->getPaymentOptions(paymentOptionIndex);
if (po.pop_apply_carry_over_to_ticket_endtime) {
ticketEndTime = ticketEndTime.addSecs(carryOverDuration * 60);
qCritical() << __func__ << __LINE__ << "ticketEndTime.time():" << ticketEndTime.time().toString(Qt::ISODate);
}
} else {
// qCritical() << __func__ << __LINE__ << "ticketEndTime.time():" << ticketEndTime.time().toString(Qt::ISODate);
if (ticketEndTime.time() < carryOverEnd) {
// qCritical() << __func__ << __LINE__ << "ticketEndTime.time():" << ticketEndTime.time().toString(Qt::ISODate); // qCritical() << __func__ << __LINE__ << "ticketEndTime.time():" << ticketEndTime.time().toString(Qt::ISODate);
ticketEndTime = ticketEndTime.addSecs(carryOverDuration * 60); ticketEndTime = ticketEndTime.addSecs(carryOverDuration * 60);
} else
if (ticketEndTime.time() == carryOverStart) {
qCritical() << __func__ << __LINE__ << "ticketEndTime.time():" << ticketEndTime.time().toString(Qt::ISODate);
qCritical() << __func__ << ":" << __LINE__ << " carryOverStart" << carryOverStart.toString(Qt::ISODate);
ATBPaymentOption const &po = tariff->getPaymentOptions(paymentOptionIndex);
if (po.pop_apply_carry_over_to_ticket_endtime) {
ticketEndTime = ticketEndTime.addSecs(carryOverDuration * 60);
qCritical() << __func__ << __LINE__ << "ticketEndTime.time():" << ticketEndTime.time().toString(Qt::ISODate);
}
} else {
// qCritical() << __func__ << __LINE__ << "ticketEndTime.time():" << ticketEndTime.time().toString(Qt::ISODate);
if (ticketEndTime.time() < carryOverEnd) {
// qCritical() << __func__ << __LINE__ << "ticketEndTime.time():" << ticketEndTime.time().toString(Qt::ISODate);
ticketEndTime = ticketEndTime.addSecs(carryOverDuration * 60);
}
} }
} else {
qCritical() << __func__ << ":" << __LINE__ << "WARNING: wrong carry-over-settings";
} }
} else {
qCritical() << __func__ << ":" << __LINE__ << "WARNING: wrong carry-over-settings";
} }
}
qCritical() << __func__ << ":" << __LINE__ << "ticketEndTime:" << ticketEndTime.toString(Qt::ISODate); qCritical() << __func__ << ":" << __LINE__ << "ticketEndTime:" << ticketEndTime.toString(Qt::ISODate);
for (auto[itr, rangeEnd] = tariff->WeekDays.equal_range((Qt::DayOfWeek)(ticketEndTime.date().dayOfWeek())); for (auto[itr, rangeEnd] = tariff->WeekDays.equal_range((Qt::DayOfWeek)(ticketEndTime.date().dayOfWeek()));
itr != rangeEnd; itr != rangeEnd;
++itr) { ++itr) {
ATBWeekDay const &wd = itr->second; ATBWeekDay const &wd = itr->second;
bool parkTimeLimitViolated = wd.getTariffCarryOverSettings().parkingTimeLimitExceeded(start_parking_time, bool parkTimeLimitViolated = wd.getTariffCarryOverSettings().parkingTimeLimitExceeded(start_parking_time,
ticketEndTime, ticketEndTime,
paymentOptionIndex); paymentOptionIndex);
if (parkTimeLimitViolated) { if (parkTimeLimitViolated) {
//QTime const &tlimit = wd.getTariffCarryOverSettings().parkingTimeLimit(); //QTime const &tlimit = wd.getTariffCarryOverSettings().parkingTimeLimit();
//ticketEndTime.setTime(tlimit); //ticketEndTime.setTime(tlimit);
QList<int> const &stepList = Calculator::GetInstance().GetTimeSteps(tariff, paymentOptionIndex); QList<int> const &stepList = Calculator::GetInstance().GetTimeSteps(tariff, paymentOptionIndex);
QDateTime newTicketEndTime = ticketEndTime; QDateTime newTicketEndTime = ticketEndTime;
qCritical() << __func__ << ":" << __LINE__ << "PARK-TIME VIOLATED"; qCritical() << __func__ << ":" << __LINE__ << "PARK-TIME VIOLATED";
for (int i = stepList.size() - 1; i > 0; --i) { for (int i = stepList.size() - 1; i > 0; --i) {
// qCritical() << __func__ << ":" << __LINE__ << "step[" << i << "]" << stepList.at(i); // qCritical() << __func__ << ":" << __LINE__ << "step[" << i << "]" << stepList.at(i);
if (netto_parking_time > 0 && stepList.at(i) <= netto_parking_time) { if (netto_parking_time > 0 && stepList.at(i) <= netto_parking_time) {
int const diff = stepList.at(i-1) - stepList.at(i); int const diff = stepList.at(i-1) - stepList.at(i);
newTicketEndTime = newTicketEndTime.addSecs(diff * 60); newTicketEndTime = newTicketEndTime.addSecs(diff * 60);
// qCritical() << __func__ << ":" << __LINE__ << "new-ticket-end-time" << newTicketEndTime.toString(Qt::ISODate); // qCritical() << __func__ << ":" << __LINE__ << "new-ticket-end-time" << newTicketEndTime.toString(Qt::ISODate);
parkTimeLimitViolated parkTimeLimitViolated
= wd.getTariffCarryOverSettings() = wd.getTariffCarryOverSettings()
.parkingTimeLimitExceeded(start_parking_time, newTicketEndTime, paymentOptionIndex); .parkingTimeLimitExceeded(start_parking_time, newTicketEndTime, paymentOptionIndex);
if (!parkTimeLimitViolated) { if (!parkTimeLimitViolated) {
qCritical() << __func__ << ":" << __LINE__ qCritical() << __func__ << ":" << __LINE__
<< "PARK-TIME NOT VIOLATED FOR" << newTicketEndTime.toString(Qt::ISODate); << "PARK-TIME NOT VIOLATED FOR" << newTicketEndTime.toString(Qt::ISODate);
int duration = stepList.at(i-1); int duration = stepList.at(i-1);
// qCritical() << __func__ << ":" << __LINE__ << "duration" << duration; // qCritical() << __func__ << ":" << __LINE__ << "duration" << duration;
std::multimap<int, ATBDuration>::const_iterator it; std::multimap<int, ATBDuration>::const_iterator it;
for (it = tariff->Duration.cbegin(); for (it = tariff->Duration.cbegin();
it != tariff->Duration.cend(); it != tariff->Duration.cend();
++it) { ++it) {
if (duration == it->second.pun_duration) { if (duration == it->second.pun_duration) {
// qCritical() << __func__ << ":" << __LINE__ << "duration" << duration; // qCritical() << __func__ << ":" << __LINE__ << "duration" << duration;
ATBPaymentOption &po = tariff->getPaymentOptions(paymentOptionIndex); ATBPaymentOption &po = tariff->getPaymentOptions(paymentOptionIndex);
int const pop_id = po.pop_id; int const pop_id = po.pop_id;
for (auto[itr, rangeEnd] = tariff->PaymentRate.equal_range(pop_id); itr != rangeEnd; ++itr) { for (auto[itr, rangeEnd] = tariff->PaymentRate.equal_range(pop_id); itr != rangeEnd; ++itr) {
int const durationId = itr->second.pra_payment_unit_id; int const durationId = itr->second.pra_payment_unit_id;
// qCritical() << __func__ << ":" << __LINE__ << "durationId" << durationId << it->second.pun_id; // qCritical() << __func__ << ":" << __LINE__ << "durationId" << durationId << it->second.pun_id;
// note: for this to work, Duration and PaymentRate must have // note: for this to work, Duration and PaymentRate must have
// exactly the same structure // exactly the same structure
if (durationId == it->second.pun_id) { if (durationId == it->second.pun_id) {
int const pra_price = itr->second.pra_price; int const pra_price = itr->second.pra_price;
po.pop_max_price = pra_price; po.pop_max_price = pra_price;
qCritical() << __func__ << ":" << __LINE__ << "new max-price" << po.pop_max_price; qCritical() << __func__ << ":" << __LINE__ << "new max-price" << po.pop_max_price;
// note: ABOVE_MAX_PARKING_TIME would also be possible // note: ABOVE_MAX_PARKING_TIME would also be possible
// but here max-parking-time is dynamic. And for // but here max-parking-time is dynamic. And for
// this dynamic value, opverpaid is actually correct // this dynamic value, opverpaid is actually correct
calcState.setDesc(CalcState::OVERPAID); calcState.setDesc(CalcState::OVERPAID);
calcState.setStatus(CalcState::OVERPAID); calcState.setStatus(CalcState::OVERPAID);
return calcState; return calcState;
}
} }
} }
} }
} }
} }
} }
}
calcState.setDesc(QString("line=%1 endTime=%2: park-time-limit violated").arg(__LINE__) calcState.setDesc(QString("line=%1 endTime=%2: park-time-limit violated").arg(__LINE__)
.arg(ticketEndTime.time().toString(Qt::ISODate))); .arg(ticketEndTime.time().toString(Qt::ISODate)));
return calcState.set(CalcState::State::ABOVE_MAX_PARKING_TIME); return calcState.set(CalcState::State::ABOVE_MAX_PARKING_TIME);
}
} }
} }

View File

@ -6,6 +6,8 @@
#include "ticket.h" #include "ticket.h"
#include "tariff_global_defines.h" #include "tariff_global_defines.h"
#include "tariff_prepaid.h" #include "tariff_prepaid.h"
#include "tariff_out_of_service.h"
#include "tariff_service.h"
#include <sstream> #include <sstream>
#include <algorithm> #include <algorithm>
@ -117,6 +119,430 @@ QDateTime Calculator::GetDailyTicketDuration(Configuration* cfg, const QDateTime
return QDateTime(); return QDateTime();
} }
///
/// \brief getPrepaid
/// \param cfg
/// \param dt
/// \return
///
std::optional<ATBTariffPrepaid> getPrepaid(Configuration const *cfg, QDateTime const &dt) {
std::optional<ATBTariffPrepaid> value = std::nullopt;
int weekDay = dt.date().dayOfWeek();
ATBTime inputTime(dt.time());
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);
break;
}
}
return value;
}
///
/// \brief getCarryOver
/// \param cfg
/// \param dt
/// \return
///
std::optional<ATBTariffCarryOver> getCarryOver(Configuration const *cfg, QDateTime const &dt) {
std::optional<ATBTariffCarryOver> value = std::nullopt;
int weekDay = dt.date().dayOfWeek();
ATBTime inputTime(dt.time());
auto const &carryOverRange = cfg->TariffCarryOvers.equal_range(weekDay);
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) {
value = value.value_or(i->second);
break;
}
}
return value;
}
///
/// \brief getService
/// \param cfg
/// \param dt
/// \return
///
std::optional<ATBTariffService> getService(Configuration const *cfg, QDateTime const &dt) {
std::optional<ATBTariffService> value = std::nullopt;
int weekDay = dt.date().dayOfWeek();
ATBTime inputTime(dt.time());
auto const &serviceRange = cfg->TariffServices.equal_range(weekDay);
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);
break;
}
}
return value;
}
///
/// \brief getOutOfService
/// \param cfg
/// \param dt
/// \return
///
std::optional<ATBTariffOutOfService> getOutOfService(Configuration const *cfg, QDateTime const &dt) {
std::optional<ATBTariffOutOfService> value = std::nullopt;
int weekDay = dt.date().dayOfWeek();
ATBTime inputTime(dt.time());
QDate date;
auto const &outOfServiceRange = cfg->TariffOutOfServices.equal_range(weekDay);
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 (date.isNull() || !date.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;
}
}
}
return value;
}
std::pair<CalcState, QDateTime>
Calculator::ComputeDurationFromCost(Configuration const *cfg,
QDateTime const &startDatetimePassed, // given in local time
int cost) {
QDateTime inputDate = startDatetimePassed;
inputDate.setTime(QTime(inputDate.time().hour(), inputDate.time().minute(), 0));
// TODO:
int paymentOptionIndex = 0;
bool overPaid = false;
bool successMaxPrice = false;
int const pop_id = cfg->getPaymentOptions(paymentOptionIndex).pop_id;
int const pop_accumulate_prices = cfg->getPaymentOptions(paymentOptionIndex).pop_accumulate_prices;
int const pop_max_price = cfg->getPaymentOptions(paymentOptionIndex).pop_max_price;
int const pop_max_time = cfg->getPaymentOptions(paymentOptionIndex).pop_max_time;
int const pop_min_price = cfg->getPaymentOptions(paymentOptionIndex).pop_min_price;
int const pop_allow_overpay = cfg->getPaymentOptions(paymentOptionIndex).pop_allow_overpay;
int price = 0;
int durationId = 0;
int netto_parking_time_in_minutes = 0;
int brutto_parking_time_in_minutes = 0;
int free_parking_time_in_minutes = 0;
QMap<int, int> nettoParktimePrice;
QMap<int, int> priceNettoParktime;
for (auto[itr, rangeEnd] = cfg->PaymentRate.equal_range(pop_id); itr != rangeEnd; ++itr) {
durationId = itr->second.pra_payment_unit_id;
int const pra_price = itr->second.pra_price;
if (pop_accumulate_prices) {
price += pra_price;
} else {
price = pra_price;
}
//if ((double)price == cost) {
auto search = cfg->Duration.find(durationId);
if (search != cfg->Duration.end()) {
// found now the duration in minutes
// check if we are still inside the working-time-range
ATBDuration duration = search->second;
nettoParktimePrice.insert(duration.pun_duration, price);
priceNettoParktime.insert(price, duration.pun_duration);
}
//}
}
// qCritical() << __func__ << ":" << __LINE__ << nettoParktimePrice;
// qCritical() << __func__ << ":" << __LINE__ << priceNettoParktime;
if (cost == pop_max_price) {
qCritical() << DBG_HEADER << "SUCCESS MAX-PARKING-PRICE" << pop_max_price << ", COST" << cost;
successMaxPrice = true;
}
if (cost > pop_max_price) {
qCritical() << DBG_HEADER << "MAX-PARKING-PRICE" << pop_max_price << ", COST" << cost;
if (pop_allow_overpay == false) {
return std::make_pair(CalcState(CalcState::State::OVERPAID), QDateTime());
}
cost = pop_max_price;
overPaid = true;
qCritical() << DBG_HEADER << "OVERPAID, MAX-PARKING-PRICE" << pop_max_price << ", COST" << cost;
// return CalcState::OVERPAID.toStdString();
}
if (cost < pop_min_price) {
qCritical() << DBG_HEADER << "MIN-PARKING-PRICE" << pop_min_price << ", COST" << cost;
return std::make_pair(CalcState(CalcState::State::BELOW_MIN_PARKING_PRICE), QDateTime());
}
int weekDay = inputDate.date().dayOfWeek();
qCritical() << __func__ << ":" << __LINE__ << "START weekDay" << weekDay << inputDate.toString(Qt::ISODate);
qCritical() << __func__ << ":" << __LINE__ << "START cost" << cost;
QDateTime dt;
bool computationStarted = false;
price = 0;
netto_parking_time_in_minutes = 0;
brutto_parking_time_in_minutes = 0;
free_parking_time_in_minutes = 0;
int const nettoParktimeForCost = priceNettoParktime[int(cost)];
qCritical() << __func__ << ":" << __LINE__ << "nettoParktimeForCost" << nettoParktimeForCost;
bool startDateNotOutOfService = false;
int cnt = 0;
while (++cnt < 10 && netto_parking_time_in_minutes < nettoParktimeForCost) {
// qCritical() << __func__ << ":" << __LINE__ << "cnt [" << cnt;
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<ATBTariffOutOfService> oos = getOutOfService(cfg, dt)) {
if (overPaid || startDateNotOutOfService) {
return std::make_pair(CalcState(CalcState::State::OVERPAID,
CalcState::OVERPAID), dt);
}
return std::make_pair(CalcState(CalcState::State::OUTSIDE_ALLOWED_PARKING_TIME,
CalcState::OUTSIDE_ALLOWED_PARKING_TIME), dt);
} else {
startDateNotOutOfService = true;
}
if (computationStarted == false) {
computationStarted = true;
if (std::optional<ATBTariffPrepaid> pp = getPrepaid(cfg, dt)) {
TimeRange const &prepaidTimeRange = pp.value().m_range;
ATBTime t(dt.time().hour(), dt.time().minute(), 0, 0);
free_parking_time_in_minutes += t.secsTo(prepaidTimeRange.m_end.toString(Qt::ISODate)) / 60;
}
}
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<ATBTariffCarryOver> co = getCarryOver(cfg, inputDate.addSecs(brutto_parking_time_in_minutes * 60))) {
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);
if (std::optional<ATBTariffService> serv = getService(cfg, dt)) {
TimeRange const &serviceTimeRange = serv.value().m_range;
if (nettoParktimeForCost > netto_parking_time_in_minutes) {
int rest_parking_time_in_minutes = nettoParktimeForCost - netto_parking_time_in_minutes;
ATBTime t(dt.time().hour(), dt.time().minute(), 0, 0);
int timeToServiceEnd = t.secsTo(serviceTimeRange.m_end.toString(Qt::ISODate)) / 60;
if (serviceTimeRange.m_duration > 0) {
if (timeToServiceEnd < rest_parking_time_in_minutes) {
netto_parking_time_in_minutes += timeToServiceEnd;
} else {
netto_parking_time_in_minutes += rest_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);
// qCritical() << __func__ << ":" << __LINE__ << "cnt" << cnt << "]";
}
if (cnt >= 10) {
qCritical() << __func__ << ":" << __LINE__ << "BREAK";
}
// configure if last carry-over ranges shall be added to ticket-end-time
cnt = 0;
while (std::optional<ATBTariffCarryOver> co = getCarryOver(cfg, inputDate.addSecs(brutto_parking_time_in_minutes * 60))) {
if (++cnt > 5) {
qCritical() << __func__ << ":" << __LINE__ << "BREAK";
break;
}
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;
}
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("ticket-end-time %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 (successMaxPrice) {
qCritical() << __func__ << ":" << __LINE__ << "SUCC" << dt;
return std::make_pair(CalcState(CalcState::State::SUCCESS_MAXPRICE), dt);
}
if (overPaid) {
qCritical() << __func__ << ":" << __LINE__ << "OVER" << dt;
return std::make_pair(CalcState(CalcState::State::OVERPAID), dt);
}
qCritical() << __func__ << ":" << __LINE__ << "DT" << dt.toString(Qt::ISODate);
return std::make_pair(CalcState(CalcState::State::SUCCESS), dt);
}
std::pair<CalcState, std::optional<int>>
Calculator::ComputeCostFromDuration(Configuration const* cfg, QDateTime const &startDatetime,
QDateTime &endDatetime, int nettoParkingTime) {
// TODO
int paymentOptionIndex = 0;
std::optional<int> cost{};
int const pop_id = cfg->getPaymentOptions(paymentOptionIndex).pop_id;
int const pop_accumulate_prices = cfg->getPaymentOptions(paymentOptionIndex).pop_accumulate_prices;
int price = 0;
int durationId = 0;
int netto_parking_time_in_minutes = 0;
int brutto_parking_time_in_minutes = 0;
int free_parking_time_in_minutes = 0;
QMap<int, int> nettoParktimePrice;
QMap<int, int> priceNettoParktime;
for (auto[itr, rangeEnd] = cfg->PaymentRate.equal_range(pop_id); itr != rangeEnd; ++itr) {
durationId = itr->second.pra_payment_unit_id;
int const pra_price = itr->second.pra_price;
if (pop_accumulate_prices) {
price += pra_price;
} else {
price = pra_price;
}
auto search = cfg->Duration.find(durationId);
if (search != cfg->Duration.end()) {
// found now the duration in minutes
// check if we are still inside the working-time-range
ATBDuration duration = search->second;
nettoParktimePrice.insert(duration.pun_duration, price);
priceNettoParktime.insert(price, duration.pun_duration);
}
}
qCritical() << __func__ << ":" << __LINE__ << "START netto-parking-time" << nettoParkingTime;
CalcState returnState;
QList<int> keys = nettoParktimePrice.keys();
int index = keys.indexOf(nettoParkingTime);
if (index != -1) {
int c = nettoParktimePrice[keys.at(index)];
qCritical() << __func__ << ":" << __LINE__ << "cost for netto-parking-time" << c;
std::pair<CalcState, QDateTime> r = ComputeDurationFromCost(cfg, startDatetime, c);
qCritical() << __func__ << ":" << __LINE__ << "result"
<< r.first.toString() << r.second.toString(Qt::ISODate);
returnState = r.first;
if (returnState.getStatus() == CalcState::State::SUCCESS ||
returnState.getStatus() == CalcState::State::SUCCESS_MAXPRICE) {
endDatetime = r.second;
qCritical() << __func__ << ":" << __LINE__ << "--- endDateTime" << endDatetime.toString(Qt::ISODate);
qCritical() << __func__ << ":" << __LINE__ << "------ r.second" << r.second.toString(Qt::ISODate);
if (!endDatetime.isNull() && endDatetime.isValid()) {
cost = c;
}
}
}
if (cost) {
qCritical() << __func__ << ":" << __LINE__ << "--- return cost" << cost.value();
return std::make_pair(returnState, cost);
}
qCritical() << __func__ << ":" << __LINE__ << "--- return error for cost" << returnState.toString();
return std::make_pair(returnState, cost);
}
/// <inheritdoc/> /// <inheritdoc/>
std::pair<std::string, QDateTime> std::pair<std::string, QDateTime>
Calculator::GetDurationFromCost(Configuration* cfg, Calculator::GetDurationFromCost(Configuration* cfg,
@ -253,6 +679,17 @@ Calculator::GetDurationFromCost(Configuration* cfg,
qCritical() << DBG_HEADER << " TODO"; qCritical() << DBG_HEADER << " TODO";
} }
} else } else
if (paymentMethodId == PaymentMethod::Unified) {
std::pair<CalcState, QDateTime> r =
ComputeDurationFromCost(cfg, QDateTime::fromString(startDatetimePassed, Qt::ISODate), cost);
CalcState cs = r.first;
qCritical() << __func__ << ":" << __LINE__ << cs.toString();
qCritical() << __func__ << ":" << __LINE__ << r.second.toString(Qt::ISODate);
return std::make_pair(r.first.toString().toStdString(), r.second);
} else
if (paymentMethodId == PaymentMethod::Steps) { if (paymentMethodId == PaymentMethod::Steps) {
if (tariffIs24_7(cfg)) { if (tariffIs24_7(cfg)) {
// use tariff with structure as for instance Schoenau, Koenigsee: // use tariff with structure as for instance Schoenau, Koenigsee:
@ -271,8 +708,11 @@ Calculator::GetDurationFromCost(Configuration* cfg,
int const pop_id = cfg->getPaymentOptions(paymentOptionIndex).pop_id; int const pop_id = cfg->getPaymentOptions(paymentOptionIndex).pop_id;
int const pop_max_price = cfg->getPaymentOptions(paymentOptionIndex).pop_max_price; int const pop_max_price = cfg->getPaymentOptions(paymentOptionIndex).pop_max_price;
//int const pop_max_time = cfg->getPaymentOptions(paymentOptionIndex).pop_max_time;
int const pop_min_price = cfg->getPaymentOptions(paymentOptionIndex).pop_min_price; int const pop_min_price = cfg->getPaymentOptions(paymentOptionIndex).pop_min_price;
int const pop_allow_overpay = cfg->getPaymentOptions(paymentOptionIndex).pop_allow_overpay; int const pop_allow_overpay = cfg->getPaymentOptions(paymentOptionIndex).pop_allow_overpay;
int const pop_accumulate_prices = cfg->getPaymentOptions(paymentOptionIndex).pop_accumulate_prices;
int price = 0;
if (cost == pop_max_price) { if (cost == pop_max_price) {
qCritical() << DBG_HEADER << "SUCCESS MAX-PARKING-PRICE" << pop_max_price << ", COST" << cost; qCritical() << DBG_HEADER << "SUCCESS MAX-PARKING-PRICE" << pop_max_price << ", COST" << cost;
@ -410,9 +850,9 @@ Calculator::GetDurationFromCost(Configuration* cfg,
qCritical() << DBG_HEADER << " CURRENT WORKING-TIME-TO" << current_working_time_to.toString(Qt::ISODate); qCritical() << DBG_HEADER << " CURRENT WORKING-TIME-TO" << current_working_time_to.toString(Qt::ISODate);
#endif #endif
int const pop_accumulate_prices = cfg->getPaymentOptions(paymentOptionIndex).pop_accumulate_prices; // int const pop_accumulate_prices = cfg->getPaymentOptions(paymentOptionIndex).pop_accumulate_prices;
// int const pop_accumulate_durations = cfg->getPaymentOptions(paymentOptionIndex).pop_accumulate_durations; // int const pop_accumulate_durations = cfg->getPaymentOptions(paymentOptionIndex).pop_accumulate_durations;
int price = 0; // int price = 0;
int new_price = 0; int new_price = 0;
int durationInSecs = 0; int durationInSecs = 0;
uint32_t duration_previous = 0; uint32_t duration_previous = 0;
@ -1203,7 +1643,7 @@ CalcState Calculator::isParkingAllowedForWeekDay(Configuration const *cfg,
(int)cfg->TimeRange.count(pop_carry_over_end_time_range) <= 0) { (int)cfg->TimeRange.count(pop_carry_over_end_time_range) <= 0) {
qCritical() << DBG_HEADER << "PARKING_ALLOWED. startTime" << startTime.toString(Qt::ISODate); qCritical() << DBG_HEADER << "PARKING_ALLOWED. startTime" << startTime.toString(Qt::ISODate);
return CalcState(CalcState::State::SUCCESS, "PARKING_ALLOWED", startTime); return CalcState(CalcState::State::SUCCESS, "PARKING_ALLOWED", startTime, QTime());
} else } else
// search entry in time-range-field of tariff-file // search entry in time-range-field of tariff-file