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()
This commit is contained in:
parent
ada7bebd90
commit
1086e360e5
@ -119,6 +119,430 @@ QDateTime Calculator::GetDailyTicketDuration(Configuration* cfg, const 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/>
|
||||
std::pair<std::string, QDateTime>
|
||||
Calculator::GetDurationFromCost(Configuration* cfg,
|
||||
|
Loading…
Reference in New Issue
Block a user