Compare commits
	
		
			32 Commits
		
	
	
		
			0.0.1
			...
			moransBran
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 6ea58be34d | |||
| 8a7828c1e6 | |||
| 7bd7f66666 | |||
| 617eee39ed | |||
| 6b3c1cbf0c | |||
| 1142efaec2 | |||
| 2f8c8cab4c | |||
| 2599513ef9 | |||
| ef66c1f0c0 | |||
| 219d820104 | |||
| ed9166c226 | |||
| 818c67ad63 | |||
| 
						
						
							
						
						4a076e1dad
	
				 | 
					
					
						|||
| 
						
						
							
						
						62426e60d5
	
				 | 
					
					
						|||
| 
						
						
							
						
						c28351b1bf
	
				 | 
					
					
						|||
| 
						
						
							
						
						69c469c918
	
				 | 
					
					
						|||
| e7606b6568 | |||
| 
						
						
							
						
						fe0ebb409a
	
				 | 
					
					
						|||
| 
						
						
							
						
						3d88c8e67e
	
				 | 
					
					
						|||
| eebb6d926e | |||
| 69c48e3acc | |||
| 
						
						
							
						
						5c0bbf1502
	
				 | 
					
					
						|||
| 
						
						
							
						
						31df676b57
	
				 | 
					
					
						|||
| 889fceaae0 | |||
| 
						
						
							
						
						5ec2a6d730
	
				 | 
					
					
						|||
| 136e6687b8 | |||
| 
						
						
							
						
						3cc447ca87
	
				 | 
					
					
						|||
| b02115bf75 | |||
| ceb545b432 | |||
| 01f2dc07ce | |||
| c5960a031e | |||
| 1c03745333 | 
@@ -4,6 +4,9 @@
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
 | 
			
		||||
#include <QString>
 | 
			
		||||
#include <QDateTime>
 | 
			
		||||
 | 
			
		||||
#ifdef WIN32
 | 
			
		||||
#ifdef CALCULATE_LIBRARY_EXPORTS
 | 
			
		||||
#define CALCULATE_LIBRARY_API __declspec(dllexport)
 | 
			
		||||
@@ -18,9 +21,9 @@ class Configuration;
 | 
			
		||||
 | 
			
		||||
typedef Configuration parking_tariff_t;
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
//#ifdef __cplusplus
 | 
			
		||||
//extern "C" {
 | 
			
		||||
//#endif
 | 
			
		||||
 | 
			
		||||
struct CALCULATE_LIBRARY_API price_t {
 | 
			
		||||
    uint32_t units;
 | 
			
		||||
@@ -30,16 +33,117 @@ struct CALCULATE_LIBRARY_API price_t {
 | 
			
		||||
    double vat;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
bool CALCULATE_LIBRARY_API init_tariff(parking_tariff_t **tariff, char const *config_file);
 | 
			
		||||
void CALCULATE_LIBRARY_API free_tariff(parking_tariff_t *tariff);
 | 
			
		||||
int CALCULATE_LIBRARY_API get_zone_nr();
 | 
			
		||||
struct CALCULATE_LIBRARY_API CalcState {
 | 
			
		||||
    enum class State : uint8_t {
 | 
			
		||||
        SUCCESS,
 | 
			
		||||
        ERROR_PARSING_ZONE_NR,
 | 
			
		||||
        ERROR_LOADING_TARIFF,
 | 
			
		||||
        ERROR_PARSING_TARIFF,
 | 
			
		||||
        NEGATIVE_PARING_TIME,
 | 
			
		||||
        INVALID_START_DATE,
 | 
			
		||||
        WRONG_PARAM_VALUES,
 | 
			
		||||
        WRONG_ISO_TIME_FORMAT,
 | 
			
		||||
        ABOVE_MAX_PARKING_TIME,
 | 
			
		||||
        BELOW_MIN_PARKING_TIME,
 | 
			
		||||
        BELOW_MIN_PARKING_PRICE,
 | 
			
		||||
        OVERPAID
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
bool CALCULATE_LIBRARY_API compute_price_for_parking_ticket(parking_tariff_t *tariff,
 | 
			
		||||
                                      time_t start_parking_time,
 | 
			
		||||
                                      time_t end_parking_time,
 | 
			
		||||
                                      struct price_t *price);
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
} // extern "C"
 | 
			
		||||
#endif
 | 
			
		||||
    State m_status;
 | 
			
		||||
    QString m_desc;
 | 
			
		||||
 | 
			
		||||
    explicit CalcState() : m_status(State::SUCCESS), m_desc("") {}
 | 
			
		||||
 | 
			
		||||
    explicit operator bool() const noexcept {
 | 
			
		||||
        return (m_status == State::SUCCESS);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    explicit operator QString () const noexcept {
 | 
			
		||||
        QString s;
 | 
			
		||||
        switch (m_status) {
 | 
			
		||||
        case State::SUCCESS:
 | 
			
		||||
            s = "SUCCESS";
 | 
			
		||||
            break;
 | 
			
		||||
        case State::ERROR_PARSING_ZONE_NR:
 | 
			
		||||
            s = "ERROR_PARSING_ZONE_NR";
 | 
			
		||||
            break;
 | 
			
		||||
        case State::ERROR_LOADING_TARIFF:
 | 
			
		||||
            s = "ERROR_LOADING_TARIFF";
 | 
			
		||||
            break;
 | 
			
		||||
        case State::ERROR_PARSING_TARIFF:
 | 
			
		||||
            s = "ERROR_PARSING_TARIFF";
 | 
			
		||||
            break;
 | 
			
		||||
        case State::NEGATIVE_PARING_TIME:
 | 
			
		||||
            s = "NEGATIVE_PARKING_TIME";
 | 
			
		||||
            break;
 | 
			
		||||
        case State::ABOVE_MAX_PARKING_TIME:
 | 
			
		||||
            s = "ABOVE_MAX_PARKING_TIME";
 | 
			
		||||
            break;
 | 
			
		||||
        case State::WRONG_PARAM_VALUES:
 | 
			
		||||
            s = "WRONG_PARAM_VALUES";
 | 
			
		||||
            break;
 | 
			
		||||
        case State::BELOW_MIN_PARKING_TIME:
 | 
			
		||||
            s = "BELOW_MIN_PARKING_TIME";
 | 
			
		||||
            break;
 | 
			
		||||
        case State::BELOW_MIN_PARKING_PRICE:
 | 
			
		||||
            s = "BELOW_MIN_PARKING_PRICE";
 | 
			
		||||
            break;
 | 
			
		||||
        case State::OVERPAID:
 | 
			
		||||
            s = "OVERPAID";
 | 
			
		||||
            break;
 | 
			
		||||
        case State::INVALID_START_DATE:
 | 
			
		||||
            s = "INVALID_START_DATE";
 | 
			
		||||
            break;
 | 
			
		||||
        case State::WRONG_ISO_TIME_FORMAT:
 | 
			
		||||
            s = "WRONG_ISO_TIME_FORMAT";
 | 
			
		||||
        }
 | 
			
		||||
        return s + ":" + m_desc;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    CalcState &set(State s) { m_status = s; return *this; }
 | 
			
		||||
    CalcState &setDesc(QString s) { m_desc = s; return *this; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
CalcState CALCULATE_LIBRARY_API init_tariff(parking_tariff_t **tariff,
 | 
			
		||||
                                            char const *config_file);
 | 
			
		||||
void CALCULATE_LIBRARY_API free_tariff(parking_tariff_t *tariff);
 | 
			
		||||
int CALCULATE_LIBRARY_API get_zone_nr(int zone = -1);
 | 
			
		||||
 | 
			
		||||
CalcState CALCULATE_LIBRARY_API compute_price_for_parking_ticket(
 | 
			
		||||
                                            parking_tariff_t *tariff,
 | 
			
		||||
                                            time_t start_parking_time,
 | 
			
		||||
                                            time_t end_parking_time,
 | 
			
		||||
                                            struct price_t *price);
 | 
			
		||||
 | 
			
		||||
CalcState CALCULATE_LIBRARY_API compute_price_for_parking_ticket(
 | 
			
		||||
                                            parking_tariff_t *tariff,
 | 
			
		||||
                                            QDateTime const &start_parking_time,
 | 
			
		||||
                                            QDateTime const &end_parking_time,
 | 
			
		||||
                                            struct price_t *price);
 | 
			
		||||
 | 
			
		||||
CalcState CALCULATE_LIBRARY_API compute_duration_for_parking_ticket(
 | 
			
		||||
                                            parking_tariff_t *tariff,
 | 
			
		||||
                                            time_t start_parking_time,
 | 
			
		||||
                                            double cost,
 | 
			
		||||
                                            QString &duration);
 | 
			
		||||
 | 
			
		||||
CalcState CALCULATE_LIBRARY_API compute_duration_for_parking_ticket(
 | 
			
		||||
                                            parking_tariff_t *tariff,
 | 
			
		||||
                                            QDateTime const &start_parking_time,
 | 
			
		||||
                                            double cost,
 | 
			
		||||
                                            QDateTime &ticketEndTime);
 | 
			
		||||
 | 
			
		||||
CalcState CALCULATE_LIBRARY_API compute_duration_for_daily_ticket(
 | 
			
		||||
                                            parking_tariff_t *tariff,
 | 
			
		||||
                                            QString const &start_parking_time,
 | 
			
		||||
                                            uint8_t paymentMethod);
 | 
			
		||||
 | 
			
		||||
CalcState CALCULATE_LIBRARY_API compute_duration_for_24hour_daily_ticket(
 | 
			
		||||
                                            parking_tariff_t *tariff,
 | 
			
		||||
                                            QString const &start_parking_time,
 | 
			
		||||
                                            uint8_t paymentMethod);
 | 
			
		||||
//#ifdef __cplusplus
 | 
			
		||||
//} // extern "C"
 | 
			
		||||
//#endif
 | 
			
		||||
 | 
			
		||||
#endif // CALCULATE_PRICE_H
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include "configuration.h"
 | 
			
		||||
 | 
			
		||||
#include <QDateTime>
 | 
			
		||||
using namespace std;
 | 
			
		||||
 | 
			
		||||
class Calculator
 | 
			
		||||
@@ -26,4 +26,10 @@ public:
 | 
			
		||||
	/// <param name="durationMin">Duration of parking in minutes</param>
 | 
			
		||||
	/// <returns>Returns cost (data type: double)</returns>
 | 
			
		||||
	double GetCostFromDuration(Configuration* cfg, uint8_t vehicle_type, char const* start_datetime, double durationMin, bool nextDay = false, bool prepaid = false);
 | 
			
		||||
 | 
			
		||||
    // Daily ticket
 | 
			
		||||
    QString GetDailyTicketDuration(Configuration* cfg, QString start_datetime, uint8_t payment_option, bool carry_over);
 | 
			
		||||
 | 
			
		||||
    // 24-hour daily ticket
 | 
			
		||||
    QString Get24HourTicketDuration(Configuration* cfg, QString start_datetime, uint8_t payment_option);
 | 
			
		||||
};
 | 
			
		||||
@@ -14,4 +14,6 @@ public:
 | 
			
		||||
	double pop_max_time;
 | 
			
		||||
	double pop_min_price;
 | 
			
		||||
	int pop_carry_over;
 | 
			
		||||
    int pop_daily_card_price;
 | 
			
		||||
    int pop_multi_hour_price;
 | 
			
		||||
};
 | 
			
		||||
@@ -68,5 +68,6 @@ public:
 | 
			
		||||
	/// </summary>
 | 
			
		||||
	/// <param name="pra_price"></param>
 | 
			
		||||
	/// <returns></returns>
 | 
			
		||||
	static double CalculatePricePerUnit(double pra_price);
 | 
			
		||||
    static double CalculatePricePerUnit(double pra_price, double durationUnit = -1);
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
TEMPLATE = lib
 | 
			
		||||
TARGET = mobilisis_calc
 | 
			
		||||
# CONFIG += staticlib
 | 
			
		||||
#CONFIG += staticlib
 | 
			
		||||
 | 
			
		||||
QMAKE_CXXFLAGS += -std=c++17 -g -O0
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -10,25 +10,31 @@
 | 
			
		||||
 | 
			
		||||
static Calculator calculator;
 | 
			
		||||
 | 
			
		||||
int CALCULATE_LIBRARY_API get_zone_nr() {
 | 
			
		||||
    QFile zone("/etc/zone_nr");
 | 
			
		||||
    if (zone.exists()) {
 | 
			
		||||
        QFileInfo finfo(zone);
 | 
			
		||||
        if (finfo.size() <= 4) { // decimal 000\n
 | 
			
		||||
            if (zone.open(QIODevice::ReadOnly | QIODevice::Text)) {
 | 
			
		||||
 | 
			
		||||
                QTextStream in(&zone);
 | 
			
		||||
                return in.readLine(100).toInt();
 | 
			
		||||
 | 
			
		||||
int CALCULATE_LIBRARY_API get_zone_nr(int zone)
 | 
			
		||||
{
 | 
			
		||||
    if(zone > -1) return zone;
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        QFile zone("/etc/zone_nr");
 | 
			
		||||
        if (zone.exists()) {
 | 
			
		||||
            QFileInfo finfo(zone);
 | 
			
		||||
            if (finfo.size() <= 4) { // decimal 000\n
 | 
			
		||||
                if (zone.open(QIODevice::ReadOnly | QIODevice::Text)) {
 | 
			
		||||
                    QTextStream in(&zone);
 | 
			
		||||
                    return in.readLine(100).toInt();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool CALCULATE_LIBRARY_API init_tariff(parking_tariff_t **tariff, char const *config_file) {
 | 
			
		||||
CalcState CALCULATE_LIBRARY_API init_tariff(parking_tariff_t **tariff, char const *config_file) {
 | 
			
		||||
    *tariff = new Configuration();
 | 
			
		||||
 | 
			
		||||
    CalcState calcState;
 | 
			
		||||
#if __linux__
 | 
			
		||||
 | 
			
		||||
    int const zone = get_zone_nr();
 | 
			
		||||
 | 
			
		||||
    // DEBUG
 | 
			
		||||
@@ -36,7 +42,9 @@ bool CALCULATE_LIBRARY_API init_tariff(parking_tariff_t **tariff, char const *co
 | 
			
		||||
    qCritical() << "      ... zone = " << zone;
 | 
			
		||||
 | 
			
		||||
    if (zone <= 0) {
 | 
			
		||||
        return false;
 | 
			
		||||
        delete *tariff;
 | 
			
		||||
        *tariff = nullptr;
 | 
			
		||||
        return calcState.set(CalcState::State::ERROR_PARSING_ZONE_NR);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    QString confFile(config_file);
 | 
			
		||||
@@ -48,62 +56,240 @@ bool CALCULATE_LIBRARY_API init_tariff(parking_tariff_t **tariff, char const *co
 | 
			
		||||
    memset(buffer, 0x00, sizeof(buffer));
 | 
			
		||||
    snprintf(buffer, sizeof(buffer)-1, "tariff%02d.json", zone);
 | 
			
		||||
    confFile += buffer;
 | 
			
		||||
#else // windows
 | 
			
		||||
    QString confFile(config_file);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    // DEBUG
 | 
			
		||||
    qCritical() << "      ... confFile = " << confFile;
 | 
			
		||||
 | 
			
		||||
    QFile fname(confFile);
 | 
			
		||||
    if (fname.exists()) {
 | 
			
		||||
 | 
			
		||||
    if (fname.exists() &&
 | 
			
		||||
            fname.open(QIODevice::ReadOnly | QIODevice::Text)) {
 | 
			
		||||
        // DEBUG
 | 
			
		||||
        qCritical() << "      ... confFile exists";
 | 
			
		||||
        qCritical() << "      ... confFile is open";
 | 
			
		||||
 | 
			
		||||
        if (fname.open(QIODevice::ReadOnly | QIODevice::Text)) {
 | 
			
		||||
 | 
			
		||||
            // DEBUG
 | 
			
		||||
            qCritical() << "      ... confFile is open";
 | 
			
		||||
 | 
			
		||||
            QString json = fname.readAll();
 | 
			
		||||
            return (*tariff)->ParseJson(*tariff, json.toStdString().c_str());
 | 
			
		||||
        QString json = fname.readAll();
 | 
			
		||||
        if (! (*tariff)->ParseJson(*tariff, json.toStdString().c_str())) {
 | 
			
		||||
            delete *tariff;
 | 
			
		||||
            *tariff = nullptr;
 | 
			
		||||
            return calcState.set(CalcState::State::ERROR_PARSING_TARIFF);
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        delete *tariff;
 | 
			
		||||
        *tariff = nullptr;
 | 
			
		||||
        return calcState.set(CalcState::State::ERROR_LOADING_TARIFF);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qCritical() << "init_tariff: Parsing tariff config (" << confFile << ") failed!";
 | 
			
		||||
    qCritical() << "init_tariff: Parsing tariff config (" << confFile << ")";
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
    return calcState;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CALCULATE_LIBRARY_API free_tariff(parking_tariff_t *tariff) {
 | 
			
		||||
    delete tariff;
 | 
			
		||||
    if (tariff != nullptr) {
 | 
			
		||||
        delete tariff;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool CALCULATE_LIBRARY_API compute_price_for_parking_ticket(
 | 
			
		||||
                                      parking_tariff_t *tariff,
 | 
			
		||||
                                      time_t start_parking_time, // in minutes
 | 
			
		||||
                                      time_t end_parking_time,   // in minutes
 | 
			
		||||
                                      struct price_t *price) {
 | 
			
		||||
CalcState CALCULATE_LIBRARY_API compute_price_for_parking_ticket(
 | 
			
		||||
        parking_tariff_t *tariff,
 | 
			
		||||
        time_t start_parking_time, // in minutes
 | 
			
		||||
        time_t end_parking_time,   // in minutes
 | 
			
		||||
        struct price_t *price) {
 | 
			
		||||
    CalcState calcState;
 | 
			
		||||
    double minMin = tariff->PaymentOption.find(PaymentOption::Option1)->second.pop_min_time;
 | 
			
		||||
    double maxMin = tariff->PaymentOption.find(PaymentOption::Option1)->second.pop_max_time;
 | 
			
		||||
 | 
			
		||||
    if (minMin < 0 || maxMin < 0 || maxMin < minMin) {
 | 
			
		||||
        calcState.setDesc(QString("minMin=%1, maxMin=%2").arg(minMin).arg(maxMin));
 | 
			
		||||
        return calcState.set(CalcState::State::WRONG_PARAM_VALUES);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int const duration = end_parking_time - start_parking_time;
 | 
			
		||||
    if (duration > 0) {
 | 
			
		||||
        QDate const d(1970, 1, 1);
 | 
			
		||||
        QTime const t(0, 0, 0);
 | 
			
		||||
        QDateTime start(d, t, Qt::UTC);
 | 
			
		||||
        start = start.toLocalTime().addSecs(start_parking_time * 60);
 | 
			
		||||
        if (start.isValid()) {
 | 
			
		||||
            QString cs = start.toString(Qt::ISODate);
 | 
			
		||||
            double cost = calculator.GetCostFromDuration(
 | 
			
		||||
                            tariff, PaymentOption::Option1,
 | 
			
		||||
                            cs.toLocal8Bit().constData(),
 | 
			
		||||
                            duration, false, true);
 | 
			
		||||
            price->units = cost;
 | 
			
		||||
            price->netto = cost;
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    } else
 | 
			
		||||
    if (duration < 0) {
 | 
			
		||||
        calcState.setDesc(QString("end=%1, start=%2")
 | 
			
		||||
                          .arg(end_parking_time, start_parking_time));
 | 
			
		||||
        return calcState.set(CalcState::State::NEGATIVE_PARING_TIME);
 | 
			
		||||
    }
 | 
			
		||||
    if (duration > maxMin) {
 | 
			
		||||
        calcState.setDesc(QString("duration=%1, maxMin=%2").arg(duration).arg(maxMin));
 | 
			
		||||
        return calcState.set(CalcState::State::ABOVE_MAX_PARKING_TIME);
 | 
			
		||||
    }
 | 
			
		||||
    if (duration < minMin) {
 | 
			
		||||
        calcState.setDesc(QString("duration=%1, minMin=%2").arg(duration).arg(minMin));
 | 
			
		||||
        return calcState.set(CalcState::State::BELOW_MIN_PARKING_TIME);
 | 
			
		||||
    }
 | 
			
		||||
    if (duration == 0) {
 | 
			
		||||
        memset(price, 0x00, sizeof(*price));
 | 
			
		||||
        return true;
 | 
			
		||||
        return calcState.set(CalcState::State::SUCCESS);
 | 
			
		||||
    }
 | 
			
		||||
    qCritical() << "ERROR start_parking_time (" << start_parking_time << ") <"
 | 
			
		||||
                << "end_parking_time (" << end_parking_time << ")";
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
    QDate const d(1970, 1, 1);
 | 
			
		||||
    QTime const t(0, 0, 0);
 | 
			
		||||
    QDateTime start(d, t, Qt::UTC);
 | 
			
		||||
    start = start.toLocalTime().addSecs(start_parking_time * 60);
 | 
			
		||||
    if (start.isValid()) {
 | 
			
		||||
        QString cs = start.toString(Qt::ISODate);
 | 
			
		||||
        double cost = calculator.GetCostFromDuration(
 | 
			
		||||
                    tariff, PaymentOption::Option1,
 | 
			
		||||
                    cs.toLocal8Bit().constData(),
 | 
			
		||||
                    duration, false, true);
 | 
			
		||||
        double minCost = tariff->PaymentOption.find(PaymentOption::Option1)->second.pop_min_price;
 | 
			
		||||
        if (cost < minCost) {
 | 
			
		||||
            calcState.setDesc(QString("minCost=%1, cost=%2").arg(minCost).arg(cost));
 | 
			
		||||
            return calcState.set(CalcState::State::BELOW_MIN_PARKING_PRICE);
 | 
			
		||||
        }
 | 
			
		||||
        price->units = cost;
 | 
			
		||||
        price->netto = cost;
 | 
			
		||||
    } else {
 | 
			
		||||
        return calcState.set(CalcState::State::INVALID_START_DATE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return calcState.set(CalcState::State::SUCCESS);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CalcState CALCULATE_LIBRARY_API compute_price_for_parking_ticket(
 | 
			
		||||
        parking_tariff_t *tariff,
 | 
			
		||||
        QDateTime const &start_parking_time,
 | 
			
		||||
        QDateTime const &end_parking_time,
 | 
			
		||||
        struct price_t *price) {
 | 
			
		||||
    CalcState calcState;
 | 
			
		||||
    double minMin = tariff->PaymentOption.find(PaymentOption::Option1)->second.pop_min_time;
 | 
			
		||||
    double maxMin = tariff->PaymentOption.find(PaymentOption::Option1)->second.pop_max_time;
 | 
			
		||||
 | 
			
		||||
    // DEBUG
 | 
			
		||||
    qCritical() << "compute_price_for_parking_ticket() " << endl
 | 
			
		||||
                << "          start_parking_time: " << start_parking_time << endl
 | 
			
		||||
                << "            end_parking_time: " << end_parking_time   << endl
 | 
			
		||||
                << "                      minMin: " << minMin << endl
 | 
			
		||||
                << "                      maxMin: " << maxMin;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    int const duration = (end_parking_time.toSecsSinceEpoch() -
 | 
			
		||||
                          start_parking_time.toSecsSinceEpoch()) / 60;
 | 
			
		||||
 | 
			
		||||
    if (duration < 0) {
 | 
			
		||||
        calcState.setDesc(QString("end=%1, start=%2")
 | 
			
		||||
                          .arg(end_parking_time.toString(Qt::ISODate),
 | 
			
		||||
                               start_parking_time.toString(Qt::ISODate)));
 | 
			
		||||
        return calcState.set(CalcState::State::NEGATIVE_PARING_TIME);
 | 
			
		||||
    }
 | 
			
		||||
    if (duration > maxMin) {
 | 
			
		||||
        calcState.setDesc(QString("duration=%1, maxMin=%2").arg(duration, maxMin));
 | 
			
		||||
        return calcState.set(CalcState::State::ABOVE_MAX_PARKING_TIME);
 | 
			
		||||
    }
 | 
			
		||||
    if (duration < minMin) {
 | 
			
		||||
        calcState.setDesc(QString("duration=%1, minMin=%2").arg(duration, minMin));
 | 
			
		||||
        return calcState.set(CalcState::State::BELOW_MIN_PARKING_TIME);
 | 
			
		||||
    }
 | 
			
		||||
    if (duration == 0) {
 | 
			
		||||
        memset(price, 0x00, sizeof(*price));
 | 
			
		||||
        return calcState.set(CalcState::State::SUCCESS);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (start_parking_time.isValid()) {
 | 
			
		||||
        QString cs = start_parking_time.toString(Qt::ISODate);
 | 
			
		||||
        double cost = calculator.GetCostFromDuration(
 | 
			
		||||
                    tariff, PaymentOption::Option1,
 | 
			
		||||
                    cs.toLocal8Bit().constData(),
 | 
			
		||||
                    duration, false, true);
 | 
			
		||||
        double minCost = tariff->PaymentOption.find(PaymentOption::Option1)->second.pop_min_price;
 | 
			
		||||
        if (cost < minCost) {
 | 
			
		||||
            calcState.setDesc(QString("minCost=%1, cost=%2").arg(minCost, cost));
 | 
			
		||||
            return calcState.set(CalcState::State::BELOW_MIN_PARKING_PRICE);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // DEBUG
 | 
			
		||||
        qCritical() << "               -> calculated cost (price->netto) =  " << cost;
 | 
			
		||||
 | 
			
		||||
        price->units = cost;
 | 
			
		||||
        price->netto = cost;
 | 
			
		||||
    } else {
 | 
			
		||||
        return calcState.set(CalcState::State::INVALID_START_DATE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return calcState.set(CalcState::State::SUCCESS);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CalcState CALCULATE_LIBRARY_API compute_duration_for_parking_ticket(
 | 
			
		||||
        parking_tariff_t *tariff,
 | 
			
		||||
        time_t start_parking_time,
 | 
			
		||||
        double price,
 | 
			
		||||
        QString &duration) {
 | 
			
		||||
    CalcState calcState;
 | 
			
		||||
    QDate const d(1970, 1, 1);
 | 
			
		||||
    QTime const t(0, 0, 0);
 | 
			
		||||
    QDateTime start(d, t, Qt::UTC);
 | 
			
		||||
    start = start.toLocalTime().addSecs(start_parking_time * 60);
 | 
			
		||||
    if (start.isValid()) {
 | 
			
		||||
        QString cs = start.toString(Qt::ISODate);
 | 
			
		||||
 | 
			
		||||
        // DEBUG
 | 
			
		||||
        qCritical() << "compute_duration_for_parking_ticket(): ";
 | 
			
		||||
        qCritical() << "      start (cs): " << cs;
 | 
			
		||||
        qCritical() << "           price: " << price;
 | 
			
		||||
 | 
			
		||||
        duration = calculator.GetDurationFromCost(tariff, PaymentOption::Option1,
 | 
			
		||||
                                                  cs.toLocal8Bit().constData(),
 | 
			
		||||
                                                  price, false, true).c_str();
 | 
			
		||||
        QDateTime d = QDateTime::fromString(duration);
 | 
			
		||||
        if (!d.isValid()) {
 | 
			
		||||
            calcState.setDesc(QString("ticketEndTime=%1").arg(duration));
 | 
			
		||||
            return calcState.set(CalcState::State::WRONG_ISO_TIME_FORMAT);
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        return calcState.set(CalcState::State::INVALID_START_DATE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return calcState.set(CalcState::State::SUCCESS);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CalcState CALCULATE_LIBRARY_API compute_duration_for_parking_ticket(
 | 
			
		||||
        parking_tariff_t *tariff,
 | 
			
		||||
        QDateTime const &start_parking_time,
 | 
			
		||||
        double price,
 | 
			
		||||
        QDateTime &ticketEndTime) {
 | 
			
		||||
    CalcState calcState;
 | 
			
		||||
    if (start_parking_time.isValid()) {
 | 
			
		||||
        QString cs = start_parking_time.toString(Qt::ISODate);
 | 
			
		||||
        QString endTime = calculator.GetDurationFromCost(
 | 
			
		||||
                    tariff, PaymentOption::Option1,
 | 
			
		||||
                    cs.toLocal8Bit().constData(),
 | 
			
		||||
                    price, false, true).c_str();
 | 
			
		||||
        ticketEndTime = QDateTime::fromString(endTime,Qt::ISODate);
 | 
			
		||||
 | 
			
		||||
        // DEBUG
 | 
			
		||||
        qCritical() << "compute_duration_for_parking_ticket(): ";
 | 
			
		||||
        qCritical() << "                 endTime: " << endTime;
 | 
			
		||||
        qCritical() << "           ticketEndTime: " << ticketEndTime;
 | 
			
		||||
 | 
			
		||||
        if (!ticketEndTime.isValid()) {
 | 
			
		||||
            calcState.setDesc(QString("ticketEndTime=%1").arg(endTime));
 | 
			
		||||
            return calcState.set(CalcState::State::WRONG_ISO_TIME_FORMAT);
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        return calcState.set(CalcState::State::INVALID_START_DATE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return calcState.set(CalcState::State::SUCCESS);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CalcState CALCULATE_LIBRARY_API compute_duration_for_daily_ticket(parking_tariff_t *tariff, QString const &start_parking_time,uint8_t paymentMethod)
 | 
			
		||||
{
 | 
			
		||||
    CalcState calcState;
 | 
			
		||||
    QString result = calculator.GetDailyTicketDuration(tariff, start_parking_time, PaymentOption::Option1,false);
 | 
			
		||||
    qDebug() << "DailyTicket() => " + result;
 | 
			
		||||
 | 
			
		||||
    return calcState.set(CalcState::State::SUCCESS);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CalcState CALCULATE_LIBRARY_API compute_duration_for_24hour_daily_ticket(parking_tariff_t *tariff, QString const &start_parking_time,uint8_t paymentMethod)
 | 
			
		||||
{
 | 
			
		||||
    CalcState calcState;
 | 
			
		||||
    QString result = calculator.Get24HourTicketDuration(tariff, start_parking_time, PaymentOption::Option1,false);
 | 
			
		||||
    qDebug() << "24HourDailyTicket() => " + result;
 | 
			
		||||
 | 
			
		||||
    return calcState.set(CalcState::State::SUCCESS);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -4,273 +4,378 @@
 | 
			
		||||
#include "tariff_log.h"
 | 
			
		||||
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include <QDateTime>
 | 
			
		||||
#include <qdebug.h>
 | 
			
		||||
 | 
			
		||||
double total_duration_min = 0.0f;
 | 
			
		||||
double total_cost = 0.0f;
 | 
			
		||||
bool overtime = false;
 | 
			
		||||
int protection_counter = 0;
 | 
			
		||||
 | 
			
		||||
/// <inheritdoc/>
 | 
			
		||||
std::string Calculator::GetDurationFromCost(Configuration* cfg, uint8_t payment_option, char const* start_datetime, double price, bool nextDay, bool prepaid)
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
inline struct tm* localtime_r(const time_t *clock, struct tm* result){
 | 
			
		||||
    if(!clock || !result) return NULL;
 | 
			
		||||
    memcpy(result,localtime(clock),sizeof(*result));
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
QString Calculator::Get24HourTicketDuration(Configuration *cfg, QString start_datetime, uint8_t payment_option)
 | 
			
		||||
{
 | 
			
		||||
	// Get current date time from input
 | 
			
		||||
	struct tm current_datetime = Utilities::DateTimeToStructTm(start_datetime);
 | 
			
		||||
	time_t current_datetime_t;
 | 
			
		||||
     if(start_datetime.isNull() || start_datetime.isEmpty()) return "Invalid date-time";
 | 
			
		||||
      protection_counter = 0;
 | 
			
		||||
 | 
			
		||||
	// Get day of week
 | 
			
		||||
	DayOfWeek weekdayId = DayOfWeek::UndefinedDay;
 | 
			
		||||
	weekdayId = Utilities::GetDayOfWeek(¤t_datetime);
 | 
			
		||||
      double day_price = 0.0f;
 | 
			
		||||
      int current_special_day_id = -1;
 | 
			
		||||
 | 
			
		||||
	//std::stringstream ss;
 | 
			
		||||
      QDateTime inputDateTime = QDateTime::fromString(start_datetime, Qt::ISODate);
 | 
			
		||||
      QTime worktime_from;
 | 
			
		||||
      QTime worktime_to;
 | 
			
		||||
 | 
			
		||||
	// ss << "*** Input date is: " << start_datetime << " [weekday id = " << weekdayId << "]" << endl;
 | 
			
		||||
	LOG_DEBUG("*** Input date is: ", start_datetime, " [weekday id = ", weekdayId, "]");
 | 
			
		||||
      int daily_24hour_card_price = cfg->PaymentOption.find(payment_option)->second.pop_multi_hour_price;
 | 
			
		||||
      if(daily_24hour_card_price <= 0) return "24-hour daily ticket: price zero or less";
 | 
			
		||||
 | 
			
		||||
	double minMin = 0;
 | 
			
		||||
	minMin = cfg->PaymentOption.find(payment_option)->second.pop_min_time;
 | 
			
		||||
	if (minMin < 0) minMin = 0;
 | 
			
		||||
      bool is_special_day = Utilities::CheckSpecialDay(cfg, start_datetime.toStdString().c_str(), ¤t_special_day_id, &day_price);
 | 
			
		||||
 | 
			
		||||
	double maxMin = 0;
 | 
			
		||||
	maxMin = cfg->PaymentOption.find(payment_option)->second.pop_max_time;
 | 
			
		||||
	if (maxMin <= 0) maxMin = 60;
 | 
			
		||||
      if(is_special_day)
 | 
			
		||||
      {
 | 
			
		||||
          worktime_from = QTime::fromString(cfg->SpecialDaysWorktime.find(current_special_day_id)->second.pedwt_time_from.c_str(), Qt::ISODate);
 | 
			
		||||
          worktime_to = QTime::fromString(cfg->SpecialDaysWorktime.find(current_special_day_id)->second.pedwt_time_to.c_str(),Qt::ISODate);
 | 
			
		||||
          return "24-hour ticket cannot be bought on special day";
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
	if (minMin >= maxMin)
 | 
			
		||||
	{
 | 
			
		||||
		LOG_ERROR("Error: min_min cannot be greater or equal to max_min");
 | 
			
		||||
		return "PARKING NOT ALLOWED";
 | 
			
		||||
	}
 | 
			
		||||
      // Check next special day
 | 
			
		||||
      inputDateTime = inputDateTime.addSecs(86400);
 | 
			
		||||
      while(Utilities::CheckSpecialDay(cfg, inputDateTime.toString(Qt::ISODate).toLocal8Bit(), ¤t_special_day_id, &day_price))
 | 
			
		||||
      {
 | 
			
		||||
         protection_counter++;
 | 
			
		||||
         if(protection_counter >=7) return NULL;
 | 
			
		||||
         inputDateTime = inputDateTime.addSecs(86400);
 | 
			
		||||
      }
 | 
			
		||||
      return inputDateTime.toString(Qt::ISODate) + ", price = " + to_string(daily_24hour_card_price).c_str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
	if (maxMin <= minMin)
 | 
			
		||||
	{
 | 
			
		||||
		LOG_ERROR("Error: max_min cannot be lower or equal than min_min");
 | 
			
		||||
		return "PARKING NOT ALLOWED";
 | 
			
		||||
	}
 | 
			
		||||
QString Calculator::GetDailyTicketDuration(Configuration* cfg, QString start_datetime, uint8_t payment_option, bool carry_over)
 | 
			
		||||
{
 | 
			
		||||
    if(start_datetime.isNull() || start_datetime.isEmpty()) return NULL;
 | 
			
		||||
 | 
			
		||||
	uint8_t p_method = PaymentMethod::Undefined;
 | 
			
		||||
	p_method = payment_option;
 | 
			
		||||
	LOG_DEBUG("Payment method id: ", (unsigned)p_method);
 | 
			
		||||
    double day_price = 0.0f;
 | 
			
		||||
    int current_special_day_id = -1;
 | 
			
		||||
    bool is_special_day = Utilities::CheckSpecialDay(cfg, start_datetime.toStdString().c_str(), ¤t_special_day_id, &day_price);
 | 
			
		||||
 | 
			
		||||
	double day_price = 0.0f;
 | 
			
		||||
	int current_special_day_id = -1;
 | 
			
		||||
	bool is_special_day = Utilities::CheckSpecialDay(cfg, start_datetime, ¤t_special_day_id, &day_price);
 | 
			
		||||
	LOG_DEBUG("Special day: ", is_special_day);
 | 
			
		||||
    QDateTime inputDateTime = QDateTime::fromString(start_datetime, Qt::ISODate);
 | 
			
		||||
    QTime worktime_from;
 | 
			
		||||
    QTime worktime_to;
 | 
			
		||||
 | 
			
		||||
	double money_left = price;
 | 
			
		||||
	LOG_DEBUG("Total money:", money_left);
 | 
			
		||||
    int daily_card_price = cfg->PaymentOption.find(payment_option)->second.pop_daily_card_price;
 | 
			
		||||
    if(daily_card_price <= 0) return "Daily ticket price zero or less";
 | 
			
		||||
 | 
			
		||||
	double price_per_unit = 0.0f;
 | 
			
		||||
    if(is_special_day)
 | 
			
		||||
    {
 | 
			
		||||
        worktime_from = QTime::fromString(cfg->SpecialDaysWorktime.find(current_special_day_id)->second.pedwt_time_from.c_str(), Qt::ISODate);
 | 
			
		||||
        worktime_to = QTime::fromString(cfg->SpecialDaysWorktime.find(current_special_day_id)->second.pedwt_time_to.c_str(),Qt::ISODate);
 | 
			
		||||
 | 
			
		||||
	string worktime_from = "";
 | 
			
		||||
	string worktime_to = "";
 | 
			
		||||
        if(inputDateTime.time() < worktime_from) inputDateTime.setTime(worktime_from);
 | 
			
		||||
        if(carry_over) inputDateTime.setTime(worktime_from);
 | 
			
		||||
 | 
			
		||||
	if (is_special_day)
 | 
			
		||||
	{
 | 
			
		||||
		// Set special day price
 | 
			
		||||
		price_per_unit = Utilities::CalculatePricePerUnit(day_price);
 | 
			
		||||
		worktime_from = cfg->SpecialDaysWorktime.find(current_special_day_id)->second.pedwt_time_from;
 | 
			
		||||
		worktime_to = cfg->SpecialDaysWorktime.find(current_special_day_id)->second.pedwt_time_to;
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		// Set new price for the normal day
 | 
			
		||||
		day_price = cfg->PaymentRate.find(payment_option)->second.pra_price;
 | 
			
		||||
		price_per_unit = Utilities::CalculatePricePerUnit(day_price);
 | 
			
		||||
        if(inputDateTime.time() >= worktime_to)
 | 
			
		||||
        {
 | 
			
		||||
            // Go to next day if outside worktime
 | 
			
		||||
            inputDateTime = inputDateTime.addSecs(86400);
 | 
			
		||||
            return GetDailyTicketDuration(cfg,inputDateTime.toString(Qt::ISODate), payment_option,true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
		// If no working day found, skip it (recursively call method again)
 | 
			
		||||
		size_t found = 0;
 | 
			
		||||
		found = cfg->WeekDaysWorktime.count(weekdayId);
 | 
			
		||||
        if(day_price <=0)
 | 
			
		||||
        {
 | 
			
		||||
            // Go to next day if special day price is 0
 | 
			
		||||
            inputDateTime = inputDateTime.addSecs(86400);
 | 
			
		||||
            return GetDailyTicketDuration(cfg,inputDateTime.toString(Qt::ISODate), payment_option,true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
		if (found <= 0)
 | 
			
		||||
		{
 | 
			
		||||
			LOG_DEBUG("- No workday found, trying to find next available day");
 | 
			
		||||
			current_datetime_t = mktime(¤t_datetime);
 | 
			
		||||
			current_datetime_t += 86400;
 | 
			
		||||
			current_datetime = *localtime(¤t_datetime_t);
 | 
			
		||||
        int diff = abs(inputDateTime.time().secsTo(worktime_to));
 | 
			
		||||
        inputDateTime = inputDateTime.addSecs(diff);
 | 
			
		||||
 | 
			
		||||
			char buffer_datetime[80];
 | 
			
		||||
			strftime(buffer_datetime, 80, "%Y-%m-%dT%H:%M:%S", ¤t_datetime);
 | 
			
		||||
        //qDebug() << "Ticket is valid until: " << inputDateTime.toString(Qt::ISODate) << "price = " << daily_card_price << ", duration = " << diff / 60;
 | 
			
		||||
        return inputDateTime.toString(Qt::ISODate) + ", price = " + to_string(daily_card_price).c_str() + ", duration = " + to_string((diff/60)).c_str();
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        // Get day of week
 | 
			
		||||
        int weekdayId = 0;
 | 
			
		||||
        weekdayId = Utilities::ZellersAlgorithm(inputDateTime.date().day(),inputDateTime.date().month(),inputDateTime.date().year());
 | 
			
		||||
 | 
			
		||||
			// Make new datetime string and call function again recursively
 | 
			
		||||
			start_datetime = buffer_datetime;
 | 
			
		||||
			return GetDurationFromCost(cfg, payment_option, start_datetime, price, true);
 | 
			
		||||
		}
 | 
			
		||||
        // If no working day found, skip it (recursively call method again)
 | 
			
		||||
        size_t found = 0;
 | 
			
		||||
        found = cfg->WeekDaysWorktime.count(weekdayId);
 | 
			
		||||
 | 
			
		||||
		worktime_from = cfg->WeekDaysWorktime.find(weekdayId)->second.pwd_time_from;
 | 
			
		||||
		worktime_to = cfg->WeekDaysWorktime.find(weekdayId)->second.pwd_time_to;
 | 
			
		||||
	}
 | 
			
		||||
        // When no workday found, go to next available day
 | 
			
		||||
        if(found <=0)
 | 
			
		||||
        {
 | 
			
		||||
            inputDateTime = inputDateTime.addSecs(86400);
 | 
			
		||||
            return GetDailyTicketDuration(cfg,inputDateTime.toString(Qt::ISODate), payment_option,true);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            worktime_from = QTime::fromString(cfg->WeekDaysWorktime.find(weekdayId)->second.pwd_time_from.c_str(),Qt::ISODate);
 | 
			
		||||
            worktime_to = QTime::fromString(cfg->WeekDaysWorktime.find(weekdayId)->second.pwd_time_to.c_str(),Qt::ISODate);
 | 
			
		||||
            if(inputDateTime.time() < worktime_from)
 | 
			
		||||
                inputDateTime.setTime(worktime_from);
 | 
			
		||||
 | 
			
		||||
	if (price_per_unit < 0) price_per_unit = 1.0f;
 | 
			
		||||
	LOG_DEBUG("Calculated price per minute: ", price_per_unit);
 | 
			
		||||
            if(carry_over)
 | 
			
		||||
                inputDateTime.setTime(worktime_from);
 | 
			
		||||
 | 
			
		||||
	LOG_DEBUG("Worktime from: ", worktime_from);
 | 
			
		||||
	LOG_DEBUG("Worktime to: ", worktime_to);
 | 
			
		||||
            if(inputDateTime.time() >= worktime_to)
 | 
			
		||||
            {
 | 
			
		||||
                // Go to next day if outside worktime
 | 
			
		||||
                inputDateTime = inputDateTime.addSecs(86400);
 | 
			
		||||
                return GetDailyTicketDuration(cfg,inputDateTime.toString(Qt::ISODate), payment_option,true);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
	struct tm from_tm;
 | 
			
		||||
	struct tm to_tm;
 | 
			
		||||
            int diff = abs(inputDateTime.time().secsTo(worktime_to));
 | 
			
		||||
            inputDateTime = inputDateTime.addSecs(diff);
 | 
			
		||||
 | 
			
		||||
	from_tm = Utilities::TimeToStructTm(worktime_from.c_str(), current_datetime.tm_year, current_datetime.tm_mon, current_datetime.tm_mday, current_datetime.tm_wday);
 | 
			
		||||
	from_tm.tm_year = current_datetime.tm_year;
 | 
			
		||||
	from_tm.tm_mon = current_datetime.tm_mon;
 | 
			
		||||
	from_tm.tm_wday = current_datetime.tm_wday;
 | 
			
		||||
	from_tm.tm_mday = current_datetime.tm_mday;
 | 
			
		||||
            //qDebug() << "Ticket is valid until: " << inputDateTime.toString(Qt::ISODate) << "price = " << daily_card_price << ", duration = " << diff / 60;
 | 
			
		||||
            return inputDateTime.toString(Qt::ISODate) + ", price = " + to_string(daily_card_price).c_str() + ", duration = " + to_string((diff/60)).c_str();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	to_tm = Utilities::TimeToStructTm(worktime_to.c_str(), current_datetime.tm_year, current_datetime.tm_mon, current_datetime.tm_mday, current_datetime.tm_wday);
 | 
			
		||||
	to_tm.tm_year = current_datetime.tm_year;
 | 
			
		||||
	to_tm.tm_mon = current_datetime.tm_mon;
 | 
			
		||||
	to_tm.tm_wday = current_datetime.tm_wday;
 | 
			
		||||
	to_tm.tm_mday = current_datetime.tm_mday;
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
/// <inheritdoc/>
 | 
			
		||||
std::string Calculator::GetDurationFromCost(Configuration* cfg,
 | 
			
		||||
                                            uint8_t payment_option,
 | 
			
		||||
                                            char const* start_datetime, // given in local time
 | 
			
		||||
                                            double price,
 | 
			
		||||
                                            bool nextDay,
 | 
			
		||||
                                            bool prepaid)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	// Convert tm structures to time_t
 | 
			
		||||
	current_datetime_t = mktime(¤t_datetime);
 | 
			
		||||
	time_t from_datetime_t = mktime(&from_tm);
 | 
			
		||||
	time_t to_datetime_t = mktime(&to_tm);
 | 
			
		||||
    // Get input date
 | 
			
		||||
    QDateTime inputDate = QDateTime::fromString(start_datetime,Qt::ISODate);
 | 
			
		||||
 | 
			
		||||
	/*Newly added */
 | 
			
		||||
	//current_datetime.tm_hour = from_tm.tm_hour;
 | 
			
		||||
	//current_datetime.tm_min = from_tm.tm_min;
 | 
			
		||||
	//current_datetime.tm_sec = from_tm.tm_sec;
 | 
			
		||||
	//current_datetime_t = mktime(¤t_datetime);
 | 
			
		||||
    // Get day of week
 | 
			
		||||
    int weekdayId = 0;
 | 
			
		||||
    weekdayId = Utilities::ZellersAlgorithm(inputDate.date().day(),inputDate.date().month(),inputDate.date().year());
 | 
			
		||||
 | 
			
		||||
	// If overtime flag is set
 | 
			
		||||
	if (overtime || nextDay)
 | 
			
		||||
	{
 | 
			
		||||
		current_datetime.tm_hour = from_tm.tm_hour;
 | 
			
		||||
		current_datetime.tm_min = from_tm.tm_min;
 | 
			
		||||
		current_datetime.tm_sec = from_tm.tm_sec;
 | 
			
		||||
		current_datetime_t = mktime(¤t_datetime);
 | 
			
		||||
		LOG_DEBUG(" *** New input date set according to worktime: ", asctime(¤t_datetime));
 | 
			
		||||
		overtime = false;
 | 
			
		||||
	}
 | 
			
		||||
    //Get min and max time defined in JSON
 | 
			
		||||
    double minMin = 0;
 | 
			
		||||
    minMin = cfg->PaymentOption.find(payment_option)->second.pop_min_time;
 | 
			
		||||
 | 
			
		||||
	// Validate ticket
 | 
			
		||||
	if (!prepaid)
 | 
			
		||||
	{
 | 
			
		||||
		if ((current_datetime_t < from_datetime_t) || (current_datetime_t > to_datetime_t))
 | 
			
		||||
		{
 | 
			
		||||
			LOG_DEBUG("[STOP] * Ticket is not valid * ");
 | 
			
		||||
			return "PARKING NOT ALLOWED";
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		LOG_DEBUG("* PREPAID MODE ACTIVE *");
 | 
			
		||||
    double maxMin = 0;
 | 
			
		||||
    maxMin = cfg->PaymentOption.find(payment_option)->second.pop_max_time;
 | 
			
		||||
 | 
			
		||||
		if (current_datetime_t < from_datetime_t)
 | 
			
		||||
		{
 | 
			
		||||
			current_datetime.tm_hour = from_tm.tm_hour;
 | 
			
		||||
			current_datetime.tm_min = from_tm.tm_min;
 | 
			
		||||
			current_datetime.tm_sec = from_tm.tm_sec;
 | 
			
		||||
			current_datetime_t = mktime(¤t_datetime);
 | 
			
		||||
			LOG_DEBUG(" *** PREPAID *** Current time is before the time range start, adjusting time to: ", asctime(¤t_datetime));
 | 
			
		||||
		}
 | 
			
		||||
		else if (current_datetime_t > to_datetime_t)
 | 
			
		||||
		{
 | 
			
		||||
			LOG_DEBUG(" *** PREPAID *** Current time is past the time range end, searching for next available day");
 | 
			
		||||
			current_datetime_t = mktime(¤t_datetime);
 | 
			
		||||
			current_datetime_t += 86400;
 | 
			
		||||
			current_datetime = *localtime(¤t_datetime_t);
 | 
			
		||||
    double min_price = 0;
 | 
			
		||||
    min_price = cfg->PaymentOption.find(payment_option)->second.pop_min_price;
 | 
			
		||||
 | 
			
		||||
			char buffer_datetime[80];
 | 
			
		||||
			strftime(buffer_datetime, 80, "%Y-%m-%dT%H:%M:%S", ¤t_datetime);
 | 
			
		||||
    if(price < min_price)
 | 
			
		||||
    {
 | 
			
		||||
        return "PARKING NOT ALLOWED";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
			//Make new datetime string and call function again recursively
 | 
			
		||||
			start_datetime = buffer_datetime;
 | 
			
		||||
			return GetDurationFromCost(cfg, payment_option, start_datetime, price, true, prepaid);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
    if (minMin < 0) minMin = 0;
 | 
			
		||||
    if (maxMin < 0) maxMin = 0;
 | 
			
		||||
    if (minMin >= maxMin)
 | 
			
		||||
    {
 | 
			
		||||
        LOG_ERROR("Error: min_min cannot be greater or equal to max_min");
 | 
			
		||||
        return "PARKING NOT ALLOWED";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	while (true)
 | 
			
		||||
	{
 | 
			
		||||
		if (!Utilities::IsYearPeriodActive(cfg, ¤t_datetime))
 | 
			
		||||
		{
 | 
			
		||||
			LOG_DEBUG("Year period is not valid");
 | 
			
		||||
			return "PARKING NOT ALLOWED";
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Increment by 1 minute
 | 
			
		||||
		current_datetime_t = mktime(¤t_datetime);
 | 
			
		||||
		current_datetime_t += 60;
 | 
			
		||||
		current_datetime = *localtime(¤t_datetime_t);
 | 
			
		||||
		total_duration_min += 1.0f;
 | 
			
		||||
		money_left -= price_per_unit;
 | 
			
		||||
    if (maxMin <= minMin)
 | 
			
		||||
    {
 | 
			
		||||
        LOG_ERROR("Error: max_min cannot be lower or equal than min_min");
 | 
			
		||||
        return "PARKING NOT ALLOWED";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		// If no money left (e.g. spent all of the money before reaching end of worktime)
 | 
			
		||||
		if (money_left <= 0)
 | 
			
		||||
		{
 | 
			
		||||
			LOG_DEBUG("No money left ");
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			if (total_duration_min >= maxMin)
 | 
			
		||||
			{
 | 
			
		||||
				LOG_DEBUG("Total duration is greater or equal to max_min");
 | 
			
		||||
    // Get payment method
 | 
			
		||||
    uint8_t p_method = PaymentMethod::Undefined;
 | 
			
		||||
    p_method = payment_option;
 | 
			
		||||
    LOG_DEBUG("Payment method id: ", (unsigned)p_method);
 | 
			
		||||
 | 
			
		||||
				current_datetime_t = mktime(¤t_datetime);
 | 
			
		||||
				current_datetime_t -= 60;
 | 
			
		||||
				current_datetime = *localtime(¤t_datetime_t);
 | 
			
		||||
				total_duration_min = maxMin;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
    // Check special day
 | 
			
		||||
    double day_price = 0.0f;
 | 
			
		||||
    int current_special_day_id = -1;
 | 
			
		||||
    bool is_special_day = Utilities::CheckSpecialDay(cfg, inputDate.toString(Qt::ISODate).toStdString().c_str(), ¤t_special_day_id, &day_price);
 | 
			
		||||
    LOG_DEBUG("Special day: ", is_special_day);
 | 
			
		||||
 | 
			
		||||
			// If money has left but the end of worktime has been reached (go to next day)
 | 
			
		||||
			if (current_datetime_t >= to_datetime_t)
 | 
			
		||||
			{
 | 
			
		||||
				total_duration_min -= 1.0f;
 | 
			
		||||
    double money_left = price;
 | 
			
		||||
    double price_per_unit = 0.0f;
 | 
			
		||||
 | 
			
		||||
				int carry_over_status = 0;
 | 
			
		||||
				carry_over_status = cfg->PaymentOption.find(payment_option)->second.pop_carry_over;
 | 
			
		||||
				LOG_DEBUG("Carry over status: ", carry_over_status);
 | 
			
		||||
				if (carry_over_status < 1) break;
 | 
			
		||||
    QTime worktime_from;
 | 
			
		||||
    QTime worktime_to;
 | 
			
		||||
 | 
			
		||||
				LOG_DEBUG("Reached end of worktime");
 | 
			
		||||
				LOG_DEBUG("Trying to find next available day, money left = ", money_left);
 | 
			
		||||
				current_datetime_t = mktime(¤t_datetime);
 | 
			
		||||
				current_datetime_t += 86400;
 | 
			
		||||
				current_datetime = *localtime(¤t_datetime_t);
 | 
			
		||||
    if(is_special_day)
 | 
			
		||||
    {
 | 
			
		||||
        // Set special day price
 | 
			
		||||
        price_per_unit = Utilities::CalculatePricePerUnit(day_price);
 | 
			
		||||
        worktime_from = QTime::fromString(cfg->SpecialDaysWorktime.find(current_special_day_id)->second.pedwt_time_from.c_str());
 | 
			
		||||
        worktime_to = QTime::fromString(cfg->SpecialDaysWorktime.find(current_special_day_id)->second.pedwt_time_to.c_str());
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        // Set new price for the normal day
 | 
			
		||||
        int pop_id = cfg->PaymentOption.find(payment_option)->second.pop_id;
 | 
			
		||||
        day_price = cfg->PaymentRate.find(pop_id)->second.pra_price;
 | 
			
		||||
 | 
			
		||||
				char buffer_datetime[80];
 | 
			
		||||
				strftime(buffer_datetime, 80, "%Y-%m-%dT%H:%M:%S", ¤t_datetime);
 | 
			
		||||
        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);
 | 
			
		||||
 | 
			
		||||
				// Make new datetime string and call function again recursively
 | 
			
		||||
				start_datetime = buffer_datetime;
 | 
			
		||||
				overtime = true;
 | 
			
		||||
                return GetDurationFromCost(cfg, payment_option, start_datetime, price);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
        // If no working day found, skip it (recursively call method again)
 | 
			
		||||
        size_t found = 0;
 | 
			
		||||
        found = cfg->WeekDaysWorktime.count(weekdayId);
 | 
			
		||||
 | 
			
		||||
	time_t valid_until_datetime_t = current_datetime_t;
 | 
			
		||||
        // When no workday found, go to next available day
 | 
			
		||||
        if(found <=0)
 | 
			
		||||
        {
 | 
			
		||||
            LOG_DEBUG("- No workday found, trying to find next available day");
 | 
			
		||||
            inputDate = inputDate.addDays(1);
 | 
			
		||||
            return GetDurationFromCost(cfg, payment_option, inputDate.toString(Qt::ISODate).toStdString().c_str(), money_left,true,prepaid);
 | 
			
		||||
        }
 | 
			
		||||
        worktime_from = QTime::fromString(cfg->WeekDaysWorktime.find(weekdayId)->second.pwd_time_from.c_str());
 | 
			
		||||
        worktime_to = QTime::fromString(cfg->WeekDaysWorktime.find(weekdayId)->second.pwd_time_to.c_str());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	if ((total_duration_min < minMin) || (price / price_per_unit) < minMin)
 | 
			
		||||
	{
 | 
			
		||||
		LOG_DEBUG("Total duration is lower than min_min");
 | 
			
		||||
		//valid_until_datetime_t += (minMin - total_duration_min) * 60;
 | 
			
		||||
		//total_duration_min = minMin;
 | 
			
		||||
		//return "PARKING NOT ALLOWED";
 | 
			
		||||
    if (price_per_unit < 0) price_per_unit = 1.0f;
 | 
			
		||||
 | 
			
		||||
		valid_until_datetime_t = from_datetime_t;
 | 
			
		||||
		total_duration_min = 0;
 | 
			
		||||
	}
 | 
			
		||||
    // Commented on 07.06.2023
 | 
			
		||||
    //if((price/price_per_unit) < minMin)
 | 
			
		||||
    //   return "PARKING NOT ALLOWED";
 | 
			
		||||
 | 
			
		||||
	double ret_val = 0;
 | 
			
		||||
	double calc_price = (int)total_duration_min - (int)price / price_per_unit;
 | 
			
		||||
    LOG_DEBUG("Calculated price per minute: ", price_per_unit);
 | 
			
		||||
 | 
			
		||||
	if (calc_price > 0 && total_duration_min > 0)
 | 
			
		||||
	{
 | 
			
		||||
		valid_until_datetime_t -= (int)ceil(calc_price) * 60;
 | 
			
		||||
		ret_val = total_duration_min - calc_price;
 | 
			
		||||
	}
 | 
			
		||||
	else ret_val = total_duration_min;
 | 
			
		||||
    if (price_per_unit < 0)
 | 
			
		||||
    {
 | 
			
		||||
        inputDate = inputDate.addDays(1);
 | 
			
		||||
        inputDate.setTime(worktime_from);
 | 
			
		||||
        return GetDurationFromCost(cfg, payment_option, inputDate.toString(Qt::ISODate).toStdString().c_str(), money_left, true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	cout << "Total minutes: " << (int)ret_val << endl;
 | 
			
		||||
	if (ret_val <= 0) return "PARKING NOT ALLOWED";
 | 
			
		||||
    // If overtime flag is set
 | 
			
		||||
    if (overtime || nextDay)
 | 
			
		||||
    {
 | 
			
		||||
        inputDate.setTime(worktime_from);
 | 
			
		||||
        overtime = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	struct tm valid_until_datetime = *localtime(&valid_until_datetime_t);
 | 
			
		||||
	LOG_DEBUG("Ticket is valid until ", asctime(&valid_until_datetime));
 | 
			
		||||
    // Check prepaid
 | 
			
		||||
    if (!prepaid)
 | 
			
		||||
    {
 | 
			
		||||
        if ((inputDate.time() < worktime_from) || (inputDate.time() > worktime_to))
 | 
			
		||||
        {
 | 
			
		||||
            LOG_DEBUG("[STOP] * Ticket is not valid * ");
 | 
			
		||||
            return "PARKING NOT ALLOWED";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        LOG_DEBUG("* PREPAID MODE ACTIVE *");
 | 
			
		||||
        if (inputDate.time() < worktime_from)
 | 
			
		||||
        {
 | 
			
		||||
            inputDate.setTime(worktime_from);
 | 
			
		||||
        }
 | 
			
		||||
        else if(inputDate.time() > worktime_to)
 | 
			
		||||
        {
 | 
			
		||||
            LOG_DEBUG(" *** PREPAID *** Current time is past the time range end, searching for next available day");
 | 
			
		||||
            inputDate = inputDate.addDays(1);
 | 
			
		||||
            return GetDurationFromCost(cfg, payment_option, inputDate.toString(Qt::ISODate).toStdString().c_str(), money_left, true);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	total_duration_min = 0.0f;
 | 
			
		||||
	return asctime(&valid_until_datetime);
 | 
			
		||||
    while(true)
 | 
			
		||||
    {
 | 
			
		||||
        if((int)money_left <= 0) break;
 | 
			
		||||
 | 
			
		||||
        // Check year period
 | 
			
		||||
        bool isYearPeriodActive = false;
 | 
			
		||||
 | 
			
		||||
        //// Parse input date
 | 
			
		||||
        int dayCurrent = inputDate.date().day();
 | 
			
		||||
        int monthCurrent = inputDate.date().month();
 | 
			
		||||
 | 
			
		||||
        // Current date time
 | 
			
		||||
        int cdt = (monthCurrent * 100) + dayCurrent;
 | 
			
		||||
 | 
			
		||||
        multimap<int, ATBPeriodYear>::iterator year_period_itr;
 | 
			
		||||
        for (year_period_itr = cfg->YearPeriod.begin(); year_period_itr != cfg->YearPeriod.end(); ++year_period_itr)
 | 
			
		||||
        {
 | 
			
		||||
            int dStart = year_period_itr->second.pye_start_day;
 | 
			
		||||
            int dEnd = year_period_itr->second.pye_end_day;
 | 
			
		||||
 | 
			
		||||
            int mStart = year_period_itr->second.pye_start_month;
 | 
			
		||||
            int mEnd = year_period_itr->second.pye_end_month;
 | 
			
		||||
 | 
			
		||||
            int start = (mStart * 100) + dStart;
 | 
			
		||||
            int end = (mEnd * 100) + dEnd;
 | 
			
		||||
 | 
			
		||||
            if (cdt >= start && cdt <= end) {
 | 
			
		||||
                isYearPeriodActive = true;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (!isYearPeriodActive)
 | 
			
		||||
        {
 | 
			
		||||
            LOG_DEBUG("Year period is not valid");
 | 
			
		||||
            return "PARKING NOT ALLOWED";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(total_duration_min > maxMin)
 | 
			
		||||
        {
 | 
			
		||||
            total_duration_min = maxMin;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // If reached end of worktime go to next day
 | 
			
		||||
        if(inputDate.time() >= worktime_to)
 | 
			
		||||
        {
 | 
			
		||||
            int carry_over_status = 0;
 | 
			
		||||
            carry_over_status = cfg->PaymentOption.find(payment_option)->second.pop_carry_over;
 | 
			
		||||
            if (carry_over_status < 1) break;
 | 
			
		||||
 | 
			
		||||
            inputDate = inputDate.addDays(1);
 | 
			
		||||
            overtime = true;
 | 
			
		||||
            return GetDurationFromCost(cfg, payment_option, inputDate.toString(Qt::ISODate).toStdString().c_str(), money_left ,true, prepaid);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(money_left > 1)
 | 
			
		||||
            inputDate = inputDate.addSecs(60);
 | 
			
		||||
 | 
			
		||||
        if(price_per_unit > 0) total_duration_min +=1;
 | 
			
		||||
        money_left -= price_per_unit;
 | 
			
		||||
 | 
			
		||||
        //qDebug() <<"Timestamp:" << inputDate << ", total duration min: " << total_duration_min << ", money left = " << money_left;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //    if ((total_duration_min < minMin) || (price / price_per_unit) < minMin)
 | 
			
		||||
    //    {
 | 
			
		||||
    //        LOG_DEBUG("Total duration is lower than min_min");
 | 
			
		||||
    //        inputDate.time() = worktime_from;
 | 
			
		||||
    //        total_duration_min = 0;
 | 
			
		||||
    //    }
 | 
			
		||||
 | 
			
		||||
    double ret_val = 0;
 | 
			
		||||
    double calc_price = (int)total_duration_min - (int)price / price_per_unit;
 | 
			
		||||
 | 
			
		||||
    if (calc_price > 0 && total_duration_min > 0)
 | 
			
		||||
    {
 | 
			
		||||
        inputDate.addSecs(-(int)ceil(calc_price) * 60);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(price >= min_price && total_duration_min >= minMin)
 | 
			
		||||
        qDebug() << "Valid until: " << inputDate.toString(Qt::ISODate);
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        qDebug() << "Parking not allowed";
 | 
			
		||||
        total_duration_min = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret_val = total_duration_min;
 | 
			
		||||
    if(ret_val < 0) ret_val = 0;
 | 
			
		||||
    qDebug() << "Duration: " << ret_val;
 | 
			
		||||
    if (ret_val <= 0) return "PARKING NOT ALLOWED";
 | 
			
		||||
 | 
			
		||||
    total_duration_min = 0;
 | 
			
		||||
    return inputDate.toString(Qt::ISODate).toStdString();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
///////////////////////////////////////
 | 
			
		||||
@@ -278,236 +383,200 @@ std::string Calculator::GetDurationFromCost(Configuration* cfg, uint8_t payment_
 | 
			
		||||
/// <inheritdoc/>
 | 
			
		||||
double Calculator::GetCostFromDuration(Configuration* cfg, uint8_t payment_option, const char* start_datetime, double durationMin, bool nextDay, bool prepaid)
 | 
			
		||||
{
 | 
			
		||||
	// Get current date time from input
 | 
			
		||||
	struct tm current_datetime = Utilities::DateTimeToStructTm(start_datetime);
 | 
			
		||||
	time_t current_datetime_t;
 | 
			
		||||
    // Get input date
 | 
			
		||||
    QDateTime inputDate = QDateTime::fromString(start_datetime,Qt::ISODate);
 | 
			
		||||
 | 
			
		||||
	// Get day of week
 | 
			
		||||
	DayOfWeek weekdayId = DayOfWeek::UndefinedDay;
 | 
			
		||||
	weekdayId = Utilities::GetDayOfWeek(¤t_datetime);
 | 
			
		||||
	LOG_DEBUG("*** Input date is: ", start_datetime, " [weekday id = ", weekdayId, "]");
 | 
			
		||||
    // Get day of week
 | 
			
		||||
    int weekdayId = 0;
 | 
			
		||||
    weekdayId = Utilities::ZellersAlgorithm(inputDate.date().day(),inputDate.date().month(),inputDate.date().year());
 | 
			
		||||
 | 
			
		||||
	double minMin = 0;
 | 
			
		||||
	minMin = cfg->PaymentOption.find(payment_option)->second.pop_min_time;
 | 
			
		||||
	if (minMin < 0) minMin = 0;
 | 
			
		||||
    //Get min and max time defined in JSON
 | 
			
		||||
    double minMin = 0;
 | 
			
		||||
    minMin = cfg->PaymentOption.find(payment_option)->second.pop_min_time;
 | 
			
		||||
 | 
			
		||||
	double maxMin = 0;
 | 
			
		||||
	maxMin = cfg->PaymentOption.find(payment_option)->second.pop_max_time;
 | 
			
		||||
	if (maxMin <= 0) maxMin = 60;
 | 
			
		||||
    double maxMin = 0;
 | 
			
		||||
    maxMin = cfg->PaymentOption.find(payment_option)->second.pop_max_time;
 | 
			
		||||
 | 
			
		||||
	if (minMin >= maxMin)
 | 
			
		||||
	{
 | 
			
		||||
		LOG_ERROR("Error: min_min cannot be greater or equal to max_min");
 | 
			
		||||
		return 0.0f;
 | 
			
		||||
	}
 | 
			
		||||
    if (minMin < 0) minMin = 0;
 | 
			
		||||
    if (maxMin < 0) maxMin = 0;
 | 
			
		||||
    if (minMin >= maxMin)
 | 
			
		||||
    {
 | 
			
		||||
        LOG_ERROR("Error: min_min cannot be greater or equal to max_min");
 | 
			
		||||
        return 0.0f;
 | 
			
		||||
    }
 | 
			
		||||
    if (maxMin <= minMin)
 | 
			
		||||
    {
 | 
			
		||||
        LOG_ERROR("Error: max_min cannot be lower or equal than min_min");
 | 
			
		||||
        return 0.0f;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	if (maxMin <= minMin)
 | 
			
		||||
	{
 | 
			
		||||
		LOG_ERROR("Error: max_min cannot be lower or equal than min_min");
 | 
			
		||||
		return 0.0f;
 | 
			
		||||
	}
 | 
			
		||||
    // Check overtime
 | 
			
		||||
    if (!overtime)
 | 
			
		||||
    {
 | 
			
		||||
        if (durationMin > maxMin)
 | 
			
		||||
        {
 | 
			
		||||
            LOG_WARNING("Total duration is greater or equal to max_min");
 | 
			
		||||
            return maxMin;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
	if (!overtime)
 | 
			
		||||
	{
 | 
			
		||||
		if (durationMin > maxMin)
 | 
			
		||||
		{
 | 
			
		||||
			LOG_WARNING("Total duration is greater or equal to max_min");
 | 
			
		||||
			return 0.0f;
 | 
			
		||||
		}
 | 
			
		||||
        if (durationMin < minMin)
 | 
			
		||||
        {
 | 
			
		||||
            LOG_WARNING("Total duration is lower or equal to min_min");
 | 
			
		||||
            return 0.0f;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
		if (durationMin < minMin)
 | 
			
		||||
		{
 | 
			
		||||
			LOG_WARNING("Total duration is lower or equal to min_min");
 | 
			
		||||
			return 0.0f;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
    // Get payment method
 | 
			
		||||
    uint8_t p_method = PaymentMethod::Undefined;
 | 
			
		||||
    p_method = payment_option;
 | 
			
		||||
    LOG_DEBUG("Payment method id: ", (unsigned)p_method);
 | 
			
		||||
 | 
			
		||||
	uint8_t p_method = PaymentMethod::Undefined;
 | 
			
		||||
	p_method = payment_option;
 | 
			
		||||
	LOG_DEBUG("Payment method id: ", (unsigned)p_method);
 | 
			
		||||
    // Check special day
 | 
			
		||||
    double day_price = 0.0f;
 | 
			
		||||
    int current_special_day_id = -1;
 | 
			
		||||
    bool is_special_day = Utilities::CheckSpecialDay(cfg, inputDate.toString(Qt::ISODate).toStdString().c_str(), ¤t_special_day_id, &day_price);
 | 
			
		||||
    LOG_DEBUG("Special day: ", is_special_day);
 | 
			
		||||
 | 
			
		||||
	double day_price = 0.0f;
 | 
			
		||||
	int current_special_day_id = -1;
 | 
			
		||||
	bool is_special_day = Utilities::CheckSpecialDay(cfg, start_datetime, ¤t_special_day_id, &day_price);
 | 
			
		||||
	LOG_DEBUG("Special day: ", is_special_day);
 | 
			
		||||
    total_duration_min = durationMin;
 | 
			
		||||
    LOG_DEBUG("Total min:", total_duration_min);
 | 
			
		||||
 | 
			
		||||
	total_duration_min = durationMin;
 | 
			
		||||
	LOG_DEBUG("Total min:", total_duration_min);
 | 
			
		||||
    double price_per_unit = 0.0f;
 | 
			
		||||
    QTime worktime_from;
 | 
			
		||||
    QTime worktime_to;
 | 
			
		||||
 | 
			
		||||
	double price_per_unit = 0.0f;
 | 
			
		||||
    if(is_special_day)
 | 
			
		||||
    {
 | 
			
		||||
        // Set special day price
 | 
			
		||||
        price_per_unit = Utilities::CalculatePricePerUnit(day_price);
 | 
			
		||||
        worktime_from = QTime::fromString(cfg->SpecialDaysWorktime.find(current_special_day_id)->second.pedwt_time_from.c_str());
 | 
			
		||||
        worktime_to = QTime::fromString(cfg->SpecialDaysWorktime.find(current_special_day_id)->second.pedwt_time_to.c_str());
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        // Set new price for the normal day
 | 
			
		||||
 | 
			
		||||
	string worktime_from = "";
 | 
			
		||||
	string worktime_to = "";
 | 
			
		||||
        int pop_id = cfg->PaymentOption.find(payment_option)->second.pop_id;
 | 
			
		||||
        day_price = cfg->PaymentRate.find(pop_id)->second.pra_price;
 | 
			
		||||
 | 
			
		||||
	if (is_special_day)
 | 
			
		||||
	{
 | 
			
		||||
		// Set special day price
 | 
			
		||||
		price_per_unit = Utilities::CalculatePricePerUnit(day_price);
 | 
			
		||||
		worktime_from = cfg->SpecialDaysWorktime.find(current_special_day_id)->second.pedwt_time_from;
 | 
			
		||||
		worktime_to = cfg->SpecialDaysWorktime.find(current_special_day_id)->second.pedwt_time_to;
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		// Set new price for the normal day
 | 
			
		||||
		day_price = cfg->PaymentRate.find(payment_option)->second.pra_price;
 | 
			
		||||
		price_per_unit = Utilities::CalculatePricePerUnit(day_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);
 | 
			
		||||
 | 
			
		||||
		// If no working day found, skip it (recursively call method again)
 | 
			
		||||
		size_t found = 0;
 | 
			
		||||
		found = cfg->WeekDaysWorktime.count(weekdayId);
 | 
			
		||||
        // If no working day found, skip it (recursively call method again)
 | 
			
		||||
        size_t found = 0;
 | 
			
		||||
        found = cfg->WeekDaysWorktime.count(weekdayId);
 | 
			
		||||
 | 
			
		||||
		if (found <= 0)
 | 
			
		||||
		{
 | 
			
		||||
			LOG_DEBUG("- No workday found, trying to find next available day");
 | 
			
		||||
			current_datetime_t = mktime(¤t_datetime);
 | 
			
		||||
			current_datetime_t += 86400;
 | 
			
		||||
			current_datetime = *localtime(¤t_datetime_t);
 | 
			
		||||
        // When no workday found, go to next available day
 | 
			
		||||
        if(found <=0)
 | 
			
		||||
        {
 | 
			
		||||
            LOG_DEBUG("- No workday found, trying to find next available day");
 | 
			
		||||
            inputDate = inputDate.addDays(1);
 | 
			
		||||
            return floor(GetCostFromDuration(cfg, payment_option, inputDate.toString(Qt::ISODate).toStdString().c_str(), durationMin, true, prepaid));
 | 
			
		||||
        }
 | 
			
		||||
        worktime_from = QTime::fromString(cfg->WeekDaysWorktime.find(weekdayId)->second.pwd_time_from.c_str());
 | 
			
		||||
        worktime_to = QTime::fromString(cfg->WeekDaysWorktime.find(weekdayId)->second.pwd_time_to.c_str());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
			char buffer_datetime[80];
 | 
			
		||||
			strftime(buffer_datetime, 80, "%Y-%m-%dT%H:%M:%S", ¤t_datetime);
 | 
			
		||||
    if (price_per_unit < 0) price_per_unit = 1.0f;
 | 
			
		||||
    LOG_DEBUG("Calculated price per minute: ", price_per_unit);
 | 
			
		||||
 | 
			
		||||
			//Make new datetime string and call function again recursively
 | 
			
		||||
			start_datetime = buffer_datetime;
 | 
			
		||||
            return floor(GetCostFromDuration(cfg, payment_option, start_datetime, durationMin, true, prepaid));
 | 
			
		||||
		}
 | 
			
		||||
    if (price_per_unit == 0)
 | 
			
		||||
    {
 | 
			
		||||
        inputDate = inputDate.addDays(1);
 | 
			
		||||
        inputDate.setTime(worktime_from);
 | 
			
		||||
        return GetCostFromDuration(cfg, payment_option, inputDate.toString(Qt::ISODate).toStdString().c_str(), durationMin, true, prepaid);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
		worktime_from = cfg->WeekDaysWorktime.find(weekdayId)->second.pwd_time_from;
 | 
			
		||||
		worktime_to = cfg->WeekDaysWorktime.find(weekdayId)->second.pwd_time_to;
 | 
			
		||||
	}
 | 
			
		||||
    // If overtime flag is set
 | 
			
		||||
    if (overtime || nextDay)
 | 
			
		||||
    {
 | 
			
		||||
        inputDate.setTime(worktime_from);
 | 
			
		||||
        overtime = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	if (price_per_unit < 0) price_per_unit = 1.0f;
 | 
			
		||||
	LOG_DEBUG("Calculated price per minute: ", price_per_unit);
 | 
			
		||||
    // Check prepaid
 | 
			
		||||
    if (!prepaid)
 | 
			
		||||
    {
 | 
			
		||||
        if ((inputDate.time() < worktime_from) || (inputDate.time() > worktime_to))
 | 
			
		||||
        {
 | 
			
		||||
            LOG_DEBUG("[STOP] * Ticket is not valid * ");
 | 
			
		||||
            return 0.0f;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        LOG_DEBUG("* PREPAID MODE ACTIVE *");
 | 
			
		||||
        if (inputDate.time() < worktime_from)
 | 
			
		||||
        {
 | 
			
		||||
            inputDate.setTime(worktime_from);
 | 
			
		||||
        }
 | 
			
		||||
        else if(inputDate.time() > worktime_to)
 | 
			
		||||
        {
 | 
			
		||||
            LOG_DEBUG(" *** PREPAID *** Current time is past the time range end, searching for next available day");
 | 
			
		||||
            inputDate = inputDate.addDays(1);
 | 
			
		||||
            return GetCostFromDuration(cfg, payment_option, inputDate.toString(Qt::ISODate).toStdString().c_str(), durationMin, true, prepaid);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	LOG_DEBUG("Worktime from: ", worktime_from);
 | 
			
		||||
	LOG_DEBUG("Worktime to: ", worktime_to);
 | 
			
		||||
    while(true)
 | 
			
		||||
    {
 | 
			
		||||
        if(total_duration_min <= 0) break;
 | 
			
		||||
 | 
			
		||||
	struct tm from_tm;
 | 
			
		||||
	struct tm to_tm;
 | 
			
		||||
        // Check year period
 | 
			
		||||
        bool isYearPeriodActive = false;
 | 
			
		||||
 | 
			
		||||
	from_tm = Utilities::TimeToStructTm(worktime_from.c_str(), current_datetime.tm_year, current_datetime.tm_mon, current_datetime.tm_mday, current_datetime.tm_wday);
 | 
			
		||||
	from_tm.tm_year = current_datetime.tm_year;
 | 
			
		||||
	from_tm.tm_mon = current_datetime.tm_mon;
 | 
			
		||||
	from_tm.tm_wday = current_datetime.tm_wday;
 | 
			
		||||
	from_tm.tm_mday = current_datetime.tm_mday;
 | 
			
		||||
        //// Parse input date
 | 
			
		||||
        int dayCurrent = inputDate.date().day();
 | 
			
		||||
        int monthCurrent = inputDate.date().month();
 | 
			
		||||
 | 
			
		||||
	to_tm = Utilities::TimeToStructTm(worktime_to.c_str(), current_datetime.tm_year, current_datetime.tm_mon, current_datetime.tm_mday, current_datetime.tm_wday);
 | 
			
		||||
	to_tm.tm_year = current_datetime.tm_year;
 | 
			
		||||
	to_tm.tm_mon = current_datetime.tm_mon;
 | 
			
		||||
	to_tm.tm_wday = current_datetime.tm_wday;
 | 
			
		||||
	to_tm.tm_mday = current_datetime.tm_mday;
 | 
			
		||||
        // Current date time
 | 
			
		||||
        int cdt = (monthCurrent * 100) + dayCurrent;
 | 
			
		||||
 | 
			
		||||
	// Convert tm structures to time_t
 | 
			
		||||
	current_datetime_t = mktime(¤t_datetime);
 | 
			
		||||
        multimap<int, ATBPeriodYear>::iterator year_period_itr;
 | 
			
		||||
        for (year_period_itr = cfg->YearPeriod.begin(); year_period_itr != cfg->YearPeriod.end(); ++year_period_itr)
 | 
			
		||||
        {
 | 
			
		||||
            int dStart = year_period_itr->second.pye_start_day;
 | 
			
		||||
            int dEnd = year_period_itr->second.pye_end_day;
 | 
			
		||||
 | 
			
		||||
	time_t from_datetime_t = mktime(&from_tm);
 | 
			
		||||
	time_t to_datetime_t = mktime(&to_tm);
 | 
			
		||||
            int mStart = year_period_itr->second.pye_start_month;
 | 
			
		||||
            int mEnd = year_period_itr->second.pye_end_month;
 | 
			
		||||
 | 
			
		||||
	// If overtime flag is set
 | 
			
		||||
	if (overtime || nextDay)
 | 
			
		||||
	{
 | 
			
		||||
		current_datetime.tm_hour = from_tm.tm_hour;
 | 
			
		||||
		current_datetime.tm_min = from_tm.tm_min;
 | 
			
		||||
		current_datetime.tm_sec = from_tm.tm_sec;
 | 
			
		||||
		current_datetime_t = mktime(¤t_datetime);
 | 
			
		||||
		LOG_DEBUG(" *** New input date set according to worktime: ", asctime(¤t_datetime));
 | 
			
		||||
		overtime = false;
 | 
			
		||||
	}
 | 
			
		||||
            int start = (mStart * 100) + dStart;
 | 
			
		||||
            int end = (mEnd * 100) + dEnd;
 | 
			
		||||
 | 
			
		||||
	// Validate ticket
 | 
			
		||||
	if (!prepaid)
 | 
			
		||||
	{
 | 
			
		||||
		if ((current_datetime_t < from_datetime_t) || (current_datetime_t > to_datetime_t))
 | 
			
		||||
		{
 | 
			
		||||
			LOG_DEBUG("[STOP] * Ticket is not valid * ");
 | 
			
		||||
			return 0.0f;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		LOG_DEBUG("* PREPAID MODE ACTIVE *");
 | 
			
		||||
            if (cdt >= start && cdt <= end) {
 | 
			
		||||
                isYearPeriodActive = true;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (!isYearPeriodActive)
 | 
			
		||||
        {
 | 
			
		||||
            LOG_DEBUG("Year period is not valid");
 | 
			
		||||
            return 0.0f;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
		if (current_datetime_t < from_datetime_t)
 | 
			
		||||
		{
 | 
			
		||||
			current_datetime.tm_hour = from_tm.tm_hour;
 | 
			
		||||
			current_datetime.tm_min = from_tm.tm_min;
 | 
			
		||||
			current_datetime.tm_sec = from_tm.tm_sec;
 | 
			
		||||
			current_datetime_t = mktime(¤t_datetime);
 | 
			
		||||
			LOG_DEBUG(" *** PREPAID *** Current time is before the time range start, adjusting time to: ", asctime(¤t_datetime));
 | 
			
		||||
		}
 | 
			
		||||
		else if (current_datetime_t > to_datetime_t)
 | 
			
		||||
		{
 | 
			
		||||
			LOG_DEBUG(" *** PREPAID *** Current time is past the time range end, searching for next available day");
 | 
			
		||||
			current_datetime_t = mktime(¤t_datetime);
 | 
			
		||||
			current_datetime_t += 86400;
 | 
			
		||||
			current_datetime = *localtime(¤t_datetime_t);
 | 
			
		||||
        int carry_over_status = 0;
 | 
			
		||||
        carry_over_status = cfg->PaymentOption.find(payment_option)->second.pop_carry_over;
 | 
			
		||||
 | 
			
		||||
			char buffer_datetime[80];
 | 
			
		||||
			strftime(buffer_datetime, 80, "%Y-%m-%dT%H:%M:%S", ¤t_datetime);
 | 
			
		||||
        // Go to next day if minutes not spent
 | 
			
		||||
        if(inputDate.time() >= worktime_to)
 | 
			
		||||
        {
 | 
			
		||||
            if (carry_over_status < 1) break;
 | 
			
		||||
 | 
			
		||||
			//Make new datetime string and call function again recursively
 | 
			
		||||
			start_datetime = buffer_datetime;
 | 
			
		||||
			return floor(GetCostFromDuration(cfg, payment_option, start_datetime, durationMin, true, prepaid));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
            LOG_DEBUG("Reached end of worktime, searching for the next working day");
 | 
			
		||||
            inputDate = inputDate.addDays(1);
 | 
			
		||||
            overtime = true;
 | 
			
		||||
            return GetCostFromDuration(cfg, payment_option, inputDate.toString(Qt::ISODate).toStdString().c_str(), total_duration_min);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
	while (true)
 | 
			
		||||
	{
 | 
			
		||||
		if (!Utilities::IsYearPeriodActive(cfg, ¤t_datetime))
 | 
			
		||||
		{
 | 
			
		||||
			LOG_DEBUG("Year period is not valid");
 | 
			
		||||
			return 0.0f;
 | 
			
		||||
		}
 | 
			
		||||
        // Increment input date minutes for each monetary unit
 | 
			
		||||
        inputDate = inputDate.addSecs(60);
 | 
			
		||||
        total_duration_min -=1;
 | 
			
		||||
        total_cost += price_per_unit;
 | 
			
		||||
 | 
			
		||||
		// Decrement by 1 minute
 | 
			
		||||
		current_datetime_t = mktime(¤t_datetime);
 | 
			
		||||
		current_datetime_t += 60;
 | 
			
		||||
		current_datetime = *localtime(¤t_datetime_t);
 | 
			
		||||
 | 
			
		||||
		total_duration_min -= 1.0f;
 | 
			
		||||
		total_cost += price_per_unit;
 | 
			
		||||
 | 
			
		||||
		// If no minutes left (e.g. spent all of the money before reaching end of worktime)
 | 
			
		||||
		if (total_duration_min <= 0)
 | 
			
		||||
		{
 | 
			
		||||
			//total_duration_min -= 1.0f;
 | 
			
		||||
			LOG_DEBUG("No minutes left ");
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			// If minutes has left but the end of worktime has been reached (go to next day)
 | 
			
		||||
			if (current_datetime_t >= to_datetime_t)
 | 
			
		||||
			{
 | 
			
		||||
				int carry_over_status = 0;
 | 
			
		||||
				carry_over_status = cfg->PaymentOption.find(payment_option)->second.pop_carry_over;
 | 
			
		||||
				total_duration_min += 1.0f;
 | 
			
		||||
				total_cost -= price_per_unit;
 | 
			
		||||
 | 
			
		||||
				LOG_DEBUG("Carry over status: ", carry_over_status);
 | 
			
		||||
				if (carry_over_status < 1) break;
 | 
			
		||||
 | 
			
		||||
				LOG_DEBUG("Reached end of worktime");
 | 
			
		||||
				LOG_DEBUG("Trying to find next available day, min left = ", total_duration_min);
 | 
			
		||||
				current_datetime_t = mktime(¤t_datetime);
 | 
			
		||||
				current_datetime_t += 86400;
 | 
			
		||||
				current_datetime = *localtime(¤t_datetime_t);
 | 
			
		||||
 | 
			
		||||
				char buffer_datetime[80];
 | 
			
		||||
				strftime(buffer_datetime, 80, "%Y-%m-%dT%H:%M:%S", ¤t_datetime);
 | 
			
		||||
 | 
			
		||||
				// Make new datetime string and call function again recursively
 | 
			
		||||
				start_datetime = buffer_datetime;
 | 
			
		||||
				overtime = true;
 | 
			
		||||
				return floor(GetCostFromDuration(cfg, payment_option, start_datetime, total_duration_min));
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	time_t valid_until_datetime_t = current_datetime_t;
 | 
			
		||||
	struct tm valid_until_datetime = *localtime(&valid_until_datetime_t);
 | 
			
		||||
	LOG_DEBUG("Ticket is valid until ", asctime(&valid_until_datetime));
 | 
			
		||||
 | 
			
		||||
	double ret_val = total_cost;
 | 
			
		||||
	total_cost = 0.0f;
 | 
			
		||||
	return floor(ret_val);
 | 
			
		||||
    }
 | 
			
		||||
    qDebug() << "Valid until:" << inputDate.toString(Qt::ISODate).toStdString().c_str();
 | 
			
		||||
    double ret_val = total_cost;
 | 
			
		||||
    total_cost = 0.0f;
 | 
			
		||||
    return ceil(ret_val);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -154,6 +154,8 @@ bool Configuration::ParseJson(Configuration* cfg, const char* json)
 | 
			
		||||
						else if (strcmp(inner_obj_name, "pop_max_time") == 0) PaymentOption.pop_max_time = k->value.GetDouble();
 | 
			
		||||
						else if (strcmp(inner_obj_name, "pop_min_price") == 0) PaymentOption.pop_min_price = k->value.GetDouble();
 | 
			
		||||
						else if (strcmp(inner_obj_name, "pop_carry_over") == 0) PaymentOption.pop_carry_over = k->value.GetInt();
 | 
			
		||||
                        else if (strcmp(inner_obj_name, "pop_daily_card_price") == 0) PaymentOption.pop_daily_card_price = k->value.GetInt();
 | 
			
		||||
                        else if (strcmp(inner_obj_name, "pop_multi_hour_price") == 0) PaymentOption.pop_multi_hour_price = k->value.GetInt();
 | 
			
		||||
						break;
 | 
			
		||||
					case MemberType::DurationType:
 | 
			
		||||
						if (strcmp(inner_obj_name, "pun_id") == 0) Duration.pun_id = k->value.GetInt();
 | 
			
		||||
@@ -209,7 +211,7 @@ bool Configuration::ParseJson(Configuration* cfg, const char* json)
 | 
			
		||||
					cfg->PaymentMethod.insert(pair<int, ATBPaymentMethod>(PaymentMethod.pme_id, PaymentMethod));
 | 
			
		||||
					break;
 | 
			
		||||
				case MemberType::PaymentRateType:
 | 
			
		||||
					cfg->PaymentRate.insert(pair<int, ATBPaymentRate>(PaymentRate.pra_payment_unit_id, PaymentRate));
 | 
			
		||||
                    cfg->PaymentRate.insert(pair<int, ATBPaymentRate>(PaymentRate.pra_payment_option_id, PaymentRate));
 | 
			
		||||
					break;
 | 
			
		||||
				case MemberType::PaymentOptionType:
 | 
			
		||||
					cfg->PaymentOption.insert(pair<int, ATBPaymentOption>(PaymentOption.pop_payment_method_id, PaymentOption));
 | 
			
		||||
 
 | 
			
		||||
@@ -8,12 +8,15 @@ static int protection_counter = 0;
 | 
			
		||||
/// </summary>
 | 
			
		||||
/// <param name="pra_price"></param>
 | 
			
		||||
/// <returns></returns>
 | 
			
		||||
double Utilities::CalculatePricePerUnit(double pra_price)
 | 
			
		||||
double Utilities::CalculatePricePerUnit(double pra_price, double durationUnit)
 | 
			
		||||
{
 | 
			
		||||
	try
 | 
			
		||||
	{
 | 
			
		||||
		double price_per_unit = pra_price;
 | 
			
		||||
		price_per_unit /= 60.0f; // Divided by 60 because price per unit is set per hour and we are using minutes
 | 
			
		||||
        double unit = durationUnit;
 | 
			
		||||
 | 
			
		||||
        if(unit < 0 || unit > 65535 ) unit = 60.0f;
 | 
			
		||||
        price_per_unit /= unit; // Divided by 60 because price per unit is set per hour and we are using minutes
 | 
			
		||||
		//printf("Price per unit (min) is: %lf\n", price_per_unit);
 | 
			
		||||
		return price_per_unit;
 | 
			
		||||
	}
 | 
			
		||||
@@ -33,7 +36,7 @@ time_t Utilities::GetCurrentLocalTime()
 | 
			
		||||
		memset(&tm_curr_time, '\0', sizeof(struct tm));
 | 
			
		||||
 | 
			
		||||
		tm_curr_time = *localtime(&curr_time);
 | 
			
		||||
		curr_time = mktime(&tm_curr_time) - timezone;
 | 
			
		||||
        curr_time = mktime(&tm_curr_time); //- timezone;
 | 
			
		||||
		return curr_time;
 | 
			
		||||
	}
 | 
			
		||||
	catch (...)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										171
									
								
								main/main.cpp
									
									
									
									
									
								
							
							
						
						
									
										171
									
								
								main/main.cpp
									
									
									
									
									
								
							@@ -1,10 +1,9 @@
 | 
			
		||||
#include <calculate_price.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#ifdef WIN32
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#include <iomanip>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include <calculate_price.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
extern "C" char* strptime(const char* s,
 | 
			
		||||
                          const char* f,
 | 
			
		||||
@@ -27,87 +26,101 @@ extern "C" char* strptime(const char* s,
 | 
			
		||||
 | 
			
		||||
#include <QDebug>
 | 
			
		||||
#include <QDateTime>
 | 
			
		||||
 | 
			
		||||
int main() {
 | 
			
		||||
 | 
			
		||||
    parking_tariff_t *tariff = 0;
 | 
			
		||||
    if (init_tariff(&tariff, "/etc/psa_tariff/")) {
 | 
			
		||||
    if (init_tariff(&tariff, "C:\\Users\\MROD\\Documents\\QtCreator\\Old\\build-MOBILISIS-Calculator-Desktop_Qt_5_12_12_MSVC2017_32bit-Debug\\main\\etc\\psa_tariff\\tariff01.json"))
 | 
			
		||||
    {
 | 
			
		||||
        struct price_t price;
 | 
			
		||||
        memset(&price, 0x00, sizeof(price));
 | 
			
		||||
        QDateTime start = QDateTime::currentDateTime();
 | 
			
		||||
        QDateTime start = QDateTime::fromString("2023-06-01T07:50:00.000Z",Qt::ISODate); //QDateTime::currentDateTime();
 | 
			
		||||
        time_t start_parking_time = start.toSecsSinceEpoch() / 60;
 | 
			
		||||
        time_t end_parking_time = start_parking_time + 60;
 | 
			
		||||
        time_t end_parking_time = start_parking_time + 1230;
 | 
			
		||||
 | 
			
		||||
        if (compute_price_for_parking_ticket(tariff,
 | 
			
		||||
                                             start_parking_time,
 | 
			
		||||
                                             end_parking_time,
 | 
			
		||||
                                             &price)) {
 | 
			
		||||
            qDebug() << "price=" << price.netto;
 | 
			
		||||
        }
 | 
			
		||||
//        if (compute_price_for_parking_ticket(tariff,
 | 
			
		||||
//                                             start_parking_time,
 | 
			
		||||
//                                             end_parking_time,
 | 
			
		||||
//                                             &price))
 | 
			
		||||
//        {
 | 
			
		||||
//            qDebug() << "GetCostFromDuration() => price=" << price.netto;
 | 
			
		||||
//        }
 | 
			
		||||
 | 
			
		||||
        // tests
 | 
			
		||||
        struct tm now;
 | 
			
		||||
        memset(&now, 0, sizeof(now));
 | 
			
		||||
//        QString duration;
 | 
			
		||||
//        if(compute_duration_for_parking_ticket(tariff,start_parking_time,1650,duration))
 | 
			
		||||
//        {
 | 
			
		||||
//            qDebug() << "GetDurationFromCost() => duration=" << duration;
 | 
			
		||||
//        }
 | 
			
		||||
 | 
			
		||||
        // 3.Jan 2023 -> Tuesday
 | 
			
		||||
        strptime("2023-01-03T14:00:00", "%Y-%m-%dT%H:%M:%S", &now);
 | 
			
		||||
        for (int i = 0; i < 600; ++i) {
 | 
			
		||||
            start_parking_time = mktime(&now);
 | 
			
		||||
            end_parking_time = start_parking_time + 240; // duration == 240
 | 
			
		||||
        // Daily ticket
 | 
			
		||||
//        compute_duration_for_daily_ticket(tariff,start.toString(Qt::ISODate),3);
 | 
			
		||||
 | 
			
		||||
            if (compute_price_for_parking_ticket(tariff,
 | 
			
		||||
                                                 start_parking_time,
 | 
			
		||||
                                                 end_parking_time,
 | 
			
		||||
                                                 &price)) {
 | 
			
		||||
                int const zone = get_zone_nr();
 | 
			
		||||
                switch (zone) {
 | 
			
		||||
                case 1:
 | 
			
		||||
                    assert(price.netto == 879);    // expected value: 880
 | 
			
		||||
                    break;
 | 
			
		||||
                case 2:
 | 
			
		||||
                    /* fall through */
 | 
			
		||||
                case 3:
 | 
			
		||||
                    assert(price.netto == 1920);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            time_t t = start_parking_time + 60;
 | 
			
		||||
            now = *localtime(&t);
 | 
			
		||||
        }
 | 
			
		||||
        //
 | 
			
		||||
        // test May 1st 2023
 | 
			
		||||
        //
 | 
			
		||||
        memset(&now, 0, sizeof(now));
 | 
			
		||||
        strptime("2023-04-30T06:00:00", "%Y-%m-%dT%H:%M:%S", &now);
 | 
			
		||||
        now.tm_hour -= 1; // for ctime
 | 
			
		||||
        for (int i=0; i<6*24; ++i) {
 | 
			
		||||
            int const duration = 120;
 | 
			
		||||
            time_t t = mktime(&now);
 | 
			
		||||
            start_parking_time = t / 60;
 | 
			
		||||
            end_parking_time = start_parking_time + duration;
 | 
			
		||||
            compute_duration_for_24hour_daily_ticket(tariff,start.toString(Qt::ISODate),3);
 | 
			
		||||
 | 
			
		||||
            if (compute_price_for_parking_ticket(tariff,
 | 
			
		||||
                                                 start_parking_time,
 | 
			
		||||
                                                 end_parking_time,
 | 
			
		||||
                                                 &price)) {
 | 
			
		||||
                int const zone = get_zone_nr();
 | 
			
		||||
                switch (zone) {
 | 
			
		||||
                case 1:
 | 
			
		||||
                    qDebug() << i << zone << ctime(&t) << price.netto << " FT";
 | 
			
		||||
                    assert(price.netto == 440);
 | 
			
		||||
                    break;
 | 
			
		||||
                case 2:
 | 
			
		||||
                    /* fall through */
 | 
			
		||||
                case 3:
 | 
			
		||||
                    qDebug() << i << zone << ctime(&t) << price.netto << " FT";
 | 
			
		||||
                    assert(price.netto == 960);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        //Configuration* cfg, QString start_datetime, uint8_t payment_option, bool carry_over
 | 
			
		||||
        //        // tests
 | 
			
		||||
        //        struct tm now;
 | 
			
		||||
        //        memset(&now, 0, sizeof(now));
 | 
			
		||||
 | 
			
		||||
            t = (start_parking_time + 60)*60;
 | 
			
		||||
            now = *localtime(&t);
 | 
			
		||||
        }
 | 
			
		||||
        //        // 3.Jan 2023 -> Tuesday
 | 
			
		||||
        //        strptime("2023-01-03T14:00:00", "%Y-%m-%dT%H:%M:%S", &now);
 | 
			
		||||
        //        for (int i = 0; i < 600; ++i) {
 | 
			
		||||
        //            start_parking_time = mktime(&now);
 | 
			
		||||
        //            end_parking_time = start_parking_time + 240; // duration == 240
 | 
			
		||||
 | 
			
		||||
        //            if (compute_price_for_parking_ticket(tariff,
 | 
			
		||||
        //                                                 start_parking_time,
 | 
			
		||||
        //                                                 end_parking_time,
 | 
			
		||||
        //                                                 &price)) {
 | 
			
		||||
        //                int const zone = get_zone_nr(1);
 | 
			
		||||
        //                switch (zone) {
 | 
			
		||||
        //                case 1:
 | 
			
		||||
        //                    assert(price.netto == 879);    // expected value: 880
 | 
			
		||||
        //                    break;
 | 
			
		||||
        //                case 2:
 | 
			
		||||
        //                    /* fall through */
 | 
			
		||||
        //                case 3:
 | 
			
		||||
        //                    assert(price.netto == 1920);
 | 
			
		||||
        //                    break;
 | 
			
		||||
        //                }
 | 
			
		||||
        //            }
 | 
			
		||||
        //            time_t t = start_parking_time + 60;
 | 
			
		||||
        //            now = *localtime(&t);
 | 
			
		||||
        //        }
 | 
			
		||||
        //        //
 | 
			
		||||
        //        // test May 1st 2023
 | 
			
		||||
        //        //
 | 
			
		||||
        //        memset(&now, 0, sizeof(now));
 | 
			
		||||
        //        strptime("2023-04-30T06:00:00", "%Y-%m-%dT%H:%M:%S", &now);
 | 
			
		||||
        //        now.tm_hour -= 1; // for ctime
 | 
			
		||||
        //        // for (int i=0; i<6*24; ++i) {
 | 
			
		||||
        //        for (int i=0; i<1; ++i) {
 | 
			
		||||
        //            int const duration = 120;
 | 
			
		||||
        //            time_t t = mktime(&now);
 | 
			
		||||
        //            start_parking_time = t / 60;
 | 
			
		||||
        //            end_parking_time = start_parking_time + duration;
 | 
			
		||||
 | 
			
		||||
        //            if (compute_price_for_parking_ticket(tariff,
 | 
			
		||||
        //                                                 start_parking_time,
 | 
			
		||||
        //                                                 end_parking_time,
 | 
			
		||||
        //                                                 &price)) {
 | 
			
		||||
        //                int const zone = get_zone_nr();
 | 
			
		||||
        //                switch (zone) {
 | 
			
		||||
        //                case 1:
 | 
			
		||||
        //                    qDebug() << i << zone << ctime(&t) << price.netto << " FT";
 | 
			
		||||
        //                    assert(price.netto == 440);
 | 
			
		||||
        //                    break;
 | 
			
		||||
        //                case 2:
 | 
			
		||||
        //                    /* fall through */
 | 
			
		||||
        //                case 3:
 | 
			
		||||
        //                    qDebug() << i << zone << ctime(&t) << price.netto << " FT";
 | 
			
		||||
        //                    assert(price.netto == 960);
 | 
			
		||||
        //                    break;
 | 
			
		||||
        //                }
 | 
			
		||||
        //            }
 | 
			
		||||
 | 
			
		||||
        //            t = (start_parking_time + 60)*60;
 | 
			
		||||
        //            now = *localtime(&t);
 | 
			
		||||
        //        }
 | 
			
		||||
 | 
			
		||||
        free_tariff(tariff);
 | 
			
		||||
    }
 | 
			
		||||
@@ -144,7 +157,7 @@ int main() {
 | 
			
		||||
            struct tm now; // = Utilities::DateTimeToStructTm("2023-03-01T16:00:00");
 | 
			
		||||
            memset(&now, 0, sizeof(now));
 | 
			
		||||
            char buffer[64];
 | 
			
		||||
//#if 0
 | 
			
		||||
            //#if 0
 | 
			
		||||
            // 3.Jan 2023 -> Tuesday
 | 
			
		||||
            strptime("2023-01-03T14:00:00", "%Y-%m-%dT%H:%M:%S", &now);
 | 
			
		||||
            for (int i = 0; i < 600; ++i) {
 | 
			
		||||
@@ -219,8 +232,8 @@ int main() {
 | 
			
		||||
 | 
			
		||||
                int const duration = 120;
 | 
			
		||||
                double cost = calculator.GetCostFromDuration(&cfg,
 | 
			
		||||
                                PaymentOption::Option1, buffer, duration,
 | 
			
		||||
                                false, true);
 | 
			
		||||
                                                             PaymentOption::Option1, buffer, duration,
 | 
			
		||||
                                                             false, true);
 | 
			
		||||
 | 
			
		||||
                switch (zone) {
 | 
			
		||||
                case 1:
 | 
			
		||||
@@ -258,8 +271,8 @@ int main() {
 | 
			
		||||
 | 
			
		||||
                int const duration = 120;
 | 
			
		||||
                double cost = calculator.GetCostFromDuration(&cfg,
 | 
			
		||||
                                PaymentOption::Option1, buffer, duration,
 | 
			
		||||
                                false, true);
 | 
			
		||||
                                                             PaymentOption::Option1, buffer, duration,
 | 
			
		||||
                                                             false, true);
 | 
			
		||||
 | 
			
		||||
                switch (zone) {
 | 
			
		||||
                case 1:
 | 
			
		||||
@@ -292,8 +305,8 @@ int main() {
 | 
			
		||||
 | 
			
		||||
                double const compCost = (duration < 15) ? 0 : duration * ((zone == 1) ? 3.6666 : 8.0);
 | 
			
		||||
                double cost = calculator.GetCostFromDuration(&cfg,
 | 
			
		||||
                                PaymentOption::Option1, buffer, duration,
 | 
			
		||||
                                false, true);
 | 
			
		||||
                                                             PaymentOption::Option1, buffer, duration,
 | 
			
		||||
                                                             false, true);
 | 
			
		||||
                if (fabs(cost - compCost) > 1.0) { // zone 1 has rounding errors
 | 
			
		||||
                    cout << "ERROR ===> [" << i << "] " << asctime(&now)
 | 
			
		||||
                         << " - Total cost is: " << cost << " FT (computed="
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
		Reference in New Issue
	
	Block a user