var global_pricelist = (function () { function toTimeString(date) { if (date instanceof Date) { return date.toISOString().replace('T', ' ').replace('Z', ''); } throw new Error('Expected Date'); } function toDate(str) { if (str instanceof Date) { return str; } else if (typeof str == 'string') { var mainparts = str.split(' '); var dparts = mainparts[0].split('-'); var tparts = mainparts[1].split(':'); return new Date(dparts[0], parseInt(dparts[1]) - 1, dparts[2], tparts[0], tparts[1], tparts[2], 0); } return new Date(0); } return { payday: { pricelists: { '62': { type: 'loan-fee', fee_type_id: '62', tax_percentage: '0.000', data: {}, dimension: { evaluate: function (bag) { var vars = {}; var control = { canceled: false, quit: false }; bag['exit'] = function() { control.canceled = true; }; bag['stopProcessing'] = function() { bag['exit'](); control.quit = true; }; bag['skip'] = function() { control.skip = true; }; control.skip = false; // Stage: Stage (function () { // Group: Group if (true) { bag['setAmountRange'](300, 12000, 100); if (control.canceled) return; bag['setTermRange'](30, 30, 1); if (control.canceled) return; bag['setTermRange'](62, 62, 1); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; return control; } }, price: { evaluate: function (bag) { var vars = {}; var control = { canceled: false, quit: false }; bag['exit'] = function() { control.canceled = true; }; bag['stopProcessing'] = function() { bag['exit'](); control.quit = true; }; bag['skip'] = function() { control.skip = true; }; control.skip = false; // Stage: Stage (function () { // Group: Group if (true) { vars['varterm'] = bag['getTerm'](); if (control.canceled) return; vars['varamount'] = bag['getAmount'](); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if ((vars['varterm'] == 30 && vars['varamount'] <= 1500)) { bag['setRate'](132.004); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if (((vars['varterm'] == 30 && vars['varamount'] > 1500) && vars['varamount'] <= 2500)) { bag['setRate'](131.997); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if (((vars['varterm'] == 30 && vars['varamount'] > 2500) && vars['varamount'] <= 4000)) { bag['setRate'](132); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if (((vars['varterm'] == 30 && vars['varamount'] > 4000) && vars['varamount'] <= 4500)) { bag['setRate'](132.00123); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if (((vars['varterm'] == 30 && vars['varamount'] > 4500) && vars['varamount'] <= 5800)) { bag['setRate'](131.9988); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if (((vars['varterm'] == 30 && vars['varamount'] > 5800) && vars['varamount'] <= 7000)) { bag['setRate'](132); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if (((vars['varterm'] == 30 && vars['varamount'] > 7000) && vars['varamount'] <= 7500)) { bag['setRate'](132.0007); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if ((vars['varterm'] == 30 && vars['varamount'] > 7500)) { bag['setRate'](132); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if (vars['varterm'] == 62) { bag['setRate'](69.0323225806452); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; return control; } }, buildDimension: function (bag = {}) { if (!this.amounts) { var amounts = []; var terms = []; bag = Object.assign({ setAmountRange: function (min, max, step) { if (!Number.isInteger(min) || !Number.isInteger(max) || !Number.isInteger(step) || step <= 0 || min > max) { throw new Error('Parameters provided are not defined or have a wrong value'); } for (var i = min; i <= max; i += step) { if (amounts.indexOf(i) === -1) { amounts.push(i); } } }, setTermRange: function (min, max, step) { if (!Number.isInteger(min) || !Number.isInteger(max) || !Number.isInteger(step) || step <= 0 || min > max) { throw new Error('Parameters provided are not defined or have a wrong value'); } for (var i = min; i <= max; i += step) { if (terms.indexOf(i) === -1) { terms.push(i); } } } }, bag); this.dimension.evaluate(bag); amounts.sort(function (a, b) { return a - b; }); terms.sort(function (a, b) { return a - b; }); this.amounts = amounts; this.terms = terms; } return { amounts: this.amounts, terms: this.terms }; }, buildPrice: function (amount, term, pricelist_bag) { var price = null; var rate = 0; var pricebag = pricelist_bag || {}; pricebag.getAmount = function () { return parseFloat(amount); }; pricebag.getTerm = function () { return parseFloat(term); }; pricebag.setPrice = function (data) { price = data; }; pricebag.setRate = function (data) { rate = data; }; pricebag.parseFloat = parseFloat; this.price.evaluate(pricebag); return { price: parseFloat(price).toFixed(2), rate: parseFloat(rate).toFixed(10) }; } }, '63': { type: 'principal', fee_type_id: '63', tax_percentage: '0.000', data: {}, dimension: { evaluate: function (bag) { var vars = {}; var control = { canceled: false, quit: false }; bag['exit'] = function() { control.canceled = true; }; bag['stopProcessing'] = function() { bag['exit'](); control.quit = true; }; bag['skip'] = function() { control.skip = true; }; control.skip = false; // Stage: Stage (function () { // Group: Group if (true) { bag['setAmountRange'](300, 12000, 100); if (control.canceled) return; bag['setTermRange'](30, 30, 1); if (control.canceled) return; bag['setTermRange'](62, 62, 1); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; return control; } }, price: { evaluate: function (bag) { var vars = {}; var control = { canceled: false, quit: false }; bag['exit'] = function() { control.canceled = true; }; bag['stopProcessing'] = function() { bag['exit'](); control.quit = true; }; bag['skip'] = function() { control.skip = true; }; control.skip = false; // Stage: Stage (function () { // Group: Group if (true) { vars['varamnt'] = bag['getAmount'](); if (control.canceled) return; bag['setPrice'](vars['varamnt']); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; return control; } }, buildDimension: function (bag = {}) { if (!this.amounts) { var amounts = []; var terms = []; bag = Object.assign({ setAmountRange: function (min, max, step) { if (!Number.isInteger(min) || !Number.isInteger(max) || !Number.isInteger(step) || step <= 0 || min > max) { throw new Error('Parameters provided are not defined or have a wrong value'); } for (var i = min; i <= max; i += step) { if (amounts.indexOf(i) === -1) { amounts.push(i); } } }, setTermRange: function (min, max, step) { if (!Number.isInteger(min) || !Number.isInteger(max) || !Number.isInteger(step) || step <= 0 || min > max) { throw new Error('Parameters provided are not defined or have a wrong value'); } for (var i = min; i <= max; i += step) { if (terms.indexOf(i) === -1) { terms.push(i); } } } }, bag); this.dimension.evaluate(bag); amounts.sort(function (a, b) { return a - b; }); terms.sort(function (a, b) { return a - b; }); this.amounts = amounts; this.terms = terms; } return { amounts: this.amounts, terms: this.terms }; }, buildPrice: function (amount, term, pricelist_bag) { var price = null; var rate = 0; var pricebag = pricelist_bag || {}; pricebag.getAmount = function () { return parseFloat(amount); }; pricebag.getTerm = function () { return parseFloat(term); }; pricebag.setPrice = function (data) { price = data; }; pricebag.setRate = function (data) { rate = data; }; pricebag.parseFloat = parseFloat; this.price.evaluate(pricebag); return { price: parseFloat(price).toFixed(2), rate: parseFloat(rate).toFixed(10) }; } }, '64': { type: 'extension-fee', fee_type_id: '64', tax_percentage: '0.000', data: {}, dimension: { evaluate: function (bag) { var vars = {}; var control = { canceled: false, quit: false }; bag['exit'] = function() { control.canceled = true; }; bag['stopProcessing'] = function() { bag['exit'](); control.quit = true; }; bag['skip'] = function() { control.skip = true; }; control.skip = false; // Stage: Stage (function () { // Group: Group if (true) { bag['setAmountRange'](100, 12000, 5); if (control.canceled) return; bag['setTermRange'](30, 30, 1); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; return control; } }, price: { evaluate: function (bag) { var vars = {}; var control = { canceled: false, quit: false }; bag['exit'] = function() { control.canceled = true; }; bag['stopProcessing'] = function() { bag['exit'](); control.quit = true; }; bag['skip'] = function() { control.skip = true; }; control.skip = false; // Stage: final price (function () { // Group: Group if (true) { bag['setPrice'](0); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; return control; } }, buildDimension: function (bag = {}) { if (!this.amounts) { var amounts = []; var terms = []; bag = Object.assign({ setAmountRange: function (min, max, step) { if (!Number.isInteger(min) || !Number.isInteger(max) || !Number.isInteger(step) || step <= 0 || min > max) { throw new Error('Parameters provided are not defined or have a wrong value'); } for (var i = min; i <= max; i += step) { if (amounts.indexOf(i) === -1) { amounts.push(i); } } }, setTermRange: function (min, max, step) { if (!Number.isInteger(min) || !Number.isInteger(max) || !Number.isInteger(step) || step <= 0 || min > max) { throw new Error('Parameters provided are not defined or have a wrong value'); } for (var i = min; i <= max; i += step) { if (terms.indexOf(i) === -1) { terms.push(i); } } } }, bag); this.dimension.evaluate(bag); amounts.sort(function (a, b) { return a - b; }); terms.sort(function (a, b) { return a - b; }); this.amounts = amounts; this.terms = terms; } return { amounts: this.amounts, terms: this.terms }; }, buildPrice: function (amount, term, pricelist_bag) { var price = null; var rate = 0; var pricebag = pricelist_bag || {}; pricebag.getAmount = function () { return parseFloat(amount); }; pricebag.getTerm = function () { return parseFloat(term); }; pricebag.setPrice = function (data) { price = data; }; pricebag.setRate = function (data) { rate = data; }; pricebag.parseFloat = parseFloat; this.price.evaluate(pricebag); return { price: parseFloat(price).toFixed(2), rate: parseFloat(rate).toFixed(10) }; } }, '428': { type: 'preparation-fee', fee_type_id: '428', tax_percentage: '0.000', data: {}, dimension: { evaluate: function (bag) { var vars = {}; var control = { canceled: false, quit: false }; bag['exit'] = function() { control.canceled = true; }; bag['stopProcessing'] = function() { bag['exit'](); control.quit = true; }; bag['skip'] = function() { control.skip = true; }; control.skip = false; // Stage: Stage (function () { // Group: Group if (true) { bag['setAmountRange'](300, 12000, 100); if (control.canceled) return; bag['setTermRange'](30, 30, 1); if (control.canceled) return; bag['setTermRange'](62, 62, 1); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; return control; } }, price: { evaluate: function (bag) { var vars = {}; var control = { canceled: false, quit: false }; bag['exit'] = function() { control.canceled = true; }; bag['stopProcessing'] = function() { bag['exit'](); control.quit = true; }; bag['skip'] = function() { control.skip = true; }; control.skip = false; // Stage: Stage (function () { // Group: Group if (true) { bag['setPrice'](0); if (control.canceled) return; } else { bag['setPrice'](0); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; return control; } }, buildDimension: function (bag = {}) { if (!this.amounts) { var amounts = []; var terms = []; bag = Object.assign({ setAmountRange: function (min, max, step) { if (!Number.isInteger(min) || !Number.isInteger(max) || !Number.isInteger(step) || step <= 0 || min > max) { throw new Error('Parameters provided are not defined or have a wrong value'); } for (var i = min; i <= max; i += step) { if (amounts.indexOf(i) === -1) { amounts.push(i); } } }, setTermRange: function (min, max, step) { if (!Number.isInteger(min) || !Number.isInteger(max) || !Number.isInteger(step) || step <= 0 || min > max) { throw new Error('Parameters provided are not defined or have a wrong value'); } for (var i = min; i <= max; i += step) { if (terms.indexOf(i) === -1) { terms.push(i); } } } }, bag); this.dimension.evaluate(bag); amounts.sort(function (a, b) { return a - b; }); terms.sort(function (a, b) { return a - b; }); this.amounts = amounts; this.terms = terms; } return { amounts: this.amounts, terms: this.terms }; }, buildPrice: function (amount, term, pricelist_bag) { var price = null; var rate = 0; var pricebag = pricelist_bag || {}; pricebag.getAmount = function () { return parseFloat(amount); }; pricebag.getTerm = function () { return parseFloat(term); }; pricebag.setPrice = function (data) { price = data; }; pricebag.setRate = function (data) { rate = data; }; pricebag.parseFloat = parseFloat; this.price.evaluate(pricebag); return { price: parseFloat(price).toFixed(2), rate: parseFloat(rate).toFixed(10) }; } }, '-67': { type: 'interest-fee', fee_type_id: '67', tax_percentage: '0.000', data: {}, dimension: { evaluate: function (bag) { var vars = {}; var control = { canceled: false, quit: false }; bag['exit'] = function() { control.canceled = true; }; bag['stopProcessing'] = function() { bag['exit'](); control.quit = true; }; bag['skip'] = function() { control.skip = true; }; control.skip = false; // Stage: Stage (function () { // Group: Group if (true) { bag['setAmountRange'](300, 12000, 100); if (control.canceled) return; bag['setTermRange'](30, 30, 1); if (control.canceled) return; bag['setTermRange'](62, 62, 1); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; return control; } }, price: { evaluate: function (bag) { var vars = {}; var control = { canceled: false, quit: false }; bag['exit'] = function() { control.canceled = true; }; bag['stopProcessing'] = function() { bag['exit'](); control.quit = true; }; bag['skip'] = function() { control.skip = true; }; control.skip = false; // Stage: Stage (function () { // Group: Group if (true) { bag['setRate'](18.5); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; return control; } }, buildDimension: function (bag = {}) { if (!this.amounts) { var amounts = []; var terms = []; bag = Object.assign({ setAmountRange: function (min, max, step) { if (!Number.isInteger(min) || !Number.isInteger(max) || !Number.isInteger(step) || step <= 0 || min > max) { throw new Error('Parameters provided are not defined or have a wrong value'); } for (var i = min; i <= max; i += step) { if (amounts.indexOf(i) === -1) { amounts.push(i); } } }, setTermRange: function (min, max, step) { if (!Number.isInteger(min) || !Number.isInteger(max) || !Number.isInteger(step) || step <= 0 || min > max) { throw new Error('Parameters provided are not defined or have a wrong value'); } for (var i = min; i <= max; i += step) { if (terms.indexOf(i) === -1) { terms.push(i); } } } }, bag); this.dimension.evaluate(bag); amounts.sort(function (a, b) { return a - b; }); terms.sort(function (a, b) { return a - b; }); this.amounts = amounts; this.terms = terms; } return { amounts: this.amounts, terms: this.terms }; }, buildPrice: function (amount, term, pricelist_bag) { var price = null; var rate = 0; var pricebag = pricelist_bag || {}; pricebag.getAmount = function () { return parseFloat(amount); }; pricebag.getTerm = function () { return parseFloat(term); }; pricebag.setPrice = function (data) { price = data; }; pricebag.setRate = function (data) { rate = data; }; pricebag.parseFloat = parseFloat; this.price.evaluate(pricebag); return { price: parseFloat(price).toFixed(2), rate: parseFloat(rate).toFixed(10) }; } }, }, fee_types: { 62: 'loan_fee', 63: 'principal', 64: 'extension_fee', 65: 'penalty', 66: 'late_fee', 67: 'interest_fee', 72: 'payout_fee', 89: 'cash_fee', 103: 'court_fee', 105: 'court_fee', 107: 'court_fee', 109: 'court_fee', 111: 'court_fee', 113: 'court_fee', 165: 'consolidation_fee', 249: 'court_fee', 250: 'court_fee', 357: 'sale_fee', 428: 'preparation_fee', }, promotions: { }, loanlimit: { evaluate: function (bag) { var vars = {}; var control = { canceled: false, quit: false }; bag['exit'] = function() { control.canceled = true; }; bag['stopProcessing'] = function() { bag['exit'](); control.quit = true; }; bag['skip'] = function() { control.skip = true; }; control.skip = false; // Stage: Failsafe for homepage (function () { // Group: Group if (true) { bag['setAvailableLimit'](12000); if (control.canceled) return; bag['setLowerBound'](500); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; control.skip = false; // Stage: FL & CL limits (function () { // Group: Group if ((bag['10507'] == 883 || ((bag['10526'] == 883 && bag['799'] == 0) && bag['4438'] == 1))) { bag['skip'](); if (control.canceled) return; } else { vars['maxloanamount'] = 12000; if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: 1st if (bag[799] == 0) { vars['maxloanamount'] = 3000; bag['setAvailableLimit'](12000); bag['setVisibleLimit'](3000); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: 2nd if ((bag['10049'] == 1 && bag['799'] > 0)) { vars['maxloanamount'] = 3500; if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: 3rd if ((bag['10049'] == 2 && bag['799'] > 0)) { vars['maxloanamount'] = 4500; if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: 4th if ((bag['10049'] == 3 && bag['799'] > 0)) { vars['maxloanamount'] = 6000; if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: 5th if ((bag['10049'] == 4 && bag['799'] > 0)) { vars['maxloanamount'] = 7000; if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: 6th if ((bag['10049'] == 5 && bag['799'] > 0)) { vars['maxloanamount'] = 8400; if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: 7th and next if ((bag['10049'] >= 6 && bag['799'] > 0)) { vars['maxloanamount'] = 12000; if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if (true) { bag['setVisibleLimit'](vars['maxloanamount']); if (control.canceled) return; bag['setLowerBound'](500); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; control.skip = false; // Stage: Sample Limits (function () { // Group: Group if ((bag['10507'] == 883 || ((bag['10526'] == 883 && bag['799'] == 0) && bag['4438'] == 1))) { vars['maxloanamount'] = 1500; if (control.canceled) return; } else { bag['skip'](); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: 1st if (bag[799] == 0) { vars['maxloanamount'] = 3000; vars['lowerbound'] = 300; if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: 2nd if ((bag['10049'] == 1 && bag['799'] > 0)) { vars['maxloanamount'] = 1000; vars['lowerbound'] = 300; if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: 3rd if ((bag['10049'] >= 2 && bag['799'] > 0)) { vars['maxloanamount'] = 1500; vars['lowerbound'] = 500; if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if (true) { bag['setVisibleLimit'](vars['maxloanamount']); if (control.canceled) return; bag['setLowerBound'](vars['lowerbound']); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; control.skip = false; // Stage: DayLimits (function () { // Group: only 30 days CL regulation if (bag['799'] > 0) { bag['setDayLimit'](30); bag['setAvailableDayLimit'](30); bag['exit'](); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: 62 days for affiliate promo62 if ((bag['799'] == 0 && ([459].indexOf(bag[-8]) !== -1 || bag['190'] == 459))) { bag['setDayLimit'](62); bag['exit'](); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: 30 days for extraportfel.pl and mobile if ((bag['799'] == 0 && [157,809].indexOf(bag[-8]) !== -1)) { bag['setDayLimit'](30); bag['setAvailableDayLimit'](30); bag['exit'](); if (control.canceled) return; } else { bag['setDayLimit'](30); bag['setAvailableDayLimit'](30); bag['exit'](); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; return control; } }, freshBagLimit: function (bag) { var n = { _available_limit: null, _day_lower_bound: null, _visible_limit: null, _day_limit: null, _available_day_limit: null, _lowerbound: null, _income_verify_limit: null, _reject_message: null, _slider_default: null, setSliderDefault: function (amount, term, product) { if (Number.isInteger(amount) && Number.isInteger(term) && Number.isInteger(product)) { this._slider_default = { "amount" : parseFloat(amount).toFixed(2), "term" : parseFloat(term).toFixed(0), "default_product" : product }; } }, setIncomeVerifyLimit: function (limit) { this._income_verify_limit = limit; }, setLimit: function (limit) { this._visible_limit = limit; }, setVisibleLimit: function (limit) { this._visible_limit = limit; }, setAvailableLimit: function (limit) { this._available_limit = limit; }, setHardLimit: function (limit) { this._available_limit = limit; }, getRegistrationField: function (field) { return this[field] || 0; }, getAmount: function () { return bag[-3]; }, getMaximumRepayableAmount: function () { return bag[-22]; }, setDayLowerBound: function (amount) { this._day_lower_bound = amount; }, setDayLimit: function (limit) { this._day_limit = limit; }, setAvailableDayLimit: function (limit) { this._available_day_limit = limit; }, setInstallmentsCount: function (limit) { this._day_limit = limit; }, getInstallmentsCount: function (limit) { return this._day_limit; }, setRejectLoan: function (message) { this._reject_message = message; }, setExtensionLimit: function (limit) { this._extension_limit = limit; }, setLowerBound: function (limit) { this._lowerbound = limit; }, getMonthlyPayment: function (initialPrincipal, annualInterestRate, term) { var annualInterestRate = annualInterestRate / 100; var monthlyInterestRate = annualInterestRate / 12; var divident = initialPrincipal * monthlyInterestRate; var divisor = 1 - (1 / Math.pow((1 + monthlyInterestRate), term)); return divident / divisor; }, getOverpaymentCoefficient: function (annualInterestRate, term) { var initialPrincipal = 1000; var monthlyPayment = this.getMonthlyPayment(initialPrincipal, annualInterestRate, term); var totalRepayableAmount = monthlyPayment * term; return totalRepayableAmount / initialPrincipal; }, getInterestRateApproximationFromAnnuity: function (termMonths) { var termDays = termMonths * 30; var constant = 1.000018; var resultBase = constant + (termDays * 0.0007 + 1) / termMonths; var resultLog = Math.log2(1 + 1 / termMonths); resultBase = Math.pow(resultBase, (1 / resultLog)) - 1; var interestRateApproximationFromAnnuity = ((Math.pow(resultBase, resultLog) - constant) * 12) * 100; var flooredResult = Math.floor(interestRateApproximationFromAnnuity * 100) / 100; return flooredResult; }, calculateTermUpperLimit: function (amount, maxRepayable, interest) { let monthlyInterest = (interest/100) / 12; return (isNaN(Math.ceil(Math.log(1 / (1 - ((amount * monthlyInterest) / maxRepayable))) / Math.log1p(monthlyInterest))) ? 0 : Math.ceil(Math.log(1 / (1 - ((amount * monthlyInterest) / maxRepayable))) / Math.log1p(monthlyInterest))); }, }; for (var id in bag) { if (!(id in n)) { n[id] = bag[id]; } } if ( ! (-8 in n)) { n[-8] = 459; } n["round"] = this.round return n; }, filterAvailableTerms: function (bag, checkFunction) { var limits = this.evaluateLimit(bag); var lowestAvailableTerm = null; for (var index = 0; index < limits.terms.length; index++) { var t = limits.terms[index]; bag['-2'] = t; var currentTermLimits = this.evaluateLimit(bag); if (checkFunction) { if (checkFunction(currentTermLimits)) { lowestAvailableTerm = t; break; } } else if (currentTermLimits.amounts_available.length > 0) { lowestAvailableTerm = t; break; } } return limits.terms.slice(limits.terms.indexOf(lowestAvailableTerm)); }, evaluateLimit: function (bag) { var finalAmounts_available = []; var finalAmounts_visible = []; var finalDays = []; var finalDays_available = []; var finalExtDays = []; var ibag = this.freshBagLimit(bag); var result = this.loanlimit.evaluate(ibag); this.pricelists[62].buildDimension(bag); this.pricelists[63].buildDimension(bag); this.pricelists[64].buildDimension(bag); this.pricelists[428].buildDimension(bag); this.pricelists[-67].buildDimension(bag); for (var i = 0; i < this.pricelists[63].amounts.length; i++) { var amn = this.pricelists[63].amounts[i]; if ((ibag._available_limit == null || parseFloat(amn) <= ibag._available_limit) && (ibag._lowerbound == null || parseFloat(amn) >= ibag._lowerbound)) { finalAmounts_available.push(parseFloat(amn).toFixed(2)); } if ((ibag._visible_limit == null || parseFloat(amn) <= ibag._visible_limit) && (ibag._lowerbound == null || parseFloat(amn) >= ibag._lowerbound)) { finalAmounts_visible.push(parseFloat(amn).toFixed(2)); } } for (var i = 0; i < this.pricelists[63].terms.length; i++) { var day = this.pricelists[63].terms[i]; if ((ibag._day_limit == null || parseFloat(day) <= ibag._day_limit) && (ibag._day_lower_bound == null || parseFloat(day) >= ibag._day_lower_bound)) { finalDays.push(parseFloat(day).toFixed(0)); } if ((ibag._available_day_limit == null || parseFloat(day) <= ibag._available_day_limit) && (ibag._day_lower_bound == null || parseFloat(day) >= ibag._day_lower_bound)) { finalDays_available.push(parseFloat(day).toFixed(0)); } } for (var i = 0; i < this.pricelists[64].terms.length; i++) { var day = this.pricelists[64].terms[i]; if (ibag._extension_limit == null || parseFloat(day) <= ibag._extension_limit) { finalExtDays.push(parseFloat(day).toFixed(0)) } } return { 'pricelist-amount-limit': ibag._visible_limit, 'pricelist-amount-hard-limit': ibag._available_limit, 'pricelist-amount-lower-bound': ibag._lowerbound, 'pricelist-day-limit': ibag._day_limit, 'pricelist-available-day-limit': ibag._available_day_limit, 'pricelist-income-verify-limit': ibag._income_verify_limit, 'pricelist-day-lower-bound': ibag._day_lower_bound, 'reject_message': ibag._reject_message, amounts_available: finalAmounts_available, amounts_visible: finalAmounts_visible, terms: finalDays, terms_available: finalDays_available, extterms: finalExtDays, slider_default: ibag._slider_default } }, freshBagProlongPermissionRule: function (bag) { var n = { _loanProlongPermitted: false, _loanProlongPermittedToPayday: false, _loanProlongPermittedToInstallment: false, _loanProlongPermittedToCreditline: false, _errorMessage: null, _errorMessageProlongToPayday: null, _errorMessageProlongToInstallment: null, _errorMessageProlongToCreditline: null, _loanSettingId: null, _feetypes: [], allowProlong: function () { this._loanProlongPermitted = true; this._errorMessage = null; }, allowProlongToPayday: function () { this._loanProlongPermittedToPayday = true; this._errorMessageProlongToPayday = null; }, allowProlongToInstallment: function () { this._loanProlongPermittedToInstallment = true; this._errorMessageProlongToInstallment = null; }, allowProlongToCreditline: function () { this._loanProlongPermittedToCreditline = true; this._errorMessageProlongToCreditline = null; }, prohibitProlong: function (msg) { this._loanProlongPermitted = false; this._errorMessage = msg; }, setLoanSettings: function (id) { this._loanSettingId = id; }, prohibitProlongToPayday: function (msg) { this._loanProlongPermittedToPayday = false; this._errorMessageProlongToPayday = msg; }, prohibitProlongToInstallment: function (msg) { this._loanProlongPermittedToInstallment = false; this._errorMessageProlongToInstallment = msg; }, prohibitProlongToCreditline: function (msg) { this._loanProlongPermittedToCreditline = false; this._errorMessageProlongToCreditline = msg; }, feetypeToNewLoan: function (feetype, percentage, maxvalue) { this._feetypes.push({ "feetype": feetype, "percentage": percentage, "maxvalue": maxvalue }); }, hasLoanOpenInAllLenders: function () { return bag[-33]; }, isRequestForLoanFromPlaton: function () { return bag["is-backend"] || false; }, isRequestForLoanFromClientzone: function () { return !bag["is-backend"]; }, }; for (var id in bag) { if ((typeof bag[id] === 'string') && !isNaN(bag[id]) && isFinite(bag[id]) && typeof bag[id] !== "boolean") { bag[id] = parseFloat(bag[id]); } if (!(id in n)) { n[id] = bag[id]; } } return n; }, evaluateProlongPermisson: function (bag) { var ibag = this.freshBagProlongPermissionRule(bag); var result = {}; return { 'loan-prolong-permitted': false, 'loan-prolong-permitted-to-payday': false, 'loan-prolong-permitted-to-installment': false, 'loan-prolong-permitted-to-creditline': false, 'loan-prolong-error': undefined, 'loan-prolong-to-payday-error': undefined, 'loan-prolong-to-installment-error': undefined, 'loan-prolong-to-creditline-error': undefined, 'prolong-loan-setting-id': undefined, 'loan-feetype-mapping': undefined } }, evaluateExtensionPromotions: function (now, bag, values) { var ibag = {}; var result = null; var existingpromo = null; var percentpromo = null; var percentpromos = []; var usedPromotions = []; if (bag['existing-promotions']) { var existingPromos = bag['existing-promotions'].sort(function (a, b) { return b.priority - a.priority; }); ibag = this.freshBag(bag, null); for (var i = 0; i < existingPromos.length; i++) { existingpromo = existingPromos[i]; if (existingpromo['type'] == 'percent') { percentpromo = { 'amount': parseFloat(existingpromo['amount']), 'id': existingpromo['promoId'] }; if (existingpromo['installmentIndex'] !== undefined) { // installment index can be 0 and then it wouldnt go in percentpromo['installment_index'] = existingpromo['installmentIndex'] + 1; // + 1 because for promostaging the first installment is index 1 and not 0 } if (!percentpromos[existingpromo['feeTypeId']]) { percentpromos[existingpromo['feeTypeId']] = []; } percentpromos[existingpromo['feeTypeId']].push(percentpromo); usedPromotions.push(existingpromo['promoId'].toString()); } } ibag['discounts_per'] = percentpromos; this.fillDiscounts(ibag, values); } }, extensionPermissionRules: { evaluate: function (bag) { var vars = {}; var control = { canceled: false, quit: false }; bag['exit'] = function() { control.canceled = true; }; bag['stopProcessing'] = function() { bag['exit'](); control.quit = true; }; bag['skip'] = function() { control.skip = true; }; control.skip = false; // Stage: allow employee (test) customers on production (function () { // Group: Group if (bag['1071'] == 2151430) { bag['allowExtension'](); bag['exit'](); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; control.skip = false; // Stage: Prohibit (DC categories,buyers,customer groups) (function () { // Group: Prohibited DC categories if ([111,113,115,117,118,119,291,292,293,564,560,561,563,565,892,114].indexOf(parseInt(bag['2593'])) !== -1) { bag['prohibitExtension']('DC Category is prohibited'); bag['exit'](); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Prohibited Buyers if ([250,252,28,247,144].indexOf(parseInt(bag['2697'])) !== -1) { bag['prohibitExtension']('Buyer is prohibited'); bag['exit'](); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Prohibited Customer Groups if ([628,524,671,842,359,1061,1062,1063,1064,1065,1066,1124,1123,1122,1121].indexOf(parseInt(bag['196'])) !== -1) { bag['prohibitExtension']('Customer Group is prohibited'); bag['exit'](); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; control.skip = false; // Stage: Rules for ext from the website (function () { // Group: prohibit for already extended loans or delay>60 if ((bag[-14] == 0 && (bag['8736'] > 0 || (bag['8736'] == 0 && bag['802'] > 60)))) { bag['prohibitExtension']('1. Ext limit or delay exceeded'); bag['exit'](); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: allow for loans NOT extended yet and delay<60 if ((bag[-14] == 0 && (bag['8736'] == 0 && bag['802'] <= 60))) { bag['allowExtension'](); bag['exit'](); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; control.skip = false; // Stage: Rules for ext from Platon (function () { // Group: ext source check if (bag[-14] == 1) { } else { bag['exit'](); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: allow for PR risk customers if (bag['196'] == 553) { bag['allowExtension'](); bag['exit'](); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: prohibit for already extended loans and delay>60 if ((bag['8736'] > 0 && bag['802'] < 60)) { bag['allowExtension'](); bag['exit'](); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: allow for loans NOT extended yet and delay >=30&<=60 if ((bag['8736'] == 0 && (bag['802'] >= 30 && bag['802'] <= 60))) { bag['allowExtension'](); bag['exit'](); if (control.canceled) return; } else { bag['prohibitExtension']('Extension prohibited'); bag['exit'](); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; return control; }, freshBag: function (bag, extensionPrice) { var n = { _loanExtensionPermitted: false, _errorMessage: null, allowExtension: function () { this._loanExtensionPermitted = true; this._errorMessage = null; }, prohibitExtension: function (msg) { this._loanExtensionPermitted = false; this._errorMessage = msg; } }; for (var id in bag) { if ((typeof bag[id] === 'string') && !isNaN(bag[id]) && isFinite(bag[id])) { bag[id] = parseFloat(bag[id]); } if (!(id in n)) { n[id] = bag[id]; } } n[-11] = extensionPrice; return n; } }, evaluateExtension: function (bag) { bag[-3] = bag[-3] || bag['loan-amount'] bag[-2] = bag[-2] || bag['loan-term'] bag[-1] = bag[-1] || bag['loan-extension-term'] var values = { discounts_per: {}, discounts_amn: {}, extensions: {}, total_extension_term: 0, prices: {}, feeRates: {}, discounts: {}, discounted: {} } var that = this; var now = new Date().getTime(); var amn = parseFloat(bag[-3]).toFixed(2); var term = parseFloat(bag[-2]).toFixed(0); var extterm = parseFloat(bag[-1]).toFixed(0); if (bag[-7] && (bag[-7] == '-1' || bag[-7] == '1')) { aleg: for (var id in this.pricelists) { if (!('buildPrice' in this.pricelists[id])) { if (!(amn in this.pricelists[id].data)) { var pamn = '0.0'; for (var iamn in this.pricelists[id].data) { if (parseFloat(iamn) > parseFloat(amn)) { if (bag[-7] == '1') { amn = iamn; break aleg; } else { if (parseFloat(pamn) == 0.0) { throw new Error('There is no smaller amount than ' + amn + ' to round down to for pricelist with id ' + id); } amn = pamn; break aleg; } } pamn = iamn; } if (parseFloat(pamn) < parseFloat(amn) && bag[-7] == '1') { amn = pamn; break aleg; } if (parseFloat(pamn) < parseFloat(amn) && bag[-7] == '-1') { amn = pamn; break aleg; } } } } } for (var id in this.pricelists) { if ( id != 64 && id != -67 ) { continue; } if ('buildPrice' in this.pricelists[id]) { var save = bag['pricelist_bag']; bag['pricelist_bag'] = {}; for (var x in save) { bag['pricelist_bag'][x] = save[x]; } for (var x in bag) { if (x != 'pricelist_bag') { bag['pricelist_bag'][x] = bag[x]; } } var pricelist_bag = {}; for (var item in bag['pricelist_bag']) { pricelist_bag[item] = bag['pricelist_bag'][item]; } var pricelistResult = this.pricelists[id].buildPrice(amn, extterm, pricelist_bag); var price = parseFloat(pricelistResult.price); var feeRate = parseFloat(pricelistResult.rate); if (id == -67 && isNaN(price) && !isNaN(feeRate)) { price = (parseFloat(bag['open-principal']) * feeRate / 100 / 366 * extterm).toFixed(2); } values.prices[id] = price; values.feeRates[id] = feeRate; } else { if (!this.pricelists[id].data) continue; if (!(amn in this.pricelists[id].data)) { throw new Error('Invalid Amount ' + amn + ' for pricelist with id ' + id); } if (!extterm in this.pricelists[id].data[amn]) { throw new Error('Invalid Term ' + extterm + ' for pricelist with id ' + id + ' for amount ' + amn); } values.prices[id] = parseFloat(this.pricelists[id].data[amn][extterm]); values.feeRates[id] = 0; } } if ('existing-fees' in bag) { for (var id in bag['existing-fees']) { if (bag['existing-fees'][id].amount == 0) { continue; } if (!values.prices[id]) { values.prices[id] = 0; } values.prices[id] += bag['existing-fees'][id].amount; } } this.evaluateExtensionPromotions(now, bag, values); values.discounts_per[-67] = values.discounts_per[67]; values.discounts_amn[-67] = values.discounts_amn[67]; var details2 = { 'percent-discounts' : {}, 'amount-discounts' : {}, 'extensions': {}, original: {}, discount: {}, final: {} }; for (var id in values.prices) { var feeType = null; if (id in this.pricelists) { feeType = this.pricelists[id].type; } else if (id in bag['existing-fees']) { feeType = bag['existing-fees'][id].type; } var discountPercentAr = values.discounts_per[id]; var discountPercent = 0; if (discountPercentAr) { details2['percent-discounts'][feeType] = discountPercentAr; for (var i = 0; i < discountPercentAr.length; i++) { discountPercent += parseFloat(discountPercentAr[i].amount); } } var discountAmountAr = values.discounts_amn[id]; var discountAmount = 0; if (discountAmountAr) { details2['amount-discounts'][feeType] = discountAmountAr; for (var i = 0; i < discountAmountAr.length; i++) { discountAmount += parseFloat(discountAmountAr[i].amount); } } var discount = Math.min(values.prices[id] * discountPercent / 100 + discountAmount, values.prices[id]); var finalAmount = values.prices[id] - discount; values.discounts[id] = parseFloat(discount.toFixed(2)); values.discounted[id] = parseFloat(finalAmount.toFixed(2)); if (id != -67) { details2.original[feeType] = values.prices[id]; details2.discount[feeType] = values.discounts[id]; details2.final[feeType] = values.discounted[id]; } } var ibag = null; var extensionPrice = values.discounted[64]; ibag = this.extensionPermissionRules.freshBag(bag, extensionPrice); this.extensionPermissionRules.evaluate(ibag); var calc_loan_duedate = bag['loan_duedate'] ? new Date(Date.parse(bag['loan_duedate'])) : null; if (calc_loan_duedate) { var calc_loan_is_payday = bag['loan_is_payday']; var calc_extension_permitted = ibag == null || (!ibag._errorMessage && ibag._loanExtensionPermitted); var calc_term = parseInt(extterm); var calc_duedate = null; var calc_now = new Date(); var calc_late = calc_loan_duedate < calc_now; var calc_actDuedate = calc_late ? calc_now : calc_loan_duedate; if (calc_extension_permitted) { calc_duedate = calc_actDuedate; if (calc_loan_is_payday) { calc_duedate.setDate(calc_duedate.getDate() + calc_term); } else { calc_duedate.setMonth(calc_duedate.getMonth() + calc_term); } calc_duedate = (calc_duedate.toISOString()).substr(0, 10); } } return { 'loan-amount': bag[-3], 'loan-term': bag[-2], 'loan-extension-term': bag[-1], 'loan-extension-fees': values.discounted[64], 'loan-extension-duedate': calc_duedate, 'loan-extension-permitted': ibag._loanExtensionPermitted, 'loan-extension-error': ibag._errorMessage, 'loan-original-extension-fees': values.prices[64], 'loan-free-extension': values.total_extension_term, details: details2, 'loan-additional-extension-fees': values.discounted[-67], 'loan-additional-original-extension-fees': values.prices[-67], 'loan-additional-extension-fee-type-id': 67, 'loan-additional-extension-fee-rate': values.feeRates[-67], }; }, evaluateAdditionalPayout: function (bag) { bag[-3] = bag[-3] || bag['loan-amount'] bag[-2] = bag[-2] || bag['loan-term'] bag[-1] = bag[-1] || bag['loan-additional-payout-amount'] var prices = {} var that = this; var now = new Date().getTime(); var amn = parseFloat(bag[-3]).toFixed(2); var term = parseFloat(bag[-2]).toFixed(0); var additionalPayoutAmount = parseFloat(bag[-1]).toFixed(2); if ('buildPrice' in this.additional_payout_pricelist) { var save = bag['pricelist_bag']; bag['pricelist_bag'] = {}; for (var x in save) { bag['pricelist_bag'][x] = save[x]; } for (var x in bag) { if (x != 'pricelist_bag') { bag['pricelist_bag'][x] = bag[x]; } } prices = this.additional_payout_pricelist.buildPrice(additionalPayoutAmount, term, bag['pricelist_bag']); } else { if (!(additionalPayoutAmount in this.additional_payout_pricelist.data)) { throw new Error('Invalid Amount ' + additionalPayoutAmount + ' for additionalPayoutPricelist with id ' + this.additional_payout_pricelist.id); } if (!term in this.additional_payout_pricelist.data[additionalPayoutAmount]) { throw new Error('Invalid Term ' + term + ' for additionalPayoutPricelist with id ' + this.additional_payout_pricelist.id + ' for amount ' + additionalPayoutAmount); } prices = this.additional_payout_pricelist.data[additionalPayoutAmount][term]; } return { 'loan-amount': bag[-3], 'loan-term': bag[-2], 'loan-additional-payout-amount': bag[-1], 'loan-additional-payout-fees': prices, }; }, calculateAPR: function (startAmount, values) { var PRECISION = 0.00001; var MAX_ATTEMPTS = 1000; var bsMinB = -0.999999999999; var bsMaxB = 2000000000; var principal = startAmount; var attempts = 0; var repaymentTotal = 0; var APRRate = 1; while (Math.abs(repaymentTotal - principal) > PRECISION) { if (attempts >= MAX_ATTEMPTS || (bsMaxB == bsMinB) && (bsMaxB == APRRate)) { return NaN; } attempts++; if (attempts > 1) { if (repaymentTotal < principal) { bsMaxB = APRRate; } else { bsMinB = APRRate; } APRRate = bsMaxB - (bsMaxB - bsMinB) / 2; } repaymentTotal = 0; for (var i = 0; i < values.length; i++) { var rt = Math.pow(1 + APRRate, (i + 1) / 12); // 12 = compounding period repaymentTotal += values[i].payment / rt; } } return APRRate * 100; }, DaysBetween: function(date1, date2) { var oneDay = 24*60*60*1000; return Math.round(Math.abs((date1.getTime() - date2.getTime())/oneDay)); }, XNPV: function(rate, values, daysInYear) { var payment_fee_percentage = 0.00 / 100; var xnpv = 0.0; var firstDate = new Date(values[0].date); for (var i = 0, max = values.length; i < max; i += 1) { var tmp = values[i]; var value; if (tmp.payment < 0) { value = tmp.payment * (1 - payment_fee_percentage); } else { value = tmp.payment * (1 + payment_fee_percentage); } var date = new Date(tmp.date); xnpv += value / Math.pow(1 + rate, this.DaysBetween(firstDate, date)/daysInYear); } return xnpv; }, aprForPayday: function(initialAmount, term, toRepay, daysInYear) { var payment_fee_percentage = 0.00 / 100; return Math.round((Math.pow(toRepay * (1 + payment_fee_percentage) / (initialAmount * (1 - payment_fee_percentage)), daysInYear / term) - 1) * 10000) / 100; }, arForPayday: function (initialAmount, term, toRepay, daysInYear) { var price = toRepay - initialAmount; return Math.round(price / initialAmount * (daysInYear / term) * 10000) / 100; }, rpyForPayday: function (initialAmount, term, toRepay, daysInYear) { var price = toRepay - initialAmount; return Math.round((price / initialAmount / term * daysInYear) * 10000) / 100; }, rpmForPayday: function (initialAmount, term, toRepay, daysInYear) { var price = toRepay - initialAmount; return Math.round((price / initialAmount / term * daysInYear / 12) * 10000) / 100; }, rpdForPayday: function (initialAmount, term, toRepay) { var price = toRepay - initialAmount; return Math.round((price / initialAmount / term) * 10000) / 100; }, XIRR: function(initialAmount, values, guess, forcePayoutDate, daysInYear) { if (!guess) guess = 0.1; var payoutDate = forcePayoutDate ? new Date(forcePayoutDate) : new Date(); payoutDate.setMinutes(0); payoutDate.setHours(0); payoutDate.setSeconds(0); values = values.slice(0); values.unshift({payment:-initialAmount, date:payoutDate}); var x1 = 0.0; var x2 = guess; var f1 = this.XNPV(x1, values, daysInYear); var f2 = this.XNPV(x2, values, daysInYear); for (var i = 0; i < 100; i++) { if ((f1 * f2) < 0.0) break; if (Math.abs(f1) < Math.abs(f2)) { f1 = this.XNPV(x1 += 1.6 * (x1 - x2), values, daysInYear); } else { f2 = this.XNPV(x2 += 1.6 * (x2 - x1), values, daysInYear); } }; if ((f1 * f2) > 0.0) return null; var f = this.XNPV(x1, values, daysInYear); if (f < 0.0) { var rtb = x1; var dx = x2 - x1; } else { var rtb = x2; var dx = x1 - x2; }; for (var i = 0; i < 100; i++) { dx *= 0.5; var x_mid = rtb + dx; var f_mid = this.XNPV(x_mid, values, daysInYear); if (f_mid <= 0.0) rtb = x_mid; if ((Math.abs(f_mid) < 1.0e-6) || (Math.abs(dx) < 1.0e-6)) return Math.max(0, x_mid * 100); }; return null; }, rpyForInstallment: function (interestRate) { return interestRate; }, rpmForInstallment: function (interestRate) { return interestRate / 12; }, rpdForInstallment: function (interestRate, daysInYear) { return interestRate / daysInYear; }, // add base functions fillDiscounts: function (ibag, values) { for (var id in ibag.discounts_per) { if (!(id in values.discounts_per)) { values.discounts_per[id] = []; } for (var i = 0; i < ibag.discounts_per[id].length; i++) { values.discounts_per[id].push(ibag.discounts_per[id][i]); } } for (var id in ibag.discounts_amn) { if (!(id in values.discounts_amn)) { values.discounts_amn[id] = []; } for (var i = 0; i < ibag.discounts_amn[id].length; i++) { values.discounts_amn[id].push(ibag.discounts_amn[id][i]); } } if (!('designations' in values)) { values.designations = {}; } for (var promo_id in ibag.designations) { values.designations[promo_id] = ibag.designations[promo_id]; } for (var promo_id in ibag.customer_data) { if (!('customer_data' in values)) { values.customer_data = {}; } values.customer_data[promo_id] = ibag.customer_data[promo_id]; } var totalTerm = 0; for (var i = 0; i < ibag.extensions.length; i++) { var promo_id = ibag.extensions[i].id; if (!(promo_id in values.extensions)) { values.extensions[promo_id] = 0; } values.extensions[promo_id] += ibag.extensions[i].term; values.total_extension_term += ibag.extensions[i].term; } }, round: function (value, interval, mode) { var returnValue = null; var value = value / interval; switch (mode) { case 0: //FUNCTION_ROUND_FLOOR returnValue = Math.floor(value); break; case 1: //FUNCTION_ROUND_CEIL returnValue = Math.ceil(value); break; case 3: //FUNCTION_ROUND_HALF_DOWN returnValue = -Math.round(-value); break; case 2: //FUNCTION_ROUND_HALF_UP returnValue = Math.round(value); break; } return returnValue ? returnValue / (1 / interval) : returnValue; }, freshBag: function (bag, promo_id) { var that = this; var n = { discounts_per: {}, discounts_amn: {}, extensions: [], designations: {}, customer_data: {}, addInstallmentPromotionPercent: function (feetype, amount, installment_index) { if (!(feetype in this.discounts_per)) { this.discounts_per[feetype] = []; } this.discounts_per[feetype].push({ amount: amount, id: promo_id, installment_index: installment_index }); }, addInstallmentPromotion: function (feetype, amount, installment_index) { if (!(feetype in this.discounts_amn)) { this.discounts_amn[feetype] = []; } this.discounts_amn[feetype].push({ amount: amount, id: promo_id, installment_index: installment_index }); }, addDiscountPercent: function (feetype, amount) { if (!(feetype in this.discounts_per)) { this.discounts_per[feetype] = []; } this.discounts_per[feetype].push({ amount: amount, id: promo_id }); }, addDiscount: function (feetype, amount) { if (!(feetype in this.discounts_amn)) { this.discounts_amn[feetype] = []; } this.discounts_amn[feetype].push({ amount: amount, id: promo_id }); }, setDesignation: function (designation) { this.designations[promo_id] = designation; }, addFreeExtension: function (term) { this.extensions.push({ term: term, id: promo_id }); }, getCustomerData: function (type) { if ('customer-data' in bag && type in bag['customer-data']) { return bag['customer-data'][type]; } else { return null; } }, setCustomerData: function (type, data) { if (!(promo_id in this.customer_data)) { this.customer_data[promo_id] = []; } this.customer_data[promo_id].push({ type: type, data: data }); }, round: that.round, }; for (var id in bag) { if (!(id in n)) { n[id] = bag[id]; } } return n; }, evaluateLoanPromotions: function (now, bag, values) { var ibag = {}; var existingpromo = null; var percentpromo = null; var percentpromos = []; var usedPromotions = []; if (bag['existing-promotions']) { var existingPromos = bag['existing-promotions'].sort(function (a, b) { return b.priority - a.priority; }); ibag = this.freshBag(bag, null); for (var i = 0; i < existingPromos.length; i++) { existingpromo = existingPromos[i]; if (existingpromo['type'] == 'percent') { percentpromo = { 'amount': parseFloat(existingpromo['amount']), 'id': existingpromo['promoId'] }; if (existingpromo['installmentIndex'] !== undefined) { // installment index can be 0 and then it wouldnt go in percentpromo['installment_index'] = existingpromo['installmentIndex'] + 1; // + 1 because for promostaging the first installment is index 1 and not 0 } if (!percentpromos[existingpromo['feeTypeId']]) { percentpromos[existingpromo['feeTypeId']] = []; } percentpromos[existingpromo['feeTypeId']].push(percentpromo); usedPromotions.push(existingpromo['promoId'].toString()); } } ibag['discounts_per'] = percentpromos; this.fillDiscounts(ibag, values); } var quit = false; }, addPromotionResult: function (now, bag, values, start, end, id) { var ibag = {}; var result = null; if (start < now && now < end) { ibag = this.freshBag(bag, id); result = this.promotions[id].evaluate(ibag); this.fillDiscounts(ibag, values); return result.quit; } return false; }, evaluate: function (bag) { bag[-3] = bag[-3] || bag['loan-amount'] bag[-2] = bag[-2] || bag['loan-term'] var values = { discounts_per: {}, discounts_amn: {}, extensions: {}, total_extension_term: 0, prices: {}, feeRates: {}, discounts: {}, discounted: {}, prices_without_tax: {}, discounted_without_tax: {}, discounts_without_tax: {} } var that = this; var now = new Date().getTime(); var amn = parseFloat(bag[-3]).toFixed(2); var term = parseFloat(bag[-2]).toFixed(0); var extterm = bag[-1] == undefined ? term : parseFloat(bag[-1]).toFixed(0); // apr calculation is by default true, unless apr is set var apr = bag[-13] == undefined ? true : bag[-13]; var excludeFeesFromApr = bag['exclude-fees-from-apr'] == undefined ? [] : bag['exclude-fees-from-apr']; if (bag[-7] && (bag[-7] == '-1' || bag[-7] == '1')) { aleg: for (var id in this.pricelists) { if (!('buildPrice' in this.pricelists[id])) { if (!(amn in this.pricelists[id].data)) { var pamn = '0.0'; for (var iamn in this.pricelists[id].data) { if (parseFloat(iamn) > parseFloat(amn)) { if (bag[-7] == '1') { amn = iamn; break aleg; } else { if (parseFloat(pamn) == 0.0) { throw new Error('There is no smaller amount than ' + amn + ' to round down to for pricelist with id ' + id); } amn = pamn; break aleg; } } pamn = iamn; } if (parseFloat(pamn) < parseFloat(amn) && bag[-7] == '1') { amn = pamn; break aleg; } if (parseFloat(pamn) < parseFloat(amn) && bag[-7] == '-1') { amn = pamn; break aleg; } } } } } for (var id in this.pricelists) { if (bag['ignore-fee'] && bag['ignore-fee'][this.fee_types[id]]) { values.prices[id] = 0; continue; } if ('buildPrice' in this.pricelists[id]) { bag['pricelist_bag'] = bag['pricelist_bag'] || {}; for (var x in bag) { if (x != 'pricelist_bag') { bag['pricelist_bag'][x] = bag[x]; } } var pricelistResult = this.pricelists[id].buildPrice(amn, term, bag['pricelist_bag']); values.prices[id] = pricelistResult.price; values.feeRates[id] = pricelistResult.rate; } else { if (!(amn in this.pricelists[id].data)) { throw new Error('Invalid Amount ' + amn + ' for pricelist with id ' + id); } if (!term in this.pricelists[id].data[amn]) { throw new Error('Invalid Term ' + term + ' for pricelist with id ' + id + ' for amount ' + amn); } values.prices[id] = this.pricelists[id].data[amn][term]; values.feeRates[id] = 0; } if (this.pricelists[id].tax_percentage && this.pricelists[id].tax_percentage > 0) { values.prices_without_tax[id] = (values.prices[id] / (1 + parseFloat(this.pricelists[id].tax_percentage))).toFixed(2); } } this.evaluateLoanPromotions(now, bag, values); values.discounts_per[-67] = values.discounts_per[67]; values.discounts_amn[-67] = values.discounts_amn[67]; var details2 = { 'percent-discounts': {}, 'amount-discounts': {}, extensions: {}, original: {}, discount: {}, final: {}, tax_percentage: {}, without_tax: { original: {}, final: {}, discount: {} } }; var productId = false ? 2 : 1; var recalcVersion = 0; if (bag[-42]) { bag[-42].forEach(function (version) { if (version.product_id == productId) { recalcVersion = version.recalc_version; } }); } if (recalcVersion >= 3.1) { function calculateInterestFee(amount, interestRate, daysInYear, term) { return (amount * interestRate / 100 / daysInYear * term).toFixed(2); } function calculateLoanFee(amount, loanFeeRate, daysInYear, term) { return (parseFloat(loanFeeRate) / 100 / daysInYear * amount * term).toFixed(2); } var amount = bag[-3]; values.feeRates[67] = 18.5000000000; if (!values.feeRates[62]) { values.feeRates[62] = 0; } var daysPerYear = 366; var interestRate = values.feeRates[67]; var loanFeeRate = values.feeRates[62]; var interest = calculateInterestFee(amount, interestRate, daysPerYear, term); var loanFee = calculateLoanFee(amount, loanFeeRate, daysPerYear, term); values.prices[67] = interest; values.prices[62] = loanFee; values.feeRates['interest-fee-rate'] = interestRate; values.feeRates['loan-fee-rate'] = loanFeeRate; if (0.000 > 0) { values.prices_without_tax[67] = (values.prices[67] / (1 + parseFloat(0.000))).toFixed(2); } if (0.000 > 0) { values.prices_without_tax[62] = (values.prices[62] / (1 + parseFloat(0.000))).toFixed(2); } } for (var id in values.prices) { var discountPercentAr = values.discounts_per[id]; var discountPercent = 0; var name = ''; if (id in this.pricelists) { name = this.pricelists[id].type; } else if (id == 67) { name = 'interest-fee'; } else if (id == 62) { name = 'loan-fee'; } if (discountPercentAr) { if (discountPercentAr) { details2['percent-discounts'][name] = discountPercentAr; } for (var i = 0; i < discountPercentAr.length; i++) { discountPercent += parseFloat(discountPercentAr[i].amount); } } var discountAmountAr = values.discounts_amn[id]; var discountAmount = 0; if (discountAmountAr) { details2['amount-discounts'][name] = discountAmountAr; for (var i = 0; i < discountAmountAr.length; i++) { discountAmount += parseFloat(discountAmountAr[i].amount); } } var discount = Math.min(values.prices[id] * discountPercent / 100 + discountAmount, values.prices[id]); var finalAmount = values.prices[id] - discount; values.discounts[id] = parseFloat(discount.toFixed(2)); values.discounted[id] = parseFloat(finalAmount.toFixed(2)); if (values.prices_without_tax[id]) { if (id in this.pricelists) { values.discounted_without_tax[id] = parseFloat((values.discounted[id] / (1 + parseFloat(this.pricelists[id].tax_percentage))).toFixed(2)); } else if (id == 67) { values.discounted_without_tax[id] = parseFloat((values.discounted[id] / (1 + parseFloat(0.000))).toFixed(2)); } else if (id == 62) { values.discounted_without_tax[id] = parseFloat((values.discounted[id] / (1 + parseFloat(0.000))).toFixed(2)); } else { values.discounted_without_tax[id] = values.discounted[id]; } values.discounts_without_tax[id] = parseFloat((values.prices_without_tax[id] - values.discounted_without_tax[id]).toFixed(2)); } } var loan_total = values.discounted[63] + values.discounted[62]; loan_total += values.discounted[428]; if (values.discounted[67]) { loan_total += values.discounted[67]; } var loan_total_without_tax = (values.discounted_without_tax[63] || values.discounted[63]) + (values.discounted_without_tax[62] || values.discounted[62]); loan_total_without_tax += (values.discounted_without_tax[428] || values.discounted[428]); if (values.discounted_without_tax[67] || values.discounted[67]) { loan_total_without_tax += (values.discounted_without_tax[67] || values.discounted[67]); } for (var id in values.prices) { if (id < 0) { continue; } var name = ''; if (id in this.pricelists) { name = this.pricelists[id].type; } else if (id == 67) { name = 'interest-fee'; } else if (id == 62) { name = 'loan-fee'; } details2.original[name] = values.prices[id]; details2.discount[name] = values.discounts[id]; details2.final[name] = values.discounted[id]; details2.without_tax.original[name] = values.prices_without_tax[id] || values.prices[id]; details2.without_tax.discount[name] = values.discounts_without_tax[id] || values.discounts[id]; details2.without_tax.final[name] = values.discounted_without_tax[id] || values.discounted[id]; if (id in this.pricelists) { details2.tax_percentage[name] = parseFloat(this.pricelists[id].tax_percentage || 0); details2.without_tax.original[name] = values.prices_without_tax[id] || values.prices[id]; details2.without_tax.discount[name] = values.discounts_without_tax[id] || values.discounts[id]; details2.without_tax.final[name] = values.discounted_without_tax[id] || values.discounted[id]; } else { var interest_tax_percantage = parseFloat(0.000); details2.tax_percentage[name] = parseFloat(interest_tax_percantage || 0); details2.without_tax.original[name] = parseFloat(values.prices[id] / (1 + interest_tax_percantage)).toFixed(2); details2.without_tax.discount[name] = parseFloat(values.discounts[id] / (1 + interest_tax_percantage)).toFixed(2); details2.without_tax.final[name] = parseFloat(values.discounted[id] / (1 + interest_tax_percantage)).toFixed(2); } } if ((bag[-15] || false) == false) { delete details2.without_tax; delete details2.tax_percentage; } var ret = { 'loan-amount': bag[-3], 'loan-term': bag[-2], 'loan-repay-amount': values.discounted[63], 'loan-total': loan_total, 'loan-free-extension': values.total_extension_term, 'annual-loan-fee-rate': (values.feeRates[62]/ 100).toFixed(10), details: details2, }; var total_fee_cost_values = 0; if (values.prices['62']) { ret['loan-fees'] = values.discounted[62]; ret['loan-original-fees'] = values.prices[62]; total_fee_cost_values += values.discounted[62]; } if (values.prices['67']) { ret['loan-interest-fees'] = values.discounted['67']; ret['loan-original-interest-fees'] = values.prices['67']; ret['loan-interest-fee-type-id'] = 67; total_fee_cost_values += values.discounted[67]; } if (values.prices['428']) { ret['loan-preparation-fees'] = values.discounted[428]; ret['loan-original-preparation-fees'] = values.prices[428]; ret['loan-preparation-fee-type-id'] = 428; total_fee_cost_values += values.discounted[428]; } ret['loan-total-fee-cost'] = total_fee_cost_values; if (values.prices[-67]) { ret['loan-additional-extension-fees'] = values.discounted[-67]; ret['loan-additional-original-extension-fees'] = values.prices[-67]; ret['loan-additional-extension-fee-type-id'] = 67; } if (values.customer_data) { ret['customer-data'] = values.customer_data; } if (values.feeRates) { ret['fee-rates'] = values.feeRates; } var loanFee = 62; var interestFee = 67; var preparationFee = 428; var principalFee = 63; if ((bag[-15] || false) != false) { ret['without_tax'] = { 'loan-repay-amount': values.discounted_without_tax[principalFee] || values.discounted[principalFee], 'loan-fees': values.discounted_without_tax[loanFee] || values.discounted[loanFee], 'loan-original-fees': values.prices_without_tax[loanFee] || values.prices[loanFee], 'loan-total': loan_total_without_tax, }; } if (apr == true) { //calculate apr for Payday var recalcVersion = 0; if (bag[-42]) { bag[-42].forEach(function (version) { if (version.product_id == 1) { recalcVersion = version.recalc_version; } }); } var daysInYear = recalcVersion >= 3.1 ? 366 : 365; var discounted_params = parseFloat(bag[-3]) + parseFloat(bag['additional-apr-calculation-cost'] || 0) + parseFloat(values.discounted[loanFee] || 0) + parseFloat(values.discounted[interestFee] || 0); if (preparationFee) { discounted_params = discounted_params + parseFloat(values.discounted[preparationFee] || 0); } ret['loan-apr'] = this.aprForPayday(parseFloat(bag[-3]), parseFloat(bag[-2]), discounted_params, daysInYear); ret['loan-ar'] = this.arForPayday(parseFloat(bag[-3]), parseFloat(bag[-2]), discounted_params, daysInYear); ret['loan-rpy'] = this.rpyForPayday(parseFloat(bag[-3]), parseFloat(bag[-2]), discounted_params, daysInYear); ret['loan-rpm'] = this.rpmForPayday(parseFloat(bag[-3]), parseFloat(bag[-2]), discounted_params, daysInYear); ret['loan-rpd'] = this.rpdForPayday(parseFloat(bag[-3]), parseFloat(bag[-2]), discounted_params); var original_params = parseFloat(bag[-3]) + parseFloat(bag['additional-apr-calculation-cost'] || 0) + parseFloat(values.prices[loanFee] || 0) + parseFloat(values.prices[interestFee] || 0); if (preparationFee) { original_params = original_params + parseFloat(values.prices[preparationFee] || 0); } ret['loan-apr-original'] = this.aprForPayday(parseFloat(bag[-3]), parseFloat(bag[-2]), original_params, daysInYear); ret['loan-ar-original'] = this.arForPayday(parseFloat(bag[-3]), parseFloat(bag[-2]), original_params, daysInYear); ret['loan-rpy-original'] = this.rpyForPayday(parseFloat(bag[-3]), parseFloat(bag[-2]), original_params, daysInYear); ret['loan-rpm-original'] = this.rpmForPayday(parseFloat(bag[-3]), parseFloat(bag[-2]), original_params, daysInYear); ret['loan-rpd-original'] = this.rpdForPayday(parseFloat(bag[-3]), parseFloat(bag[-2]), original_params); if ((bag[-15] || false) != false) { discounted_params = parseFloat(bag[-3]) + parseFloat(bag['additional-apr-calculation-cost'] || 0) + (!values.discounted_without_tax[loanFee] ? parseFloat(values.discounted[loanFee] || 0) : parseFloat(values.discounted_without_tax[loanFee] || 0)) + (!values.discounted_without_tax[interestFee] ? parseFloat(values.discounted[interestFee] || 0) : parseFloat(values.discounted_without_tax[interestFee] || 0)); if (preparationFee) { discounted_params = discounted_params + (!values.discounted_without_tax[preparationFee] ? parseFloat(values.discounted[preparationFee] || 0) : parseFloat(values.discounted_without_tax[preparationFee] || 0)); } ret['loan-apr'] = this.aprForPayday(parseFloat(bag[-3]), parseFloat(bag[-2]), discounted_params, daysInYear); original_params = parseFloat(bag[-3]) + parseFloat(bag['additional-apr-calculation-cost'] || 0) + (!values.prices_without_tax[loanFee] ? parseFloat(values.prices[loanFee] || 0) : parseFloat(values.prices_without_tax[loanFee] || 0)) + (!values.prices_without_tax[interestFee] ? parseFloat(values.prices[interestFee] || 0) : parseFloat(values.prices_without_tax[interestFee] || 0)); if (preparationFee) { original_params = original_params + (!values.prices_without_tax[preparationFee] ? parseFloat(values.prices[preparationFee] || 0) : parseFloat(values.prices_without_tax[preparationFee] || 0)); } ret['loan-apr-original'] = this.aprForPayday(parseFloat(bag[-3]), parseFloat(bag[-2]), original_params, daysInYear); } } let baseDate = bag['calculation-date'] ? new Date(bag['calculation-date']) : new Date(); baseDate.setDate(baseDate.getDate() + bag[-2]); ret['loan-duedate'] = new Date(baseDate); return ret; }, fields: { 190: 'Affiliate', 196: 'Customer Group', 799: 'Loan count', 802: 'Delay', 1071: 'Id', 2593: 'Dc Category', 2697: 'Buyer', 4438: 'Has Open Application', 8736: 'PL Special Extensions Count', 10049: 'PL Paid Loan Count PDL', 10507: 'FL Loan Category ID', 10526: 'Last Loan Category ID', }, dateValidationDefinition: { evaluate: function (bag) { var control = { canceled: false, quit: false }; (function () { bag["isValid"](); })(); return control; }, freshBag: function (bag, date_to_check) { var n = { date_to_check: date_to_check, holidays: ["2025-04-20","2026-04-05","2027-03-28","2025-04-21","2026-04-06","2027-03-29","2024-05-19","2025-06-08","2026-05-24","2027-05-16","2024-05-30","2025-06-19","2026-06-04","2027-05-27"], valid: false, confirmDate: function (checkDate) { var resultDate = null; if (checkDate === null) { checkDate = "1900-01-01"; } // Check if it is a correct Date object if (checkDate instanceof Date && !isNaN(checkDate.getMonth())) { resultDate = checkDate; } if (typeof checkDate === 'string') { resultDate = new Date(checkDate); // Check if it is a correct date if (isNaN(resultDate.getMonth())) { resultDate = new Date("1900-01-01"); } } if (resultDate === null) { resultDate = new Date("1900-01-01"); } resultDate = new Date(this.getDateToString(resultDate)); resultDate.setMinutes(resultDate.getMinutes() + resultDate.getTimezoneOffset()); return resultDate; }, getDateToString: function (date) { return date.getFullYear() + '-' + ("0" + (date.getMonth() + 1 )).slice(-2) + '-' + ("0" + date.getDate()).slice(-2); }, getValidDateToString: function () { if (!this.valid) { return null; } var date = this.confirmDate(this.date_to_check); return this.getDateToString(date); }, getDay: function () { var date = this.confirmDate(this.date_to_check); return date.getDate(); }, getMonth: function () { var date = this.confirmDate(this.date_to_check); // date.getMonth is 0-indexed return (date.getMonth() + 1); }, getYear: function () { var date = this.confirmDate(this.date_to_check); return date.getFullYear(); }, getDayOfWeek: function () { var date = this.confirmDate(this.date_to_check); return date.getDay(); }, daysInCurrentMonth: function () { var date = this.confirmDate(new Date()); return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate(); }, isHoliday: function () { var date = this.confirmDate(this.date_to_check); for(var i = 0; i < this.holidays.length; ++i) { holiday = this.confirmDate(this.holidays[i]); if (Date.parse(date) === Date.parse(holiday)) { return true; } } return false; }, daysFromNow: function () { var now = this.confirmDate(new Date()); var dateToCheck = this.confirmDate(this.date_to_check); var diff = Date.UTC(now.getFullYear(), now.getMonth(), now.getDate()) - Date.UTC(dateToCheck.getFullYear(), dateToCheck.getMonth(), dateToCheck.getDate()); return (Math.floor(diff / (24 * 60 * 60 * 1000)) * -1); }, isValid: function () { this.valid = true; } }; for (var id in bag) { if (!(id in n)) { n[id] = bag[id]; } } return n; }, getRange: function (wished_range) { var max_range = 50; if (wished_range === null || wished_range > max_range || wished_range < 1) { return max_range; } return wished_range; }, getOffset: function (wished_offset) { var min_offset = 0; if (wished_offset === null || wished_offset < min_offset) { return min_offset; } return wished_offset; }, getValidDates: function (bag, offset, range) { range = this.getRange(range); var date = new Date(); date.setDate(date.getDate() + parseInt(this.getOffset(offset))); var validDays = []; for (var i = 0; i < range; i++) { var _date = new Date(date); _date.setDate(_date.getDate() + i); var ibag = this.freshBag(bag, _date); this.evaluate(ibag); if (ibag.valid) { validDays.push(ibag.getValidDateToString()); } } return validDays; }, }, }, }; })(); ;