diff --git a/library/src/calculator_functions.cpp b/library/src/calculator_functions.cpp index bbfe323..704b363 100644 --- a/library/src/calculator_functions.cpp +++ b/library/src/calculator_functions.cpp @@ -475,64 +475,64 @@ int Calculator::findNextWorkTimeRange(QDateTime const &dt, using namespace Utilities; -uint32_t Calculator::private_GetCostFromDuration(Configuration const* cfg, - QDateTime const &start, - QDateTime &end, - int durationMinutes, - bool nextDay, - bool prepaid, - bool overtime) { - // TODO - static const PaymentMethod paymentMethodId = PaymentMethod::Linear; +Ticket Calculator::private_GetCostFromDuration(Configuration const* cfg, + QDateTime const &start, + QDateTime &end, + int &durationMinutes, + bool nextDay, + bool prepaid, + bool overtime) { + static const PaymentMethod paymentMethodId = Utilities::getPaymentMethodId(cfg); + static const bool carryOverNotSet = isCarryOverNotSet(cfg, paymentMethodId); static int const minParkingTimeMinutes = getMinimalParkingTime(cfg, paymentMethodId); static int const maxParkingTimeMinutes = getMaximalParkingTime(cfg, paymentMethodId); static bool const checkMinMaxMinutes = (minParkingTimeMinutes < maxParkingTimeMinutes); + static const int durationMinutesNetto = durationMinutes; + if (!checkMinMaxMinutes) { qCritical() << QString( "ERROR: CONDITION minMin < maxMin (%1 < %2) IS NOT VALID") .arg(minParkingTimeMinutes).arg(maxParkingTimeMinutes); - return 0; + return Ticket(); } if (!checkDurationMinutes(overtime, minParkingTimeMinutes, maxParkingTimeMinutes, durationMinutes)) { - return 0; + return Ticket(); } - // Get input date QDateTime inputDate = start; - // Get day of week int const weekdayId = inputDate.date().dayOfWeek(); - - uint32_t day_price = 0; - uint32_t price_per_unit = 0; + uint32_t price = 0; + double durationUnit = 60.0; int current_special_day_id = -1; + // there might be more than 1 worktime ranges per day int const timeRanges = std::max((int)cfg->WeekDaysWorktime.count(weekdayId), 1); QScopedArrayPointer worktime(new TariffTimeRange[timeRanges]); - int index = 0; + int ranges = 0; - if(Utilities::CheckSpecialDay(cfg, inputDate, ¤t_special_day_id, &day_price)) { + if(Utilities::CheckSpecialDay(cfg, inputDate, ¤t_special_day_id, &price)) { // Set special day price: - // ACHTUNG: price_per_unit ist eigentlich immer preis pro minute !!! - price_per_unit = CalculatePricePerUnit(day_price); - worktime[index].setTimeRange(SpecialDaysWorkTimeFrom(cfg, current_special_day_id), - SpecialDaysWorkTimeUntil(cfg, current_special_day_id)); + durationUnit = 60.0; + worktime[ranges].setTimeRange(SpecialDaysWorkTimeFrom(cfg, current_special_day_id), + SpecialDaysWorkTimeUntil(cfg, current_special_day_id)); + ranges = 1; } else { - // Set new price for the normal day - + // Set new price for the normal day: do not use a floating-point type + // for the price, rather compute with integers. Only at the very end of + // the computation the price is divided by durationUnit. int pop_id = cfg->PaymentOption.find(paymentMethodId)->second.pop_id; - day_price = cfg->PaymentRate.find(pop_id)->second.pra_price; + price = cfg->PaymentRate.find(pop_id)->second.pra_price; int durationId = cfg->PaymentRate.find(pop_id)->second.pra_payment_unit_id; - double durationUnit = cfg->Duration.find(durationId)->second.pun_duration; - price_per_unit = Utilities::CalculatePricePerUnit(day_price,durationUnit); + durationUnit = cfg->Duration.find(durationId)->second.pun_duration; // If no working day found, skip it (recursively call method again) if (cfg->WeekDaysWorktime.count(weekdayId) <= 0) { @@ -542,114 +542,162 @@ uint32_t Calculator::private_GetCostFromDuration(Configuration const* cfg, return private_GetCostFromDuration(cfg, inputDate, end, durationMinutes, true, prepaid, overtime); } - for (auto[itr, rangeEnd] = cfg->WeekDaysWorktime.equal_range(weekdayId); itr != rangeEnd; ++itr) { - qCritical() << itr->first << itr->second.pwd_time_from.c_str() << itr->second.pwd_time_to.c_str(); - worktime[index].setTimeRange(QTime::fromString(itr->second.pwd_time_from.c_str()), - QTime::fromString(itr->second.pwd_time_to.c_str())); - index += 1; + using WTIterator = std::multimap::const_iterator; + std::pair p = cfg->WeekDaysWorktime.equal_range(weekdayId); + + for (WTIterator itr = p.first; itr != p.second; ++itr) { + worktime[ranges].setTimeRange(WeekDaysWorkTimeFrom(itr), + WeekDaysWorkTimeUntil(itr)); + ranges += 1; } } - if (price_per_unit < 0) price_per_unit = 1.0f; - qDebug() << "Calculated price per minute=" << price_per_unit; + uint32_t costFromDuration = 0; + QTime const &lastWorktimeTo = worktime[ranges-1].getTimeUntil(); + int currentRange = -1; - double costFromDuration = 0.0; - for (int w = 0; w < index; ++w) { - QTime worktime_from = worktime[w].getTimeFrom(); - QTime worktime_to = worktime[w].getTimeUntil(); - - if (price_per_unit == 0) { - inputDate = inputDate.addDays(1); - inputDate.setTime(worktime_from); - uint32_t const partialCost = private_GetCostFromDuration(cfg, inputDate, end, durationMinutes, true, prepaid); - if (partialCost == 0) { - return 0; + if (nextDay) { // this means the function has been called recursively + currentRange = 0; + inputDate.setTime(worktime[currentRange].getTimeFrom()); + } else { + // check if inputDate is located inside a valid worktime-range... + currentRange = findWorkTimeRange(inputDate, worktime, ranges); + if (currentRange == -1) { // no... + if (!prepaid) { // parking is not allowed + return Ticket(start, end, durationMinutesNetto, 0, + 0, Ticket::s[INVALID_FROM_DATETIME]); } - costFromDuration += partialCost; - continue; + // find the next worktime-range (on the same day), and start from there + currentRange = findNextWorkTimeRange(inputDate, worktime, ranges); + inputDate.setTime(worktime[currentRange].getTimeFrom()); } + } - // If overtime flag is set - if (overtime || nextDay) { - inputDate.setTime(worktime_from); - overtime = false; - } + for (int w = currentRange; w < ranges; ++w) { + if (durationMinutes > 0) { + QTime const &worktime_from = worktime[w].getTimeFrom(); + QTime const &worktime_to = worktime[w].getTimeUntil(); - // Check prepaid - if (!prepaid) { - if ((inputDate.time() < worktime_from) || (inputDate.time() > worktime_to)) { - qDebug() << "[STOP] * Ticket is not valid * "; - return 0.0f; - } - } else { - qDebug() << "* PREPAID MODE ACTIVE *"; - if (inputDate.time() < worktime_from) { - inputDate.setTime(worktime_from); - } else if(inputDate.time() > worktime_to) { - qDebug() << " *** PREPAID *** Current time is past the time range end, searching for next available day"; + qCritical() << "from" << worktime_from; + qCritical() << "until" << worktime_to; + + //if (w > 0) { // durationMinutes are always meant as netto time and + // // the time between worktime-ranges are free. + // inputDate.setTime(worktime_from); + //} + + // TODO: hier muss dann der preis hin + if (price == 0) { inputDate = inputDate.addDays(1); - uint32_t const partialCost = private_GetCostFromDuration(cfg, inputDate, end, durationMinutes, true, prepaid); - if (partialCost == 0) { - return 0; + inputDate.setTime(worktime_from); + Ticket t = private_GetCostFromDuration( + cfg, // TODO: erklaerung + inputDate, + end, durationMinutes, + true, prepaid); + if (!t.isValid()) { + return t; } - costFromDuration += partialCost; + costFromDuration += t.getPrice(); continue; } - } - while(durationMinutes > 0) { - // Check for active year period - if (std::none_of(cfg->YearPeriod.begin(), - cfg->YearPeriod.end(), - [&inputDate](std::pair const &year) { - QDate const input(2004, // 2004 is a leap year - inputDate.date().month(), - inputDate.date().day()); - QDate const s(2004, year.second.pye_start_day, year.second.pye_start_month); - QDate const e(2004, year.second.pye_end_day, year.second.pye_end_month); - return (input >= s && input <= e); - })) { - qCritical() << "NO VALID YEAR PERIOD"; - return 0.0; + // If overtime flag is set + // TODO: ueberpruefen, kann man wohl nach oben ziehen + //if (overtime || nextDay) { + // inputDate.setTime(worktime_from); + // overtime = false; + //} + + qCritical() << "inputDate.time()=" << inputDate.time(); + + // Check prepaid + if (!prepaid) { + if ((inputDate.time() < worktime_from) || (inputDate.time() > worktime_to)) { + qDebug() << "[STOP] * Ticket is not valid * "; + return Ticket(); + } + } else { + qDebug() << "* PREPAID MODE ACTIVE *"; + if (inputDate.time() < worktime_from) { + inputDate.setTime(worktime_from); + //} else if(inputDate.time() > worktime_to) { + } else if(inputDate.time() > lastWorktimeTo) { + qDebug() << " *** PREPAID *** Current time is past the time range end, searching for next available day"; + // wieso ist hier overtime nicht gesetzt + inputDate = inputDate.addDays(1); + Ticket t = private_GetCostFromDuration( + cfg, inputDate, end, + durationMinutes, true, prepaid); + if (!t.isValid()) { + return t; + } + costFromDuration += t.getPrice(); + continue; + } } - // Go to next day if minutes not spent - if(inputDate.time() >= worktime_to) { - // check for carry_over status - if (cfg->PaymentOption.find(paymentMethodId)->second.pop_carry_over < 1) { - break; + while(durationMinutes > 0) { + // Check for active year period + if (!IsYearPeriodActive(cfg, inputDate)) { + return Ticket(); } - qDebug() << "Reached end of worktime, searching for the next working day"; + qDebug() << "inputDate" << inputDate.toString(Qt::ISODate); - inputDate = inputDate.addDays(1); - overtime = true; - uint32_t const partialCost = private_GetCostFromDuration(cfg, inputDate, end, durationMinutes, true, prepaid, overtime); - if (partialCost == 0) { - return 0; + if(inputDate.time() >= lastWorktimeTo) { + // Go to next day if minutes not spent + if (carryOverNotSet) { + // no carry_over, so stop computation + break; + } + + qDebug() << "Reached end of worktime, searching for the next working day"; + + inputDate = inputDate.addDays(1); + inputDate.setTime(QTime()); + overtime = true; + Ticket t = private_GetCostFromDuration( + cfg, inputDate, end, + durationMinutes, true, prepaid, overtime); + if (!t.isValid()) { + return t; + } + costFromDuration += t.getPrice(); + break; // stop while, and continue in outer loop + } else { + if(inputDate.time() < worktime_to) { + // Increment input date minutes for each monetary unit + inputDate = inputDate.addSecs(60); + + qDebug() << "inputDate" << inputDate.toString(Qt::ISODate); + + durationMinutes -= 1; + //costFromDuration += price_per_unit; + costFromDuration += price; + } else break; } - costFromDuration += partialCost; - break; // stop while, and continue in outer loop - } else { - // Increment input date minutes for each monetary unit - inputDate = inputDate.addSecs(60); - durationMinutes -= 1; - costFromDuration += price_per_unit; } } } - qDebug() << "GetCostFromDuration(): Valid until:" << inputDate.toString(Qt::ISODate); - end = inputDate; + if (inputDate >= end) { + end = inputDate; + } - //double ret_val = total_cost; - //total_cost = 0.0f; - //return ceil(ret_val); + if (nextDay == false) { + qDebug() << "GetCostFromDuration(): Valid until:" << end.toString(Qt::ISODate); + } - // TODO: runden nur falls oberster stack-rahmen - - return ceil(costFromDuration); + return + Ticket(start, end, + durationMinutesNetto, + start.secsTo(end) / 60, + nextDay ? + costFromDuration : + llround(Utilities::CalculatePricePerUnit(costFromDuration, durationUnit)), + Ticket::s[VALID]); }