const fs = require('fs');
const path = require('path');
const xml2js = require('xml2js');
const AdmZip = require('adm-zip');
const moment = require('moment-timezone');
const _ = require('lodash'); // TODO
const log = require('loglevel');
const formData = require('form-data');

const constants = require('../models/constants');
const constantsXml = require('../models/constants-xml');
const enums = require('../models/enums');
const enumsXml = require('../models/enums-xml');
const HelpersCtrl = require('./helpers');

const FinancialTransaction = require('../models/financial-transaction');
const FinancialTransactionServ = require('../services/financial-transaction');

const Employee = require('../models/employee');
const EmployeeServ = require('../services/employee');
const AccountHolderCtrl = require('./account-holder');
const AccountHolderServ = require('../services/account-holder');
const ReasonsServ = require('../services/reasons');
const PosTransactionCtrl = require('./pos-transaction');
const FareDistanceConfiguration = require('../models/fare-distance-configuration');
const FareEnvironmentConfiguration = require('../models/fare-environment-configuration');
const FareTable = require('../models/fare-table');
const Translations = require('../models/translations');
const SmartCardServ = require('../services/smart-card');
const SmartCardActions = require('../models/smart-card');

class FinancialTransactionCtrl {
    constructor () {
        this.modelTicket = FinancialTransaction.Ticket;
        this.modelFinancial = FinancialTransaction.FinancialTransaction;
        this.modelOrganizations = FinancialTransaction.Organizations;
        this.service = new FinancialTransactionServ();

        this.employeeWorkTimeModel = Employee.EmployeeWorkTime;
        this.employeeServ = new EmployeeServ();
        this.accountHolderCtrl = new AccountHolderCtrl();
        this.accountHolderServ = new AccountHolderServ();
        this.reasonsServ = new ReasonsServ();
        this.posTransactionCtrl = new PosTransactionCtrl();
        this.fareDistanceModel = FareDistanceConfiguration;
        this.fareEnvironmentModel = FareEnvironmentConfiguration;
        this.fareTableModel = FareTable;
        this.translationsModel = Translations;
        this.smartCardServ = new SmartCardServ();
        this.smartCardActionsModel = SmartCardActions;
        this.helpersCtrl = new HelpersCtrl();
        this.dateTTimeFormat = 'YYYY-MM-DD[T]HH:mm:ss';
        this.dateTimeFormat = 'DD/MM/YYYY HH:mm:ss';
        this.dateRFormat = 'YYYY-MM-DD';
        this.dateFormat = 'DD/MM/YYYY';
        this.timeFormat = 'HH:mm';
        this.weekDayFormat = 'dddd';
        this.employeeRoleNameLength = 25;
    }

    test = async (req, res) => {
        try {
            await this.service.test();

            res.status(200).json();
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    user = async (req, res) => {
        try {
            let transactions = [];

            if (!/maintenance|supervisor/i.test(global.currentEmployeeData.employeeRoleName)) {
                transactions = await this.modelFinancial.find({employeeId: global.currentEmployeeData.employeeId, transactionTime: {$gt: moment.utc().subtract(8, 'days').startOf('day').add(2, 'hours').toISOString()}}).sort({transactionTime: -1});
            } else {
                transactions = await this.modelFinancial.find({transactionTime: {$gt: moment.utc().subtract(14, 'days').startOf('day').add(2, 'hours').toISOString()}}).sort({transactionTime: -1}); // 2024-05-23T08:04:25.915+00:00
            }

            const waybill = await this.getWaybill(_.groupBy(transactions, 'employeeId'), true);

            res.status(200).json(waybill);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    waybill = async (req, res) => {
        try {
            let transactions = [];

            if (!/maintenance|supervisor/i.test(global.currentEmployeeData.employeeRoleName)) {
                transactions = await this.modelFinancial.find({employeeId: global.currentEmployeeData.employeeId, transactionTime: {$gt: moment.utc(global.currentEmployeeData.employeeLoginTime).toISOString()}}).sort({transactionTime: -1});
            } else {
                transactions = await this.modelFinancial.find({transactionTime: {$gt: moment.utc().subtract(1, 'days').startOf('day').add(2, 'hours').toISOString()}}).sort({transactionTime: -1});
            }

            const waybill = await this.getWaybill(_.groupBy(transactions, 'employeeId'), true);

            res.status(200).json(waybill);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    getWaybill = async (transactions, isReport) => {
        const translations1st = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'translations', global.currentPosData.firstLanguage+'.json')));

        const employees = await this.employeeServ.getUsersForType(constants.deviceType.pos);

        if (!Object.keys(transactions).includes(global.currentEmployeeData.employeeId)) {
            transactions[global.currentEmployeeData.employeeId] = [];
        }

        const usersTransactions = Object.keys(transactions);
        let waybills = [];

        for (let i=0; i<usersTransactions.length; i++) {
            const employee = employees.find((f) => f.id == usersTransactions[i]);
            const employeeSession = await this.employeeWorkTimeModel.findOne({employeeId: employee.id}).sort({loginTime: -1});
            const currentEmployeeData = {
                employeeId: employee.id,
                employeeName: `${employee.firstName} ${employee.lastName}`,
                employeeRoleId: employee.employeeRoleExternalId,
                employeeLoginTime: moment.utc(employeeSession.loginTime).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat),
                employeeLogoutTime: (moment.utc(employeeSession.logoutTime).toISOString() == moment.utc(0).toISOString()) ? moment.utc().utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat) : moment.utc(employeeSession.logoutTime).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat),
                employeeStartBalanceAmount: (global.currentPosData.cashFloat) ? employeeSession.startBalanceAmount : 0,
                employeeEndBalanceAmount: (global.currentPosData.cashFloat) ? employeeSession.endBalanceAmount : 0,
                employeeSessionId: (employeeSession.sessionId) ? employeeSession.sessionId : 0
            };

            const todayTransactions = transactions[usersTransactions[i]].filter((f) => moment.utc(f.transactionTime).format(this.dateFormat) == moment.utc().format(this.dateFormat));

            const saleTransactions = todayTransactions.filter((f) => f.activityType != constantsXml.activityType.issueReversal);
            const refundTransactions = todayTransactions.filter((f) => f.activityType == constantsXml.activityType.issueReversal);

            const totalSales = saleTransactions.map((m) => m.farePrice).reduce((p, n) => p + n, 0);
            const totalSalesCash = saleTransactions.filter((f) => f.paymentInstrument == constantsXml.paymentInstrument.prepayCash).map((m) => m.farePrice).reduce((p, n) => p + n, 0);
            const totalSalesCard = saleTransactions.filter((f) => f.paymentInstrument != constantsXml.paymentInstrument.prepayCash).map((m) => m.farePrice).reduce((p, n) => p + n, 0);
            const totalRefunds = refundTransactions.map((m) => m.farePrice).reduce((p, n) => p + n, 0);
            const totalRefundsCash = refundTransactions.filter((f) => f.paymentInstrument == constantsXml.paymentInstrument.prepayCash).map((m) => m.farePrice).reduce((p, n) => p + n, 0);
            const totalRefundsCard = refundTransactions.filter((f) => f.paymentInstrument != constantsXml.paymentInstrument.prepayCash).map((m) => m.farePrice).reduce((p, n) => p + n, 0);
            const currentCash = currentEmployeeData.employeeStartBalanceAmount + totalSalesCash - totalRefundsCash;

            const totalRefundsPrefix = (isReport && totalRefunds > 0) ? '-' : '';
            const totalRefundsCashPrefix = (isReport && totalRefundsCash > 0) ? '-' : '';
            const totalRefundsCardPrefix = (isReport && totalRefundsCard > 0) ? '-' : '';

            let userWaybill = {
                shiftDate: moment.utc().utcOffset(global.currentPosData.utcOffset).format(this.dateFormat),
                shiftTime: `${currentEmployeeData.employeeLoginTime.slice(11)} - ${currentEmployeeData.employeeLogoutTime.slice(11)}`,
                cashIn: (currentEmployeeData.employeeStartBalanceAmount / Math.pow(10, global.currentPosData.currencyDecimal)).toFixed(global.currentPosData.currencyDecimal),
                cashOut: (currentCash / Math.pow(10, global.currentPosData.currencyDecimal)).toFixed(global.currentPosData.currencyDecimal),
                totalSales: (totalSales / Math.pow(10, global.currentPosData.currencyDecimal)).toFixed(global.currentPosData.currencyDecimal),
                totalSalesNum: saleTransactions.length,
                totalSalesCash: (totalSalesCash / Math.pow(10, global.currentPosData.currencyDecimal)).toFixed(global.currentPosData.currencyDecimal),
                totalSalesCard: (totalSalesCard / Math.pow(10, global.currentPosData.currencyDecimal)).toFixed(global.currentPosData.currencyDecimal),
                totalRefunds: `${totalRefundsPrefix}${(totalRefunds / Math.pow(10, global.currentPosData.currencyDecimal)).toFixed(global.currentPosData.currencyDecimal)}`,
                totalRefundsNum: refundTransactions.length,
                totalRefundsCash: `${totalRefundsCashPrefix}${(totalRefundsCash / Math.pow(10, global.currentPosData.currencyDecimal)).toFixed(global.currentPosData.currencyDecimal)}`,
                totalRefundsCard: `${totalRefundsCardPrefix}${(totalRefundsCard / Math.pow(10, global.currentPosData.currencyDecimal)).toFixed(global.currentPosData.currencyDecimal)}`,
                totalNetSales: ((totalSales - totalRefunds) / Math.pow(10, global.currentPosData.currencyDecimal)).toFixed(global.currentPosData.currencyDecimal)
            };

            let waybillItems = [];
            let transactionsItems = todayTransactions.reduce((p, n) => {(p[n['waybillName']] = p[n['waybillName']] || []).push(n); return p;}, []);

            for (let j=0; j<todayTransactions.length; j++) {
                if (!waybillItems.some((s) => s.name == todayTransactions[j].waybillName)) {
                    waybillItems.push({
                        name: todayTransactions[j].waybillName,
                        number: transactionsItems[todayTransactions[j].waybillName].length,
                        valueNum: (transactionsItems[todayTransactions[j].waybillName].map((m) => m.farePrice).reduce((p, n) => p + n, 0) / Math.pow(10, global.currentPosData.currencyDecimal)).toFixed(global.currentPosData.currencyDecimal),
                        value: `${(transactionsItems[todayTransactions[j].waybillName].map((m) => m.farePrice).reduce((p, n) => p + n, 0) / Math.pow(10, global.currentPosData.currencyDecimal)).toFixed(global.currentPosData.currencyDecimal)} ${global.currentPosData.currencyCode}`
                    });
                }
            }

            let canceled = waybillItems.filter((f) => f.name.includes(translations1st['Canceled']));
            for (let k=0; k<canceled.length; k++) {
                let substract = waybillItems.findIndex((f) => f.name == canceled[k].name.slice(0, -(translations1st['Canceled'].length + 3)));
                if (substract != -1) {
                    waybillItems[substract].number = waybillItems[substract].number - canceled[k].number;
                    waybillItems[substract].valueNum = waybillItems[substract].valueNum - canceled[k].valueNum;
                    waybillItems[substract].value = `${waybillItems[substract].valueNum.toFixed(global.currentPosData.currencyDecimal)} ${global.currentPosData.currencyCode}`;
                }
            }

            userWaybill.items = waybillItems.filter((f) => !f.name.includes(translations1st['Canceled']) && f.number > 0);

            let weeklyTransactions = {
                [moment.utc().subtract(1, 'days').format('ddd')]: transactions[usersTransactions[i]].filter((f) => moment.utc(f.transactionTime).format(this.dateFormat) == moment.utc().subtract(1, 'days').format(this.dateFormat)).length,
                [moment.utc().subtract(2, 'days').format('ddd')]: transactions[usersTransactions[i]].filter((f) => moment.utc(f.transactionTime).format(this.dateFormat) == moment.utc().subtract(2, 'days').format(this.dateFormat)).length,
                [moment.utc().subtract(3, 'days').format('ddd')]: transactions[usersTransactions[i]].filter((f) => moment.utc(f.transactionTime).format(this.dateFormat) == moment.utc().subtract(3, 'days').format(this.dateFormat)).length,
                [moment.utc().subtract(4, 'days').format('ddd')]: transactions[usersTransactions[i]].filter((f) => moment.utc(f.transactionTime).format(this.dateFormat) == moment.utc().subtract(4, 'days').format(this.dateFormat)).length,
                [moment.utc().subtract(5, 'days').format('ddd')]: transactions[usersTransactions[i]].filter((f) => moment.utc(f.transactionTime).format(this.dateFormat) == moment.utc().subtract(5, 'days').format(this.dateFormat)).length,
                [moment.utc().subtract(6, 'days').format('ddd')]: transactions[usersTransactions[i]].filter((f) => moment.utc(f.transactionTime).format(this.dateFormat) == moment.utc().subtract(6, 'days').format(this.dateFormat)).length,
                [moment.utc().subtract(7, 'days').format('ddd')]: transactions[usersTransactions[i]].filter((f) => moment.utc(f.transactionTime).format(this.dateFormat) == moment.utc().subtract(7, 'days').format(this.dateFormat)).length,
                [moment.utc().subtract(1, 'days').format('ddd')+'Date']: moment.utc().subtract(1, 'days').format(this.dateFormat),
                [moment.utc().subtract(2, 'days').format('ddd')+'Date']: moment.utc().subtract(2, 'days').format(this.dateFormat),
                [moment.utc().subtract(3, 'days').format('ddd')+'Date']: moment.utc().subtract(3, 'days').format(this.dateFormat),
                [moment.utc().subtract(4, 'days').format('ddd')+'Date']: moment.utc().subtract(4, 'days').format(this.dateFormat),
                [moment.utc().subtract(5, 'days').format('ddd')+'Date']: moment.utc().subtract(5, 'days').format(this.dateFormat),
                [moment.utc().subtract(6, 'days').format('ddd')+'Date']: moment.utc().subtract(6, 'days').format(this.dateFormat),
                [moment.utc().subtract(7, 'days').format('ddd')+'Date']: moment.utc().subtract(7, 'days').format(this.dateFormat),
                ['Previous'+moment.utc().subtract(8, 'days').format('ddd')]: transactions[usersTransactions[i]].filter((f) => moment.utc(f.transactionTime).format(this.dateFormat) == moment.utc().subtract(8, 'days').format(this.dateFormat)).length,
                ['Previous'+moment.utc().subtract(9, 'days').format('ddd')]: transactions[usersTransactions[i]].filter((f) => moment.utc(f.transactionTime).format(this.dateFormat) == moment.utc().subtract(9, 'days').format(this.dateFormat)).length,
                ['Previous'+moment.utc().subtract(10, 'days').format('ddd')]: transactions[usersTransactions[i]].filter((f) => moment.utc(f.transactionTime).format(this.dateFormat) == moment.utc().subtract(10, 'days').format(this.dateFormat)).length,
                ['Previous'+moment.utc().subtract(11, 'days').format('ddd')]: transactions[usersTransactions[i]].filter((f) => moment.utc(f.transactionTime).format(this.dateFormat) == moment.utc().subtract(11, 'days').format(this.dateFormat)).length,
                ['Previous'+moment.utc().subtract(12, 'days').format('ddd')]: transactions[usersTransactions[i]].filter((f) => moment.utc(f.transactionTime).format(this.dateFormat) == moment.utc().subtract(12, 'days').format(this.dateFormat)).length,
                ['Previous'+moment.utc().subtract(13, 'days').format('ddd')]: transactions[usersTransactions[i]].filter((f) => moment.utc(f.transactionTime).format(this.dateFormat) == moment.utc().subtract(13, 'days').format(this.dateFormat)).length,
                ['Previous'+moment.utc().subtract(14, 'days').format('ddd')]: transactions[usersTransactions[i]].filter((f) => moment.utc(f.transactionTime).format(this.dateFormat) == moment.utc().subtract(14, 'days').format(this.dateFormat)).length,
                ['Previous'+moment.utc().subtract(8, 'days').format('ddd')+'Date']: moment.utc().subtract(8, 'days').format(this.dateFormat),
                ['Previous'+moment.utc().subtract(9, 'days').format('ddd')+'Date']: moment.utc().subtract(9, 'days').format(this.dateFormat),
                ['Previous'+moment.utc().subtract(10, 'days').format('ddd')+'Date']: moment.utc().subtract(10, 'days').format(this.dateFormat),
                ['Previous'+moment.utc().subtract(11, 'days').format('ddd')+'Date']: moment.utc().subtract(11, 'days').format(this.dateFormat),
                ['Previous'+moment.utc().subtract(12, 'days').format('ddd')+'Date']: moment.utc().subtract(12, 'days').format(this.dateFormat),
                ['Previous'+moment.utc().subtract(13, 'days').format('ddd')+'Date']: moment.utc().subtract(13, 'days').format(this.dateFormat),
                ['Previous'+moment.utc().subtract(14, 'days').format('ddd')+'Date']: moment.utc().subtract(14, 'days').format(this.dateFormat)
            };

            const employeeWorkTime = await this.employeeWorkTimeModel.find({employeeId: currentEmployeeData.employeeId, loginTime: {$gte: moment.utc().subtract(30, 'days').startOf('day').add(2, 'hours').toISOString()}}).sort({loginTime: -1});
            let monthlyCash = {
                cashIn: (global.currentPosData.cashFloat) ? (employeeWorkTime.map((m) => m.startBalanceAmount).reduce((p, n) => p + n, 0) / Math.pow(10, global.currentPosData.currencyDecimal)).toFixed(global.currentPosData.currencyDecimal) : 0,
                cashOut: (global.currentPosData.cashFloat) ? (employeeWorkTime.map((m) => m.endBalanceAmount).reduce((p, n) => p + n, 0) / Math.pow(10, global.currentPosData.currencyDecimal)).toFixed(global.currentPosData.currencyDecimal) : 0
            };

            const penaltiesTransactions = await this.modelFinancial.find({activityType: constantsXml.activityType.penaltyPayment, employeeId: currentEmployeeData.employeeId, transactionTime: {$gt: moment.utc().startOf('month').startOf('day').add(2, 'hours').toISOString()}}).sort({transactionTime: -1});

            const penaltiesTransactionsToday = penaltiesTransactions.filter((f) => moment.utc(f.transactionTime).format(this.dateFormat) == moment.utc().format(this.dateFormat));
            const penaltiesTransactionsYesterday = penaltiesTransactions.filter((f) => moment.utc(f.transactionTime).format(this.dateFormat) == moment.utc().subtract(1, 'days').format(this.dateFormat));
            const penaltiesTransactionsWeek = penaltiesTransactions.filter((f) => moment.utc(f.transactionTime).format(this.dateFormat) >= moment.utc().startOf('week').format(this.dateFormat) && moment.utc(f.transactionTime).format(this.dateFormat) <= moment.utc().format(this.dateFormat));
            const penaltiesTransactionsMonth = penaltiesTransactions;

            const penalties = {
                today: {
                    count: penaltiesTransactionsToday.length,
                    total: (penaltiesTransactionsToday.map((m) => m.farePrice).reduce((p, n) => p + n, 0) / Math.pow(10, global.currentPosData.currencyDecimal)).toFixed(global.currentPosData.currencyDecimal)
                },
                yesterday: {
                    count: penaltiesTransactionsYesterday.length,
                    total: (penaltiesTransactionsYesterday.map((m) => m.farePrice).reduce((p, n) => p + n, 0) / Math.pow(10, global.currentPosData.currencyDecimal)).toFixed(global.currentPosData.currencyDecimal)
                },
                week: {
                    count: penaltiesTransactionsWeek.length,
                    total: (penaltiesTransactionsWeek.map((m) => m.farePrice).reduce((p, n) => p + n, 0) / Math.pow(10, global.currentPosData.currencyDecimal)).toFixed(global.currentPosData.currencyDecimal)
                },
                month: {
                    count: penaltiesTransactionsMonth.length,
                    total: (penaltiesTransactionsMonth.map((m) => m.farePrice).reduce((p, n) => p + n, 0) / Math.pow(10, global.currentPosData.currencyDecimal)).toFixed(global.currentPosData.currencyDecimal)
                }
            };

            waybills.push({
                employeeIpa: currentEmployeeData.employeeId,
                employeeName: currentEmployeeData.employeeName,
                posLocation: global.currentPosData.location,
                sessionId: currentEmployeeData.employeeSessionId,
                weeklyTransactions: weeklyTransactions,
                monthlyCash: monthlyCash,
                employeeWaybill: userWaybill,
                penalties: penalties
            });
        }

        return waybills;
    }

    waybillByDate = async (req, res) => {
        try {
            const employeeId = req.query.employeeIpa;
            const shiftDate = req.query.shiftDate;
            let transactions = await this.modelFinancial.find({transactionTime: {$gte: moment.utc(shiftDate, this.dateFormat).startOf('day').toISOString()}}).sort({transactionTime: -1});

            const waybill = await this.getWaybillByDate(employeeId, shiftDate, _.groupBy(transactions, 'employeeId'), true);

            res.status(200).json(waybill);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    getWaybillByDate = async (employeeId, transactionsDate, transactions, isReport) => {
        const translations1st = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'translations', global.currentPosData.firstLanguage+'.json')));

        if (!Object.keys(transactions).includes(employeeId)) {
            transactions[employeeId] = [];
        }

        const usersTransactions = Object.keys(transactions);
        let waybills = [];

        const employeeSessions = await this.employeeWorkTimeModel.find({employeeId: employeeId, $and: [{loginTime: {$gte: moment.utc(transactionsDate, this.dateFormat).toISOString()}}, {loginTime: {$lt: moment.utc(transactionsDate, this.dateFormat).add(1, 'days').toISOString()}}]}).sort({loginTime: -1});

        for (let i=0; i<employeeSessions.length; i++) {
            const todayTransactions = transactions[employeeSessions[i].employeeId].filter((f) => moment.utc(f.transactionTime).isBetween(moment.utc(employeeSessions[i].loginTime), (moment.utc(employeeSessions[i].logoutTime).toISOString() == moment.utc(0).toISOString()) ? moment.utc() : moment.utc(employeeSessions[i].logoutTime), 'seconds'));

            const currentEmployeeData = {
                employeeId: employeeSessions[i].employeeId,
                employeeName: `${employeeSessions[i].firstName} ${employeeSessions[i].lastName}`,
                employeeRoleId: employeeSessions[i].employeeRoleId,
                employeeLoginTime: moment.utc(employeeSessions[i].loginTime).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat),
                employeeLogoutTime: (moment.utc(employeeSessions[i].logoutTime).toISOString() == moment.utc(0).toISOString()) ? moment.utc().utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat) : moment.utc(employeeSessions[i].logoutTime).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat),
                employeeStartBalanceAmount: (global.currentPosData.cashFloat) ? employeeSessions[i].startBalanceAmount : 0,
                employeeEndBalanceAmount: (global.currentPosData.cashFloat) ? employeeSessions[i].endBalanceAmount : 0,
                employeeSessionId: (employeeSessions[i].sessionId) ? employeeSessions[i].sessionId : 0
            };

            const saleTransactions = todayTransactions.filter((f) => f.activityType != constantsXml.activityType.issueReversal);
            const refundTransactions = todayTransactions.filter((f) => f.activityType == constantsXml.activityType.issueReversal);

            const totalSales = saleTransactions.map((m) => m.farePrice).reduce((p, n) => p + n, 0);
            const totalSalesCash = saleTransactions.filter((f) => f.paymentInstrument == constantsXml.paymentInstrument.prepayCash).map((m) => m.farePrice).reduce((p, n) => p + n, 0);
            const totalSalesCard = saleTransactions.filter((f) => f.paymentInstrument != constantsXml.paymentInstrument.prepayCash).map((m) => m.farePrice).reduce((p, n) => p + n, 0);
            const totalRefunds = refundTransactions.map((m) => m.farePrice).reduce((p, n) => p + n, 0);
            const totalRefundsCash = refundTransactions.filter((f) => f.paymentInstrument == constantsXml.paymentInstrument.prepayCash).map((m) => m.farePrice).reduce((p, n) => p + n, 0);
            const totalRefundsCard = refundTransactions.filter((f) => f.paymentInstrument != constantsXml.paymentInstrument.prepayCash).map((m) => m.farePrice).reduce((p, n) => p + n, 0);
            const currentCash = ((global.currentPosData.cashFloat) ? currentEmployeeData.employeeStartBalanceAmount : 0) + totalSalesCash - totalRefundsCash;

            const totalRefundsPrefix = (isReport && totalRefunds > 0) ? '-' : '';
            const totalRefundsCashPrefix = (isReport && totalRefundsCash > 0) ? '-' : '';
            const totalRefundsCardPrefix = (isReport && totalRefundsCard > 0) ? '-' : '';

            let userWaybill = {
                shiftDate: moment.utc(transactionsDate, this.dateFormat).utcOffset(global.currentPosData.utcOffset).format(this.dateFormat),
                shiftTime: `${currentEmployeeData.employeeLoginTime.slice(11)} - ${currentEmployeeData.employeeLogoutTime.slice(11)}`,
                cashIn: (currentEmployeeData.employeeStartBalanceAmount / Math.pow(10, global.currentPosData.currencyDecimal)).toFixed(global.currentPosData.currencyDecimal),
                cashOut: (currentCash / Math.pow(10, global.currentPosData.currencyDecimal)).toFixed(global.currentPosData.currencyDecimal),
                totalSales: (totalSales / Math.pow(10, global.currentPosData.currencyDecimal)).toFixed(global.currentPosData.currencyDecimal),
                totalSalesNum: saleTransactions.length,
                totalSalesCash: (totalSalesCash / Math.pow(10, global.currentPosData.currencyDecimal)).toFixed(global.currentPosData.currencyDecimal),
                totalSalesCard: (totalSalesCard / Math.pow(10, global.currentPosData.currencyDecimal)).toFixed(global.currentPosData.currencyDecimal),
                totalRefunds: `${totalRefundsPrefix}${(totalRefunds / Math.pow(10, global.currentPosData.currencyDecimal)).toFixed(global.currentPosData.currencyDecimal)}`,
                totalRefundsNum: refundTransactions.length,
                totalRefundsCash: `${totalRefundsCashPrefix}${(totalRefundsCash / Math.pow(10, global.currentPosData.currencyDecimal)).toFixed(global.currentPosData.currencyDecimal)}`,
                totalRefundsCard: `${totalRefundsCardPrefix}${(totalRefundsCard / Math.pow(10, global.currentPosData.currencyDecimal)).toFixed(global.currentPosData.currencyDecimal)}`,
                totalNetSales: ((totalSales - totalRefunds) / Math.pow(10, global.currentPosData.currencyDecimal)).toFixed(global.currentPosData.currencyDecimal)
            };

            let waybillItems = [];
            let transactionsItems = todayTransactions.reduce((p, n) => {(p[n['waybillName']] = p[n['waybillName']] || []).push(n); return p;}, []);

            for (let j=0; j<todayTransactions.length; j++) {
                if (!waybillItems.some((s) => s.name == todayTransactions[j].waybillName)) {
                    waybillItems.push({
                        name: todayTransactions[j].waybillName,
                        number: transactionsItems[todayTransactions[j].waybillName].length,
                        valueNum: (transactionsItems[todayTransactions[j].waybillName].map((m) => m.farePrice).reduce((p, n) => p + n, 0) / Math.pow(10, global.currentPosData.currencyDecimal)).toFixed(global.currentPosData.currencyDecimal),
                        value: `${(transactionsItems[todayTransactions[j].waybillName].map((m) => m.farePrice).reduce((p, n) => p + n, 0) / Math.pow(10, global.currentPosData.currencyDecimal)).toFixed(global.currentPosData.currencyDecimal)} ${global.currentPosData.currencyCode}`
                    });
                }
            }

            let canceled = waybillItems.filter((f) => f.name.includes(translations1st['Canceled']));
            for (let k=0; k<canceled.length; k++) {
                let substract = waybillItems.findIndex((f) => f.name == canceled[k].name.slice(0, -(translations1st['Canceled'].length + 3)));
                waybillItems[substract].number = waybillItems[substract].number - canceled[k].number;
                waybillItems[substract].valueNum = waybillItems[substract].valueNum - canceled[k].valueNum;
                waybillItems[substract].value = `${waybillItems[substract].valueNum.toFixed(global.currentPosData.currencyDecimal)} ${global.currentPosData.currencyCode}`;
            }

            userWaybill.items = waybillItems.filter((f) => !f.name.includes(translations1st['Canceled']) && f.number > 0);

            waybills.push({
                employeeIpa: currentEmployeeData.employeeId,
                employeeName: currentEmployeeData.employeeName,
                posLocation: global.currentPosData.location,
                sessionId: currentEmployeeData.employeeSessionId,
                weeklyTransactions: null,
                monthlyCash: null,
                employeeWaybill: userWaybill,
                penalties: null
            });
        }

        return waybills;
    }

    allTransactions = async (req, res) => {
        try {
            const translations1st = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'translations', global.currentPosData.firstLanguage+'.json')));

            let transactions = [];

            if (!/maintenance|supervisor/i.test(global.currentEmployeeData.employeeRoleName)) {
                transactions = await this.modelFinancial.find({employeeId: global.currentEmployeeData.employeeId, $and: [{transactionTime: {$gt: moment.utc(global.currentEmployeeData.employeeLoginTime).toISOString()}}, {transactionTime: {$lt: moment.utc().toISOString()}}]}).sort({transactionTime: -1});
            } else {
                transactions = await this.modelFinancial.find({transactionTime: {$gt: moment.utc().subtract(1, 'days').startOf('day').add(2, 'hours').toISOString()}}).sort({transactionTime: -1});
            }

            let allTransactions = [];
            if (transactions) {
                for (let i=0; i<transactions.length; i++) {
                    const ticket = (transactions[i].ticketId) ? await this.modelTicket.findOne({ _id: transactions[i].ticketId }) : null;
                    const ticketItem = (transactions[i].ticketItemId) ? ticket.items.find((f) => f._id == transactions[i].ticketItemId) : null;
                    let transaction = {
                        id: transactions[i].id, // Buffer.from(transactions[i].id, 'hex').toString('base64'), // shorter id
                        ticket: ticket,
                        ticketFareItemIssuedNumbers: transactions.filter((f) => f.ticketId == transactions[i].ticketId).map((m) => m.fareItemIssuedNumber),
                        ticketItem: ticketItem,
                        fareItemIssuedNumber: transactions[i].fareItemIssuedNumber,
                        date: moment.utc(transactions[i].transactionTime).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat),
                        employee: transactions[i].employeeId,
                        total: transactions[i].farePrice / Math.pow(10, global.currentPosData.currencyDecimal)
                    };

                    let type = '';
                    let details = '';

                    switch (transactions[i].fareItemType) {
                        case constantsXml.fareItemType.productTicket:
                        case constantsXml.fareItemType.productAccount:
                            type = translations1st['Product'];
                            details = `${transactions[i].riderName} ${transactions[i].prodName}`;
                        break;
                        case constantsXml.fareItemType.penaltyTicket:
                            type = translations1st['Penalty'];
                            details = `ID ${transactions[i].penaltyRefNumber}`;
                        break;
                        case constantsXml.fareItemType.creditEpurseAccount:
                        case constantsXml.fareItemType.refundEpurseAccount:
                            type = translations1st['Top up'];
                            details = `ID ${transactions[i].accountNo}`;
                        break;
                        default:
                            type = translations1st['Ticket'];
                            let group = (transactions[i].numberOfPassengers) ? ((transactions[i].numberOfPassengers > 1) ? `(${transactions[i].numberOfPassengers} passengers)` : '') : '';
                            details = `${transactions[i].riderName} ${(transactions[i].prodName) ? transactions[i].prodName : transactions[i].fareItemName} ${group}`;
                    }

                    if ([constantsXml.activityType.issueReversal, constantsXml.activityType.debit].includes(transactions[i].activityType)) {
                        type = `${type} - ${translations1st['Canceled']}`;
                        transaction.canceled = true;
                    }

                    transaction.type = type;
                    transaction.details = details;

                    allTransactions.push(transaction);
                }
            }

            res.status(200).json(allTransactions);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    routeTypes = async (req, res) => {
        try {
            const riderId = req.query.riderId;

            const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
            const transitTypes = await this.fareEnvironmentModel.TransitTypes.find({fareEnvironmentConfigurationId: activeConfig._id, externalId: {$ne: 0}});
            const transitTypesLen = transitTypes.length;

            let transitTypesList = [];
            for (let i=0; i<transitTypesLen; i++) {
                if (transitTypes[i].fareSystemModel != constantsXml.fareSystemModel.polygons) {
                    transitTypesList.push({
                        id: transitTypes[i].id,
                        externalId: transitTypes[i].externalId,
                        name: transitTypes[i].name,
                        nameTranslated: transitTypes[i].nameTranslations,
                        fareSystemModel: transitTypes[i].fareSystemModel,
                        orderValue: (global.currentPosData.transitTypeOrder) ? global.currentPosData.transitTypeOrder[transitTypes[i].externalId] : transitTypes[i].shortNumber,
                        isTrip: [constantsXml.fareSystemModel.distanceBased, constantsXml.fareSystemModel.distanceBasedStaged, constantsXml.fareSystemModel.distanceBasedPerKilometer, constantsXml.fareSystemModel.fromToStation].includes(transitTypes[i].fareSystemModel)
                    });
                }
            }
            const transitTypesListOrdered = transitTypesList.sort((a, b) => a.orderValue - b.orderValue);

            res.status(200).json(transitTypesListOrdered);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    routes = async (req, res) => {
        try {
            const externalId = req.query.externalId;

            const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
            const routes = await this.fareEnvironmentModel.Routes.find({fareEnvironmentConfigurationId: activeConfig._id, machineId: {$ne: 0}});
            const routesLen = routes.length;

            let routesList = [];
            for (let i=0; i<routesLen; i++) {
                routesList.push({
                    machineId: routes[i].machineId,
                    routeType: routes[i].routeType.externalId,
                    externalId: routes[i].externalId,
                    name: routes[i].name,
                    destination: routes[i].destination,
                    variant: routes[i].variant,
                    version: routes[i].version
                });
            }
            const routesListOrdered = routesList.sort((a, b) => a.machineId - b.machineId);

            res.status(200).json(routesListOrdered);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    riders = async (req, res) => {
        try {
            const transitTypeId = req.query.transitTypeId;
            const withoutGroup = this.helpersCtrl.getBool(req.query.withoutGroup);
            const hasCash = this.helpersCtrl.getBool(req.query.hasCash);

            const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
            const transitType = await this.fareEnvironmentModel.TransitTypes.findOne({fareEnvironmentConfigurationId: activeConfig._id, externalId: transitTypeId});
            let riderTypes = await this.fareTableModel.RiderTypes.find({fareEnvironmentConfigurationId: activeConfig._id, type: 'Rider'});

            if (transitTypeId != 0 && withoutGroup) {
                // riderTypes = riderTypes.filter((f) => f.isGroupFare == false);
            }

            if (transitTypeId != 0 && hasCash) {
                riderTypes = riderTypes.filter((f) => f.hasCash == true);
            }

            if (transitTypeId == 0) {
                let riderType = [];
                let ridersLen = riderTypes.length;
                for (let i=0; i<ridersLen; i++) {
                    riderType.push({
                        id: riderTypes[i].id,
                        name: riderTypes[i].name,
                        externalId: riderTypes[i].externalId,
                        isGroup: riderTypes[i].isGroupFare,
                        hasCash: riderTypes[i].hasCash,
                        hasSmartCard: riderTypes[i].hasSmartCard,
                        storedValue: riderTypes[i].storedValue,
                        nameTranslated: riderTypes[i].nameTranslations
                    });
                }
                return res.status(200).json(riderType);
            }

            let riders = [];
            const riderTypesLen = riderTypes.length;
            for (let i=0; i<riderTypesLen; i++) {
                switch (transitType.fareSystemModel) {
                    case constantsXml.fareSystemModel.flat:
                        const flatFare = await this.fareTableModel.FlatFares.find({fareEnvironmentConfigurationId: activeConfig._id, riderType: riderTypes[i].externalId, transitType: transitTypeId});
                        if (flatFare.length) {
                            riders.push(riderTypes[i]);
                        }
                    break;
                    case constantsXml.fareSystemModel.distanceBased:
                    case constantsXml.fareSystemModel.distanceBasedStaged:
                    case constantsXml.fareSystemModel.distanceBasedPerKilometer:
                        const distanceFare = await this.fareDistanceModel.DistanceFares.find({fareEnvironmentConfigurationId: activeConfig._id, riderType: riderTypes[i].externalId, transitType: transitTypeId});
                        if (distanceFare.length) {
                            riders.push(riderTypes[i]);
                        }
                    break;
                    case constantsXml.fareSystemModel.fromToStation:
                        const fromToStationFare = await this.fareDistanceModel.FromToStationFares.find({fareEnvironmentConfigurationId: activeConfig._id, riderType: riderTypes[i].externalId});
                        if (fromToStationFare.length) {
                            riders.push(riderTypes[i]);
                        }
                    break;
                    case constantsXml.fareSystemModel.zones:
                        const zoneFare = await this.fareTableModel.ZoneFares.find({fareEnvironmentConfigurationId: activeConfig._id, riderType: riderTypes[i].externalId, transitType: transitTypeId});
                        if (zoneFare.length) {
                            riders.push(riderTypes[i]);
                        }
                    break;
                }
            }

            let ridersModel = [];
            let ridersLen = riders.length;
            for (let i=0; i<ridersLen; i++) {
                ridersModel.push({
                    id: riders[i].id,
                    name: riders[i].name,
                    externalId: riders[i].externalId,
                    isGroup: riders[i].isGroupFare,
                    hasCash: riders[i].hasCash,
                    storedValue: riders[i].storedValue,
                    nameTranslated: riders[i].nameTranslations
                });
            }

            res.status(200).json(ridersModel);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    ridersVerify = async (req, res) => {
        try {
            const transitTypeId = req.query.transitTypeId;
            const withoutGroup = this.helpersCtrl.getBool(req.query.withoutGroup);
            const hasCash = this.helpersCtrl.getBool(req.query.hasCash);

            const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
            const transitType = await this.fareEnvironmentModel.TransitTypes.findOne({fareEnvironmentConfigurationId: activeConfig._id, externalId: transitTypeId});
            let riderTypes = await this.fareTableModel.RiderTypes.find({fareEnvironmentConfigurationId: activeConfig._id, type: 'Rider'});

            riderTypes = riderTypes.filter((f) => f.isBusinessOnly == false && f.externalId != 1);

            if (transitTypeId == 0) {
                return res.status(200).json(riderTypes);
            }

            let riders = [];
            const riderTypesLen = riderTypes.length;
            for (let i=0; i<riderTypesLen; i++) {
                switch (transitType.fareSystemModel) {
                    case constantsXml.fareSystemModel.flat:
                        const flatFare = await this.fareTableModel.FlatFares.find({fareEnvironmentConfigurationId: activeConfig._id, riderType: riderTypes[i].externalId, transitType: transitTypeId});
                        if (flatFare.length) {
                            riders.push(riderTypes[i]);
                        }
                    break;
                    case constantsXml.fareSystemModel.distanceBased:
                    case constantsXml.fareSystemModel.distanceBasedStaged:
                    case constantsXml.fareSystemModel.distanceBasedPerKilometer:
                        const distanceFare = await this.fareDistanceModel.DistanceFares.find({fareEnvironmentConfigurationId: activeConfig._id, riderType: riderTypes[i].externalId, transitType: transitTypeId});
                        if (distanceFare.length) {
                            riders.push(riderTypes[i]);
                        }
                    break;
                    case constantsXml.fareSystemModel.fromToStation:
                        const fromToStationFare = await this.fareDistanceModel.FromToStationFares.find({fareEnvironmentConfigurationId: activeConfig._id, riderType: riderTypes[i].externalId});
                        if (fromToStationFare.length) {
                            riders.push(riderTypes[i]);
                        }
                    break;
                    case constantsXml.fareSystemModel.zones:
                        const zoneFare = await this.fareTableModel.ZoneFares.find({fareEnvironmentConfigurationId: activeConfig._id, riderType: riderTypes[i].externalId, transitType: transitTypeId});
                        if (zoneFare.length) {
                            riders.push(riderTypes[i]);
                        }
                    break;
                }
            }

            if (withoutGroup) {
                riders = riders.filter((f) => f.isGroupFare == false);
            }

            let ridersModel = [];
            let ridersLen = riders.length;
            for (let i=0; i<ridersLen; i++) {
                ridersModel.push({
                    id: riders[i].id,
                    name: riders[i].name,
                    externalId: riders[i].externalId,
                    isGroup: riders[i].isGroupFare,
                    nameTranslated: riders[i].nameTranslations
                });
            };

            res.status(200).json(ridersModel);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    tripRoutes = async (req, res) => {
        try {
            const transitTypeId = req.query.transitTypeId;

            const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
            const transitType = await this.fareEnvironmentModel.TransitTypes.findOne({fareEnvironmentConfigurationId: activeConfig._id, externalId: transitTypeId});
            const secondLanguage = new RegExp('^'+global.currentPosData.secondLanguage.split('-')[0]);

            let routeType = [];
            const transitTypeRoutesLen = transitType.routes.length;
            for (let i=0; i<transitTypeRoutesLen; i++) {
                let route = await this.fareEnvironmentModel.Routes.findOne({fareEnvironmentConfigurationId: activeConfig._id, externalId: transitType.routes[i].externalId}).sort({'stopsOnRoute.sequenceNumber': 1});

                let originStop = await this.fareEnvironmentModel.Stops.findOne({fareEnvironmentConfigurationId: activeConfig._id, machineId: route.stopsOnRoute[0].machineId});
                let originStopTranslation = (originStop) ? await this.translationsModel.findOne({fareEnvironmentConfigurationId: activeConfig._id, key: originStop.stopName, locale: secondLanguage}) : '';
                let originStation = await this.fareEnvironmentModel.Stations.findOne({fareEnvironmentConfigurationId: activeConfig._id, stationCode: originStop.stationCode});
                let originStationTranslation = (originStation) ? await this.translationsModel.findOne({fareEnvironmentConfigurationId: activeConfig._id, key: originStation.stationName, locale: secondLanguage}) : '';

                let destinationStop = await this.fareEnvironmentModel.Stops.findOne({fareEnvironmentConfigurationId: activeConfig._id, machineId: route.stopsOnRoute[route.stopsOnRoute.length-1].machineId});
                let destinationStopTranslation = (destinationStop) ? await this.translationsModel.findOne({fareEnvironmentConfigurationId: activeConfig._id, key: destinationStop.stopName, locale: secondLanguage}) : '';
                let destinationStation = await this.fareEnvironmentModel.Stations.findOne({fareEnvironmentConfigurationId: activeConfig._id, stationCode: destinationStop.stationCode});
                let destinationStationTranslation = (destinationStation) ? await this.translationsModel.findOne({fareEnvironmentConfigurationId: activeConfig._id, key: destinationStation.stationName, locale: secondLanguage}) : '';

                routeType.push({
                    routeId: route.machineId,
                    name: route.name,
                    originStopCode: originStop.stopCode,
                    originStopName: originStop.stopName,
                    originStopNameTranslate: (originStopTranslation) ? originStopTranslation.value : originStop.stopName+'?',
                    originStationCode: originStation.stationCode,
                    originStationName: originStation.stationName,
                    originStationNameTranslated: (originStationTranslation) ? originStationTranslation.value : originStation.stationName+'?',
                    destinationStopCode: destinationStop.stopCode,
                    destinationStopName: destinationStop.stopName,
                    destinationStopNameTranslate: (destinationStopTranslation) ? destinationStopTranslation.value : destinationStop.stopName+'?',
                    destinationStationCode: destinationStation.stationCode,
                    destinationStationName: destinationStation.stationName,
                    destinationStationNameTranslated: (destinationStationTranslation) ? destinationStationTranslation.value : destinationStation.stationName+'?'
                });
            }

            const routeTypesOrdered = routeType.sort((a, b) => a.name.localeCompare(b.name));

            res.status(200).json(routeTypesOrdered);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    tripStops = async (req, res) => {
        try {
            const routeId = req.query.routeId;
            const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
            const route = await this.fareEnvironmentModel.Routes.findOne({fareEnvironmentConfigurationId: activeConfig._id, machineId: routeId}).sort({'stopsOnRoute.sequenceNumber': 1});
            const secondLanguage = new RegExp('^'+global.currentPosData.secondLanguage.split('-')[0]);

            let stopsOnRouteList = [];
            const stopsOnRouteLen = (route.stopsOnRoute).length;
            for (let i=0; i<stopsOnRouteLen; i++) {
                let stop = await this.fareEnvironmentModel.Stops.findOne({fareEnvironmentConfigurationId: activeConfig._id, machineId: route.stopsOnRoute[i].machineId});
                let stopTranslation = (stop) ? await this.translationsModel.findOne({fareEnvironmentConfigurationId: activeConfig._id, key: stop.stopName, locale: secondLanguage}) : '';
                let station = await this.fareEnvironmentModel.Stations.findOne({fareEnvironmentConfigurationId: activeConfig._id, stationCode: stop.stationCode});
                let stationTranslation = (station) ? await this.translationsModel.findOne({fareEnvironmentConfigurationId: activeConfig._id, key: station.stationName, locale: secondLanguage}) : '';

                if (!stopsOnRouteList.find((f) => f.stationName === station.stationName)) {
                    stopsOnRouteList.push({
                        id: stop.id,
                        machineId: stop.machineId,
                        stopCode: stop.stopCode,
                        stopName: stop.stopName,
                        stopNameTranslate: (stopTranslation) ? stopTranslation.value : stop.stopName+'?',
                        routeId: route.machineId,
                        route: route.name,
                        // distance: null,
                        distanceMeters: parseInt(route.stopsOnRoute[i].distanceOnRouteInMeters),
                        // distanceFromPrevious: null,
                        distanceFromPreviousMeters: parseInt(route.stopsOnRoute[i].distanceFromPreviousStopInMeters),
                        sequenceNumber: parseInt(route.stopsOnRoute[i].sequenceNumber),
                        stationCode: station.stationCode,
                        stationName: station.stationName,
                        stationNameTranslated: (stationTranslation) ? stationTranslation.value : station.stationName+'?'
                    });
                }
            }

            res.status(200).json(stopsOnRouteList);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    trip = async (req, res) => {
        try {
            const origStopId = req.query.origStop;
            const destStopId = req.query.destStop;
            const riderId = req.query.riderId;
            const routeId = req.query.routeId;
            const transitTypeId = req.query.transitTypeId;
            const accountId = req.query.accountId;
            const travelType = req.query.travelType;
            const flexibilityType = req.query.flexibilityType;
            const saleType = constants.salesTypes.direct;
            const secondLanguage = new RegExp('^'+global.currentPosData.secondLanguage.split('-')[0]);

            const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
            let originStop = await this.fareEnvironmentModel.Stops.findOne({fareEnvironmentConfigurationId: activeConfig._id, _id: origStopId});
            let originStopTranslation = (originStop) ? await this.translationsModel.findOne({fareEnvironmentConfigurationId: activeConfig._id, key: originStop.stopName, locale: secondLanguage}) : '';
            let originStation = await this.fareEnvironmentModel.Stations.findOne({fareEnvironmentConfigurationId: activeConfig._id, stationCode: originStop.stationCode});
            let originStationTranslation = (originStation) ? await this.translationsModel.findOne({fareEnvironmentConfigurationId: activeConfig._id, key: originStation.stationName, locale: secondLanguage}) : '';

            let destinationStop = await this.fareEnvironmentModel.Stops.findOne({fareEnvironmentConfigurationId: activeConfig._id, _id: destStopId});
            let destinationStopTranslation = (destinationStop) ? await this.translationsModel.findOne({fareEnvironmentConfigurationId: activeConfig._id, key: destinationStop.stopName, locale: secondLanguage}) : '';
            let destinationStation = await this.fareEnvironmentModel.Stations.findOne({fareEnvironmentConfigurationId: activeConfig._id, stationCode: destinationStop.stationCode});
            let destinationStationTranslation = (destinationStation) ? await this.translationsModel.findOne({fareEnvironmentConfigurationId: activeConfig._id, key: destinationStation.stationName, locale: secondLanguage}) : '';

            const route = await this.fareEnvironmentModel.Routes.findOne({fareEnvironmentConfigurationId: activeConfig._id, machineId: routeId});
            const transitType = await this.fareEnvironmentModel.TransitTypes.findOne({fareEnvironmentConfigurationId: activeConfig._id, externalId: transitTypeId});

            let trip = {
                id: [transitTypeId, routeId, originStation.stationCode, destinationStation.stationCode, riderId, accountId].join('-'),
                route: route.name,
                routeTranslated: route.name,
                busStopFromCode: originStop.stopCode,
                busStopFromName: originStop.stopName,
                busStopFromNameTranslate: (originStopTranslation) ? originStopTranslation.value : originStop.stopName+'?',
                busStationFromCode: originStation.stationCode,
                busStationFromName: originStation.stationName,
                busStationFromNameTranslate: (originStationTranslation) ? originStationTranslation.value : originStation.stationName+'?',
                busStopToCode: destinationStop.stopCode,
                busStopToName: destinationStop.stopName,
                busStopToNameTranslate: (destinationStopTranslation) ? destinationStopTranslation.value : destinationStop.stopName+'?',
                busStationToCode: destinationStation.stationCode,
                busStationToName: destinationStation.stationName,
                busStationToNameTranslate: (destinationStationTranslation) ? destinationStationTranslation.value : destinationStation.stationName+'?',
                name: transitType.name,
                nameTranslated: transitType.nameTranslations,
                fareType: transitType.fareSystemModel
            };

            let mediaType = constants.mediaType.ticket;

            switch (transitType.fareSystemModel) {
                case constantsXml.fareSystemModel.distanceBased:
                case constantsXml.fareSystemModel.distanceBasedStaged: {
                    const distanceFare = await this.fareDistanceModel.DistanceFares.find({fareEnvironmentConfigurationId: activeConfig._id, riderType: riderId, mediaType: mediaType, transitType: transitTypeId});
                    const distanceSegmentType = await this.fareEnvironmentModel.DistanceSegmentTypes.findOne({fareEnvironmentConfigurationId: activeConfig._id, 'transitTypes.externalId': transitTypeId});
                    const stageByRouteBorderAdjustments = await this.fareEnvironmentModel.StageByRouteBorderAdjustments.find({fareEnvironmentConfigurationId: activeConfig._id, route: route.externalId});
                    const fromStopToStopFareAdjustments = await this.fareDistanceModel.FromStopToStopFareAdjustments.findOne({fareEnvironmentConfigurationId: activeConfig._id, route: route.externalId, riderType: riderId, mediaType: constants.mediaType.ticket, boardingStop: originStation.stationCode, alightingStop: destinationStation.stationCode});
                    const multiPriceProduct = await this.fareTableModel.MultiPriceProducts.findOne({fareEnvironmentConfigurationId: activeConfig._id, mediaType: constants.mediaType.ticket, fareType: transitType.fareSystemModel});

                    let distanceSegments = distanceSegmentType.distanceSegments.map((m) => ({
                        stagesTraveled: parseInt(m.sequenceNumber),
                        startDistance: parseInt(m.startDistanceInMeters),
                        endDistance: parseInt(m.endDistanceInMeters),
                        baseFare: distanceFare.find((f) => parseInt(f.startDistanceInMeters) == parseInt(m.startDistanceInMeters) && parseInt(f.endDistanceInMeters) == parseInt(m.endDistanceInMeters) && parseInt(f.stagesOrZonesTraveled) == parseInt(m.sequenceNumber)).baseFare
                    }));

                    let stopsOnRoute = route.stopsOnRoute.map((m) => ({
                        stopCode: m.stopCode,
                        distanceOnRoute: parseInt(m.distanceOnRouteInMeters),
                        sequenceNumber: parseInt(m.sequenceNumber)
                    }));

                    let stagesTransformed = this.tripAdjustment({
                        adjustments: stageByRouteBorderAdjustments,
                        segments: distanceSegments,
                        stopsOnRoute: stopsOnRoute
                    });

                    const tripStart = stagesTransformed.flatMap((fm) => fm.stopsOnRoute).find((f) => f.stopCode == originStation.stationCode);
                    const tripEnd = stagesTransformed.flatMap((fm) => fm.stopsOnRoute).find((f) => f.stopCode == destinationStation.stationCode);
                    const tripStagesTraveled = stagesTransformed.find((f) => f.segment.stagesTraveled == (tripEnd.stagesTraveled - tripStart.stagesTraveled + 1)).segment;

                    const distanceSegment = (fromStopToStopFareAdjustments != null) ? fromStopToStopFareAdjustments : tripStagesTraveled;

                    trip.price = distanceSegment.baseFare / Math.pow(10, global.currentPosData.currencyDecimal);
                    trip.priceCent = distanceSegment.baseFare;

                    const multiPriceAdjustment = this.multiPriceAdjustment(trip.priceCent, multiPriceProduct, riderId, transitTypeId, travelType, flexibilityType, saleType);
                    trip.priceCentCalculated = multiPriceAdjustment.priceCentCalculated;
                    trip.adjustmentExternalId = multiPriceAdjustment.adjustmentExternalId;

                    trip.isReturn = false;
                    trip.stageTraveled = distanceSegment.stagesTraveled;
                break;
                }
                case constantsXml.fareSystemModel.distanceBasedPerKilometer: {
                    const distanceFare = await this.fareDistanceModel.DistanceFares.find({fareEnvironmentConfigurationId: activeConfig._id, riderType: riderId, mediaType: mediaType, transitType: transitTypeId});
                    const fromStopToStopFareAdjustments = await this.fareDistanceModel.FromStopToStopFareAdjustments.findOne({fareEnvironmentConfigurationId: activeConfig._id, route: route.externalId, riderType: riderId, mediaType: constants.mediaType.ticket, boardingStop: originStation.stationCode, alightingStop: destinationStation.stationCode});
                    const multiPriceProduct = await this.fareTableModel.MultiPriceProducts.findOne({fareEnvironmentConfigurationId: activeConfig._id, mediaType: constants.mediaType.ticket, fareType: transitType.fareSystemModel});

                    let stopsOnRoute = route.stopsOnRoute.map((m) => ({
                        stopCode: m.stopCode,
                        distanceOnRoute: parseInt(m.distanceOnRouteInMeters),
                        sequenceNumber: parseInt(m.sequenceNumber)
                    }));

                    const tripStart = stopsOnRoute.find((f) => f.stopCode == originStation.stationCode);
                    const tripEnd = stopsOnRoute.find((f) => f.stopCode == destinationStation.stationCode);
                    const tripDistanceTraveled = {baseFare: Math.ceil((tripEnd.distanceOnRoute - tripStart.distanceOnRoute) / 1000 * distanceFare[0].baseFare), distanceBetweenStops: (tripEnd.distanceOnRoute - tripStart.distanceOnRoute) / 1000};

                    const distanceSegment = (fromStopToStopFareAdjustments != null) ? fromStopToStopFareAdjustments : tripDistanceTraveled;

                    trip.price = this.roundPriceByRoundingType(distanceSegment.baseFare, multiPriceProduct.priceRoundingType) / Math.pow(10, global.currentPosData.currencyDecimal);
                    trip.priceCent = this.roundPriceByRoundingType(distanceSegment.baseFare, multiPriceProduct.priceRoundingType);

                    const multiPriceAdjustment = this.multiPriceAdjustment(trip.priceCent, multiPriceProduct, riderId, transitTypeId, travelType, flexibilityType, saleType);
                    trip.priceCentCalculated = multiPriceAdjustment.priceCentCalculated;
                    trip.adjustmentExternalId = multiPriceAdjustment.adjustmentExternalId;

                    trip.isReturn = false;
                    trip.stageTraveled = 1;
                break;
                }
                case constantsXml.fareSystemModel.fromToStation: {
                    const fromToStationFare = await this.fareDistanceModel.FromToStationFares.findOne({fareEnvironmentConfigurationId: activeConfig._id, riderType: riderId, mediaType: mediaType, route: route.externalId, $and: [{ $or: [{fromStation: originStation.stationCode, toStation: destinationStation.stationCode}, {fromStation: destinationStation.stationCode, toStation: originStation.stationCode}] }]});

                    if (fromToStationFare.isOneWayOnly) {
                        trip.price = fromToStationFare.baseFare / Math.pow(10, global.currentPosData.currencyDecimal);
                        trip.priceCent = fromToStationFare.baseFare;
                    } else {
                        trip.priceReturn = fromToStationFare.baseFare / Math.pow(10, global.currentPosData.currencyDecimal);
                        trip.priceCentReturn = fromToStationFare.baseFare;
                    }
                    trip.priceCentCalculated = fromToStationFare.baseFare;
                    trip.isReturn = (fromToStationFare.isOneWayOnly) ? false : true;
                break;
                }
            }

            const riderType = await this.fareTableModel.RiderTypes.findOne({fareEnvironmentConfigurationId: activeConfig._id, type: 'Rider', externalId: riderId});

            if (riderType.isGroupFare) {
                trip.priceReturn = 0;
                trip.priceCentReturn = 0;
                trip.isReturn = false;
            }

            const vatRates = await this.fareTableModel.VatRates.find({fareEnvironmentConfigurationId: activeConfig._id});
            const vatRate = (vatRates.find((f) => [constants.productType.multiPrice].includes(f.associatedProductTypes))) ? vatRates.find((f) => [constants.productType.multiPrice].includes(f.associatedProductTypes)) : vatRates.find((f) => f.isDefault == true);
            trip.vatRate = (vatRate) ? vatRate.percents : 0;

            res.status(200).json(trip);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    tripAdjustment = (stage) => {
        let stopsOnRoute = stage.stopsOnRoute;
        let lowerBorderPreviousStage;
        let previousSegmentEnd = 0;

        return stage.segments.map((segment) => {
            const stageAdjustment = stage.adjustments.find((f) => f.lowerDistanceSegment === segment.stagesTraveled);
            const stopOnRouteLowerAdjustment = this.tripAdjustmentByStop(stageAdjustment, stage.stopsOnRoute);

            // determine the upper and lower Stage border regarding on segment def. or previous stage.
            let upperStageBorder;
            let lowerStageBorder;
            if (lowerBorderPreviousStage === undefined) {
                upperStageBorder = segment.startDistance;
            } else if (lowerBorderPreviousStage === null) {
                upperStageBorder = previousSegmentEnd;
            } else {
                upperStageBorder = lowerBorderPreviousStage.distanceOnRoute;
            }

            if (stopOnRouteLowerAdjustment === undefined) {
                lowerStageBorder = segment.endDistance;
                previousSegmentEnd = segment.endDistance;
            } else if (stopOnRouteLowerAdjustment === null) {
                lowerStageBorder = upperStageBorder - 1;
            } else {
                lowerStageBorder = stopOnRouteLowerAdjustment.distanceOnRoute;
                previousSegmentEnd = stopOnRouteLowerAdjustment.distanceOnRoute;
            }
            lowerBorderPreviousStage = stopOnRouteLowerAdjustment;

            const stageDefinition = {
                segment: segment,
                stopsOnRoute: this.tripAdjustmentWithBorderRestriction(segment, stopsOnRoute, upperStageBorder, lowerStageBorder)
            };

            // filter all alredy staged stops
            stopsOnRoute = stopsOnRoute.filter((f) => !stageDefinition.stopsOnRoute.map((m) => m.stopCode).includes(f.stopCode));

            return stageDefinition;
        });
    }

    tripAdjustmentByStop = (stageAdjustment, stopsOnRoute) => {
        if (stageAdjustment !== undefined) {
            return (stageAdjustment.borderStop !== null) ? stopsOnRoute.find((f) => f.stopCode === stageAdjustment.borderStop) : null;
        }
        return undefined;
    }

    tripAdjustmentWithBorderRestriction = (segment, stopsOnRoute, upperBorder, lowerBorder) => {
        const stops = stopsOnRoute.filter((stopOnRoute) => (stopOnRoute.distanceOnRoute === 0 || stopOnRoute.distanceOnRoute) > upperBorder && stopOnRoute.distanceOnRoute <= lowerBorder);
        stops.forEach((stopOnRoute) => {
            stopOnRoute.adjustedToLower = stopOnRoute.distanceOnRoute > segment.endDistance;
            stopOnRoute.adjustedToUpper = stopOnRoute.distanceOnRoute <= segment.startDistance && segment.stagesTraveled > 1;
            stopOnRoute.stagesTraveled = segment.stagesTraveled;
            stopOnRoute.baseFare = segment.baseFare;
        });
        return stops;
    }

    roundPriceByRoundingType = (price, priceRoundingType) => {
        const power = Math.pow(10, global.currentPosData.currencyDecimal);
        const value = price / power;
        let roundedPrice;

        switch (priceRoundingType) {
            case constants.priceRoundingType.none:
                roundedPrice = value * power;
            break;
            case constants.priceRoundingType.ceiling:
                roundedPrice = Math.ceil(value) * power;
            break;
            case constants.priceRoundingType.floor:
                roundedPrice = Math.floor(value) * power;
            break;
            default:
                roundedPrice = price;
        }

        return roundedPrice;
    }

    multiPriceAdjustment = (priceInCents, multiPriceProduct, riderId, transitTypeId, travelType, flexibilityType, saleType) => {
        let adjustmentExternalId = 0;
        if (multiPriceProduct && multiPriceProduct.adjustments) {
            let adjustments = (Array.isArray(multiPriceProduct.adjustments)) ? multiPriceProduct.adjustments : Array(multiPriceProduct.adjustments);
            adjustments.sort((a, b) => a.priority - b.priority);

            const adjustmentsLen = adjustments.length;
            for (let j=0; j<adjustmentsLen; j++) {
                const rider = (Array.isArray(adjustments[j].riderTypeExternalIds.riderTypeExternalId)) ? adjustments[j].riderTypeExternalIds.riderTypeExternalId : Array(adjustments[j].riderTypeExternalIds.riderTypeExternalId);
                const route = (Array.isArray(adjustments[j].routeTypeExternalIds.routeTypeExternalId)) ? adjustments[j].routeTypeExternalIds.routeTypeExternalId : Array(adjustments[j].routeTypeExternalIds.routeTypeExternalId);
                const organization = (Array.isArray(adjustments[j].organizationExternalIds.organizationExternalId)) ? adjustments[j].organizationExternalIds.organizationExternalId : Array(adjustments[j].organizationExternalIds.organizationExternalId);
                const employeeRole = (Array.isArray(adjustments[j].employeeRoleExternalIds.employeeRoleExternalId)) ? adjustments[j].employeeRoleExternalIds.employeeRoleExternalId : Array(adjustments[j].employeeRoleExternalIds.employeeRoleExternalId);
                const forDevice = (Array.isArray(adjustments[j].adjustableForDeviceTypes.adjustableForDeviceType)) ? adjustments[j].adjustableForDeviceTypes.adjustableForDeviceType : Array(adjustments[j].adjustableForDeviceTypes.adjustableForDeviceType);
                const travelTypes = (Array.isArray(adjustments[j].travelTypes.travelType)) ? adjustments[j].travelTypes.travelType : Array(adjustments[j].travelTypes.travelType);
                const travelFlexibilities = (Array.isArray(adjustments[j].travelFlexibilities.travelFlexibility)) ? adjustments[j].travelFlexibilities.travelFlexibility : Array(adjustments[j].travelFlexibilities.travelFlexibility);
                const salesTypes = (Array.isArray(adjustments[j].salesTypes.salesType)) ? adjustments[j].salesTypes.salesType : Array(adjustments[j].salesTypes.salesType);
                //salesTypes.push(constants.salesTypes.direct);

                const organizationExternalId = (global.currentPosData.organizationExternalId == 0 || global.currentPosData.organizationExternalId == null) ? 'default' : global.currentPosData.organizationExternalId;
                const employeeRoleId = global.currentEmployeeData.employeeRoleId;
                const deviceType = process.env.CLIENT_DEVICE_MNEMONIC;

                const hasRider = (rider.includes(undefined)) ? true : rider.includes(riderId);
                const hasRoute = (route.includes(undefined)) ? true : route.includes(transitTypeId);
                const hasOrganization = (organization.includes(undefined)) ? true : organization.includes(organizationExternalId);
                const hasEmployeeRole = (employeeRole.includes(undefined)) ? true : employeeRole.includes(employeeRoleId);
                const hasDeviceType = (forDevice.includes(undefined)) ? true : forDevice.includes(deviceType);
                const hasTravelTypes = (travelTypes.includes(undefined)) ? true : travelTypes.includes(constants.travelTypes[travelType]);
                const hasTravelFlexibilities = (travelFlexibilities.includes(undefined)) ? true : travelFlexibilities.includes(constants.travelFlexibilityTypes[flexibilityType]);
                const hasSalesTypes = (salesTypes.includes(undefined)) ? true : salesTypes.includes(constants.salesTypes[saleType]);

                let timeRange = false;
                if (adjustments[j].scheduleType == 'Date') {
                    timeRange = moment.utc().isBetween(moment.utc(adjustments[j].timeInRange.fromDateTime), moment.utc(adjustments[j].timeInRange.toDateTime), 'seconds');
                }
                if (adjustments[j].scheduleType == 'DayOfWeek') {
                    timeRange = adjustments[j].isWeekday.weekday.includes(moment.utc().format(this.weekDayFormat).toUpperCase()) && moment.utc().isBetween(moment.utc(adjustments[j].timeInRange.fromTime, this.timeFormat).subtract(1, 'days'), moment.utc(adjustments[j].timeInRange.toTime, this.timeFormat), 'seconds');
                }

                if (hasRider && hasRoute && timeRange && hasOrganization && hasEmployeeRole && hasDeviceType && hasTravelTypes && hasTravelFlexibilities && hasSalesTypes) {
                    switch (adjustments[j].actionType) {
                        case 'TravelRestriction':
                            continue;
                        break;
                        case 'SalesRestriction':
                            continue;
                        break;
                        case 'FixedAmountCorrection':
                            priceInCents = priceInCents + adjustments[j].valueInCents;
                        break;
                        case 'PercentageCorrection':
                            priceInCents = priceInCents + (priceInCents * (adjustments[j].percentageAmountInOneHundredths / 10000));
                        break;
                    }
                    adjustmentExternalId = adjustments[j].externalId;
                }
            }
        }

        const priceCentCalculated = this.roundPriceByRoundingType(priceInCents, (multiPriceProduct && multiPriceProduct.hasOwnProperty('priceRoundingType')) ? multiPriceProduct.priceRoundingType : '');

        return {priceCentCalculated: priceCentCalculated, adjustmentExternalId: adjustmentExternalId};
    }

    luggage = async (req, res) => {
        try {
            const transitTypeId = req.query.transitTypeId;
            const riderId = req.query.riderId;

            const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
            let products = await this.fareTableModel.Products.find({fareEnvironmentConfigurationId: activeConfig._id, productEntityClass: constants.productEntityClass.fareSellableProduct, purchasableAccess: {$in: String(riderId)}, 'sellableAccess.deviceType': constantsXml.deviceType.officePos, 'transitTypes.externalId': transitTypeId, $or:[{sellingStartDay: {$eq: null}}, {sellingStartDay: {$lte: moment.utc().date()}}, {sellingEndDay: {$eq: null}}, {sellingEndDay: {$gte: moment.utc().date()}}]});

            products = products.filter((f) => [constants.productType.luggage].includes(f.productType) && [constants.mediaType.ticket].includes(f.mediaType));

            const vatRates = await this.fareTableModel.VatRates.find({fareEnvironmentConfigurationId: activeConfig._id});

            let transitType;
            if (transitTypeId > 0) {
                transitType = await this.fareEnvironmentModel.TransitTypes.findOne({fareEnvironmentConfigurationId: activeConfig._id, externalId: transitTypeId});
            } else {
                transitType = {name: '', nameTranslations: ''};
            }

            let productsList = [];

            const productsLen = products.length;
            for (let i=0; i<productsLen; i++) {
                let priceInCents = products[i].priceInCents;

                const productAdjustment = this.productAdjustment(priceInCents, products[i], riderId, transitTypeId, 'oneway', 'standard', 'direct');

                const vatRate = (vatRates.find((f) => [products[i].productType].includes(f.associatedProductTypes))) ? vatRates.find((f) => [products[i].productType].includes(f.associatedProductTypes)) : vatRates.find((f) => f.isDefault == true);

                productsList.push({
                    id: [products[i].externalId, transitTypeId].join('_'),
                    // busStopFromCode: originStop.stopCode,
                    // busStopFromName: originStop.stopName,
                    // busStopFromNameTranslate: (originStopTranslation) ? originStopTranslation.value : originStop.stopName+'?',
                    // busStationFromCode: originStation.stationCode,
                    // busStationFromName: originStation.stationName,
                    // busStationFromNameTranslate: (originStationTranslation) ? originStationTranslation.value : originStation.stationName+'?',
                    // busStopToCode: destinationStop.stopCode,
                    // busStopToName: destinationStop.stopName,
                    // busStopToNameTranslate: (destinationStopTranslation) ? destinationStopTranslation.value : destinationStop.stopName+'?',
                    // busStationToCode: destinationStation.stationCode,
                    // busStationToName: destinationStation.stationName,
                    // busStationToNameTranslate: (destinationStationTranslation) ? destinationStationTranslation.value : destinationStation.stationName+'?',
                    externalId: products[i].externalId,
                    name: products[i].name,
                    nameTranslated: products[i].nameTranslations,
                    activateOn: (products[i].activateOn) ? products[i].activateOn.split(/(?=[A-Z])/).map((m) => m.toLowerCase()).join(' ') : '',
                    description: products[i].print4,
                    descriptionTranslated: products[i].print4Translations,
                    price: priceInCents / Math.pow(10, global.currentPosData.currencyDecimal),
                    priceCent: priceInCents,
                    priceCentCalculated: productAdjustment.priceCentCalculated,
                    adjustmentExternalId: productAdjustment.adjustmentExternalId,
                    type: (products[i].productType) ? products[i].productType : '',
                    expiry: (products[i].duration) ? (products[i].activateOn == 'FirstUse') ? moment.utc().format(this.dateTTimeFormat) : moment.utc().add(products[i].duration, 'days').startOf('day').add(businessDayStart, 'hours').format(this.dateTTimeFormat) : moment.utc().format(this.dateTTimeFormat),
                    renewal: (products[i].productRenewal) ? products[i].duration : 0,
                    numberOfRides: (products[i].numberOfRides) ? products[i].numberOfRides : 0,
                    route: transitType.name,
                    routeTranslated: transitType.nameTranslations,

                    vatRate: (vatRate) ? vatRate.percents : 0
                });
            }

            res.status(200).json(productsList);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    seat = async (req, res) => {
        try {
            const transitTypeId = req.query.transitTypeId;
            const riderId = req.query.riderId;

            const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
            let products = await this.fareTableModel.Products.find({fareEnvironmentConfigurationId: activeConfig._id, productEntityClass: constants.productEntityClass.fareSellableProduct, purchasableAccess: {$in: String(riderId)}, 'sellableAccess.deviceType': constantsXml.deviceType.officePos, 'transitTypes.externalId': transitTypeId, $or:[{sellingStartDay: {$eq: null}}, {sellingStartDay: {$lte: moment.utc().date()}}, {sellingEndDay: {$eq: null}}, {sellingEndDay: {$gte: moment.utc().date()}}]});

            products = products.filter((f) => [constants.productType.seat].includes(f.productType) && [constants.mediaType.ticket].includes(f.mediaType));

            const vatRates = await this.fareTableModel.VatRates.find({fareEnvironmentConfigurationId: activeConfig._id});

            let productsList = [];

            const productsLen = products.length;
            for (let i=0; i<productsLen; i++) {
                let priceInCents = products[i].priceInCents;

                const productAdjustment = this.productAdjustment(priceInCents, products[i], riderId, transitTypeId, 'oneway', 'standard', 'direct');

                const vatRate = (vatRates.find((f) => [products[i].productType].includes(f.associatedProductTypes))) ? vatRates.find((f) => [products[i].productType].includes(f.associatedProductTypes)) : vatRates.find((f) => f.isDefault == true);

                productsList.push({
                    id: [products[i].externalId, transitTypeId].join('_'),
                    externalId: products[i].externalId,
                    name: products[i].name,
                    nameTranslated: products[i].nameTranslations,
                    activateOn: (products[i].activateOn) ? products[i].activateOn.split(/(?=[A-Z])/).map((m) => m.toLowerCase()).join(' ') : '',
                    description: products[i].print4,
                    descriptionTranslated: products[i].print4Translations,
                    price: priceInCents / Math.pow(10, global.currentPosData.currencyDecimal),
                    priceCent: priceInCents,
                    priceCentCalculated: productAdjustment.priceCentCalculated,
                    adjustmentExternalId: productAdjustment.adjustmentExternalId,
                    type: (products[i].productType) ? products[i].productType : '',
                    expiry: (products[i].duration) ? (products[i].activateOn == 'FirstUse') ? moment.utc().format(this.dateTTimeFormat) : moment.utc().add(products[i].duration, 'days').startOf('day').add(businessDayStart, 'hours').format(this.dateTTimeFormat) : moment.utc().format(this.dateTTimeFormat),
                    renewal: (products[i].productRenewal) ? products[i].duration : 0,
                    numberOfRides: (products[i].numberOfRides) ? products[i].numberOfRides : 0,
                    route: transitType.name,
                    routeTranslated: transitType.nameTranslations,

                    vatRate: (vatRate) ? vatRate.percents : 0
                });
            }

            res.status(200).json(productsList);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    ride = async (req, res) => {
        try {
            const transitTypeId = req.query.transitTypeId;
            const riderId = req.query.riderId;
            const accountId = req.query.accountId;

            const translations1st = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'translations', global.currentPosData.firstLanguage+'.json')));
            const translations2nd = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'translations', global.currentPosData.secondLanguage+'.json')));

            const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
            const transitType = await this.fareEnvironmentModel.TransitTypes.findOne({fareEnvironmentConfigurationId: activeConfig._id, externalId: transitTypeId});

            const vatRates = await this.fareTableModel.VatRates.find({fareEnvironmentConfigurationId: activeConfig._id});
            const vatRate = (vatRates.find((f) => [constants.productType.multiPrice].includes(f.associatedProductTypes))) ? vatRates.find((f) => [constants.productType.multiPrice].includes(f.associatedProductTypes)) : vatRates.find((f) => f.isDefault == true);

            let mediaType = constants.mediaType.ticket;

            let rideList = [];
            switch (transitType.fareSystemModel) {
                case constantsXml.fareSystemModel.flat:
                    const flatFare = await this.fareTableModel.FlatFares.find({fareEnvironmentConfigurationId: activeConfig._id, riderType: riderId, transitType: transitTypeId, mediaType: mediaType});

                    const flatFareLen = flatFare.length;
                    for (let i=0; i<flatFareLen; i++) {
                        rideList.push({
                            id: [transitTypeId, flatFare[i].id, riderId, accountId].join('_'),
                            route: transitType.name,
                            routeTranslated: transitType.nameTranslations,
                            name: transitType.name,
                            nameTranslated: transitType.nameTranslations,
                            fareType: transitType.fareSystemModel,
                            price: flatFare[i].baseFare / Math.pow(10, global.currentPosData.currencyDecimal),
                            priceCent: flatFare[i].baseFare,
                            priceCentCalculated: flatFare[i].baseFare,

                            vatRate: (vatRate) ? vatRate.percents : 0
                        });
                    }
                break;
                case constantsXml.fareSystemModel.zones:
                    const zoneFare = await this.fareTableModel.ZoneFares.find({fareEnvironmentConfigurationId: activeConfig._id, riderType: riderId, transitType: transitTypeId, mediaType: mediaType});

                    const zoneFareLen = zoneFare.length;
                    for (let i=0; i<zoneFareLen; i++) {
                        rideList.push({
                            id: [transitTypeId, zoneFare[i].zonesTraveled, riderId, accountId].join('_'),
                            route: transitType.name,
                            routeTranslated: transitType.nameTranslations,
                            name: (zoneFare[i].zonesTraveled == 1) ? `${translations1st['zone']} - ${zoneFare[i].zonesTraveled}` : `${translations1st['zones']} - ${zoneFare[i].zonesTraveled}`,
                            nameTranslated: (zoneFare[i].zonesTraveled == 1) ? `${translations2nd['zone']} - ${zoneFare[i].zonesTraveled}` : `${translations2nd['zones']} - ${zoneFare[i].zonesTraveled}`,
                            fareType: transitType.fareSystemModel,
                            price: zoneFare[i].baseFare / Math.pow(10, global.currentPosData.currencyDecimal),
                            priceCent: zoneFare[i].baseFare,
                            priceCentCalculated: zoneFare[i].baseFare,

                            zoneTraveled: zoneFare[i].zonesTraveled,
                            vatRate: (vatRate) ? vatRate.percents : 0
                        });
                    }
                break;
            }

            const rideListOrdered = rideList.sort((a, b) => a.priceCent - b.priceCent);

            res.status(200).json(rideListOrdered);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    checkout = async (req, res) => {
        try {
            const cart = req.body;

            const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
            const organization = await this.modelOrganizations.findOne({organizationId: global.currentPosData.organizationExternalId}); // default
            // const organizationsFiltered = organizations.filter((f) => f.organizationTypes.some((m) => [constants.organizationTypes.simpleOrganization, constants.organizationTypes.merchant].includes(m)));
            const vatRates = await this.fareTableModel.VatRates.find({fareEnvironmentConfigurationId: activeConfig._id});
            const vatAddedToTotal = true;
            const vatRateInOneHundredths = (vatRates.find((f) => f.isDefault == true)) ? vatRates.find((f) => f.isDefault == true).percents : 0;

            let ticket = await this.modelTicket.create({
                sessionId: global.currentEmployeeData.employeeSessionId,
                transactionNo: cart.transactionNo,
                paymentType: cart.paymentType,
                issueTime: moment.utc().toISOString(),
                currencyCode: cart.currency,
                totalCent: cart.totalCent,
                total: cart.total,
                itemNumber: cart.items.filter((f) => f.penalty == null && f.topupAccount == null).map((m) => m.quantity).reduce((p, n) => p + n, 0),
                reason: (cart.reason) ? cart.reason : null,
                discount: (cart.discount) ? cart.discount : null
            });

            global.currentTicketData = ticket;

            const issuingTimestamp = moment.utc(global.currentTicketData.issueTime).utcOffset(global.currentPosData.utcOffset);

            const invoiceId = await this.posTransactionCtrl.invoice(issuingTimestamp);

            let taxData = '';
            let taxQrCode = '';
            if (['hafil'].includes(process.env.CLIENT_NAME)) {
                taxData = {
                    name: (organization && organization.businessName) ? organization.businessName : 'Darb Alwatan Transportation Co',
                    vatNumber: (organization && organization.vatNumber) ? organization.vatNumber : '311510940300003'
                };
                taxQrCode = await this.service.generateTaxQrCode({
                    organizationName: taxData.name,
                    organizationVatNumber: taxData.vatNumber,
                    timestamp: issuingTimestamp.format(this.dateTTimeFormat),
                    invoiceTotal: (global.currentTicketData.totalCent / Math.pow(10, global.currentPosData.currencyDecimal)).toFixed(global.currentPosData.currencyDecimal),
                    vatTotal: (Math.round(global.currentTicketData.totalCent * (vatRateInOneHundredths / 10000) / (1 + (vatRateInOneHundredths / 10000))) / Math.pow(10, global.currentPosData.currencyDecimal)).toFixed(global.currentPosData.currencyDecimal)
                });
            }
            if (['mok'].includes(process.env.CLIENT_NAME) && process.env.CLIENT_FISCAL_VERIFICATION == 'true') {
                const vatDetails = await this.getVatDetails(cart);
                const fiscalVerification = await this.service.fiscalVerificationInvoice({
                    issueDateTime: issuingTimestamp,
                    invoiceNumber: invoiceId,
                    invoiceAmount: global.currentTicketData.totalCent / Math.pow(10, global.currentPosData.currencyDecimal),
                    vatDetails: vatDetails
                });
                taxData = {
                    name: fiscalVerification.eor,
                    vatNumber: fiscalVerification.zoi
                };
                taxQrCode = fiscalVerification.qrCode;
            }

            const ticketQr = await this.modelTicket.findOneAndUpdate({ _id: ticket.id }, {
                taxData: taxData,
                taxQrCode: taxQrCode,
                invoiceId: invoiceId
            }, {new: true});

            global.currentTicketData = ticketQr;

            if (global.currentPosData.hasSalesOrder) {
                global.currentTicketData.salesOrderIdNumber = global.currentPosData.transactionCounter;
                const cartItem = cart.items[0];

                const transitType = await this.fareEnvironmentModel.TransitTypes.findOne({fareEnvironmentConfigurationId: activeConfig._id, externalId: cartItem.routeId});
                const transitTypeDef = await this.fareEnvironmentModel.TransitTypes.findOne({fareEnvironmentConfigurationId: activeConfig._id}).sort({externalId: 1});
                const fare = (!transitType) ? {
                    externalId: transitTypeDef.externalId,
                    name: transitTypeDef.name,
                    fareSystemModel: transitTypeDef.fareSystemModel,
                    routeSystemType: transitTypeDef.routeSystemType
                } : {
                    externalId: transitType.externalId,
                    name: transitType.name,
                    fareSystemModel: transitType.fareSystemModel,
                    routeSystemType: transitType.routeSystemType
                };

                let paymentInstrument = constantsXml.paymentInstrument.none;
                let paymentMethod = constantsXml.paymentMethod.none;
                let paymentInfoType = constants.paymentInfoType.none;

                switch (ticket.paymentType) {
                    case enums.paymentTypes.cash:
                        paymentInstrument = constantsXml.paymentInstrument.prepayCash;
                        paymentMethod = constantsXml.paymentMethod.manualyCollected;
                        paymentInfoType = constants.paymentInfoType.cash;
                    break;
                    case enums.paymentTypes.card:
                        paymentInstrument = constantsXml.paymentInstrument.prepayCreditCard;
                        paymentMethod = constantsXml.paymentMethod.automaticalyCollected;
                        paymentInfoType = constants.paymentInfoType.creditCard;
                    break;
                    case enums.paymentTypes.credit:
                        paymentInstrument = constantsXml.paymentInstrument.prepayAccountEPurse;
                        paymentMethod = constantsXml.paymentMethod.automaticalyCollected;
                        paymentInfoType = constants.paymentInfoType.account;
                    break;
                }

                let transaction = {
                    transactionTime: ticket.issueTime,
                    riderExternalId: (cartItem.riderType) ? cartItem.riderType.externalId : constants.defaults.accountId,
                    riderName: (cartItem.riderType) ? cartItem.riderType.name : constants.defaults.accountName,
                    paymentInstrument: paymentInstrument,
                    paymentMethod: paymentMethod,
                    currencyCode: ticket.currencyCode,
                    employeeId: global.currentEmployeeData.employeeId,
                    sessionId: global.currentEmployeeData.employeeSessionId,
                    cardTransactionNo: ticket.transactionNo,
                    accountNo: cartItem.accountNo
                };

                transaction.fareItemIssuedNumber = null;
                transaction.activityType = constantsXml.activityType.payment;
                transaction.issuedFormatType = '';
                transaction.numberOfPassengers = cart.items.filter((f) => f.penalty == null && f.topupAccount == null && f.smartCardIssue == null).map((m) => m.numberOfPassengers).reduce((p, n) => p + n, 0);
                transaction.fareItemCategory = constantsXml.fareItemCategory.salesOrderType;
                transaction.fareItemType = constantsXml.fareItemType.salesOrder;
                transaction.farePrice = ticket.totalCent;
                transaction.farePriceCurrency = ticket.total;
                const salesOrderProduct = await this.fareTableModel.Products.findOne({fareEnvironmentConfigurationId: activeConfig._id, productType: constants.productType.salesOrder, purchasableAccess: {$in: String(cartItem.riderType.externalId)}, 'sellableAccess.deviceType': constantsXml.deviceType.officePos, $or:[{sellingStartDay: {$eq: null}}, {sellingStartDay: {$lte: moment.utc().date()}}, {sellingEndDay: {$eq: null}}, {sellingEndDay: {$gte: moment.utc().date()}}]});
                transaction.fareItemExternalId = (salesOrderProduct) ? salesOrderProduct.externalId : 0;
                transaction.fareItemName = constantsXml.fareItemName.salesOrderPayment;
                transaction.totalPaymentInCents = transaction.farePrice;
                //transaction.prodName = ticketItem.prodName;
                transaction.productType = constants.productType.salesOrder;
                transaction.waybillName = '';
                transaction.waybillNameTranslated = '';

                transaction.vatRate = vatRateInOneHundredths;
                transaction.totalVat = Math.round(transaction.farePrice * (vatRateInOneHundredths / 10000) / (1 + (vatRateInOneHundredths / 10000)));

                let vatDetails = await this.getVatDetails(cart);

                let createdFile = await this.posTransactionCtrl.create({
                    function: 'salesOrder',
                    component: 'ticketPrinter',
                    parameters: {
                        avl: {
                            locationLongitude: global.currentPosData.locationLongitude,
                            locationLatitude: global.currentPosData.locationLatitude
                        },
                        fare: fare,
                        financial: {
                            activityType: transaction.activityType,
                            fareItemCategory: transaction.fareItemCategory,
                            fareItemType: transaction.fareItemType,
                            fareItemExternalId: transaction.fareItemExternalId,
                            fareItemName: transaction.fareItemName,
                            payingRiderTypeExternalId: transaction.riderExternalId,
                            payingRiderTypeName: transaction.riderName,
                            payingRiderAccountId: transaction.accountNo,
                            cumulativeFareOrProductPrice: transaction.farePrice,
                            totalPaymentInCents: transaction.totalPaymentInCents,
                            totalVatInCents: transaction.totalVat,
                            vatAddedToTotalPayment: vatAddedToTotal,
                            paymentInstrument: transaction.paymentInstrument,
                            paymentMethod: transaction.paymentMethod,
                            currencyCode: global.currentPosData.currencyCode,
                            paymentInstrumentTransactionId: (transaction.cardTransactionNo) ? transaction.cardTransactionNo : 0,
                            taxInvoiceInformation: {
                                invoiceIdNumber: invoiceId.split('-')[2],
                                invoiceExternalReferenceCode: invoiceId,
                                taxInvoiceQrCode: taxQrCode
                            },
                            vatDetails: vatDetails
                        },
                        order: {
                            salesOrderIdNumber: global.currentTicketData.salesOrderIdNumber,
                            salesOrderExternalReferenceCode: invoiceId
                        }
                    }
                });

                //await this.modelFinancial.create(transaction);
            }

            if (ticket.paymentType == 3 && cart.items[0].accountNo) {
                const cartItem = cart.items[0];

                const transitType = await this.fareEnvironmentModel.TransitTypes.findOne({fareEnvironmentConfigurationId: activeConfig._id, externalId: cartItem.routeId});
                const transitTypeDef = await this.fareEnvironmentModel.TransitTypes.findOne({fareEnvironmentConfigurationId: activeConfig._id}).sort({externalId: 1});
                const fare = (!transitType) ? {
                    externalId: transitTypeDef.externalId,
                    name: transitTypeDef.name,
                    fareSystemModel: transitTypeDef.fareSystemModel,
                    routeSystemType: transitTypeDef.routeSystemType
                } : {
                    externalId: transitType.externalId,
                    name: transitType.name,
                    fareSystemModel: transitType.fareSystemModel,
                    routeSystemType: transitType.routeSystemType
                };

                let paymentInstrument = constantsXml.paymentInstrument.none;
                let paymentMethod = constantsXml.paymentMethod.none;
                let paymentInfoType = constants.paymentInfoType.none;

                switch (ticket.paymentType) {
                    case enums.paymentTypes.cash:
                        paymentInstrument = constantsXml.paymentInstrument.prepayCash;
                        paymentMethod = constantsXml.paymentMethod.manualyCollected;
                        paymentInfoType = constants.paymentInfoType.cash;
                    break;
                    case enums.paymentTypes.card:
                        paymentInstrument = constantsXml.paymentInstrument.prepayCreditCard;
                        paymentMethod = constantsXml.paymentMethod.automaticalyCollected;
                        paymentInfoType = constants.paymentInfoType.creditCard;
                    break;
                    case enums.paymentTypes.credit:
                        paymentInstrument = constantsXml.paymentInstrument.prepayAccountEPurse;
                        paymentMethod = constantsXml.paymentMethod.automaticalyCollected;
                        paymentInfoType = constants.paymentInfoType.account;
                    break;
                }

                let transaction = {
                    transactionTime: ticket.issueTime,
                    riderExternalId: (cartItem.riderType) ? cartItem.riderType.externalId : constants.defaults.accountId,
                    riderName: (cartItem.riderType) ? cartItem.riderType.name : constants.defaults.accountName,
                    paymentInstrument: paymentInstrument,
                    paymentMethod: paymentMethod,
                    currencyCode: ticket.currencyCode,
                    employeeId: global.currentEmployeeData.employeeId,
                    sessionId: global.currentEmployeeData.employeeSessionId,
                    cardTransactionNo: ticket.transactionNo,
                    accountNo: cartItem.accountNo
                };

                let account = await this.accountHolderServ.getAccount(cartItem.accountNo);
                if (!account) {
                    account = {
                        riderTypeId: cartItem.riderType.externalId,
                        type: cartItem.riderType.name.toUpperCase(),
                        shortId: cartItem.accountNo
                    };
                }

                transaction.fareItemIssuedNumber = null;
                transaction.activityType = constantsXml.activityType.debit;
                transaction.issuedFormatType = '';
                transaction.numberOfPassengers = cart.items.filter((f) => f.penalty == null && f.topupAccount == null && f.smartCardIssue == null).map((m) => m.numberOfPassengers).reduce((p, n) => p + n, 0);;
                transaction.fareItemCategory = constantsXml.fareItemCategory.fareEpurse;
                transaction.fareItemType = constantsXml.fareItemType.debitEpurseAccount;
                transaction.farePrice = ticket.totalCent;
                transaction.farePriceCurrency = ticket.total;

                transaction.fareItemExternalId = 0;
                transaction.fareItemName = constantsXml.fareItemName.na;
                transaction.totalPaymentInCents = transaction.farePrice;
                //transaction.prodName = ticketItem.prodName;
                transaction.productType = constants.productType.eWallet;
                transaction.waybillName = '';
                transaction.waybillNameTranslated = '';

                transaction.riderExternalId = account.riderTypeId;
                transaction.riderName = account.type;

                transaction.vatRate = vatRateInOneHundredths;
                transaction.totalVat = Math.round(transaction.farePrice * (vatRateInOneHundredths / 10000) / (1 + (vatRateInOneHundredths / 10000)));

                let vatRates = {};
                const ticketItemsLen = (cart.items).length;
                for (let i=0; i<ticketItemsLen; i++) {
                    let item = cart.items[i];

                    if (item.cityTicket != null) {
                        vatRates = await this.addVatRates(vatRates, item.cityTicket.vatRate, item.cityTicket.priceCent, item.quantity, item.numberOfPassengers);
                    } else if (item.productBaggage != null) {
                        vatRates = await this.addVatRates(vatRates, item.productBaggage.vatRate, item.productBaggage.priceCent, item.quantity, item.numberOfPassengers);
                    } else if (item.productBaggageReturn != null) {
                        vatRates = await this.addVatRates(vatRates, item.productBaggageReturn.vatRate, item.productBaggageReturn.priceCent, item.quantity, item.numberOfPassengers);
                    } else if (item.intercityTicket != null) {
                        vatRates = await this.addVatRates(vatRates, item.intercityTicket.vatRate, item.intercityTicket.priceCent, item.quantity, item.numberOfPassengers);
                    } else if (item.penalty != null) {
                        vatRates = await this.addVatRates(vatRates, item.penalty.vatRate, item.penalty.priceCent, item.quantity, item.numberOfPassengers);
                    } else if (item.productPeriod != null) {
                        vatRates = await this.addVatRates(vatRates, item.productPeriod.vatRate, item.productPeriod.priceCent, item.quantity, item.numberOfPassengers);
                    } else if (item.topupAccount != null) {
                        vatRates = await this.addVatRates(vatRates, item.topupAccount.vatRate, item.topupAccount.priceCent, item.quantity, item.numberOfPassengers);
                    } else if (item.smartCardIssue != null) {
                        vatRates = await this.addVatRates(vatRates, item.smartCardIssue.vatRate, item.smartCardIssue.priceCent, item.quantity, item.numberOfPassengers);
                    } else if (item.topupSmartCard != null) {
                        vatRates = await this.addVatRates(vatRates, item.topupSmartCard.vatRate, item.topupSmartCard.priceCent, item.quantity, item.numberOfPassengers);
                    }
                }

                let vatDetails = [];
                for (let i = 0; i < Object.keys(vatRates).length; i++) {
                    let vatRate = Object.keys(vatRates)[i];
                    let priceCent = vatRates[vatRate];
                    let totalVat = Math.round(priceCent * (vatRate / 10000) / (1 + (vatRate / 10000)));

                    vatDetails.push({
                        taxableAmountInCents: priceCent - totalVat,
                        vatRate: vatRate,
                        vatInCents: totalVat
                    });
                }

                let createdFile = await this.posTransactionCtrl.create({
                    function: 'accountPayment',
                    component: 'ticketPrinter',
                    parameters: {
                        avl: {
                            locationLongitude: global.currentPosData.locationLongitude,
                            locationLatitude: global.currentPosData.locationLatitude
                        },
                        fare: fare,
                        financial: {
                            activityType: transaction.activityType,
                            fareItemCategory: transaction.fareItemCategory,
                            fareItemType: transaction.fareItemType,
                            fareItemExternalId: transaction.fareItemExternalId,
                            //fareItemProductType: transaction.productType,
                            //fareItemIssuedNumber: transaction.fareItemIssuedNumber,
                            //fareItemIssuedExpirationTimeStamp: moment.utc().format(this.dateTTimeFormat),
                            //fareItemPurchasedStagesOrZones: 0,
                            fareItemName: transaction.fareItemName,
                            payingRiderTypeExternalId: transaction.riderExternalId,
                            payingRiderTypeName: transaction.riderName,
                            payingRiderAccountId: transaction.accountNo,
                            cumulativeFareOrProductPrice: transaction.farePrice,
                            totalPaymentInCents: transaction.totalPaymentInCents,
                            totalVatInCents: transaction.totalVat,
                            vatAddedToTotalPayment: vatAddedToTotal,
                            //penaltyFee: 0,
                            paymentInstrument: transaction.paymentInstrument,
                            paymentMethod: transaction.paymentMethod,
                            currencyCode: global.currentPosData.currencyCode,
                            paymentInstrumentTransactionId: 0,
                            taxInvoiceInformation: {
                                invoiceIdNumber: invoiceId.split('-')[2],
                                invoiceExternalReferenceCode: invoiceId,
                                taxInvoiceQrCode: taxQrCode
                            },
                            vatDetails: vatDetails
                        },
                        order: (global.currentPosData.hasSalesOrder) ? {
                            salesOrderIdNumber: global.currentTicketData.salesOrderIdNumber,
                            salesOrderExternalReferenceCode: invoiceId
                        } : ''
                    }
                });

                //await this.modelFinancial.create(transaction);
            }

            res.status(200).json({
                invoiceId: invoiceId,
                ticketId: global.currentTicketData._id,
                issueTime: issuingTimestamp.format(this.dateTimeFormat),
                taxData: taxData,
                taxQrCode: taxQrCode
            });
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    checkoutItem = async (req, res) => {
        try {
            const cartItem = req.body;

            const translations1st = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'translations', global.currentPosData.firstLanguage+'.json')));
            const translations2nd = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'translations', global.currentPosData.secondLanguage+'.json')));

            const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
            const transitType = await this.fareEnvironmentModel.TransitTypes.findOne({fareEnvironmentConfigurationId: activeConfig._id, externalId: cartItem.routeId});
            const transitTypeDef = await this.fareEnvironmentModel.TransitTypes.findOne({fareEnvironmentConfigurationId: activeConfig._id}).sort({externalId: 1});
            const fare = (!transitType) ? {
                externalId: transitTypeDef.externalId,
                name: transitTypeDef.name,
                fareSystemModel: transitTypeDef.fareSystemModel,
                routeSystemType: transitTypeDef.routeSystemType
            } : {
                externalId: transitType.externalId,
                name: transitType.name,
                fareSystemModel: transitType.fareSystemModel,
                routeSystemType: transitType.routeSystemType
            };
            let routeIntercityId = '';
            if (cartItem.productBaggage != null) {
                routeIntercityId = cartItem.productBaggage.routeIntercityId;
            } else if (cartItem.intercityTicket != null) {
                routeIntercityId = cartItem.intercityTicket.routeIntercityId;
            }
            const route = (routeIntercityId) ? await this.fareEnvironmentModel.Routes.findOne({fareEnvironmentConfigurationId: activeConfig._id, machineId: routeIntercityId}) : '';
            const routeDef = await this.fareEnvironmentModel.Routes.findOne({fareEnvironmentConfigurationId: activeConfig._id}).sort({externalId: 1});
            const avl = (!route) ? {
                externalId: routeDef.externalId,
                name: routeDef.name,
                destination: routeDef.destination,
                variant: routeDef.variant,
                version: routeDef.version
            } : {
                externalId: route.externalId,
                name: route.name,
                destination: route.destination,
                variant: route.variant,
                version: route.version
            };
            avl.locationLongitude = global.currentPosData.locationLongitude;
            avl.locationLatitude = global.currentPosData.locationLatitude;
            avl.tripId = cartItem.tripId;

            const ticket = global.currentTicketData;
            let fareItemIssuedNumber = this.helpersCtrl.getFareItemIssuedNumber(global.currentPosData.terminalId, global.currentPosData.transactionCounter);
            let businessDayStart = (activeConfig.businessDayStart) ? moment(activeConfig.businessDayStart, this.timeFormat).subtract(global.currentPosData.utcOffset).hour() : moment(global.currentPosData.endOfWorkHour, this.timeFormat).subtract(global.currentPosData.utcOffset).hour();
            let fareItemIssuedExpirationTimeStamp = moment.utc(ticket.issueTime).add(1, 'days');
            let qrCode = '';
            let issueTime = moment.utc(ticket.issueTime);
            let shouldPrint = true; // (transaction.accountNo) ? false : true;
            let shouldNotPrint = false;
            let qrCodeReturn = '';
            let checkoutModel = {};
            let transactions = [];
            let account = {};

            if (cartItem.accountNo) {
                account = await this.accountHolderServ.getAccount(cartItem.accountId);

                if (account.blocked) {
                    return res.status(400).json({
                        accountBlocked: true,
                        accountId: account.shortId
                    });
                }
            }

            let ticketItem = {
                quantity: cartItem.quantity,
                riderExternalId: (cartItem.riderType) ? cartItem.riderType.externalId : constants.defaults.accountId,
                riderName: (cartItem.riderType) ? cartItem.riderType.name : constants.defaults.accountName,
                accountId: cartItem.accountId,
                accountNo: cartItem.accountNo,
                routeId: cartItem.routeId,
                tripId: cartItem.tripId
            };

            let paymentInstrument = constantsXml.paymentInstrument.none;
            let paymentMethod = constantsXml.paymentMethod.none;
            let paymentInfoType = constants.paymentInfoType.none;

            switch (ticket.paymentType) {
                case enums.paymentTypes.cash:
                    paymentInstrument = constantsXml.paymentInstrument.prepayCash;
                    paymentMethod = constantsXml.paymentMethod.manualyCollected;
                    paymentInfoType = constants.paymentInfoType.cash;
                break;
                case enums.paymentTypes.card:
                    paymentInstrument = constantsXml.paymentInstrument.prepayCreditCard;
                    paymentMethod = constantsXml.paymentMethod.automaticalyCollected;
                    paymentInfoType = constants.paymentInfoType.creditCard;
                break;
                case enums.paymentTypes.credit:
                    paymentInstrument = constantsXml.paymentInstrument.prepayAccountEPurse;
                    paymentMethod = constantsXml.paymentMethod.automaticalyCollected;
                    paymentInfoType = constants.paymentInfoType.account;
                break;
            }

            let transaction = {
                ticketId: ticket.id,
                ticketItemId: '',
                transactionTime: ticket.issueTime,
                riderExternalId: (cartItem.riderType) ? cartItem.riderType.externalId : constants.defaults.accountId,
                riderName: (cartItem.riderType) ? cartItem.riderType.name : constants.defaults.accountName,
                paymentInstrument: paymentInstrument,
                paymentMethod: paymentMethod,
                currencyCode: ticket.currencyCode,
                employeeId: global.currentEmployeeData.employeeId,
                sessionId: global.currentEmployeeData.employeeSessionId,
                cardTransactionNo: ticket.transactionNo,
                accountNo: cartItem.accountNo
            };

            let mediaType, ticketType, fareItemCategory, fareItemType, issuedFormatType, passengerBaseFare, passengerCorrectionFareDetails, customParameters;
            mediaType = constants.mediaType.ticket;

            if (cartItem.accountId) {

                if (cartItem.cityTicket != null) {
                    fareItemCategory = (cartItem.numberOfPassengers > 1) ? constantsXml.fareItemCategory.fareJourneyGroup : constantsXml.fareItemCategory.fareJourney;
                    fareItemType = (cartItem.numberOfPassengers > 1) ? constantsXml.fareItemType.journeyGroupElectronicTicket : constantsXml.fareItemType.journeyElectronicTicket;
                    issuedFormatType = constantsXml.issuedFormatType.permanentQrCodeAccountStored;
                } else if (cartItem.productBaggage != null) {
                    fareItemCategory =  constantsXml.fareItemCategory.fareProduct;
                    fareItemType = constantsXml.fareItemType.productAccount;
                    issuedFormatType = constantsXml.issuedFormatType.permanentQrCodeAccountStored;
                } else if (cartItem.productBaggageReturn != null) {
                    fareItemCategory =  constantsXml.fareItemCategory.fareProduct;
                    fareItemType = constantsXml.fareItemType.productAccount;
                    issuedFormatType = constantsXml.issuedFormatType.permanentQrCodeAccountStored;
                } else if (cartItem.intercityTicket != null) {
                    if (cartItem.intercityTicket.isReturn) {
                        fareItemCategory = (cartItem.numberOfPassengers > 1) ? constantsXml.fareItemCategory.fareReturnSingleRideGroup : constantsXml.fareItemCategory.fareReturnSingleRide;
                        fareItemType = (cartItem.numberOfPassengers > 1) ? constantsXml.fareItemType.returnSingleRideGroupElectronicTicket : constantsXml.fareItemType.returnSingleRideElectronicTicket;
                    } else {
                        fareItemCategory = (cartItem.numberOfPassengers > 1) ? constantsXml.fareItemCategory.fareSingleRideGroup : constantsXml.fareItemCategory.fareSingleRide;
                        fareItemType = (cartItem.numberOfPassengers > 1) ? constantsXml.fareItemType.singleRideGroupElectronicTicket : constantsXml.fareItemType.singleRideElectronicTicket;
                    }
                    issuedFormatType = constantsXml.issuedFormatType.permanentQrCodeAccountStored;
                } else if (cartItem.penalty != null) {
                    fareItemCategory =  constantsXml.fareItemCategory.farePenaltyItem;
                    fareItemType = constantsXml.fareItemType.penaltyTicket;
                    issuedFormatType = '';
                } else if (cartItem.productPeriod != null) {
                    fareItemCategory =  constantsXml.fareItemCategory.fareProduct;
                    fareItemType = constantsXml.fareItemType.productAccount;
                    issuedFormatType = constantsXml.issuedFormatType.permanentQrCodeAccountStored;
                } else if (cartItem.topupAccount != null) {
                    fareItemCategory = constantsXml.fareItemCategory.fareEpurse;
                    fareItemType = constantsXml.fareItemType.creditEpurseAccount;
                    issuedFormatType = '';
                } else if (cartItem.smartCardIssue != null)  {
                    fareItemCategory = constantsXml.fareItemCategory.initSmartCard;
                    fareItemType = constantsXml.fareItemType.issueSmartCard;
                    issuedFormatType = '';
                } else if (cartItem.topupSmartCard != null) {
                    fareItemCategory = constantsXml.fareItemCategory.fareProduct;
                    fareItemType = constantsXml.fareItemType.productSmartCard;
                    issuedFormatType = '';
                }

            } else {

                if (cartItem.cityTicket != null) {
                    fareItemCategory = (cartItem.numberOfPassengers > 1) ? constantsXml.fareItemCategory.fareJourneyGroup : constantsXml.fareItemCategory.fareJourney;
                    fareItemType = (cartItem.numberOfPassengers > 1) ? constantsXml.fareItemType.journeyGroupTicket : constantsXml.fareItemType.journeyTicket;
                    issuedFormatType = constantsXml.issuedFormatType.printedPaperTicket;
                } else if (cartItem.productBaggage != null) {
                    fareItemCategory =  constantsXml.fareItemCategory.fareProduct;
                    fareItemType = constantsXml.fareItemType.productTicket;
                    issuedFormatType = constantsXml.issuedFormatType.printedPaperTicket;
                } else if (cartItem.productBaggageReturn != null) {
                    fareItemCategory =  constantsXml.fareItemCategory.fareProduct;
                    fareItemType = constantsXml.fareItemType.productTicket;
                    issuedFormatType = constantsXml.issuedFormatType.printedPaperTicket;
                }  else if (cartItem.intercityTicket != null) {
                    if (cartItem.intercityTicket.isReturn) {
                        fareItemCategory = (cartItem.numberOfPassengers > 1) ? constantsXml.fareItemCategory.fareReturnSingleRideGroup : constantsXml.fareItemCategory.fareReturnSingleRide;
                        fareItemType = (cartItem.numberOfPassengers > 1) ? constantsXml.fareItemType.returnSingleRideGroupTicket : constantsXml.fareItemType.returnSingleRideTicket;
                    } else {
                        fareItemCategory = (cartItem.numberOfPassengers > 1) ? constantsXml.fareItemCategory.fareSingleRideGroup : constantsXml.fareItemCategory.fareSingleRide;
                        fareItemType = (cartItem.numberOfPassengers > 1) ? constantsXml.fareItemType.singleRideGroupTicket : constantsXml.fareItemType.singleRideTicket;
                    }
                    issuedFormatType = constantsXml.issuedFormatType.printedPaperTicket;
                } else if (cartItem.penalty != null) {
                    fareItemCategory =  constantsXml.fareItemCategory.farePenaltyItem;
                    fareItemType = constantsXml.fareItemType.penaltyTicket;
                    issuedFormatType = '';
                } else if (cartItem.productPeriod != null) {
                    fareItemCategory =  constantsXml.fareItemCategory.fareProduct;
                    fareItemType = constantsXml.fareItemType.productTicket;
                    issuedFormatType = constantsXml.issuedFormatType.printedPaperTicket;
                } else if (cartItem.topupAccount != null) {
                    fareItemCategory = constantsXml.fareItemCategory.fareEpurse;
                    fareItemType = constantsXml.fareItemType.creditEpurseAccount;
                    issuedFormatType = '';
                } else if (cartItem.smartCardIssue != null)  {
                    fareItemCategory = constantsXml.fareItemCategory.initSmartCard;
                    fareItemType = constantsXml.fareItemType.issueSmartCard;
                    issuedFormatType = '';
                } else if (cartItem.topupSmartCard != null) {
                    fareItemCategory = constantsXml.fareItemCategory.fareProduct;
                    fareItemType = constantsXml.fareItemType.productSmartCard;
                    issuedFormatType = '';
                }
            }

            ticketType = (mediaType == constants.mediaType.ticket) ? 'paperTicketValidityInDays' : 'digitalTicketValidityInDays';

            if (cartItem.cityTicket != null) {

                ticketItem.price = cartItem.cityTicket.price * cartItem.numberOfPassengers;
                ticketItem.priceCent = cartItem.cityTicket.priceCent * cartItem.numberOfPassengers;
                ticketItem.prodName = cartItem.cityTicket.name;
                ticketItem.prodNameTranslate = cartItem.cityTicket.nameTranslated;
                // ticketItem.ticketExpiryTime = issueTime.add(2, 'days').startOf('day').add(2, 'hours');
                ticketItem.zoneTraveled = cartItem.cityTicket.zoneTraveled;
                // transaction
                transaction.fareItemIssuedNumber = fareItemIssuedNumber;
                transaction.activityType = constantsXml.activityType.issue;
                transaction.issuedFormatType = issuedFormatType;
                transaction.numberOfPassengers = cartItem.numberOfPassengers;
                transaction.fareItemCategory =  fareItemCategory;
                transaction.fareItemType = fareItemType;
                transaction.farePrice = cartItem.cityTicket.priceCent * cartItem.numberOfPassengers;
                transaction.farePriceCurrency = ticketItem.price;
                const multiPriceProduct = await this.fareTableModel.MultiPriceProducts.findOne({fareEnvironmentConfigurationId: activeConfig._id, mediaType: mediaType, fareType: cartItem.cityTicket.fareType});
                transaction.fareItemExternalId = (multiPriceProduct) ? multiPriceProduct.externalId : 0;
                transaction.fareItemName = (multiPriceProduct) ? multiPriceProduct.name : constantsXml.fareItemName.na;
                fareItemIssuedExpirationTimeStamp = (multiPriceProduct) ? issueTime.add(multiPriceProduct[ticketType], 'days') : fareItemIssuedExpirationTimeStamp;
                transaction.fareItemIssuedExpirationTimeStamp = (multiPriceProduct) ? ((multiPriceProduct.validityCalculationStartCondition == constants.validityStart.timeOfPurchase) ? fareItemIssuedExpirationTimeStamp : fareItemIssuedExpirationTimeStamp.startOf('day').add(businessDayStart, 'hours')) : fareItemIssuedExpirationTimeStamp;
                transaction.totalPaymentInCents = cartItem.cityTicket.priceCentCalculated;
                transaction.prodName = ticketItem.prodName;
                transaction.productType = constants.productType.multiPrice;
                transaction.waybillName = `${cartItem.cityTicket.route} - ${cartItem.riderType.name}`;
                transaction.waybillNameTranslated = `${cartItem.cityTicket.routeTranslated} - ${cartItem.riderType.nameTranslated}`;

            } else if (cartItem.productBaggage != null) {

                ticketItem.price = cartItem.productBaggage.price;
                ticketItem.priceCent = cartItem.productBaggage.priceCent;
                ticketItem.prodName = cartItem.productBaggage.name;
                ticketItem.prodNameTranslate = cartItem.productBaggage.nameTranslated;
                ticketItem.origStopCode = (cartItem.productBaggage.busStopFromCode) ? cartItem.productBaggage.busStopFromCode : '';
                ticketItem.destStopCode = (cartItem.productBaggage.busStopToCode) ? cartItem.productBaggage.busStopToCode : '';
                ticketItem.origStationCode = (cartItem.productBaggage.busStationFromCode) ? cartItem.productBaggage.busStationFromCode : '';
                ticketItem.destStationCode = (cartItem.productBaggage.busStationToCode) ? cartItem.productBaggage.busStationToCode : '';
                // ticketItem.ticketExpiryTime = (cartItem.accountId) ? issueTime.add(365, 'days').startOf('day').add(2, 'hours') : issueTime.add(2, 'days').startOf('day').add(2, 'hours');
                ticketItem.origStop = (cartItem.productBaggage.busStopFromName) ? cartItem.productBaggage.busStopFromName : '';
                ticketItem.origStopTranslate = (cartItem.productBaggage.busStationFromNameTranslate) ? cartItem.productBaggage.busStationFromNameTranslate : '';
                ticketItem.destStop = (cartItem.productBaggage.busStopToName) ? cartItem.productBaggage.busStopToName : '';
                ticketItem.destStopTranslate = (cartItem.productBaggage.busStopToNameTranslate) ? cartItem.productBaggage.busStopToNameTranslate : '';
                ticketItem.routeIntercityId = cartItem.productBaggage.routeIntercityId;
                ticketItem.routeId = cartItem.routeId;
                ticketItem.route = cartItem.productBaggage.route;
                ticketItem.routeTranslated = cartItem.productBaggage.routeTranslated;
                ticketItem.fareItemCategory = fareItemCategory;
                ticketItem.baggage = cartItem.baggageQuantity;
                ticketItem.baggageReturn = cartItem.baggageQuantity2;

                if (global.currentPosData.hasReservation) {
                    ticketItem.reservationCartId = cartItem.productBaggage.reservationCartId;
                    ticketItem.reservationPassengerId = cartItem.productBaggage.reservationPassengerId;
                    ticketItem.reservationPassengerName = cartItem.productBaggage.passengerName;
                    ticketItem.reservationPassengerPhoneNumber = cartItem.productBaggage.passengerPhoneNumber;
                    ticketItem.reservationPassengerSeat = cartItem.productBaggage.passengerSeat;
                    ticketItem.reservationBusStationFromTime = cartItem.productBaggage.busStationFromTime;
                    ticketItem.reservationBusStationToTime = cartItem.productBaggage.busStationToTime;

                    ticketItem.reservationDiscount = cartItem.productBaggage.reservationDiscount;
                    passengerBaseFare = ticketItem.reservationDiscount.basePriceInCents;

                    ticketItem.reservationJourneyId = cartItem.productBaggage.reservationJourneyId;
                    ticketItem.reservationPassenger = cartItem.productBaggage.reservationPassenger;
                    ticketItem.reservationPassenger.fareItemIssuedNumber = fareItemIssuedNumber;

                    ticketItem.reservationBaggage = cartItem.productBaggage.reservationBaggage;
                }
                ticketItem.qrCode = '';

                // transaction
                transaction.fareItemIssuedNumber = fareItemIssuedNumber;
                transaction.activityType = constantsXml.activityType.issue;
                transaction.issuedFormatType = issuedFormatType;
                transaction.numberOfPassengers = cartItem.numberOfPassengers;
                transaction.fareItemCategory = fareItemCategory;
                transaction.fareItemType = fareItemType;
                transaction.farePrice = cartItem.productBaggage.priceCent;
                transaction.farePriceCurrency = ticketItem.price;
                const products = await this.fareTableModel.Products.find({fareEnvironmentConfigurationId: activeConfig._id, productEntityClass: constants.productEntityClass.fareSellableProduct, purchasableAccess: {$in: String(cartItem.riderType.externalId)}, 'sellableAccess.deviceType': constantsXml.deviceType.officePos, $or:[{sellingStartDay: {$eq: null}}, {sellingStartDay: {$lte: moment.utc().date()}}, {sellingEndDay: {$eq: null}}, {sellingEndDay: {$gte: moment.utc().date()}}]});
                const productLuggage = products.find((f) => f.externalId == cartItem.productBaggage.externalId);
                transaction.fareItemExternalId = (productLuggage) ? productLuggage.externalId : 0;
                transaction.fareItemName = (productLuggage) ? productLuggage.name : constantsXml.fareItemName.na;
                fareItemIssuedExpirationTimeStamp = (productLuggage) ? issueTime.add(productLuggage[ticketType], 'days') : fareItemIssuedExpirationTimeStamp;
                transaction.fareItemIssuedExpirationTimeStamp = (productLuggage) ? ((productLuggage.validityCalculationStartCondition == constants.validityStart.timeOfPurchase) ? fareItemIssuedExpirationTimeStamp : fareItemIssuedExpirationTimeStamp.startOf('day').add(businessDayStart, 'hours')) : fareItemIssuedExpirationTimeStamp;
                transaction.totalPaymentInCents = (productLuggage) ? productLuggage.priceInCents : transaction.farePrice;
                transaction.prodName = ticketItem.prodName;
                transaction.productType = (productLuggage) ? productLuggage.productType : constants.productType.luggage;
                transaction.waybillName = `${translations1st[constants.waybillName.baggage]}`;
                transaction.waybillNameTranslated = `${translations2nd[constants.waybillName.baggage]}`;
                transaction.anonimusLuggage = cartItem.productBaggage.isIntercityTicket;

                if (productLuggage && productLuggage.adjustments && cartItem.productBaggage.adjustmentExternalId != 0) {
                    const productLuggageAdjustment = productPeriod.adjustments.find((f) => f.externalId == cartItem.productBaggage.adjustmentExternalId);
                    transaction.fareAdjustment = productLuggageAdjustment;
                    passengerCorrectionFareDetails = this.passengerCorrectionFareDetails(cartItem, transaction.fareAdjustment, (passengerBaseFare) ? passengerBaseFare : transaction.farePrice, '');
                }

            } else if (cartItem.productBaggageReturn != null) {

                ticketItem.price = cartItem.productBaggageReturn.price;
                ticketItem.priceCent = cartItem.productBaggageReturn.priceCent;
                ticketItem.prodName = cartItem.productBaggageReturn.name;
                ticketItem.prodNameTranslate = cartItem.productBaggageReturn.nameTranslated;
                ticketItem.origStopCode = (cartItem.productBaggageReturn.busStopFromCode) ? cartItem.productBaggageReturn.busStopFromCode : '';
                ticketItem.destStopCode = (cartItem.productBaggageReturn.busStopToCode) ? cartItem.productBaggageReturn.busStopToCode : '';
                ticketItem.origStationCode = (cartItem.productBaggageReturn.busStationFromCode) ? cartItem.productBaggageReturn.busStationFromCode : '';
                ticketItem.destStationCode = (cartItem.productBaggageReturn.busStationToCode) ? cartItem.productBaggageReturn.busStationToCode : '';
                // ticketItem.ticketExpiryTime =  (cartItem.accountId) ? issueTime.add(365, 'days').startOf('day').add(2, 'hours') : issueTime.add(32, 'days').startOf('day').add(2, 'hours');
                // transaction
                transaction.fareItemIssuedNumber = fareItemIssuedNumber;
                transaction.activityType = constantsXml.activityType.issue;
                transaction.issuedFormatType = issuedFormatType;
                transaction.numberOfPassengers = cartItem.numberOfPassengers;
                transaction.fareItemCategory = fareItemCategory;
                transaction.fareItemType = fareItemType;
                transaction.farePrice = cartItem.productBaggageReturn.priceCent;
                transaction.farePriceCurrency = ticketItem.price;
                const products = await this.fareTableModel.Products.find({fareEnvironmentConfigurationId: activeConfig._id, productEntityClass: constants.productEntityClass.fareSellableProduct, purchasableAccess: {$in: String(cartItem.riderType.externalId)}, 'sellableAccess.deviceType': constantsXml.deviceType.officePos, $or:[{sellingStartDay: {$eq: null}}, {sellingStartDay: {$lte: moment.utc().date()}}, {sellingEndDay: {$eq: null}}, {sellingEndDay: {$gte: moment.utc().date()}}]});
                const productLuggage = products.find((f) => f.externalId == cartItem.productBaggageReturn.externalId);
                transaction.fareItemExternalId = (productLuggage) ? productLuggage.externalId : 0;
                transaction.fareItemName = (productLuggage) ? productLuggage.name : constantsXml.fareItemName.na;
                fareItemIssuedExpirationTimeStamp = (productLuggage) ? issueTime.add(productLuggage[ticketType], 'days') : fareItemIssuedExpirationTimeStamp;
                transaction.fareItemIssuedExpirationTimeStamp = (productLuggage) ? ((productLuggage.validityCalculationStartCondition == constants.validityStart.timeOfPurchase) ? fareItemIssuedExpirationTimeStamp : fareItemIssuedExpirationTimeStamp.startOf('day').add(businessDayStart, 'hours')) : fareItemIssuedExpirationTimeStamp;
                transaction.totalPaymentInCents = (productLuggage) ? productLuggage.priceInCents : transaction.farePrice;
                transaction.prodName = ticketItem.prodName;
                transaction.productType = (productLuggage) ? productLuggage.productType : constants.productType.luggage;
                transaction.waybillName = `${translations1st[constants.waybillName.baggageReturn]}`;
                transaction.waybillNameTranslated = `${translations2nd[constants.waybillName.baggageReturn]}`;
                transaction.anonimusLuggage = cartItem.productBaggageReturn.isIntercityTicket;

            } else if (cartItem.intercityTicket != null) {

                ticketItem.price = (cartItem.intercityTicket.isReturn) ? cartItem.intercityTicket.priceReturn * cartItem.numberOfPassengers : cartItem.intercityTicket.price * cartItem.numberOfPassengers;
                ticketItem.priceCent = (cartItem.intercityTicket.isReturn) ? cartItem.intercityTicket.priceCentReturn * cartItem.numberOfPassengers : cartItem.intercityTicket.priceCent * cartItem.numberOfPassengers;
                ticketItem.prodName = cartItem.intercityTicket.name;
                ticketItem.prodNameTranslate = cartItem.intercityTicket.nameTranslated;
                ticketItem.origStopCode = (cartItem.intercityTicket.busStopFromCode) ? cartItem.intercityTicket.busStopFromCode : '';
                ticketItem.destStopCode = (cartItem.intercityTicket.busStopToCode) ? cartItem.intercityTicket.busStopToCode : '';
                ticketItem.origStationCode = (cartItem.intercityTicket.busStationFromCode) ? cartItem.intercityTicket.busStationFromCode : '';
                ticketItem.destStationCode = (cartItem.intercityTicket.busStationToCode) ? cartItem.intercityTicket.busStationToCode : '';
                // ticketItem.ticketExpiryTime =  (cartItem.intercityTicket.isReturn) ? ((cartItem.accountId) ? issueTime.add(365, 'days').startOf('day').add(2, 'hours') : issueTime.add(32, 'days').startOf('day').add(2, 'hours')) : ((cartItem.accountId) ? issueTime.add(365, 'days').startOf('day').add(2, 'hours') : issueTime.add(2, 'days').startOf('day').add(2, 'hours'));
                ticketItem.origStop = (cartItem.intercityTicket.busStopFromName) ? cartItem.intercityTicket.busStopFromName : '';
                ticketItem.origStopTranslate = (cartItem.intercityTicket.busStationFromNameTranslate) ? cartItem.intercityTicket.busStationFromNameTranslate : '';
                ticketItem.destStop = (cartItem.intercityTicket.busStopToName) ? cartItem.intercityTicket.busStopToName : '';
                ticketItem.destStopTranslate = (cartItem.intercityTicket.busStopToNameTranslate) ? cartItem.intercityTicket.busStopToNameTranslate : '';
                ticketItem.routeIntercityId = cartItem.intercityTicket.routeIntercityId;
                ticketItem.routeId = cartItem.routeId;
                ticketItem.route = cartItem.intercityTicket.route;
                ticketItem.routeTranslated = cartItem.intercityTicket.routeTranslated;
                ticketItem.fareItemCategory = fareItemCategory;
                ticketItem.returnTicket = cartItem.intercityTicket.isReturn;
                //ticketItem.baggage = cartItem.baggageQuantity;
                //ticketItem.baggageReturn = cartItem.baggageQuantity2;
                //ticketItem.priceBaggage = cartItem.intercityTicket.priceBaggage;

                if (global.currentPosData.hasReservation) {
                    ticketItem.reservationCartId = cartItem.intercityTicket.reservationCartId;
                    ticketItem.reservationPassengerId = cartItem.intercityTicket.reservationPassengerId;
                    ticketItem.reservationPassengerName = cartItem.intercityTicket.passengerName;
                    ticketItem.reservationPassengerPhoneNumber = cartItem.intercityTicket.passengerPhoneNumber;
                    ticketItem.reservationPassengerSeat = cartItem.intercityTicket.passengerSeat;
                    ticketItem.reservationBusStationFromTime = cartItem.intercityTicket.busStationFromTime;
                    ticketItem.reservationBusStationToTime = cartItem.intercityTicket.busStationToTime;

                    ticketItem.reservationDiscount = cartItem.intercityTicket.reservationDiscount;
                    passengerBaseFare = ticketItem.reservationDiscount.basePriceInCents;

                    ticketItem.reservationJourneyId = cartItem.intercityTicket.reservationJourneyId;
                    ticketItem.reservationPassenger = cartItem.intercityTicket.reservationPassenger;
                    ticketItem.reservationPassenger.fareItemIssuedNumber = fareItemIssuedNumber;

                    ticketItem.reservationBaggage = cartItem.intercityTicket.reservationBaggage;
                }
                ticketItem.qrCode = '';

                // transaction
                transaction.fareItemIssuedNumber = fareItemIssuedNumber;
                transaction.activityType = constantsXml.activityType.issue;
                transaction.issuedFormatType = issuedFormatType;
                transaction.numberOfPassengers = cartItem.numberOfPassengers;
                transaction.fareItemCategory =  fareItemCategory;
                transaction.fareItemType = fareItemType;
                transaction.farePrice = ticketItem.priceCent;
                transaction.farePriceCurrency = ticketItem.price;
                const multiPriceProduct = await this.fareTableModel.MultiPriceProducts.findOne({fareEnvironmentConfigurationId: activeConfig._id, mediaType: mediaType, fareType: cartItem.intercityTicket.fareType});
                transaction.fareItemExternalId = (multiPriceProduct) ? multiPriceProduct.externalId : 0;
                transaction.fareItemName = (multiPriceProduct) ? multiPriceProduct.name : constantsXml.fareItemName.na;
                fareItemIssuedExpirationTimeStamp = (multiPriceProduct) ? issueTime.add(multiPriceProduct[ticketType], 'days') : fareItemIssuedExpirationTimeStamp;
                transaction.fareItemIssuedExpirationTimeStamp = (multiPriceProduct) ? ((multiPriceProduct.validityCalculationStartCondition == constants.validityStart.timeOfPurchase) ? fareItemIssuedExpirationTimeStamp : fareItemIssuedExpirationTimeStamp.startOf('day').add(businessDayStart, 'hours')) : fareItemIssuedExpirationTimeStamp;
                transaction.totalPaymentInCents = cartItem.intercityTicket.priceCentCalculated;
                transaction.prodName = ticketItem.prodName;
                transaction.productType = constants.productType.multiPrice;
                transaction.waybillName = `${cartItem.intercityTicket.route} - ${cartItem.riderType.name}`;
                transaction.waybillNameTranslated = `${cartItem.intercityTicket.routeTranslated} - ${cartItem.riderType.nameTranslated}`;

                if (multiPriceProduct && multiPriceProduct.adjustments && cartItem.intercityTicket.adjustmentExternalId != 0) {
                    const multiPriceProductAdjustment = multiPriceProduct.adjustments.find((f) => f.externalId == cartItem.intercityTicket.adjustmentExternalId);
                    transaction.fareAdjustment = multiPriceProductAdjustment;
                    passengerCorrectionFareDetails = this.passengerCorrectionFareDetails(cartItem, transaction.fareAdjustment, (passengerBaseFare) ? passengerBaseFare : transaction.farePrice, (multiPriceProduct && multiPriceProduct.hasOwnProperty('priceRoundingType')) ? multiPriceProduct.priceRoundingType : '');
                }

            } else if (cartItem.penalty != null) {

                ticketItem.price = cartItem.penalty.price;
                ticketItem.priceCent = cartItem.penalty.priceCent;
                ticketItem.prodName = cartItem.penalty.name;
                ticketItem.prodNameTranslate = cartItem.penalty.nameTranslated;
                // ticketItem.ticketExpiryTime =  issueTime;
                ticketItem.penaltyRefNumber = cartItem.penalty.refNumber;
                ticketItem.riderExternalId = constants.defaults.accountId;
                // transaction
                transaction.fareItemIssuedNumber = fareItemIssuedNumber;
                transaction.activityType = constantsXml.activityType.penaltyPayment;
                transaction.issuedFormatType = issuedFormatType;
                transaction.fareItemCategory = fareItemCategory;
                transaction.fareItemType = fareItemType;
                transaction.farePrice = cartItem.penalty.priceCent;
                transaction.farePriceCurrency = ticketItem.price;
                const products = await this.fareTableModel.Products.find({fareEnvironmentConfigurationId: activeConfig._id, productEntityClass: constants.productEntityClass.fareSellableProduct, purchasableAccess: {$in: String(cartItem.riderType.externalId)}, 'sellableAccess.deviceType': constantsXml.deviceType.officePos, $or:[{sellingStartDay: {$eq: null}}, {sellingStartDay: {$lte: moment.utc().date()}}, {sellingEndDay: {$eq: null}}, {sellingEndDay: {$gte: moment.utc().date()}}]});
                const penaltyProduct = products.find((f) => f.externalId == cartItem.penalty.productId);
                transaction.fareItemExternalId = (penaltyProduct) ? penaltyProduct.externalId : 0;
                transaction.fareItemName = (penaltyProduct) ? penaltyProduct.name : constantsXml.fareItemName.na;
                transaction.fareItemIssuedExpirationTimeStamp = (penaltyProduct) ?  issueTime.add(penaltyProduct.duration, 'days').startOf('day').add(businessDayStart, 'hours') : fareItemIssuedExpirationTimeStamp;
                transaction.totalPaymentInCents = transaction.farePrice;
                transaction.penaltyRefNumber = cartItem.penalty.refNumber;
                transaction.prodName = ticketItem.prodName;
                transaction.productType = (penaltyProduct) ? penaltyProduct.productType : constants.productType.absolutePenalty;
                transaction.waybillName = `${translations1st[constants.waybillName.penalty]}`;
                transaction.waybillNameTranslated = `${translations2nd[constants.waybillName.penalty]}`;
                transaction.riderExternalId = constants.defaults.accountId;
                transaction.riderName = constants.defaults.accountName;

                const vatRates = await this.fareTableModel.VatRates.find({fareEnvironmentConfigurationId: activeConfig._id});
                const vatRate = (vatRates.find((f) => [transaction.productType].includes(f.associatedProductTypes))) ? vatRates.find((f) => [transaction.productType].includes(f.associatedProductTypes)) : vatRates.find((f) => f.isDefault == true);
                const vatRule = await this.fareTableModel.VatRules.findOne({fareEnvironmentConfigurationId: activeConfig._id, paymentInstrumentType: constantsXml.paymentInstrument.prepayAccountEPurse});
                const vatAddedToTotal = (vatRule) ? ((transaction.paymentInstrument != constantsXml.paymentInstrument.prepayAccountEPurse) ? true : vatRule.isVatDeductedDuringTopup) : false;
                const vatRateInOneHundredths = (vatRate) ? vatRate.percents : 0;
                ticketItem.vatRate = vatRateInOneHundredths;
                transaction.vatRate = vatRateInOneHundredths;
                transaction.totalVat = Math.round(transaction.farePrice * (vatRateInOneHundredths / 10000) / (1 + (vatRateInOneHundredths / 10000)));

                const ticketItemPenalty = await this.modelTicket.findOneAndUpdate({ _id: ticket.id }, {
                    $push: {
                        items: ticketItem
                    }
                }, {new: true});

                transaction.ticketItemId = ticketItemPenalty.items[ticketItemPenalty.items.length - 1].id;

                let createdFile = await this.posTransactionCtrl.create({
                    function: 'penalty',
                    component: 'ticketPrinter',
                    parameters: {
                        avl: avl,
                        fare: fare,
                        financial: {
                            activityType: transaction.activityType,
                            fareItemCategory: transaction.fareItemCategory,
                            fareItemType: transaction.fareItemType,
                            fareItemExternalId: transaction.fareItemExternalId,
                            fareItemProductType: transaction.productType,
                            fareItemIssuedNumber: transaction.fareItemIssuedNumber,
                            fareItemIssuedExpirationTimeStamp: moment.utc(transaction.fareItemIssuedExpirationTimeStamp).format(this.dateTTimeFormat),
                            fareItemPurchasedStagesOrZones: 0,
                            fareItemName: transaction.fareItemName,
                            payingRiderTypeExternalId: transaction.riderExternalId,
                            payingRiderTypeName: transaction.riderName,
                            payingRiderAccountId: transaction.accountNo,
                            cumulativeFareOrProductPrice: transaction.farePrice,
                            totalPaymentInCents: transaction.totalPaymentInCents,
                            totalVatInCents: transaction.totalVat,
                            vatAddedToTotalPayment: vatAddedToTotal,
                            itemReferenceId: ticketItem.penaltyRefNumber,
                            penaltyFee: transaction.farePrice,
                            paymentInstrument: transaction.paymentInstrument,
                            paymentMethod: transaction.paymentMethod,
                            currencyCode: global.currentPosData.currencyCode,
                            paymentInstrumentTransactionId: (transaction.cardTransactionNo) ? transaction.cardTransactionNo : 0,
                            relatedTransactionInformation: {
                                relatedTransactionTimestamp: moment.utc(transaction.transactionTime).format(this.dateTTimeFormat),
                                relatedTransactionDeviceType: constantsXml.deviceType.officePos,
                                relatedTransactionDeviceId: global.currentPosData.deviceId,
                                relatedTransactionTerminalId: global.currentPosData.terminalId,
                                relatedTransactionDeviceTransactionCounter: global.currentPosData.transactionCounter
                            },
                            taxInvoiceInformation: {
                                invoiceIdNumber: ticket.invoiceId.split('-')[2],
                                invoiceExternalReferenceCode: ticket.invoiceId,
                                taxInvoiceQrCode: ticket.taxQrCode
                            },
                            vatDetails: {
                                taxableAmountInCents: transaction.farePrice - transaction.totalVat,
                                vatRate: transaction.vatRate,
                                vatInCents: transaction.totalVat
                            }
                        }
                    }
                });

                await this.modelFinancial.create(transaction);

                return res.status(200).json({
                    ticketId: transaction.fareItemIssuedNumber,
                    ticketValid: '',
                    qrCodes: [qrCode],
                    issueTime: moment.utc(ticket.issueTime).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat),
                    shouldPrint: shouldNotPrint
                });

            } else if (cartItem.productPeriod != null) {

                ticketItem.price = cartItem.productPeriod.price;
                ticketItem.priceCent = cartItem.productPeriod.priceCent;
                ticketItem.prodName = cartItem.productPeriod.name;
                ticketItem.prodNameTranslate = cartItem.productPeriod.nameTranslated;
                // ticketItem.ticketExpiryTime =  issueTime.add(180, 'days').startOf('day').add(2, 'hours');
                // transaction
                transaction.fareItemIssuedNumber = fareItemIssuedNumber;
                transaction.activityType = constantsXml.activityType.issue;
                transaction.issuedFormatType = issuedFormatType;
                transaction.numberOfPassengers = cartItem.numberOfPassengers;
                transaction.fareItemCategory = fareItemCategory;
                transaction.fareItemType = fareItemType;
                transaction.farePrice = cartItem.productPeriod.priceCent;
                transaction.farePriceCurrency = ticketItem.price;
                const products = await this.fareTableModel.Products.find({fareEnvironmentConfigurationId: activeConfig._id, productEntityClass: constants.productEntityClass.fareSellableProduct, purchasableAccess: {$in: String(cartItem.riderType.externalId)}, 'sellableAccess.deviceType': constantsXml.deviceType.officePos, $or:[{sellingStartDay: {$eq: null}}, {sellingStartDay: {$lte: moment.utc().date()}}, {sellingEndDay: {$eq: null}}, {sellingEndDay: {$gte: moment.utc().date()}}]});
                const productPeriod = products.find((f) => f.externalId == cartItem.productPeriod.id);
                transaction.fareItemExternalId = (productPeriod) ? productPeriod.externalId : 0;
                transaction.fareItemName = (productPeriod) ? productPeriod.name : constantsXml.fareItemName.na;
                fareItemIssuedExpirationTimeStamp = (productPeriod) ? issueTime.add(productPeriod.duration, 'days') : fareItemIssuedExpirationTimeStamp;
                transaction.fareItemIssuedExpirationTimeStamp = (productPeriod) ? ((productPeriod.validityCalculationStartCondition == constants.validityStart.timeOfPurchase) ? fareItemIssuedExpirationTimeStamp : fareItemIssuedExpirationTimeStamp.startOf('day').add(businessDayStart, 'hours')) : fareItemIssuedExpirationTimeStamp;
                if (cartItem.accountNo) {
                    const accountContracts = await this.accountHolderServ.getAccountContractsShort(cartItem.accountNo);
                    const contractTimestamp = await this.accountContractTimestamp(products, productPeriod, accountContracts);
                    transaction.fareItemIssuedExpirationTimeStamp = moment.utc(contractTimestamp.expirationTimestamp);
                }
                transaction.totalPaymentInCents = (productPeriod) ? productPeriod.priceInCents : transaction.farePrice;
                transaction.prodName = ticketItem.prodName;
                transaction.productType = (productPeriod) ? productPeriod.productType : constants.productType.periodPass;
                transaction.waybillName = `${cartItem.productPeriod.name}`;
                transaction.waybillNameTranslated = `${cartItem.productPeriod.nameTranslated}`;

                if (productPeriod && productPeriod.adjustments && cartItem.productPeriod.adjustmentExternalId != 0) {
                    const productPeriodAdjustment = productPeriod.adjustments.find((f) => f.externalId == cartItem.productPeriod.adjustmentExternalId);
                    transaction.fareAdjustment = productPeriodAdjustment;
                    passengerCorrectionFareDetails = this.passengerCorrectionFareDetails(cartItem, transaction.fareAdjustment, transaction.farePrice, '');
                }

            } else if (cartItem.topupAccount != null) {

                let account = await this.accountHolderServ.getAccount(cartItem.topupAccount.accountNo);
                if (!account) {
                    account = {
                        riderTypeId: cartItem.topupAccount.riderTypeId,
                        type: cartItem.topupAccount.type,
                        shortId: cartItem.topupAccount.id
                    };
                }

                ticketItem.price = cartItem.topupAccount.price;
                ticketItem.priceCent = cartItem.topupAccount.priceCent;
                ticketItem.prodName = cartItem.topupAccount.name;
                ticketItem.prodNameTranslate = cartItem.topupAccount.nameTranslated;
                // ticketItem.ticketExpiryTime =  issueTime.add(365, 'days');
                ticketItem.riderExternalId = account.riderTypeId;
                // transaction
                transaction.fareItemIssuedNumber = fareItemIssuedNumber;
                transaction.activityType = constantsXml.activityType.credit;
                transaction.issuedFormatType = issuedFormatType;
                transaction.numberOfPassengers = cartItem.numberOfPassengers;
                transaction.fareItemCategory = fareItemCategory;
                transaction.fareItemType = fareItemType;
                transaction.farePrice = cartItem.topupAccount.priceCent;
                transaction.farePriceCurrency = ticketItem.price;
                const topupProduct = await this.accountHolderCtrl.topupProduct(cartItem.topupAccount.riderTypeId);
                transaction.fareItemExternalId = (Object.keys(topupProduct).length != 0) ? topupProduct.id : 0;
                transaction.fareItemName = (Object.keys(topupProduct).length != 0) ? topupProduct.name : constantsXml.fareItemName.na;
                transaction.fareItemIssuedExpirationTimeStamp = (Object.keys(topupProduct).length != 0) ?  issueTime.add(topupProduct.duration, 'days').startOf('day').add(businessDayStart, 'hours') : fareItemIssuedExpirationTimeStamp;
                transaction.totalPaymentInCents = transaction.farePrice;
                //transaction.prodName = ticketItem.prodName;
                transaction.productType = (Object.keys(topupProduct).length != 0) ? topupProduct.type : constants.productType.eWallet;
                transaction.waybillName = `${translations1st[constants.waybillName.topUp]}`;
                transaction.waybillNameTranslated = `${translations2nd[constants.waybillName.topUp]}`;
                transaction.riderExternalId = account.riderTypeId;
                transaction.riderName = account.type;

                const vatRates = await this.fareTableModel.VatRates.find({fareEnvironmentConfigurationId: activeConfig._id});
                const vatRate = (vatRates.find((f) => [transaction.productType].includes(f.associatedProductTypes))) ? vatRates.find((f) => [transaction.productType].includes(f.associatedProductTypes)) : vatRates.find((f) => f.isDefault == true);
                const vatRule = await this.fareTableModel.VatRules.findOne({fareEnvironmentConfigurationId: activeConfig._id, paymentInstrumentType: constantsXml.paymentInstrument.prepayAccountEPurse});
                const vatAddedToTotal = (vatRule) ? ((transaction.paymentInstrument != constantsXml.paymentInstrument.prepayAccountEPurse) ? true : vatRule.isVatDeductedDuringTopup) : false;
                const vatRateInOneHundredths = (vatRate) ? vatRate.percents : 0;
                ticketItem.vatRate = vatRateInOneHundredths;
                transaction.vatRate = vatRateInOneHundredths;
                transaction.totalVat = Math.round(transaction.farePrice * (vatRateInOneHundredths / 10000) / (1 + (vatRateInOneHundredths / 10000)));

                const ticketItemTopup = await this.modelTicket.findOneAndUpdate({ _id: ticket.id }, {
                    $push: {
                        items: ticketItem
                    }
                }, {new: true});

                transaction.ticketItemId = ticketItemTopup.items[ticketItemTopup.items.length - 1].id;

                let createdFile = await this.posTransactionCtrl.create({
                    function: 'topUp',
                    component: 'ticketPrinter',
                    parameters: {
                        avl: avl,
                        fare: fare,
                        financial: {
                            activityType: transaction.activityType,
                            fareItemCategory: transaction.fareItemCategory,
                            fareItemType: transaction.fareItemType,
                            fareItemExternalId: transaction.fareItemExternalId,
                            fareItemProductType: transaction.productType,
                            fareItemIssuedNumber: transaction.fareItemIssuedNumber,
                            fareItemIssuedExpirationTimeStamp: moment.utc(transaction.fareItemIssuedExpirationTimeStamp).format(this.dateTTimeFormat),
                            fareItemPurchasedStagesOrZones: (ticketItem.zoneTraveled) ? ticketItem.zoneTraveled : 0,
                            fareItemName: transaction.fareItemName,
                            payingRiderTypeExternalId: transaction.riderExternalId,
                            payingRiderTypeName: transaction.riderName,
                            payingRiderAccountId: transaction.accountNo,
                            cumulativeFareOrProductPrice: transaction.farePrice,
                            totalPaymentInCents: transaction.totalPaymentInCents,
                            totalVatInCents: transaction.totalVat,
                            vatAddedToTotalPayment: vatAddedToTotal,
                            penaltyFee: 0,
                            paymentInstrument: transaction.paymentInstrument,
                            paymentMethod: transaction.paymentMethod,
                            currencyCode: global.currentPosData.currencyCode,
                            paymentInstrumentTransactionId: (transaction.cardTransactionNo) ? transaction.cardTransactionNo : 0,
                            relatedTransactionInformation: {
                                relatedTransactionTimestamp: moment.utc(transaction.transactionTime).format(this.dateTTimeFormat),
                                relatedTransactionDeviceType: constantsXml.deviceType.officePos,
                                relatedTransactionDeviceId: global.currentPosData.deviceId,
                                relatedTransactionTerminalId: global.currentPosData.terminalId,
                                relatedTransactionDeviceTransactionCounter: global.currentPosData.transactionCounter
                            },
                            taxInvoiceInformation: {
                                invoiceIdNumber: ticket.invoiceId.split('-')[2],
                                invoiceExternalReferenceCode: ticket.invoiceId,
                                taxInvoiceQrCode: ticket.taxQrCode
                            },
                            vatDetails: {
                                taxableAmountInCents: transaction.farePrice - transaction.totalVat,
                                vatRate: transaction.vatRate,
                                vatInCents: transaction.totalVat
                            }
                        },
                        order: (global.currentPosData.hasSalesOrder) ? {
                            salesOrderIdNumber: ticket.salesOrderIdNumber,
                            salesOrderExternalReferenceCode: ticket.invoiceId
                        } : ''
                    }
                });

                await this.modelFinancial.create(transaction);

                return res.status(200).json({
                    ticketId: transaction.fareItemIssuedNumber,
                    ticketValid: moment.utc(transaction.fareItemIssuedExpirationTimeStamp).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat),
                    qrCodes: [qrCode],
                    issueTime: moment.utc(ticket.issueTime).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat),
                    shouldPrint: shouldNotPrint
                });
            } else if (cartItem.smartCardIssue != null) {

                const cardHolder = (cartItem.smartCardIssue.cardholderId) ? await this.service.getCardHolderById(cartItem.smartCardIssue.cardholderId) : {id: null};

                ticketItem.price = cartItem.smartCardIssue.price;
                ticketItem.priceCent = cartItem.smartCardIssue.priceCent;
                ticketItem.prodName = cartItem.smartCardIssue.name;
                ticketItem.prodNameTranslate = cartItem.smartCardIssue.nameTranslated;
                ticketItem.riderExternalId = cartItem.riderType.externalId;
                ticketItem.cardVersion = cartItem.smartCardIssue.data.cardVersion;
                ticketItem.cardUid = cartItem.smartCardIssue.data.cardUid;
                ticketItem.cardholderId = cardHolder.id;
                // transaction
                transaction.cardVersion = ticketItem.cardVersion;
                transaction.cardUid = ticketItem.cardUid;
                transaction.cardholderId = ticketItem.cardholderId;
                transaction.fareItemIssuedNumber = fareItemIssuedNumber;
                transaction.activityType = constantsXml.activityType.manageSmartCard;
                transaction.issuedFormatType = issuedFormatType;
                transaction.numberOfPassengers = cartItem.numberOfPassengers;
                transaction.fareItemCategory = fareItemCategory;
                transaction.fareItemType = fareItemType;
                transaction.farePrice = cartItem.smartCardIssue.priceCent;
                transaction.farePriceCurrency = ticketItem.price;
                const ePurseProduct = await this.fareTableModel.Products.findOne({fareEnvironmentConfigurationId: activeConfig._id, productType: constants.productType.ePurse, purchasableAccess: {$in: String(cartItem.riderType.externalId)}, 'sellableAccess.deviceType': constantsXml.deviceType.officePos, $or:[{sellingStartDay: {$eq: null}}, {sellingStartDay: {$lte: moment.utc().date()}}, {sellingEndDay: {$eq: null}}, {sellingEndDay: {$gte: moment.utc().date()}}]});
                const products = await this.fareTableModel.Products.find({fareEnvironmentConfigurationId: activeConfig._id, productEntityClass: constants.productEntityClass.fareSellableProduct, purchasableAccess: {$in: String(cartItem.riderType.externalId)}, 'sellableAccess.deviceType': constantsXml.deviceType.officePos, $or:[{sellingStartDay: {$eq: null}}, {sellingStartDay: {$lte: moment.utc().date()}}, {sellingEndDay: {$eq: null}}, {sellingEndDay: {$gte: moment.utc().date()}}]});
                const productIssue = products.find((f) => f.externalId == cartItem.smartCardIssue.productId);
                const smartcardprice = await this.fareTableModel.SmartCardPrices.findOne({fareEnvironmentConfigurationId: activeConfig._id, productEntityClass: constants.productEntityClass.smartCardPrice, purchasableAccess: {$in: String(cartItem.riderType.externalId)}});
                transaction.fareItemExternalId = (productIssue) ? productIssue.externalId : (smartcardprice) ? smartcardprice.externalId : 0;
                transaction.fareItemName = (productIssue) ? productIssue.name : (smartcardprice) ? smartcardprice.name : constantsXml.fareItemName.na;
                transaction.fareItemIssuedExpirationTimeStamp = (productIssue) ?  issueTime.add(productIssue.duration, 'days') : issueTime;
                transaction.totalPaymentInCents = cartItem.smartCardIssue.priceCent;
                transaction.prodName = ticketItem.prodName;
                transaction.productType = constants.productType.ePurse;
                transaction.waybillName = `${translations1st[constants.waybillName.issueSmartCard]}`;
                transaction.waybillNameTranslated = `${translations2nd[constants.waybillName.issueSmartCard]}`;
                transaction.riderExternalId = cartItem.riderType.externalId;
                transaction.riderName = cartItem.riderType.name;

                let smartCardContracts = await this.smartCardContracts(cartItem.smartCardIssue.data);
                const smartCardProfiles = await this.fareTableModel.SmartCardProfiles.find({fareEnvironmentConfigurationId: activeConfig._id});

                const vatRates = await this.fareTableModel.VatRates.find({fareEnvironmentConfigurationId: activeConfig._id});
                const vatRate = (vatRates.find((f) => [productIssue.productType].includes(f.associatedProductTypes))) ? vatRates.find((f) => [productIssue.productType].includes(f.associatedProductTypes)) : vatRates.find((f) => f.isDefault == true);
                const vatRule = await this.fareTableModel.VatRules.findOne({fareEnvironmentConfigurationId: activeConfig._id, paymentInstrumentType: constantsXml.paymentInstrument.prepaySmartCardEPurse});
                const vatAddedToTotal = (vatRule) ? ((transaction.paymentInstrument != constantsXml.paymentInstrument.prepaySmartCardEPurse) ? true : vatRule.isVatDeductedDuringTopup) : false;
                const vatRateInOneHundredths = (vatRate) ? vatRate.percents : 0;
                ticketItem.vatRate = vatRateInOneHundredths;
                transaction.vatRate = vatRateInOneHundredths;
                transaction.totalVat = Math.round(transaction.farePrice * (vatRateInOneHundredths / 10000) / (1 + (vatRateInOneHundredths / 10000)));

                const ticketItemIssue = await this.modelTicket.findOneAndUpdate({ _id: ticket.id }, {
                    $push: {
                        items: ticketItem
                    }
                }, {new: true});

                transaction.ticketItemId = ticketItemIssue.items[ticketItemIssue.items.length - 1].id;

                const currentTimestamp = moment.utc().format(this.dateTTimeFormat);

                if (cartItem.smartCardIssue.data.transactionCounter == 0) {

                    // issue smart card
                    const issueValues = {
                        preferredLanguage: constantsXml.languageHexCode[global.currentPosData.firstLanguage.split('-')[0]],
                        refundableDeposit: 0,
                        riderTypeExternalId: transaction.riderExternalId,
                        riderTypeExpirationTimestamp: moment.utc(cartItem.smartCardIssue.data.cardExpirationTimestamp).format('X'),
                        terminalId: global.currentPosData.terminalId,
                        merchantId: 1,
                        ptoReference: 0
                    };

                    const issuePiccCardData = await this.smartCardServ.issuePersonalizedCard(issueValues);
                    const issuerIdPiccCardData = await this.smartCardServ.issuerId(global.currentPosData.organizationExternalId);
                    const issueCardCounters = await this.smartCardServ.cardCounters({
                        transactionCounter: issuePiccCardData.transactionCounter,
                        countersTimestamp: moment.utc().format('X')
                    });
                    const issueTopUpLogs = await this.smartCardServ.topUpLogs({
                        topupLogTransactionTimestamp: moment.utc().format('X'),
                        topupLogContractCategory: enumsXml.smartCardContractCategory.Undefined,
                        topupLogContractId: 0,
                        topupLogContractPointer: 0,
                        topupLogFareItemCategory: enumsXml.fareItemCategoryType.initSmartCard,
                        topupLogFareItemType: enumsXml.fareItemType.issueSmartCard,
                        topupLogActivityType: enumsXml.activityType.issue,
                        topupLogTerminalId: issueValues.terminalId,
                        topupLogTerminalType: enumsXml.deviceType.officePos,
                        topupLogTransactionId: issuePiccCardData.transactionCounter,
                        topupLogAmountCredited: 0,
                        topupLogMerchantNumber: issueValues.merchantId,
                        topupLogPtoRference: issueValues.ptoReference
                    });
                    //const issueCardIteration = await this.smartCardServ.cardIteration(issuePiccCardData.cardIteration + 1);
                    const issueCardPrintedId = await this.smartCardServ.getCardPrintedId(issuePiccCardData.cardUid);
                    const issueSmartCardProfile = smartCardProfiles.find((f) => f.cardTypeShortNumber == issuePiccCardData.cardType && f.cardVersionShortNumber == issuePiccCardData.cardVersion && f.isEnabled);
                    const issueCardVersion = (issueSmartCardProfile) ? issueSmartCardProfile.cardVersion : cartItem.smartCardIssue.data.version;
                    const issueCardType = (issueSmartCardProfile) ? issueSmartCardProfile.cardType : constantsXml.smartCardType.standard;
                    const issueCardTechnology = (issueSmartCardProfile) ? issueSmartCardProfile.cardTechnology : constantsXml.smartCardPhysicalType.mifareDesfireEv3;

                    await this.posTransactionCtrl.create({
                        function: 'smartCardIinitializeIssue',
                        component: 'smartCardReader',
                        parameters: {
                            avl: avl,
                            fare: fare,
                            financial: {
                                activityType: transaction.activityType,
                                fareItemCategory: transaction.fareItemCategory,
                                fareItemType: transaction.fareItemType,
                                fareItemExternalId: 0,
                                fareItemProductType: transaction.productType,
                                fareItemIssuedNumber: 0,
                                fareItemIssuedExpirationTimeStamp: moment.utc().format(this.dateTTimeFormat),
                                fareItemPurchasedStagesOrZones: 0,
                                fareItemName: constantsXml.fareItemName.na,
                                payingRiderTypeExternalId: transaction.riderExternalId,
                                payingRiderTypeName: transaction.riderName,
                                payingRiderAccountId: transaction.accountNo,
                                cumulativeFareOrProductPrice: 0,
                                totalPaymentInCents: 0,
                                totalVatInCents: 0,
                                vatAddedToTotalPayment: vatAddedToTotal,
                                penaltyFee: 0,
                                paymentInstrument: constantsXml.paymentInstrument.none,
                                paymentMethod: constantsXml.paymentMethod.none,
                                currencyCode: global.currentPosData.currencyCode,
                                paymentInstrumentTransactionId: 0,
                                relatedTransactionInformation: {
                                    relatedTransactionTimestamp: moment.utc(transaction.transactionTime).format(this.dateTTimeFormat),
                                    relatedTransactionDeviceType: constantsXml.deviceType.officePos,
                                    relatedTransactionDeviceId: global.currentPosData.deviceId,
                                    relatedTransactionTerminalId: global.currentPosData.terminalId,
                                    relatedTransactionDeviceTransactionCounter: global.currentPosData.transactionCounter
                                },
                                taxInvoiceInformation: {
                                    invoiceIdNumber: ticket.invoiceId.split('-')[2],
                                    invoiceExternalReferenceCode: ticket.invoiceId,
                                    taxInvoiceQrCode: ticket.taxQrCode
                                },
                                vatDetails: {
                                    taxableAmountInCents: 0,
                                    vatRate: 0,
                                    vatInCents: 0
                                }
                            },
                            card: {
                                // card reference
                                cardUid: issuePiccCardData.cardUid,
                                cardPrintedId: issueCardPrintedId,
                                cardType: issueCardType,
                                cardTypeShortNumber: issuePiccCardData.cardType,
                                cardVersion: issueCardVersion,
                                cardVersionShortNumber: issuePiccCardData.cardVersion,
                                cardIteration: issuePiccCardData.cardIteration,
                                physicalCardType: issueCardTechnology,
                                issuerId: global.currentPosData.organizationExternalId,
                                transactionCounter: issuePiccCardData.transactionCounter,
                                // card snapshot
                                timestamp: currentTimestamp,
                                currencyCode: issuePiccCardData.currencyCode,
                                preferredLanguage: issuePiccCardData.preferredLanguage,
                                riderTypeExternalId: issuePiccCardData.riderTypeExternalId,
                                storedValue: 0,
                                refundableDeposit: issuePiccCardData.refundableDeposit,
                                cardExpirationTimestamp: issuePiccCardData.cardExpirationTimestamp,
                                alic: issuePiccCardData.alic,
                                alwc: issuePiccCardData.alwc,
                                blockStatus: issuePiccCardData.blockStatus,
                                blockTimestamp: issuePiccCardData.blockTimestamp,
                                contracts: smartCardContracts.contracts,
                                // card operation
                                issuing: constantsXml.smartCardIssueType.issue,
                                // cardHolderInfo
                                cardHolderInfoBasic: (cardHolder.id) ? {
                                    type: constantsXml.cardHolderType[cardHolder.cardHolderInfoBasic.cardHolderType],
                                    gender: constantsXml.cardHolderGenderType[cardHolder.cardHolderInfoBasic.cardHolderGender],
                                    firstName: cardHolder.cardHolderInfoBasic.cardHolderFirstName,
                                    lastName: cardHolder.cardHolderInfoBasic.cardHolderLastName,
                                    dayOfBirth: (cardHolder.cardHolderInfoBasic.cardHolderDayOfBirth) ? moment.utc(cardHolder.cardHolderInfoBasic.cardHolderDayOfBirth).format(this.dateTTimeFormat) : ''
                                } : '',
                                cardHolderInfoExtended: (cardHolder.id) ? {
                                    addressLine1: cardHolder.cardHolderInfoExtended.cardHolderAddressLine1,
                                    addressLine2: cardHolder.cardHolderInfoExtended.cardHolderAddressLine2,
                                    addressLine3: cardHolder.cardHolderInfoExtended.cardHolderAddressLine3,
                                    city: cardHolder.cardHolderInfoExtended.cardHolderCity,
                                    postalCode: cardHolder.cardHolderInfoExtended.cardHolderPostalCode,
                                    countryCode: cardHolder.cardHolderInfoExtended.cardHolderCountryCode,
                                    countryProvince: cardHolder.cardHolderInfoExtended.cardHolderCountryProvince,
                                    phoneNumber: cardHolder.cardHolderInfoExtended.cardHolderPhoneNumber,
                                    identificationType: constantsXml.cardHolderIdentificationType[cardHolder.cardHolderInfoExtended.cardHolderIdentificationType],
                                    identificationId: cardHolder.cardHolderInfoExtended.cardHolderIdentificationId,
                                    identificationName: cardHolder.cardHolderInfoExtended.cardHolderIdentificationName,
                                    identificationExpiry: moment.utc(cardHolder.cardHolderInfoExtended.cardHolderIdentificationExpiry).format(this.dateTTimeFormat)
                                } : ''
                            },
                            order: (global.currentPosData.hasSalesOrder) ? {
                                salesOrderIdNumber: ticket.salesOrderIdNumber,
                                salesOrderExternalReferenceCode: ticket.invoiceId
                            } : ''
                        }
                    });

                    // add ePurse contract to smart card
                    if (cartItem.riderType.storedValue && !cartItem.smartCardIssue.hasEPurse) {
                        let contractNo = 1;
                        const ePurseValues = {
                            contractId: ePurseProduct.externalId,
                            instanceId: '0002000000',
                            terminalId: issueValues.terminalId,
                            merchantId: issueValues.merchantId,
                            ptoReference: issueValues.ptoReference
                        };

                        const ePursePiccCardData = await this.smartCardServ.loadEpurseContract(ePurseValues);
                        const cardCounters = await this.smartCardServ.cardCounters({
                            transactionCounter: ePursePiccCardData.transactionCounter,
                            countersTimestamp: moment.utc().format('X')
                        });
                        const topUpLogs = await this.smartCardServ.topUpLogs({
                            topupLogTransactionTimestamp: moment.utc().format('X'),
                            topupLogContractCategory: enumsXml.smartCardContractCategory.StoredValueRelative,
                            topupLogContractId: ePurseValues.contractId,
                            topupLogContractPointer: contractNo,
                            topupLogFareItemCategory: enumsXml.fareItemCategoryType.fareEpurse,
                            topupLogFareItemType: enumsXml.fareItemType.createEpurseSmartCard,
                            topupLogActivityType: enumsXml.activityType.issue,
                            topupLogTerminalId: ePurseValues.terminalId,
                            topupLogTerminalType: enumsXml.deviceType.officePos,
                            topupLogTransactionId: ePursePiccCardData.transactionCounter,
                            topupLogAmountCredited: 0,
                            topupLogMerchantNumber: ePurseValues.merchantId,
                            topupLogPtoRference: ePurseValues.ptoReference
                        });
                        const cardPrintedId = await this.smartCardServ.getCardPrintedId(ePursePiccCardData.cardUid);
                        const smartCardProfile = smartCardProfiles.find((f) => f.cardTypeShortNumber == ePursePiccCardData.cardType && f.cardVersionShortNumber == ePursePiccCardData.cardVersion && f.isEnabled);
                        const cardVersion = (smartCardProfile) ? smartCardProfile.mediaPurposeType : cartItem.smartCardIssue.data.version;
                        const cardType = (smartCardProfile) ? smartCardProfile.cardType : constantsXml.smartCardType.standard;
                        const cardTechnology = (smartCardProfile) ? smartCardProfile.cardTechnology : constantsXml.smartCardPhysicalType.mifareDesfireEv3;

                        smartCardContracts = await this.smartCardContracts(ePursePiccCardData);

                        await this.posTransactionCtrl.create({
                            function: 'smartCardContract',
                            component: 'smartCardReader',
                            parameters: {
                                avl: avl,
                                fare: fare,
                                financial: {
                                    activityType: constantsXml.activityType.issue,
                                    fareItemCategory: transaction.fareItemCategory,
                                    fareItemType: constantsXml.fareItemType.createEpurseSmartCard,
                                    fareItemExternalId: ePurseProduct.externalId,
                                    fareItemProductType: transaction.productType,
                                    fareItemIssuedNumber: 0,
                                    fareItemIssuedExpirationTimeStamp: moment.utc().format(this.dateTTimeFormat),
                                    fareItemPurchasedStagesOrZones: 0,
                                    fareItemName: ePurseProduct.name,
                                    payingRiderTypeExternalId: transaction.riderExternalId,
                                    payingRiderTypeName: transaction.riderName,
                                    payingRiderAccountId: transaction.accountNo,
                                    cumulativeFareOrProductPrice: ePurseProduct.priceInCents,
                                    totalPaymentInCents: transaction.totalPaymentInCents,
                                    totalVatInCents: transaction.totalVat,
                                    vatAddedToTotalPayment: vatAddedToTotal,
                                    penaltyFee: 0,
                                    paymentInstrument: transaction.paymentInstrument,
                                    paymentMethod: transaction.paymentMethod,
                                    currencyCode: global.currentPosData.currencyCode,
                                    paymentInstrumentTransactionId: (transaction.cardTransactionNo) ? transaction.cardTransactionNo : 0,
                                    relatedTransactionInformation: {
                                        relatedTransactionTimestamp: moment.utc(transaction.transactionTime).format(this.dateTTimeFormat),
                                        relatedTransactionDeviceType: constantsXml.deviceType.officePos,
                                        relatedTransactionDeviceId: global.currentPosData.deviceId,
                                        relatedTransactionTerminalId: global.currentPosData.terminalId,
                                        relatedTransactionDeviceTransactionCounter: global.currentPosData.transactionCounter
                                    },
                                    taxInvoiceInformation: {
                                        invoiceIdNumber: ticket.invoiceId.split('-')[2],
                                        invoiceExternalReferenceCode: ticket.invoiceId,
                                        taxInvoiceQrCode: ticket.taxQrCode
                                    },
                                    vatDetails: {
                                        taxableAmountInCents: transaction.farePrice - transaction.totalVat,
                                        vatRate: transaction.vatRate,
                                        vatInCents: transaction.totalVat
                                    }
                                },
                                card: {
                                    // card reference
                                    cardUid: ePursePiccCardData.cardUid,
                                    cardPrintedId: cardPrintedId,
                                    cardType: cardType,
                                    cardTypeShortNumber: ePursePiccCardData.cardType,
                                    cardVersion: cardVersion,
                                    cardVersionShortNumber: ePursePiccCardData.cardVersion,
                                    cardIteration: ePursePiccCardData.cardIteration,
                                    physicalCardType: cardTechnology,
                                    issuerId: ePursePiccCardData.issuerId,
                                    transactionCounter: ePursePiccCardData.transactionCounter,
                                    // card snapshot
                                    timestamp: currentTimestamp,
                                    currencyCode: ePursePiccCardData.currencyCode,
                                    preferredLanguage: ePursePiccCardData.preferredLanguage,
                                    riderTypeExternalId: ePursePiccCardData.riderTypeExternalId,
                                    storedValue: ePursePiccCardData['contractValue'+contractNo],
                                    refundableDeposit: ePursePiccCardData.refundableDeposit,
                                    cardExpirationTimestamp: ePursePiccCardData.cardExpirationTimestamp,
                                    alic: ePursePiccCardData.alic,
                                    alwc: ePursePiccCardData.alwc,
                                    blockStatus: ePursePiccCardData.blockStatus,
                                    blockTimestamp: ePursePiccCardData.blockTimestamp,
                                    contracts: smartCardContracts.contracts,
                                    // card operation
                                    addContract: {
                                        contractType: constantsXml.contractType.ePurse,
                                        productId: ePursePiccCardData['contractId'+contractNo],
                                        contractId: ePursePiccCardData['contractId'+contractNo],
                                        instanceId: ePursePiccCardData['instanceId'+contractNo],
                                        contractStatus: ePursePiccCardData['contractStatus'+contractNo],
                                        contractStartTimestamp: ePursePiccCardData['contractStartTimestamp'+contractNo],
                                        contractExpirationTimestamp: ePursePiccCardData['contractExpirationTimestamp'+contractNo],
                                        contractStateChangeTimestamp: ePursePiccCardData['contractStateChangeTimestamp'+contractNo],
                                        contractValue: ePursePiccCardData['contractValue'+contractNo]
                                    }
                                },
                                order: (global.currentPosData.hasSalesOrder) ? {
                                    salesOrderIdNumber: ticket.salesOrderIdNumber,
                                    salesOrderExternalReferenceCode: ticket.invoiceId
                                } : ''
                            }
                        });
                    }

                }

                // processing fee
                const piccCardData = await this.smartCardServ.getPiccCardData();
                /*const cardCounters = await this.smartCardServ.cardCounters({
                    transactionCounter: piccCardData.transactionCounter + 1,
                    countersTimestamp: moment.utc().format('X')
                });
                const topUpLogs = await this.smartCardServ.topUpLogs({
                    topupLogTransactionTimestamp: moment.utc().format('X'),
                    topupLogContractCategory: enumsXml.smartCardContractCategory.Undefined,
                    topupLogContractId: 0,
                    topupLogContractPointer: 0,
                    topupLogFareItemCategory: enumsXml.fareItemCategoryType.fareFeeItem,
                    topupLogFareItemType: enumsXml.fareItemType.issueSmartCard,
                    topupLogActivityType: enumsXml.activityType.issue,
                    topupLogTerminalId: issueValues.terminalId,
                    topupLogTerminalType: enumsXml.deviceType.officePos,
                    topupLogTransactionId: piccCardData.transactionCounter + 1,
                    topupLogAmountCredited: 0,
                    topupLogMerchantNumber: issueValues.merchantId,
                    topupLogPtoRference: piccCardData.ptoReference
                });*/
                const cardPrintedId = await this.smartCardServ.getCardPrintedId(piccCardData.cardUid);
                const smartCardProfile = smartCardProfiles.find((f) => f.cardTypeShortNumber == piccCardData.cardType && f.cardVersionShortNumber == piccCardData.cardVersion && f.isEnabled);
                const cardVersion = (smartCardProfile) ? smartCardProfile.cardVersion : cartItem.smartCardIssue.data.version;
                const cardType = (smartCardProfile) ? smartCardProfile.cardType : constantsXml.smartCardType.standard;
                const cardTechnology = (smartCardProfile) ? smartCardProfile.cardTechnology : constantsXml.smartCardPhysicalType.mifareDesfireEv3;

                await this.posTransactionCtrl.create({
                    function: 'smartCardIinitializeIssue',
                    component: 'smartCardReader',
                    parameters: {
                        avl: avl,
                        fare: fare,
                        financial: {
                            activityType: constantsXml.activityType.issue,
                            fareItemCategory: constantsXml.fareItemCategory.fareFeeItem,
                            fareItemType: constantsXml.fareItemType.issueSmartCard,
                            fareItemExternalId: transaction.fareItemExternalId,
                            fareItemProductType: transaction.productType,
                            fareItemIssuedNumber: transaction.fareItemIssuedNumber,
                            fareItemIssuedExpirationTimeStamp: moment.utc().format(this.dateTTimeFormat),
                            fareItemPurchasedStagesOrZones: 0,
                            fareItemName: transaction.fareItemName,
                            payingRiderTypeExternalId: transaction.riderExternalId,
                            payingRiderTypeName: transaction.riderName,
                            payingRiderAccountId: transaction.accountNo,
                            cumulativeFareOrProductPrice: transaction.farePrice,
                            totalPaymentInCents: transaction.totalPaymentInCents,
                            totalVatInCents: transaction.totalVat,
                            vatAddedToTotalPayment: vatAddedToTotal,
                            penaltyFee: 0,
                            paymentInstrument: transaction.paymentInstrument,
                            paymentMethod: transaction.paymentMethod,
                            currencyCode: global.currentPosData.currencyCode,
                            paymentInstrumentTransactionId: (transaction.cardTransactionNo) ? transaction.cardTransactionNo : 0,
                            relatedTransactionInformation: {
                                relatedTransactionTimestamp: moment.utc(transaction.transactionTime).format(this.dateTTimeFormat),
                                relatedTransactionDeviceType: constantsXml.deviceType.officePos,
                                relatedTransactionDeviceId: global.currentPosData.deviceId,
                                relatedTransactionTerminalId: global.currentPosData.terminalId,
                                relatedTransactionDeviceTransactionCounter: global.currentPosData.transactionCounter
                            },
                            taxInvoiceInformation: {
                                invoiceIdNumber: ticket.invoiceId.split('-')[2],
                                invoiceExternalReferenceCode: ticket.invoiceId,
                                taxInvoiceQrCode: ticket.taxQrCode
                            },
                            vatDetails: {
                                taxableAmountInCents: transaction.farePrice - transaction.totalVat,
                                vatRate: transaction.vatRate,
                                vatInCents: transaction.totalVat
                            }
                        },
                        card: {
                            // card reference
                            cardUid: piccCardData.cardUid,
                            cardPrintedId: cardPrintedId,
                            cardType: cardType,
                            cardTypeShortNumber: piccCardData.cardType,
                            cardVersion: cardVersion,
                            cardVersionShortNumber: piccCardData.cardVersion,
                            cardIteration: piccCardData.cardIteration,
                            physicalCardType: cardTechnology,
                            issuerId: global.currentPosData.organizationExternalId,
                            transactionCounter: piccCardData.transactionCounter,
                            // card snapshot
                            timestamp: currentTimestamp,
                            currencyCode: piccCardData.currencyCode,
                            preferredLanguage: piccCardData.preferredLanguage,
                            riderTypeExternalId: piccCardData.riderTypeExternalId,
                            storedValue: 0,
                            refundableDeposit: piccCardData.refundableDeposit,
                            cardExpirationTimestamp: piccCardData.cardExpirationTimestamp,
                            alic: piccCardData.alic,
                            alwc: piccCardData.alwc,
                            blockStatus: piccCardData.blockStatus,
                            blockTimestamp: piccCardData.blockTimestamp,
                            contracts: smartCardContracts.contracts,
                            // card operation
                            issuing: (cartItem.smartCardIssue.data.transactionCounter == 0) ? constantsXml.smartCardIssueType.issue : constantsXml.smartCardIssueType.reIssue
                        },
                        order: (global.currentPosData.hasSalesOrder) ? {
                            salesOrderIdNumber: ticket.salesOrderIdNumber,
                            salesOrderExternalReferenceCode: ticket.invoiceId
                        } : ''
                    }
                });

                await this.modelFinancial.create(transaction);

                if (global.currentPosData.hasAccounts && cartItem.accountId) {
                    await this.accountHolderServ.registerCard(cartItem.accountId, cartItem.smartCardIssue.id);
                }

                return res.status(200).json({
                    ticketId: transaction.fareItemIssuedNumber,
                    ticketValid: moment.utc(transaction.fareItemIssuedExpirationTimeStamp).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat),
                    qrCodes: [qrCode],
                    issueTime: moment.utc(ticket.issueTime).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat),
                    shouldPrint: shouldNotPrint
                });
            } else if (cartItem.topupSmartCard != null) {

                //const cardHolder = (cartItem.topupSmartCard.cardholderId) ? await this.service.getCardHolderById(cartItem.topupSmartCard.cardholderId) : {id: null};

                ticketItem.price = cartItem.topupSmartCard.price;
                ticketItem.priceCent = cartItem.topupSmartCard.priceCent;
                ticketItem.prodName = cartItem.topupSmartCard.name;
                ticketItem.prodNameTranslate = cartItem.topupSmartCard.nameTranslated;
                ticketItem.riderExternalId = cartItem.riderType.externalId;
                ticketItem.cardVersion = cartItem.topupSmartCard.data.cardVersion;
                ticketItem.cardUid = cartItem.topupSmartCard.data.cardUid;
                ticketItem.cardholderId = cartItem.topupSmartCard.cardholderId;
                // transaction
                transaction.cardVersion = ticketItem.cardVersion;
                transaction.cardUid = ticketItem.cardUid;
                transaction.cardholderId = ticketItem.cardholderId;
                transaction.fareItemIssuedNumber = fareItemIssuedNumber;
                transaction.activityType = constantsXml.activityType.issue;
                transaction.issuedFormatType = issuedFormatType;
                transaction.numberOfPassengers = cartItem.numberOfPassengers;
                transaction.fareItemCategory = fareItemCategory;
                transaction.fareItemType = fareItemType;
                transaction.farePrice = cartItem.topupSmartCard.priceCent;
                transaction.farePriceCurrency = ticketItem.price;
                const smartcardprice = await this.fareTableModel.SmartCardPrices.findOne({fareEnvironmentConfigurationId: activeConfig._id, productEntityClass: constants.productEntityClass.smartCardPrice, purchasableAccess: {$in: String(cartItem.riderType.externalId)}});
                const products = await this.fareTableModel.Products.find({fareEnvironmentConfigurationId: activeConfig._id, productEntityClass: constants.productEntityClass.fareSellableProduct, purchasableAccess: {$in: String(cartItem.riderType.externalId)}, 'sellableAccess.deviceType': constantsXml.deviceType.officePos, $or:[{sellingStartDay: {$eq: null}}, {sellingStartDay: {$lte: moment.utc().date()}}, {sellingEndDay: {$eq: null}}, {sellingEndDay: {$gte: moment.utc().date()}}]});
                const topupProduct = products.find((f) => f.externalId == cartItem.topupSmartCard.productId);
                transaction.fareItemExternalId = (topupProduct) ? topupProduct.externalId : 0;
                transaction.fareItemName = (topupProduct) ? topupProduct.name : constantsXml.fareItemName.na;
                fareItemIssuedExpirationTimeStamp = (topupProduct) ? issueTime.add(topupProduct.duration, 'days') : fareItemIssuedExpirationTimeStamp;
                transaction.fareItemIssuedExpirationTimeStamp = (topupProduct) ? ((topupProduct.validityCalculationStartCondition == constants.validityStart.timeOfPurchase) ? fareItemIssuedExpirationTimeStamp : fareItemIssuedExpirationTimeStamp.startOf('day').add(businessDayStart, 'hours')) : fareItemIssuedExpirationTimeStamp;
                transaction.totalPaymentInCents = cartItem.topupSmartCard.priceCent;
                transaction.prodName = ticketItem.prodName;
                transaction.productType = (topupProduct) ? topupProduct.productType : constants.productType.ePurse;
                transaction.waybillName = `${translations1st[constants.waybillName.topUpSmartCard]}`;
                transaction.waybillNameTranslated = `${translations2nd[constants.waybillName.topUpSmartCard]}`;

                transaction.riderExternalId = cartItem.riderType.externalId;
                transaction.riderName = cartItem.riderType.name;

                let smartCardContracts = await this.smartCardContracts(cartItem.topupSmartCard.data);
                const smartCardProfiles = await this.fareTableModel.SmartCardProfiles.find({fareEnvironmentConfigurationId: activeConfig._id});

                const vatRates = await this.fareTableModel.VatRates.find({fareEnvironmentConfigurationId: activeConfig._id});
                const vatRate = (vatRates.find((f) => [topupProduct.productType].includes(f.associatedProductTypes))) ? vatRates.find((f) => [topupProduct.productType].includes(f.associatedProductTypes)) : vatRates.find((f) => f.isDefault == true);
                const vatRule = await this.fareTableModel.VatRules.findOne({fareEnvironmentConfigurationId: activeConfig._id, paymentInstrumentType: constantsXml.paymentInstrument.prepaySmartCardEPurse});
                const vatAddedToTotal = (vatRule) ? ((transaction.paymentInstrument != constantsXml.paymentInstrument.prepaySmartCardEPurse) ? true : vatRule.isVatDeductedDuringTopup) : false;
                const vatRateInOneHundredths = (vatRate) ? vatRate.percents : 0;
                ticketItem.vatRate = vatRateInOneHundredths;
                transaction.vatRate = vatRateInOneHundredths;
                transaction.totalVat = Math.round(transaction.farePrice * (vatRateInOneHundredths / 10000) / (1 + (vatRateInOneHundredths / 10000)));

                const ticketItemTopupSmartCard = await this.modelTicket.findOneAndUpdate({ _id: ticket.id }, {
                    $push: {
                        items: ticketItem
                    }
                }, {new: true});

                transaction.ticketItemId = ticketItemTopupSmartCard.items[ticketItemTopupSmartCard.items.length - 1].id;

                const currentTimestamp = moment.utc().format(this.dateTTimeFormat);

                if (process.env.CLIENT_SECURE_ACCESS_MODULE == 'demo') {
                    return res.status(200).json({
                        ticketId: transaction.fareItemIssuedNumber,
                        ticketValid: moment.utc(transaction.fareItemIssuedExpirationTimeStamp).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat),
                        qrCodes: [qrCode],
                        issueTime: moment.utc(ticket.issueTime).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat),
                        shouldPrint: shouldNotPrint
                    });
                }

                const piccCardData = await this.smartCardServ.getPiccCardData();
                smartCardContracts = await this.smartCardContracts(piccCardData);

                // add ePurse credit to smart card
                if (topupProduct.productType == constants.productType.topUpRelative) {
                    let contractNo = 1;
                    const creditValues = {
                        amount: transaction.farePrice,
                        contractId: transaction.fareItemExternalId,
                        terminalId: global.currentPosData.terminalId,
                        terminalType: enumsXml.deviceType.officePos,
                        merchantId: 1,
                        ptoReference: 0
                    };

                    const creditPiccCardData = await this.smartCardServ.creditEpurse(creditValues);
                    const cardCounters = await this.smartCardServ.cardCounters({
                        transactionCounter: creditPiccCardData.transactionCounter + 1,
                        countersTimestamp: moment.utc().format('X')
                    });
                    const topUpLogs = await this.smartCardServ.topUpLogs({
                        topupLogTransactionTimestamp: moment.utc().format('X'),
                        topupLogContractCategory: enumsXml.smartCardContractCategory[topupProduct.productType],
                        topupLogContractId: creditValues.contractId,
                        topupLogContractPointer: contractNo,
                        topupLogFareItemCategory: enumsXml.fareItemCategoryType.fareEpurse,
                        topupLogFareItemType: enumsXml.fareItemType.creditEpurseSmartCard,
                        topupLogActivityType: enumsXml.activityType.credit,
                        topupLogTerminalId: creditValues.terminalId,
                        topupLogTerminalType: creditValues.terminalType,
                        topupLogTransactionId: creditPiccCardData.transactionCounter + 1,
                        topupLogAmountCredited: creditValues.amount,
                        topupLogMerchantNumber: creditValues.merchantId,
                        topupLogPtoRference: creditValues.ptoReference
                    });
                    const cardPrintedId = await this.smartCardServ.getCardPrintedId(creditPiccCardData.cardUid);
                    const smartCardProfile = smartCardProfiles.find((f) => f.cardTypeShortNumber == creditPiccCardData.cardType && f.cardVersionShortNumber == creditPiccCardData.cardVersion && f.isEnabled);
                    const cardVersion = (smartCardProfile) ? smartCardProfile.cardVersion : cartItem.topupSmartCard.data.version;
                    const cardType = (smartCardProfile) ? smartCardProfile.cardType : constantsXml.smartCardType.standard;
                    const cardTechnology = (smartCardProfile) ? smartCardProfile.cardTechnology : constantsXml.smartCardPhysicalType.mifareDesfireEv3;

                    let topUpRelativeContract = {
                        contractType: constantsXml.smartCardContractType.topUpRelative,
                        productId: creditPiccCardData['contractId'+contractNo],
                        contractId: creditPiccCardData['contractId'+contractNo],
                        instanceId: creditPiccCardData['instanceId'+contractNo],
                        contractStatus: creditPiccCardData['contractStatus'+contractNo],
                        contractStartTimestamp: creditPiccCardData['contractStartTimestamp'+contractNo],
                        contractExpirationTimestamp: creditPiccCardData['contractExpirationTimestamp'+contractNo],
                        contractStateChangeTimestamp: creditPiccCardData['contractStateChangeTimestamp'+contractNo],
                        contractValue: creditPiccCardData['contractValue'+contractNo]
                    };

                    smartCardContracts = await this.smartCardContracts(creditPiccCardData);

                    transaction.activityType = constantsXml.activityType.credit;
                    transaction.fareItemCategory = constantsXml.fareItemCategory.fareEpurse;
                    transaction.fareItemType = constantsXml.fareItemType.creditEpurseSmartCard;

                    await this.posTransactionCtrl.create({
                        function: 'smartCardContract',
                        component: 'smartCardReader',
                        parameters: {
                            avl: avl,
                            fare: fare,
                            financial: {
                                activityType: transaction.activityType,
                                fareItemCategory: transaction.fareItemCategory,
                                fareItemType: transaction.fareItemType,
                                fareItemExternalId: transaction.fareItemExternalId,
                                fareItemProductType: transaction.productType,
                                fareItemIssuedNumber: transaction.fareItemIssuedNumber,
                                fareItemIssuedExpirationTimeStamp: moment.utc(transaction.fareItemIssuedExpirationTimeStamp).format(this.dateTTimeFormat),
                                fareItemPurchasedStagesOrZones: 0,
                                fareItemName: transaction.fareItemName,
                                payingRiderTypeExternalId: transaction.riderExternalId,
                                payingRiderTypeName: transaction.riderName,
                                payingRiderAccountId: transaction.accountNo,
                                cumulativeFareOrProductPrice: transaction.farePrice,
                                totalPaymentInCents: transaction.totalPaymentInCents,
                                totalVatInCents: transaction.totalVat,
                                vatAddedToTotalPayment: vatAddedToTotal,
                                penaltyFee: 0,
                                paymentInstrument: transaction.paymentInstrument,
                                paymentMethod: transaction.paymentMethod,
                                currencyCode: global.currentPosData.currencyCode,
                                paymentInstrumentTransactionId: (transaction.cardTransactionNo) ? transaction.cardTransactionNo : 0,
                                relatedTransactionInformation: {
                                    relatedTransactionTimestamp: moment.utc(transaction.transactionTime).format(this.dateTTimeFormat),
                                    relatedTransactionDeviceType: constantsXml.deviceType.officePos,
                                    relatedTransactionDeviceId: global.currentPosData.deviceId,
                                    relatedTransactionTerminalId: global.currentPosData.terminalId,
                                    relatedTransactionDeviceTransactionCounter: global.currentPosData.transactionCounter
                                },
                                taxInvoiceInformation: {
                                    invoiceIdNumber: ticket.invoiceId.split('-')[2],
                                    invoiceExternalReferenceCode: ticket.invoiceId,
                                    taxInvoiceQrCode: ticket.taxQrCode
                                },
                                vatDetails: {
                                    taxableAmountInCents: transaction.farePrice - transaction.totalVat,
                                    vatRate: transaction.vatRate,
                                    vatInCents: transaction.totalVat
                                }
                            },
                            card: {
                                // card reference
                                cardUid: creditPiccCardData.cardUid,
                                cardPrintedId: cardPrintedId,
                                cardType: cardType,
                                cardTypeShortNumber: creditPiccCardData.cardType,
                                cardVersion: cardVersion,
                                cardVersionShortNumber: creditPiccCardData.cardVersion,
                                cardIteration: creditPiccCardData.cardIteration,
                                physicalCardType: cardTechnology,
                                issuerId: creditPiccCardData.issuerId,
                                transactionCounter: creditPiccCardData.transactionCounter + 1,
                                // card snapshot
                                timestamp: currentTimestamp,
                                currencyCode: creditPiccCardData.currencyCode,
                                preferredLanguage: creditPiccCardData.preferredLanguage,
                                riderTypeExternalId: creditPiccCardData.riderTypeExternalId,
                                storedValue: cartItem.topupSmartCard.data['contractValue'+contractNo],
                                refundableDeposit: creditPiccCardData.refundableDeposit,
                                cardExpirationTimestamp: creditPiccCardData.cardExpirationTimestamp,
                                alic: creditPiccCardData.alic,
                                alwc: creditPiccCardData.alwc,
                                blockStatus: creditPiccCardData.blockStatus,
                                blockTimestamp: creditPiccCardData.blockTimestamp,
                                contracts: smartCardContracts.contracts,
                                // card operation
                                creditEpurse: {
                                    newEpurseValue: cartItem.topupSmartCard.data['contractValue'+contractNo] + transaction.farePrice,
                                    previousEpurseValue: cartItem.topupSmartCard.data['contractValue'+contractNo]
                                }
                            },
                            order: (global.currentPosData.hasSalesOrder) ? {
                                salesOrderIdNumber: ticket.salesOrderIdNumber,
                                salesOrderExternalReferenceCode: ticket.invoiceId
                            } : ''
                        }
                    });
                }

                // add periodPass contract to smart card
                if (topupProduct.productType == constants.productType.periodPass) {
                    let contractNo = smartCardContracts.emptyContract;
                    let smartCardContractTimestamp = await this.smartCardContractTimestamp(products, topupProduct, cartItem.topupSmartCard.data);
                    transaction.fareItemIssuedExpirationTimeStamp = moment.utc(smartCardContractTimestamp.expirationTimestamp, 'X');
                    const periodPassValues = {
                        contractId: transaction.fareItemExternalId,
                        instanceId: smartCardContracts.lastInstance,
                        contractValue: (topupProduct.activateOn == 'FirstUse') ? enumsXml.smartCardContractValue.pending : enumsXml.smartCardContractValue.active,
                        contractCategory: enumsXml.smartCardContractCategory[topupProduct.productType],
                        contractStatus: (topupProduct.activateOn == 'FirstUse') ? enumsXml.smartCardContractStatus.pending : enumsXml.smartCardContractStatus.active,
                        contractStartTimestamp: smartCardContractTimestamp.startTimestamp,
                        contractExpirationTimestamp: smartCardContractTimestamp.expirationTimestamp,
                        contractStateChangeTimestamp: moment.utc().format('X')
                    };

                    const contractPiccCardData = await this.smartCardServ.loadContract(periodPassValues, contractNo);
                    const cardCounters = await this.smartCardServ.cardCounters({
                        transactionCounter: contractPiccCardData.transactionCounter + 1,
                        countersTimestamp: moment.utc().format('X')
                    });
                    const topUpLogs = await this.smartCardServ.topUpLogs({
                        topupLogTransactionTimestamp: moment.utc().format('X'),
                        topupLogContractCategory: enumsXml.smartCardContractCategory[topupProduct.productType],
                        topupLogContractId: transaction.fareItemExternalId,
                        topupLogContractPointer: contractNo,
                        topupLogFareItemCategory: enumsXml.fareItemCategoryType.fareProduct,
                        topupLogFareItemType: enumsXml.fareItemType.productSmartCard,
                        topupLogActivityType: enumsXml.activityType.issue,
                        topupLogTerminalId: global.currentPosData.terminalId,
                        topupLogTerminalType: enumsXml.deviceType.officePos,
                        topupLogTransactionId: contractPiccCardData.transactionCounter + 1,
                        topupLogAmountCredited: 0,
                        topupLogMerchantNumber: 1,
                        topupLogPtoRference: 0
                    });
                    const cardPrintedId = await this.smartCardServ.getCardPrintedId(contractPiccCardData.cardUid);
                    const smartCardProfile = smartCardProfiles.find((f) => f.cardTypeShortNumber == contractPiccCardData.cardType && f.cardVersionShortNumber == contractPiccCardData.cardVersion && f.isEnabled);
                    const cardVersion = (smartCardProfile) ? smartCardProfile.cardVersion : cartItem.topupSmartCard.data.version;
                    const cardType = (smartCardProfile) ? smartCardProfile.cardType : constantsXml.smartCardType.standard;
                    const cardTechnology = (smartCardProfile) ? smartCardProfile.cardTechnology : constantsXml.smartCardPhysicalType.mifareDesfireEv3;

                    smartCardContracts = await this.smartCardContracts(contractPiccCardData);

                    await this.posTransactionCtrl.create({
                        function: 'smartCardContract',
                        component: 'smartCardReader',
                        parameters: {
                            avl: avl,
                            fare: fare,
                            financial: {
                                activityType: transaction.activityType,
                                fareItemCategory: transaction.fareItemCategory,
                                fareItemType: transaction.fareItemType,
                                fareItemExternalId: transaction.fareItemExternalId,
                                fareItemProductType: transaction.productType,
                                fareItemIssuedNumber: transaction.fareItemIssuedNumber,
                                fareItemIssuedExpirationTimeStamp: moment.utc(transaction.fareItemIssuedExpirationTimeStamp).format(this.dateTTimeFormat),
                                fareItemPurchasedStagesOrZones: 0,
                                fareItemName: transaction.fareItemName,
                                payingRiderTypeExternalId: transaction.riderExternalId,
                                payingRiderTypeName: transaction.riderName,
                                payingRiderAccountId: transaction.accountNo,
                                cumulativeFareOrProductPrice: transaction.farePrice,
                                totalPaymentInCents: transaction.totalPaymentInCents,
                                totalVatInCents: transaction.totalVat,
                                vatAddedToTotalPayment: vatAddedToTotal,
                                penaltyFee: 0,
                                paymentInstrument: transaction.paymentInstrument,
                                paymentMethod: transaction.paymentMethod,
                                currencyCode: global.currentPosData.currencyCode,
                                paymentInstrumentTransactionId: (transaction.cardTransactionNo) ? transaction.cardTransactionNo : 0,
                                relatedTransactionInformation: {
                                    relatedTransactionTimestamp: moment.utc(transaction.transactionTime).format(this.dateTTimeFormat),
                                    relatedTransactionDeviceType: constantsXml.deviceType.officePos,
                                    relatedTransactionDeviceId: global.currentPosData.deviceId,
                                    relatedTransactionTerminalId: global.currentPosData.terminalId,
                                    relatedTransactionDeviceTransactionCounter: global.currentPosData.transactionCounter
                                },
                                reasonInformation: (ticket.reason) ? {
                                    reasonId: ticket.reason.id,
                                    reasonName: ticket.reason.name
                                } : '',
                                taxInvoiceInformation: {
                                    invoiceIdNumber: ticket.invoiceId.split('-')[2],
                                    invoiceExternalReferenceCode: ticket.invoiceId,
                                    taxInvoiceQrCode: ticket.taxQrCode
                                },
                                vatDetails: {
                                    taxableAmountInCents: transaction.farePrice - transaction.totalVat,
                                    vatRate: transaction.vatRate,
                                    vatInCents: transaction.totalVat
                                }
                            },
                            card: {
                                // card reference
                                cardUid: contractPiccCardData.cardUid,
                                cardPrintedId: cardPrintedId,
                                cardType: cardType,
                                cardTypeShortNumber: contractPiccCardData.cardType,
                                cardVersion: cardVersion,
                                cardVersionShortNumber: contractPiccCardData.cardVersion,
                                cardIteration: contractPiccCardData.cardIteration,
                                physicalCardType: cardTechnology,
                                issuerId: contractPiccCardData.issuerId,
                                transactionCounter: contractPiccCardData.transactionCounter + 1,
                                // card snapshot
                                timestamp: currentTimestamp,
                                currencyCode: contractPiccCardData.currencyCode,
                                preferredLanguage: contractPiccCardData.preferredLanguage,
                                riderTypeExternalId: contractPiccCardData.riderTypeExternalId,
                                storedValue: cartItem.topupSmartCard.data['contractValue1'],
                                refundableDeposit: contractPiccCardData.refundableDeposit,
                                cardExpirationTimestamp: contractPiccCardData.cardExpirationTimestamp,
                                alic: contractPiccCardData.alic,
                                alwc: contractPiccCardData.alwc,
                                blockStatus: contractPiccCardData.blockStatus,
                                blockTimestamp: contractPiccCardData.blockTimestamp,
                                contracts: smartCardContracts.contracts,
                                // card operation
                                addContract: {
                                    contractType: constantsXml.contractType.periodPass,
                                    productId: contractPiccCardData['contractId'+contractNo],
                                    contractId: contractPiccCardData['contractId'+contractNo],
                                    instanceId: contractPiccCardData['instanceId'+contractNo],
                                    contractStatus: contractPiccCardData['contractStatus'+contractNo],
                                    contractStartTimestamp: contractPiccCardData['contractStartTimestamp'+contractNo],
                                    contractExpirationTimestamp: contractPiccCardData['contractExpirationTimestamp'+contractNo],
                                    contractStateChangeTimestamp: contractPiccCardData['contractStateChangeTimestamp'+contractNo],
                                    contractValue: contractPiccCardData['contractValue'+contractNo]
                                }
                            },
                            order: (global.currentPosData.hasSalesOrder) ? {
                                salesOrderIdNumber: ticket.salesOrderIdNumber,
                                salesOrderExternalReferenceCode: ticket.invoiceId
                            } : ''
                        }
                    });
                }

                // add storedRides contract to smart card
                if (topupProduct.productType == constants.productType.storedRides) {
                    let contractNo = smartCardContracts.emptyContract;
                    let smartCardContractTimestamp = await this.smartCardContractTimestamp(products, topupProduct, cartItem.topupSmartCard.data);
                    transaction.fareItemIssuedExpirationTimeStamp = moment.utc(smartCardContractTimestamp.expirationTimestamp, 'X');
                    const storedRidesValues = {
                        contractId: transaction.fareItemExternalId,
                        instanceId: smartCardContracts.lastInstance,
                        contractValue: topupProduct.numberOfRides,
                        contractCategory: enumsXml.smartCardContractCategory[topupProduct.productType],
                        contractStatus: (topupProduct.activateOn == 'FirstUse') ? enumsXml.smartCardContractStatus.pending : enumsXml.smartCardContractStatus.active,
                        contractStartTimestamp: smartCardContractTimestamp.startTimestamp,
                        contractExpirationTimestamp: smartCardContractTimestamp.expirationTimestamp,
                        contractStateChangeTimestamp: moment.utc().format('X')
                    };

                    const contractPiccCardData = await this.smartCardServ.loadContract(storedRidesValues, contractNo);
                    const cardCounters = await this.smartCardServ.cardCounters({
                        transactionCounter: contractPiccCardData.transactionCounter + 1,
                        countersTimestamp: moment.utc().format('X')
                    });
                    const topUpLogs = await this.smartCardServ.topUpLogs({
                        topupLogTransactionTimestamp: moment.utc().format('X'),
                        topupLogContractCategory: enumsXml.smartCardContractCategory[topupProduct.productType],
                        topupLogContractId: transaction.fareItemExternalId,
                        topupLogContractPointer: contractNo,
                        topupLogFareItemCategory: enumsXml.fareItemCategoryType.fareProduct,
                        topupLogFareItemType: enumsXml.fareItemType.productSmartCard,
                        topupLogActivityType: enumsXml.activityType.issue,
                        topupLogTerminalId: global.currentPosData.terminalId,
                        topupLogTerminalType: enumsXml.deviceType.officePos,
                        topupLogTransactionId: contractPiccCardData.transactionCounter + 1,
                        topupLogAmountCredited: 0,
                        topupLogMerchantNumber: 1,
                        topupLogPtoRference: 0
                    });
                    const cardPrintedId = await this.smartCardServ.getCardPrintedId(contractPiccCardData.cardUid);
                    const smartCardProfile = smartCardProfiles.find((f) => f.cardTypeShortNumber == contractPiccCardData.cardType && f.cardVersionShortNumber == contractPiccCardData.cardVersion && f.isEnabled);
                    const cardVersion = (smartCardProfile) ? smartCardProfile.cardVersion : cartItem.topupSmartCard.data.version;
                    const cardType = (smartCardProfile) ? smartCardProfile.cardType : constantsXml.smartCardType.standard;
                    const cardTechnology = (smartCardProfile) ? smartCardProfile.cardTechnology : constantsXml.smartCardPhysicalType.mifareDesfireEv3;

                    smartCardContracts = await this.smartCardContracts(contractPiccCardData);

                    await this.posTransactionCtrl.create({
                        function: 'smartCardContract',
                        component: 'smartCardReader',
                        parameters: {
                            avl: avl,
                            fare: fare,
                            financial: {
                                activityType: transaction.activityType,
                                fareItemCategory: transaction.fareItemCategory,
                                fareItemType: transaction.fareItemType,
                                fareItemExternalId: transaction.fareItemExternalId,
                                fareItemProductType: transaction.productType,
                                fareItemIssuedNumber: transaction.fareItemIssuedNumber,
                                fareItemIssuedExpirationTimeStamp: moment.utc(transaction.fareItemIssuedExpirationTimeStamp).format(this.dateTTimeFormat),
                                fareItemPurchasedStagesOrZones: 0,
                                fareItemName: transaction.fareItemName,
                                payingRiderTypeExternalId: transaction.riderExternalId,
                                payingRiderTypeName: transaction.riderName,
                                payingRiderAccountId: transaction.accountNo,
                                cumulativeFareOrProductPrice: transaction.farePrice,
                                totalPaymentInCents: transaction.totalPaymentInCents,
                                totalVatInCents: transaction.totalVat,
                                vatAddedToTotalPayment: vatAddedToTotal,
                                penaltyFee: 0,
                                paymentInstrument: transaction.paymentInstrument,
                                paymentMethod: transaction.paymentMethod,
                                currencyCode: global.currentPosData.currencyCode,
                                paymentInstrumentTransactionId: (transaction.cardTransactionNo) ? transaction.cardTransactionNo : 0,
                                relatedTransactionInformation: {
                                    relatedTransactionTimestamp: moment.utc(transaction.transactionTime).format(this.dateTTimeFormat),
                                    relatedTransactionDeviceType: constantsXml.deviceType.officePos,
                                    relatedTransactionDeviceId: global.currentPosData.deviceId,
                                    relatedTransactionTerminalId: global.currentPosData.terminalId,
                                    relatedTransactionDeviceTransactionCounter: global.currentPosData.transactionCounter
                                },
                                reasonInformation: (ticket.reason) ? {
                                    reasonId: ticket.reason.id,
                                    reasonName: ticket.reason.name
                                } : '',
                                taxInvoiceInformation: {
                                    invoiceIdNumber: ticket.invoiceId.split('-')[2],
                                    invoiceExternalReferenceCode: ticket.invoiceId,
                                    taxInvoiceQrCode: ticket.taxQrCode
                                },
                                vatDetails: {
                                    taxableAmountInCents: transaction.farePrice - transaction.totalVat,
                                    vatRate: transaction.vatRate,
                                    vatInCents: transaction.totalVat
                                }
                            },
                            card: {
                                // card reference
                                cardUid: contractPiccCardData.cardUid,
                                cardPrintedId: cardPrintedId,
                                cardType: cardType,
                                cardTypeShortNumber: contractPiccCardData.cardType,
                                cardVersion: cardVersion,
                                cardVersionShortNumber: contractPiccCardData.cardVersion,
                                cardIteration: contractPiccCardData.cardIteration,
                                physicalCardType: cardTechnology,
                                issuerId: contractPiccCardData.issuerId,
                                transactionCounter: contractPiccCardData.transactionCounter + 1,
                                // card snapshot
                                timestamp: currentTimestamp,
                                currencyCode: contractPiccCardData.currencyCode,
                                preferredLanguage: contractPiccCardData.preferredLanguage,
                                riderTypeExternalId: contractPiccCardData.riderTypeExternalId,
                                storedValue: cartItem.topupSmartCard.data['contractValue1'],
                                refundableDeposit: contractPiccCardData.refundableDeposit,
                                cardExpirationTimestamp: contractPiccCardData.cardExpirationTimestamp,
                                alic: contractPiccCardData.alic,
                                alwc: contractPiccCardData.alwc,
                                blockStatus: contractPiccCardData.blockStatus,
                                blockTimestamp: contractPiccCardData.blockTimestamp,
                                contracts: smartCardContracts.contracts,
                                // card operation
                                addContract: {
                                    contractType: constantsXml.contractType.storedRides,
                                    productId: contractPiccCardData['contractId'+contractNo],
                                    contractId: contractPiccCardData['contractId'+contractNo],
                                    instanceId: contractPiccCardData['instanceId'+contractNo],
                                    contractStatus: contractPiccCardData['contractStatus'+contractNo],
                                    contractStartTimestamp: contractPiccCardData['contractStartTimestamp'+contractNo],
                                    contractExpirationTimestamp: contractPiccCardData['contractExpirationTimestamp'+contractNo],
                                    contractStateChangeTimestamp: contractPiccCardData['contractStateChangeTimestamp'+contractNo],
                                    contractValue: contractPiccCardData['contractValue'+contractNo]
                                }
                            },
                            order: (global.currentPosData.hasSalesOrder) ? {
                                salesOrderIdNumber: ticket.salesOrderIdNumber,
                                salesOrderExternalReferenceCode: ticket.invoiceId
                            } : ''
                        }
                    });
                }

                await this.modelFinancial.create(transaction);

                return res.status(200).json({
                    ticketId: transaction.fareItemIssuedNumber,
                    ticketValid: moment.utc(transaction.fareItemIssuedExpirationTimeStamp).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat),
                    qrCodes: [qrCode],
                    issueTime: moment.utc(ticket.issueTime).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat),
                    shouldPrint: shouldNotPrint
                });
            }

            const origStation = await this.fareEnvironmentModel.Stations.findOne({fareEnvironmentConfigurationId: activeConfig._id, stationCode: ticketItem.origStationCode});
            const destStation = await this.fareEnvironmentModel.Stations.findOne({fareEnvironmentConfigurationId: activeConfig._id, stationCode: ticketItem.destStationCode});
            const origStop = await this.fareEnvironmentModel.Stops.findOne({fareEnvironmentConfigurationId: activeConfig._id, stopCode: ticketItem.origStopCode});
            const destStop = await this.fareEnvironmentModel.Stops.findOne({fareEnvironmentConfigurationId: activeConfig._id, stopCode: ticketItem.destStopCode});

            const vatRates = await this.fareTableModel.VatRates.find({fareEnvironmentConfigurationId: activeConfig._id});
            const vatRate = (vatRates.find((f) => [transaction.productType].includes(f.associatedProductTypes))) ? vatRates.find((f) => [transaction.productType].includes(f.associatedProductTypes)) : vatRates.find((f) => f.isDefault == true);
            const vatAddedToTotal = true;
            const vatRateInOneHundredths = (vatRate) ? vatRate.percents : 0;
            ticketItem.vatRate = vatRateInOneHundredths;
            transaction.vatRate = vatRateInOneHundredths;
            transaction.totalVat = Math.round(transaction.farePrice * (vatRateInOneHundredths / 10000) / (1 + (vatRateInOneHundredths / 10000)));

            if (global.currentPosData.hasReservation) {
                avl.tripStartTimestamp = moment.utc(ticketItem.reservationBusStationFromTime).format(this.dateTTimeFormat);
                avl.tripEstimatedEndTimestamp = moment.utc(ticketItem.reservationBusStationToTime).format(this.dateTTimeFormat);

                //if (ticketItem.reservationDiscount.staticDiscountCode == null && ticketItem.reservationDiscount.dynamicDiscountCode != null) {
                    passengerCorrectionFareDetails = {
                        calculationSequenceCounter: 0,
                        fareAdjustmentId: ticketItem.reservationDiscount.dynamicDiscountCode,
                        fareAdjustmentType: 'compoundMultiTier',
                        fareAdjustmentName: ticketItem.reservationDiscount.dynamicDiscountType,
                        [(ticketItem.reservationDiscount.dynamicDiscountPercentage > 0) ? 'percentageCorrectionValue' : 'fixedAmountCorrectionValue']: (ticketItem.reservationDiscount.dynamicDiscountPercentage > 0) ? ticketItem.reservationDiscount.dynamicDiscountPercentage * 100 : ticketItem.reservationDiscount.dynamicDiscountInCents,
                        correctionFare: ticketItem.reservationDiscount.dynamicDiscountInCents,
                        inputBaseFare: ticketItem.reservationDiscount.basePriceInCents,
                        correctedBaseFare: ticketItem.reservationDiscount.discountedPriceInCents
                    };
                //}

                if (ticketItem.reservationBaggage != null && ticketItem.reservationBaggage > 0) {
                    customParameters = [
                        {code: 'luggagePieces', value: ticketItem.reservationBaggage}
                    ];
                }
            }

            if (['hafil'].includes(process.env.CLIENT_NAME)) {
                transaction.fareItemIssuedExpirationTimeStamp = moment.utc(ticketItem.reservationBusStationFromTime).add(4, 'hours');
            }

            const ticketItemProd = await this.modelTicket.findOneAndUpdate({ _id: ticket.id }, {
                $push: {
                    items: ticketItem
                }
            }, {new: true});

            transaction.ticketItemId = ticketItemProd.items[ticketItemProd.items.length - 1].id;

            let createdFile = await this.posTransactionCtrl.create({
                function: 'ticket',
                component: 'ticketPrinter',
                parameters: {
                    avl: avl,
                    fare: fare,
                    financial: {
                        activityType: transaction.activityType,
                        fareItemCategory: transaction.fareItemCategory,
                        fareItemType: transaction.fareItemType,
                        fareItemExternalId: transaction.fareItemExternalId,
                        fareItemProductType: transaction.productType,
                        fareItemIssuedNumber: transaction.fareItemIssuedNumber,
                        fareItemIssuedExpirationTimeStamp: moment.utc(transaction.fareItemIssuedExpirationTimeStamp).format(this.dateTTimeFormat),
                        fareItemPurchasedStagesOrZones: (ticketItem.zoneTraveled) ? ticketItem.zoneTraveled : 0,
                        fareItemName: transaction.fareItemName,
                        payingRiderTypeExternalId: transaction.riderExternalId,
                        payingRiderTypeName: transaction.riderName,
                        payingRiderAccountId: transaction.accountNo,
                        cumulativeFareOrProductPrice: transaction.farePrice,
                        totalPaymentInCents: transaction.totalPaymentInCents,
                        totalVatInCents: transaction.totalVat,
                        vatAddedToTotalPayment: vatAddedToTotal,
                        penaltyFee: 0,
                        paymentInstrument: transaction.paymentInstrument,
                        paymentMethod: transaction.paymentMethod,
                        currencyCode: global.currentPosData.currencyCode,
                        paymentInstrumentTransactionId: (transaction.cardTransactionNo) ? transaction.cardTransactionNo : 0,
                        passengerDetails: {
                            passengerRiderTypeExternalId: transaction.riderExternalId,
                            passengerRiderTypeName: transaction.riderName,
                            noOfPassengers: transaction.numberOfPassengers,
                            passengerBaseFare: (passengerBaseFare) ? passengerBaseFare : transaction.farePrice / transaction.numberOfPassengers,
                            passengerCorrectionFareDetails: passengerCorrectionFareDetails
                        },
                        relatedTransactionInformation: {
                            relatedTransactionTimestamp: moment.utc(transaction.transactionTime).format(this.dateTTimeFormat),
                            relatedTransactionDeviceType: constantsXml.deviceType.officePos,
                            relatedTransactionDeviceId: global.currentPosData.deviceId,
                            relatedTransactionTerminalId: global.currentPosData.terminalId,
                            relatedTransactionDeviceTransactionCounter: global.currentPosData.transactionCounter
                        },
                        boardingInformation: (ticketItem.origStopCode || ticketItem.origStationCode || ticketItem.reservationBusStationFromTime) ? {
                            boardingActivity: constantsXml.boardingActivityType.issuing,
                            issuedBoardingStopCode: ticketItem.origStopCode,
                            issuedAligthingStopCode: ticketItem.destStopCode,
                            issuedBoardingStationCode: ticketItem.origStationCode,
                            issuedAligthingStationCode: ticketItem.destStationCode,
                            actualBoardingStopCode: ticketItem.origStopCode,
                            actualAligthingStopCode: ticketItem.destStopCode,
                            actualBoardingStationCode: ticketItem.origStationCode,
                            actualAligthingStationCode: ticketItem.destStationCode,
                            boardingTimestamp: (ticketItem.reservationBusStationFromTime) ? ticketItem.reservationBusStationFromTime : '',
                            alightingTimestamp: (ticketItem.reservationBusStationToTime) ? ticketItem.reservationBusStationToTime : '',
                            issuedBoardingZoneCode: '',
                            issuedAligthingZoneCode: '',
                            tripDistanceTraveled: 0,
                            tripStagesOrZonesTraveled: 0
                        } : '',
                        taxInvoiceInformation: {
                            invoiceIdNumber: ticket.invoiceId.split('-')[2],
                            invoiceExternalReferenceCode: ticket.invoiceId,
                            taxInvoiceQrCode: ticket.taxQrCode
                        },
                        vatDetails: {
                            taxableAmountInCents: transaction.farePrice - transaction.totalVat,
                            vatRate: transaction.vatRate,
                            vatInCents: transaction.totalVat
                        },
                        bookingDiscount: (global.currentPosData.hasReservation) ? ticketItem.reservationDiscount : ''
                    },
                    ticket: {
                        ticketIssueTimestamp: moment.utc(transaction.transactionTime).format(this.dateTTimeFormat),
                        ticketSerialNumber: transaction.fareItemIssuedNumber,
                        fareItemType: transaction.fareItemType,
                        issuingDeviceId: global.currentPosData.deviceId,
                        issuingDeviceType: constantsXml.deviceType.officePos,
                        ticketExpiryTimestamp: moment.utc(transaction.fareItemIssuedExpirationTimeStamp).format(this.dateTTimeFormat),
                        ticketType: (transaction.numberOfPassengers > 1) ? constantsXml.transactionTicketType.multiplePassengers : constantsXml.transactionTicketType.singlePassenger,
                        passengerRiderTypeExternalIdFirst: transaction.riderExternalId,
                        noOfPassengersFirst: transaction.numberOfPassengers,
                        fareItemExternalId: transaction.fareItemExternalId,
                        fareItemIssuedNumber: transaction.fareItemIssuedNumber,
                        issuedBoardingStopCode: ticketItem.origStopCode,
                        issuedAlightingStopCode: ticketItem.destStopCode,
                        issuedBoardingStationCode: ticketItem.origStationCode,
		                issuedAlightingStationCode: ticketItem.destStationCode,
                        allowedStagesOrZonesNumber: (ticketItem.zoneTraveled) ? ticketItem.zoneTraveled : 0,
                        routeTypeOrServiceTypeExternalId: fare.externalId,
                        routeTypeOrServiceTypeName: fare.name,
                        issuedFormatType: transaction.issuedFormatType
                    },
                    booking: (global.currentPosData.hasReservation) ? {
                        bookingReference: ticketItem.reservationCartId,
                        reservationSeat: ticketItem.reservationPassengerSeat,
                        eTicketReference: ticketItem.reservationPassengerId
                    } : '',
                    order: (global.currentPosData.hasSalesOrder) ? {
                        salesOrderIdNumber: ticket.salesOrderIdNumber,
                        salesOrderExternalReferenceCode: ticket.invoiceId
                    } : '',
                    custom: (customParameters) ? {
                        parameters: customParameters
                    } : ''
                }
            });

            await this.modelFinancial.create(transaction);

            if ((!cartItem.accountId) && (cartItem.cityTicket || cartItem.productBaggage || cartItem.productBaggageReturn || cartItem.intercityTicket || cartItem.productPeriod)) {
                let fareItemType = 0;
                let fareItemCategory = 0;
                let statusTransfersLeft = 0;
                let statusRemainingRides = 1;

                if (cartItem.cityTicket != null) {
                    fareItemType = enumsXml.fareItemType.singleJourneyTicket;
                    fareItemCategory = enumsXml.fareItemCategoryType.fareJourney;
                } else if (cartItem.intercityTicket != null) {
                    if (cartItem.intercityTicket.isReturn) {
                        fareItemType = enumsXml.fareItemType.returnSingleRideTicket;
                        fareItemCategory = enumsXml.fareItemCategoryType.fareReturnSingleRide;
                        statusRemainingRides = 2;
                    } else {
                        fareItemType = enumsXml.fareItemType.singleRideTicket;
                        fareItemCategory = enumsXml.fareItemCategoryType.fareSingleRide;
                        statusRemainingRides = 1;
                    }
                } else if (cartItem.productPeriod != null) {
                    fareItemType = enumsXml.fareItemType.productTicket;
                    fareItemCategory = enumsXml.fareItemCategoryType.fareProduct;
                    statusTransfersLeft = 255;
                } else if (cartItem.productBaggage != null) {
                    fareItemType = enumsXml.fareItemType.productTicket;
                    fareItemCategory = enumsXml.fareItemCategoryType.fareProduct;
                } else if (cartItem.productBaggageReturn != null) {
                    fareItemType = enumsXml.fareItemType.productTicket;
                    fareItemCategory = enumsXml.fareItemCategoryType.fareProduct;
                }

                const issuingTimestamp = moment.utc(transaction.transactionTime).format(this.dateTTimeFormat);
                const expirationTimestamp = moment.utc(transaction.fareItemIssuedExpirationTimeStamp).format(this.dateTTimeFormat);
                const boardingTimestamp = (ticketItem.reservationBusStationFromTime) ? moment.utc(ticketItem.reservationBusStationFromTime).format(this.dateTTimeFormat) : '';
                const alightingTimestamp = (ticketItem.reservationBusStationToTime) ? moment.utc(ticketItem.reservationBusStationToTime).format(this.dateTTimeFormat) : '';

                if (['hafil'].includes(process.env.CLIENT_NAME)) {
                    ticketItem.tripId = await this.service.qrCodeTripIdToCrc16ccitt(ticketItem.tripId);
                }

                if (cartItem.intercityTicket != null) {
                    if (cartItem.intercityTicket.isReturn) {
                        let qrCodeTwoway = {
                            qrCodeIssuingTimestamp: issuingTimestamp,
                            qrCodeExpirationTimestamp: expirationTimestamp,
                            qrCodeIssuingTerminalId: (global.currentPosData.terminalId) ? global.currentPosData.terminalId : 0,
                            qrCodeMerchantOrganizationId: 0,
                            fareItemExternalId: transaction.fareItemExternalId,
                            fareItemTypeShortNumber: fareItemType,
                            fareItemCategoryShortNumber: fareItemCategory,
                            fareItemIssuedNumber: transaction.fareItemIssuedNumber,
                            fareItemIssuedStatus: enumsXml.fareItemStatus.active,
                            issuedBoardingStation: (origStation) ? origStation.machineId : 0,
                            issuedAlightingStation: (destStation) ? destStation.machineId : 0,
                            issuedBoardingStop: (origStop) ? origStop.machineId : 0,
                            issuedAlightingStop: (destStop) ? destStop.machineId : 0,
                            issuedRouteType: (transitType) ? transitType.shortNumber : 0,
                            contractCategoryShortNumber: 0,
                            issuedRouteTypeTransferEligibility: 0,
                            fareProductTypeShortNumber: enumsXml.smartCardContractCategory.Undefined,
                            fareItemStatus: enumsXml.fareItemStatus.active,
                            fareItemStatusZonesLeft: (ticketItem.zoneTraveled) ? ticketItem.zoneTraveled : 0,
                            fareItemStatusLastChangedTimestamp: issuingTimestamp,
                            fareItemStatusTransfersLeft: statusTransfersLeft,
                            fareItemStatusRemainingRides: statusRemainingRides,
                            fareItemExpirationTimestamp: expirationTimestamp,
                            passengerRiderTypeExternalIdFirst: ticketItem.riderExternalId,
                            noOfPassengersFirst: transaction.numberOfPassengers,
                            passengerRiderTypeExternalIdSecond: 0,
                            noOfPassengersSecond: 0,
                            passengerRiderTypeExternalIdThird: 0,
                            noOfPassengersThird: 0,
                            passengerRiderTypeExternalIdFourth: 0,
                            noOfPassengersFourth: 0,
                            accountRiderType: transaction.riderExternalId,
                            accountId: (transaction.accountNo) ? transaction.accountNo : 0,
                            ePurseBalanceInCents: 0,
                            dailyCumulativeSpendingInCents: 0,
                            weeklyCumulativeSpendingInCents: 0,
                            monthlyCumulativeSpendingInCents: 0,
                            issuedTripId: (ticketItem.tripId) ? ticketItem.tripId : 0,
                            issuedRoute: (ticketItem.routeIntercityId) ? ticketItem.routeIntercityId : 0,
                            issuedTripStartTimestamp: boardingTimestamp,
                            issuedTripEndEstimatedTimestamp: alightingTimestamp
                        };

                        qrCodeReturn = await this.service.generateQrCode(qrCodeTwoway);
                    }
                }
                let qrCodeOneway = {
                    qrCodeIssuingTimestamp: issuingTimestamp,
                    qrCodeExpirationTimestamp: expirationTimestamp,
                    qrCodeIssuingTerminalId: (global.currentPosData.terminalId) ? global.currentPosData.terminalId : 0,
                    qrCodeMerchantOrganizationId: 0,
                    fareItemExternalId: transaction.fareItemExternalId,
                    fareItemTypeShortNumber: fareItemType,
                    fareItemCategoryShortNumber: fareItemCategory,
                    fareItemIssuedNumber: transaction.fareItemIssuedNumber,
                    fareItemIssuedStatus: enumsXml.fareItemStatus.active,
                    issuedBoardingStation: (origStation) ? origStation.machineId : 0,
                    issuedAlightingStation: (destStation) ? destStation.machineId : 0,
                    issuedBoardingStop: (origStop) ? origStop.machineId : 0,
                    issuedAlightingStop: (destStop) ? destStop.machineId : 0,
                    issuedRouteType: (transitType) ? transitType.shortNumber : 0,
                    contractCategoryShortNumber: 0,
                    issuedRouteTypeTransferEligibility: 0,
                    fareProductTypeShortNumber: enumsXml.smartCardContractCategory.Undefined,
                    fareItemStatus: enumsXml.fareItemStatus.active,
                    fareItemStatusZonesLeft: (ticketItem.zoneTraveled) ? ticketItem.zoneTraveled : 0,
                    fareItemStatusLastChangedTimestamp: issuingTimestamp,
                    fareItemStatusTransfersLeft: statusTransfersLeft,
                    fareItemStatusRemainingRides: statusRemainingRides,
                    fareItemExpirationTimestamp: expirationTimestamp,
                    passengerRiderTypeExternalIdFirst: ticketItem.riderExternalId,
                    noOfPassengersFirst: transaction.numberOfPassengers,
                    passengerRiderTypeExternalIdSecond: 0,
                    noOfPassengersSecond: 0,
                    passengerRiderTypeExternalIdThird: 0,
                    noOfPassengersThird: 0,
                    passengerRiderTypeExternalIdFourth: 0,
                    noOfPassengersFourth: 0,
                    accountRiderType: transaction.riderExternalId,
                    accountId: (transaction.accountNo) ? transaction.accountNo : 0,
                    ePurseBalanceInCents: 0,
                    dailyCumulativeSpendingInCents: 0,
                    weeklyCumulativeSpendingInCents: 0,
                    monthlyCumulativeSpendingInCents: 0,
                    issuedTripId: (ticketItem.tripId) ? ticketItem.tripId : 0,
                    issuedRoute: (ticketItem.routeIntercityId) ? ticketItem.routeIntercityId : 0,
                    issuedTripStartTimestamp: boardingTimestamp,
                    issuedTripEndEstimatedTimestamp: alightingTimestamp
                };

                qrCode = await this.service.generateQrCode(qrCodeOneway);
            }

            checkoutModel.ticketId = transaction.fareItemIssuedNumber;
            checkoutModel.ticketValid = moment.utc(transaction.fareItemIssuedExpirationTimeStamp).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat);
            checkoutModel.qrCodes = (cartItem.intercityTicket != null && cartItem.intercityTicket.isReturn) ? [qrCode, qrCodeReturn] : [qrCode];
            checkoutModel.issueTime = moment.utc(ticket.issueTime).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat);
            checkoutModel.shouldPrint = shouldPrint;

            const ticketItemProdQr = await this.modelTicket.findOneAndUpdate({ _id: transaction.ticketId, 'items._id': transaction.ticketItemId }, {
                'items.$.qrCode': qrCode
            });

            res.status(200).json(checkoutModel);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    getVatDetails = async (cart) => {
        let vatRates = {};
        const ticketItemsLen = (cart.items).length;
        for (let i=0; i<ticketItemsLen; i++) {
            let item = cart.items[i];

            if (item.cityTicket != null) {
                vatRates = await this.addVatRates(vatRates, item.cityTicket.vatRate, item.cityTicket.priceCent, item.quantity, item.numberOfPassengers);
            } else if (item.productBaggage != null) {
                vatRates = await this.addVatRates(vatRates, item.productBaggage.vatRate, item.productBaggage.priceCent, item.quantity, item.numberOfPassengers);
            } else if (item.productBaggageReturn != null) {
                vatRates = await this.addVatRates(vatRates, item.productBaggageReturn.vatRate, item.productBaggageReturn.priceCent, item.quantity, item.numberOfPassengers);
            } else if (item.intercityTicket != null) {
                vatRates = await this.addVatRates(vatRates, item.intercityTicket.vatRate, item.intercityTicket.priceCent, item.quantity, item.numberOfPassengers);
            } else if (item.penalty != null) {
                vatRates = await this.addVatRates(vatRates, item.penalty.vatRate, item.penalty.priceCent, item.quantity, item.numberOfPassengers);
            } else if (item.productPeriod != null) {
                vatRates = await this.addVatRates(vatRates, item.productPeriod.vatRate, item.productPeriod.priceCent, item.quantity, item.numberOfPassengers);
            } else if (item.topupAccount != null) {
                vatRates = await this.addVatRates(vatRates, item.topupAccount.vatRate, item.topupAccount.priceCent, item.quantity, item.numberOfPassengers);
            } else if (item.smartCardIssue != null) {
                vatRates = await this.addVatRates(vatRates, item.smartCardIssue.vatRate, item.smartCardIssue.priceCent, item.quantity, item.numberOfPassengers);
            } else if (item.topupSmartCard != null) {
                vatRates = await this.addVatRates(vatRates, item.topupSmartCard.vatRate, item.topupSmartCard.priceCent, item.quantity, item.numberOfPassengers);
            }
        }

        let vatDetails = [];
        for (let i = 0; i < Object.keys(vatRates).length; i++) {
            let vatRate = Object.keys(vatRates)[i];
            let priceCent = vatRates[vatRate];
            let totalVat = Math.round(priceCent * (vatRate / Math.pow(10, 4)) / (1 + (vatRate / Math.pow(10, 4))));

            vatDetails.push({
                taxableAmountInCents: priceCent - totalVat,
                vatRate: vatRate,
                vatInCents: totalVat
            });
        }

        return vatDetails;
    }

    addVatRates = async (vatRates, vatRate, priceCent, quantity, numberOfPassengers) => {
        if (!vatRates[vatRate]) {
            vatRates[vatRate] = priceCent * quantity * numberOfPassengers;
        } else {
            vatRates[vatRate] += priceCent * quantity * numberOfPassengers;
        }

        return vatRates;
    }

    passengerCorrectionFareDetails = (cartItem, fareAdjustment, farePrice, priceRoundingType) => {
        let passengerCorrectionFareDetails = {
            calculationSequenceCounter: fareAdjustment.externalId,
            fareAdjustmentId: fareAdjustment.externalId,
            fareAdjustmentType: fareAdjustment.adjustmentType,
            fareAdjustmentName: fareAdjustment.name
        };

        let input = farePrice;
        let price = farePrice;
        switch (fareAdjustment.actionType) {
            case 'FixedAmountCorrection':
                let fixedValue = parseInt(fareAdjustment.valueInCents);
                price = input + fixedValue;
                passengerCorrectionFareDetails.fixedAmountCorrectionValue = fixedValue;
                passengerCorrectionFareDetails.correctionFare = this.roundPriceByRoundingType(fixedValue, priceRoundingType);
            break;
            case 'PercentageCorrection':
                let percentageValue = parseInt(fareAdjustment.percentageAmountInOneHundredths);
                let percentageResult = input * (percentageValue / 10000);
                price = input + percentageResult;
                passengerCorrectionFareDetails.percentageCorrectionValue = percentageValue;
                passengerCorrectionFareDetails.correctionFare = this.roundPriceByRoundingType(percentageResult, priceRoundingType);
            break;
        }

        if (cartItem.intercityTicket != null) {

            passengerCorrectionFareDetails.inputBaseFare = this.roundPriceByRoundingType(input, priceRoundingType);
            passengerCorrectionFareDetails.correctedBaseFare = this.roundPriceByRoundingType(price, priceRoundingType);

        } else if (cartItem.productPeriod != null) {

            passengerCorrectionFareDetails.inputProductPrice = this.roundPriceByRoundingType(input, priceRoundingType);
            passengerCorrectionFareDetails.correctedProductPrice = this.roundPriceByRoundingType(price, priceRoundingType);

        }

        return passengerCorrectionFareDetails;
    }

    smartCardContracts = async (smartCardData) => {
        const contractStatuses = Object.keys(smartCardData).filter((f) => f.startsWith('contractStatus')).reduce((o, k) => { o[k] = smartCardData[k]; return o; }, {});
        const contractStatus = [constantsXml.smartCardContractStatus[enumsXml.smartCardContractStatus.pending], constantsXml.smartCardContractStatus[enumsXml.smartCardContractStatus.active]];
        const hasContracts = contractStatus.some((s) => Object.values(contractStatuses).includes(s));
        let contracts = [];

        if (hasContracts) {
            for (let i=1; i<=5; i++) {
                if (contractStatus.includes(smartCardData['contractStatus'+i])) {
                    contracts.push({
                        contractId: smartCardData['contractId'+i],
                        instanceId: smartCardData['instanceId'+i],
                        contractValue: smartCardData['contractValue'+i],
                        contractCategory: smartCardData['contractCategory'+i],
                        contractStatus: smartCardData['contractStatus'+i],
                        contractStartTimestamp: smartCardData['contractStartTimestamp'+i],
                        contractExpirationTimestamp: smartCardData['contractExpirationTimestamp'+i],
                        contractStateChangeTimestamp: smartCardData['contractStateChangeTimestamp'+i]
                    });
                }
            }
        }

        const emptyContractSlot = Object.keys(contractStatuses).find((k) => k != 'contractStatus1' && contractStatuses[k] === 'invalid');
        let contractNo = (emptyContractSlot) ? parseInt(emptyContractSlot.slice(-1)) : null;

        let contractIns = Buffer.alloc(5);
        contractIns.writeUint8(parseInt(enumsXml.smartCardContractSales.regular), 0);
        contractIns.writeInt32LE(parseInt(smartCardData.transactionCounter + 1), 1);
        let contractIn = contractIns.toString('hex').toUpperCase();

        return {contracts: contracts, emptyContract: contractNo, lastInstance: contractIn};
    }

    smartCardContractTimestamp = async (products, topupProduct, smartCardData) => {
        const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
        let businessDayStart = (activeConfig.businessDayStart) ? moment(activeConfig.businessDayStart, this.timeFormat).subtract(global.currentPosData.utcOffset).hour() : moment(global.currentPosData.endOfWorkHour, this.timeFormat).subtract(global.currentPosData.utcOffset).hour();

        const contractStatuses = Object.keys(smartCardData).filter((f) => f.startsWith('contractStatus')).reduce((o, k) => { o[k] = smartCardData[k]; return o; }, {});
        const contractStatus = [constantsXml.smartCardContractStatus[enumsXml.smartCardContractStatus.pending], constantsXml.smartCardContractStatus[enumsXml.smartCardContractStatus.active]];
        const hasContracts = contractStatus.some((s) => Object.values(contractStatuses).includes(s));
        const renewalProducts = products.filter((f) => f.productRenewal).map((m) => m.externalId);
        let contracts = [];

        if (hasContracts) {
            for (let i=2; i<=5; i++) {
                if (contractStatus.includes(smartCardData['contractStatus'+i]) && renewalProducts.includes(smartCardData['contractId'+i])) {
                    contracts.push({
                        contractId: smartCardData['contractId'+i],
                        contractStartTimestamp: smartCardData['contractStartTimestamp'+i],
                        contractExpirationTimestamp: smartCardData['contractExpirationTimestamp'+i],
                        contractExpirationDays: moment.utc(smartCardData['contractExpirationTimestamp'+i]).diff(moment.utc(), 'days') + 1
                    });
                }
            }
        }

        let contractStartTimestamp = moment.utc().startOf('day').add(businessDayStart, 'hours');
        let contractExpirationTimestamp = (topupProduct.activateOn == 'FirstUse') ? contractStartTimestamp : moment.utc().add(topupProduct.duration, 'days').startOf('day').add(businessDayStart, 'hours');

        if (renewalProducts.includes(topupProduct.externalId)) {
            let lastContractToExpire = contracts.filter((f) => topupProduct.externalId == f.contractId  || topupProduct.renewalGroupProducts.map(Number).includes(f.contractId)).filter((f) => f.contractExpirationDays >= 0).sort((a, b) => moment.utc(b.contractExpirationTimestamp).diff(moment.utc(a.contractExpirationTimestamp)));

            if (lastContractToExpire.length) {
                contractStartTimestamp = contractStartTimestamp.add(lastContractToExpire[0].contractExpirationDays, 'days');
                contractExpirationTimestamp = contractExpirationTimestamp.add(lastContractToExpire[0].contractExpirationDays, 'days');
            }
        }

        return {startTimestamp: contractStartTimestamp.format('X'), expirationTimestamp: contractExpirationTimestamp.format('X')};
    }

    accountContractTimestamp = async (products, topupProduct, accountData) => {
        const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
        let businessDayStart = (activeConfig.businessDayStart) ? moment(activeConfig.businessDayStart, this.timeFormat).subtract(global.currentPosData.utcOffset).hour() : moment(global.currentPosData.endOfWorkHour, this.timeFormat).subtract(global.currentPosData.utcOffset).hour();

        const contractStatuses = accountData.map((m) => m.fareItemStatusType);
        const contractStatus = [constantsXml.contractStatusType.pending, constantsXml.contractStatusType.active];
        const hasContracts = contractStatus.some((s) => contractStatuses.includes(s));
        const renewalProducts = products.filter((f) => f.productRenewal).map((m) => m.externalId);
        let contracts = [];

        if (hasContracts) {
            for (let i=0; i<accountData.length; i++) {
                if (contractStatus.includes(accountData[i].fareItemStatusType) && renewalProducts.includes(accountData[i].contractId)) {
                    contracts.push({
                        contractId: accountData[i].contractId,
                        contractStartTimestamp: accountData[i].creationTimestamp,
                        contractExpirationTimestamp: accountData[i].expirationTimestamp,
                        contractExpirationDays: moment.utc(accountData[i].expirationTimestamp).diff(moment.utc(), 'days') + 1
                    });
                }
            }
        }

        let contractStartTimestamp = moment.utc().startOf('day').add(businessDayStart, 'hours');
        let contractExpirationTimestamp = (topupProduct.activateOn == 'FirstUse') ? contractStartTimestamp : moment.utc().add(topupProduct.duration, 'days').startOf('day').add(businessDayStart, 'hours');

        if (renewalProducts.includes(topupProduct.externalId)) {
            let lastContractToExpire = contracts.filter((f) => topupProduct.externalId == f.contractId  || topupProduct.renewalGroupProducts.map(Number).includes(f.contractId)).filter((f) => f.contractExpirationDays >= 0).sort((a, b) => moment.utc(b.contractExpirationTimestamp).diff(moment.utc(a.contractExpirationTimestamp)));

            if (lastContractToExpire.length) {
                contractStartTimestamp = contractStartTimestamp.add(lastContractToExpire[0].contractExpirationDays, 'days');
                contractExpirationTimestamp = contractExpirationTimestamp.add(lastContractToExpire[0].contractExpirationDays, 'days');
            }
        }

        return {startTimestamp: contractStartTimestamp, expirationTimestamp: contractExpirationTimestamp};
    }

    hotlist = async (req, res) => {
        try {
            const hotlistTicket = req.body;

            const translations1st = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'translations', global.currentPosData.firstLanguage+'.json')));
            const translations2nd = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'translations', global.currentPosData.secondLanguage+'.json')));

            const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
            const organization = await this.modelOrganizations.findOne({organizationId: global.currentPosData.organizationExternalId});
            // const organizationsFiltered = organizations.filter((f) => f.organizationTypes.some((m) => [constants.organizationTypes.simpleOrganization, constants.organizationTypes.merchant].includes(m)));
            const vatRates = await this.fareTableModel.VatRates.find({fareEnvironmentConfigurationId: activeConfig._id});
            const vatRate = (vatRates.find((f) => f.isDefault == true)) ? vatRates.find((f) => f.isDefault == true).percents : 0;
            const vatAddedToTotal = true;

            const reasonConfiguration = await this.reasonsServ.reasonConfiguration();
            const reasonType = reasonConfiguration.reasonTypes.find((f) => f.name == constants.reasonTypes.issueReversal);
            const reasonTypeId = (reasonType) ? reasonType.id : 0;
            const ticketReversalReason = reasonConfiguration.reasons.find((f) => f.reasonTypeId == reasonTypeId && f.name == constants.issueReversalTypes.ticketReversal);
            const creditReversalReason = reasonConfiguration.reasons.find((f) => f.reasonTypeId == reasonTypeId && f.name == constants.issueReversalTypes.creditReversal);
            const canceledPurchaseReason = reasonConfiguration.reasons.find((f) => f.reasonTypeId == reasonTypeId && f.name == constants.issueReversalTypes.canceledPurchase);
            const refundReversalReason = reasonConfiguration.reasons.find((f) => f.reasonTypeId == reasonTypeId && f.name == constants.issueReversalTypes.refundPurchase);
            let reversalReason = (hotlistTicket.ticket.reasonId != 0) ? reasonConfiguration.reasons.find((f) => f.reasonTypeId == reasonTypeId && f.id == hotlistTicket.ticket.reasonId) : canceledPurchaseReason;
            let reversalComment = (hotlistTicket.ticket.reasonComment) ? hotlistTicket.ticket.reasonComment : '';

            //const ticket = await this.modelTicket.findOne({ _id: hotlistTicket.ticket });
            let ticket = await this.modelTicket.create({
                sessionId: global.currentEmployeeData.employeeSessionId,
                transactionNo: hotlistTicket.transactionNo,
                paymentType: hotlistTicket.ticket.paymentType,
                issueTime: moment.utc().toISOString(),
                currencyCode: hotlistTicket.ticket.currency,
                totalCent: hotlistTicket.ticket.totalCent,
                total: hotlistTicket.ticket.total,
                itemNumber: hotlistTicket.ticket.items.filter((f) => f.penalty == null && f.topupAccount == null).map((m) => m.quantity).reduce((p, n) => p + n, 0)
            });

            const issuingTimestamp = moment.utc(ticket.issueTime).utcOffset(global.currentPosData.utcOffset);

            const invoiceId = await this.posTransactionCtrl.invoice(issuingTimestamp);

            let taxData = '';
            let taxQrCode = '';
            if (['hafil'].includes(process.env.CLIENT_NAME)) {
                taxData = {
                    name: (organization && organization.businessName) ? organization.businessName : 'Darb Alwatan Transportation Co',
                    vatNumber: (organization && organization.vatNumber) ? organization.vatNumber : '311510940300003'
                };
                taxQrCode = await this.service.generateTaxQrCode({
                    organizationName: taxData.name,
                    organizationVatNumber: taxData.vatNumber,
                    timestamp: issuingTimestamp.format(this.dateTTimeFormat),
                    invoiceTotal: (ticket.totalCent / Math.pow(10, global.currentPosData.currencyDecimal)).toFixed(global.currentPosData.currencyDecimal),
                    vatTotal: (Math.round(ticket.totalCent * (vatRate / 10000) / (1 + (vatRate / 10000))) / Math.pow(10, global.currentPosData.currencyDecimal)).toFixed(global.currentPosData.currencyDecimal)
                });
            }
            if (['mok'].includes(process.env.CLIENT_NAME) && process.env.CLIENT_FISCAL_VERIFICATION == 'true') {
                const vatDetails = await this.getVatDetails(hotlistTicket.ticket);
                const fiscalVerification = await this.service.fiscalVerificationInvoice({
                    issueDateTime: issuingTimestamp,
                    invoiceNumber: invoiceId,
                    invoiceAmount: ticket.totalCent / Math.pow(10, global.currentPosData.currencyDecimal),
                    vatDetails: vatDetails,

                    referenceNumber: hotlistTicket.ticket.invoiceId,
                    referenceIssueDateTime: moment.utc(hotlistTicket.ticket.issueTime).utcOffset(global.currentPosData.utcOffset)
                });
                taxData = {
                    name: fiscalVerification.eor,
                    vatNumber: fiscalVerification.zoi
                };
                taxQrCode = fiscalVerification.qrCode;
            }

            const ticketQr = await this.modelTicket.findOneAndUpdate({ _id: ticket.id }, {
                taxData: taxData,
                taxQrCode: taxQrCode,
                invoiceId: invoiceId
            }, {new: true});

            const ticketItemsLen = (hotlistTicket.ticket.items).length;
            for (let i=0; i<ticketItemsLen; i++) {
                const cartItem = hotlistTicket.ticket.items[i];

                const transitType = (cartItem.routeId) ? await this.fareEnvironmentModel.TransitTypes.findOne({fareEnvironmentConfigurationId: activeConfig._id, externalId: cartItem.routeId}) : (cartItem.type) ? await this.fareEnvironmentModel.TransitTypes.findOne({fareEnvironmentConfigurationId: activeConfig._id, name: cartItem[cartItem.type].routeTypeName}) : '';
                const transitTypeDef = await this.fareEnvironmentModel.TransitTypes.findOne({fareEnvironmentConfigurationId: activeConfig._id}).sort({externalId: 1});
                const fare = (!transitType) ? {
                    externalId: transitTypeDef.externalId,
                    name: transitTypeDef.name,
                    fareSystemModel: transitTypeDef.fareSystemModel,
                    routeSystemType: transitTypeDef.routeSystemType
                } : {
                    externalId: transitType.externalId,
                    name: transitType.name,
                    fareSystemModel: transitType.fareSystemModel,
                    routeSystemType: transitType.routeSystemType
                };
                let routeIntercityId = '';
                if (cartItem.productBaggage != null) {
                    routeIntercityId = cartItem.productBaggage.routeIntercityId;
                } else if (cartItem.intercityTicket != null) {
                    routeIntercityId = cartItem.intercityTicket.routeIntercityId;
                }
                const route = (routeIntercityId) ? await this.fareEnvironmentModel.Routes.findOne({fareEnvironmentConfigurationId: activeConfig._id, machineId: routeIntercityId}) : (cartItem.type) ? await this.fareEnvironmentModel.Routes.findOne({fareEnvironmentConfigurationId: activeConfig._id, name: cartItem[cartItem.type].route, destination: cartItem[cartItem.type].routeDestination}) : '';
                const routeDef = await this.fareEnvironmentModel.Routes.findOne({fareEnvironmentConfigurationId: activeConfig._id}).sort({externalId: 1});
                const avl = (!route) ? {
                    externalId: routeDef.externalId,
                    name: routeDef.name,
                    destination: routeDef.destination,
                    variant: routeDef.variant,
                    version: routeDef.version
                } : {
                    externalId: route.externalId,
                    name: route.name,
                    destination: route.destination,
                    variant: route.variant,
                    version: route.version
                };
                avl.locationLongitude = global.currentPosData.locationLongitude;
                avl.locationLatitude = global.currentPosData.locationLatitude;
                avl.tripId = cartItem.tripId;

                let fareItemIssuedNumber = cartItem.fareItemIssuedNumber;
                let businessDayStart = (activeConfig.businessDayStart) ? moment(activeConfig.businessDayStart, this.timeFormat).subtract(global.currentPosData.utcOffset).hour() : moment(global.currentPosData.endOfWorkHour, this.timeFormat).subtract(global.currentPosData.utcOffset).hour();
                let fareItemIssuedExpirationTimeStamp = moment.utc(ticket.issueTime).add(1, 'days');
                let issueTime = moment.utc(ticket.issueTime);

                let ticketItem = {
                    quantity: cartItem.quantity,
                    riderExternalId: (cartItem.riderType) ? cartItem.riderType.externalId : constants.defaults.accountId,
                    riderName: (cartItem.riderType) ? cartItem.riderType.name.charAt(0).toUpperCase() + cartItem.riderType.name.slice(1) : constants.defaults.accountName,
                    accountId: cartItem.accountId,
                    accountNo: cartItem.accountNo,
                    routeId: transitType.externalId,
                    tripId: cartItem.tripId
                };

                let paymentInstrument = constantsXml.paymentInstrument.none;
                let paymentMethod = constantsXml.paymentMethod.none;
                let paymentInfoType = constants.paymentInfoType.none;

                switch (ticket.paymentType) {
                    case enums.paymentTypes.cash:
                        paymentInstrument = constantsXml.paymentInstrument.prepayCash;
                        paymentMethod = constantsXml.paymentMethod.manualyCollected;
                        paymentInfoType = constants.paymentInfoType.cash;
                    break;
                    case enums.paymentTypes.card:
                        paymentInstrument = constantsXml.paymentInstrument.prepayCreditCard;
                        paymentMethod = constantsXml.paymentMethod.automaticalyCollected;
                        paymentInfoType = constants.paymentInfoType.creditCard;
                    break;
                    case enums.paymentTypes.credit:
                        paymentInstrument = constantsXml.paymentInstrument.prepayAccountEPurse;
                        paymentMethod = constantsXml.paymentMethod.automaticalyCollected;
                        paymentInfoType = constants.paymentInfoType.account;
                    break;
                }

                let transaction = {
                    ticketId: ticket.id,
                    ticketItemId: '',
                    transactionTime: ticket.issueTime,
                    riderExternalId: (cartItem.riderType) ? cartItem.riderType.externalId : constants.defaults.accountId,
                    riderName: (cartItem.riderType) ? cartItem.riderType.name.charAt(0).toUpperCase() + cartItem.riderType.name.slice(1) : constants.defaults.accountName,
                    paymentInstrument: paymentInstrument,
                    paymentMethod: paymentMethod,
                    currencyCode: ticket.currencyCode,
                    employeeId: global.currentEmployeeData.employeeId,
                    sessionId: global.currentEmployeeData.employeeSessionId,
                    cardTransactionNo: ticket.transactionNo,
                    accountNo: cartItem.accountNo
                };

                let mediaType, ticketType, fareItemCategory, fareItemType, issuedFormatType, passengerBaseFare, passengerCorrectionFareDetails;
                mediaType = constants.mediaType.ticket;

                if (cartItem.accountId) {

                    if (cartItem.cityTicket != null) {
                        fareItemCategory = (cartItem.numberOfPassengers > 1) ? constantsXml.fareItemCategory.fareJourneyGroup : constantsXml.fareItemCategory.fareJourney;
                        fareItemType = (cartItem.numberOfPassengers > 1) ? constantsXml.fareItemType.journeyGroupElectronicTicket : constantsXml.fareItemType.journeyElectronicTicket;
                        issuedFormatType = constantsXml.issuedFormatType.permanentQrCodeAccountStored;
                    } else if (cartItem.productBaggage != null) {
                        fareItemCategory =  constantsXml.fareItemCategory.fareProduct;
                        fareItemType = constantsXml.fareItemType.productAccount;
                        issuedFormatType = constantsXml.issuedFormatType.permanentQrCodeAccountStored;
                    } else if (cartItem.productBaggageReturn != null) {
                        fareItemCategory =  constantsXml.fareItemCategory.fareProduct;
                        fareItemType = constantsXml.fareItemType.productAccount;
                        issuedFormatType = constantsXml.issuedFormatType.permanentQrCodeAccountStored;
                    } else if (cartItem.intercityTicket != null) {
                        if (cartItem.intercityTicket.isReturn) {
                            fareItemCategory = (cartItem.numberOfPassengers > 1) ? constantsXml.fareItemCategory.fareReturnSingleRideGroup : constantsXml.fareItemCategory.fareReturnSingleRide;
                            fareItemType = (cartItem.numberOfPassengers > 1) ? constantsXml.fareItemType.returnSingleRideGroupElectronicTicket : constantsXml.fareItemType.returnSingleRideElectronicTicket;
                        } else {
                            fareItemCategory = (cartItem.numberOfPassengers > 1) ? constantsXml.fareItemCategory.fareSingleRideGroup : constantsXml.fareItemCategory.fareSingleRide;
                            fareItemType = (cartItem.numberOfPassengers > 1) ? constantsXml.fareItemType.singleRideGroupElectronicTicket : constantsXml.fareItemType.singleRideElectronicTicket;
                        }
                        issuedFormatType = constantsXml.issuedFormatType.permanentQrCodeAccountStored;
                    } else if (cartItem.penalty != null) {
                        fareItemCategory =  constantsXml.fareItemCategory.farePenaltyItem;
                        fareItemType = constantsXml.fareItemType.penaltyPaymentReversal;
                        issuedFormatType = '';
                    } else if (cartItem.productPeriod != null) {
                        fareItemCategory =  constantsXml.fareItemCategory.fareProduct;
                        fareItemType = constantsXml.fareItemType.productAccount;
                        issuedFormatType = constantsXml.issuedFormatType.permanentQrCodeAccountStored;
                    } else if (cartItem.topupAccount != null) {
                        fareItemCategory = constantsXml.fareItemCategory.fareEpurse;
                        fareItemType = constantsXml.fareItemType.refundEpurseAccount;
                        issuedFormatType = '';
                    } else if (cartItem.smartCardIssue != null)  {
                        fareItemCategory = constantsXml.fareItemCategory.initSmartCard;
                        fareItemType = constantsXml.fareItemType.issueSmartCard;
                        issuedFormatType = '';
                    } else if (cartItem.topupSmartCard != null) {
                        fareItemCategory = constantsXml.fareItemCategory.fareProduct;
                        fareItemType = constantsXml.fareItemType.productSmartCard;
                        issuedFormatType = '';
                    }

                } else {

                    if (cartItem.cityTicket != null) {
                        fareItemCategory = (cartItem.numberOfPassengers > 1) ? constantsXml.fareItemCategory.fareJourneyGroup : constantsXml.fareItemCategory.fareJourney;
                        fareItemType = (cartItem.numberOfPassengers > 1) ? constantsXml.fareItemType.journeyGroupTicket : constantsXml.fareItemType.journeyTicket;
                        issuedFormatType = constantsXml.issuedFormatType.printedPaperTicket;
                    } else if (cartItem.productBaggage != null) {
                        fareItemCategory =  constantsXml.fareItemCategory.fareProduct;
                        fareItemType = constantsXml.fareItemType.productTicket;
                        issuedFormatType = constantsXml.issuedFormatType.printedPaperTicket;
                    } else if (cartItem.productBaggageReturn != null) {
                        fareItemCategory =  constantsXml.fareItemCategory.fareProduct;
                        fareItemType = constantsXml.fareItemType.productTicket;
                        issuedFormatType = constantsXml.issuedFormatType.printedPaperTicket;
                    }  else if (cartItem.intercityTicket != null) {
                        if (cartItem.intercityTicket.isReturn) {
                            fareItemCategory = (cartItem.numberOfPassengers > 1) ? constantsXml.fareItemCategory.fareReturnSingleRideGroup : constantsXml.fareItemCategory.fareReturnSingleRide;
                            fareItemType = (cartItem.numberOfPassengers > 1) ? constantsXml.fareItemType.returnSingleRideGroupTicket : constantsXml.fareItemType.returnSingleRideTicket;
                        } else {
                            fareItemCategory = (cartItem.numberOfPassengers > 1) ? constantsXml.fareItemCategory.fareSingleRideGroup : constantsXml.fareItemCategory.fareSingleRide;
                            fareItemType = (cartItem.numberOfPassengers > 1) ? constantsXml.fareItemType.singleRideGroupTicket : constantsXml.fareItemType.singleRideTicket;
                        }
                        issuedFormatType = constantsXml.issuedFormatType.printedPaperTicket;
                    } else if (cartItem.penalty != null) {
                        fareItemCategory =  constantsXml.fareItemCategory.farePenaltyItem;
                        fareItemType = constantsXml.fareItemType.penaltyPaymentReversal;
                        issuedFormatType = '';
                    } else if (cartItem.productPeriod != null) {
                        fareItemCategory =  constantsXml.fareItemCategory.fareProduct;
                        fareItemType = constantsXml.fareItemType.productTicket;
                        issuedFormatType = constantsXml.issuedFormatType.printedPaperTicket;
                    } else if (cartItem.topupAccount != null) {
                        fareItemCategory = constantsXml.fareItemCategory.fareEpurse;
                        fareItemType = constantsXml.fareItemType.refundEpurseAccount;
                        issuedFormatType = '';
                    } else if (cartItem.smartCardIssue != null)  {
                        fareItemCategory = constantsXml.fareItemCategory.initSmartCard;
                        fareItemType = constantsXml.fareItemType.issueSmartCard;
                        issuedFormatType = '';
                    } else if (cartItem.topupSmartCard != null) {
                        fareItemCategory = constantsXml.fareItemCategory.fareProduct;
                        fareItemType = constantsXml.fareItemType.productSmartCard;
                        issuedFormatType = '';
                    }
                }

                ticketType = (mediaType == constants.mediaType.ticket) ? 'paperTicketValidityInDays' : 'digitalTicketValidityInDays';

                if (cartItem.cityTicket != null) {

                    ticketItem.price = cartItem.cityTicket.price * cartItem.numberOfPassengers;
                    ticketItem.priceCent = cartItem.cityTicket.priceCent * cartItem.numberOfPassengers;
                    ticketItem.prodName = cartItem.cityTicket.name;
                    ticketItem.prodNameTranslate = cartItem.cityTicket.nameTranslated;
                    // ticketItem.ticketExpiryTime = moment.utc(ticket.issueTime).add(2, 'days').startOf('day').add(2, 'hours');
                    ticketItem.zoneTraveled = cartItem.cityTicket.zoneTraveled;
                    // transaction
                    transaction.fareItemIssuedNumber = fareItemIssuedNumber;
                    transaction.activityType = (fareItemCategory == constantsXml.fareItemCategory.fareEpurse) ? constantsXml.activityType.debit : constantsXml.activityType.issueReversal;
                    transaction.issuedFormatType = issuedFormatType;
                    transaction.numberOfPassengers = cartItem.numberOfPassengers;
                    transaction.fareItemCategory =  fareItemCategory;
                    transaction.fareItemType = fareItemType;
                    transaction.farePrice = cartItem.cityTicket.priceCent * cartItem.numberOfPassengers;
                    transaction.farePriceCurrency = ticketItem.price;
                    const multiPriceProduct = await this.fareTableModel.MultiPriceProducts.findOne({fareEnvironmentConfigurationId: activeConfig._id, mediaType: mediaType, fareType: cartItem.cityTicket.fareType});
                    transaction.fareItemExternalId = (multiPriceProduct) ? multiPriceProduct.externalId : 0;
                    transaction.fareItemName = (multiPriceProduct) ? multiPriceProduct.name : constantsXml.fareItemName.na;
                    fareItemIssuedExpirationTimeStamp = (multiPriceProduct) ? issueTime.add(multiPriceProduct[ticketType], 'days') : fareItemIssuedExpirationTimeStamp;
                    transaction.fareItemIssuedExpirationTimeStamp = (multiPriceProduct) ? ((multiPriceProduct.validityCalculationStartCondition == constants.validityStart.timeOfPurchase) ? fareItemIssuedExpirationTimeStamp : fareItemIssuedExpirationTimeStamp.startOf('day').add(businessDayStart, 'hours')) : fareItemIssuedExpirationTimeStamp;
                    transaction.totalPaymentInCents = cartItem.cityTicket.priceCentCalculated;
                    transaction.prodName = ticketItem.prodName;
                    transaction.productType = constants.productType.multiPrice;
                    transaction.waybillName = `${cartItem.cityTicket.route} - ${cartItem.riderType.name} - ${translations1st['Canceled']}`;
                    transaction.waybillNameTranslated = `${cartItem.cityTicket.routeTranslated} - ${cartItem.riderType.nameTranslated} - ${translations2nd['Canceled']}`;

                } else if (cartItem.productBaggage != null) {

                    ticketItem.price = cartItem.productBaggage.price;
                    ticketItem.priceCent = cartItem.productBaggage.priceCent;
                    ticketItem.prodName = cartItem.productBaggage.name;
                    ticketItem.prodNameTranslate = cartItem.productBaggage.nameTranslated;
                    ticketItem.origStopCode = (cartItem.productBaggage.busStopFromCode) ? cartItem.productBaggage.busStopFromCode : '';
                    ticketItem.destStopCode = (cartItem.productBaggage.busStopToCode) ? cartItem.productBaggage.busStopToCode : '';
                    ticketItem.origStationCode = (cartItem.productBaggage.busStationFromCode) ? cartItem.productBaggage.busStationFromCode : '';
                    ticketItem.destStationCode = (cartItem.productBaggage.busStationToCode) ? cartItem.productBaggage.busStationToCode : '';
                    // ticketItem.ticketExpiryTime = (cartItem.accountId) ? moment.utc(ticket.issueTime).add(365, 'days').startOf('day').add(2, 'hours') : moment.utc(ticket.issueTime).add(2, 'days').startOf('day').add(2, 'hours');
                    ticketItem.origStop = (cartItem.productBaggage.busStopFromName) ? cartItem.productBaggage.busStopFromName : '';
                    ticketItem.origStopTranslate = (cartItem.productBaggage.busStationFromNameTranslate) ? cartItem.productBaggage.busStationFromNameTranslate : '';
                    ticketItem.destStop = (cartItem.productBaggage.busStopToName) ? cartItem.productBaggage.busStopToName : '';
                    ticketItem.destStopTranslate = (cartItem.productBaggage.busStopToNameTranslate) ? cartItem.productBaggage.busStopToNameTranslate : '';
                    ticketItem.routeIntercityId = cartItem.productBaggage.routeIntercityId;
                    ticketItem.routeId = cartItem.routeId;
                    ticketItem.route = cartItem.productBaggage.route;
                    ticketItem.routeTranslated = cartItem.productBaggage.routeTranslated;
                    ticketItem.fareItemCategory = fareItemCategory;
                    ticketItem.baggage = cartItem.baggageQuantity;
                    ticketItem.baggageReturn = cartItem.baggageQuantity2;

                    if (global.currentPosData.hasReservation) {
                        ticketItem.reservationCartId = cartItem.productBaggage.reservationCartId;
                        ticketItem.reservationPassengerId = cartItem.productBaggage.reservationPassengerId;
                        ticketItem.reservationPassengerName = cartItem.productBaggage.passengerName;
                        ticketItem.reservationPassengerPhoneNumber = cartItem.productBaggage.passengerPhoneNumber;
                        ticketItem.reservationPassengerSeat = cartItem.productBaggage.passengerSeat;
                        ticketItem.reservationBusStationFromTime = cartItem.productBaggage.busStationFromTime;
                        ticketItem.reservationBusStationToTime = cartItem.productBaggage.busStationToTime;

                        ticketItem.reservationDiscount = cartItem.productBaggage.reservationDiscount;
                        passengerBaseFare = ticketItem.reservationDiscount.basePriceInCents;

                        ticketItem.reservationJourneyId = cartItem.productBaggage.reservationJourneyId;
                        ticketItem.reservationPassenger = cartItem.productBaggage.reservationPassenger;
                        ticketItem.reservationPassenger.fareItemIssuedNumber = fareItemIssuedNumber;
                    }
                    ticketItem.qrCode = '';

                    // transaction
                    transaction.fareItemIssuedNumber = fareItemIssuedNumber;
                    transaction.activityType = (fareItemCategory == constantsXml.fareItemCategory.fareEpurse) ? constantsXml.activityType.debit : constantsXml.activityType.issueReversal;
                    transaction.issuedFormatType = issuedFormatType;
                    transaction.numberOfPassengers = cartItem.numberOfPassengers;
                    transaction.fareItemCategory = fareItemCategory;
                    transaction.fareItemType = fareItemType;
                    transaction.farePrice = cartItem.productBaggage.priceCent;
                    transaction.farePriceCurrency = ticketItem.price;
                    const products = await this.fareTableModel.Products.find({fareEnvironmentConfigurationId: activeConfig._id, productEntityClass: constants.productEntityClass.fareSellableProduct, purchasableAccess: {$in: String(cartItem.riderType.externalId)}, 'sellableAccess.deviceType': constantsXml.deviceType.officePos, $or:[{sellingStartDay: {$eq: null}}, {sellingStartDay: {$lte: moment.utc().date()}}, {sellingEndDay: {$eq: null}}, {sellingEndDay: {$gte: moment.utc().date()}}]});
                    const productLuggage = products.find((f) => f.externalId == cartItem.productBaggage.externalId);
                    transaction.fareItemExternalId = (productLuggage) ? productLuggage.externalId : 0;
                    transaction.fareItemName = (productLuggage) ? productLuggage.name : constantsXml.fareItemName.na;
                    fareItemIssuedExpirationTimeStamp = (productLuggage) ? issueTime.add(productLuggage[ticketType], 'days') : fareItemIssuedExpirationTimeStamp;
                    transaction.fareItemIssuedExpirationTimeStamp = (productLuggage) ? ((productLuggage.validityCalculationStartCondition == constants.validityStart.timeOfPurchase) ? fareItemIssuedExpirationTimeStamp : fareItemIssuedExpirationTimeStamp.startOf('day').add(businessDayStart, 'hours')) : fareItemIssuedExpirationTimeStamp;
                    transaction.totalPaymentInCents = (productLuggage) ? productLuggage.priceInCents : transaction.farePrice;
                    transaction.prodName = ticketItem.prodName;
                    transaction.productType = (productLuggage) ? productLuggage.productType : constants.productType.luggage;
                    transaction.waybillName = `${translations1st[constants.waybillName.baggage]} - ${translations1st['Canceled']}`;
                    transaction.waybillNameTranslated = `${translations2nd[constants.waybillName.baggage]} - ${translations2nd['Canceled']}`;
                    transaction.anonimusLuggage = cartItem.productBaggage.isIntercityTicket;

                    if (productLuggage && productLuggage.adjustments && cartItem.productBaggage.adjustmentExternalId != 0) {
                        const productLuggageAdjustment = productPeriod.adjustments.find((f) => f.externalId == cartItem.productBaggage.adjustmentExternalId);
                        transaction.fareAdjustment = productLuggageAdjustment;
                        passengerCorrectionFareDetails = this.passengerCorrectionFareDetails(cartItem, transaction.fareAdjustment, (passengerBaseFare) ? passengerBaseFare : transaction.farePrice, '');
                    }

                } else if (cartItem.productBaggageReturn != null) {

                } else if (cartItem.intercityTicket != null) {

                    ticketItem.price = (cartItem.intercityTicket.isReturn) ? cartItem.intercityTicket.priceReturn * cartItem.numberOfPassengers : cartItem.intercityTicket.price * cartItem.numberOfPassengers;
                    ticketItem.priceCent = (cartItem.intercityTicket.isReturn) ? cartItem.intercityTicket.priceCentReturn * cartItem.numberOfPassengers : cartItem.intercityTicket.priceCent * cartItem.numberOfPassengers;
                    ticketItem.prodName = cartItem.intercityTicket.name;
                    ticketItem.prodNameTranslate = cartItem.intercityTicket.nameTranslated;
                    ticketItem.origStopCode = (cartItem.intercityTicket.busStopFromCode) ? cartItem.intercityTicket.busStopFromCode : '';
                    ticketItem.destStopCode = (cartItem.intercityTicket.busStopToCode) ? cartItem.intercityTicket.busStopToCode : '';
                    ticketItem.origStationCode = (cartItem.intercityTicket.busStationFromCode) ? cartItem.intercityTicket.busStationFromCode : '';
                    ticketItem.destStationCode = (cartItem.intercityTicket.busStationToCode) ? cartItem.intercityTicket.busStationToCode : '';
                    // ticketItem.ticketExpiryTime =  (cartItem.intercityTicket.isReturn) ? ((cartItem.accountId) ? moment.utc(ticket.issueTime).add(365, 'days').startOf('day').add(2, 'hours') : moment.utc(ticket.issueTime).add(32, 'days').startOf('day').add(2, 'hours')) : ((cartItem.accountId) ? moment.utc(ticket.issueTime).add(365, 'days').startOf('day').add(2, 'hours') : moment.utc(ticket.issueTime).add(2, 'days').startOf('day').add(2, 'hours'));
                    ticketItem.origStop = (cartItem.intercityTicket.busStopFromName) ? cartItem.intercityTicket.busStopFromName : '';
                    ticketItem.origStopTranslate = (cartItem.intercityTicket.busStationFromNameTranslate) ? cartItem.intercityTicket.busStationFromNameTranslate : '';
                    ticketItem.destStop = (cartItem.intercityTicket.busStopToName) ? cartItem.intercityTicket.busStopToName : '';
                    ticketItem.destStopTranslate = (cartItem.intercityTicket.busStopToNameTranslate) ? cartItem.intercityTicket.busStopToNameTranslate : '';
                    ticketItem.routeIntercityId = cartItem.intercityTicket.routeIntercityId;
                    ticketItem.routeId = cartItem.routeId;
                    ticketItem.route = cartItem.intercityTicket.route;
                    ticketItem.routeTranslated = cartItem.intercityTicket.routeTranslated;
                    ticketItem.fareItemCategory = fareItemCategory;
                    ticketItem.returnTicket = cartItem.intercityTicket.isReturn;
                    //ticketItem.baggage = cartItem.baggageQuantity;
                    //ticketItem.baggageReturn = cartItem.baggageQuantity2;
                    //ticketItem.priceBaggage = cartItem.intercityTicket.priceBaggage;

                    if (global.currentPosData.hasReservation) {
                        ticketItem.reservationCartId = cartItem.intercityTicket.reservationCartId;
                        ticketItem.reservationPassengerId = cartItem.intercityTicket.reservationPassengerId;
                        ticketItem.reservationPassengerName = cartItem.intercityTicket.passengerName;
                        ticketItem.reservationPassengerPhoneNumber = cartItem.intercityTicket.passengerPhoneNumber;
                        ticketItem.reservationPassengerSeat = cartItem.intercityTicket.passengerSeat;
                        ticketItem.reservationBusStationFromTime = cartItem.intercityTicket.busStationFromTime;
                        ticketItem.reservationBusStationToTime = cartItem.intercityTicket.busStationToTime;

                        ticketItem.reservationDiscount = cartItem.intercityTicket.reservationDiscount;
                        passengerBaseFare = ticketItem.reservationDiscount.basePriceInCents;

                        ticketItem.reservationJourneyId = cartItem.intercityTicket.reservationJourneyId;
                        ticketItem.reservationPassenger = cartItem.intercityTicket.reservationPassenger;
                        ticketItem.reservationPassenger.fareItemIssuedNumber = fareItemIssuedNumber;
                    }
                    ticketItem.qrCode = '';

                    // transaction
                    transaction.fareItemIssuedNumber = fareItemIssuedNumber;
                    transaction.activityType = (fareItemCategory == constantsXml.fareItemCategory.fareEpurse) ? constantsXml.activityType.debit : constantsXml.activityType.issueReversal;
                    transaction.issuedFormatType = issuedFormatType;
                    transaction.numberOfPassengers = cartItem.numberOfPassengers;
                    transaction.fareItemCategory =  fareItemCategory;
                    transaction.fareItemType = fareItemType;
                    transaction.farePrice = ticketItem.priceCent;
                    transaction.farePriceCurrency = ticketItem.price;
                    const multiPriceProduct = await this.fareTableModel.MultiPriceProducts.findOne({fareEnvironmentConfigurationId: activeConfig._id, mediaType: mediaType, fareType: cartItem.intercityTicket.fareType});
                    transaction.fareItemExternalId = (multiPriceProduct) ? multiPriceProduct.externalId : 0;
                    transaction.fareItemName = (multiPriceProduct) ? multiPriceProduct.name : constantsXml.fareItemName.na;
                    fareItemIssuedExpirationTimeStamp = (multiPriceProduct) ? issueTime.add(multiPriceProduct[ticketType], 'days') : fareItemIssuedExpirationTimeStamp;
                    transaction.fareItemIssuedExpirationTimeStamp = (multiPriceProduct) ? ((multiPriceProduct.validityCalculationStartCondition == constants.validityStart.timeOfPurchase) ? fareItemIssuedExpirationTimeStamp : fareItemIssuedExpirationTimeStamp.startOf('day').add(businessDayStart, 'hours')) : fareItemIssuedExpirationTimeStamp;
                    transaction.totalPaymentInCents = cartItem.intercityTicket.priceCentCalculated;
                    transaction.prodName = ticketItem.prodName;
                    transaction.productType = constants.productType.multiPrice;
                    transaction.waybillName = `${cartItem.intercityTicket.route} - ${cartItem.riderType.name} - ${translations1st['Canceled']}`;
                    transaction.waybillNameTranslated = `${cartItem.intercityTicket.routeTranslated} - ${cartItem.riderType.nameTranslated} - ${translations2nd['Canceled']}`;

                    if (multiPriceProduct && multiPriceProduct.adjustments && cartItem.intercityTicket.adjustmentExternalId != 0) {
                        const multiPriceProductAdjustment = multiPriceProduct.adjustments.find((f) => f.externalId == cartItem.intercityTicket.adjustmentExternalId);
                        transaction.fareAdjustment = multiPriceProductAdjustment;
                        passengerCorrectionFareDetails = this.passengerCorrectionFareDetails(cartItem, transaction.fareAdjustment, (passengerBaseFare) ? passengerBaseFare : transaction.farePrice, (multiPriceProduct && multiPriceProduct.hasOwnProperty('priceRoundingType')) ? multiPriceProduct.priceRoundingType : '');
                    }

                } else if (cartItem.penalty != null) {

                } else if (cartItem.productPeriod != null) {

                    ticketItem.price = cartItem.productPeriod.price;
                    ticketItem.priceCent = cartItem.productPeriod.priceCent;
                    ticketItem.prodName = cartItem.productPeriod.name;
                    ticketItem.prodNameTranslate = cartItem.productPeriod.nameTranslated;
                    // ticketItem.ticketExpiryTime =  moment.utc(ticket.issueTime).add(180, 'days').startOf('day').add(2, 'hours');
                    // transaction
                    transaction.fareItemIssuedNumber = fareItemIssuedNumber;
                    transaction.activityType = (fareItemCategory == constantsXml.fareItemCategory.fareEpurse) ? constantsXml.activityType.debit : constantsXml.activityType.issueReversal;
                    transaction.issuedFormatType = issuedFormatType;
                    transaction.numberOfPassengers = cartItem.numberOfPassengers;
                    transaction.fareItemCategory = fareItemCategory;
                    transaction.fareItemType = fareItemType;
                    transaction.farePrice = cartItem.productPeriod.priceCent;
                    transaction.farePriceCurrency = ticketItem.price;
                    const products = await this.fareTableModel.Products.find({fareEnvironmentConfigurationId: activeConfig._id, productEntityClass: constants.productEntityClass.fareSellableProduct, purchasableAccess: {$in: String(cartItem.riderType.externalId)}, 'sellableAccess.deviceType': constantsXml.deviceType.officePos, $or:[{sellingStartDay: {$eq: null}}, {sellingStartDay: {$lte: moment.utc().date()}}, {sellingEndDay: {$eq: null}}, {sellingEndDay: {$gte: moment.utc().date()}}]});
                    const productPeriod = products.find((f) => f.externalId == cartItem.productPeriod.id);
                    transaction.fareItemExternalId = (productPeriod) ? productPeriod.externalId : 0;
                    transaction.fareItemName = (productPeriod) ? productPeriod.name : constantsXml.fareItemName.na;
                    fareItemIssuedExpirationTimeStamp = (productPeriod) ? issueTime.add(productPeriod.duration, 'days') : fareItemIssuedExpirationTimeStamp;
                    transaction.fareItemIssuedExpirationTimeStamp = (productPeriod) ? ((productPeriod.validityCalculationStartCondition == constants.validityStart.timeOfPurchase) ? fareItemIssuedExpirationTimeStamp : fareItemIssuedExpirationTimeStamp.startOf('day').add(businessDayStart, 'hours')) : fareItemIssuedExpirationTimeStamp;
                    transaction.totalPaymentInCents = (productPeriod) ? productPeriod.priceInCents : transaction.farePrice;
                    transaction.prodName = ticketItem.prodName;
                    transaction.productType = (productPeriod) ? productPeriod.productType : constants.productType.periodPass;
                    transaction.waybillName = `${cartItem.productPeriod.name} - ${translations1st['Canceled']}`;
                    transaction.waybillNameTranslated = `${cartItem.productPeriod.nameTranslated} - ${translations2nd['Canceled']}`;

                    if (productPeriod && productPeriod.adjustments && cartItem.productPeriod.adjustmentExternalId != 0) {
                        const productPeriodAdjustment = productPeriod.adjustments.find((f) => f.externalId == cartItem.productPeriod.adjustmentExternalId);
                        transaction.fareAdjustment = productPeriodAdjustment;
                        passengerCorrectionFareDetails = this.passengerCorrectionFareDetails(cartItem, transaction.fareAdjustment, transaction.farePrice, '');
                    }

                } else if (cartItem.topupAccount != null) {

                    let account = await this.accountHolderServ.getAccount(cartItem.topupAccount.accountNo);
                    if (!account) {
                        account = {
                            riderTypeId: cartItem.topupAccount.riderTypeId,
                            type: cartItem.topupAccount.type,
                            shortId: cartItem.topupAccount.id
                        };
                    }

                    ticketItem.price = cartItem.topupAccount.price;
                    ticketItem.priceCent = cartItem.topupAccount.priceCent;
                    ticketItem.prodName = cartItem.topupAccount.name;
                    ticketItem.prodNameTranslate = cartItem.topupAccount.nameTranslated;
                    // ticketItem.ticketExpiryTime =  moment.utc(ticket.issueTime).add(365, 'days');
                    ticketItem.riderExternalId = account.riderTypeId;
                    // transaction
                    transaction.fareItemIssuedNumber = fareItemIssuedNumber;
                    transaction.activityType = constantsXml.activityType.debit;
                    transaction.issuedFormatType = issuedFormatType;
                    transaction.numberOfPassengers = cartItem.numberOfPassengers;
                    transaction.fareItemCategory = fareItemCategory;
                    transaction.fareItemType = fareItemType;
                    transaction.farePrice = cartItem.topupAccount.priceCent;
                    transaction.farePriceCurrency = ticketItem.price;
                    const topupProduct = await this.accountHolderCtrl.topupProduct(cartItem.topupAccount.riderTypeId);
                    transaction.fareItemExternalId = (Object.keys(topupProduct).length != 0) ? topupProduct.id : 0;
                    transaction.fareItemName = (Object.keys(topupProduct).length != 0) ? topupProduct.name : constantsXml.fareItemName.na;
                    transaction.fareItemIssuedExpirationTimeStamp = (Object.keys(topupProduct).length != 0) ?  issueTime.add(topupProduct.duration, 'days').startOf('day').add(businessDayStart, 'hours') : fareItemIssuedExpirationTimeStamp;
                    transaction.totalPaymentInCents = transaction.farePrice;
                    transaction.prodName = ticketItem.prodName;
                    transaction.productType = (Object.keys(topupProduct).length != 0) ? topupProduct.type : constants.productType.eWallet;
                    transaction.waybillName = `${translations1st[constants.waybillName.topUp]} - ${translations1st['Canceled']}`;
                    transaction.waybillNameTranslated = `${translations2nd[constants.waybillName.topUp]} - ${translations2nd['Canceled']}`;
                    transaction.riderExternalId = account.riderTypeId;
                    transaction.riderName = account.type;

                    const vatRule = await this.fareTableModel.VatRules.findOne({fareEnvironmentConfigurationId: activeConfig._id, paymentInstrumentType: constantsXml.paymentInstrument.prepayAccountEPurse});
                    const vatAddedToTotal = (vatRule) ? ((transaction.paymentInstrument != constantsXml.paymentInstrument.prepayAccountEPurse) ? true : vatRule.isVatDeductedDuringTopup) : false;

                    ticketItem.vatRate = vatRate;
                    transaction.vatRate = vatRate;
                    transaction.totalVat = Math.round(transaction.farePrice * (vatRate / 10000) / (1 + (vatRate / 10000)));

                    const ticketItemTopup = await this.modelTicket.findOneAndUpdate({ _id: ticket.id }, {
                        $push: {
                            items: ticketItem
                        }
                    }, {new: true});

                    transaction.ticketItemId = ticketItemTopup.items[ticketItemTopup.items.length - 1].id;

                    let createdFile = await this.posTransactionCtrl.create({
                        function: 'cancellation',
                        component: 'ticketPrinter',
                        parameters: {
                            avl: avl,
                            fare: fare,
                            financial: {
                                activityType: transaction.activityType,
                                fareItemCategory: transaction.fareItemCategory,
                                fareItemType: transaction.fareItemType,
                                fareItemExternalId: transaction.fareItemExternalId,
                                fareItemProductType: transaction.productType,
                                fareItemIssuedNumber: transaction.fareItemIssuedNumber,
                                fareItemIssuedExpirationTimeStamp: moment.utc(transaction.fareItemIssuedExpirationTimeStamp).format(this.dateTTimeFormat),
                                fareItemPurchasedStagesOrZones: (ticketItem.zoneTraveled) ? ticketItem.zoneTraveled : 0,
                                fareItemName: transaction.fareItemName,
                                payingRiderTypeExternalId: transaction.riderExternalId,
                                payingRiderTypeName: transaction.riderName,
                                payingRiderAccountId: transaction.accountNo,
                                cumulativeFareOrProductPrice: transaction.farePrice,
                                totalPaymentInCents: transaction.totalPaymentInCents,
                                totalVatInCents: transaction.totalVat,
                                vatAddedToTotalPayment: vatAddedToTotal,
                                penaltyFee: 0,
                                paymentInstrument: transaction.paymentInstrument,
                                paymentMethod: transaction.paymentMethod,
                                currencyCode: global.currentPosData.currencyCode,
                                paymentInstrumentTransactionId: (transaction.cardTransactionNo) ? transaction.cardTransactionNo : 0,
                                relatedTransactionInformation: {
                                    relatedTransactionTimestamp: moment.utc(transaction.transactionTime).format(this.dateTTimeFormat),
                                    relatedTransactionDeviceType: constantsXml.deviceType.officePos,
                                    relatedTransactionDeviceId: global.currentPosData.deviceId,
                                    relatedTransactionTerminalId: global.currentPosData.terminalId,
                                    relatedTransactionDeviceTransactionCounter: global.currentPosData.transactionCounter
                                },
                                reasonInformation: {
                                    reasonId: creditReversalReason.id,
                                    reasonName: creditReversalReason.name,
                                    operatorComment: reversalComment
                                },
                                taxInvoiceInformation: {
                                    invoiceIdNumber: invoiceId.split('-')[2],
                                    invoiceExternalReferenceCode: invoiceId,
                                    taxInvoiceQrCode: taxQrCode
                                },
                                vatDetails: {
                                    taxableAmountInCents: transaction.farePrice - transaction.totalVat,
                                    vatRate: transaction.vatRate,
                                    vatInCents: transaction.totalVat
                                }
                            }
                        }
                    });

                    await this.modelFinancial.create(transaction);

                    return res.status(200).json({
                        creditNoteId: invoiceId,
                        invoiceId: hotlistTicket.ticket.invoiceId,
                        issueTime: issuingTimestamp.format(this.dateTimeFormat),
                        taxData: taxData,
                        taxQrCode: taxQrCode
                    });
                } else if (cartItem.smartCardIssue != null) {

                } else if (cartItem.topupSmartCard != null) {

                    ticketItem.price = cartItem.topupSmartCard.price;
                    ticketItem.priceCent = cartItem.topupSmartCard.priceCent;
                    ticketItem.prodName = cartItem.topupSmartCard.name;
                    ticketItem.prodNameTranslate = cartItem.topupSmartCard.nameTranslated;
                    ticketItem.riderExternalId = cartItem.riderType.externalId;
                    ticketItem.cardVersion = cartItem.topupSmartCard.data.cardVersion;
                    ticketItem.cardUid = cartItem.topupSmartCard.data.cardUid;
                    ticketItem.cardholderId = cartItem.topupSmartCard.cardholderId;
                    // transaction
                    transaction.cardVersion = ticketItem.cardVersion;
                    transaction.cardUid = ticketItem.cardUid;
                    transaction.cardholderId = ticketItem.cardholderId;
                    transaction.fareItemIssuedNumber = fareItemIssuedNumber;
                    transaction.activityType = constantsXml.activityType.issueReversal;
                    transaction.issuedFormatType = issuedFormatType;
                    transaction.fareItemCategory = fareItemCategory;
                    transaction.fareItemType = fareItemType;
                    transaction.farePrice = cartItem.topupSmartCard.priceCent;
                    transaction.farePriceCurrency = ticketItem.price;
                    const smartcardprice = await this.fareTableModel.SmartCardPrices.findOne({fareEnvironmentConfigurationId: activeConfig._id, productEntityClass: constants.productEntityClass.smartCardPrice, purchasableAccess: {$in: String(cartItem.riderType.externalId)}});
                    const products = await this.fareTableModel.Products.find({fareEnvironmentConfigurationId: activeConfig._id, productEntityClass: constants.productEntityClass.fareSellableProduct, purchasableAccess: {$in: String(cartItem.riderType.externalId)}, 'sellableAccess.deviceType': constantsXml.deviceType.officePos, $or:[{sellingStartDay: {$eq: null}}, {sellingStartDay: {$lte: moment.utc().date()}}, {sellingEndDay: {$eq: null}}, {sellingEndDay: {$gte: moment.utc().date()}}]});
                    const topupProduct = products.find((f) => f.externalId == cartItem.topupSmartCard.productId);
                    transaction.fareItemExternalId = (topupProduct) ? topupProduct.externalId : 0;
                    transaction.fareItemName = (topupProduct) ? topupProduct.name : constantsXml.fareItemName.na;
                    transaction.fareItemIssuedExpirationTimeStamp = moment.utc(cartItem.topupSmartCard.expiry, this.dateTimeFormat).utcOffset(global.currentPosData.utcOffset.replace('+', '-')).format().replace(global.currentPosData.utcOffset.replace('+', '-'), 'Z');
                    transaction.totalPaymentInCents = cartItem.topupSmartCard.priceCent;
                    transaction.prodName = ticketItem.prodName;
                    transaction.productType = (topupProduct) ? topupProduct.productType : constants.productType.ePurse;
                    transaction.waybillName = `${translations1st[constants.waybillName.topUpSmartCard]} - ${translations1st['Canceled']}`;
                    transaction.waybillNameTranslated = `${translations2nd[constants.waybillName.topUpSmartCard]} - ${translations2nd['Canceled']}`;

                    transaction.riderExternalId = cartItem.riderType.externalId;
                    transaction.riderName = cartItem.riderType.name;

                    let smartCardContracts = await this.smartCardContracts(cartItem.topupSmartCard.data);
                    const smartCardProfiles = await this.fareTableModel.SmartCardProfiles.find({fareEnvironmentConfigurationId: activeConfig._id});

                    const vatRates = await this.fareTableModel.VatRates.find({fareEnvironmentConfigurationId: activeConfig._id});
                    const vatRate = (vatRates.find((f) => [topupProduct.productType].includes(f.associatedProductTypes))) ? vatRates.find((f) => [topupProduct.productType].includes(f.associatedProductTypes)) : vatRates.find((f) => f.isDefault == true);
                    const vatRule = await this.fareTableModel.VatRules.findOne({fareEnvironmentConfigurationId: activeConfig._id, paymentInstrumentType: constantsXml.paymentInstrument.prepaySmartCardEPurse});
                    const vatAddedToTotal = (vatRule) ? ((transaction.paymentInstrument != constantsXml.paymentInstrument.prepaySmartCardEPurse) ? true : vatRule.isVatDeductedDuringTopup) : false;
                    const vatRateInOneHundredths = (vatRate) ? vatRate.percents : 0;
                    ticketItem.vatRate = vatRateInOneHundredths;
                    transaction.vatRate = vatRateInOneHundredths;
                    transaction.totalVat = Math.round(transaction.farePrice * (vatRateInOneHundredths / 10000) / (1 + (vatRateInOneHundredths / 10000)));

                    const ticketItemTopupSmartCard = await this.modelTicket.findOneAndUpdate({ _id: ticket.id }, {
                        $push: {
                            items: ticketItem
                        }
                    }, {new: true});

                    transaction.ticketItemId = ticketItemTopupSmartCard.items[ticketItemTopupSmartCard.items.length - 1].id;

                    const currentTimestamp = moment.utc().format(this.dateTTimeFormat);

                    // remove ePurse credit from smart card
                    if (topupProduct.productType == constants.productType.topUpRelative) {
                        let contractNo = 1;
                        const creditValues = {
                            amount: -transaction.farePrice,
                            contractId: transaction.fareItemExternalId,
                            terminalId: global.currentPosData.terminalId,
                            terminalType: enumsXml.deviceType.officePos,
                            merchantId: 1,
                            ptoReference: 0
                        };

                        const creditPiccCardData = await this.smartCardServ.creditEpurse(creditValues);
                        const cardCounters = await this.smartCardServ.cardCounters({
                            transactionCounter: creditPiccCardData.transactionCounter + 1,
                            countersTimestamp: moment.utc().format('X')
                        });
                        const topUpLogs = await this.smartCardServ.topUpLogs({
                            topupLogTransactionTimestamp: moment.utc().format('X'),
                            topupLogContractCategory: enumsXml.smartCardContractCategory[topupProduct.productType],
                            topupLogContractId: creditValues.contractId,
                            topupLogContractPointer: contractNo,
                            topupLogFareItemCategory: enumsXml.fareItemCategoryType.fareEpurse,
                            topupLogFareItemType: enumsXml.fareItemType.debitEpurseSmartCard,
                            topupLogActivityType: enumsXml.activityType.debit,
                            topupLogTerminalId: creditValues.terminalId,
                            topupLogTerminalType: creditValues.terminalType,
                            topupLogTransactionId: creditPiccCardData.transactionCounter + 1,
                            topupLogAmountCredited: creditValues.amount,
                            topupLogMerchantNumber: creditValues.merchantId,
                            topupLogPtoRference: creditValues.ptoReference
                        });
                        const cardPrintedId = await this.smartCardServ.getCardPrintedId(creditPiccCardData.cardUid);
                        const smartCardProfile = smartCardProfiles.find((f) => f.cardTypeShortNumber == creditPiccCardData.cardType && f.cardVersionShortNumber == creditPiccCardData.cardVersion && f.isEnabled);
                        const cardVersion = (smartCardProfile) ? smartCardProfile.cardVersion : cartItem.topupSmartCard.data.version;
                        const cardType = (smartCardProfile) ? smartCardProfile.cardType : constantsXml.smartCardType.standard;
                        const cardTechnology = (smartCardProfile) ? smartCardProfile.cardTechnology : constantsXml.smartCardPhysicalType.mifareDesfireEv3;

                        let topUpRelativeContract = {
                            contractType: constantsXml.smartCardContractType.topUpRelative,
                            productId: creditPiccCardData['contractId'+contractNo],
                            contractId: creditPiccCardData['contractId'+contractNo],
                            instanceId: creditPiccCardData['instanceId'+contractNo],
                            contractStatus: creditPiccCardData['contractStatus'+contractNo],
                            contractStartTimestamp: creditPiccCardData['contractStartTimestamp'+contractNo],
                            contractExpirationTimestamp: creditPiccCardData['contractExpirationTimestamp'+contractNo],
                            contractStateChangeTimestamp: creditPiccCardData['contractStateChangeTimestamp'+contractNo],
                            contractValue: creditPiccCardData['contractValue'+contractNo]
                        };

                        smartCardContracts = await this.smartCardContracts(creditPiccCardData);

                        transaction.activityType = constantsXml.activityType.debit;
                        transaction.fareItemCategory = constantsXml.fareItemCategory.fareEpurse;
                        transaction.fareItemType = constantsXml.fareItemType.debitEpurseSmartCard;

                        await this.posTransactionCtrl.create({
                            function: 'smartCardContract',
                            component: 'smartCardReader',
                            parameters: {
                                avl: avl,
                                fare: fare,
                                financial: {
                                    activityType: transaction.activityType,
                                    fareItemCategory: transaction.fareItemCategory,
                                    fareItemType: transaction.fareItemType,
                                    fareItemExternalId: transaction.fareItemExternalId,
                                    fareItemProductType: transaction.productType,
                                    fareItemIssuedNumber: transaction.fareItemIssuedNumber,
                                    fareItemIssuedExpirationTimeStamp: moment.utc(transaction.fareItemIssuedExpirationTimeStamp).format(this.dateTTimeFormat),
                                    fareItemPurchasedStagesOrZones: 0,
                                    fareItemName: transaction.fareItemName,
                                    payingRiderTypeExternalId: transaction.riderExternalId,
                                    payingRiderTypeName: transaction.riderName,
                                    payingRiderAccountId: transaction.accountNo,
                                    cumulativeFareOrProductPrice: transaction.farePrice,
                                    totalPaymentInCents: transaction.totalPaymentInCents,
                                    totalVatInCents: transaction.totalVat,
                                    vatAddedToTotalPayment: vatAddedToTotal,
                                    penaltyFee: 0,
                                    paymentInstrument: transaction.paymentInstrument,
                                    paymentMethod: transaction.paymentMethod,
                                    currencyCode: global.currentPosData.currencyCode,
                                    paymentInstrumentTransactionId: (transaction.cardTransactionNo) ? transaction.cardTransactionNo : 0,
                                    relatedTransactionInformation: {
                                        relatedTransactionTimestamp: moment.utc(transaction.transactionTime).format(this.dateTTimeFormat),
                                        relatedTransactionDeviceType: constantsXml.deviceType.officePos,
                                        relatedTransactionDeviceId: global.currentPosData.deviceId,
                                        relatedTransactionTerminalId: global.currentPosData.terminalId,
                                        relatedTransactionDeviceTransactionCounter: global.currentPosData.transactionCounter
                                    },
                                    reasonInformation: {
                                        reasonId: reversalReason.id,
                                        reasonName: reversalReason.name,
                                        operatorComment: reversalComment
                                    },
                                    taxInvoiceInformation: {
                                        invoiceIdNumber: invoiceId.split('-')[2],
                                        invoiceExternalReferenceCode: invoiceId,
                                        taxInvoiceQrCode: taxQrCode
                                    },
                                    vatDetails: {
                                        taxableAmountInCents: transaction.farePrice - transaction.totalVat,
                                        vatRate: transaction.vatRate,
                                        vatInCents: transaction.totalVat
                                    }
                                },
                                card: {
                                    // card reference
                                    cardUid: creditPiccCardData.cardUid,
                                    cardPrintedId: cardPrintedId,
                                    cardType: cardType,
                                    cardTypeShortNumber: creditPiccCardData.cardType,
                                    cardVersion: cardVersion,
                                    cardVersionShortNumber: creditPiccCardData.cardVersion,
                                    cardIteration: creditPiccCardData.cardIteration,
                                    physicalCardType: cardTechnology,
                                    issuerId: creditPiccCardData.issuerId,
                                    transactionCounter: creditPiccCardData.transactionCounter + 1,
                                    // card snapshot
                                    timestamp: currentTimestamp,
                                    currencyCode: creditPiccCardData.currencyCode,
                                    preferredLanguage: creditPiccCardData.preferredLanguage,
                                    riderTypeExternalId: creditPiccCardData.riderTypeExternalId,
                                    storedValue: cartItem.topupSmartCard.data['contractValue'+contractNo],
                                    refundableDeposit: creditPiccCardData.refundableDeposit,
                                    cardExpirationTimestamp: creditPiccCardData.cardExpirationTimestamp,
                                    alic: creditPiccCardData.alic,
                                    alwc: creditPiccCardData.alwc,
                                    blockStatus: creditPiccCardData.blockStatus,
                                    blockTimestamp: creditPiccCardData.blockTimestamp,
                                    contracts: smartCardContracts.contracts,
                                    // card operation
                                    creditEpurse: {
                                        newEpurseValue: cartItem.topupSmartCard.data['contractValue'+contractNo] - transaction.farePrice,
                                        previousEpurseValue: cartItem.topupSmartCard.data['contractValue'+contractNo]
                                    }
                                }
                            }
                        });
                    }

                    // remove periodPass contract from smart card
                    if (topupProduct.productType == constants.productType.periodPass) {
                        let smartCardData = cartItem.topupSmartCard.data;
                        let instanceValue = smartCardContracts.contracts.sort((a, b) => moment.utc(b.contractStateChangeTimestamp) - moment.utc(a.contractStateChangeTimestamp)).find((f) => f.contractId == transaction.fareItemExternalId).instanceId;
                        let instanceKey = Object.keys(smartCardData).find((f) => smartCardData[f] === instanceValue);
                        let contractNo = parseInt(instanceKey.slice(-1));
                        const removePeriodPassValues = {
                            contractId: 0,
                            instanceId: '0000000000',
                            contractValue: enumsXml.smartCardContractValue.pending,
                            contractCategory: enumsXml.smartCardContractCategory.Undefined,
                            contractStatus: enumsXml.smartCardContractStatus.invalid,
                            contractStartTimestamp: moment.utc(0).format('X'),
                            contractExpirationTimestamp: moment.utc(0).format('X'),
                            contractStateChangeTimestamp: moment.utc(0).format('X')
                        };

                        const contractPiccCardData = await this.smartCardServ.removeContract(removePeriodPassValues, contractNo);
                        const cardCounters = await this.smartCardServ.cardCounters({
                            transactionCounter: contractPiccCardData.transactionCounter + 1,
                            countersTimestamp: moment.utc().format('X')
                        });
                        const topUpLogs = await this.smartCardServ.topUpLogs({
                            topupLogTransactionTimestamp: moment.utc().format('X'),
                            topupLogContractCategory: enumsXml.smartCardContractCategory[topupProduct.productType],
                            topupLogContractId: transaction.fareItemExternalId,
                            topupLogContractPointer: contractNo,
                            topupLogFareItemCategory: enumsXml.fareItemCategoryType.fareProduct,
                            topupLogFareItemType: enumsXml.fareItemType.productSmartCard,
                            topupLogActivityType: enumsXml.activityType.issueReversal,
                            topupLogTerminalId: global.currentPosData.terminalId,
                            topupLogTerminalType: enumsXml.deviceType.officePos,
                            topupLogTransactionId: contractPiccCardData.transactionCounter + 1,
                            topupLogAmountCredited: 0,
                            topupLogMerchantNumber: 1,
                            topupLogPtoRference: 0
                        });
                        const cardPrintedId = await this.smartCardServ.getCardPrintedId(contractPiccCardData.cardUid);
                        const smartCardProfile = smartCardProfiles.find((f) => f.cardTypeShortNumber == contractPiccCardData.cardType && f.cardVersionShortNumber == contractPiccCardData.cardVersion && f.isEnabled);
                        const cardVersion = (smartCardProfile) ? smartCardProfile.cardVersion : cartItem.topupSmartCard.data.version;
                        const cardType = (smartCardProfile) ? smartCardProfile.cardType : constantsXml.smartCardType.standard;
                        const cardTechnology = (smartCardProfile) ? smartCardProfile.cardTechnology : constantsXml.smartCardPhysicalType.mifareDesfireEv3;

                        smartCardContracts = await this.smartCardContracts(contractPiccCardData);

                        await this.posTransactionCtrl.create({
                            function: 'smartCardRemoveContract',
                            component: 'smartCardReader',
                            parameters: {
                                avl: avl,
                                fare: fare,
                                financial: {
                                    activityType: transaction.activityType,
                                    fareItemCategory: transaction.fareItemCategory,
                                    fareItemType: transaction.fareItemType,
                                    fareItemExternalId: transaction.fareItemExternalId,
                                    fareItemProductType: transaction.productType,
                                    fareItemIssuedNumber: transaction.fareItemIssuedNumber,
                                    fareItemIssuedExpirationTimeStamp: moment.utc(transaction.fareItemIssuedExpirationTimeStamp).format(this.dateTTimeFormat),
                                    fareItemPurchasedStagesOrZones: 0,
                                    fareItemName: transaction.fareItemName,
                                    payingRiderTypeExternalId: transaction.riderExternalId,
                                    payingRiderTypeName: transaction.riderName,
                                    payingRiderAccountId: transaction.accountNo,
                                    cumulativeFareOrProductPrice: transaction.farePrice,
                                    totalPaymentInCents: transaction.totalPaymentInCents,
                                    totalVatInCents: transaction.totalVat,
                                    vatAddedToTotalPayment: vatAddedToTotal,
                                    penaltyFee: 0,
                                    paymentInstrument: transaction.paymentInstrument,
                                    paymentMethod: transaction.paymentMethod,
                                    currencyCode: global.currentPosData.currencyCode,
                                    paymentInstrumentTransactionId: (transaction.cardTransactionNo) ? transaction.cardTransactionNo : 0,
                                    relatedTransactionInformation: {
                                        relatedTransactionTimestamp: moment.utc(transaction.transactionTime).format(this.dateTTimeFormat),
                                        relatedTransactionDeviceType: constantsXml.deviceType.officePos,
                                        relatedTransactionDeviceId: global.currentPosData.deviceId,
                                        relatedTransactionTerminalId: global.currentPosData.terminalId,
                                        relatedTransactionDeviceTransactionCounter: global.currentPosData.transactionCounter
                                    },
                                    reasonInformation: {
                                        reasonId: reversalReason.id,
                                        reasonName: reversalReason.name,
                                        operatorComment: reversalComment
                                    },
                                    taxInvoiceInformation: {
                                        invoiceIdNumber: invoiceId.split('-')[2],
                                        invoiceExternalReferenceCode: invoiceId,
                                        taxInvoiceQrCode: taxQrCode
                                    },
                                    vatDetails: {
                                        taxableAmountInCents: transaction.farePrice - transaction.totalVat,
                                        vatRate: transaction.vatRate,
                                        vatInCents: transaction.totalVat
                                    }
                                },
                                card: {
                                    // card reference
                                    cardUid: contractPiccCardData.cardUid,
                                    cardPrintedId: cardPrintedId,
                                    cardType: cardType,
                                    cardTypeShortNumber: contractPiccCardData.cardType,
                                    cardVersion: cardVersion,
                                    cardVersionShortNumber: contractPiccCardData.cardVersion,
                                    cardIteration: contractPiccCardData.cardIteration,
                                    physicalCardType: cardTechnology,
                                    issuerId: contractPiccCardData.issuerId,
                                    transactionCounter: contractPiccCardData.transactionCounter + 1,
                                    // card snapshot
                                    timestamp: currentTimestamp,
                                    currencyCode: contractPiccCardData.currencyCode,
                                    preferredLanguage: contractPiccCardData.preferredLanguage,
                                    riderTypeExternalId: contractPiccCardData.riderTypeExternalId,
                                    storedValue: cartItem.topupSmartCard.data['contractValue'+contractNo],
                                    refundableDeposit: contractPiccCardData.refundableDeposit,
                                    cardExpirationTimestamp: contractPiccCardData.cardExpirationTimestamp,
                                    alic: contractPiccCardData.alic,
                                    alwc: contractPiccCardData.alwc,
                                    blockStatus: contractPiccCardData.blockStatus,
                                    blockTimestamp: contractPiccCardData.blockTimestamp,
                                    contracts: smartCardContracts.contracts,
                                    // card operation
                                    removeContract: {
                                        contractType: constantsXml.contractType.periodPass,
                                        productId: smartCardData['contractId'+contractNo],
                                        contractId: smartCardData['contractId'+contractNo],
                                        instanceId: smartCardData['instanceId'+contractNo]
                                    }
                                }
                            }
                        });
                    }

                    // remove storedRides contract from smart card
                    if (topupProduct.productType == constants.productType.storedRides) {
                        let smartCardData = cartItem.topupSmartCard.data;
                        let instanceValue = smartCardContracts.contracts.sort((a, b) => moment.utc(b.contractStateChangeTimestamp) - moment.utc(a.contractStateChangeTimestamp)).find((f) => f.contractId == transaction.fareItemExternalId).instanceId;
                        let instanceKey = Object.keys(smartCardData).find((f) => smartCardData[f] === instanceValue);
                        let contractNo = parseInt(instanceKey.slice(-1));
                        const removeStoredRidesValues = {
                            contractId: 0,
                            instanceId: '0000000000',
                            contractValue: enumsXml.smartCardContractValue.pending,
                            contractCategory: enumsXml.smartCardContractCategory.Undefined,
                            contractStatus: enumsXml.smartCardContractStatus.invalid,
                            contractStartTimestamp: moment.utc(0).format('X'),
                            contractExpirationTimestamp: moment.utc(0).format('X'),
                            contractStateChangeTimestamp: moment.utc(0).format('X')
                        };

                        const contractPiccCardData = await this.smartCardServ.removeContract(removeStoredRidesValues, contractNo);
                        const cardCounters = await this.smartCardServ.cardCounters({
                            transactionCounter: contractPiccCardData.transactionCounter + 1,
                            countersTimestamp: moment.utc().format('X')
                        });
                        const topUpLogs = await this.smartCardServ.topUpLogs({
                            topupLogTransactionTimestamp: moment.utc().format('X'),
                            topupLogContractCategory: enumsXml.smartCardContractCategory[topupProduct.productType],
                            topupLogContractId: transaction.fareItemExternalId,
                            topupLogContractPointer: contractNo,
                            topupLogFareItemCategory: enumsXml.fareItemCategoryType.fareProduct,
                            topupLogFareItemType: enumsXml.fareItemType.productSmartCard,
                            topupLogActivityType: enumsXml.activityType.issueReversal,
                            topupLogTerminalId: global.currentPosData.terminalId,
                            topupLogTerminalType: enumsXml.deviceType.officePos,
                            topupLogTransactionId: contractPiccCardData.transactionCounter + 1,
                            topupLogAmountCredited: 0,
                            topupLogMerchantNumber: 1,
                            topupLogPtoRference: 0
                        });
                        const cardPrintedId = await this.smartCardServ.getCardPrintedId(contractPiccCardData.cardUid);
                        const smartCardProfile = smartCardProfiles.find((f) => f.cardTypeShortNumber == contractPiccCardData.cardType && f.cardVersionShortNumber == contractPiccCardData.cardVersion && f.isEnabled);
                        const cardVersion = (smartCardProfile) ? smartCardProfile.cardVersion : cartItem.topupSmartCard.data.version;
                        const cardType = (smartCardProfile) ? smartCardProfile.cardType : constantsXml.smartCardType.standard;
                        const cardTechnology = (smartCardProfile) ? smartCardProfile.cardTechnology : constantsXml.smartCardPhysicalType.mifareDesfireEv3;

                        smartCardContracts = await this.smartCardContracts(contractPiccCardData);

                        await this.posTransactionCtrl.create({
                            function: 'smartCardRemoveContract',
                            component: 'smartCardReader',
                            parameters: {
                                avl: avl,
                                fare: fare,
                                financial: {
                                    activityType: transaction.activityType,
                                    fareItemCategory: transaction.fareItemCategory,
                                    fareItemType: transaction.fareItemType,
                                    fareItemExternalId: transaction.fareItemExternalId,
                                    fareItemProductType: transaction.productType,
                                    fareItemIssuedNumber: transaction.fareItemIssuedNumber,
                                    fareItemIssuedExpirationTimeStamp: moment.utc(transaction.fareItemIssuedExpirationTimeStamp).format(this.dateTTimeFormat),
                                    fareItemPurchasedStagesOrZones: 0,
                                    fareItemName: transaction.fareItemName,
                                    payingRiderTypeExternalId: transaction.riderExternalId,
                                    payingRiderTypeName: transaction.riderName,
                                    payingRiderAccountId: transaction.accountNo,
                                    cumulativeFareOrProductPrice: transaction.farePrice,
                                    totalPaymentInCents: transaction.totalPaymentInCents,
                                    totalVatInCents: transaction.totalVat,
                                    vatAddedToTotalPayment: vatAddedToTotal,
                                    penaltyFee: 0,
                                    paymentInstrument: transaction.paymentInstrument,
                                    paymentMethod: transaction.paymentMethod,
                                    currencyCode: global.currentPosData.currencyCode,
                                    paymentInstrumentTransactionId: (transaction.cardTransactionNo) ? transaction.cardTransactionNo : 0,
                                    relatedTransactionInformation: {
                                        relatedTransactionTimestamp: moment.utc(transaction.transactionTime).format(this.dateTTimeFormat),
                                        relatedTransactionDeviceType: constantsXml.deviceType.officePos,
                                        relatedTransactionDeviceId: global.currentPosData.deviceId,
                                        relatedTransactionTerminalId: global.currentPosData.terminalId,
                                        relatedTransactionDeviceTransactionCounter: global.currentPosData.transactionCounter
                                    },
                                    reasonInformation: {
                                        reasonId: reversalReason.id,
                                        reasonName: reversalReason.name,
                                        operatorComment: reversalComment
                                    },
                                    taxInvoiceInformation: {
                                        invoiceIdNumber: invoiceId.split('-')[2],
                                        invoiceExternalReferenceCode: invoiceId,
                                        taxInvoiceQrCode: taxQrCode
                                    },
                                    vatDetails: {
                                        taxableAmountInCents: transaction.farePrice - transaction.totalVat,
                                        vatRate: transaction.vatRate,
                                        vatInCents: transaction.totalVat
                                    }
                                },
                                card: {
                                    // card reference
                                    cardUid: contractPiccCardData.cardUid,
                                    cardPrintedId: cardPrintedId,
                                    cardType: cardType,
                                    cardTypeShortNumber: contractPiccCardData.cardType,
                                    cardVersion: cardVersion,
                                    cardVersionShortNumber: contractPiccCardData.cardVersion,
                                    cardIteration: contractPiccCardData.cardIteration,
                                    physicalCardType: cardTechnology,
                                    issuerId: contractPiccCardData.issuerId,
                                    transactionCounter: contractPiccCardData.transactionCounter + 1,
                                    // card snapshot
                                    timestamp: currentTimestamp,
                                    currencyCode: contractPiccCardData.currencyCode,
                                    preferredLanguage: contractPiccCardData.preferredLanguage,
                                    riderTypeExternalId: contractPiccCardData.riderTypeExternalId,
                                    storedValue: cartItem.topupSmartCard.data['contractValue'+contractNo],
                                    refundableDeposit: contractPiccCardData.refundableDeposit,
                                    cardExpirationTimestamp: contractPiccCardData.cardExpirationTimestamp,
                                    alic: contractPiccCardData.alic,
                                    alwc: contractPiccCardData.alwc,
                                    blockStatus: contractPiccCardData.blockStatus,
                                    blockTimestamp: contractPiccCardData.blockTimestamp,
                                    contracts: smartCardContracts.contracts,
                                    // card operation
                                    removeContract: {
                                        contractType: constantsXml.contractType.storedRides,
                                        productId: smartCardData['contractId'+contractNo],
                                        contractId: smartCardData['contractId'+contractNo],
                                        instanceId: smartCardData['instanceId'+contractNo]
                                    }
                                }
                            }
                        });
                    }

                    await this.modelFinancial.create(transaction);

                    return res.status(200).json({
                        creditNoteId: invoiceId,
                        invoiceId: hotlistTicket.ticket.invoiceId,
                        issueTime: issuingTimestamp.format(this.dateTimeFormat),
                        taxData: taxData,
                        taxQrCode: taxQrCode
                    });
                }

                ticketItem.vatRate = vatRate;
                transaction.vatRate = vatRate;
                transaction.totalVat = Math.round(transaction.farePrice * (vatRate / 10000) / (1 + (vatRate / 10000)));

                if (global.currentPosData.hasReservation) {
                    avl.tripStartTimestamp = moment.utc(ticketItem.reservationBusStationFromTime).format(this.dateTTimeFormat);
                    avl.tripEstimatedEndTimestamp = moment.utc(ticketItem.reservationBusStationToTime).format(this.dateTTimeFormat);

                    //if (ticketItem.reservationDiscount.staticDiscountCode == null && ticketItem.reservationDiscount.dynamicDiscountCode != null) {
                        passengerCorrectionFareDetails = {
                            calculationSequenceCounter: 0,
                            fareAdjustmentId: ticketItem.reservationDiscount.dynamicDiscountCode,
                            fareAdjustmentType: 'compoundMultiTier',
                            fareAdjustmentName: ticketItem.reservationDiscount.dynamicDiscountType,
                            [(ticketItem.reservationDiscount.dynamicDiscountPercentage > 0) ? 'percentageCorrectionValue' : 'fixedAmountCorrectionValue']: (ticketItem.reservationDiscount.dynamicDiscountPercentage > 0) ? ticketItem.reservationDiscount.dynamicDiscountPercentage * 100 : ticketItem.reservationDiscount.dynamicDiscountInCents,
                            correctionFare: ticketItem.reservationDiscount.dynamicDiscountInCents,
                            inputBaseFare: ticketItem.reservationDiscount.basePriceInCents,
                            correctedBaseFare: ticketItem.reservationDiscount.discountedPriceInCents
                        };
                    //}
                }

                const ticketItemProd = await this.modelTicket.findOneAndUpdate({ _id: ticket.id }, {
                    $push: {
                        items: ticketItem
                    }
                }, {new: true});

                let createdFile = await this.posTransactionCtrl.create({
                    function: 'cancellation',
                    component: 'ticketPrinter',
                    parameters: {
                        avl: avl,
                        fare: fare,
                        financial: {
                            activityType: transaction.activityType,
                            fareItemCategory: transaction.fareItemCategory,
                            fareItemType: transaction.fareItemType,
                            fareItemExternalId: transaction.fareItemExternalId,
                            fareItemProductType: transaction.productType,
                            fareItemIssuedNumber: transaction.fareItemIssuedNumber,
                            fareItemIssuedExpirationTimeStamp: moment.utc(transaction.fareItemIssuedExpirationTimeStamp).format(this.dateTTimeFormat),
                            fareItemPurchasedStagesOrZones: (ticketItem.zoneTraveled) ? ticketItem.zoneTraveled : 0,
                            fareItemName: transaction.fareItemName,
                            payingRiderTypeExternalId: transaction.riderExternalId,
                            payingRiderTypeName: transaction.riderName,
                            payingRiderAccountId: transaction.accountNo,
                            cumulativeFareOrProductPrice: transaction.farePrice,
                            totalPaymentInCents: transaction.totalPaymentInCents,
                            totalVatInCents: transaction.totalVat,
                            vatAddedToTotalPayment: vatAddedToTotal,
                            penaltyFee: 0,
                            paymentInstrument: transaction.paymentInstrument,
                            paymentMethod: transaction.paymentMethod,
                            currencyCode: global.currentPosData.currencyCode,
                            paymentInstrumentTransactionId: (transaction.cardTransactionNo) ? transaction.cardTransactionNo : 0,
                            passengerDetails: {
                                passengerRiderTypeExternalId: transaction.riderExternalId,
                                passengerRiderTypeName: transaction.riderName,
                                noOfPassengers: transaction.numberOfPassengers,
                                passengerBaseFare: (passengerBaseFare) ? passengerBaseFare : transaction.farePrice / transaction.numberOfPassengers,
                                passengerCorrectionFareDetails: passengerCorrectionFareDetails
                            },
                            relatedTransactionInformation: {
                                relatedTransactionTimestamp: moment.utc(transaction.transactionTime).format(this.dateTTimeFormat),
                                relatedTransactionDeviceType: constantsXml.deviceType.officePos,
                                relatedTransactionDeviceId: global.currentPosData.deviceId,
                                relatedTransactionTerminalId: global.currentPosData.terminalId,
                                relatedTransactionDeviceTransactionCounter: global.currentPosData.transactionCounter
                            },
                            reasonInformation: {
                                reasonId: reversalReason.id,
                                reasonName: reversalReason.name,
                                operatorComment: reversalComment
                            },
                            boardingInformation: (ticketItem.origStopCode || ticketItem.origStationCode || ticketItem.reservationBusStationFromTime) ? {
                                boardingActivity: constantsXml.boardingActivityType.issuing,
                                issuedBoardingStopCode: ticketItem.origStopCode,
                                issuedAligthingStopCode: ticketItem.destStopCode,
                                issuedBoardingStationCode: ticketItem.origStationCode,
        		                issuedAligthingStationCode: ticketItem.destStationCode,
                                actualBoardingStopCode: ticketItem.origStopCode,
                                actualAligthingStopCode: ticketItem.destStopCode,
                                actualBoardingStationCode: ticketItem.origStationCode,
                                actualAligthingStationCode: ticketItem.destStationCode,
                                boardingTimestamp: (ticketItem.reservationBusStationFromTime) ? moment.utc(ticketItem.reservationBusStationFromTime).format(this.dateTTimeFormat) : moment.utc().format(this.dateTTimeFormat),
                                alightingTimestamp: (ticketItem.reservationBusStationToTime) ? moment.utc(ticketItem.reservationBusStationToTime).format(this.dateTTimeFormat) : moment.utc().format(this.dateTTimeFormat),
                                issuedBoardingZoneCode: '',
                                issuedAligthingZoneCode: '',
                                tripDistanceTraveled: 0,
                                tripStagesOrZonesTraveled: 0
                            } : '',
                            taxInvoiceInformation: {
                                invoiceIdNumber: invoiceId.split('-')[2],
                                invoiceExternalReferenceCode: invoiceId,
                                taxInvoiceQrCode: taxQrCode
                            },
                            vatDetails: {
                                taxableAmountInCents: transaction.farePrice - transaction.totalVat,
                                vatRate: transaction.vatRate,
                                vatInCents: transaction.totalVat
                            },
                            bookingDiscount: (global.currentPosData.hasReservation) ? ticketItem.reservationDiscount : ''
                        },
                        ticket: {
                            ticketIssueTimestamp: moment.utc(transaction.transactionTime).format(this.dateTTimeFormat),
                            ticketSerialNumber: transaction.fareItemIssuedNumber,
                            fareItemType: transaction.fareItemType,
                            issuingDeviceId: global.currentPosData.deviceId,
                            issuingDeviceType: constantsXml.deviceType.officePos,
                            ticketExpiryTimestamp: moment.utc(transaction.fareItemIssuedExpirationTimeStamp).format(this.dateTTimeFormat),
                            ticketType: (transaction.numberOfPassengers > 1) ? constantsXml.transactionTicketType.multiplePassengers : constantsXml.transactionTicketType.singlePassenger,
                            passengerRiderTypeExternalIdFirst: transaction.riderExternalId,
                            noOfPassengersFirst: transaction.numberOfPassengers,
                            fareItemExternalId: transaction.fareItemExternalId,
                            fareItemIssuedNumber: transaction.fareItemIssuedNumber,
                            issuedBoardingStopCode: ticketItem.origStopCode,
                            issuedAlightingStopCode: ticketItem.destStopCode,
                            issuedBoardingStationCode: ticketItem.origStationCode,
                            issuedAlightingStationCode: ticketItem.destStationCode,
                            allowedStagesOrZonesNumber: (ticketItem.zoneTraveled) ? ticketItem.zoneTraveled : 0,
                            routeTypeOrServiceTypeExternalId: fare.externalId,
                            routeTypeOrServiceTypeName: fare.name,
                            issuedFormatType: transaction.issuedFormatType
                        },
                        booking: (global.currentPosData.hasReservation) ? {
                            bookingReference: ticketItem.reservationCartId,
                            reservationSeat: ticketItem.reservationPassengerSeat,
                            eTicketReference: ticketItem.reservationPassengerId
                        } : ''
                    }
                });

                await this.modelFinancial.create(transaction);
            }

            res.status(200).json({
                creditNoteId: invoiceId,
                invoiceId: hotlistTicket.ticket.invoiceId,
                issueTime: issuingTimestamp.format(this.dateTimeFormat),
                taxData: taxData,
                taxQrCode: taxQrCode
            });
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    products = async (req, res) => {
        try {
            const transitTypeId = req.query.transitTypeId;
            const riderId = req.query.riderId;
            const accountId = req.query.accountId;

            const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
            const businessDayStart = (activeConfig.businessDayStart) ? moment(activeConfig.businessDayStart, this.timeFormat).subtract(global.currentPosData.utcOffset).hour() : moment(global.currentPosData.endOfWorkHour, this.timeFormat).subtract(global.currentPosData.utcOffset).hour();

            let products;
            if (transitTypeId > 0) {
                products = await this.fareTableModel.Products.find({fareEnvironmentConfigurationId: activeConfig._id, productEntityClass: constants.productEntityClass.fareSellableProduct, purchasableAccess: {$in: String(riderId)}, 'sellableAccess.deviceType': constantsXml.deviceType.officePos, 'transitTypes.externalId': transitTypeId, $or:[{sellingStartDay: {$eq: null}}, {sellingStartDay: {$lte: moment.utc().date()}}, {sellingEndDay: {$eq: null}}, {sellingEndDay: {$gte: moment.utc().date()}}]});
            } else {
                products = await this.fareTableModel.Products.find({fareEnvironmentConfigurationId: activeConfig._id, productEntityClass: constants.productEntityClass.fareSellableProduct, purchasableAccess: {$in: String(riderId)}, 'sellableAccess.deviceType': constantsXml.deviceType.officePos, $or:[{sellingStartDay: {$eq: null}}, {sellingStartDay: {$lte: moment.utc().date()}}, {sellingEndDay: {$eq: null}}, {sellingEndDay: {$gte: moment.utc().date()}}]});
            }

            if (accountId != 0) {
                products = products.filter((f) => ([constants.productType.storedRides, constants.productType.periodPass].includes(f.productType) && [constants.mediaType.smartCard].includes(f.mediaType)) || ([constants.productType.luggage].includes(f.productType) && [constants.mediaType.ticket].includes(f.mediaType)));
            } else if (transitTypeId > 0) {
                products = products.filter((f) => [constants.productType.periodPass, constants.productType.luggage].includes(f.productType) && [constants.mediaType.ticket].includes(f.mediaType));
            } else {
                if (['sharjah'].includes(process.env.CLIENT_NAME)) {
                    products = products.filter((f) => [constants.productType.storedRides, constants.productType.periodPass, constants.productType.topUpFixed, constants.productType.topUpRelative].includes(f.productType) && [constants.mediaType.smartCard].includes(f.mediaType));
                } else {
                    products = products.filter((f) => [constants.productType.storedRides, constants.productType.periodPass].includes(f.productType) && [constants.mediaType.smartCard].includes(f.mediaType));
                }
            }

            if (typeof req.query.issue != 'undefined') {
                let issueProducts = await this.fareTableModel.Products.find({fareEnvironmentConfigurationId: activeConfig._id, productEntityClass: constants.productEntityClass.fareSellableProduct, purchasableAccess: {$in: String(riderId)}, 'sellableAccess.deviceType': constantsXml.deviceType.officePos, $or:[{sellingStartDay: {$eq: null}}, {sellingStartDay: {$lte: moment.utc().date()}}, {sellingEndDay: {$eq: null}}, {sellingEndDay: {$gte: moment.utc().date()}}]});
                let issueProductsFiltered = issueProducts.filter((f) => [constants.productType.processingFee].includes(f.productType) && [constants.mediaType.ticket].includes(f.mediaType));
                const smartcardprice = await this.fareTableModel.SmartCardPrices.findOne({fareEnvironmentConfigurationId: activeConfig._id, productEntityClass: constants.productEntityClass.smartCardPrice, purchasableAccess: {$in: String(riderId)}});

                if (req.query.issue == 1) {
                    products = (issueProductsFiltered) ? issueProductsFiltered : [smartcardprice];
                }
                if (req.query.issue == 2) {
                    products.push(smartcardprice);
                }
                if (req.query.issue == 3) {
                    products = await this.fareTableModel.Products.find({fareEnvironmentConfigurationId: activeConfig._id, productEntityClass: constants.productEntityClass.fareSellableProduct});
                }
            }

            let transitType;
            if (transitTypeId > 0) {
                transitType = await this.fareEnvironmentModel.TransitTypes.findOne({fareEnvironmentConfigurationId: activeConfig._id, externalId: transitTypeId});
            } else {
                transitType = {name: '', nameTranslations: ''};
            }

            let productsList = [];

            const productsLen = products.length;
            for (let i=0; i<productsLen; i++) {
                let priceInCents = products[i].priceInCents;

                const productAdjustment = this.productAdjustment(priceInCents, products[i], riderId, transitTypeId, 'oneway', 'standard', 'direct');

                const vatRates = await this.fareTableModel.VatRates.find({fareEnvironmentConfigurationId: activeConfig._id});
                const vatRate = (vatRates.find((f) => [products[i].productType].includes(f.associatedProductTypes))) ? vatRates.find((f) => [products[i].productType].includes(f.associatedProductTypes)) : vatRates.find((f) => f.isDefault == true);

                productsList.push({
                    id: products[i].externalId,
                    name: products[i].name,
                    nameTranslated: products[i].nameTranslations,
                    activateOn: (products[i].activateOn) ? products[i].activateOn.split(/(?=[A-Z])/).map((m) => m.toLowerCase()).join(' ') : '',
                    description: products[i].print4,
                    descriptionTranslated: products[i].print4Translations,
                    price: priceInCents / Math.pow(10, global.currentPosData.currencyDecimal),
                    priceCent: priceInCents,
                    priceCentCalculated: productAdjustment.priceCentCalculated,
                    adjustmentExternalId: productAdjustment.adjustmentExternalId,
                    type: (products[i].productType) ? products[i].productType : '',
                    expiry: (products[i].duration) ? (products[i].activateOn == 'FirstUse') ? moment.utc().format(this.dateTTimeFormat) : moment.utc().add(products[i].duration, 'days').startOf('day').add(businessDayStart, 'hours').format(this.dateTTimeFormat) : moment.utc().format(this.dateTTimeFormat),
                    renewal: (products[i].productRenewal) ? products[i].duration : 0,
                    numberOfRides: (products[i].numberOfRides) ? products[i].numberOfRides : 0,
                    route: transitType.name,
                    routeTranslated: transitType.nameTranslations,

                    vatRate: (vatRate) ? vatRate.percents : 0
                });
            }

            res.status(200).json(productsList);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    productAdjustment = (priceInCents, product, riderId, transitTypeId, travelType, flexibilityType, saleType) => {
        let adjustmentExternalId = 0;
        if (product && product.adjustments) {
            let adjustments = (Array.isArray(product.adjustments)) ? product.adjustments : Array(product.adjustments);
            adjustments.sort((a, b) => a.priority - b.priority);

            const adjustmentsLen = adjustments.length;
            for (let j=0; j<adjustmentsLen; j++) {
                const rider = (Array.isArray(adjustments[j].riderTypeExternalIds.riderTypeExternalId)) ? adjustments[j].riderTypeExternalIds.riderTypeExternalId : Array(adjustments[j].riderTypeExternalIds.riderTypeExternalId);
                const route = (Array.isArray(adjustments[j].routeTypeExternalIds.routeTypeExternalId)) ? adjustments[j].routeTypeExternalIds.routeTypeExternalId : Array(adjustments[j].routeTypeExternalIds.routeTypeExternalId);
                const organization = (Array.isArray(adjustments[j].organizationExternalIds.organizationExternalId)) ? adjustments[j].organizationExternalIds.organizationExternalId : Array(adjustments[j].organizationExternalIds.organizationExternalId);
                const employeeRole = (Array.isArray(adjustments[j].employeeRoleExternalIds.employeeRoleExternalId)) ? adjustments[j].employeeRoleExternalIds.employeeRoleExternalId : Array(adjustments[j].employeeRoleExternalIds.employeeRoleExternalId);
                const forDevice = (Array.isArray(adjustments[j].adjustableForDeviceTypes.adjustableForDeviceType)) ? adjustments[j].adjustableForDeviceTypes.adjustableForDeviceType : Array(adjustments[j].adjustableForDeviceTypes.adjustableForDeviceType);
                const travelTypes = (Array.isArray(adjustments[j].travelTypes.travelType)) ? adjustments[j].travelTypes.travelType : Array(adjustments[j].travelTypes.travelType);
                const travelFlexibilities = (Array.isArray(adjustments[j].travelFlexibilities.travelFlexibility)) ? adjustments[j].travelFlexibilities.travelFlexibility : Array(adjustments[j].travelFlexibilities.travelFlexibility);
                const salesTypes = (Array.isArray(adjustments[j].salesTypes.salesType)) ? adjustments[j].salesTypes.salesType : Array(adjustments[j].salesTypes.salesType);
                //salesTypes.push(constants.salesTypes.direct);

                const organizationExternalId = (global.currentPosData.organizationExternalId == 0 || global.currentPosData.organizationExternalId == null) ? 'default' : global.currentPosData.organizationExternalId;
                const employeeRoleId = global.currentEmployeeData.employeeRoleId;
                const deviceType = process.env.CLIENT_DEVICE_MNEMONIC;

                const hasRider = (rider.includes(undefined)) ? true : rider.includes(riderId);
                const hasRoute = (route.includes(undefined)) ? true : route.includes(transitTypeId);
                const hasOrganization = (organization.includes(undefined)) ? true : organization.includes(organizationExternalId);
                const hasEmployeeRole = (employeeRole.includes(undefined)) ? true : employeeRole.includes(employeeRoleId);
                const hasDeviceType = (forDevice.includes(undefined)) ? true : forDevice.includes(deviceType);
                const hasTravelTypes = (travelTypes.includes(undefined)) ? true : travelTypes.includes(constants.travelTypes[travelType]);
                const hasTravelFlexibilities = (travelFlexibilities.includes(undefined)) ? true : travelFlexibilities.includes(constants.travelFlexibilityTypes[flexibilityType]);
                const hasSalesTypes = (salesTypes.includes(undefined)) ? true : salesTypes.includes(constants.salesTypes[saleType]);

                let timeRange = false;
                if (adjustments[j].scheduleType == 'Date') {
                    timeRange = moment.utc().isBetween(moment.utc(adjustments[j].timeInRange.fromDateTime), moment.utc(adjustments[j].timeInRange.toDateTime), 'seconds');
                }
                if (adjustments[j].scheduleType == 'DayOfWeek') {
                    timeRange = adjustments[j].isWeekday.weekday.includes(moment.utc().format(this.weekDayFormat).toUpperCase()) && moment.utc().isBetween(moment.utc(adjustments[j].timeInRange.fromTime, this.timeFormat).subtract(1, 'days'), moment.utc(adjustments[j].timeInRange.toTime, this.timeFormat), 'seconds');
                }

                if (hasRider && hasRoute && timeRange && hasOrganization && hasEmployeeRole && hasDeviceType && hasTravelTypes && hasTravelFlexibilities && hasSalesTypes) {
                    switch (adjustments[j].actionType) {
                        case 'TravelRestriction':
                            continue;
                        break;
                        case 'SalesRestriction':
                            continue;
                        break;
                        case 'FixedAmountCorrection':
                            priceInCents = priceInCents + adjustments[j].valueInCents;
                        break;
                        case 'PercentageCorrection':
                            priceInCents = priceInCents + (priceInCents * (adjustments[j].percentageAmountInOneHundredths / 10000));
                        break;
                    }
                    adjustmentExternalId = adjustments[j].externalId;
                }
            }
        }

        const priceCentCalculated = this.roundPriceByRoundingType(priceInCents, (product && product.hasOwnProperty('priceRoundingType')) ? product.priceRoundingType : '');

	    return {priceCentCalculated: priceCentCalculated, adjustmentExternalId: adjustmentExternalId};
    }

    cardTransaction = async (req, res) => {
        try {
            const transaction = req.body;

            await this.modelTicket.findOneAndUpdate({ _id: transaction.ticket }, {
                transactionNo: transaction.number
            });

            await this.modelFinancial.updateMany({ ticketId: transaction.ticket }, {
                cardTransactionNo: transaction.number
            });

            res.status(200).json(transaction.ticket);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    refund = async (req, res) => {
        try {
            const ticket = req.body;

            const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
            const transitType = await this.fareEnvironmentModel.TransitTypes.findOne({fareEnvironmentConfigurationId: activeConfig._id, externalId: ticket.routeTypeId});

            let mediaType = (ticket.accountId) ? constants.mediaType.smartCard : constants.mediaType.ticket;

            if (ticket.fareItemId == 0) {
                const multiPriceProduct = await this.fareTableModel.MutliPriceProducts.find({fareEnvironmentConfigurationId: activeConfig._id, mediaType: constants.mediaType.ticket, fareType: (ticket.zonesLeft > 0) ? constantsXml.fareSystemModel.zones : constantsXml.fareSystemModel.flat});
                if (multiPriceProduct) {
                    ticket.fareItemName = multiPriceProduct.name;
                    ticket.fareItemId = multiPriceProduct.externalId;
                }
            }

            await this.posTransactionCtrl.create({
                function: 'ticketReversal',
                component: 'ticketPrinter',
                parameters: {
                    fare: {
                        externalId: transitType.externalId,
                        name: transitType.name,
                        fareSystemModel: transitType.fareSystemModel,
                        routeSystemType: transitType.routeSystemType
                    },
                    financial: {
                        activityType: constantsXml.activityType.issueReversal,
                        fareItemCategory: (ticket.fareItemType == constantsXml.fareItemType.productTicket) ? constantsXml.fareItemCategory.fareProduct : constantsXml.fareItemCategory.fareJourney,
                        fareItemType: constantsXml.fareItemType[ticket.fareItemType],
                        fareItemExternalId: ticket.fareItemId,
                        fareItemIssuedNumber: ticket.fareItemIssuedNumber,
                        fareItemIssuedExpirationTimeStamp: moment.utc(ticket.expirationTimestamp).format(this.dateTTimeFormat),
                        fareItemName: ticket.fareItemName,
                        // payingRiderTypeExternalId: account.riderTypeId,
                        // payingRiderTypeName: account.type,
                        // payingRiderAccountId: account.shortId,
                        cumulativeFareOrProductPrice: ticket.price,
                        penaltyFee: 0,
                        paymentInstrument: constantsXml.paymentInstrument.none,
                        paymentMethod: constantsXml.paymentMethod.manualyCollected,
                        currencyCode: global.currentPosData.currencyCode,
                        passengerDetails: {
                            passengerRiderTypeExternalId: ticket.riderExternalId,
                            passengerRiderTypeName: ticket.riderName,
                            noOfPassengers: 1,
                            passengerBaseFare: ticket.price,
                        }
                    },
                    ticket: {
                        ticketIssueTimestamp: moment.utc(ticket.issueTime).format(this.dateTTimeFormat),
                        ticketSerialNumber: ticket.fareItemIssuedNumber,
                        fareItemType: ticket.fareItemType,
                        issuingDeviceId: global.currentPosData.deviceId,
                        issuingDeviceType: constantsXml.deviceType.officePos,
                        ticketExpiryTimestamp: moment.utc(ticket.expirationTimestamp).format(this.dateTTimeFormat),
                        ticketType: constantsXml.transactionTicketType.singlePassenger,
                        passengerRiderTypeExternalIdFirst: ticket.riderExternalId,
                        noOfPassengersFirst: 1,
                        fareItemExternalId: ticket.fareItemExternalId,
                        fareItemIssuedNumber: ticket.fareItemIssuedNumber,
                        issuedBoardingStopCode: 0,
                        issuedAlightingStopCode: 0,
                        issuedBoardingStationCode: 0,
                        issuedAlightingStationCode: 0,
                        allowedStagesOrZonesNumber: ticket.zonesLeft,
                        routeTypeOrServiceTypeExternalId: (route) ? route.externalId : 0,
                        routeTypeOrServiceTypeName: (route) ? route.name : '',
                        issuedFormatType: constantsXml.issuedFormatType.permanentQrCodeAccountStored
                    }
                }
            });

            res.status(200).json();
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    translations = async (req, res) => {
        try {
            const generateTranslations = (typeof req.query.generate != 'undefined') ? true : false;
            const languageTranslations = (typeof req.query.language != 'undefined') ? true : false;

            const translations1st = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'translations', global.currentPosData.firstLanguage+'.json')));
            const translations2nd = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'translations', global.currentPosData.secondLanguage+'.json')));

            let translationsList = {[global.currentPosData.firstLanguage.split('-')[0]]: {}, [global.currentPosData.secondLanguage.split('-')[0]]: {}};

            for (const [key, value] of Object.entries(translations1st)) {
                translationsList[global.currentPosData.firstLanguage.split('-')[0]][key] = translations1st[key];
                translationsList[global.currentPosData.secondLanguage.split('-')[0]][key] = translations2nd[key];
            }

            // http://localhost:1000/api/ticket/translations?generate
            if (generateTranslations) {
                await this.generateTranslations();
            }

            res.status(200).json(translationsList);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    generateTranslations = async () => {
        try {
            log.info('generate-translations-start');
            // generate translations https://translate.i18next.com
            const translationsTransform = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'files', 'trans.json')));
            let translationsTransformList = [];
            let translationsNoKeys = [];

            const translationsTransformEnLen = translationsTransform[global.currentPosData.firstLanguage.split('-')[0]].length;
            for (let i=0; i<translationsTransformEnLen; i++) {
                let k = translationsTransform[global.currentPosData.firstLanguage.split('-')[0]][i].item;
                let a = await translationsTransform[global.currentPosData.secondLanguage.split('-')[0]].find((f) => f.item == k);

                if (!a) { translationsNoKeys.push(k); } // no translations keys

                translationsTransformList.push({
                    key: k,
                    translations: {
                        [global.currentPosData.firstLanguage]: translationsTransform[global.currentPosData.firstLanguage.split('-')[0]][i]['translation'],
                        [global.currentPosData.secondLanguage]: (!a) ? translationsTransform[global.currentPosData.firstLanguage.split('-')[0]][i]['translation'] : a['translation']
                    }
                });
            }

            fs.writeFileSync(path.join(process.cwd(), 'files', 'translations.json'), JSON.stringify({translations: translationsTransformList}, null, 4));
            fs.unlinkSync(path.join(process.cwd(), 'files', 'trans.json'));

            log.info('translations-no-keys', translationsNoKeys);
            log.info('generate-translations-end');
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    accountLanguages = async (req, res) => {
        try {
            const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
            const riderLanguages = await this.fareEnvironmentModel.RiderLanguages.find({fareEnvironmentConfigurationId: activeConfig._id});

            let accountLanguages = [];
            let riderLanguagesLen = riderLanguages.length;
            for (let i=0; i<riderLanguagesLen; i++) {
                accountLanguages.push({
                    languageCode: riderLanguages[i].languageCode,
                    description: riderLanguages[i].description,
                    isDefaultLanguage: riderLanguages[i].isDefaultLanguage
                });
            }

            res.status(200).json(accountLanguages);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    searchQr = async (req, res) => {
        try {
            const searchQr = req.body.value;

            const qrcodeData = await this.service.readQrCode(searchQr, false);

            res.status(200).json(qrcodeData.accountId);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    readSmartCard = async (req, res) => {
        try {
            const readSmartCard = await this.smartCardServ.getPiccCardData();

            res.status(200).json(readSmartCard);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    initializeSmartCard = async (req, res) => {
        try {
            const smartCardData = req.body;
            const currentTimestamp = moment.utc().format(this.dateTTimeFormat);

            const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
            const transitTypeDef = await this.fareEnvironmentModel.TransitTypes.findOne({fareEnvironmentConfigurationId: activeConfig._id}).sort({externalId: 1});
            const riderType = await this.fareTableModel.RiderTypes.findOne({fareEnvironmentConfigurationId: activeConfig._id, type: 'Rider', isDefault: true});

            const smartCardProfiles = await this.fareTableModel.SmartCardProfiles.find({fareEnvironmentConfigurationId: activeConfig._id});
            const smartCardProfile = smartCardProfiles.find((f) => f.cardTypeShortNumber == smartCardData.content.cardType && f.cardVersionShortNumber == smartCardData.content.cardVersion && f.isEnabled);
            const cardVersion = (smartCardProfile) ? smartCardProfile.mediaPurposeType : constantsXml.smartCardVersion.anonymous.toUpperCase();
            const cardType = (smartCardProfile) ? smartCardProfile.cardType : constantsXml.smartCardType.standard;

            let contracts = [];

            let contractNo = 1;
            const initializeValues = {
                injectKeys: false,
                cardId: smartCardData.uid,
                cardType: parseInt(smartCardProfile.cardTypeShortNumber),
                cardVersion: parseInt(smartCardProfile.cardVersionShortNumber),
                issuerId: global.currentPosData.organizationExternalId,
                cardExpirationTimestamp: moment.utc().add(10, 'years').format('X'),
                currencyCode: constantsXml.currencyHexCode[global.currentPosData.currencyCode],
                preferredLanguage: constantsXml.languageHexCode[global.currentPosData.firstLanguage.split('-')[0]],
                riderTypeExternalId: riderType.externalId
            };

            const initializePiccCardData = await this.smartCardServ.initializePersonalizedCard(initializeValues);
            const cardPrintedId = await this.smartCardServ.getCardPrintedId(initializePiccCardData.cardUid);
            //const cardIteration = await this.smartCardServ.cardIteration(smartCardData.content.cardIteration + 1);

            await this.posTransactionCtrl.create({
                function: 'smartCardIinitializeIssue',
                component: 'smartCardReader',
                parameters: {
                    fare: {
                        externalId: transitTypeDef.externalId,
                        name: transitTypeDef.name,
                        fareSystemModel: transitTypeDef.fareSystemModel,
                        routeSystemType: transitTypeDef.routeSystemType
                    },
                    financial: {
                        activityType: constantsXml.activityType.manageSmartCard,
                        fareItemCategory: constantsXml.fareItemCategory.initSmartCard,
                        fareItemType: constantsXml.fareItemType.initSmartCard,
                        fareItemExternalId: 0,
                        fareItemName: constantsXml.fareItemName.na,
                        payingRiderTypeExternalId: riderType.externalId,
                        payingRiderTypeName: riderType.name,
                        cumulativeFareOrProductPrice: 0,
                        totalPaymentInCents: 0,
                        totalVatInCents: 0,
                        vatAddedToTotalPayment: true,
                        penaltyFee: 0,
                        paymentInstrument: constantsXml.paymentInstrument.none,
                        paymentMethod: constantsXml.paymentMethod.none,
                        currencyCode: global.currentPosData.currencyCode,
                        paymentInstrumentTransactionId: 0
                    },
                    card: {
                        // card reference
                        cardUid: initializePiccCardData.cardUid,
                        cardPrintedId: cardPrintedId,
                        cardType: cardType,
                        cardTypeShortNumber: initializePiccCardData.cardType,
                        cardVersion: cardVersion,
                        cardVersionShortNumber: initializePiccCardData.cardVersion,
                        cardIteration: initializePiccCardData.cardIteration,
                        issuerId: initializePiccCardData.issuerId,
                        transactionCounter: initializePiccCardData.transactionCounter,
                        // card snapshot
                        timestamp: currentTimestamp,
                        currencyCode: initializePiccCardData.currencyCode,
                        preferredLanguage: initializePiccCardData.preferredLanguage,
                        riderTypeExternalId: initializePiccCardData.riderTypeExternalId,
                        storedValue: initializePiccCardData['contractValue'+contractNo],
                        refundableDeposit: initializePiccCardData.refundableDeposit,
                        cardExpirationTimestamp: initializePiccCardData.cardExpirationTimestamp,
                        alic: initializePiccCardData.alic,
                        alwc: initializePiccCardData.alwc,
                        blockStatus: initializePiccCardData.blockStatus,
                        blockTimestamp: initializePiccCardData.blockTimestamp,
                        contracts: contracts,
                        // card operation
                        issuing: constantsXml.smartCardIssueType.issue
                    }
                }
            });

            res.status(200).json(initializePiccCardData);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    actionListSmartCard = async (req, res) => {
        try {
            const smartCardData = req.body;
            const actionList = await this.smartCardActionsModel.find({cardUid: smartCardData.cardUid}).sort({sequenceNumber: 1});
            let actions = {
                immediateActions: false,
                waitingActions: false
            };

            if (actionList.length) {
                const immediateActionList = actionList.filter((f) => f.queue == false);
                if (immediateActionList.length) {
                    const immediateCounter = await this.immediateActions(smartCardData, immediateActionList);
                    actions.immediateActions = (immediateActionList.length == immediateCounter) ? true : false;
                }

                const waitingActionList = actionList.filter((f) => f.queue == true);
                if (waitingActionList.length) {
                    const waitingCounter = await this.waitingActions(smartCardData, waitingActionList);
                    actions.waitingActions = (waitingActionList.length == waitingCounter) ? true : false;
                }
            }

            res.status(200).json(actions);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    immediateActions = async (smartCardData, actionList) => {
        const currentTimestamp = moment.utc().format(this.dateTTimeFormat);

        const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
        const riderTypes = await this.fareTableModel.RiderTypes.find({fareEnvironmentConfigurationId: activeConfig._id, type: 'Rider'});
        const riderType = await this.fareTableModel.RiderTypes.findOne({fareEnvironmentConfigurationId: activeConfig._id, type: 'Rider', isDefault: true});
        const products = await this.fareTableModel.Products.find({fareEnvironmentConfigurationId: activeConfig._id});
        const vatRates = await this.fareTableModel.VatRates.find({fareEnvironmentConfigurationId: activeConfig._id});
        const vatRule = await this.fareTableModel.VatRules.findOne({fareEnvironmentConfigurationId: activeConfig._id, paymentInstrumentType: constantsXml.paymentInstrument.prepaySmartCardEPurse});

        const smartCardProfiles = await this.fareTableModel.SmartCardProfiles.find({fareEnvironmentConfigurationId: activeConfig._id});

        let immediateActionsCounter = 0;

        if (actionList.length) {

            let smartCardContracts = await this.smartCardContracts(smartCardData);

            const cardPrintedId = await this.smartCardServ.getCardPrintedId(smartCardData.cardUid);
            const smartCardProfile = smartCardProfiles.find((f) => f.cardTypeShortNumber == smartCardData.cardType && f.cardVersionShortNumber == smartCardData.cardVersion && f.isEnabled);
            const cardVersion = (smartCardProfile) ? smartCardProfile.cardVersion : constantsXml.smartCardVersion.anonymous;
            const cardType = (smartCardProfile) ? smartCardProfile.cardType : constantsXml.smartCardType.standard;
            const cardTechnology = (smartCardProfile) ? smartCardProfile.cardTechnology : constantsXml.smartCardPhysicalType.mifareDesfireEv3;

            for (let i=0; i<actionList.length; i++) {

                if (actionList[i].queueCounter > smartCardData.alic) {

                    if (actionList[i].action == 'CardBlock') {

                        const blockStatus = await this.smartCardServ.blockStatus({
                            blockStatus: enumsXml.smartCardBlockStatus.blocked,
                            blockTimestamp: moment.utc().format('X')
                        });
                        const cardCounters = await this.smartCardServ.cardCounters({
                            transactionCounter: smartCardData.transactionCounter + 1,
                            countersTimestamp: moment.utc().format('X'),
                            alic: actionList[i].queueCounter
                        });

                        await this.posTransactionCtrl.create({
                            function: 'smartCardAction',
                            component: 'smartCardReader',
                            parameters: {
                                financial: {
                                    activityType: constantsXml.activityType.actionExecuted,
                                    fareItemCategory: constantsXml.fareItemCategory.blockType,
                                    fareItemType: constantsXml.fareItemType.blockSmartCard,
                                    fareItemExternalId: 0,
                                    fareItemProductType: '',
                                    fareItemIssuedNumber: 0,
                                    fareItemIssuedExpirationTimeStamp: '',
                                    fareItemPurchasedStagesOrZones: 0,
                                    fareItemName: constantsXml.fareItemName.na,
                                    payingRiderTypeExternalId: riderType.externalId,
                                    payingRiderTypeName: riderType.name,
                                    payingRiderAccountId: '',
                                    cumulativeFareOrProductPrice: 0,
                                    totalPaymentInCents: 0,
                                    totalVatInCents: 0,
                                    vatAddedToTotalPayment: true,
                                    penaltyFee: 0,
                                    paymentInstrument: constantsXml.paymentInstrument.none,
                                    paymentMethod: constantsXml.paymentMethod.none,
                                    currencyCode: global.currentPosData.currencyCode,
                                    paymentInstrumentTransactionId: 0,
                                    relatedTransactionInformation: {
                                        relatedTransactionTimestamp: currentTimestamp,
                                        relatedTransactionDeviceType: constantsXml.deviceType.officePos,
                                        relatedTransactionDeviceId: global.currentPosData.deviceId,
                                        relatedTransactionTerminalId: global.currentPosData.terminalId,
                                        relatedTransactionDeviceTransactionCounter: global.currentPosData.transactionCounter
                                    }
                                },
                                card: {
                                    // card reference
                                    cardUid: smartCardData.cardUid,
                                    cardPrintedId: cardPrintedId,
                                    cardType: cardType,
                                    cardTypeShortNumber: smartCardData.cardType,
                                    cardVersion: cardVersion,
                                    cardVersionShortNumber: smartCardData.cardVersion,
                                    cardIteration: smartCardData.cardIteration,
                                    physicalCardType: cardTechnology,
                                    issuerId: smartCardData.issuerId,
                                    transactionCounter: smartCardData.transactionCounter + 1,
                                    // card snapshot
                                    timestamp: currentTimestamp,
                                    currencyCode: smartCardData.currencyCode,
                                    preferredLanguage: smartCardData.preferredLanguage,
                                    riderTypeExternalId: smartCardData.riderTypeExternalId,
                                    storedValue: smartCardData.contractValue1,
                                    refundableDeposit: smartCardData.refundableDeposit,
                                    cardExpirationTimestamp: smartCardData.cardExpirationTimestamp,
                                    alic: actionList[i].queueCounter,
                                    alwc: smartCardData.alwc,
                                    blockStatus: constantsXml.smartCardBlockStatus[enumsXml.smartCardBlockStatus.blocked],
                                    blockTimestamp: currentTimestamp,
                                    contracts: smartCardContracts.contracts,
                                    // card operation
                                    cardBlock: ''
                                },
                                action: {
                                    sequenceNumber: actionList[i].sequenceNumber,
                                    name: 'cardBlock',
                                    success: true,
                                    keptInQueue: false,
                                    alic: actionList[i].queueCounter,
                                    alwc: smartCardData.alwc
                                }
                            }
                        });

                        immediateActionsCounter++;

                    } else if (actionList[i].action == 'CardUnblock') {

                        const blockStatus = await this.smartCardServ.blockStatus({
                            blockStatus: enumsXml.smartCardBlockStatus.notBlocked,
                            blockTimestamp: moment.utc().format('X')
                        });
                        const cardCounters = await this.smartCardServ.cardCounters({
                            transactionCounter: smartCardData.transactionCounter + 1,
                            countersTimestamp: moment.utc().format('X'),
                            alic: actionList[i].queueCounter
                        });

                        await this.posTransactionCtrl.create({
                            function: 'smartCardAction',
                            component: 'smartCardReader',
                            parameters: {
                                financial: {
                                    activityType: constantsXml.activityType.actionExecuted,
                                    fareItemCategory: constantsXml.fareItemCategory.blockType,
                                    fareItemType: constantsXml.fareItemType.unblockSmartCard,
                                    fareItemExternalId: 0,
                                    fareItemProductType: '',
                                    fareItemIssuedNumber: 0,
                                    fareItemIssuedExpirationTimeStamp: '',
                                    fareItemPurchasedStagesOrZones: 0,
                                    fareItemName: constantsXml.fareItemName.na,
                                    payingRiderTypeExternalId: riderType.externalId,
                                    payingRiderTypeName: riderType.name,
                                    payingRiderAccountId: '',
                                    cumulativeFareOrProductPrice: 0,
                                    totalPaymentInCents: 0,
                                    totalVatInCents: 0,
                                    vatAddedToTotalPayment: true,
                                    penaltyFee: 0,
                                    paymentInstrument: constantsXml.paymentInstrument.none,
                                    paymentMethod: constantsXml.paymentMethod.none,
                                    currencyCode: global.currentPosData.currencyCode,
                                    paymentInstrumentTransactionId: 0,
                                    relatedTransactionInformation: {
                                        relatedTransactionTimestamp: currentTimestamp,
                                        relatedTransactionDeviceType: constantsXml.deviceType.officePos,
                                        relatedTransactionDeviceId: global.currentPosData.deviceId,
                                        relatedTransactionTerminalId: global.currentPosData.terminalId,
                                        relatedTransactionDeviceTransactionCounter: global.currentPosData.transactionCounter
                                    }
                                },
                                card: {
                                    // card reference
                                    cardUid: smartCardData.cardUid,
                                    cardPrintedId: cardPrintedId,
                                    cardType: cardType,
                                    cardTypeShortNumber: smartCardData.cardType,
                                    cardVersion: cardVersion,
                                    cardVersionShortNumber: smartCardData.cardVersion,
                                    cardIteration: smartCardData.cardIteration,
                                    physicalCardType: cardTechnology,
                                    issuerId: smartCardData.issuerId,
                                    transactionCounter: smartCardData.transactionCounter + 1,
                                    // card snapshot
                                    timestamp: currentTimestamp,
                                    currencyCode: smartCardData.currencyCode,
                                    preferredLanguage: smartCardData.preferredLanguage,
                                    riderTypeExternalId: smartCardData.riderTypeExternalId,
                                    storedValue: smartCardData.contractValue1,
                                    refundableDeposit: smartCardData.refundableDeposit,
                                    cardExpirationTimestamp: smartCardData.cardExpirationTimestamp,
                                    alic: actionList[i].queueCounter,
                                    alwc: smartCardData.alwc,
                                    blockStatus: constantsXml.smartCardBlockStatus[enumsXml.smartCardBlockStatus.notBlocked],
                                    blockTimestamp: currentTimestamp,
                                    contracts: smartCardContracts.contracts,
                                    // card operation
                                    cardUnblock: ''
                                },
                                action: {
                                    sequenceNumber: actionList[i].sequenceNumber,
                                    name: 'cardUnblock',
                                    success: true,
                                    keptInQueue: false,
                                    alic: actionList[i].queueCounter,
                                    alwc: smartCardData.alwc
                                }
                            }
                        });

                        immediateActionsCounter++;

                    } else if (actionList[i].action == 'contractBlock') {

                        immediateActionsCounter++;

                    } else if (actionList[i].action == 'contractUnblock') {

                        immediateActionsCounter++;

                    } else if (actionList[i].action == 'recreateCard') {

                        const cardCounters = await this.smartCardServ.cardCounters({
                            transactionCounter: smartCardData.transactionCounter + 1,
                            countersTimestamp: moment.utc().format('X'),
                            alic: actionList[i].queueCounter
                        });

                        //const cardIteration = await this.smartCardServ.cardIteration(smartCardData.cardIteration + 1);

                        let cardContracts = [];
                        const actionListContracts = actionList[i].actionParams.card.contracts.contract;
                        for (let j=0; j<actionListContracts.length; j++) {
                            let topupProduct = products.find((f) => f.externalId == actionListContracts[j].contractID);

                            cardContracts.push({
                                contractId: actionListContracts[j].contractID,
                                instanceId: actionListContracts[j].instanceID,
                                contractValue: (actionListContracts[j].type == 'ePurse') ? actionList[i].actionParams.card.storedValue : (actionListContracts[j].type == 'storedRides') ? actionListContracts[j].ridesLeft : enumsXml.smartCardContractValue.active,
                                contractCategory: actionListContracts[j].type,
                                contractStatus: actionListContracts[j].status,
                                contractStartTimestamp: actionListContracts[j].creationTime,
                                contractExpirationTimestamp: actionListContracts[j].expiryTime,
                                contractStateChangeTimestamp: actionListContracts[j].lastUsageTime
                            });
                        }

                        await this.posTransactionCtrl.create({
                            function: 'smartCardAction',
                            component: 'smartCardReader',
                            parameters: {
                                financial: {
                                    activityType: constantsXml.activityType.actionExecuted,
                                    fareItemCategory: constantsXml.fareItemCategory.initSmartCard,
                                    fareItemType: constantsXml.fareItemType.reIssueSmartCard,
                                    fareItemExternalId: 0,
                                    fareItemProductType: '',
                                    fareItemIssuedNumber: 0,
                                    fareItemIssuedExpirationTimeStamp: '',
                                    fareItemPurchasedStagesOrZones: 0,
                                    fareItemName: constantsXml.fareItemName.na,
                                    payingRiderTypeExternalId: riderType.externalId,
                                    payingRiderTypeName: riderType.name,
                                    payingRiderAccountId: '',
                                    cumulativeFareOrProductPrice: 0,
                                    totalPaymentInCents: 0,
                                    totalVatInCents: 0,
                                    vatAddedToTotalPayment: true,
                                    penaltyFee: 0,
                                    paymentInstrument: constantsXml.paymentInstrument.none,
                                    paymentMethod: constantsXml.paymentMethod.none,
                                    currencyCode: global.currentPosData.currencyCode,
                                    paymentInstrumentTransactionId: 0,
                                    relatedTransactionInformation: {
                                        relatedTransactionTimestamp: currentTimestamp,
                                        relatedTransactionDeviceType: constantsXml.deviceType.officePos,
                                        relatedTransactionDeviceId: global.currentPosData.deviceId,
                                        relatedTransactionTerminalId: global.currentPosData.terminalId,
                                        relatedTransactionDeviceTransactionCounter: global.currentPosData.transactionCounter
                                    }
                                },
                                card: {
                                    // card reference
                                    cardUid: smartCardData.cardUid,
                                    cardUidOld: actionList[i].cardUidOld,
                                    cardPrintedId: cardPrintedId,
                                    cardType: cardType,
                                    cardTypeShortNumber: smartCardData.cardType,
                                    cardVersion: cardVersion,
                                    cardVersionShortNumber: smartCardData.cardVersion,
                                    cardIteration: smartCardData.cardIteration,
                                    physicalCardType: cardTechnology,
                                    issuerId: smartCardData.issuerId,
                                    transactionCounter: smartCardData.transactionCounter + 1,
                                    // card snapshot
                                    timestamp: currentTimestamp,
                                    currencyCode: smartCardData.currencyCode,
                                    preferredLanguage: smartCardData.preferredLanguage,
                                    riderTypeExternalId: smartCardData.riderTypeExternalId,
                                    storedValue: smartCardData.contractValue1,
                                    refundableDeposit: smartCardData.refundableDeposit,
                                    cardExpirationTimestamp: smartCardData.cardExpirationTimestamp,
                                    alic: actionList[i].queueCounter,
                                    alwc: smartCardData.alwc,
                                    blockStatus: constantsXml.smartCardBlockStatus[enumsXml.smartCardBlockStatus.notBlocked],
                                    blockTimestamp: currentTimestamp,
                                    contracts: cardContracts, // smartCardContracts.contracts,
                                    // card operation
                                    issuing: constantsXml.smartCardIssueType.reIssue
                                },
                                action: {
                                    sequenceNumber: actionList[i].sequenceNumber,
                                    name: 'recreateCard',
                                    success: true,
                                    keptInQueue: false,
                                    alic: actionList[i].queueCounter,
                                    alwc: smartCardData.alwc
                                }
                            }
                        });

                        immediateActionsCounter++;

                    }

                }

            }
        }

        return immediateActionsCounter;
    }

    waitingActions = async (smartCardData, actionList) => {
        const currentTimestamp = moment.utc().format(this.dateTTimeFormat);

        const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
        const transitTypeDef = await this.fareEnvironmentModel.TransitTypes.findOne({fareEnvironmentConfigurationId: activeConfig._id}).sort({externalId: 1});
        const fare = {
            externalId: transitTypeDef.externalId,
            name: transitTypeDef.name,
            fareSystemModel: transitTypeDef.fareSystemModel,
            routeSystemType: transitTypeDef.routeSystemType
        };
        const routeDef = await this.fareEnvironmentModel.Routes.findOne({fareEnvironmentConfigurationId: activeConfig._id}).sort({externalId: 1});
        const avl = {
            externalId: routeDef.externalId,
            name: routeDef.name,
            destination: routeDef.destination,
            variant: routeDef.variant,
            version: routeDef.version
        };
        avl.locationLongitude = global.currentPosData.locationLongitude;
        avl.locationLatitude = global.currentPosData.locationLatitude;
        const riderTypes = await this.fareTableModel.RiderTypes.find({fareEnvironmentConfigurationId: activeConfig._id, type: 'Rider'});
        const riderType = await this.fareTableModel.RiderTypes.findOne({fareEnvironmentConfigurationId: activeConfig._id, type: 'Rider', isDefault: true});
        const products = await this.fareTableModel.Products.find({fareEnvironmentConfigurationId: activeConfig._id});
        const vatRates = await this.fareTableModel.VatRates.find({fareEnvironmentConfigurationId: activeConfig._id});
        const vatRule = await this.fareTableModel.VatRules.findOne({fareEnvironmentConfigurationId: activeConfig._id, paymentInstrumentType: constantsXml.paymentInstrument.prepaySmartCardEPurse});

        const smartCardProfiles = await this.fareTableModel.SmartCardProfiles.find({fareEnvironmentConfigurationId: activeConfig._id});

        const reasonConfiguration = await this.reasonsServ.reasonConfiguration();

        let waitingActionsCounter = 0;

        if (actionList.length) {

            let smartCardContracts = await this.smartCardContracts(smartCardData);

            for (let i=0; i<actionList.length; i++) {

                if (actionList[i].queueCounter > smartCardData.alwc) {
                    let topupProduct = products.find((f) => f.externalId == actionList[i].actionValue);
                    if (!topupProduct) {
                        topupProduct = {
                            externalId: actionList[i].actionValue,
                            name: constantsXml.fareItemName.na,
                            priceInCents: 0,
                            activateOn: '',
                            productType: '',
                            numberOfRides: 0
                        };
                    }

                    let paymentInstrument = constantsXml.paymentInstrument.none;
                    let paymentMethod = constantsXml.paymentMethod.automaticalyCollected;
                    let fareItemIssuedNumber = this.helpersCtrl.getFareItemIssuedNumber(global.currentPosData.terminalId, global.currentPosData.transactionCounter);

                    const vatRate = (vatRates.find((f) => [topupProduct.productType].includes(f.associatedProductTypes))) ? vatRates.find((f) => [topupProduct.productType].includes(f.associatedProductTypes)) : vatRates.find((f) => f.isDefault == true);
                    const vatAddedToTotal = true;
                    const vatRateInOneHundredths = (vatRate) ? vatRate.percents : 0;

                    let totalVat = (topupProduct) ? Math.round(topupProduct.priceInCents * (vatRateInOneHundredths / 10000) / (1 + (vatRateInOneHundredths / 10000))) : 0;

                    if (smartCardContracts.emptyContract) {

                        if (actionList[i].action == 'RemoveContract') {

                            const contractPiccCardData = await this.smartCardServ.getPiccCardData();

                            let contractList = [];
                            for (let i=2; i<=5; i++) {
                                contractList.push({
                                    slot: i,
                                    contract: contractPiccCardData['contractId'+i],
                                    instance: contractPiccCardData['instanceId'+i]
                                });
                            }

                            let contractNo = contractList.find((f) => f.contract == actionList[i].actionValue && f.instance == actionList[i].actionParams.instanceID).slot;
                            const removeContractValues = {
                                contractId: 0,
                                instanceId: '0000000000',
                                contractValue: enumsXml.smartCardContractValue.pending,
                                contractCategory: enumsXml.smartCardContractCategory.Undefined,
                                contractStatus: enumsXml.smartCardContractStatus.invalid,
                                contractStartTimestamp: moment.utc(0).format('X'),
                                contractExpirationTimestamp: moment.utc(0).format('X'),
                                contractStateChangeTimestamp: moment.utc(0).format('X')
                            };

                            const removeContract = await this.smartCardServ.removeContract(removeContractValues, contractNo);

                            const cardCounters = await this.smartCardServ.cardCounters({
                                transactionCounter: contractPiccCardData.transactionCounter + 1,
                                countersTimestamp: moment.utc().format('X')
                            });

                            const cardPrintedId = await this.smartCardServ.getCardPrintedId(contractPiccCardData.cardUid);
                            const smartCardProfile = smartCardProfiles.find((f) => f.cardTypeShortNumber == contractPiccCardData.cardType && f.cardVersionShortNumber == contractPiccCardData.cardVersion && f.isEnabled);
                            const cardVersion = (smartCardProfile) ? smartCardProfile.cardVersion : constantsXml.smartCardVersion.anonymous;
                            const cardType = (smartCardProfile) ? smartCardProfile.cardType : constantsXml.smartCardType.standard;
                            const cardTechnology = (smartCardProfile) ? smartCardProfile.cardTechnology : constantsXml.smartCardPhysicalType.mifareDesfireEv3;

                            smartCardContracts = await this.smartCardContracts(contractPiccCardData);

                            await this.posTransactionCtrl.create({
                                function: 'smartCardRemoveContract',
                                component: 'smartCardReader',
                                parameters: {
                                    financial: {
                                        activityType: constantsXml.activityType.manageSmartCard,
                                        fareItemCategory: constantsXml.fareItemCategory.initSmartCard,
                                        fareItemType: constantsXml.fareItemType.productSmartCard,
                                        fareItemExternalId: 0,
                                        fareItemProductType: '',
                                        fareItemIssuedNumber: 0,
                                        fareItemIssuedExpirationTimeStamp: '',
                                        fareItemPurchasedStagesOrZones: 0,
                                        fareItemName: constantsXml.fareItemName.na,
                                        payingRiderTypeExternalId: riderType.externalId,
                                        payingRiderTypeName: riderType.name,
                                        payingRiderAccountId: '',
                                        cumulativeFareOrProductPrice: 0,
                                        totalPaymentInCents: 0,
                                        totalVatInCents: totalVat,
                                        vatAddedToTotalPayment: vatAddedToTotal,
                                        penaltyFee: 0,
                                        paymentInstrument: paymentInstrument,
                                        paymentMethod: paymentMethod,
                                        currencyCode: global.currentPosData.currencyCode,
                                        paymentInstrumentTransactionId: 0,
                                        relatedTransactionInformation: {
                                            relatedTransactionTimestamp: currentTimestamp,
                                            relatedTransactionDeviceType: constantsXml.deviceType.officePos,
                                            relatedTransactionDeviceId: global.currentPosData.deviceId,
                                            relatedTransactionTerminalId: global.currentPosData.terminalId,
                                            relatedTransactionDeviceTransactionCounter: global.currentPosData.transactionCounter
                                        },
                                        reasonInformation: {
                                            reasonId: block.reason.id,
                                            reasonName: block.reason.name
                                        }
                                    },
                                    card: {
                                        // card reference
                                        cardUid: contractPiccCardData.cardUid,
                                        cardPrintedId: cardPrintedId,
                                        cardType: cardType,
                                        cardTypeShortNumber: contractPiccCardData.cardType,
                                        cardVersion: cardVersion,
                                        cardVersionShortNumber: contractPiccCardData.cardVersion,
                                        cardIteration: contractPiccCardData.cardIteration,
                                        physicalCardType: cardTechnology,
                                        issuerId: contractPiccCardData.issuerId,
                                        transactionCounter: contractPiccCardData.transactionCounter + 1,
                                        // card snapshot
                                        timestamp: currentTimestamp,
                                        currencyCode: contractPiccCardData.currencyCode,
                                        preferredLanguage: contractPiccCardData.preferredLanguage,
                                        riderTypeExternalId: contractPiccCardData.riderTypeExternalId,
                                        storedValue: contractPiccCardData.contractValue1,
                                        refundableDeposit: contractPiccCardData.refundableDeposit,
                                        cardExpirationTimestamp: contractPiccCardData.cardExpirationTimestamp,
                                        alic: contractPiccCardData.alic,
                                        alwc: contractPiccCardData.alwc + 1,
                                        blockStatus: contractPiccCardData.blockStatus,
                                        blockTimestamp: contractPiccCardData.blockTimestamp,
                                        contracts: smartCardContracts.contracts,
                                        // card operation
                                        removeContract: {
                                            contractType: constantsXml.contractType.undefined,
                                            productId: contractPiccCardData['contractId'+contractNo],
                                            contractId: contractPiccCardData['contractId'+contractNo],
                                            instanceId: contractPiccCardData['instanceId'+contractNo]
                                        }
                                    }
                                }
                            });

                            waitingActionsCounter++;

                        } else if (actionList[i].action == 'RechargeValue') {

                            waitingActionsCounter++;

                        } else if (actionList[i].action == 'refundableDeposit') {

                            const contractPiccCardData = await this.smartCardServ.getPiccCardData();

                            const changeRefundableDeposit = await this.smartCardServ.changeRefundableDeposit(actionList[i].actionValue);

                            const cardCounters = await this.smartCardServ.cardCounters({
                                transactionCounter: contractPiccCardData.transactionCounter + 1,
                                countersTimestamp: moment.utc().format('X'),
                                alwc: actionList[i].queueCounter
                            });

                            const cardPrintedId = await this.smartCardServ.getCardPrintedId(contractPiccCardData.cardUid);
                            const smartCardProfile = smartCardProfiles.find((f) => f.cardTypeShortNumber == contractPiccCardData.cardType && f.cardVersionShortNumber == contractPiccCardData.cardVersion && f.isEnabled);
                            const cardVersion = (smartCardProfile) ? smartCardProfile.cardVersion : constantsXml.smartCardVersion.anonymous;
                            const cardType = (smartCardProfile) ? smartCardProfile.cardType : constantsXml.smartCardType.standard;
                            const cardTechnology = (smartCardProfile) ? smartCardProfile.cardTechnology : constantsXml.smartCardPhysicalType.mifareDesfireEv3;

                            smartCardContracts = await this.smartCardContracts(contractPiccCardData);

                            await this.posTransactionCtrl.create({
                                function: 'smartCardAction',
                                component: 'smartCardReader',
                                parameters: {
                                    financial: {
                                        activityType: constantsXml.activityType.actionExecuted,
                                        fareItemCategory: constantsXml.fareItemCategory.initSmartCard,
                                        fareItemType: constantsXml.fareItemType.refundDepositSmartCard,
                                        fareItemExternalId: 0,
                                        fareItemProductType: '',
                                        fareItemIssuedNumber: 0,
                                        fareItemIssuedExpirationTimeStamp: '',
                                        fareItemPurchasedStagesOrZones: 0,
                                        fareItemName: constantsXml.fareItemName.na,
                                        payingRiderTypeExternalId: riderType.externalId,
                                        payingRiderTypeName: riderType.name,
                            			payingRiderAccountId: '',
                            			cumulativeFareOrProductPrice: 0,
                            			totalPaymentInCents: 0,
                            			totalVatInCents: totalVat,
                                        vatAddedToTotalPayment: vatAddedToTotal,
                                        penaltyFee: 0,
                                        paymentInstrument: paymentInstrument,
                                        paymentMethod: paymentMethod,
                                        currencyCode: global.currentPosData.currencyCode,
                                        paymentInstrumentTransactionId: 0,
                                        relatedTransactionInformation: {
                                            relatedTransactionTimestamp: currentTimestamp,
                                            relatedTransactionDeviceType: constantsXml.deviceType.officePos,
                                            relatedTransactionDeviceId: global.currentPosData.deviceId,
                                            relatedTransactionTerminalId: global.currentPosData.terminalId,
                                            relatedTransactionDeviceTransactionCounter: global.currentPosData.transactionCounter
                                        }
                                    },
                                    card: {
                                        // card reference
                                        cardUid: contractPiccCardData.cardUid,
                                        cardPrintedId: cardPrintedId,
                                        cardType: cardType,
                                        cardTypeShortNumber: contractPiccCardData.cardType,
                                        cardVersion: cardVersion,
                                        cardVersionShortNumber: contractPiccCardData.cardVersion,
                                        cardIteration: contractPiccCardData.cardIteration,
                                        physicalCardType: cardTechnology,
                                        issuerId: contractPiccCardData.issuerId,
                                        transactionCounter: contractPiccCardData.transactionCounter + 1,
                                        // card snapshot
                                        timestamp: currentTimestamp,
                                        currencyCode: contractPiccCardData.currencyCode,
                                        preferredLanguage: contractPiccCardData.preferredLanguage,
                                        riderTypeExternalId: contractPiccCardData.riderTypeExternalId,
                                        storedValue: contractPiccCardData.contractValue1,
                                        refundableDeposit: actionList[i].actionValue,
                                        cardExpirationTimestamp: contractPiccCardData.cardExpirationTimestamp,
                                        alic: contractPiccCardData.alic,
                                        alwc: actionList[i].queueCounter,
                                        blockStatus: contractPiccCardData.blockStatus,
                                        blockTimestamp: contractPiccCardData.blockTimestamp,
                                        contracts: smartCardContracts.contracts
                                        // card operation
                                    },
                                    action: {
                                        sequenceNumber: actionList[i].sequenceNumber,
                                        name: actionList[i].action,
                                        success: true,
                                        keptInQueue: false,
                                        alic: contractPiccCardData.alic,
                                        alwc: actionList[i].queueCounter
                                    }
                                }
                            });

                            waitingActionsCounter++;

                        } else if (actionList[i].action == 'changeRiderType') {

                            const contractPiccCardData = await this.smartCardServ.getPiccCardData();

                            const changeRiderType = riderTypes.find((f) => f.externalId == actionList[i].actionValue);

                            const changeRiderTypeExternalId = await this.smartCardServ.changeRiderTypeExternalId(changeRiderType.externalId);

                            const cardCounters = await this.smartCardServ.cardCounters({
                                transactionCounter: contractPiccCardData.transactionCounter + 1,
                                countersTimestamp: moment.utc().format('X'),
                                alwc: actionList[i].queueCounter
                            });

                            const cardPrintedId = await this.smartCardServ.getCardPrintedId(contractPiccCardData.cardUid);
                            const smartCardProfile = smartCardProfiles.find((f) => f.cardTypeShortNumber == contractPiccCardData.cardType && f.cardVersionShortNumber == contractPiccCardData.cardVersion && f.isEnabled);
                            const cardVersion = (smartCardProfile) ? smartCardProfile.cardVersion : constantsXml.smartCardVersion.anonymous;
                            const cardType = (smartCardProfile) ? smartCardProfile.cardType : constantsXml.smartCardType.standard;
                            const cardTechnology = (smartCardProfile) ? smartCardProfile.cardTechnology : constantsXml.smartCardPhysicalType.mifareDesfireEv3;

                            smartCardContracts = await this.smartCardContracts(contractPiccCardData);

                            await this.posTransactionCtrl.create({
                                function: 'smartCardAction',
                                component: 'smartCardReader',
                                parameters: {
                                    financial: {
                                        activityType: constantsXml.activityType.actionExecuted,
                                        fareItemCategory: constantsXml.fareItemCategory.initSmartCard,
                                        fareItemType: constantsXml.fareItemType.assignRiderTypeSmartCard,
                                        fareItemExternalId: 0,
                                        fareItemProductType: '',
                                        fareItemIssuedNumber: 0,
                                        fareItemIssuedExpirationTimeStamp: '',
                                        fareItemPurchasedStagesOrZones: 0,
                                        fareItemName: constantsXml.fareItemName.na,
                            			payingRiderTypeExternalId: changeRiderType.externalId,
                            			payingRiderTypeName: changeRiderType.name,
                            			payingRiderAccountId: '',
                            			cumulativeFareOrProductPrice: 0,
                            			totalPaymentInCents: 0,
                            			totalVatInCents: totalVat,
                                        vatAddedToTotalPayment: vatAddedToTotal,
                                        penaltyFee: 0,
                                        paymentInstrument: paymentInstrument,
                                        paymentMethod: paymentMethod,
                                        currencyCode: global.currentPosData.currencyCode,
                                        paymentInstrumentTransactionId: 0,
                                        relatedTransactionInformation: {
                                            relatedTransactionTimestamp: currentTimestamp,
                                            relatedTransactionDeviceType: constantsXml.deviceType.officePos,
                                            relatedTransactionDeviceId: global.currentPosData.deviceId,
                                            relatedTransactionTerminalId: global.currentPosData.terminalId,
                                            relatedTransactionDeviceTransactionCounter: global.currentPosData.transactionCounter
                                        }
                                    },
                                    card: {
                                        // card reference
                                        cardUid: contractPiccCardData.cardUid,
                                        cardPrintedId: cardPrintedId,
                                        cardType: cardType,
                                        cardTypeShortNumber: contractPiccCardData.cardType,
                                        cardVersion: cardVersion,
                                        cardVersionShortNumber: contractPiccCardData.cardVersion,
                                        cardIteration: contractPiccCardData.cardIteration,
                                        physicalCardType: cardTechnology,
                                        issuerId: contractPiccCardData.issuerId,
                                        transactionCounter: contractPiccCardData.transactionCounter + 1,
                                        // card snapshot
                                        timestamp: currentTimestamp,
                                        currencyCode: contractPiccCardData.currencyCode,
                                        preferredLanguage: contractPiccCardData.preferredLanguage,
                                        riderTypeExternalId: contractPiccCardData.riderTypeExternalId,
                                        storedValue: contractPiccCardData.contractValue1,
                                        refundableDeposit: contractPiccCardData.refundableDeposit,
                                        cardExpirationTimestamp: contractPiccCardData.cardExpirationTimestamp,
                                        alic: contractPiccCardData.alic,
                                        alwc: actionList[i].queueCounter,
                                        blockStatus: contractPiccCardData.blockStatus,
                                        blockTimestamp: contractPiccCardData.blockTimestamp,
                                        contracts: smartCardContracts.contracts,
                                        // card operation
                                        changeRiderType: {
                                            previousRiderType: contractPiccCardData.riderTypeExternalId,
                                            newRiderType: changeRiderType.externalId,
                                            newRiderTypeExpirationTimestamp: (changeRiderType.expirationDate) ? moment.utc(changeRiderType.expirationDate).format(this.dateTTimeFormat) : moment.utc(contractPiccCardData.cardExpirationTimestamp).format(this.dateTTimeFormat)
                                        }
                                    },
                                    action: {
                                        sequenceNumber: actionList[i].sequenceNumber,
                                        name: actionList[i].action,
                                        success: true,
                                        keptInQueue: false,
                                        alic: contractPiccCardData.alic,
                                        alwc: actionList[i].queueCounter
                                    }
                                }
                            });

                            waitingActionsCounter++;

                        } else if (actionList[i].action == 'AddEPurseContract') {
                            if (!topupProduct.productType) {
                                topupProduct.productType = constantsXml.smartCardContractType.ePurse;
                            }

                            let contractNo = 1;
                            let smartCardContractTimestamp = await this.smartCardContractTimestamp(products, topupProduct, smartCardData);
                            const ePurseValues = {
                                contractId: topupProduct.externalId,
                                instanceId: smartCardContracts.lastInstance,
                                contractValue: topupProduct.minimumPrice, // valueCents
                                contractCategory: enumsXml.smartCardContractCategory[topupProduct.productType],
                                contractStatus: (topupProduct.activateOn == 'FirstUse') ? enumsXml.smartCardContractStatus.pending : enumsXml.smartCardContractStatus.active,
                                contractStartTimestamp: smartCardContractTimestamp.startTimestamp,
                                contractExpirationTimestamp: moment.utc(smartCardData.cardExpirationTimestamp).format('X'),
                                contractStateChangeTimestamp: moment.utc().format('X')
                            };

                            const contractPiccCardData = await this.smartCardServ.loadContract(ePurseValues, contractNo);
                            const cardCounters = await this.smartCardServ.cardCounters({
                                transactionCounter: contractPiccCardData.transactionCounter + 1,
                                countersTimestamp: moment.utc().format('X'),
                                alwc: actionList[i].queueCounter
                            });
                            const topUpLogs = await this.smartCardServ.topUpLogs({
                                topupLogTransactionTimestamp: moment.utc().format('X'),
                                topupLogContractCategory: enumsXml.smartCardContractCategory[topupProduct.productType],
                                topupLogContractId: topupProduct.externalId,
                                topupLogContractPointer: contractNo,
                                topupLogFareItemCategory: enumsXml.fareItemCategoryType.fareEpurse,
                                topupLogFareItemType: enumsXml.fareItemType.createEpurseSmartCard,
                                topupLogActivityType: enumsXml.activityType.issue,
                                topupLogTerminalId: global.currentPosData.terminalId,
                                topupLogTerminalType: enumsXml.deviceType.officePos,
                                topupLogTransactionId: contractPiccCardData.transactionCounter + 1,
                                topupLogAmountCredited: 0,
                                topupLogMerchantNumber: 1,
                                topupLogPtoRference: 0
                            });
                            const cardPrintedId = await this.smartCardServ.getCardPrintedId(contractPiccCardData.cardUid);
                            const smartCardProfile = smartCardProfiles.find((f) => f.cardTypeShortNumber == contractPiccCardData.cardType && f.cardVersionShortNumber == contractPiccCardData.cardVersion && f.isEnabled);
                            const cardVersion = (smartCardProfile) ? smartCardProfile.cardVersion : constantsXml.smartCardVersion.anonymous;
                            const cardType = (smartCardProfile) ? smartCardProfile.cardType : constantsXml.smartCardType.standard;
                            const cardTechnology = (smartCardProfile) ? smartCardProfile.cardTechnology : constantsXml.smartCardPhysicalType.mifareDesfireEv3;

                            smartCardContracts = await this.smartCardContracts(contractPiccCardData);

                            if (actionList[i].createIssue) {
                                await this.posTransactionCtrl.create({
                                    function: 'smartCardContract',
                                    component: 'smartCardReader',
                                    parameters: {
                                        avl: avl,
                                        fare: fare,
                                        financial: {
                                            activityType: constantsXml.activityType.issue,
                                            fareItemCategory: constantsXml.fareItemCategory.fareEpurse,
                                            fareItemType: constantsXml.fareItemType.createEpurseSmartCard,
                                            fareItemExternalId: topupProduct.externalId,
                                            fareItemProductType: topupProduct.productType,
                                            fareItemIssuedNumber: fareItemIssuedNumber,
                                            fareItemIssuedExpirationTimeStamp: moment.utc(smartCardData.cardExpirationTimestamp).format(this.dateTTimeFormat),
                                            fareItemPurchasedStagesOrZones: 0,
                                            fareItemName: topupProduct.name,
                                            payingRiderTypeExternalId: riderType.externalId,
                                            payingRiderTypeName: riderType.name,
                                            payingRiderAccountId: '',
                                            cumulativeFareOrProductPrice: topupProduct.priceInCents,
                                            totalPaymentInCents: topupProduct.priceInCents,
                                            totalVatInCents: totalVat,
                                            vatAddedToTotalPayment: vatAddedToTotal,
                                            penaltyFee: 0,
                                            paymentInstrument: paymentInstrument,
                                            paymentMethod: paymentMethod,
                                            currencyCode: global.currentPosData.currencyCode,
                                            paymentInstrumentTransactionId: 0,
                                            relatedTransactionInformation: {
                                                relatedTransactionTimestamp: currentTimestamp,
                                                relatedTransactionDeviceType: constantsXml.deviceType.officePos,
                                                relatedTransactionDeviceId: global.currentPosData.deviceId,
                                                relatedTransactionTerminalId: global.currentPosData.terminalId,
                                                relatedTransactionDeviceTransactionCounter: global.currentPosData.transactionCounter
                                            },
                                            vatDetails: {
                                                taxableAmountInCents: topupProduct.priceInCents - totalVat,
                                                vatRate: vatRateInOneHundredths,
                                                vatInCents: totalVat
                                            }
                                        },
                                        card: {
                                            // card reference
                                            cardUid: contractPiccCardData.cardUid,
                                            cardPrintedId: cardPrintedId,
                                            cardType: cardType,
                                            cardTypeShortNumber: contractPiccCardData.cardType,
                                            cardVersion: cardVersion,
                                            cardVersionShortNumber: contractPiccCardData.cardVersion,
                                            cardIteration: contractPiccCardData.cardIteration,
                                            physicalCardType: cardTechnology,
                                            issuerId: contractPiccCardData.issuerId,
                                            transactionCounter: contractPiccCardData.transactionCounter + 1,
                                            // card snapshot
                                            timestamp: currentTimestamp,
                                            currencyCode: contractPiccCardData.currencyCode,
                                            preferredLanguage: contractPiccCardData.preferredLanguage,
                                            riderTypeExternalId: contractPiccCardData.riderTypeExternalId,
                                            storedValue: contractPiccCardData.contractValue1,
                                            refundableDeposit: contractPiccCardData.refundableDeposit,
                                            cardExpirationTimestamp: contractPiccCardData.cardExpirationTimestamp,
                                            alic: contractPiccCardData.alic,
                                            alwc: actionList[i].queueCounter,
                                            blockStatus: contractPiccCardData.blockStatus,
                                            blockTimestamp: contractPiccCardData.blockTimestamp,
                                            contracts: smartCardContracts.contracts,
                                            // card operation
                                            addContract: {
                                                contractType: constantsXml.contractType.ePurse,
                                                productId: contractPiccCardData['contractId'+contractNo],
                                                contractId: contractPiccCardData['contractId'+contractNo],
                                                instanceId: contractPiccCardData['instanceId'+contractNo],
                                                contractStatus: contractPiccCardData['contractStatus'+contractNo],
                                                contractStartTimestamp: contractPiccCardData['contractStartTimestamp'+contractNo],
                                                contractExpirationTimestamp: contractPiccCardData['contractExpirationTimestamp'+contractNo],
                                                contractStateChangeTimestamp: contractPiccCardData['contractStateChangeTimestamp'+contractNo],
                                                contractValue: contractPiccCardData['contractValue'+contractNo]
                                            }
                                        }
                                    }
                                });
                            }

                            await this.posTransactionCtrl.create({
                                function: 'smartCardAction',
                                component: 'smartCardReader',
                                parameters: {
                                    financial: {
                                        activityType: constantsXml.activityType.actionExecuted,
                                        fareItemCategory: constantsXml.fareItemCategory.fareEpurse,
                                        fareItemType: constantsXml.fareItemType.createEpurseSmartCard,
                                        fareItemExternalId: topupProduct.externalId,
                                        fareItemProductType: topupProduct.productType,
                                        fareItemIssuedNumber: fareItemIssuedNumber,
                                        fareItemIssuedExpirationTimeStamp: moment.utc(smartCardData.cardExpirationTimestamp).format(this.dateTTimeFormat),
                                        fareItemPurchasedStagesOrZones: 0,
                                        fareItemName: topupProduct.name,
                            			payingRiderTypeExternalId: riderType.externalId,
                            			payingRiderTypeName: riderType.name,
                            			payingRiderAccountId: '',
                            			cumulativeFareOrProductPrice: topupProduct.priceInCents,
                            			totalPaymentInCents: topupProduct.priceInCents,
                            			totalVatInCents: totalVat,
                                        vatAddedToTotalPayment: vatAddedToTotal,
                                        penaltyFee: 0,
                                        paymentInstrument: paymentInstrument,
                                        paymentMethod: paymentMethod,
                                        currencyCode: global.currentPosData.currencyCode,
                                        paymentInstrumentTransactionId: 0,
                                        relatedTransactionInformation: {
                                            relatedTransactionTimestamp: currentTimestamp,
                                            relatedTransactionDeviceType: constantsXml.deviceType.officePos,
                                            relatedTransactionDeviceId: global.currentPosData.deviceId,
                                            relatedTransactionTerminalId: global.currentPosData.terminalId,
                                            relatedTransactionDeviceTransactionCounter: global.currentPosData.transactionCounter
                                        },
                                        vatDetails: {
                                            taxableAmountInCents: topupProduct.priceInCents - totalVat,
                            				vatRate: vatRateInOneHundredths,
                            				vatInCents: totalVat
                                        }
                                    },
                                    card: {
                                        // card reference
                                        cardUid: contractPiccCardData.cardUid,
                                        cardPrintedId: cardPrintedId,
                                        cardType: cardType,
                                        cardTypeShortNumber: contractPiccCardData.cardType,
                                        cardVersion: cardVersion,
                                        cardVersionShortNumber: contractPiccCardData.cardVersion,
                                        cardIteration: contractPiccCardData.cardIteration,
                                        physicalCardType: cardTechnology,
                                        issuerId: contractPiccCardData.issuerId,
                                        transactionCounter: contractPiccCardData.transactionCounter + 1,
                                        // card snapshot
                                        timestamp: currentTimestamp,
                                        currencyCode: contractPiccCardData.currencyCode,
                                        preferredLanguage: contractPiccCardData.preferredLanguage,
                                        riderTypeExternalId: contractPiccCardData.riderTypeExternalId,
                                        storedValue: contractPiccCardData.contractValue1,
                                        refundableDeposit: contractPiccCardData.refundableDeposit,
                                        cardExpirationTimestamp: contractPiccCardData.cardExpirationTimestamp,
                                        alic: contractPiccCardData.alic,
                                        alwc: actionList[i].queueCounter,
                                        blockStatus: contractPiccCardData.blockStatus,
                                        blockTimestamp: contractPiccCardData.blockTimestamp,
                                        contracts: smartCardContracts.contracts,
                                        // card operation
                                        addContract: {
                                            contractType: constantsXml.contractType.ePurse,
                                            productId: contractPiccCardData['contractId'+contractNo],
                                            contractId: contractPiccCardData['contractId'+contractNo],
                                            instanceId: contractPiccCardData['instanceId'+contractNo],
                                            contractStatus: contractPiccCardData['contractStatus'+contractNo],
                                            contractStartTimestamp: contractPiccCardData['contractStartTimestamp'+contractNo],
                                            contractExpirationTimestamp: contractPiccCardData['contractExpirationTimestamp'+contractNo],
                                            contractStateChangeTimestamp: contractPiccCardData['contractStateChangeTimestamp'+contractNo],
                                            contractValue: contractPiccCardData['contractValue'+contractNo]
                                        }
                                    },
                                    action: {
                                        sequenceNumber: actionList[i].sequenceNumber,
                                        name: actionList[i].action,
                                        success: true,
                                        keptInQueue: false,
                                        alic: contractPiccCardData.alic,
                                        alwc: actionList[i].queueCounter
                                    }
                                }
                            });

                            waitingActionsCounter++;

                        } else if (actionList[i].action == 'addPeriodPass') {
                            if (!topupProduct.productType) {
                                topupProduct.productType = constantsXml.smartCardContractType.periodPass;
                            }

                            let contractNo = smartCardContracts.emptyContract;
                            let smartCardContractTimestamp = await this.smartCardContractTimestamp(products, topupProduct, smartCardData);
                            const periodPassValues = {
                                contractId: topupProduct.externalId,
                                instanceId: smartCardContracts.lastInstance,
                                contractValue: (topupProduct.activateOn == 'FirstUse') ? enumsXml.smartCardContractValue.pending : enumsXml.smartCardContractValue.active,
                                contractCategory: enumsXml.smartCardContractCategory[topupProduct.productType],
                                contractStatus: (topupProduct.activateOn == 'FirstUse') ? enumsXml.smartCardContractStatus.pending : enumsXml.smartCardContractStatus.active,
                                contractStartTimestamp: moment.utc(actionList[i].actionParams.creationDate).format('X'),
                                contractExpirationTimestamp: moment.utc(actionList[i].actionParams.expiryDate).format('X'),
                                contractStateChangeTimestamp: moment.utc().format('X')
                            };

                            const contractPiccCardData = await this.smartCardServ.loadContract(periodPassValues, contractNo);
                            const cardCounters = await this.smartCardServ.cardCounters({
                                transactionCounter: contractPiccCardData.transactionCounter + 1,
                                countersTimestamp: moment.utc().format('X'),
                                alwc: actionList[i].queueCounter
                            });
                            const topUpLogs = await this.smartCardServ.topUpLogs({
                                topupLogTransactionTimestamp: moment.utc().format('X'),
                                topupLogContractCategory: enumsXml.smartCardContractCategory[topupProduct.productType],
                                topupLogContractId: topupProduct.externalId,
                                topupLogContractPointer: contractNo,
                                topupLogFareItemCategory: enumsXml.fareItemCategoryType.fareProduct,
                                topupLogFareItemType: enumsXml.fareItemType.productSmartCard,
                                topupLogActivityType: enumsXml.activityType.issue,
                                topupLogTerminalId: global.currentPosData.terminalId,
                                topupLogTerminalType: enumsXml.deviceType.officePos,
                                topupLogTransactionId: contractPiccCardData.transactionCounter + 1,
                                topupLogAmountCredited: 0,
                                topupLogMerchantNumber: 1,
                                topupLogPtoRference: 0
                            });
                            const cardPrintedId = await this.smartCardServ.getCardPrintedId(contractPiccCardData.cardUid);
                            const smartCardProfile = smartCardProfiles.find((f) => f.cardTypeShortNumber == contractPiccCardData.cardType && f.cardVersionShortNumber == contractPiccCardData.cardVersion && f.isEnabled);
                            const cardVersion = (smartCardProfile) ? smartCardProfile.cardVersion : constantsXml.smartCardVersion.anonymous;
                            const cardType = (smartCardProfile) ? smartCardProfile.cardType : constantsXml.smartCardType.standard;
                            const cardTechnology = (smartCardProfile) ? smartCardProfile.cardTechnology : constantsXml.smartCardPhysicalType.mifareDesfireEv3;

                            smartCardContracts = await this.smartCardContracts(contractPiccCardData);

                            if (actionList[i].createIssue) {
                                await this.posTransactionCtrl.create({
                                    function: 'smartCardContract',
                                    component: 'smartCardReader',
                                    parameters: {
                                        avl: avl,
                                        fare: fare,
                                        financial: {
                                            activityType: constantsXml.activityType.issue,
                                            fareItemCategory: constantsXml.fareItemCategory.fareProduct,
                                            fareItemType: constantsXml.fareItemType.productSmartCard,
                                            fareItemExternalId: topupProduct.externalId,
                                            fareItemProductType: topupProduct.productType,
                                            fareItemIssuedNumber: fareItemIssuedNumber,
                                            fareItemIssuedExpirationTimeStamp: moment.utc(actionList[i].actionParams.expiryDate).format(this.dateTTimeFormat),
                                            fareItemPurchasedStagesOrZones: 0,
                                            fareItemName: topupProduct.name,
                                            payingRiderTypeExternalId: riderType.externalId,
                                            payingRiderTypeName: riderType.name,
                                            payingRiderAccountId: '',
                                            cumulativeFareOrProductPrice: topupProduct.priceInCents,
                                            totalPaymentInCents: topupProduct.priceInCents,
                                            totalVatInCents: totalVat,
                                            vatAddedToTotalPayment: vatAddedToTotal,
                                            penaltyFee: 0,
                                            paymentInstrument: paymentInstrument,
                                            paymentMethod: paymentMethod,
                                            currencyCode: global.currentPosData.currencyCode,
                                            paymentInstrumentTransactionId: 0,
                                            relatedTransactionInformation: {
                                                relatedTransactionTimestamp: currentTimestamp,
                                                relatedTransactionDeviceType: constantsXml.deviceType.officePos,
                                                relatedTransactionDeviceId: global.currentPosData.deviceId,
                                                relatedTransactionTerminalId: global.currentPosData.terminalId,
                                                relatedTransactionDeviceTransactionCounter: global.currentPosData.transactionCounter
                                            },
                                            vatDetails: {
                                                taxableAmountInCents: topupProduct.priceInCents - totalVat,
                                                vatRate: vatRateInOneHundredths,
                                                vatInCents: totalVat
                                            }
                                        },
                                        card: {
                                            // card reference
                                            cardUid: contractPiccCardData.cardUid,
                                            cardPrintedId: cardPrintedId,
                                            cardType: cardType,
                                            cardTypeShortNumber: contractPiccCardData.cardType,
                                            cardVersion: cardVersion,
                                            cardVersionShortNumber: contractPiccCardData.cardVersion,
                                            cardIteration: contractPiccCardData.cardIteration,
                                            physicalCardType: cardTechnology,
                                            issuerId: contractPiccCardData.issuerId,
                                            transactionCounter: contractPiccCardData.transactionCounter + 1,
                                            // card snapshot
                                            timestamp: currentTimestamp,
                                            currencyCode: contractPiccCardData.currencyCode,
                                            preferredLanguage: contractPiccCardData.preferredLanguage,
                                            riderTypeExternalId: contractPiccCardData.riderTypeExternalId,
                                            storedValue: contractPiccCardData.contractValue1,
                                            refundableDeposit: contractPiccCardData.refundableDeposit,
                                            cardExpirationTimestamp: contractPiccCardData.cardExpirationTimestamp,
                                            alic: contractPiccCardData.alic,
                                            alwc: contractPiccCardData.alwc + 1,
                                            blockStatus: contractPiccCardData.blockStatus,
                                            blockTimestamp: contractPiccCardData.blockTimestamp,
                                            contracts: smartCardContracts.contracts,
                                            // card operation
                                            addContract: {
                                                contractType: constantsXml.contractType.periodPass,
                                                productId: contractPiccCardData['contractId'+contractNo],
                                                contractId: contractPiccCardData['contractId'+contractNo],
                                                instanceId: contractPiccCardData['instanceId'+contractNo],
                                                contractStatus: contractPiccCardData['contractStatus'+contractNo],
                                                contractStartTimestamp: contractPiccCardData['contractStartTimestamp'+contractNo],
                                                contractExpirationTimestamp: contractPiccCardData['contractExpirationTimestamp'+contractNo],
                                                contractStateChangeTimestamp: contractPiccCardData['contractStateChangeTimestamp'+contractNo],
                                                contractValue: contractPiccCardData['contractValue'+contractNo]
                                            }
                                        }
                                    }
                                });
                            }

                            await this.posTransactionCtrl.create({
                                function: 'smartCardAction',
                                component: 'smartCardReader',
                                parameters: {
                                    financial: {
                                        activityType: constantsXml.activityType.actionExecuted,
                                        fareItemCategory: constantsXml.fareItemCategory.fareProduct,
                                        fareItemType: constantsXml.fareItemType.productSmartCard,
                                        fareItemExternalId: topupProduct.externalId,
                                        fareItemProductType: topupProduct.productType,
                                        fareItemIssuedNumber: fareItemIssuedNumber,
                                        fareItemIssuedExpirationTimeStamp: moment.utc(actionList[i].actionParams.expiryDate).format(this.dateTTimeFormat),
                                        fareItemPurchasedStagesOrZones: 0,
                                        fareItemName: topupProduct.name,
                                        payingRiderTypeExternalId: riderType.externalId,
                                        payingRiderTypeName: riderType.name,
                                        payingRiderAccountId: '',
                                        cumulativeFareOrProductPrice: topupProduct.priceInCents,
                                        totalPaymentInCents: topupProduct.priceInCents,
                                        totalVatInCents: totalVat,
                                        vatAddedToTotalPayment: vatAddedToTotal,
                                        penaltyFee: 0,
                                        paymentInstrument: paymentInstrument,
                                        paymentMethod: paymentMethod,
                                        currencyCode: global.currentPosData.currencyCode,
                                        paymentInstrumentTransactionId: 0,
                                        relatedTransactionInformation: {
                                            relatedTransactionTimestamp: currentTimestamp,
                                            relatedTransactionDeviceType: constantsXml.deviceType.officePos,
                                            relatedTransactionDeviceId: global.currentPosData.deviceId,
                                            relatedTransactionTerminalId: global.currentPosData.terminalId,
                                            relatedTransactionDeviceTransactionCounter: global.currentPosData.transactionCounter
                                        },
                                        vatDetails: {
                                            taxableAmountInCents: topupProduct.priceInCents - totalVat,
                                            vatRate: vatRateInOneHundredths,
                                            vatInCents: totalVat
                                        }
                                    },
                                    card: {
                                        // card reference
                                        cardUid: contractPiccCardData.cardUid,
                                        cardPrintedId: cardPrintedId,
                                        cardType: cardType,
                                        cardTypeShortNumber: contractPiccCardData.cardType,
                                        cardVersion: cardVersion,
                                        cardVersionShortNumber: contractPiccCardData.cardVersion,
                                        cardIteration: contractPiccCardData.cardIteration,
                                        physicalCardType: cardTechnology,
                                        issuerId: contractPiccCardData.issuerId,
                                        transactionCounter: contractPiccCardData.transactionCounter + 1,
                                        // card snapshot
                                        timestamp: currentTimestamp,
                                        currencyCode: contractPiccCardData.currencyCode,
                                        preferredLanguage: contractPiccCardData.preferredLanguage,
                                        riderTypeExternalId: contractPiccCardData.riderTypeExternalId,
                                        storedValue: contractPiccCardData.contractValue1,
                                        refundableDeposit: contractPiccCardData.refundableDeposit,
                                        cardExpirationTimestamp: contractPiccCardData.cardExpirationTimestamp,
                                        alic: contractPiccCardData.alic,
                                        alwc: actionList[i].queueCounter,
                                        blockStatus: contractPiccCardData.blockStatus,
                                        blockTimestamp: contractPiccCardData.blockTimestamp,
                                        contracts: smartCardContracts.contracts,
                                        // card operation
                                        addContract: {
                                            contractType: constantsXml.contractType.periodPass,
                                            productId: contractPiccCardData['contractId'+contractNo],
                                            contractId: contractPiccCardData['contractId'+contractNo],
                                            instanceId: contractPiccCardData['instanceId'+contractNo],
                                            contractStatus: contractPiccCardData['contractStatus'+contractNo],
                                            contractStartTimestamp: contractPiccCardData['contractStartTimestamp'+contractNo],
                                            contractExpirationTimestamp: contractPiccCardData['contractExpirationTimestamp'+contractNo],
                                            contractStateChangeTimestamp: contractPiccCardData['contractStateChangeTimestamp'+contractNo],
                                            contractValue: contractPiccCardData['contractValue'+contractNo]
                                        }
                                    },
                                    action: {
                                        sequenceNumber: actionList[i].sequenceNumber,
                                        name: actionList[i].action,
                                        success: true,
                                        keptInQueue: false,
                                        alic: contractPiccCardData.alic,
                                        alwc: actionList[i].queueCounter
                                    }
                                }
                            });

                            waitingActionsCounter++;

                        } else if (actionList[i].action == 'addStoredRides') {
                            if (!topupProduct.productType) {
                                topupProduct.productType = constantsXml.smartCardContractType.storedRides;
                            }

                            let contractNo = smartCardContracts.emptyContract;
                            let smartCardContractTimestamp = await this.smartCardContractTimestamp(products, topupProduct, smartCardData);
                            const storedRidesValues = {
                                contractId: topupProduct.externalId,
                                instanceId: smartCardContracts.lastInstance,
                                contractValue: topupProduct.numberOfRides,
                                contractCategory: enumsXml.smartCardContractCategory[topupProduct.productType],
                                contractStatus: (topupProduct.activateOn == 'FirstUse') ? enumsXml.smartCardContractStatus.pending : enumsXml.smartCardContractStatus.active,
                                contractStartTimestamp: smartCardContractTimestamp.startTimestamp,
                                contractExpirationTimestamp: smartCardContractTimestamp.expirationTimestamp,
                                contractStateChangeTimestamp: moment.utc().format('X')
                            };

                            const contractPiccCardData = await this.smartCardServ.loadContract(storedRidesValues, contractNo);
                            const cardCounters = await this.smartCardServ.cardCounters({
                                transactionCounter: contractPiccCardData.transactionCounter + 1,
                                countersTimestamp: moment.utc().format('X'),
                                alwc: actionList[i].queueCounter
                            });
                            const topUpLogs = await this.smartCardServ.topUpLogs({
                                topupLogTransactionTimestamp: moment.utc().format('X'),
                                topupLogContractCategory: enumsXml.smartCardContractCategory[topupProduct.productType],
                                topupLogContractId: topupProduct.externalId,
                                topupLogContractPointer: contractNo,
                                topupLogFareItemCategory: enumsXml.fareItemCategoryType.fareProduct,
                                topupLogFareItemType: enumsXml.fareItemType.productSmartCard,
                                topupLogActivityType: enumsXml.activityType.issue,
                                topupLogTerminalId: global.currentPosData.terminalId,
                                topupLogTerminalType: enumsXml.deviceType.officePos,
                                topupLogTransactionId: contractPiccCardData.transactionCounter + 1,
                                topupLogAmountCredited: 0,
                                topupLogMerchantNumber: 1,
                                topupLogPtoRference: 0
                            });
                            const cardPrintedId = await this.smartCardServ.getCardPrintedId(contractPiccCardData.cardUid);
                            const smartCardProfile = smartCardProfiles.find((f) => f.cardTypeShortNumber == contractPiccCardData.cardType && f.cardVersionShortNumber == contractPiccCardData.cardVersion && f.isEnabled);
                            const cardVersion = (smartCardProfile) ? smartCardProfile.cardVersion : constantsXml.smartCardVersion.anonymous;
                            const cardType = (smartCardProfile) ? smartCardProfile.cardType : constantsXml.smartCardType.standard;
                            const cardTechnology = (smartCardProfile) ? smartCardProfile.cardTechnology : constantsXml.smartCardPhysicalType.mifareDesfireEv3;

                            smartCardContracts = await this.smartCardContracts(contractPiccCardData);

                            if (actionList[i].createIssue) {
                                await this.posTransactionCtrl.create({
                                    function: 'smartCardContract',
                                    component: 'smartCardReader',
                                    parameters: {
                                        avl: avl,
                                        fare: fare,
                                        financial: {
                                            activityType: constantsXml.activityType.issue,
                                            fareItemCategory: constantsXml.fareItemCategory.fareProduct,
                                            fareItemType: constantsXml.fareItemType.productSmartCard,
                                            fareItemExternalId: topupProduct.externalId,
                                            fareItemProductType: topupProduct.productType,
                                            fareItemIssuedNumber: fareItemIssuedNumber,
                                            fareItemIssuedExpirationTimeStamp: moment.utc(smartCardContractTimestamp.expirationTimestamp, 'X').format(this.dateTTimeFormat),
                                            fareItemPurchasedStagesOrZones: 0,
                                            fareItemName: topupProduct.name,
                                            payingRiderTypeExternalId: riderType.externalId,
                                            payingRiderTypeName: riderType.name,
                                            payingRiderAccountId: '',
                                            cumulativeFareOrProductPrice: topupProduct.priceInCents,
                                            totalPaymentInCents: topupProduct.priceInCents,
                                            totalVatInCents: totalVat,
                                            vatAddedToTotalPayment: vatAddedToTotal,
                                            penaltyFee: 0,
                                            paymentInstrument: paymentInstrument,
                                            paymentMethod: paymentMethod,
                                            currencyCode: global.currentPosData.currencyCode,
                                            paymentInstrumentTransactionId: 0,
                                            relatedTransactionInformation: {
                                                relatedTransactionTimestamp: currentTimestamp,
                                                relatedTransactionDeviceType: constantsXml.deviceType.officePos,
                                                relatedTransactionDeviceId: global.currentPosData.deviceId,
                                                relatedTransactionTerminalId: global.currentPosData.terminalId,
                                                relatedTransactionDeviceTransactionCounter: global.currentPosData.transactionCounter
                                            },
                                            vatDetails: {
                                                taxableAmountInCents: topupProduct.priceInCents - totalVat,
                                                vatRate: vatRateInOneHundredths,
                                                vatInCents: totalVat
                                            }
                                        },
                                        card: {
                                            // card reference
                                            cardUid: contractPiccCardData.cardUid,
                                            cardPrintedId: cardPrintedId,
                                            cardType: cardType,
                                            cardTypeShortNumber: contractPiccCardData.cardType,
                                            cardVersion: cardVersion,
                                            cardVersionShortNumber: contractPiccCardData.cardVersion,
                                            cardIteration: contractPiccCardData.cardIteration,
                                            physicalCardType: cardTechnology,
                                            issuerId: contractPiccCardData.issuerId,
                                            transactionCounter: contractPiccCardData.transactionCounter + 1,
                                            // card snapshot
                                            timestamp: currentTimestamp,
                                            currencyCode: contractPiccCardData.currencyCode,
                                            preferredLanguage: contractPiccCardData.preferredLanguage,
                                            riderTypeExternalId: contractPiccCardData.riderTypeExternalId,
                                            storedValue: contractPiccCardData.contractValue1,
                                            refundableDeposit: contractPiccCardData.refundableDeposit,
                                            cardExpirationTimestamp: contractPiccCardData.cardExpirationTimestamp,
                                            alic: contractPiccCardData.alic,
                                            alwc: actionList[i].queueCounter,
                                            blockStatus: contractPiccCardData.blockStatus,
                                            blockTimestamp: contractPiccCardData.blockTimestamp,
                                            contracts: smartCardContracts.contracts,
                                            // card operation
                                            addContract: {
                                                contractType: constantsXml.contractType.storedRides,
                                                productId: contractPiccCardData['contractId'+contractNo],
                                                contractId: contractPiccCardData['contractId'+contractNo],
                                                instanceId: contractPiccCardData['instanceId'+contractNo],
                                                contractStatus: contractPiccCardData['contractStatus'+contractNo],
                                                contractStartTimestamp: contractPiccCardData['contractStartTimestamp'+contractNo],
                                                contractExpirationTimestamp: contractPiccCardData['contractExpirationTimestamp'+contractNo],
                                                contractStateChangeTimestamp: contractPiccCardData['contractStateChangeTimestamp'+contractNo],
                                                contractValue: contractPiccCardData['contractValue'+contractNo]
                                            }
                                        }
                                    }
                                });
                            }

                            await this.posTransactionCtrl.create({
                                function: 'smartCardAction',
                                component: 'smartCardReader',
                                parameters: {
                                    financial: {
                                        activityType: constantsXml.activityType.actionExecuted,
                                        fareItemCategory: constantsXml.fareItemCategory.fareProduct,
                                        fareItemType: constantsXml.fareItemType.productSmartCard,
                                        fareItemExternalId: topupProduct.externalId,
                                        fareItemProductType: topupProduct.productType,
                                        fareItemIssuedNumber: fareItemIssuedNumber,
                                        fareItemIssuedExpirationTimeStamp: moment.utc(smartCardContractTimestamp.expirationTimestamp, 'X').format(this.dateTTimeFormat),
                                        fareItemPurchasedStagesOrZones: 0,
                                        fareItemName: topupProduct.name,
                            			payingRiderTypeExternalId: riderType.externalId,
                            			payingRiderTypeName: riderType.name,
                            			payingRiderAccountId: '',
                            			cumulativeFareOrProductPrice: topupProduct.priceInCents,
                            			totalPaymentInCents: topupProduct.priceInCents,
                            			totalVatInCents: totalVat,
                                        vatAddedToTotalPayment: vatAddedToTotal,
                                        penaltyFee: 0,
                                        paymentInstrument: paymentInstrument,
                                        paymentMethod: paymentMethod,
                                        currencyCode: global.currentPosData.currencyCode,
                                        paymentInstrumentTransactionId: 0,
                                        relatedTransactionInformation: {
                                            relatedTransactionTimestamp: currentTimestamp,
                                            relatedTransactionDeviceType: constantsXml.deviceType.officePos,
                                            relatedTransactionDeviceId: global.currentPosData.deviceId,
                                            relatedTransactionTerminalId: global.currentPosData.terminalId,
                                            relatedTransactionDeviceTransactionCounter: global.currentPosData.transactionCounter
                                        },
                                        vatDetails: {
                                            taxableAmountInCents: topupProduct.priceInCents - totalVat,
                            				vatRate: vatRateInOneHundredths,
                            				vatInCents: totalVat
                                        }
                                    },
                                    card: {
                                        // card reference
                                        cardUid: contractPiccCardData.cardUid,
                                        cardPrintedId: cardPrintedId,
                                        cardType: cardType,
                                        cardTypeShortNumber: contractPiccCardData.cardType,
                                        cardVersion: cardVersion,
                                        cardVersionShortNumber: contractPiccCardData.cardVersion,
                                        cardIteration: contractPiccCardData.cardIteration,
                                        physicalCardType: cardTechnology,
                                        issuerId: contractPiccCardData.issuerId,
                                        transactionCounter: contractPiccCardData.transactionCounter + 1,
                                        // card snapshot
                                        timestamp: currentTimestamp,
                                        currencyCode: contractPiccCardData.currencyCode,
                                        preferredLanguage: contractPiccCardData.preferredLanguage,
                                        riderTypeExternalId: contractPiccCardData.riderTypeExternalId,
                                        storedValue: contractPiccCardData.contractValue1,
                                        refundableDeposit: contractPiccCardData.refundableDeposit,
                                        cardExpirationTimestamp: contractPiccCardData.cardExpirationTimestamp,
                                        alic: contractPiccCardData.alic,
                                        alwc: actionList[i].queueCounter,
                                        blockStatus: contractPiccCardData.blockStatus,
                                        blockTimestamp: contractPiccCardData.blockTimestamp,
                                        contracts: smartCardContracts.contracts,
                                        // card operation
                                        addContract: {
                                            contractType: constantsXml.contractType.storedRides,
                                            productId: contractPiccCardData['contractId'+contractNo],
                                            contractId: contractPiccCardData['contractId'+contractNo],
                                            instanceId: contractPiccCardData['instanceId'+contractNo],
                                            contractStatus: contractPiccCardData['contractStatus'+contractNo],
                                            contractStartTimestamp: contractPiccCardData['contractStartTimestamp'+contractNo],
                                            contractExpirationTimestamp: contractPiccCardData['contractExpirationTimestamp'+contractNo],
                                            contractStateChangeTimestamp: contractPiccCardData['contractStateChangeTimestamp'+contractNo],
                                            contractValue: contractPiccCardData['contractValue'+contractNo]
                                        }
                                    },
                                    action: {
                                        sequenceNumber: actionList[i].sequenceNumber,
                                        name: actionList[i].action,
                                        success: true,
                                        keptInQueue: false,
                                        alic: contractPiccCardData.alic,
                                        alwc: actionList[i].queueCounter
                                    }
                                }
                            });

                            waitingActionsCounter++;

                        }

                    } else {

                        if (actionList[i].action == 'modifyContract') {
                            if (!topupProduct.productType) {
                                topupProduct.productType = (actionList[i].actionParams.rides == 0) ? constantsXml.smartCardContractType.periodPass : constantsXml.smartCardContractType.storedRides;
                            }

                            const correctedMediaReason = reasonConfiguration.reasons.find((f) => f.id == actionList[i].actionParams.reasonCode);
                            const contractPiccCardData = await this.smartCardServ.getPiccCardData();

                            let contractList = [];
                            for (let i=2; i<=5; i++) {
                                contractList.push({
                                    slot: i,
                                    contract: contractPiccCardData['contractId'+i],
                                    instance: contractPiccCardData['instanceId'+i],
                                    expiry: contractPiccCardData['contractExpirationTimestamp'+i]
                                });
                            }

                            let contractNo = contractList.find((f) => f.contract == actionList[i].actionValue && f.instance == actionList[i].actionParams.instanceID).slot;
                            let contractExpiry = contractList.find((f) => f.contract == actionList[i].actionValue && f.instance == actionList[i].actionParams.instanceID).expiry;

                            let modifyContractType = (actionList[i].actionParams.rides == 0) ? constantsXml.contractType.periodPass : constantsXml.contractType.storedRides;
                            let modifyContractPiccCardData = contractPiccCardData;

                            if (topupProduct.productType == constantsXml.smartCardContractType.periodPass) {
                                modifyContractPiccCardData = await this.smartCardServ.changeContractExpirationTimestamp(moment.utc(actionList[i].actionParams.expiryDate).format('X'), contractNo);
                            } else if (topupProduct.productType == constantsXml.smartCardContractType.storedRides) {
                                modifyContractPiccCardData = await this.smartCardServ.changeContractValue(actionList[i].actionParams.rides, contractNo);
                            }

                            const cardCounters = await this.smartCardServ.cardCounters({
                                transactionCounter: contractPiccCardData.transactionCounter + 1,
                                countersTimestamp: moment.utc().format('X'),
                                alwc: actionList[i].queueCounter
                            });
                            const topUpLogs = await this.smartCardServ.topUpLogs({
                                topupLogTransactionTimestamp: moment.utc().format('X'),
                                topupLogContractCategory: enumsXml.smartCardContractCategory[topupProduct.productType],
                                topupLogContractId: topupProduct.externalId,
                                topupLogContractPointer: contractNo,
                                topupLogFareItemCategory: enumsXml.fareItemCategoryType.fareProduct,
                                topupLogFareItemType: enumsXml.fareItemType.productSmartCard,
                                topupLogActivityType: enumsXml.activityType.remoteCorrectSmartCard,
                                topupLogTerminalId: global.currentPosData.terminalId,
                                topupLogTerminalType: enumsXml.deviceType.officePos,
                                topupLogTransactionId: contractPiccCardData.transactionCounter + 1,
                                topupLogAmountCredited: 0,
                                topupLogMerchantNumber: 1,
                                topupLogPtoRference: 0
                            });
                            const cardPrintedId = await this.smartCardServ.getCardPrintedId(contractPiccCardData.cardUid);
                            const smartCardProfile = smartCardProfiles.find((f) => f.cardTypeShortNumber == contractPiccCardData.cardType && f.cardVersionShortNumber == contractPiccCardData.cardVersion && f.isEnabled);
                            const cardVersion = (smartCardProfile) ? smartCardProfile.cardVersion : constantsXml.smartCardVersion.anonymous;
                            const cardType = (smartCardProfile) ? smartCardProfile.cardType : constantsXml.smartCardType.standard;
                            const cardTechnology = (smartCardProfile) ? smartCardProfile.cardTechnology : constantsXml.smartCardPhysicalType.mifareDesfireEv3;

                            smartCardContracts = await this.smartCardContracts(modifyContractPiccCardData);

                            await this.posTransactionCtrl.create({
                                function: 'smartCardAction',
                                component: 'smartCardReader',
                                parameters: {
                                    financial: {
                                        activityType: constantsXml.activityType.actionExecuted,
                                        fareItemCategory: constantsXml.fareItemCategory.fareProduct,
                                        fareItemType: constantsXml.fareItemType.productSmartCard,
                                        fareItemExternalId: topupProduct.externalId,
                                        fareItemProductType: topupProduct.productType,
                                        fareItemIssuedNumber: fareItemIssuedNumber,
                                        fareItemIssuedExpirationTimeStamp: moment.utc(contractExpiry).format(this.dateTTimeFormat),
                                        fareItemPurchasedStagesOrZones: 0,
                                        fareItemName: topupProduct.name,
                                        payingRiderTypeExternalId: riderType.externalId,
                                        payingRiderTypeName: riderType.name,
                                        payingRiderAccountId: '',
                                        cumulativeFareOrProductPrice: topupProduct.priceInCents,
                                        totalPaymentInCents: topupProduct.priceInCents,
                                        totalVatInCents: totalVat,
                                        vatAddedToTotalPayment: vatAddedToTotal,
                                        penaltyFee: 0,
                                        paymentInstrument: paymentInstrument,
                                        paymentMethod: paymentMethod,
                                        currencyCode: global.currentPosData.currencyCode,
                                        paymentInstrumentTransactionId: 0,
                                        relatedTransactionInformation: {
                                            relatedTransactionTimestamp: currentTimestamp,
                                            relatedTransactionDeviceType: constantsXml.deviceType.officePos,
                                            relatedTransactionDeviceId: global.currentPosData.deviceId,
                                            relatedTransactionTerminalId: global.currentPosData.terminalId,
                                            relatedTransactionDeviceTransactionCounter: global.currentPosData.transactionCounter
                                        },
                                        vatDetails: {
                                            taxableAmountInCents: topupProduct.priceInCents - totalVat,
                                            vatRate: vatRateInOneHundredths,
                                            vatInCents: totalVat
                                        },
                                        reasonInformation: {
                                            reasonId: correctedMediaReason.id,
                                            reasonName: correctedMediaReason.name
                                        }
                                    },
                                    card: {
                                        // card reference
                                        cardUid: contractPiccCardData.cardUid,
                                        cardPrintedId: cardPrintedId,
                                        cardType: cardType,
                                        cardTypeShortNumber: contractPiccCardData.cardType,
                                        cardVersion: cardVersion,
                                        cardVersionShortNumber: contractPiccCardData.cardVersion,
                                        cardIteration: contractPiccCardData.cardIteration,
                                        physicalCardType: cardTechnology,
                                        issuerId: contractPiccCardData.issuerId,
                                        transactionCounter: contractPiccCardData.transactionCounter + 1,
                                        // card snapshot
                                        timestamp: currentTimestamp,
                                        currencyCode: contractPiccCardData.currencyCode,
                                        preferredLanguage: contractPiccCardData.preferredLanguage,
                                        riderTypeExternalId: contractPiccCardData.riderTypeExternalId,
                                        storedValue: contractPiccCardData.contractValue1,
                                        refundableDeposit: contractPiccCardData.refundableDeposit,
                                        cardExpirationTimestamp: contractPiccCardData.cardExpirationTimestamp,
                                        alic: contractPiccCardData.alic,
                                        alwc: actionList[i].queueCounter,
                                        blockStatus: contractPiccCardData.blockStatus,
                                        blockTimestamp: contractPiccCardData.blockTimestamp,
                                        contracts: smartCardContracts.contracts,
                                        // card operation
                                        modifyContract: {
                                            contractType: modifyContractType,
                                            productId: contractPiccCardData['contractId'+contractNo],
                                            contractId: contractPiccCardData['contractId'+contractNo],
                                            instanceId: contractPiccCardData['instanceId'+contractNo],
                                            previousContractStatus: contractPiccCardData['contractStatus'+contractNo],
                                            contractStatus: modifyContractPiccCardData['contractStatus'+contractNo],
                                            previousContractStartTimestamp: contractPiccCardData['contractStartTimestamp'+contractNo],
                                            contractStartTimestamp: modifyContractPiccCardData['contractStartTimestamp'+contractNo],
                                            previousContractExpirationTimestamp: contractPiccCardData['contractExpirationTimestamp'+contractNo],
                                            contractExpirationTimestamp: modifyContractPiccCardData['contractExpirationTimestamp'+contractNo],
                                            previousContractValue: contractPiccCardData['contractValue'+contractNo],
                                            contractValue: modifyContractPiccCardData['contractValue'+contractNo]
                                        }
                                    },
                                    action: {
                                        sequenceNumber: actionList[i].sequenceNumber,
                                        name: actionList[i].action,
                                        success: true,
                                        keptInQueue: false,
                                        alic: contractPiccCardData.alic,
                                        alwc: actionList[i].queueCounter
                                    }
                                }
                            });

                            waitingActionsCounter++;

                        } else {

                            log.info('waitingActions skiped', actionList[i].action, 'queueCounter', actionList[i].queueCounter, 'sequenceNumber', actionList[i].sequenceNumber);

                        }

                    }
                }

            }
        }

        return waitingActionsCounter;
    }

    actionListSmartCardHotlistReasons = async (req, res) => {
        try {
            const reasonConfiguration = await this.reasonsServ.reasonConfiguration();
            const reasonType = reasonConfiguration.reasonTypes.find((f) => f.name == constants.reasonTypes.hotlistReasons);
            const reasonTypeId = (reasonType) ? reasonType.id : 0;
            const reasons = reasonConfiguration.reasons.filter((f) => f.reasonTypeId == reasonTypeId);

            let reasonsList = [];
            if (reasons) {
                const reasonsLen = reasons.length;
                for (let i=0; i<reasonsLen; i++) {
                    reasonsList.push({
                        id: reasons[i].id,
                        name: reasons[i].name,
                        tag: reasons[i].tag,
                        nameTranslated: reasons[i].nameTranslations.find((f) => f.languageCode == global.currentPosData.secondLanguage.split('-')[0]).translation
                    });
                }
            }

            const reasonsListOrdered = reasonsList.sort((a, b) => a.id - b.id);

            res.status(200).json(reasonsListOrdered);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    smartCardRemoveProductReasons = async (req, res) => {
        try {
            const reasonConfiguration = await this.reasonsServ.reasonConfiguration();
            const reasonType = reasonConfiguration.reasonTypes.find((f) => f.name == constants.reasonTypes.removeProductReasons);
            const reasonTypeId = (reasonType) ? reasonType.id : 0;
            const reasons = reasonConfiguration.reasons.filter((f) => f.reasonTypeId == reasonTypeId);

            let reasonsList = [];
            if (reasons) {
                const reasonsLen = reasons.length;
                for (let i=0; i<reasonsLen; i++) {
                    reasonsList.push({
                        id: reasons[i].id,
                        name: reasons[i].name,
                        tag: reasons[i].tag,
                        nameTranslated: reasons[i].nameTranslations.find((f) => f.languageCode == global.currentPosData.secondLanguage.split('-')[0]).translation
                    });
                }
            }

            const reasonsListOrdered = reasonsList.sort((a, b) => a.id - b.id);

            res.status(200).json(reasonsListOrdered);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    blockSmartCard = async (req, res) => {
        try {
            const block = req.body;

            const currentTimestamp = moment.utc().format(this.dateTTimeFormat);

            const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
            const riderType = await this.fareTableModel.RiderTypes.findOne({fareEnvironmentConfigurationId: activeConfig._id, type: 'Rider', externalId: block.smartCardContent.riderTypeExternalId});
            const smartCardProfiles = await this.fareTableModel.SmartCardProfiles.find({fareEnvironmentConfigurationId: activeConfig._id});

            let smartCardContracts = await this.smartCardContracts(block.smartCardContent);

            const cardPrintedId = await this.smartCardServ.getCardPrintedId(block.smartCardContent.cardUid);
            const smartCardProfile = smartCardProfiles.find((f) => f.cardTypeShortNumber == block.smartCardContent.cardType && f.cardVersionShortNumber == block.smartCardContent.cardVersion && f.isEnabled);
            const cardVersion = (smartCardProfile) ? smartCardProfile.cardVersion : constantsXml.smartCardVersion.anonymous;
            const cardType = (smartCardProfile) ? smartCardProfile.cardType : constantsXml.smartCardType.standard;
            const cardTechnology = (smartCardProfile) ? smartCardProfile.cardTechnology : constantsXml.smartCardPhysicalType.mifareDesfireEv3;

            const blockStatus = await this.smartCardServ.blockStatus({
                blockStatus: enumsXml.smartCardBlockStatus.blocked,
                blockTimestamp: moment.utc().format('X')
            });
            const cardCounters = await this.smartCardServ.cardCounters({
                transactionCounter: block.smartCardContent.transactionCounter + 1,
                countersTimestamp: moment.utc().format('X')
            });

            await this.posTransactionCtrl.create({
                function: 'smartCardBlockUnblock',
                component: 'smartCardReader',
                parameters: {
                    financial: {
                        activityType: constantsXml.activityType.manageSmartCard,
                        fareItemCategory: constantsXml.fareItemCategory.blockType,
                        fareItemType: constantsXml.fareItemType.blockSmartCard,
                        fareItemExternalId: 0,
                        fareItemProductType: '',
                        fareItemIssuedNumber: 0,
                        fareItemIssuedExpirationTimeStamp: '',
                        fareItemPurchasedStagesOrZones: 0,
                        fareItemName: constantsXml.fareItemName.na,
                        payingRiderTypeExternalId: riderType.externalId,
                        payingRiderTypeName: riderType.name,
                        payingRiderAccountId: '',
                        cumulativeFareOrProductPrice: 0,
                        totalPaymentInCents: 0,
                        totalVatInCents: 0,
                        vatAddedToTotalPayment: true,
                        penaltyFee: 0,
                        paymentInstrument: constantsXml.paymentInstrument.none,
                        paymentMethod: constantsXml.paymentMethod.none,
                        currencyCode: global.currentPosData.currencyCode,
                        paymentInstrumentTransactionId: 0,
                        relatedTransactionInformation: {
                            relatedTransactionTimestamp: currentTimestamp,
                            relatedTransactionDeviceType: constantsXml.deviceType.officePos,
                            relatedTransactionDeviceId: global.currentPosData.deviceId,
                            relatedTransactionTerminalId: global.currentPosData.terminalId,
                            relatedTransactionDeviceTransactionCounter: global.currentPosData.transactionCounter
                        },
                        reasonInformation: {
                            reasonId: block.reason.id,
                            reasonName: block.reason.name
                        }
                    },
                    card: {
                        // card reference
                        cardUid: block.smartCardContent.cardUid,
                        cardPrintedId: cardPrintedId,
                        cardType: cardType,
                        cardTypeShortNumber: block.smartCardContent.cardType,
                        cardVersion: cardVersion,
                        cardVersionShortNumber: block.smartCardContent.cardVersion,
                        cardIteration: block.smartCardContent.cardIteration,
                        physicalCardType: cardTechnology,
                        issuerId: block.smartCardContent.issuerId,
                        transactionCounter: block.smartCardContent.transactionCounter + 1,
                        // card snapshot
                        timestamp: currentTimestamp,
                        currencyCode: block.smartCardContent.currencyCode,
                        preferredLanguage: block.smartCardContent.preferredLanguage,
                        riderTypeExternalId: block.smartCardContent.riderTypeExternalId,
                        storedValue: block.smartCardContent.contractValue1,
                        refundableDeposit: block.smartCardContent.refundableDeposit,
                        cardExpirationTimestamp: block.smartCardContent.cardExpirationTimestamp,
                        alic: block.smartCardContent.alic,
                        alwc: block.smartCardContent.alwc,
                        blockStatus: constantsXml.smartCardBlockStatus[enumsXml.smartCardBlockStatus.blocked],
                        blockTimestamp: currentTimestamp,
                        contracts: smartCardContracts.contracts,
                        // card operation
                        cardBlock: 'cardBlock'
                    }
                }
            });

            res.status(200).json(true);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    unblockSmartCard = async (req, res) => {
        try {
            const unblock = req.body;

            const currentTimestamp = moment.utc().format(this.dateTTimeFormat);

            const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
            const riderType = await this.fareTableModel.RiderTypes.findOne({fareEnvironmentConfigurationId: activeConfig._id, type: 'Rider', externalId: unblock.smartCardContent.riderTypeExternalId});
            const smartCardProfiles = await this.fareTableModel.SmartCardProfiles.find({fareEnvironmentConfigurationId: activeConfig._id});

            let smartCardContracts = await this.smartCardContracts(unblock.smartCardContent);

            const cardPrintedId = await this.smartCardServ.getCardPrintedId(unblock.smartCardContent.cardUid);
            const smartCardProfile = smartCardProfiles.find((f) => f.cardTypeShortNumber == unblock.smartCardContent.cardType && f.cardVersionShortNumber == unblock.smartCardContent.cardVersion && f.isEnabled);
            const cardVersion = (smartCardProfile) ? smartCardProfile.cardVersion : constantsXml.smartCardVersion.anonymous;
            const cardType = (smartCardProfile) ? smartCardProfile.cardType : constantsXml.smartCardType.standard;
            const cardTechnology = (smartCardProfile) ? smartCardProfile.cardTechnology : constantsXml.smartCardPhysicalType.mifareDesfireEv3;

            const blockStatus = await this.smartCardServ.blockStatus({
                blockStatus: enumsXml.smartCardBlockStatus.notBlocked,
                blockTimestamp: moment.utc().format('X')
            });
            const cardCounters = await this.smartCardServ.cardCounters({
                transactionCounter: unblock.smartCardContent.transactionCounter + 1,
                countersTimestamp: moment.utc().format('X')
            });

            await this.posTransactionCtrl.create({
                function: 'smartCardBlockUnblock',
                component: 'smartCardReader',
                parameters: {
                    financial: {
                        activityType: constantsXml.activityType.manageSmartCard,
                        fareItemCategory: constantsXml.fareItemCategory.blockType,
                        fareItemType: constantsXml.fareItemType.unblockSmartCard,
                        fareItemExternalId: 0,
                        fareItemProductType: '',
                        fareItemIssuedNumber: 0,
                        fareItemIssuedExpirationTimeStamp: '',
                        fareItemPurchasedStagesOrZones: 0,
                        fareItemName: constantsXml.fareItemName.na,
                        payingRiderTypeExternalId: riderType.externalId,
                        payingRiderTypeName: riderType.name,
                        payingRiderAccountId: '',
                        cumulativeFareOrProductPrice: 0,
                        totalPaymentInCents: 0,
                        totalVatInCents: 0,
                        vatAddedToTotalPayment: true,
                        penaltyFee: 0,
                        paymentInstrument: constantsXml.paymentInstrument.none,
                        paymentMethod: constantsXml.paymentMethod.none,
                        currencyCode: global.currentPosData.currencyCode,
                        paymentInstrumentTransactionId: 0,
                        relatedTransactionInformation: {
                            relatedTransactionTimestamp: currentTimestamp,
                            relatedTransactionDeviceType: constantsXml.deviceType.officePos,
                            relatedTransactionDeviceId: global.currentPosData.deviceId,
                            relatedTransactionTerminalId: global.currentPosData.terminalId,
                            relatedTransactionDeviceTransactionCounter: global.currentPosData.transactionCounter
                        },
                        reasonInformation: {
                            reasonId: unblock.reason.id,
                            reasonName: unblock.reason.name
                        }
                    },
                    card: {
                        // card reference
                        cardUid: unblock.smartCardContent.cardUid,
                        cardPrintedId: cardPrintedId,
                        cardType: cardType,
                        cardTypeShortNumber: unblock.smartCardContent.cardType,
                        cardVersion: cardVersion,
                        cardVersionShortNumber: unblock.smartCardContent.cardVersion,
                        cardIteration: unblock.smartCardContent.cardIteration,
                        physicalCardType: cardTechnology,
                        issuerId: unblock.smartCardContent.issuerId,
                        transactionCounter: unblock.smartCardContent.transactionCounter + 1,
                        // card snapshot
                        timestamp: currentTimestamp,
                        currencyCode: unblock.smartCardContent.currencyCode,
                        preferredLanguage: unblock.smartCardContent.preferredLanguage,
                        riderTypeExternalId: unblock.smartCardContent.riderTypeExternalId,
                        storedValue: unblock.smartCardContent.contractValue1,
                        refundableDeposit: unblock.smartCardContent.refundableDeposit,
                        cardExpirationTimestamp: unblock.smartCardContent.cardExpirationTimestamp,
                        alic: unblock.smartCardContent.alic,
                        alwc: unblock.smartCardContent.alwc,
                        blockStatus: constantsXml.smartCardBlockStatus[enumsXml.smartCardBlockStatus.notBlocked],
                        blockTimestamp: currentTimestamp,
                        contracts: smartCardContracts.contracts,
                        // card operation
                        cardUnblock: 'cardUnblock'
                    }
                }
            });

            res.status(200).json(true);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    removeContractSmartCard = async (req, res) => {
        try {
            const remove = req.body;

            const translations1st = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'translations', global.currentPosData.firstLanguage+'.json')));
            const translations2nd = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'translations', global.currentPosData.secondLanguage+'.json')));

            const currentTimestamp = moment.utc().format(this.dateTTimeFormat);

            const issuingTimestamp = moment.utc().utcOffset(global.currentPosData.utcOffset);
            const invoiceId = await this.posTransactionCtrl.invoice(issuingTimestamp);

            const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
            const riderType = await this.fareTableModel.RiderTypes.findOne({fareEnvironmentConfigurationId: activeConfig._id, type: 'Rider', externalId: remove.smartCardContent.riderTypeExternalId});
            const smartCardProfiles = await this.fareTableModel.SmartCardProfiles.find({fareEnvironmentConfigurationId: activeConfig._id});

            let smartCardContracts = await this.smartCardContracts(remove.smartCardContent);

            const cardPrintedId = await this.smartCardServ.getCardPrintedId(remove.smartCardContent.cardUid);
            const smartCardProfile = smartCardProfiles.find((f) => f.cardTypeShortNumber == remove.smartCardContent.cardType && f.cardVersionShortNumber == remove.smartCardContent.cardVersion && f.isEnabled);
            const cardVersion = (smartCardProfile) ? smartCardProfile.cardVersion : constantsXml.smartCardVersion.anonymous;
            const cardType = (smartCardProfile) ? smartCardProfile.cardType : constantsXml.smartCardType.standard;
            const cardTechnology = (smartCardProfile) ? smartCardProfile.cardTechnology : constantsXml.smartCardPhysicalType.mifareDesfireEv3;

            let contractNo = remove.contractSlot;
            const removeContractValues = {
                contractId: 0,
                instanceId: '0000000000',
                contractValue: enumsXml.smartCardContractValue.pending,
                contractCategory: enumsXml.smartCardContractCategory.Undefined,
                contractStatus: enumsXml.smartCardContractStatus.invalid,
                contractStartTimestamp: moment.utc(0).format('X'),
                contractExpirationTimestamp: moment.utc(0).format('X'),
                contractStateChangeTimestamp: moment.utc(0).format('X')
            };

            const removeContract = await this.smartCardServ.removeContract(removeContractValues, contractNo);

            const cardCounters = await this.smartCardServ.cardCounters({
                transactionCounter: remove.smartCardContent.transactionCounter + 1,
                countersTimestamp: moment.utc().format('X')
            });

            smartCardContracts = await this.smartCardContracts(removeContract);

            let contractType;
            switch (remove.smartCardContent['contractCategory'+contractNo]) {
                case 'PeriodPassXDays':
                    contractType = constantsXml.contractType.periodPass;
                break;
                case 'StoredRides':
                    contractType = constantsXml.contractType.storedRides;
                break;
            }

            const getCardPurchases = await this.service.getCardPurchases(remove.smartCardContent.cardUid);
            const removedProduct = getCardPurchases.find((f) => moment.utc(f.timestamp).format(this.dateRFormat+'[T]'+this.timeFormat) == moment.utc(remove.smartCardContent['contractStateChangeTimestamp'+contractNo]).format(this.dateRFormat+'[T]'+this.timeFormat) && f.productType == contractType && f.fareItemExternalId == remove.smartCardContent['contractId'+contractNo]);

            await this.posTransactionCtrl.create({
                function: 'smartCardRemoveContract',
                component: 'smartCardReader',
                parameters: {
                    financial: {
                        activityType: constantsXml.activityType.issueReversal,
                        fareItemCategory: constantsXml.fareItemCategory.fareProduct,
                        fareItemType: constantsXml.fareItemType.productSmartCard,
                        fareItemExternalId: (removedProduct) ? removedProduct.fareItemExternalId : 0,
                        fareItemProductType: (removedProduct) ? removedProduct.fareProductType : '',
                        fareItemIssuedNumber: 0,
                        fareItemIssuedExpirationTimeStamp: '',
                        fareItemPurchasedStagesOrZones: 0,
                        fareItemName: (removedProduct) ? removedProduct.fareItemName : constantsXml.fareItemName.na,
                        payingRiderTypeExternalId: riderType.externalId,
                        payingRiderTypeName: riderType.name,
                        payingRiderAccountId: '',
                        cumulativeFareOrProductPrice: (removedProduct) ? removedProduct.cost : 0,
                        totalPaymentInCents: (removedProduct) ? removedProduct.cost : 0,
                        totalVatInCents: 0,
                        vatAddedToTotalPayment: true,
                        penaltyFee: 0,
                        paymentInstrument: constantsXml.paymentInstrument.none,
                        paymentMethod: constantsXml.paymentMethod.none,
                        currencyCode: global.currentPosData.currencyCode,
                        paymentInstrumentTransactionId: 0,
                        relatedTransactionInformation: {
                            relatedTransactionTimestamp: currentTimestamp,
                            relatedTransactionDeviceType: constantsXml.deviceType.officePos,
                            relatedTransactionDeviceId: global.currentPosData.deviceId,
                            relatedTransactionTerminalId: global.currentPosData.terminalId,
                            relatedTransactionDeviceTransactionCounter: global.currentPosData.transactionCounter
                        },
                        reasonInformation: {
                            reasonId: remove.reason.id,
                            reasonName: remove.reason.name
                        }
                    },
                    card: {
                        // card reference
                        cardUid: remove.smartCardContent.cardUid,
                        cardPrintedId: cardPrintedId,
                        cardType: cardType,
                        cardTypeShortNumber: remove.smartCardContent.cardType,
                        cardVersion: cardVersion,
                        cardVersionShortNumber: remove.smartCardContent.cardVersion,
                        cardIteration: remove.smartCardContent.cardIteration,
                        physicalCardType: cardTechnology,
                        issuerId: remove.smartCardContent.issuerId,
                        transactionCounter: remove.smartCardContent.transactionCounter + 1,
                        // card snapshot
                        timestamp: currentTimestamp,
                        currencyCode: remove.smartCardContent.currencyCode,
                        preferredLanguage: remove.smartCardContent.preferredLanguage,
                        riderTypeExternalId: remove.smartCardContent.riderTypeExternalId,
                        storedValue: remove.smartCardContent.contractValue1,
                        refundableDeposit: remove.smartCardContent.refundableDeposit,
                        cardExpirationTimestamp: remove.smartCardContent.cardExpirationTimestamp,
                        alic: remove.smartCardContent.alic,
                        alwc: remove.smartCardContent.alwc,
                        blockStatus: remove.smartCardContent.blockStatus,
                        blockTimestamp: remove.smartCardContent.blockTimestamp,
                        contracts: smartCardContracts.contracts,
                        // card operation
                        removeContract: {
                            contractType: contractType,
                            productId: remove.smartCardContent['contractId'+contractNo],
                            contractId: remove.smartCardContent['contractId'+contractNo],
                            instanceId: remove.smartCardContent['instanceId'+contractNo]
                        }
                    }
                }
            });

            let transaction = {
                ticketId: null,
                ticketItemId: null,
                transactionTime: moment.utc().toISOString(),
                riderExternalId: (riderType) ? riderType.externalId : constants.defaults.accountId,
                riderName: (riderType) ? riderType.name.charAt(0).toUpperCase() + riderType.name.slice(1) : constants.defaults.accountName,
                paymentInstrument: constantsXml.paymentInstrument.prepayCash,
                paymentMethod: constantsXml.paymentMethod.manualyCollected,
                currencyCode: global.currentPosData.currencyCode,
                employeeId: global.currentEmployeeData.employeeId,
                sessionId: global.currentEmployeeData.employeeSessionId,
                cardTransactionNo: null,
                accountNo: null,
                cardVersion: remove.smartCardContent.cardVersion,
                cardUid: remove.smartCardContent.cardUid,
                cardholderId: remove.cardHolderId,
                fareItemIssuedNumber: 0,
                activityType: constantsXml.activityType.issueReversal,
                issuedFormatType: '',
                numberOfPassengers: 1,
                fareItemCategory: constantsXml.fareItemCategory.fareProduct,
                fareItemType: constantsXml.fareItemType.productSmartCard,
                farePrice: (removedProduct) ? removedProduct.cost : 0,
                farePriceCurrency: (removedProduct) ? removedProduct.cost / Math.pow(10, global.currentPosData.currencyDecimal) : 0,
                fareItemExternalId: (removedProduct) ? removedProduct.fareItemExternalId : 0,
                fareItemName: (removedProduct) ? removedProduct.fareItemName : constantsXml.fareItemName.na,
                prodName: (removedProduct) ? removedProduct.fareItemName : constantsXml.fareItemName.na,
                productType: (removedProduct) ? removedProduct.fareProductType : '',
                waybillName: `${translations1st[constants.waybillName.topUpSmartCard]} - ${translations1st['Canceled']}`,
                waybillNameTranslated: `${translations2nd[constants.waybillName.topUpSmartCard]} - ${translations2nd['Canceled']}`
            };

            await this.modelFinancial.create(transaction);

            res.status(200).json({
                creditNoteId: invoiceId,
                issueTime: issuingTimestamp.format(this.dateTimeFormat),
                removedProduct: removedProduct
            });
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    changeRiderTypeSmartCard = async (req, res) => {
        try {
            const change = req.body;

            const currentTimestamp = moment.utc().format(this.dateTTimeFormat);

            const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
            const riderType = await this.fareTableModel.RiderTypes.findOne({fareEnvironmentConfigurationId: activeConfig._id, type: 'Rider', externalId: change.riderTypeId});
            const smartCardProfiles = await this.fareTableModel.SmartCardProfiles.find({fareEnvironmentConfigurationId: activeConfig._id});

            let smartCardContracts = await this.smartCardContracts(change.smartCardContent);

            const cardPrintedId = await this.smartCardServ.getCardPrintedId(change.smartCardContent.cardUid);
            const smartCardProfile = smartCardProfiles.find((f) => f.cardTypeShortNumber == change.smartCardContent.cardType && f.cardVersionShortNumber == change.smartCardContent.cardVersion && f.isEnabled);
            const cardVersion = (smartCardProfile) ? smartCardProfile.cardVersion : constantsXml.smartCardVersion.anonymous;
            const cardType = (smartCardProfile) ? smartCardProfile.cardType : constantsXml.smartCardType.standard;
            const cardTechnology = (smartCardProfile) ? smartCardProfile.cardTechnology : constantsXml.smartCardPhysicalType.mifareDesfireEv3;

            const changeRiderType = riderType;

            const changeRiderTypeExternalId = await this.smartCardServ.changeRiderTypeExternalId(changeRiderType.externalId);

            const cardCounters = await this.smartCardServ.cardCounters({
                transactionCounter: change.smartCardContent.transactionCounter + 1,
                countersTimestamp: moment.utc().format('X')
            });

            await this.posTransactionCtrl.create({
                function: 'smartCardChangeRider',
                component: 'smartCardReader',
                parameters: {
                    financial: {
                        activityType: constantsXml.activityType.manageSmartCard,
                        fareItemCategory: constantsXml.fareItemCategory.initSmartCard,
                        fareItemType: constantsXml.fareItemType.assignRiderTypeSmartCard,
                        fareItemExternalId: 0,
                        fareItemProductType: '',
                        fareItemIssuedNumber: 0,
                        fareItemIssuedExpirationTimeStamp: '',
                        fareItemPurchasedStagesOrZones: 0,
                        fareItemName: constantsXml.fareItemName.na,
                        payingRiderTypeExternalId: changeRiderType.externalId,
                        payingRiderTypeName: changeRiderType.name,
                        payingRiderAccountId: '',
                        cumulativeFareOrProductPrice: 0,
                        totalPaymentInCents: 0,
                        totalVatInCents: 0,
                        vatAddedToTotalPayment: true,
                        penaltyFee: 0,
                        paymentInstrument: constantsXml.paymentInstrument.none,
                        paymentMethod: constantsXml.paymentMethod.none,
                        currencyCode: global.currentPosData.currencyCode,
                        paymentInstrumentTransactionId: 0,
                        relatedTransactionInformation: {
                            relatedTransactionTimestamp: currentTimestamp,
                            relatedTransactionDeviceType: constantsXml.deviceType.officePos,
                            relatedTransactionDeviceId: global.currentPosData.deviceId,
                            relatedTransactionTerminalId: global.currentPosData.terminalId,
                            relatedTransactionDeviceTransactionCounter: global.currentPosData.transactionCounter
                        }
                    },
                    card: {
                        // card reference
                        cardUid: change.smartCardContent.cardUid,
                        cardPrintedId: cardPrintedId,
                        cardType: cardType,
                        cardTypeShortNumber: change.smartCardContent.cardType,
                        cardVersion: cardVersion,
                        cardVersionShortNumber: change.smartCardContent.cardVersion,
                        cardIteration: change.smartCardContent.cardIteration,
                        physicalCardType: cardTechnology,
                        issuerId: change.smartCardContent.issuerId,
                        transactionCounter: change.smartCardContent.transactionCounter + 1,
                        // card snapshot
                        timestamp: currentTimestamp,
                        currencyCode: change.smartCardContent.currencyCode,
                        preferredLanguage: change.smartCardContent.preferredLanguage,
                        riderTypeExternalId: change.smartCardContent.riderTypeExternalId,
                        storedValue: change.smartCardContent.contractValue1,
                        refundableDeposit: change.smartCardContent.refundableDeposit,
                        cardExpirationTimestamp: change.smartCardContent.cardExpirationTimestamp,
                        alic: change.smartCardContent.alic,
                        alwc: change.smartCardContent.alwc,
                        blockStatus: change.smartCardContent.blockStatus,
                        blockTimestamp: change.smartCardContent.blockTimestamp,
                        contracts: smartCardContracts.contracts,
                        // card operation
                        changeRiderType: {
                            previousRiderType: change.smartCardContent.riderTypeExternalId,
                            newRiderType: changeRiderType.externalId,
                            newRiderTypeExpirationTimestamp: (changeRiderType.expirationDate) ? moment.utc(changeRiderType.expirationDate).format(this.dateTTimeFormat) : moment.utc(change.smartCardContent.cardExpirationTimestamp).format(this.dateTTimeFormat)
                        }
                    }
                }
            });

            res.status(200).json(true);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    getSmartCard = async (req, res) => {
        try {
            const card = req.body;

            let getCard = await this.service.getSmartCard(card.smartCardUid);

            res.status(200).json(getCard);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    getSmartCardByPrintedId = async (req, res) => {
        try {
            const card = req.body;

            let getCard = await this.service.getSmartCardByPrintedId(card.smartCardPid);

            res.status(200).json(getCard);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    getSmartCardProfiles = async (req, res) => {
        try {
            //const smartCardProfiles = await this.service.getSmartCardProfiles();
            const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
            let smartCardProfiles = await this.fareTableModel.SmartCardProfiles.find({fareEnvironmentConfigurationId: activeConfig._id});

            if (process.env.CLIENT_SECURE_ACCESS_MODULE == 'demo') {
                smartCardProfiles = await this.smartCardServ.demoCardProfiles();
            }

            res.status(200).json(smartCardProfiles);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    createCardHolder = async (req, res) => {
        try {
            const cardholder = req.body;

            const accountHolder = cardholder.accountHolder;
            if (global.currentPosData.hasAccounts) {
                let accountHolderEdit = {
                    version: accountHolder.version,
                    firstName: accountHolder.name.substr(0, accountHolder.name.indexOf(' ')),
                    lastName: accountHolder.name.substr(accountHolder.name.indexOf(' ') + 1),
                    fullName: accountHolder.name,
                    dayOfBirth: moment.utc(accountHolder.birthday, this.dateFormat).toISOString(),
                    businessOrganizationName: '',
                    businessOrganizationId: '',
                    addressLine1: accountHolder.address,
                    addressLine2: accountHolder.address2,
                    city: accountHolder.city,
                    postalCode: '',
                    country: accountHolder.country,
                    personGender: accountHolder.gender,
                    personType: accountHolder.title
                };

                const accountHolderEdited = await this.accountHolderServ.edit(accountHolder.uid, accountHolderEdit);
            }

            const cardholderData = {
                cardHolderEnvelope: {
                    cardHolderInfoBasic: {
                        cardHolderType: constantsXml.cardHolderType.personalized,
                        cardHolderGender: constantsXml.cardHolderGenderType[cardholder.gender.toLowerCase()],
                        cardHolderFirstName: cardholder.name.substr(0, cardholder.name.indexOf(' ')),
                        cardHolderLastName: cardholder.name.substr(cardholder.name.indexOf(' ') + 1),
                        cardHolderDayOfBirth: (cardholder.birthday) ? moment.utc(cardholder.birthday, this.dateFormat).format(this.dateTTimeFormat) : null
                    },
                    cardHolderInfoExtended: {
                        cardHolderAddressLine1: cardholder.address,
                        cardHolderAddressLine2: cardholder.address2,
                        cardHolderAddressLine3: '',
                        cardHolderCity: cardholder.city,
                        cardHolderPostalCode: '',
                        cardHolderCountryCode: cardholder.country,
                        cardHolderCountryProvince: '',
                        cardHolderPhoneNumber: cardholder.phone,
                        cardHolderEmployeeId: '',
                        cardHolderEmployeeTypeExternalId: '',
                        cardHolderEmployeeTypeName: '',
                        cardHolderIdentificationType: constantsXml.cardHolderIdentificationType[cardholder.identificationType],
                        cardHolderIdentificationId: cardholder.identificationId,
                        cardHolderIdentificationName: cardholder.identificationName,
                        cardHolderIdentificationExpiry: (cardholder.identificationExpiry) ? moment.utc(cardholder.identificationExpiry, this.dateFormat).format(this.dateTTimeFormat) : moment.utc().format(this.dateTTimeFormat)
                    },
                    deviceInfoShort: {
                        deviceId: global.currentPosData.deviceId,
                        deviceType: constantsXml.deviceType.officePos,
                        operatorRef: global.currentEmployeeData.employeeId,
                        operatorTypeExternalID: global.currentEmployeeData.employeeRoleId,
                        operatorTypeName: global.currentEmployeeData.employeeRoleName.slice(0, this.employeeRoleNameLength)
                    }
                }
            };

            const createCardHolder = await this.service.createCardHolder(cardholderData);

            if (cardholder.image) {
                const photoArray = cardholder.image.split(',')[1];

                const photo = cardholder.image;
                let imageData = photo.split(',')[1];
                let imageType = photo.split(';')[0].split('/')[1];
                let formContent = new formData();
                formContent.append('file', Buffer.from(imageData, 'base64'));
                formContent.append('name', 'photo'+createCardHolder.id+'.'+imageType);

                const uploadPhoto = await this.service.uploadCardHolderPhoto(createCardHolder.id, imageType, formContent);
            }

            res.status(200).json(createCardHolder);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    getCardHolderById = async (req, res) => {
        try {
            const cardholder = req.query;

            let getCardHolderById = await this.service.getCardHolderById(cardholder.cardHolderId);

            res.status(200).json(getCardHolderById);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    getCardHolderByIdentificationId = async (req, res) => {
        try {
            const cardholder = req.query;

            let getCardHolderByIdentificationId = await this.service.getCardHolderByIdentificationId(cardholder.identificationId);

            res.status(200).json(getCardHolderByIdentificationId);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    getCardHolderByPhoneNumber = async (req, res) => {
        try {
            const phone = req.query.phoneNumber;

            const cardHolder = (phone != 'null') ? await this.accountHolderServ.getAccountDetailsPhone(phone) : null;

            const phoneExists = (cardHolder && cardHolder.phoneNumber) ? true : false;

            res.status(200).json(phoneExists);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    getCardHolderByEmailAddress = async (req, res) => {
        try {
            const email = req.query.emailAddress;

            const cardHolder = (email != 'null') ? await this.accountHolderServ.getAccountDetailsEmail(email) : null;

            const emailExists = (cardHolder && cardHolder.emailAddress) ? true : false;

            res.status(200).json(emailExists);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    getCardHolderBySmartCardUId = async (req, res) => {
        try {
            const cardholder = req.query;

            let getCardHolderBySmartCardUId = await this.service.getCardHolderBySmartCardUId(cardholder.smartCardUId);

            res.status(200).json((getCardHolderBySmartCardUId && !getCardHolderBySmartCardUId.hasOwnProperty('head')) ? getCardHolderBySmartCardUId : null);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    getCardHolderPhoto = async (req, res) => {
        try {
            const cardholder = req.query;

            const cardHolderPhoto = await this.service.getCardHolderPhoto(cardholder.cardHolderId);

            res.status(200).json(cardHolderPhoto);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    uploadCardHolderPhoto = async (req, res) => {
        try {
            const photo = req.body.content;
            let imageData = photo.split(',')[1];
            let imageType = photo.split(';')[0].split('/')[1];
            let formContent = new formData();
            formContent.append('file', Buffer.from(imageData, 'base64'));
            formContent.append('name', 'photo'+req.body.cardHolderId+'.'+imageType);

            const uploadPhoto = await this.service.uploadCardHolderPhoto(req.body.cardHolderId, imageType, formContent);

            res.status(200).json(uploadPhoto);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    getCardHolderIdentityProof = async (req, res) => {
        try {
            const cardholder = req.query;

            const cardHolderIdentityProof = await this.service.getCardHolderIdentityProof(cardholder.cardHolderId);

            res.status(200).json(cardHolderIdentityProof);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    uploadCardHolderIdentityProof = async (req, res) => {
        try {
            const photo = req.body.content;
            let imageData = photo.split(',')[1];
            let imageType = photo.split(';')[0].split('/')[1];
            let formContent = new formData();
            formContent.append('file', Buffer.from(imageData, 'base64'));
            formContent.append('name', 'photo'+req.body.cardHolderId+'.'+imageType);

            const uploadPhoto = await this.service.uploadCardHolderPhoto(req.body.cardHolderId, imageType, formContent);

            res.status(200).json(uploadPhoto);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    registerCardHolderToSmartCard = async (req, res) => {
        try {
            const cardholder = req.body;

            const register = await this.service.registerCardHolderToSmartCard(cardholder.cardHolderId, cardholder.smartCardUId);

            res.status(200).json(register);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    updateCardHolder = async (req, res) => {
        try {
            const cardholder = req.body;

            const cardholderData = {
                cardHolderEnvelope: {
                    cardHolderInfoBasic: {
                        cardHolderType: constantsXml.cardHolderType.personalized,
                        cardHolderGender: constantsXml.cardHolderGenderType[cardholder.gender.toLowerCase()],
                        cardHolderFirstName: cardholder.name.substr(0, cardholder.name.indexOf(' ')),
                        cardHolderLastName: cardholder.name.substr(cardholder.name.indexOf(' ') + 1),
                        cardHolderDayOfBirth: (cardholder.birthday) ? moment.utc(cardholder.birthday, this.dateFormat).format(this.dateTTimeFormat) : null
                    },
                    cardHolderInfoExtended: {
                        cardHolderAddressLine1: cardholder.address,
                        cardHolderAddressLine2: cardholder.address2,
                        cardHolderAddressLine3: '',
                        cardHolderCity: cardholder.city,
                        cardHolderPostalCode: '',
                        cardHolderCountryCode: cardholder.country,
                        cardHolderCountryProvince: '',
                        cardHolderPhoneNumber: cardholder.phone,
                        cardHolderEmployeeId: '',
                        cardHolderEmployeeTypeExternalId: '',
                        cardHolderEmployeeTypeName: '',
                        cardHolderIdentificationType: constantsXml.cardHolderIdentificationType[cardholder.identificationType],
                        cardHolderIdentificationId: cardholder.identificationId,
                        cardHolderIdentificationName: cardholder.identificationName,
                        cardHolderIdentificationExpiry: (cardholder.identificationExpiry) ? moment.utc(cardholder.identificationExpiry, this.dateFormat).format(this.dateTTimeFormat) : moment.utc().format(this.dateTTimeFormat)
                    },
                    deviceInfoShort: {
                        deviceId: global.currentPosData.deviceId,
                        deviceType: constantsXml.deviceType.officePos,
                        operatorRef: global.currentEmployeeData.employeeId,
                        operatorTypeExternalID: global.currentEmployeeData.employeeRoleId,
                        operatorTypeName: global.currentEmployeeData.employeeRoleName.slice(0, this.employeeRoleNameLength)
                    }
                }
            };

            const updateCardHolder = await this.service.updateCardHolder(cardholder.id, cardholderData);

            const getCardHolderById = await this.service.getCardHolderById(cardholder.id);

            res.status(200).json(getCardHolderById);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    recreateSmartCard = async (req, res) => {
        try {
            const card = req.body;
            if (!card.smartCardContent) {
                const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
                const riderType = await this.fareTableModel.RiderTypes.findOne({fareEnvironmentConfigurationId: activeConfig._id, type: 'Rider', isDefault: true});

                const smartCardProfiles = await this.fareTableModel.SmartCardProfiles.find({fareEnvironmentConfigurationId: activeConfig._id});
                const smartCardProfile = smartCardProfiles.find((f) => f.cardTypeShortNumber == 10 && f.cardVersionShortNumber == 2 && f.isEnabled);
                const cardVersion = (smartCardProfile) ? smartCardProfile.mediaPurposeType : constantsXml.smartCardVersion.anonymous.toUpperCase();
                const cardType = (smartCardProfile) ? smartCardProfile.cardType : constantsXml.smartCardType.standard;

                const cardData = {
                    cardId: card.smartCardUIdNew,
                    cardType: parseInt(smartCardProfile.cardTypeShortNumber),
                    cardVersion: parseInt(smartCardProfile.cardVersionShortNumber),
                    cardIteration: 0,
                    issuerId: global.currentPosData.organizationExternalId,
                    transactionCounter: 0,
                    blockTimestamp: moment.utc().format(this.dateTTimeFormat),
                    blockStatus: constantsXml.smartCardBlockStatus[0],
                    alic: 0,
                    alwc: 0,
                    cardExpirationTimestamp: moment.utc().add(10, 'years').format(this.dateTTimeFormat),
                    currencyCode: global.currentPosData.currencyCode,
                    preferredLanguage: global.currentPosData.firstLanguage.split('-')[0],
                    refundableDeposit: 0,
                    riderTypeExternalId: riderType.externalId
                };

                card.smartCardContent = await this.service.getTransformedPiccCardData(cardData);
            }
            let actions = {
                immediateActions: false,
                waitingActions: false
            };

            if (card.smartCardUIdOld != card.smartCardUIdNew && card.smartCardContent.cardUid == card.smartCardUIdNew) {
                const recreateSmartCard = await this.service.recreateSmartCard(card.smartCardUIdOld, card.smartCardUIdNew);

                if (recreateSmartCard) {
                    const getActionListBySmartCardUidv2 = await this.service.getActionListBySmartCardUidv2(card.smartCardUIdNew);
                    const action = 'recreateCard';
                    const actionListImmediate = getActionListBySmartCardUidv2.actionList.cardActions.find((f) => f.hasOwnProperty(action));
                    const actionListWaiting = getActionListBySmartCardUidv2.actionList.cardActions.filter((f) => !f.hasOwnProperty(action));

                    let immediateActionList = [];
                    let waitingActionList = [];

                    if (actionListImmediate) {
                        const actionListImmediateContracts = actionListImmediate[action].card.contracts.sort((a, b) => a.contractID - b.contractID);

                        immediateActionList.push({
                            cardType: actionListImmediate.cardType,
                            cardUid: actionListImmediate.cardID,
                            cardUidOld: card.smartCardUIdOld,
                            queue: false,
                            queueCounter: actionListImmediate.alic,
                            sequenceNumber: actionListImmediate.sequenceNumber,
                            action: action,
                            actionValue: actionListImmediate.cardID,
                            actionParams: {
                                card: {
                                    timestamp: moment.utc(actionListImmediate[action].card.timestamp).format(this.dateTTimeFormat),
                                    riderType: actionListImmediate[action].card.riderType,
                                    storedValue: actionListImmediate[action].card.storedValue,
                                    readOnly: {
                                        uid: actionListImmediate[action].card.readOnly.uid,
                                        type: actionListImmediate[action].card.readOnly.type,
                                        expiryTime: moment.utc(actionListImmediate[action].card.readOnly.expiryTime).format(this.dateTTimeFormat),
                                        alic: (actionListImmediate[action].card.readOnly.alic) ? actionListImmediate[action].card.readOnly.alic : 0,
                                        alwc: (actionListImmediate[action].card.readOnly.alwc) ? actionListImmediate[action].card.readOnly.alwc : 0
                                    },
                                    transactionCounter: (actionListImmediate[action].card.transactionCounter) ? actionListImmediate[action].card.transactionCounter : 0,
                                    blockStatus: {
                                        status: (actionListImmediate[action].card.blockStatus) ? actionListImmediate[action].card.blockStatus.status : constantsXml.smartCardBlockStatus[0],
                                        lastChangeTime: (actionListImmediate[action].card.blockStatus) ? moment.utc(actionListImmediate[action].card.blockStatus.lastChangeTime).format(this.dateTTimeFormat) : moment.utc(0).format(this.dateTTimeFormat)
                                    },
                                    contracts: {
                                        contract: []
                                    }
                                }
                            }
                        });
                        for (let i=0; i<actionListImmediateContracts.length; i++) {
                            let contract = {};
                            if (actionListImmediateContracts[i].type == 'ePurse') {
                                contract = {
                                    type: actionListImmediateContracts[i].type,
                                    contractID: actionListImmediateContracts[i].contractID,
                                    instanceID: actionListImmediateContracts[i].instanceID,
                                    status: actionListImmediateContracts[i].status,
                                    //contractValue: actionListImmediate[action].card.storedValue
                                };
                            } else if (actionListImmediateContracts[i].type == 'periodPass') {
                                contract = {
                                    type: actionListImmediateContracts[i].type,
                                    contractID: actionListImmediateContracts[i].contractID,
                                    instanceID: actionListImmediateContracts[i].instanceID,
                                    status: actionListImmediateContracts[i].status,
                                    creationTime: moment.utc(actionListImmediateContracts[i].creationTime).format(this.dateTTimeFormat),
                                    lastUsageTime: (actionListImmediateContracts[i].lastUsageTime) ? moment.utc(actionListImmediateContracts[i].lastUsageTime).format(this.dateTTimeFormat) : '',
                                    expiryTime: moment.utc(actionListImmediateContracts[i].expiryTime).format(this.dateTTimeFormat)
                                };
                            } else if (actionListImmediateContracts[i].type == 'storedRides') {
                                contract = {
                                    type: actionListImmediateContracts[i].type,
                                    contractID: actionListImmediateContracts[i].contractID,
                                    instanceID: actionListImmediateContracts[i].instanceID,
                                    status: actionListImmediateContracts[i].status,
                                    ridesLeft: actionListImmediateContracts[i].ridesLeft
                                };
                            }

                            immediateActionList[0].actionParams.card.contracts.contract.push(contract);
                        }

                    }

                    if (actionListWaiting) {
                        const actionListWaitingContracts = actionListWaiting.sort((a, b) => a.sequenceNumber - b.sequenceNumber);

                        for (let i=0; i<actionListWaitingContracts.length; i++) {
                            let action = null;
                            let contract = {};
                            if (actionListWaitingContracts[i].hasOwnProperty('refundableDeposit')) {
                                action = 'refundableDeposit';
                                contract = {
                                    cardType: actionListWaitingContracts[i].cardType,
                                    cardUid: actionListWaitingContracts[i].cardID,
                                    queue: true,
                                    queueCounter: actionListWaitingContracts[i].alwc,
                                    sequenceNumber: actionListWaitingContracts[i].sequenceNumber,
                                    action: 'refundableDeposit',
                                    actionValue: actionListWaitingContracts[i][action].price,
                                    createIssue: false
                                };
                            } else if (actionListWaitingContracts[i].hasOwnProperty('addEPurseContract')) {
                                action = 'addEPurseContract';
                                contract = {
                                    cardType: actionListWaitingContracts[i].cardType,
                                    cardUid: actionListWaitingContracts[i].cardID,
                                    queue: true,
                                    queueCounter: actionListWaitingContracts[i].alwc,
                                    sequenceNumber: actionListWaitingContracts[i].sequenceNumber,
                                    action: 'AddEPurseContract',
                                    actionValue: actionListWaitingContracts[i][action].contractID,
                                    //contractValue: actionListWaitingContracts[i][action].valueCents,
                                    createIssue: false
                                };
                            } else if (actionListWaitingContracts[i].hasOwnProperty('addPeriodPass')) {
                                action = 'addPeriodPass';
                                contract = {
                                    cardType: actionListWaitingContracts[i].cardType,
                                    cardUid: actionListWaitingContracts[i].cardID,
                                    queue: true,
                                    queueCounter: actionListWaitingContracts[i].alwc,
                                    sequenceNumber: actionListWaitingContracts[i].sequenceNumber,
                                    action: action,
                                    actionValue: actionListWaitingContracts[i][action].contractID,
                                    actionParams: {
                                        reasonID: actionListWaitingContracts[i][action].reasonID,
                                        productID: actionListWaitingContracts[i][action].productID,
                                        creationDate: moment.utc(actionListWaitingContracts[i][action].preactivated.creationDate).format(this.dateTTimeFormat),
                                        expiryDate: moment.utc(actionListWaitingContracts[i][action].preactivated.expiryDate).format(this.dateTTimeFormat)
                                    },
                                    createIssue: false
                                };
                            } else if (actionListWaitingContracts[i].hasOwnProperty('addStoredRides')) {
                                action = 'addStoredRides';
                                contract = {
                                    cardType: actionListWaitingContracts[i].cardType,
                                    cardUid: actionListWaitingContracts[i].cardID,
                                    queue: true,
                                    queueCounter: actionListWaitingContracts[i].alwc,
                                    sequenceNumber: actionListWaitingContracts[i].sequenceNumber,
                                    action: action,
                                    actionValue: actionListWaitingContracts[i][action].contractID,
                                    actionParams: {
                                        rides: actionListWaitingContracts[i][action].rides
                                    },
                                    createIssue: false
                                };
                            }

                            waitingActionList.push(contract);
                        }
                    }

                    if (immediateActionList.length) {
                        const immediateCounter = await this.immediateActions(card.smartCardContent, immediateActionList);
                        actions.immediateActions = (immediateActionList.length == immediateCounter) ? true : false;
                    }

                    if (waitingActionList.length) {
                        const waitingCounter = await this.waitingActions(card.smartCardContent, waitingActionList);
                        actions.waitingActions = (waitingActionList.length == waitingCounter) ? true : false;
                    }

                    if (global.currentPosData.hasAccounts) {
                        const cardHolder = await this.service.getCardHolderBySmartCardUId(card.smartCardUIdNew);

                        await this.accountHolderServ.deregisterCard(cardHolder.cardHolderInfoExtended.cardHolderAddressLine2, card.smartCardUIdOld);
                        await this.accountHolderServ.registerCard(cardHolder.cardHolderInfoExtended.cardHolderAddressLine2, card.smartCardUIdNew);
                    }
                }
            }

            const actionsExecuted = (actions.immediateActions && actions.waitingActions) ? true : false;

            res.status(200).json(actionsExecuted);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    replaceSmartCard = async (req, res) => {
        try {
            const card = req.body;
            let actions = false;

            /*const replaceSmartCard = await this.service.replaceSmartCard(card.smartCardUIdOld, card.smartCardUIdNew);

            const getActionListBySmartCardUidv2 = await this.service.getActionListBySmartCardUidv2(card.smartCardUIdNew);
            const filteredActionList = getActionListBySmartCardUidv2.actionList.cardActions[0].recreateCard.card.contracts.filter((f) => moment.utc(f.expiryTime).diff(moment.utc(), 'days') > 0);

            if (filteredActionList.length) {
                const waitingCounter = await this.waitingActions(card.smartCardContent, filteredActionList);
                actions = (waitingActionList.length == waitingCounter) ? true : false;
            }*/

            res.status(200).json(actions);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    getCardPurchases = async (req, res) => {
        try {
            const card = req.query;

            const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
            const products = await this.fareTableModel.Products.find({fareEnvironmentConfigurationId: activeConfig._id, productEntityClass: constants.productEntityClass.fareSellableProduct});

            let getCardPurchases = await this.service.getCardPurchases(card.smartCardUId);

            let cardPurchases = [];

            let cardPurchasesLen = getCardPurchases.length;
            for (let i=0; i<cardPurchasesLen; i++) {
                //const product = products.find((f) => f.externalId == getCardPurchases[i].fareItemExternalId); // (product) ? product.name :

                cardPurchases.push({
                    productType: getCardPurchases[i].productType,
                    cost: getCardPurchases[i].cost / Math.pow(10, global.currentPosData.currencyDecimal),
                    timestamp: moment.utc(getCardPurchases[i].timestamp).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat),
                    deviceType: getCardPurchases[i].deviceType
                });
            }

            res.status(200).json(cardPurchases);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    getCardRides = async (req, res) => {
        try {
            const card = req.query;

            const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
            const products = await this.fareTableModel.Products.find({fareEnvironmentConfigurationId: activeConfig._id, productEntityClass: constants.productEntityClass.fareSellableProduct});

            let getCardRides = await this.service.getCardRides(card.smartCardUId);

            let cardRides = [];

            let cardRidesLen = getCardRides.length;
            for (let i=0; i<cardRidesLen; i++) {
                //const product = products.find((f) => f.externalId == getCardRides[i].fareItemExternalId); // (product) ? product.name :

                cardRides.push({
                    tripId: (getCardRides[i].tripId) ? getCardRides[i].tripId : '/',
                    timestamp: moment.utc(getCardRides[i].timestamp).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat),
                    routeName: getCardRides[i].routeName,
                    busId: getCardRides[i].busId,
                    fareProductType: getCardRides[i].fareProductType,
                    remainingValue: getCardRides[i].remainingValue,
                    remainingRides: (getCardRides[i].remainingRides) ? getCardRides[i].remainingRides : 0,
                    remainingDays: getCardRides[i].remainingDays,
                    actualBoardingStopName: getCardRides[i].actualBoardingStopName,
                    validationStatusType: getCardRides[i].validationStatusType
                });
            }

            res.status(200).json(cardRides);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    getTicketCustomParameters = async (req, res) => {
        try {
            const data = req.query;

            const deviceMnemonic = (data.deviceMnemonic) ? data.deviceMnemonic : 'POS';
            const fareItemIssuedNumber = parseInt(data.fareItemIssuedNumber);
            const terminalId = this.helpersCtrl.getTerminalId(fareItemIssuedNumber);
            const transactionId = this.helpersCtrl.getTransactionCounter(fareItemIssuedNumber);
            const deviceTransactionHistoryFilter = {
                from: null,
                groupParameters: [],
                junctions: [],
                maxResults: 200,
                maxResultsPlusOne: false,
                operator: 'and',
                parameters: [{
                    name: 'fileType',
                    operator: 'notequals',
                    type: 'string',
                    values: null,
                    value: 'OPER',
                    isDtoField: false
                }],
                sort: [{
                    sortField: 'deviceTransactionCounter',
                    isDesc: true,
                    isDtoField: false
                }]
            };

            let getTicketTransactionFileData = [];

            const getDevices = await this.service.getDevices(deviceMnemonic);

            if (getDevices) {
                const device = getDevices.statuses.find((f) => f.terminalId == terminalId);

                const getDeviceTransactionHistory = await this.service.getDeviceTransactionHistory(device.id, deviceTransactionHistoryFilter);

                const transactionData = getDeviceTransactionHistory.find((f) => f.transactionId == transactionId);

                const getTicketTransactionZipFile = await this.service.getTicketTransactionFile(deviceMnemonic, transactionData.batchId);

                const getTicketTransactionZipEntries  = new AdmZip(getTicketTransactionZipFile).getEntries();

                for (var i = 0; i < getTicketTransactionZipEntries.length; i++) {
                    if (getTicketTransactionZipEntries[i].entryName == transactionData.filename) {
                        const ticketTransactionFileData = await xml2js.parseStringPromise(getTicketTransactionZipEntries[i].getData().toString('utf8'), { explicitRoot: false, explicitArray: false });
                        getTicketTransactionFileData = (ticketTransactionFileData.hasOwnProperty('customTransactionParameters')) ? ticketTransactionFileData.customTransactionParameters.customTransactionParameter : [];
                    }
                }
            }

            res.status(200).json(getTicketTransactionFileData);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

}

module.exports = FinancialTransactionCtrl;
