diff --git a/library/include/mobilisis/calculator_functions.h b/library/include/mobilisis/calculator_functions.h index 1d3b359..0e22b66 100644 --- a/library/include/mobilisis/calculator_functions.h +++ b/library/include/mobilisis/calculator_functions.h @@ -3,6 +3,7 @@ #include #include +#include #include "configuration.h" #include "calculate_price.h" @@ -70,7 +71,8 @@ public: /// Date/time of payment to be conducted in ISO8601 format (e.g. 2022-12-25T08:00:00Z) /// /// Returns duration in seconds (data type: double) - std::string GetDurationFromCost(Configuration* cfg, uint8_t vehicle_type, char const* start_datetime, double price, + std::pair + GetDurationFromCost(Configuration* cfg, uint8_t vehicle_type, char const* start_datetime, double price, PermitType permitType, bool nextDay = false, bool prepaid = false); /// diff --git a/library/include/mobilisis/spec_days_worktime.h b/library/include/mobilisis/spec_days_worktime.h index 95c7ade..83baf4f 100644 --- a/library/include/mobilisis/spec_days_worktime.h +++ b/library/include/mobilisis/spec_days_worktime.h @@ -8,5 +8,9 @@ public: int pedwt_period_exc_day_id; std::string pedwt_time_from; std::string pedwt_time_to; - double pedwt_price; -}; \ No newline at end of file + double pedwt_price; + int pedwt_paid; // 00: not paid (i.e. free) + // 01: not paid in winter term, piad in summer term + // 10: paid in winter term, not paid in summer term + // 11: paid in winter and in summer term +}; diff --git a/library/src/calculate_price.cpp b/library/src/calculate_price.cpp index 5dfdf37..fbacfb9 100644 --- a/library/src/calculate_price.cpp +++ b/library/src/calculate_price.cpp @@ -808,22 +808,48 @@ CalcState CALCULATE_LIBRARY_API compute_price_for_parking_ticket( int pop_prepaid_option_id = tariff->getPaymentOptions(paymentOptionIndex).pop_prepaid_option_id; std::optional yperiod = Utilities::GetYearPeriodActive(tariff, start_parking_time); + int period_id = -1; if (yperiod.has_value()) { ATBPeriodYear const &period = yperiod.value(); + period_id = period.pye_id; pop_carry_over_option_id = period.pye_id; pop_prepaid_option_id = period.pye_id; qCritical() << __func__ << ":" << __LINE__ << "re-computed carry-over-id" << pop_carry_over_option_id; qCritical() << __func__ << ":" << __LINE__ << " re-computed prepaid-id" << pop_prepaid_option_id; } - QTime carryOverStart = tariff->TariffCarryOverOptions.find(pop_carry_over_option_id)->second.carryover[weekDay].static_start; - int carryOverDuration = tariff->TariffCarryOverOptions.find(pop_carry_over_option_id)->second.carryover[weekDay].duration; + QDateTime effectiveStartTime(start_parking_time); - qCritical() << __func__ << ":" << __LINE__ << " carryOverStart" << carryOverStart.toString(Qt::ISODate); - qCritical() << __func__ << ":" << __LINE__ << "carryOverDuration" << carryOverDuration; + // handle special days + int const specialDayId = tariff->specialDayId(start_parking_time); + if (specialDayId > 0) { // found special day + for (auto[itr, rangeEnd] = tariff->SpecialDaysWorktime.equal_range(specialDayId); itr != rangeEnd; ++itr) { + ATBSpecialDaysWorktime const &wt = itr->second; + switch(period_id) { + case 1: // summer term + if ((wt.pedwt_paid & 1) == 0) { + // does not have to be paid, move to next midnight + + // better: start of next day (falls kein vorkauf besteht) + effectiveStartTime = effectiveStartTime.addDays(1); + effectiveStartTime.setTime(QTime(0, 0, 0)); + } + break; + case 2: // winter term + if ((wt.pedwt_paid & 2) == 0) { + // does not have to be paid, move to next midnight + + // better: start of next day (falls kein vorkauf besteht) + effectiveStartTime = effectiveStartTime.addDays(1); + effectiveStartTime.setTime(QTime(0, 0, 0)); + } + break; + default:; + } + } + } // handle prepaid option - QDateTime effectiveStartTime(start_parking_time); int const prepaid_option_id = tariff->getPaymentOptions(paymentOptionIndex).pop_prepaid_option_id; std::optional prepaidOption = tariff->getPrepaidType(prepaid_option_id); if (prepaidOption.has_value()) { @@ -846,37 +872,52 @@ CalcState CALCULATE_LIBRARY_API compute_price_for_parking_ticket( qCritical() << __func__ << ":" << __LINE__ << "effectiveStartTime:" << effectiveStartTime.toString(Qt::ISODate); - // handle carry over - int minutesUntilCarryOver = effectiveStartTime.time().secsTo(carryOverStart) / 60; - if (netto_parking_time > minutesUntilCarryOver) { - int const rest = netto_parking_time - minutesUntilCarryOver; - QDateTime s(effectiveStartTime); - s = s.addSecs(minutesUntilCarryOver * 60); - s = s.addSecs(carryOverDuration * 60); - end_parking_time = s.addSecs(rest * 60); - } else { - end_parking_time = effectiveStartTime.addSecs(netto_parking_time*60); - } + int const carryOver = tariff->getPaymentOptions(paymentOptionIndex).pop_carry_over; - qCritical() << __func__ << ":" << __LINE__ << "end-parking-time:" << end_parking_time.toString(Qt::ISODate); + qCritical() << __func__ << ":" << __LINE__ << " carryOver flag" << carryOver; - weekDay = end_parking_time.date().dayOfWeek(); + if (carryOver == 1) { + QTime carryOverStart = tariff->TariffCarryOverOptions.find(pop_carry_over_option_id)->second.carryover[weekDay].static_start; + int carryOverDuration = tariff->TariffCarryOverOptions.find(pop_carry_over_option_id)->second.carryover[weekDay].duration; - // musste man in einer schleife machen - carryOverStart = tariff->TariffCarryOverOptions.find(pop_carry_over_option_id)->second.carryover[weekDay].static_start; - carryOverDuration = tariff->TariffCarryOverOptions.find(pop_carry_over_option_id)->second.carryover[weekDay].duration; - - if (end_parking_time.time() > carryOverStart) { - end_parking_time = end_parking_time.addSecs(carryOverDuration * 60); - } else - if (end_parking_time.time() == carryOverStart) { - qCritical() << __func__ << ":" << __LINE__ << "end-parking-time:" << end_parking_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) { - end_parking_time = end_parking_time.addSecs(carryOverDuration * 60); - qCritical() << __func__ << ":" << __LINE__ << "end-parking-time:" << end_parking_time.toString(Qt::ISODate); + qCritical() << __func__ << ":" << __LINE__ << "carryOverDuration" << carryOverDuration; + + // handle carry over + int minutesUntilCarryOver = effectiveStartTime.time().secsTo(carryOverStart) / 60; + if (netto_parking_time > minutesUntilCarryOver) { + int const rest = netto_parking_time - minutesUntilCarryOver; + QDateTime s(effectiveStartTime); + s = s.addSecs(minutesUntilCarryOver * 60); + s = s.addSecs(carryOverDuration * 60); + end_parking_time = s.addSecs(rest * 60); + } else { + end_parking_time = effectiveStartTime.addSecs(netto_parking_time*60); } + + qCritical() << __func__ << ":" << __LINE__ << "end-parking-time:" << end_parking_time.toString(Qt::ISODate); + + weekDay = end_parking_time.date().dayOfWeek(); + + // musste man in einer schleife machen + carryOverStart = tariff->TariffCarryOverOptions.find(pop_carry_over_option_id)->second.carryover[weekDay].static_start; + carryOverDuration = tariff->TariffCarryOverOptions.find(pop_carry_over_option_id)->second.carryover[weekDay].duration; + + if (end_parking_time.time() > carryOverStart) { + end_parking_time = end_parking_time.addSecs(carryOverDuration * 60); + } else + if (end_parking_time.time() == carryOverStart) { + qCritical() << __func__ << ":" << __LINE__ << "end-parking-time:" << end_parking_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) { + end_parking_time = end_parking_time.addSecs(carryOverDuration * 60); + qCritical() << __func__ << ":" << __LINE__ << "end-parking-time:" << end_parking_time.toString(Qt::ISODate); + } + } + } else { + qCritical() << __func__ << ":" << __LINE__ << "NO carryOver configured"; + end_parking_time = effectiveStartTime.addSecs(netto_parking_time*60); } end_parking_time.setTime(QTime(end_parking_time.time().hour(), @@ -970,10 +1011,14 @@ CalcState CALCULATE_LIBRARY_API compute_duration_for_parking_ticket( qCritical() << " start (cs): " << cs; qCritical() << " price: " << price; - duration = Calculator::GetInstance().GetDurationFromCost(tariff, - tariff->getPaymentOptions(paymentOptionIndex).pop_payment_method_id, - cs.toLocal8Bit().constData(), - price, permitType, nextDay, prepaid).c_str(); + std::pair p_duration + = Calculator::GetInstance().GetDurationFromCost(tariff, + tariff->getPaymentOptions(paymentOptionIndex).pop_payment_method_id, + cs.toLocal8Bit().constData(), + price, permitType, nextDay, prepaid); + duration = p_duration.first.c_str(); + + QDateTime d = QDateTime::fromString(duration, Qt::ISODate); if (!d.isValid()) { calcState.setDesc(QString("ticketEndTime=%1").arg(duration)); @@ -1044,11 +1089,16 @@ CalcState CALCULATE_LIBRARY_API compute_duration_for_parking_ticket( } QString cs = start_parking_time.toString(Qt::ISODate); - QString endTime = Calculator::GetInstance().GetDurationFromCost( + + std::pair p_endTime + = Calculator::GetInstance().GetDurationFromCost( tariff, tariff->getPaymentOptions().pop_payment_method_id, cs.toLocal8Bit().constData(), - price, permitType, nextDay, prepaid).c_str(); + price, permitType, nextDay, prepaid); + + QString endTime = p_endTime.first.c_str(); + ticketEndTime = p_endTime.second; if (endTime == CalcState::SUCCESS) { calcState.setDesc(QString("SUCCESS")); diff --git a/library/src/calculator_functions.cpp b/library/src/calculator_functions.cpp index aebf3f2..22a26f8 100644 --- a/library/src/calculator_functions.cpp +++ b/library/src/calculator_functions.cpp @@ -118,13 +118,14 @@ QDateTime Calculator::GetDailyTicketDuration(Configuration* cfg, const QDateTime return QDateTime(); } /// -std::string Calculator::GetDurationFromCost(Configuration* cfg, - uint8_t payment_option, - char const *startDatetimePassed, // given in local time - double cost, - PermitType /*permitType*/, - bool nextDay, - bool prepaid) +std::pair +Calculator::GetDurationFromCost(Configuration* cfg, + uint8_t payment_option, + char const *startDatetimePassed, // given in local time + double cost, + PermitType /*permitType*/, + bool nextDay, + bool prepaid) { Q_UNUSED(payment_option); Q_UNUSED(nextDay); @@ -134,6 +135,8 @@ std::string Calculator::GetDurationFromCost(Configuration* cfg, inputDate.setTime(QTime(inputDate.time().hour(), inputDate.time().minute(), 0)); static const PaymentMethod paymentMethodId = Utilities::getPaymentMethodId(cfg); + bool overPaid = false; + int paymentOptionIndex = getPaymentOptionIndex(*cfg, inputDate); if (paymentOptionIndex == -1) { paymentOptionIndex = cfg->getPaymentOptionIndex(QDateTime::fromString(startDatetimePassed, Qt::ISODate)); @@ -162,7 +165,7 @@ std::string Calculator::GetDurationFromCost(Configuration* cfg, // minimal price is set by GetTimeSteps() qCritical() << DBG_HEADER << " provided price (cost):" << cost; qCritical() << DBG_HEADER << "configured minimal price:" << cfg->getPaymentOptions(paymentOptionIndex).pop_min_price; - return CalcState::BELOW_MIN_PARKING_PRICE.toStdString(); + return std::make_pair(CalcState::BELOW_MIN_PARKING_PRICE.toStdString(), QDateTime()); } if (prepaid == false) { @@ -225,9 +228,9 @@ std::string Calculator::GetDurationFromCost(Configuration* cfg, qCritical() << DBG_HEADER << " minimal parking time (minutes):" << cfg->getPaymentOptions(paymentOptionIndex).pop_min_time; if (duration_previous < cfg->getPaymentOptions(paymentOptionIndex).pop_min_time) { - return CalcState::BELOW_MIN_PARKING_TIME.toStdString(); // minimal parking time is set by GetTimeSteps() + return std::make_pair(CalcState::BELOW_MIN_PARKING_TIME.toStdString(), d); // minimal parking time is set by GetTimeSteps() } - return d.toString(Qt::ISODate).toStdString(); + return std::make_pair(d.toString(Qt::ISODate).toStdString(), d); } } @@ -240,7 +243,7 @@ std::string Calculator::GetDurationFromCost(Configuration* cfg, qCritical() << DBG_HEADER << " provided price (cost):" << cost; qCritical() << DBG_HEADER << " duration for price:" << durationStr; - return durationStr.toStdString(); + return std::make_pair(durationStr.toStdString(), d); } } } @@ -254,14 +257,14 @@ std::string Calculator::GetDurationFromCost(Configuration* cfg, // use tariff with structure as for instance Schoenau, Koenigsee: // without given YearPeriod, SpecialDays and SpecialDaysWorktime inputDate = inputDate.addSecs(GetDurationForPrice(cfg, cost) * 60); - return inputDate.toString(Qt::ISODate).toStdString(); + return std::make_pair(inputDate.toString(Qt::ISODate).toStdString(), inputDate); } else { if (Utilities::IsYearPeriodActive(cfg, inputDate)) { if (!prepaid) { CalcState cs = isParkingAllowed(cfg, inputDate); if (cs) { inputDate.setTime(cs.getAllowedTimeRange().getTimeUntil()); - return inputDate.toString(Qt::ISODate).toStdString(); + return std::make_pair(inputDate.toString(Qt::ISODate).toStdString(), inputDate); } } @@ -273,15 +276,16 @@ std::string Calculator::GetDurationFromCost(Configuration* cfg, if (cost > pop_max_price) { qCritical() << DBG_HEADER << "MAX-PARKING-PRICE" << pop_max_price << ", COST" << cost; if (pop_allow_overpay == false) { - return CalcState::OVERPAID.toStdString(); + return std::make_pair(CalcState::OVERPAID.toStdString(), QDateTime()); } cost = pop_max_price; + overPaid = true; // return CalcState::OVERPAID.toStdString(); } if (cost < pop_min_price) { qCritical() << DBG_HEADER << "MIN-PARKING-PRICE" << pop_min_price << ", COST" << cost; - return CalcState::BELOW_MIN_PARKING_PRICE.toStdString(); + return std::make_pair(CalcState::BELOW_MIN_PARKING_PRICE.toStdString(), QDateTime()); } // int const pop_pre_paid = 1; @@ -366,7 +370,7 @@ std::string Calculator::GetDurationFromCost(Configuration* cfg, qCritical() << DBG_HEADER << "ERROR" << inputDate.toString(Qt::ISODate) << "NOT IN VALID WORKING TIME-RANGE"; - return ""; + return std::make_pair("", QDateTime()); } //qCritical() << __func__ << __LINE__; @@ -391,7 +395,7 @@ std::string Calculator::GetDurationFromCost(Configuration* cfg, qCritical() << DBG_HEADER << "ERROR" << inputDate.toString(Qt::ISODate) << "NOT IN VALID WORKING TIME-RANGE"; - return ""; + return std::make_pair("", QDateTime()); } #if DEBUG_GET_DURATION_FROM_COST==1 @@ -440,7 +444,10 @@ std::string Calculator::GetDurationFromCost(Configuration* cfg, QDateTime d(QDateTime::fromString(s, Qt::ISODate)); d = d.addSecs(duration_previous * 60); //qCritical() << DBG_HEADER << "XXXXXXXXXXXXXXXXXXXXX" << d; - return d.toString(Qt::ISODate).toStdString(); + if (overPaid) { + return std::make_pair(CalcState::OVERPAID.toStdString(), d); + } + return std::make_pair(d.toString(Qt::ISODate).toStdString(), d); } } else { durationInSecs = cfg->Duration.find(durationId)->second.pun_duration * 60; @@ -551,7 +558,10 @@ std::string Calculator::GetDurationFromCost(Configuration* cfg, qCritical() << DBG_HEADER << "TICKET-END" << s; #endif - return s.toStdString(); + if (overPaid) { + return std::make_pair(CalcState::OVERPAID.toStdString(), inputDate); + } + return std::make_pair(s.toStdString(), inputDate); } // if ((double)price == cost) { else { //qCritical() << DBG_HEADER; @@ -561,7 +571,7 @@ std::string Calculator::GetDurationFromCost(Configuration* cfg, } } - return ""; + return std::make_pair("", QDateTime()); } } } else @@ -578,7 +588,7 @@ std::string Calculator::GetDurationFromCost(Configuration* cfg, if (cost < minParkingPrice) { qCritical() << QString("ERROR: COST < MIN_PARKING_PRICE (%1 < %2)").arg(cost).arg(minParkingPrice); - return QDateTime().toString(Qt::ISODate).toStdString(); + return std::make_pair(QDateTime().toString(Qt::ISODate).toStdString(), QDateTime()); } if (cost > maxParkingPrice) { @@ -617,7 +627,7 @@ std::string Calculator::GetDurationFromCost(Configuration* cfg, weekdayId = current.date().dayOfWeek(); if (weekdayId == weekdayIdLast) { qCritical() << "ERROR: NO VALID WORKDAY-TIMES DEFINED"; - return QDateTime().toString(Qt::ISODate).toStdString(); + return std::make_pair(QDateTime().toString(Qt::ISODate).toStdString(), QDateTime()); } } @@ -635,7 +645,10 @@ std::string Calculator::GetDurationFromCost(Configuration* cfg, if (current.time() >= to) { if (carryOverNotSet) { - return end_datetime.toString(Qt::ISODate).toStdString(); + if (overPaid) { + return std::make_pair(CalcState::OVERPAID.toStdString(), end_datetime); + } + return std::make_pair(end_datetime.toString(Qt::ISODate).toStdString(), end_datetime); } else { QDateTime const dt = start; start = start.addDays(1); @@ -682,7 +695,10 @@ std::string Calculator::GetDurationFromCost(Configuration* cfg, << "price" << price; #endif end_datetime = current; - return end_datetime.toString(Qt::ISODate).toStdString(); + if (overPaid) { + return std::make_pair(CalcState::OVERPAID.toStdString(), end_datetime); + } + return std::make_pair(end_datetime.toString(Qt::ISODate).toStdString(), end_datetime); } QTime const &from = QTime::fromString(weekDayWorkTimeRanges[weekDayWorkTimeIndex].pwd_time_from.c_str(), Qt::ISODate); @@ -777,7 +793,10 @@ std::string Calculator::GetDurationFromCost(Configuration* cfg, << "end_datetime" << end_datetime.toString(Qt::ISODate) << "price" << price; #endif - return end_datetime.toString(Qt::ISODate).toStdString(); + if (overPaid) { + return std::make_pair(CalcState::OVERPAID.toStdString(), end_datetime); + } + return std::make_pair(end_datetime.toString(Qt::ISODate).toStdString(), end_datetime); } } @@ -818,7 +837,10 @@ std::string Calculator::GetDurationFromCost(Configuration* cfg, << "end_datetime" << end_datetime.toString(Qt::ISODate) << "price" << price; #endif - return end_datetime.toString(Qt::ISODate).toStdString(); + if (overPaid) { + return std::make_pair(CalcState::OVERPAID.toStdString(), end_datetime); + } + return std::make_pair(end_datetime.toString(Qt::ISODate).toStdString(), end_datetime); } // price has been updated; use next time range @@ -849,7 +871,10 @@ std::string Calculator::GetDurationFromCost(Configuration* cfg, << "price" << price; #endif - return end_datetime.toString(Qt::ISODate).toStdString(); + if (overPaid) { + return std::make_pair(CalcState::OVERPAID.toStdString(), end_datetime); + } + return std::make_pair(end_datetime.toString(Qt::ISODate).toStdString(), end_datetime); } if (moveToNextTimeRange) { @@ -965,8 +990,10 @@ std::string Calculator::GetDurationFromCost(Configuration* cfg, << "end_datetime" << end_datetime.toString(Qt::ISODate) << "price" << price; #endif - - return end_datetime.toString(Qt::ISODate).toStdString(); + if (overPaid) { + return std::make_pair(CalcState::OVERPAID.toStdString(), end_datetime); + } + return std::make_pair(end_datetime.toString(Qt::ISODate).toStdString(), end_datetime); } } @@ -979,7 +1006,10 @@ std::string Calculator::GetDurationFromCost(Configuration* cfg, << "final price" << std::max(price, minParkingPrice); #endif - return end_datetime.toString(Qt::ISODate).toStdString(); + if (overPaid) { + return std::make_pair(CalcState::OVERPAID.toStdString(), end_datetime); + } + return std::make_pair(end_datetime.toString(Qt::ISODate).toStdString(), end_datetime); } // while (timeRangeIt != cfg->TimeRange.cend()) { } @@ -988,7 +1018,7 @@ std::string Calculator::GetDurationFromCost(Configuration* cfg, << "INVALID END TIME"; #endif end_datetime = QDateTime(); - return end_datetime.toString(Qt::ISODate).toStdString(); + return std::make_pair(end_datetime.toString(Qt::ISODate).toStdString(), end_datetime); } Ticket t = private_GetDurationFromCost(cfg, inputDate, cost, prepaid); @@ -996,7 +1026,7 @@ std::string Calculator::GetDurationFromCost(Configuration* cfg, // qCritical().noquote() << t; // TODO: im fehlerfall - return t.getValidUntil().toString(Qt::ISODate).toStdString(); + return std::make_pair(t.getValidUntil().toString(Qt::ISODate).toStdString(), t.getValidUntil()); } #undef _DEBUG_ diff --git a/library/src/configuration.cpp b/library/src/configuration.cpp index 51d8cae..be68e75 100644 --- a/library/src/configuration.cpp +++ b/library/src/configuration.cpp @@ -1110,6 +1110,7 @@ bool Configuration::ParseJson(Configuration* cfg, const char* json) else if (strcmp(inner_obj_name, "pedwt_time_from") == 0) SpecialDaysWorktime.pedwt_time_from = k->value.GetString(); else if (strcmp(inner_obj_name, "pedwt_time_to") == 0) SpecialDaysWorktime.pedwt_time_to = k->value.GetString(); else if (strcmp(inner_obj_name, "pedwt_price") == 0) SpecialDaysWorktime.pedwt_price = k->value.GetDouble(); + else if (strcmp(inner_obj_name, "pedwt_paid") == 0) SpecialDaysWorktime.pedwt_paid = k->value.GetInt(); break; /*case MemberType::WeekDaysType: if (strcmp(inner_obj_name, "pdiw_id") == 0) WeekDays.pdiw_id = k->value.GetInt();