From abbbd06f9322fecd45155da7aae03de6275924c0 Mon Sep 17 00:00:00 2001 From: Gerhard Hoffmann Date: Tue, 27 Feb 2024 12:25:13 +0100 Subject: [PATCH] GetCostFromDuration(): re-implement cpmputation for paymentMethod==progressive (e.g. neuhauser/kirchdorf (743)) --- library/src/calculator_functions.cpp | 404 +++++++++++++++++++-------- 1 file changed, 284 insertions(+), 120 deletions(-) diff --git a/library/src/calculator_functions.cpp b/library/src/calculator_functions.cpp index 75958f4..a0c31ff 100644 --- a/library/src/calculator_functions.cpp +++ b/library/src/calculator_functions.cpp @@ -522,6 +522,10 @@ double Calculator::GetCostFromDuration(Configuration* cfg, int weekdayIdLast = -1; int durationMinutesBrutto = 0; + +//#define _DEBUG_ 1 +#define _DEBUG_ 0 + QDateTime current = start; int days = 7; @@ -571,149 +575,309 @@ double Calculator::GetCostFromDuration(Configuration* cfg, uint price = 0; if (carryOverNotSet) { - int range = 0; int minsToCarryOver = 0; // from one work-time to the other on the same day int minsUsed = 0; QDateTime lastCurrent = QDateTime(); + QVector weekDayWorkTimeRanges; + + using WTIterator = std::multimap::const_iterator; + std::pair p = cfg->WeekDaysWorktime.equal_range(weekdayId); + + for (WTIterator itr = p.first; itr != p.second; ++itr) { + weekDayWorkTimeRanges.append(itr->second); // working with vector is easier + } + + int weekDayWorkTimeIndex = 0; + bool moveToNextTimeRange = false; + + // time ranges for Neuhauser-Kirchdorf (743): 30, 5, 5, ... 5 auto timeRangeIt = cfg->TimeRange.cbegin(); - for (; timeRangeIt != cfg->TimeRange.cend(); ++timeRangeIt) { - using WTIterator = std::multimap::const_iterator; - std::pair p = cfg->WeekDaysWorktime.equal_range(weekdayId); + while (timeRangeIt != cfg->TimeRange.cend()) { // ; ++timeRangeIt) { - for (WTIterator itr = p.first; itr != p.second; ++itr) { - ++range; + if (weekDayWorkTimeIndex >= weekDayWorkTimeRanges.size()) { - QTime const &from = Utilities::WeekDaysWorkTimeFrom(itr); - QTime const &to = Utilities::WeekDaysWorkTimeUntil(itr); +#if _DEBUG_==1 + qCritical() << "(" << __func__ << ":" << __LINE__ << ")" + << "weekDayWorkTimeIndex" << weekDayWorkTimeIndex + << "weekDayWorkTimeRanges.size()" << weekDayWorkTimeRanges.size(); +#endif + end_datetime = current; + return price; + } - Q_ASSERT_X(from < to, __func__, "MISCONFIGURED WORK-TIMES"); + QTime const &from = QTime::fromString(weekDayWorkTimeRanges[weekDayWorkTimeIndex].pwd_time_from.c_str(), Qt::ISODate); + QTime const &to = QTime::fromString(weekDayWorkTimeRanges[weekDayWorkTimeIndex].pwd_time_to.c_str(), Qt::ISODate); - if (current.time() >= to) { - continue; // try to use next available work-time - } else - if (current.time() <= from) { - if (prepaid) { - lastCurrent = current; - current.setTime(from); // move current forward (range==1), - // as prepaid is set - uint const minutesMoved = lastCurrent.secsTo(current) / 60; - durationMinutesBrutto += minutesMoved; +#if _DEBUG_==1 + qCritical() << "(" << __func__ << ":" << __LINE__ << ")" + << "current" << current.toString(Qt::ISODate) + << "from" << from.toString(Qt::ISODate) + << "to" << to.toString(Qt::ISODate); +#endif - if (range == 1) { - start_datetime = current; - } - } - } + Q_ASSERT_X(from < to, __func__, "MISCONFIGURED WORK-TIMES"); - while (timeRangeIt != cfg->TimeRange.cend()) { - ATBTimeRange timeRange = timeRangeIt->second; + if (current.time() >= to) { + ++weekDayWorkTimeIndex; - timeRange.computeQTimes(current.time()); +#if _DEBUG_==1 + qCritical() << "(" << __func__ << ":" << __LINE__ << ")" + << "try to use next available work-time with" + << "weekDayWorkTimeIndex" << weekDayWorkTimeIndex + << "price" << price; +#endif + // time range is not affected + continue; + } else + if (current.time() <= from) { + if (prepaid) { + lastCurrent = current; + current.setTime(from); // move current forward (range==1), + // as prepaid is set + uint const minutesMoved = lastCurrent.secsTo(current) / 60; + durationMinutesBrutto += minutesMoved; - int duration = timeRange.time_range_to_in_minutes_from_start - - timeRange.time_range_from_in_minutes_from_start; - - qCritical() << __func__ << ":" << __LINE__ +#if _DEBUG_==1 + qCritical() << "(" << __func__ << ":" << __LINE__ << ")" + << "lastCurrent" << lastCurrent.toString(Qt::ISODate) << "current" << current.toString(Qt::ISODate) - << "duration" << duration - << "minsUsed" << minsUsed - << "minsToCarryOver" << minsToCarryOver; + << "minutesMoved" << minutesMoved + << "durationMinutesBrutto" << durationMinutesBrutto; +#endif - - //if (minsUsed > 0) { - // duration -= minsUsed; - // minsUsed = 0; - //} - - if (current.addSecs(duration * 60).time() <= to) { - if (minsToCarryOver > 0) { // the price for this time range - // has been is paid already - if (minsUsed == 0) { - for(const auto &x: cfg->PaymentRate) { - ATBPaymentRate const rate = x.second; - if (rate.pra_payment_unit_id == timeRange.time_range_payment_type_id) { - price += (uint)rate.pra_price; - - durationMinutes -= duration; - durationMinutesNetto += duration; - durationMinutesBrutto += duration; - - current = current.addSecs(duration * 60); - - if (durationMinutes <= 0) { - end_datetime = current; - return price; - } - - break; - } - } - } else { - durationMinutes -= duration; - durationMinutesNetto += duration; - durationMinutesBrutto += duration; - current = current.addSecs(duration*60); - minsToCarryOver = 0; - } - } else { - for(const auto &x: cfg->PaymentRate) { - ATBPaymentRate const rate = x.second; - if (rate.pra_payment_unit_id == timeRange.time_range_payment_type_id) { - price += (uint)rate.pra_price; - - durationMinutes -= duration; - durationMinutesNetto += duration; - durationMinutesBrutto += duration; - - current = current.addSecs(duration * 60); - break; - } - } - } - - if (durationMinutes <= 0) { - end_datetime = current; - return price; - } - - ++timeRangeIt; - - } else { - - lastCurrent = current; - current.setTime(to); - minsUsed = lastCurrent.secsTo(current) / 60; - - // mod duration: possibly discard some minutes in - // the next time-range - if (durationMinutes >= minsUsed) { - minsToCarryOver = durationMinutes - minsUsed; - } - - durationMinutes -= minsUsed; - durationMinutesNetto += minsUsed; - durationMinutesBrutto += minsUsed; - - if (minsUsed > 0) { - for(const auto &x: cfg->PaymentRate) { - ATBPaymentRate const rate = x.second; - if (rate.pra_payment_unit_id == timeRange.time_range_payment_type_id) { - price += (uint)rate.pra_price; - break; - } - } - } - break; + if (weekDayWorkTimeIndex == 0) { + start_datetime = current; } } } + ATBTimeRange timeRange = timeRangeIt->second; + + timeRange.computeQTimes(current.time()); + + int duration = timeRange.time_range_to_in_minutes_from_start - + timeRange.time_range_from_in_minutes_from_start; + +#if _DEBUG_==1 + qCritical() << "(" << __func__ << ":" << __LINE__ << ")" + << "current" << current.toString(Qt::ISODate) + << "duration" << duration + << "minsUsed" << minsUsed + << "minsToCarryOver" << minsToCarryOver + << "weekDayWorkTimeIndex" << weekDayWorkTimeIndex + << "price" << price; +#endif + + if (minsToCarryOver > 0) { // the price for this time range + // has been is paid already + Q_ASSERT_X(weekDayWorkTimeIndex > 0, __func__, "WRONG-WORK-TIME"); + + current = current.addSecs(minsToCarryOver*60); + + durationMinutes -= minsToCarryOver; + durationMinutesNetto += minsToCarryOver; + durationMinutesBrutto += minsToCarryOver; + + minsToCarryOver = 0; + +#if _DEBUG_==1 + qCritical() << "(" << __func__ << ":" << __LINE__ << ")" + << "current" << current.toString(Qt::ISODate) + << "duration" << duration + << "durationMinutes" << durationMinutes + << "durationMinutesNetto" << durationMinutesNetto + << "durationMinutesBrutto" << durationMinutesBrutto; +#endif + if (durationMinutes <= 0) { + end_datetime = current; + +#if _DEBUG_==1 + qCritical() << "(" << __func__ << ":" << __LINE__ << ")" + << "end_datetime" << end_datetime.toString(Qt::ISODate) + << "price" << price; +#endif + + return price; + } + } + + if (current.addSecs(duration * 60).time() <= to) { + +#if _DEBUG_==1 + qCritical() << "(" << __func__ << ":" << __LINE__ << ")" + << "current" << current.toString(Qt::ISODate) + << "current.addSecs(" << duration * 60 << ")" << current.addSecs(duration*60).time().toString(Qt::ISODate) + << "duration" << duration + << "to" << to.toString(Qt::ISODate) + << "minsUsed" << minsUsed + << "minsToCarryOver" << minsToCarryOver + << "weekDayWorkTimeIndex" << weekDayWorkTimeIndex + << "durationMinutes" << durationMinutes; +#endif + + moveToNextTimeRange = false; + for(const auto &x: cfg->PaymentRate) { + ATBPaymentRate const rate = x.second; + if (rate.pra_payment_unit_id == timeRange.time_range_payment_type_id) { + price += (uint)rate.pra_price; + + durationMinutes -= duration; + durationMinutesNetto += duration; + durationMinutesBrutto += duration; + + current = current.addSecs(duration * 60); + + // price has been updated; use next time range + moveToNextTimeRange = true; + break; + } + } + +#if _DEBUG_==1 + qCritical() << "(" << __func__ << ":" << __LINE__ << ")" + << "current" << current.toString(Qt::ISODate) + << "duration" << duration + << "to" << to.toString(Qt::ISODate) + << "price" << price + << "minsToCarryOver" << minsToCarryOver + << "weekDayWorkTimeIndex" << weekDayWorkTimeIndex + << "durationMinutes" << durationMinutes + << "durationMinutesNetto" << durationMinutesNetto + << "durationMinutesBrutto" << durationMinutesBrutto; +#endif + + if (durationMinutes <= 0) { + end_datetime = current; + +#if _DEBUG_==1 + qCritical() << "(" << __func__ << ":" << __LINE__ << ")" + << "end_datetime" << end_datetime.toString(Qt::ISODate) + << "price" << price; +#endif + + return price; + } + + if (moveToNextTimeRange) { + if (++timeRangeIt != cfg->TimeRange.cend()) { + continue; + } + } + + // havin a new time range means that we always have a new + // work-time-range + // ++timeRangeIt; + + } else { // current.addSecs(duration * 60).time() > to + +#if _DEBUG_==1 + qCritical() << "(" << __func__ << ":" << __LINE__ << ")" + << "current" << current.toString(Qt::ISODate) + << "current.addSecs(" << duration * 60 << ")" << current.addSecs(duration*60).time().toString(Qt::ISODate) + << "duration" << duration + << ", to:" << to.toString(Qt::ISODate); +#endif + + lastCurrent = current; + current.setTime(to); + minsUsed = lastCurrent.secsTo(current) / 60; + +#if _DEBUG_==1 + qCritical() << "(" << __func__ << ":" << __LINE__ << ")" + << "lastCurrent" << lastCurrent.toString(Qt::ISODate) + << "current" << current.toString(Qt::ISODate) + << "duration" << duration + << "to" << to.toString(Qt::ISODate) + << "minsUsed" << minsUsed + << "minsToCarryOver" << minsToCarryOver + << "weekDayWorkTimeIndex" << weekDayWorkTimeIndex + << "durationMinutes" << durationMinutes; +#endif + + // mod duration: possibly discard some minutes in + // the next time-range + if (duration >= minsUsed) { + minsToCarryOver = duration - minsUsed; + } + + durationMinutes -= minsUsed; + durationMinutesNetto += minsUsed; + durationMinutesBrutto += minsUsed; + +#if _DEBUG_==1 + qCritical() << "(" << __func__ << ":" << __LINE__ << ")" + << "lastCurrent" << lastCurrent.toString(Qt::ISODate) + << "current" << current.toString(Qt::ISODate) + << "duration" << duration + << "to" << to.toString(Qt::ISODate) + << "minsUsed" << minsUsed + << "minsToCarryOver" << minsToCarryOver + << "weekDayWorkTimeIndex" << weekDayWorkTimeIndex + << "durationMinutes" << durationMinutes + << "price" << price; +#endif + + moveToNextTimeRange = false; + + if (minsUsed > 0) { + for(const auto &x: cfg->PaymentRate) { + ATBPaymentRate const rate = x.second; + if (rate.pra_payment_unit_id == timeRange.time_range_payment_type_id) { + price += (uint)rate.pra_price; + + // price has been updated; use next time range + moveToNextTimeRange = true; + break; + } + } + } + +#if _DEBUG_==1 + qCritical() << "(" << __func__ << ":" << __LINE__ << ")" + << "current" << current.toString(Qt::ISODate) + << "duration" << duration + << "minsUsed" << minsUsed + << "durationMinutes" << durationMinutes + << "moveToNextTimeRange" << moveToNextTimeRange + << "price" << price; +#endif + + if (moveToNextTimeRange) { + if (++timeRangeIt != cfg->TimeRange.cend()) { + continue; + } + + // no valid time range left + end_datetime = current; + +#if _DEBUG_==1 + qCritical() << "(" << __func__ << ":" << __LINE__ << ")" + << "end_datetime" << end_datetime.toString(Qt::ISODate) + << "price" << price; +#endif + + return price; + } + } + end_datetime = start.addSecs(durationMinutesBrutto * 60); + +#if _DEBUG_==1 + qCritical() << "(" << __func__ << ":" << __LINE__ << ")" + << "start" << start.toString(Qt::ISODate) + << "end_datetime" << end_datetime.toString(Qt::ISODate) + << "final price" << std::max(price, minParkingPrice); +#endif + return std::max(price, minParkingPrice); - } + } // while (timeRangeIt != cfg->TimeRange.cend()) { } +#if _DEBUG_==1 + qCritical() << "(" << __func__ << ":" << __LINE__ << ")" + << "INVALID END TIME"; +#endif end_datetime = QDateTime(); return 0; }