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 { installment: { pricelists: { '77': { type: 'loan-fee', fee_type_id: '77', 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, 4000, 5); if (control.canceled) return; bag['setTermRange'](3, 60, 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.00); 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) }; } }, '78': { type: 'principal', fee_type_id: '78', 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: Installment LVEP principal 1 (function () { // Group: Group if (true) { bag['setAmountRange'](100, 4000, 5); if (control.canceled) return; bag['setTermRange'](3, 60, 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['amount'] = bag['getAmount'](); bag['setPrice'](vars['amount']); 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) }; } }, '79': { type: 'extension-fee', fee_type_id: '79', 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: Installment LVEP principal 1 (function () { // Group: Group if (true) { bag['setAmountRange'](100, 4000, 5); if (control.canceled) return; bag['setTermRange'](3, 60, 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: installment extension settings (function () { // Group: Group if ([1,3].indexOf(parseInt(bag[460])) !== -1) { vars['price'] = (bag[3256] * 0.3); bag['setPrice'](vars['price']); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if ([4].indexOf(parseInt(bag[460])) !== -1) { vars['price'] = (bag[4102] * 0.3); bag['setPrice'](vars['price']); 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) }; } }, '83': { type: 'interest-fee', fee_type_id: '83', 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: Installment LVEP principal 1 (function () { // Group: Group if (true) { bag['setAmountRange'](100, 4000, 5); if (control.canceled) return; bag['setTermRange'](3, 60, 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: installment lv interest staging (function () { // Group: Group if (true) { vars['varterm'] = bag['getTerm'](); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if (vars['varterm'] == 3) { bag['setPrice'](36.85); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if (vars['varterm'] == 4) { bag['setPrice'](39.17); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if (vars['varterm'] == 5) { bag['setPrice'](40.64); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if (vars['varterm'] == 6) { bag['setPrice'](41.5); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if (vars['varterm'] == 7) { bag['setPrice'](42.18); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if (vars['varterm'] == 8) { bag['setPrice'](42.59); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if ((vars['varterm'] >= 9 && vars['varterm'] <= 10)) { bag['setPrice'](43); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if (vars['varterm'] == 11) { bag['setPrice'](43.25); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if ((vars['varterm'] >= 12 && vars['varterm'] <= 20)) { bag['setPrice'](43); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if (vars['varterm'] == 21) { bag['setPrice'](42.87); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if ((vars['varterm'] >= 22 && vars['varterm'] <= 24)) { bag['setPrice'](42.5); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if (vars['varterm'] == 25) { bag['setPrice'](42.41); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if ((vars['varterm'] >= 26 && vars['varterm'] <= 28)) { bag['setPrice'](42); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if (vars['varterm'] == 29) { bag['setPrice'](41.9); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if ((vars['varterm'] >= 30 && vars['varterm'] <= 32)) { bag['setPrice'](41.5); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if (vars['varterm'] == 33) { bag['setPrice'](41.38); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if ((vars['varterm'] >= 34 && vars['varterm'] <= 44)) { bag['setPrice'](40); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if ((vars['varterm'] >= 45 && vars['varterm'] <= 49)) { bag['setPrice'](39.5); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if ((vars['varterm'] >= 50 && vars['varterm'] <= 53)) { bag['setPrice'](39); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if ((vars['varterm'] >= 54 && vars['varterm'] <= 58)) { bag['setPrice'](38.5); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if ((vars['varterm'] >= 59 && vars['varterm'] <= 60)) { bag['setPrice'](38); 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: { 77: 'loan_fee', 78: 'principal', 79: 'extension_fee', 80: 'penalty', 81: 'late_fee', 82: 'payout_fee', 83: 'interest_fee', 87: 'cash_fee', 258: 'late_fee_delay', 260: 'late_fee_delay', 262: 'late_fee_delay', 333: 'court_fee', 359: 'sale_fee', 460: 'late_fee_delay', 465: 'court_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: Stage (function () { // Group: Group if ((bag[799] == 0 && (toDate(bag['6054']).getTime() < (1714205011090-7776000000) || [4].indexOf(parseInt(bag[2538])) === -1))) { bag['setIncomeVerifyLimit'](1); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if (bag[799] > 0) { bag['setIncomeVerifyLimit'](1); 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] = 237; } 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[77].buildDimension(bag); this.pricelists[78].buildDimension(bag); this.pricelists[79].buildDimension(bag); this.pricelists[83].buildDimension(bag); for (var i = 0; i < this.pricelists[78].amounts.length; i++) { var amn = this.pricelists[78].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[78].terms.length; i++) { var day = this.pricelists[78].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[79].terms.length; i++) { var day = this.pricelists[79].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 } }, prolongPermissionRules: { 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: Prohibit prolonging If all products open (function () { // Group: Group if ((bag[3251] == 1 && (bag[3252] == 1 && bag['3253'] == 1))) { bag['prohibitProlong']('Has all products open'); bag['prohibitProlongToInstallment']('Has all products open'); bag['exit'](); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; control.skip = false; // Stage: OLD VERSION OF PROLONGING=>HAS OPEN PD PROLONG TO CRL (function () { // Group: PD --> CRL FL BEFORE DUEDATE if (([1,3,4].indexOf(parseInt(bag[460])) !== -1 && ((((bag[802] < 31 && bag[2788] >= 50) && [1].indexOf(bag[2438] ? 1 : 0) !== -1) && bag[3251] == 1) && bag[3252] == 0))) { bag['prohibitProlong'](null); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: PD --> CRL FL AFTER DUEDATE if (([1,3,4].indexOf(parseInt(bag[460])) !== -1 && (((((bag[802] > 0 && bag[802] < 31) && bag[2788] >= 50) && [1].indexOf(bag[2438] ? 1 : 0) !== -1) && bag[3251] == 1) && bag[3252] == 0))) { bag['prohibitProlong'](null); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: PD --> CRL CL BEFORE DUEDATE if (([1,3,4].indexOf(parseInt(bag[460])) !== -1 && ((((bag[802] < 31 && bag[3252] == 0) && bag[2788] >= 50) && [0].indexOf(bag[2438] ? 1 : 0) !== -1) && bag[3251] == 1))) { bag['prohibitProlong'](null); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; control.skip = false; // Stage: NEW VERSION OF PROLONGING to IL (function () { // Group: PDL if ((bag['3141'] == 1 && ([1,3,4].indexOf(parseInt(bag['460'])) !== -1 && (bag['2788'] >= 50 && bag[802] < 31)))) { bag['prohibitProlongToInstallment'](null); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; control.skip = false; // Stage: NEW VERSION OF PROLONGING IL -> IL (function () { // Group: IL if ((bag['3141'] == 2 && ([1].indexOf(parseInt(bag[460])) !== -1 && (bag['2788'] >= 50 && bag[802] < 31)))) { bag['prohibitProlongToInstallment'](null); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; control.skip = false; // Stage: NEW VERSION OF PROLONGING - TO CRL -> IL (function () { // Group: CRL if ((bag['3141'] == 3 && ([17,1,4].indexOf(parseInt(bag[460])) !== -1 && (bag['2788'] >= 50 && bag['3353'] < 31)))) { bag['prohibitProlongToInstallment'](null); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; return control; } }, 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 = this.prolongPermissionRules.evaluate(ibag); return { 'loan-prolong-permitted': ibag._loanProlongPermitted, 'loan-prolong-permitted-to-payday': ibag._loanProlongPermittedToPayday, 'loan-prolong-permitted-to-installment': ibag._loanProlongPermittedToInstallment, 'loan-prolong-permitted-to-creditline': ibag._loanProlongPermittedToCreditline, 'loan-prolong-error': ibag._errorMessage, 'loan-prolong-to-payday-error': ibag._errorMessageProlongToPayday, 'loan-prolong-to-installment-error': ibag._errorMessageProlongToInstallment, 'loan-prolong-to-creditline-error': ibag._errorMessageProlongToCreditline, 'prolong-loan-setting-id': ibag._loanSettingId, 'loan-feetype-mapping': ibag._feetypes } }, 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: SET-3547 Allow ext for all (function () { // Group: Group if (true) { bag['allowExtension'](); bag['exit'](); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; control.skip = false; // Stage: Prohibit extension if force settledate is set (function () { // Group: Group 1 if ((bag['7560'] == 1 && bag['3141'] == 2)) { bag['prohibitExtension']('force settledate set - no extension possible CFIT-3333'); bag['exit'](); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; control.skip = false; // Stage: Remaining debt < 10 Euro (function () { // Group: Group if ((((bag[4102] > 0.02 && bag[4102] < 10) || (bag[3256] > 0.02 && bag[3256] < 10)) || bag[802] > 60)) { bag['prohibitExtension']('Fee limit reached'); bag['exit'](); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; control.skip = false; // Stage: Alow (function () { // Group: Group if (true) { bag['allowExtension'](); 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 != 79 ) { 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); 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); 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)); 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[79]; 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[79], 'loan-extension-duedate': calc_duedate, 'loan-extension-permitted': ibag._loanExtensionPermitted, 'loan-extension-error': ibag._errorMessage, 'loan-original-extension-fees': values.prices[79], 'loan-free-extension': values.total_extension_term, details: details2, }; }, installmentSettings: { 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; }; return control; }, freshBag: function(bag) { var n = { promotableInstallmentsCount: 0, setPromotableInstallmentsCount: function (count) { this.promotableInstallmentsCount = count; }, setInstallmentFee: function (dummy, dummy2) {}, setExtensionPrice: function (dummy) {}, getSum: function (dummy) { return 0; }, getOpen: function (dummy) { return 0; }, getRemaining: function (dummy) { return 0; }, round: function (dummy) { return 0; } }; for (var id in bag) { if (!(id in n)) { n[id] = bag[id]; } } return n; }, evaluatePromotableInstallments: function(amount, term, bag) { var ibag = this.freshBag(bag); ibag.getAmount = function () { return amount; }; ibag.getTerm = function () { return term; }; this.evaluate(ibag); return ibag; }, }, evaluateInstallments: function (bag, promotions) { // bag[-42] => for active recalc v3 var recalc3 = false; if (bag[-42]) { bag[-42].forEach(function (version) { if (version.product_id == 2 && version.recalc_version > 3) { recalc3 = true; } }); } var firstInstallmentDate = bag['first-installment-date']; if (firstInstallmentDate) { firstInstallmentDate.setMinutes(0); firstInstallmentDate.setHours(0); firstInstallmentDate.setSeconds(0); } var amount = parseFloat(bag['loan-amount']); var term = parseFloat(bag['loan-term']); var loan_fee = parseFloat(bag['loan-fee']); var original_amount = parseFloat(bag['original-amount']) || 0; var fast_transfer_fee = 0; var fast_transfer_fee_split = 1; var additional_amount_payout_date = null; var interest_rate = parseFloat(bag['interest-rate']) / 100; const preparation_fee = 0; var promotableInstallmentsCount = parseFloat(bag['promotable-installments-count']); var now = bag['calculation-date'] ? new Date(bag['calculation-date']) : new Date(); // update now without time var now = new Date(now.getFullYear(), now.getMonth(), now.getDate()); var due = new Date(now.getFullYear(), now.getMonth() + term, now.getDate()); // we dont allow all installments to be promotable/promoteable if (promotableInstallmentsCount && (term - promotableInstallmentsCount < 1)) { promotableInstallmentsCount = 0; } var calcTerm = term - promotableInstallmentsCount; function daysBetween(t1, t2) { var oneDay = 24 * 60 * 60 * 1000; return Math.round(Math.abs((t1.getTime() - t2.getTime())/oneDay)); } function dateWithoutTime(date) { var newdate = date ? new Date(date) : new Date(); newdate.setMinutes(0); newdate.setHours(0); newdate.setSeconds(0); return newdate; } function getIntervalJson(interval) { var yearsSplit = interval.split('y'); var monthsSplit = yearsSplit[1].split('m'); var daysSplit = monthsSplit[1].split('d'); return { years: parseInt(yearsSplit[0]), months: parseInt(monthsSplit[0]), days: parseInt(daysSplit[0]) }; } function getIntervalNewDate(paymentDate, interval) { var intervalJson = getIntervalJson(interval); var intervalNewDate = new Date(paymentDate); intervalNewDate.setFullYear(paymentDate.getFullYear() + intervalJson.years); intervalNewDate.setMonth(paymentDate.getMonth() + intervalJson.months); intervalNewDate.setDate(paymentDate.getDate() + intervalJson.days); return intervalNewDate; } function getMonthlyPaymentDate(date, bag) { if (!bag || !bag['grace-period-days']) { return date; } var monthlyPaymentDate = new Date(date); monthlyPaymentDate.setDate(date.getDate() + parseInt(bag['grace-period-days'])); return monthlyPaymentDate; } function getNextPaymentDate(paymentDate, targetDay, bag) { if (bag && 'use-interval' in bag && bag['use-interval']) { return getIntervalNewDate(paymentDate, bag['interval']); } var followingMonth, followingYear, lastOfFollowingMonth, newDate; if (paymentDate.getMonth() === 11) { followingMonth = 0; followingYear = paymentDate.getFullYear() + 1; } else { followingMonth = paymentDate.getMonth() + 1; followingYear = paymentDate.getFullYear(); } lastOfFollowingMonth = new Date(followingYear, followingMonth + 1, 0); newDate = dateWithoutTime(paymentDate); if(lastOfFollowingMonth.getDate() < targetDay) { newDate.setMonth(lastOfFollowingMonth.getMonth()); newDate.setDate(0); } else { newDate.setMonth(paymentDate.getMonth() + 1); newDate.setDate(targetDay); } return newDate; } if (bag['additional-payout-date']) { additional_amount_payout_date = new Date(bag['additional-payout-date']); } function calcPromotionDiscount(feeAmount, feeTypeId, currentInstallmentIndex, promotions) { var totalReduc = 0; if (promotions.discounts_per[feeTypeId]) { var promos = promotions.discounts_per[feeTypeId]; for (var i = 0; i < promos.length; i++) { var p = promos[i]; var a = (p.amount / 100) * feeAmount; if (p.installment_index < 0) { p.installment_index = term + p.installment_index + 1; } if (p.installment_index - 1 === currentInstallmentIndex) { totalReduc += a; } else if (p.installment_index === undefined) { totalReduc += a; } }; } if (promotions.discounts_amn[feeTypeId]) { var promos = promotions.discounts_amn[feeTypeId]; for (var z = 0; z < promos.length; z++) { var p = promos[z]; if (p.installment_index < 0) { p.installment_index = term + p.installment_index + 1; } if (p.installment_index - 1 === currentInstallmentIndex) { totalReduc += p.amount; } else if (p.installment_index === undefined) { totalReduc += p.amount; } }; } return Math.max(0, (feeAmount - totalReduc)); } var freeze_now = dateWithoutTime(now); // Todo up var make_schedule = function(pmt) { var paymentDate = new Date(freeze_now), oldDate = paymentDate, repayment_interval; // adapt repayment interval if (firstInstallmentDate) { firstInstallmentDate.setSeconds(1); repayment_interval = Math.floor((firstInstallmentDate - paymentDate) / (1000 * 60 * 60 * 24)); paymentDate = new Date(firstInstallmentDate); } else { repayment_interval = new Date(paymentDate.getFullYear(), paymentDate.getMonth() + 1, 0, 0, 0, 0, 0).getDate(); paymentDate.setMonth(paymentDate.getMonth() + 1); } var schedule = []; var curr_principal = amount; var loan_fee_per_interval = loan_fee / calcTerm; var interest; var curr_interest; var old_year_days, new_year_days; var interest_before_additional_amount, days_to_additonal_amount; var days_to_additional_amount_in_old_year, days_to_additional_amount_in_new_year; var repayment; if (recalc3) { // recalc 3 installment fees cannot have remaining debt left from rounding or cutting loan_fee_per_interval = Math.floor(loan_fee / calcTerm * 100) / 100; } var target_day = paymentDate.getDate(); for (var i = 0; i < calcTerm; i += 1) { // Check for partial payment if (recalc3 && additional_amount_payout_date && i === 0 && original_amount > 0 && paymentDate > additional_amount_payout_date) { curr_principal = original_amount; days_to_additonal_amount = daysBetween(oldDate, additional_amount_payout_date); if (additional_amount_payout_date.getFullYear() > oldDate.getFullYear()) { old_year_days = daysBetween(new Date(oldDate.getFullYear(), 0, 1), new Date(oldDate.getFullYear() + 1, 0, 1)); new_year_days = daysBetween(new Date(additional_amount_payout_date.getFullYear(), 0, 1), new Date(additional_amount_payout_date.getFullYear() + 1, 0, 1)); days_to_additional_amount_in_old_year = daysBetween(oldDate, new Date(oldDate.getFullYear() + 1, 0, 1)); days_to_additional_amount_in_new_year = daysBetween(new Date(additional_amount_payout_date.getFullYear(), 0, 1), additional_amount_payout_date); // this is because recalc 3 fees always start with the next day at 00:00:00 days_to_additional_amount_in_old_year--; days_to_additional_amount_in_new_year++; interest_before_additional_amount = ( (curr_principal * (interest_rate / old_year_days * days_to_additional_amount_in_old_year )) + (curr_principal * (interest_rate / new_year_days * days_to_additional_amount_in_new_year )) ); } else { old_year_days = daysBetween(new Date(oldDate.getFullYear(), 0, 1), new Date(oldDate.getFullYear() + 1, 0, 1)); interest_before_additional_amount = curr_principal * (interest_rate / old_year_days * days_to_additonal_amount); days_to_additional_amount_in_old_year = days_to_additonal_amount; days_to_additional_amount_in_new_year = 0; } oldDate = additional_amount_payout_date; curr_principal = amount; } else { interest_before_additional_amount = 0; days_to_additional_amount_in_old_year = 0; days_to_additional_amount_in_new_year = 0; } // Check for leap year if (paymentDate.getFullYear() > oldDate.getFullYear()) { old_year_days = daysBetween(new Date(oldDate.getFullYear(), 0, 1), new Date(oldDate.getFullYear() + 1, 0, 1)); new_year_days = daysBetween(new Date(paymentDate.getFullYear(), 0, 1), new Date(paymentDate.getFullYear() + 1, 0, 1)); var installment_days_in_old_year = daysBetween(oldDate, new Date(oldDate.getFullYear() + 1, 0, 1)) - days_to_additional_amount_in_old_year, installment_days_in_new_year = daysBetween(new Date(paymentDate.getFullYear(), 0, 1), paymentDate) - days_to_additional_amount_in_new_year; if (recalc3) { // this is because recalc 3 fees always start with the next day at 00:00:00 installment_days_in_old_year--; installment_days_in_new_year++; } var interestRateOldYear = interest_rate / old_year_days * (installment_days_in_old_year - days_to_additional_amount_in_old_year); var interestRateNewYear = interest_rate / new_year_days * (installment_days_in_new_year - days_to_additional_amount_in_new_year); interest = (curr_principal * interestRateOldYear) + (curr_principal * interestRateNewYear) + interest_before_additional_amount; curr_interest = Math.floor(interest * 100) / 100; } else { old_year_days = daysBetween(new Date(oldDate.getFullYear(), 0, 1), new Date(oldDate.getFullYear() + 1, 0, 1)); var interestRate = interest_rate / old_year_days * (repayment_interval - days_to_additional_amount_in_old_year - days_to_additional_amount_in_new_year); interest = (curr_principal * interestRate) + interest_before_additional_amount; curr_interest = Math.floor(interest * 100) / 100; } var principal = Math.max(0, pmt - loan_fee_per_interval - curr_interest); var loanfee = calcPromotionDiscount(loan_fee_per_interval, 77, i, promotions); var interest = calcPromotionDiscount(curr_interest, 83, i, promotions); // --------------- creating the installment -------------------- var og_principal = parseFloat(Math.max(0, pmt - loan_fee_per_interval - curr_interest)); var og_loanfee = parseFloat(loan_fee_per_interval); var og_preparationFee = parseFloat(0); var og_fastTransferFee = parseFloat(0); var og_interest = parseFloat(curr_interest); var disc_principal = parseFloat(principal); var disc_loanfee = parseFloat(loanfee); var disc_preparationFee = parseFloat(og_preparationFee); var disc_fastTransferFee = parseFloat(og_fastTransferFee); var disc_interest = parseFloat(interest); if (i == 0) { og_preparationFee = parseFloat(preparation_fee); disc_preparationFee = Math.min(og_preparationFee, disc_loanfee); disc_loanfee = disc_loanfee - disc_preparationFee; og_fastTransferFee = parseFloat(fast_transfer_fee); disc_fastTransferFee = og_fastTransferFee; } if (i + 1 < fast_transfer_fee_split) { og_fastTransferFee = Math.ceil(fast_transfer_fee / fast_transfer_fee_split * 100) / 100; disc_fastTransferFee = og_fastTransferFee; } else if (i + 1 == fast_transfer_fee_split && i > 0) { og_fastTransferFee = parseFloat(fast_transfer_fee - (Math.ceil(fast_transfer_fee / fast_transfer_fee_split * 100) / 100) * i); disc_fastTransferFee = og_fastTransferFee; } repayment = { date: paymentDate, original: { 78: og_principal, 77: og_loanfee, 83: og_interest }, discounted: { 78: disc_principal, 77: disc_loanfee, 83: disc_interest }, discounts: {}, payment: disc_principal + disc_loanfee + disc_interest + disc_preparationFee + disc_fastTransferFee }; repayment.discounts[78] = parseFloat(repayment.original[78] - repayment.discounted[78]); repayment.discounts[77] = parseFloat(repayment.original[77] - repayment.discounted[77]); repayment.discounts[83] = parseFloat(repayment.original[83] - repayment.discounted[83]); curr_principal -= principal; schedule.push(repayment); oldDate = paymentDate; paymentDate = getNextPaymentDate(paymentDate, target_day, null); repayment_interval = daysBetween(oldDate, paymentDate); } // Return schedule and remaining principal return { schedule: schedule, leftover: curr_principal, }; }; var guessRounding = function(value) { if (recalc3) { return Math.round(((value) / 2) * 100) / 100; } else { return Math.ceil(((value) / 2) * 100) / 100; } } var days_diff = Math.floor((due - now) / (1000 * 60 * 60 * 24)); var min_rep = (amount + loan_fee) / calcTerm; var max_rep = (amount + loan_fee + (amount * (interest_rate / 366 * days_diff))) / calcTerm; var old_guess = undefined; var guess = guessRounding(min_rep + max_rep); var sched = make_schedule(guess); var tries = 0; while (Math.abs(sched.leftover) > 0.00001 && guess !== old_guess && tries < 40) { if (sched.leftover > 0) { min_rep = guess; guess = guessRounding(guess + max_rep); } else { max_rep = guess; guess = guessRounding(guess + min_rep); } sched = make_schedule(guess); tries += 1; } var last = sched.schedule.slice(-1)[0]; last.payment += sched.leftover; last.original[78] += sched.leftover; last.discounted[78] += sched.leftover; var paymentDate = last.date; for (var j = 0; j < promotableInstallmentsCount; j += 1) { paymentDate = new Date(paymentDate.getFullYear(), paymentDate.getMonth() + 1, paymentDate.getDate()); var special_loanfee = calcPromotionDiscount(last.payment, 77, calcTerm + j, promotions); var promotableInstallment = { date: paymentDate, original: { 78: 0, 77: last.payment, 83: 0 }, discounted: { 78: 0, 77: special_loanfee, 83: 0 }, discounts: { }, payment: special_loanfee }; promotableInstallment.discounts[78] = parseFloat(promotableInstallment.original[78] - promotableInstallment.discounted[78]); promotableInstallment.discounts[77] = parseFloat(promotableInstallment.original[77] - promotableInstallment.discounted[77]); promotableInstallment.discounts[83] = parseFloat(promotableInstallment.original[83] - promotableInstallment.discounted[83]); sched.schedule.push(promotableInstallment); } return sched; }, 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); var details2 = { 'percent-discounts': {}, 'amount-discounts': {}, extensions: {}, original: {}, discount: {}, final: {}, tax_percentage: {}, without_tax: { original: {}, final: {}, discount: {} } }; var productId = true ? 2 : 1; var recalcVersion = 0; if (bag[-42]) { bag[-42].forEach(function (version) { if (version.product_id == productId) { recalcVersion = version.recalc_version; } }); } // if there are installment settings, call the installment handler var interest_rate = values.prices[83]; var fast_transfer_fee_split = null; var _promotable_installments_count = this.installmentSettings.evaluatePromotableInstallments(amn, term, bag).promotableInstallmentsCount; var additional_loan_fee_rate = null; if (bag[-23]) { // risk appetite / additional interest rate interest_rate = parseFloat(interest_rate) + parseFloat(bag[-23]); } values.feeRates[83] = interest_rate; var subbag = {}; subbag['first-installment-date'] = bag['first-installment-date']; subbag['loan-amount'] = amn; subbag['loan-term'] = term; subbag['loan-fee'] = parseFloat(values.prices[77]); if (bag[-69]) { var additional_loan_fee_rate_setting = bag[-69]; if (additional_loan_fee_rate_setting.type === 0) { additional_loan_fee_rate = amn * parseFloat(additional_loan_fee_rate_setting.value) / 100; values.feeRates[77] += additional_loan_fee_rate_setting.value; } else { additional_loan_fee_rate = parseFloat(additional_loan_fee_rate_setting.value); } subbag['loan-fee'] = subbag['loan-fee'] + additional_loan_fee_rate; } subbag['enable-fast-transfer-fee'] = bag['enable-fast-transfer-fee'] == undefined ? [] : bag['enable-fast-transfer-fee']; subbag['original-amount'] = bag[-25] == undefined ? 0 : parseFloat(bag[-25]); subbag['additional-payout-date'] = bag['additional-payout-date'] == undefined ? null : bag['additional-payout-date']; subbag['interest-rate'] = interest_rate; subbag['promotable-installments-count'] = _promotable_installments_count; if (bag[-42]) { subbag[-42] = bag[-42]; } if (bag['calculation-date']) { subbag['calculation-date'] = bag['calculation-date']; } var _pre_all_installments = this.evaluateInstallments(subbag, values).schedule; var _all_installments = []; var _all_installments_without_tax = []; values.prices = {}; for (var i = 0, max = _pre_all_installments.length; i < max; i += 1) { var inst = _pre_all_installments[i]; var ext_percent = 0; var ext_amount = 0; if (79 in values.discounts_per) { for (var di = 0; di < values.discounts_per[79].length; di++) { ext_percent += values.discounts_per[79][di].amount; } } if (79 in values.discounts_amn) { for (var di = 0; di < values.discounts_amn[79].length; di++) { ext_percent += values.discounts_amn[79][di].amount; } } var discount = Math.min(inst[79] * ext_percent / 100 + ext_amount, inst[79]); inst.original[79] = inst[79]; inst.discounts[79] = discount; inst.discounted[79] = inst.original[79] - discount; var installment = { date: inst.date, payment: inst.payment, original: { principal: inst.original[78], interest: inst.original[83], commission: inst.original[77], }, principal: inst.discounted[78], interest: inst.discounted[83], commission: inst.discounted[77] }; var installment_without_tax = { date: inst.date, payment: inst.payment, original: { principal: inst.original[78] / (1 + (this.pricelists[78] && this.pricelists[78].tax_percentage ? parseFloat(this.pricelists[78].tax_percentage) : 0)), interest: inst.original[83] / (1 + (this.pricelists[-83] && this.pricelists[-83].tax_percentage ? parseFloat(this.pricelists[-83].tax_percentage) : 0)), commission: inst.original[77] / (1 + (this.pricelists[77] && this.pricelists[77].tax_percentage ? parseFloat(this.pricelists[77].tax_percentage) : 0)) }, principal: inst.discounted[78] / (1 + (this.pricelists[78] && this.pricelists[78].tax_percentage ? parseFloat(this.pricelists[78].tax_percentage) : 0)), interest: inst.discounted[83] / (1 + (this.pricelists[-83] && this.pricelists[-83].tax_percentage ? parseFloat(this.pricelists[-83].tax_percentage) : 0)), commission: inst.discounted[77] / (1 + (this.pricelists[77] && this.pricelists[77].tax_percentage ? parseFloat(this.pricelists[77].tax_percentage) : 0)) }; installment_without_tax.payment = installment_without_tax.principal + installment_without_tax.interest + installment_without_tax.commission; _all_installments.push(installment); _all_installments_without_tax.push(installment_without_tax); for (var id in inst.original) { if (!isNaN(id)) { if (!(id in values.discounted)) { values.discounted[id] = 0; } values.discounted[id] += inst.discounted[id]; if (!(id in values.prices)) { values.prices[id] = 0; } values.prices[id] += inst.original[id]; if (!(id in values.discounts)) { values.discounts[id] = 0; } values.discounts[id] += inst.discounts[id]; } } } var loan_total = values.discounted[78] + values.discounted[77] + values.discounted[83]; 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 == 83) { name = 'interest-fee'; } else if (id == 77) { 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[78], 'loan-total': loan_total, 'loan-free-extension': values.total_extension_term, 'annual-loan-fee-rate': (values.feeRates[77]/ 100).toFixed(10), 'loan-promotable-installments-count': _promotable_installments_count, details: details2, installments: _all_installments, installments_without_tax: _all_installments_without_tax, 'installment-interest-rate': interest_rate, 'loan-duedate': _all_installments[_all_installments.length - 1].date, }; var total_fee_cost_values = 0; if (values.prices['77']) { ret['loan-fees'] = values.discounted[77]; ret['loan-original-fees'] = values.prices[77]; total_fee_cost_values += values.discounted[77]; } if (values.prices['83']) { ret['loan-interest-fees'] = values.discounted['83']; ret['loan-original-interest-fees'] = values.prices['83']; ret['loan-interest-fee-type-id'] = 83; total_fee_cost_values += values.discounted[83]; } ret['loan-total-fee-cost'] = total_fee_cost_values; if (values.customer_data) { ret['customer-data'] = values.customer_data; } if (values.feeRates) { ret['fee-rates'] = values.feeRates; } if (apr == true) { var daysInYear = 365; const installmentsForApr = JSON.parse(JSON.stringify(((bag[-15] || false) == false) ? _all_installments : _all_installments_without_tax)); if (excludeFeesFromApr.length > 0) { for (var ifa = 0; ifa < installmentsForApr.length; ifa++) { for (var ftei = 0; ftei < excludeFeesFromApr.length; ftei++) { if (installmentsForApr[ifa][excludeFeesFromApr[ftei]] != undefined) { installmentsForApr[ifa].payment = installmentsForApr[ifa].payment - installmentsForApr[ifa][excludeFeesFromApr[ftei]]; } } } } var forcePayoutDate = bag["calculation-date"] ? bag["calculation-date"] : bag[-16] ? bag[-16] : null; ret['loan-apr'] = this.XIRR(parseFloat(bag[-3]), installmentsForApr, null, forcePayoutDate, daysInYear); if (interest_rate) { ret['loan-rpy'] = this.rpyForInstallment(interest_rate); ret['loan-rpm'] = this.rpmForInstallment(interest_rate); ret['loan-rpd'] = this.rpdForInstallment(interest_rate, daysInYear); } } return ret; }, fields: { 460: 'Status', 799: 'Loan count', 802: 'Delay', 2438: 'Is First Loan', 2538: 'Verification Mode', 2788: 'Open principal', 3141: 'Product', 3251: 'Has Open Payday Loan', 3252: 'Has Open Creditline', 3253: 'Has Open Installment Loan', 3256: 'Next installment remaining debt', 3353: 'Installment Delay', 4102: 'Late installment remaining debt (w/o LF)', 6054: 'Last Instantor Succesful Verification', 7560: 'is_force_settled', }, 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: [], 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; }, }, }, creditline: { pricelists: { '78': { type: 'principal', fee_type_id: '78', 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'](50, 2000, 5); if (control.canceled) return; bag['setTermRange'](48, 48, 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['amount'] = bag['getAmount'](); bag['setPrice'](vars['amount']); 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) }; } }, '79': { type: 'extension-fee', fee_type_id: '79', 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, 1000, 5) * bag['setTermRange'](1, 1, 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 ([1,3].indexOf(parseInt(bag[460])) !== -1) { vars['price'] = (bag[3256] * 0.3); bag['setPrice'](vars['price']); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if ([17,4].indexOf(parseInt(bag[460])) !== -1) { vars['price'] = (bag[4102] * 0.3); bag['setPrice'](vars['price']); 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: { 77: 'loan_fee', 78: 'principal', 79: 'extension_fee', 80: 'penalty', 81: 'late_fee', 82: 'payout_fee', 83: 'interest_fee', 87: 'cash_fee', 258: 'late_fee_delay', 260: 'late_fee_delay', 262: 'late_fee_delay', 333: 'court_fee', 359: 'sale_fee', 460: 'late_fee_delay', 465: 'court_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: Stage (function () { // Group: Income Verify Limit if ((bag[799] == 0 && (toDate(bag[6054]).getTime() < (1714205011107-7776000000) || [4].indexOf(parseInt(bag[2538])) === -1))) { bag['setIncomeVerifyLimit'](1); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if (bag[799] > 0) { bag['setIncomeVerifyLimit'](1); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if (bag[799] == 0) { bag['setVisibleLimit'](2000); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if (bag[799] == 1) { bag['setVisibleLimit'](2000); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if (bag[799] == 2) { bag['setVisibleLimit'](2000); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if (bag[799] >= 3) { bag['setVisibleLimit'](2000); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if (true) { bag['setSliderDefault'](null, null, 3); 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] = 237; } 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[78].buildDimension(bag); this.pricelists[79].buildDimension(bag); for (var i = 0; i < this.pricelists[78].amounts.length; i++) { var amn = this.pricelists[78].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[78].terms.length; i++) { var day = this.pricelists[78].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[79].terms.length; i++) { var day = this.pricelists[79].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 } }, prolongPermissionRules: { 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: Prohibit prolonging If all products open (function () { // Group: Group if ((bag[3251] == 1 && (bag[3252] == 1 && bag['3253'] == 1))) { bag['prohibitProlong']('Has all products open'); bag['prohibitProlongToInstallment']('Has all products open'); bag['exit'](); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; control.skip = false; // Stage: OLD VERSION OF PROLONGING=>HAS OPEN PD PROLONG TO CRL (function () { // Group: PD --> CRL FL BEFORE DUEDATE if (([1,3,4].indexOf(parseInt(bag[460])) !== -1 && ((((bag[802] < 31 && bag[2788] >= 50) && [1].indexOf(bag[2438] ? 1 : 0) !== -1) && bag[3251] == 1) && bag[3252] == 0))) { bag['prohibitProlong'](null); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: PD --> CRL FL AFTER DUEDATE if (([1,3,4].indexOf(parseInt(bag[460])) !== -1 && (((((bag[802] > 0 && bag[802] < 31) && bag[2788] >= 50) && [1].indexOf(bag[2438] ? 1 : 0) !== -1) && bag[3251] == 1) && bag[3252] == 0))) { bag['prohibitProlong'](null); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: PD --> CRL CL BEFORE DUEDATE if (([1,3,4].indexOf(parseInt(bag[460])) !== -1 && ((((bag[802] < 31 && bag[3252] == 0) && bag[2788] >= 50) && [0].indexOf(bag[2438] ? 1 : 0) !== -1) && bag[3251] == 1))) { bag['prohibitProlong'](null); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; control.skip = false; // Stage: NEW VERSION OF PROLONGING to IL (function () { // Group: PDL if ((bag['3141'] == 1 && ([1,3,4].indexOf(parseInt(bag['460'])) !== -1 && (bag['2788'] >= 50 && bag[802] < 31)))) { bag['prohibitProlongToInstallment'](null); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; control.skip = false; // Stage: NEW VERSION OF PROLONGING IL -> IL (function () { // Group: IL if ((bag['3141'] == 2 && ([1].indexOf(parseInt(bag[460])) !== -1 && (bag['2788'] >= 50 && bag[802] < 31)))) { bag['prohibitProlongToInstallment'](null); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; control.skip = false; // Stage: NEW VERSION OF PROLONGING - TO CRL -> IL (function () { // Group: CRL if ((bag['3141'] == 3 && ([17,1,4].indexOf(parseInt(bag[460])) !== -1 && (bag['2788'] >= 50 && bag['3353'] < 31)))) { bag['prohibitProlongToInstallment'](null); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; return control; } }, 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 = this.prolongPermissionRules.evaluate(ibag); return { 'loan-prolong-permitted': ibag._loanProlongPermitted, 'loan-prolong-permitted-to-payday': ibag._loanProlongPermittedToPayday, 'loan-prolong-permitted-to-installment': ibag._loanProlongPermittedToInstallment, 'loan-prolong-permitted-to-creditline': ibag._loanProlongPermittedToCreditline, 'loan-prolong-error': ibag._errorMessage, 'loan-prolong-to-payday-error': ibag._errorMessageProlongToPayday, 'loan-prolong-to-installment-error': ibag._errorMessageProlongToInstallment, 'loan-prolong-to-creditline-error': ibag._errorMessageProlongToCreditline, 'prolong-loan-setting-id': ibag._loanSettingId, 'loan-feetype-mapping': ibag._feetypes } }, 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: SET-3547 Allow ext for all (function () { // Group: Group if (bag['1217'] !== null) { bag['allowExtension'](); bag['exit'](); if (control.canceled) return; } else { bag['allowExtension'](); bag['exit'](); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; control.skip = false; // Stage: Prohibit extension if force settledate is set (function () { // Group: Group 1 if ((bag['7560'] == 1 && bag['3141'] == 3)) { bag['prohibitExtension']('force settledate set - no extension possible CFIT-3333'); bag['exit'](); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; control.skip = false; // Stage: Prohibit if early repayment (function () { // Group: Prohibit if early repayment if (bag[5266] == 1) { bag['prohibitExtension']('Early settle'); bag['exit'](); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; control.skip = false; // Stage: Remaining debt < 10 Euro (function () { // Group: Group if (((bag[4102] > 0 && bag[4102] < 10) || bag[3353] > 60)) { bag['prohibitExtension']('Fee limit reached'); bag['exit'](); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; control.skip = false; // Stage: Alow (function () { // Group: Group if (true) { bag['allowExtension'](); 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 != 79 ) { 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); 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); 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)); 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[79]; 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[79], 'loan-extension-duedate': calc_duedate, 'loan-extension-permitted': ibag._loanExtensionPermitted, 'loan-extension-error': ibag._errorMessage, 'loan-original-extension-fees': values.prices[79], 'loan-free-extension': values.total_extension_term, details: details2, }; }, evaluateInstallments: function (bag, promotions) { function daysBetween(t1, t2) { var oneDay = 24 * 60 * 60 * 1000; return Math.round(Math.abs((t1.getTime() - t2.getTime())/oneDay)); } function dateWithoutTime(date) { var newdate = date ? new Date(date) : new Date(); newdate.setMinutes(0); newdate.setHours(0); newdate.setSeconds(0); return newdate; } function getIntervalJson(interval) { var yearsSplit = interval.split('y'); var monthsSplit = yearsSplit[1].split('m'); var daysSplit = monthsSplit[1].split('d'); return { years: parseInt(yearsSplit[0]), months: parseInt(monthsSplit[0]), days: parseInt(daysSplit[0]) }; } function getIntervalNewDate(paymentDate, interval) { var intervalJson = getIntervalJson(interval); var intervalNewDate = new Date(paymentDate); intervalNewDate.setFullYear(paymentDate.getFullYear() + intervalJson.years); intervalNewDate.setMonth(paymentDate.getMonth() + intervalJson.months); intervalNewDate.setDate(paymentDate.getDate() + intervalJson.days); return intervalNewDate; } function getMonthlyPaymentDate(date, bag) { if (!bag || !bag['grace-period-days']) { return date; } var monthlyPaymentDate = new Date(date); monthlyPaymentDate.setDate(date.getDate() + parseInt(bag['grace-period-days'])); return monthlyPaymentDate; } function getNextPaymentDate(paymentDate, targetDay, bag) { if (bag && 'use-interval' in bag && bag['use-interval']) { return getIntervalNewDate(paymentDate, bag['interval']); } var followingMonth, followingYear, lastOfFollowingMonth, newDate; if (paymentDate.getMonth() === 11) { followingMonth = 0; followingYear = paymentDate.getFullYear() + 1; } else { followingMonth = paymentDate.getMonth() + 1; followingYear = paymentDate.getFullYear(); } lastOfFollowingMonth = new Date(followingYear, followingMonth + 1, 0); newDate = dateWithoutTime(paymentDate); if(lastOfFollowingMonth.getDate() < targetDay) { newDate.setMonth(lastOfFollowingMonth.getMonth()); newDate.setDate(0); } else { newDate.setMonth(paymentDate.getMonth() + 1); newDate.setDate(targetDay); } return newDate; } function calcPromotionDiscount(feeAmount, feeTypeId, currentInstallmentIndex, promotions) { var totalReduc = 0; if (promotions.discounts_per[feeTypeId]) { var promos = promotions.discounts_per[feeTypeId]; for (var i = 0; i < promos.length; i++) { var p = promos[i]; var a = (p.amount / 100) * feeAmount; if (p.installment_index - 1 === currentInstallmentIndex) { totalReduc += a; } else if (p.installment_index === undefined) { totalReduc += a; } } } if (promotions.discounts_amn[feeTypeId]) { var promos = promotions.discounts_amn[feeTypeId]; for (var z = 0; z < promos.length; z++) { var p = promos[z]; if (! ('used' in p)) { p.used = 0; } var a = Math.min(p.amount - p.used, feeAmount - totalReduc); if (p.installment_index - 1 === currentInstallmentIndex) { totalReduc += a; } else if (p.installment_index === undefined) { totalReduc += a; } p.used += a; }; } return Math.max(0, (feeAmount - totalReduc)); } var amount = parseFloat(bag['loan-amount']); var term = parseFloat(bag['loan-term']); var commission_rate = parseFloat(bag['creditline-percent-creditline']) / 100 / 360; var fallback_rate = parseFloat(bag['creditline-percent-creditline-fallback']) / 100 / 360; var principal_repay_rate = bag['creditline-percent-principal-repay']; var interest_rate = parseFloat(bag['interest-rate']) / 100 / 360; var interest_rate_full = parseFloat(bag['interest-rate']); var min_repay_amount = parseFloat(bag['creditline-min-monthly-payment']); var use_equal_principal_distribution = bag['use-equal-principal-distribution']; var frontend_calculate_sample_term = bag['frontend-calculate-sample-term']; var now = bag['loan-issue-date']; var paymentDate = dateWithoutTime(now); var oldDate = dateWithoutTime(now); var sampleTermDate = dateWithoutTime(now); var schedule = []; var remaining_amount = amount; var commission_carry_over = 0; var interest_carry_over = 0; var repayment; var recalc3 = 0; if (bag['-42']) { bag['-42'].forEach(function (version) { if (version.product_id == 3) { recalc3 = version.recalc_version; } }); } // adapt repayment interval if (bag['first-installment-date']) { paymentDate = dateWithoutTime(bag['first-installment-date']); } else { paymentDate = getNextPaymentDate(paymentDate, paymentDate.getDate(), bag); } // set sample term date if (frontend_calculate_sample_term && frontend_calculate_sample_term != '0y0m0d') { sampleTermDate = getIntervalNewDate(sampleTermDate, frontend_calculate_sample_term); } else { sampleTermDate = null; } var target_day = paymentDate.getDate(); for (var i = 0; i < term; i += 1) { if (i == (term - 1) && sampleTermDate !== null) { if (sampleTermDate > paymentDate) { term++; } else { paymentDate = sampleTermDate; } } var days = daysBetween(oldDate, paymentDate), fallback = Math.floor((fallback_rate * amount * days) * 100) / 100, interest = Math.floor((interest_rate * remaining_amount * days) * 100) / 100, commission = Math.floor((commission_rate * amount * days) * 100) / 100, principal = use_equal_principal_distribution ? Math.floor((amount / term) * 100) / 100 : Math.floor(principal_repay_rate * remaining_amount) / 100, total_payment, old_year_days, new_year_days, schedule_entry; if (recalc3 > 3) { if (paymentDate.getFullYear() > oldDate.getFullYear()) { old_year_days = daysBetween(new Date(oldDate.getFullYear(), 0, 1), new Date(oldDate.getFullYear() + 1, 0, 1)); new_year_days = daysBetween(new Date(paymentDate.getFullYear(), 0, 1), new Date(paymentDate.getFullYear() + 1, 0, 1)); var installment_days_in_old_year = daysBetween(oldDate, new Date(oldDate.getFullYear() + 1, 0, 1)) + 1, installment_days_in_new_year = daysBetween(new Date(paymentDate.getFullYear(), 0, 1), paymentDate) - 1; interest = Math.floor( ( (remaining_amount * (interest_rate_full / 100 / old_year_days * installment_days_in_old_year )) + (remaining_amount * (interest_rate_full / 100 / new_year_days * installment_days_in_new_year )) ) * 100) / 100; fallback = Math.floor( ( (parseFloat(bag['creditline-percent-creditline-fallback']) / 100 / old_year_days * amount * installment_days_in_old_year ) + (parseFloat(bag['creditline-percent-creditline-fallback']) / 100 / new_year_days * amount * installment_days_in_new_year ) ) * 100) / 100; commission = Math.floor( ( (parseFloat(bag['creditline-percent-creditline']) / 100 / old_year_days * amount * installment_days_in_old_year ) + (parseFloat(bag['creditline-percent-creditline']) / 100 / new_year_days * amount * installment_days_in_new_year ) ) * 100) / 100; } else { old_year_days = daysBetween(new Date(oldDate.getFullYear(), 0, 1), new Date(oldDate.getFullYear() + 1, 0, 1)); interest = Math.floor(remaining_amount * (interest_rate_full / 100 / old_year_days * days) * 100) / 100; fallback = Math.floor(parseFloat(bag['creditline-percent-creditline-fallback']) / 100 / old_year_days * amount * days * 100) / 100; commission = Math.floor(parseFloat(bag['creditline-percent-creditline']) / 100 / old_year_days * amount * days * 100) / 100; } } commission_carry_over += commission - (Math.floor(commission * 100) / 100); interest_carry_over += (interest_rate * remaining_amount * days) - interest; if (recalc3 == 0) { if (commission_carry_over >= 0.01) { commission_carry_over -= 0.01; commission += 0.01; } if (interest_carry_over >= 0.01) { interest_carry_over -= 0.01; interest += 0.01; } } if (fallback > (interest + commission)) { commission = fallback; interest = 0; } total_payment = commission + interest + principal; if (total_payment < min_repay_amount) { principal = Math.min(principal + (min_repay_amount - total_payment), remaining_amount); } else if (i == term - 1) { principal = remaining_amount; } if (interest > 0 && bag['additional-interest-per-installment'] && !isNaN(bag['additional-interest-per-installment'])) { interest += parseFloat(bag['additional-interest-per-installment']); } schedule_entry = { date: paymentDate, monthlyPaymentDate: getMonthlyPaymentDate(paymentDate, bag), original: { 78: principal, 77: commission, 83: interest }, discounted: { 78: principal, 77: calcPromotionDiscount(commission, 77, i, promotions), 83: calcPromotionDiscount(interest, 83, i, promotions), }, discounts: {} }; schedule_entry.payment = schedule_entry.discounted[78] + schedule_entry.discounted[77] + schedule_entry.discounted[83]; schedule_entry.discounts[78] = parseFloat(schedule_entry.original[78] - schedule_entry.discounted[78]); schedule_entry.discounts[77] = parseFloat(schedule_entry.original[77] - schedule_entry.discounted[77]); schedule_entry.discounts[83] = parseFloat(schedule_entry.original[83] - schedule_entry.discounted[83]); schedule.push(schedule_entry); oldDate = new Date(paymentDate); paymentDate = getNextPaymentDate(paymentDate, target_day, bag); remaining_amount -= principal; } return { schedule: schedule }; }, 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]; 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 ('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); } } var interest_rate = 48.00; values.feeRates[83] = interest_rate; values.feeRates[77] = 0.00; this.evaluateLoanPromotions(now, bag, values); var details2 = { 'percent-discounts': {}, 'amount-discounts': {}, extensions: {}, original: {}, discount: {}, final: {}, tax_percentage: {}, without_tax: { original: {}, final: {}, discount: {} } }; var subbag = {}; subbag['additional-interest-per-installment'] = bag['additional-interest-per-installment']; subbag['first-installment-date'] = bag['first-installment-date']; subbag['loan-amount'] = amn; subbag['loan-term'] = term; subbag['loan-fee'] = values.prices[77]; subbag['creditline-percent-creditline'] = 0.00; subbag['creditline-percent-creditline-fallback'] = 0.00; subbag['creditline-percent-principal-repay'] = 180.00; subbag['creditline-percent-principal'] = 48.00; subbag['creditline-min-monthly-payment'] = 0.00; subbag['use-equal-principal-distribution'] = true; subbag['frontend-calculate-sample-term'] = bag[-43] ? '0y0m0d' : null; subbag['loan-issue-date'] = bag[-16] ? new Date(bag[-16]) : new Date(); if (bag['calculation-date']) { subbag['loan-issue-date'] = new Date(bag['calculation-date']); } subbag['interval'] = '0y1m0d'; subbag['use-interval'] = false; subbag['interest-rate'] = interest_rate; subbag['grace-period-days'] = ''; if (bag[-42]) { subbag[-42] = bag[-42]; } var _pre_all_installments = this.evaluateInstallments(subbag, values).schedule; var _all_installments = []; var _all_installments_without_tax = []; values.prices = {}; for (var i = 0, max = _pre_all_installments.length; i < max; i += 1) { var inst = _pre_all_installments[i]; var ext_percent = 0; var ext_amount = 0; if (79 in values.discounts_per) { for (var di = 0; di < values.discounts_per[79].length; di++) { ext_percent += values.discounts_per[79][di].amount; } } if (79 in values.discounts_amn) { for (var di = 0; di < values.discounts_amn[79].length; di++) { ext_percent += values.discounts_amn[79][di].amount; } } var discount = Math.min(inst[79] * ext_percent / 100 + ext_amount, inst[79]); inst.original[79] = inst[79]; inst.discounts[79] = discount; inst.discounted[79] = inst.original[79] - discount; var installment = { date: inst.date, monthlyPaymentDate: inst.monthlyPaymentDate, payment: inst.payment, original: { principal: inst.original[78], interest: inst.original[83], commission: inst.original[77], }, principal: inst.discounted[78], interest: inst.discounted[83], commission: inst.discounted[77] }; var installment_without_tax = { date: inst.date, monthlyPaymentDate: inst.monthlyPaymentDate, payment: inst.payment, original: { principal: inst.original[78] / (1 + (this.pricelists[78] && this.pricelists[78].tax_percentage ? parseFloat(this.pricelists[78].tax_percentage) : 0 )), interest: inst.original[83] / (1 + parseFloat(0.000)), commission: inst.original[77] / (1 + (this.pricelists[77] && this.pricelists[77].tax_percentage ? parseFloat(this.pricelists[77].tax_percentage): 0 )) }, principal: inst.discounted[78] / (1 + (this.pricelists[78] && this.pricelists[78].tax_percentage ? parseFloat(this.pricelists[78].tax_percentage) : 0 )), interest: inst.discounted[83] / (1 + parseFloat(0.000)), commission: inst.discounted[77] / (1 + (this.pricelists[77] && this.pricelists[77].tax_percentage ? parseFloat(this.pricelists[77].tax_percentage) : 0)) }; installment_without_tax.payment = installment_without_tax.principal + installment_without_tax.interest + installment_without_tax.commission; _all_installments.push(installment); _all_installments_without_tax.push(installment_without_tax); for (var id in inst.original) { if (!isNaN(id)) { if (!(id in values.discounted)) { values.discounted[id] = 0; } values.discounted[id] += inst.discounted[id]; if (!(id in values.prices)) { values.prices[id] = 0; } values.prices[id] += inst.original[id]; if (!(id in values.discounts)) { values.discounts[id] = 0; } values.discounts[id] += inst.discounts[id]; } } } var loan_total = values.discounted[78] + values.discounted[77] + values.discounted[83]; var loan_total_without_tax = (values.discounted_without_tax[78] || values.discounted[78]) + (values.discounted_without_tax[77] || values.discounted[77]); for (var id in values.prices) { var discountPercentAr = values.discounts_per[id]; var name = ''; if (id in this.pricelists) { name = this.pricelists[id].type; } else if (id == 83) { name = 'interest-fee'; } else if (id == 77) { name = 'loan-fee'; } if (discountPercentAr) { details2['percent-discounts'][name] = discountPercentAr; } var discountAmountAr = values.discounts_amn[id]; if (discountAmountAr) { details2['amount-discounts'][name] = discountAmountAr; } if (values.prices_without_tax[id]) { values.discounted_without_tax[id] = parseFloat((values.discounted[id] / (1 + parseFloat(this.pricelists[id].tax_percentage))).toFixed(2)); values.discounts_without_tax[id] = parseFloat((values.prices_without_tax[id] - values.discounted_without_tax[id]).toFixed(2)); } } var creditLimitResult = null; for (var id in values.prices) { var name = ''; if (id in this.pricelists) { name = this.pricelists[id].type; } else if (id == 83) { name = 'interest-fee'; } else if (id == 77) { 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[78], 'loan-fees': values.discounted[77], 'loan-original-fees': values.prices[77], 'loan-total': loan_total, 'loan-free-extension': values.total_extension_term, 'loan-interest-fees': values.discounted['83'], 'loan-original-interest-fees': values.prices['83'], 'creditline-percent-principal-repay-month': 0.1500000000, 'creditline-percent-principal-month': 0.0400000000, 'creditline-percent-creditline-month': 0.0000000000, 'creditline-percent-creditline-fallback-month': 0.0000000000, 'creditline-min-monthly-payment': 0.00, 'creditline-annual-interest-rate': (values.feeRates[83]/ 100).toFixed(10), 'creditline-monthly-interest-rate': (values.feeRates[83]/ 100 / 12).toFixed(10), details: details2, installments: _all_installments, installments_without_tax: _all_installments_without_tax, 'loan-duedate': _all_installments[_all_installments.length - 1].date }; if (apr == true) { var daysInYear = 365; var forcePayoutDate = bag["calculation-date"] ? bag["calculation-date"] : bag[-16] ? bag[-16] : null; ret['loan-apr'] = this.XIRR(parseFloat(bag[-3]), ((bag[-15] || false) == false) ? _all_installments : _all_installments_without_tax, null, forcePayoutDate, daysInYear); } if (values.customer_data) { ret['customer-data'] = values.customer_data; } if (values.feeRates) { ret['fee-rates'] = values.feeRates; } if (creditLimitResult) { ret['loan-credit-limit'] = creditLimitResult.limit; } return ret; }, additional_data: { distribute_equal: true, }, fields: { 460: 'Status', 799: 'Loan count', 802: 'Delay', 1217: 'Id', 2438: 'Is First Loan', 2538: 'Verification Mode', 2788: 'Open principal', 3141: 'Product', 3251: 'Has Open Payday Loan', 3252: 'Has Open Creditline', 3253: 'Has Open Installment Loan', 3256: 'Next installment remaining debt', 3353: 'Installment Delay', 4102: 'Late installment remaining debt (w/o LF)', 5266: 'is active early settlement', 6054: 'Last Instantor Succesful Verification', 7560: 'is_force_settled', }, 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: [], 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; }, }, calculateCreditLimitPayoutFees: function(bag) { return this.evaluate(bag); }, }, payday: { pricelists: { '77': { type: 'loan-fee', fee_type_id: '77', 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'](50, 215, 5) * bag['setTermRange'](7, 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: Stage (function () { // Group: Group if (true) { vars['am'] = bag['getAmount'](); if (control.canceled) return; vars['loanterm'] = bag['getTerm'](); if (control.canceled) return; vars['final'] = ((vars['am'] * 0.0029333333) * vars['loanterm']); if (control.canceled) return; bag['setPrice'](vars['final']); 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) }; } }, '78': { type: 'principal', fee_type_id: '78', 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'](50, 215, 5) * bag['setTermRange'](7, 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: Stage (function () { // Group: Group if (true) { vars['am'] = bag['getAmount'](); bag['setPrice'](vars['am']); 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) }; } }, '79': { type: 'extension-fee', fee_type_id: '79', 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: Set dimensions (function () { // Group: Group if (true) { bag['setAmountRange'](5, 700, 5); if (control.canceled) return; bag['setTermRange'](30, 30, 30); 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: Variables (function () { // Group: Group if (bag[799] == 0) { vars['finalprice'] = (bag[-3] * 0.075); vars['tterm'] = bag['getTerm'](); if (control.canceled) return; } else { vars['sumdisc'] = (bag[2789] - bag[4054]); if (control.canceled) return; vars['maxprice'] = (bag[-3] * 0.075); if (control.canceled) return; vars['temprice'] = ((bag[-3] * 0.06) + vars['sumdisc']); if (control.canceled) return; vars['check'] = (vars['maxprice'] - vars['temprice']); if (control.canceled) return; vars['tterm'] = bag['getTerm'](); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: First ext check if ((vars['check'] <= 0 && (bag[3626] == 0 && bag[799] != 0))) { vars['finalprice'] = vars['maxprice']; if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: First ext check if ((vars['check'] > 0 && (bag[3626] == 0 && bag[799] != 0))) { vars['finalprice'] = vars['temprice']; if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Next ext check if ((bag[3626] != 0 && bag[799] != 0)) { vars['finalprice'] = (bag[-3] * 0.06); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; control.skip = false; // Stage: Term check (function () { // Group: Group if (vars['tterm'] == 30) { bag['setPrice'](vars['finalprice']); if (control.canceled) return; } else { vars['finalprice'] = (vars['finalprice'] / 2); bag['setPrice'](vars['finalprice']); 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) }; } }, }, additional_payout_pricelist: { type: 'additional-payout-fee', fee_type_id: '77', tax_percentage: '0.000', amounts: ["10.00","20.00","30.00","40.00","50.00","60.00","70.00","80.00","90.00","100.00","110.00","120.00","130.00","140.00","150.00","160.00","170.00","180.00","190.00","200.00","210.00","220.00","230.00","240.00","250.00","260.00","270.00","280.00","290.00","300.00","310.00","320.00","330.00","340.00","350.00","360.00","370.00","380.00","390.00","400.00","410.00","420.00","430.00","440.00","450.00","460.00","470.00","480.00","490.00","500.00"], terms: ["7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23","24","25","26","27","28","29","30"], data: {"10.00":{"7":"0.39","8":"0.41","9":"0.43","10":"0.46","11":"0.48","12":"0.51","13":"0.53","14":"0.56","15":"0.58","16":"0.60","17":"0.62","18":"0.64","19":"0.66","20":"0.68","21":"0.70","22":"0.72","23":"0.74","24":"0.76","25":"0.78","26":"0.80","27":"0.82","28":"0.84","29":"0.86","30":"0.88"},"20.00":{"7":"0.77","8":"0.82","9":"0.87","10":"0.92","11":"0.97","12":"1.02","13":"1.07","14":"1.12","15":"1.16","16":"1.20","17":"1.24","18":"1.28","19":"1.32","20":"1.36","21":"1.40","22":"1.44","23":"1.48","24":"1.52","25":"1.56","26":"1.60","27":"1.64","28":"1.68","29":"1.72","30":"1.76"},"30.00":{"7":"1.16","8":"1.23","9":"1.30","10":"1.38","11":"1.45","12":"1.53","13":"1.60","14":"1.68","15":"1.74","16":"1.80","17":"1.86","18":"1.92","19":"1.98","20":"2.04","21":"2.10","22":"2.16","23":"2.22","24":"2.28","25":"2.34","26":"2.40","27":"2.46","28":"2.52","29":"2.58","30":"2.64"},"40.00":{"7":"1.54","8":"1.64","9":"1.74","10":"1.84","11":"1.94","12":"2.04","13":"2.14","14":"2.24","15":"2.32","16":"2.40","17":"2.48","18":"2.56","19":"2.64","20":"2.72","21":"2.80","22":"2.88","23":"2.96","24":"3.04","25":"3.12","26":"3.20","27":"3.28","28":"3.36","29":"3.44","30":"3.52"},"50.00":{"7":"1.93","8":"2.05","9":"2.17","10":"2.30","11":"2.42","12":"2.55","13":"2.67","14":"2.80","15":"2.90","16":"3.00","17":"3.10","18":"3.20","19":"3.30","20":"3.40","21":"3.50","22":"3.60","23":"3.70","24":"3.80","25":"3.90","26":"4.00","27":"4.10","28":"4.20","29":"4.30","30":"4.40"},"60.00":{"7":"2.31","8":"2.46","9":"2.61","10":"2.76","11":"2.91","12":"3.06","13":"3.21","14":"3.36","15":"3.48","16":"3.60","17":"3.72","18":"3.84","19":"3.96","20":"4.08","21":"4.20","22":"4.32","23":"4.44","24":"4.56","25":"4.68","26":"4.80","27":"4.92","28":"5.04","29":"5.16","30":"5.28"},"70.00":{"7":"2.70","8":"2.87","9":"3.04","10":"3.22","11":"3.39","12":"3.57","13":"3.74","14":"3.92","15":"4.06","16":"4.20","17":"4.34","18":"4.48","19":"4.62","20":"4.76","21":"4.90","22":"5.04","23":"5.18","24":"5.32","25":"5.46","26":"5.60","27":"5.74","28":"5.88","29":"6.02","30":"6.16"},"80.00":{"7":"3.08","8":"3.28","9":"3.48","10":"3.68","11":"3.88","12":"4.08","13":"4.28","14":"4.48","15":"4.64","16":"4.80","17":"4.96","18":"5.12","19":"5.28","20":"5.44","21":"5.60","22":"5.76","23":"5.92","24":"6.08","25":"6.24","26":"6.40","27":"6.56","28":"6.72","29":"6.88","30":"7.04"},"90.00":{"7":"3.47","8":"3.69","9":"3.91","10":"4.14","11":"4.36","12":"4.59","13":"4.81","14":"5.04","15":"5.22","16":"5.40","17":"5.58","18":"5.76","19":"5.94","20":"6.12","21":"6.30","22":"6.48","23":"6.66","24":"6.84","25":"7.02","26":"7.20","27":"7.38","28":"7.56","29":"7.74","30":"7.92"},"100.00":{"7":"3.85","8":"4.10","9":"4.35","10":"4.60","11":"4.85","12":"5.10","13":"5.35","14":"5.60","15":"5.80","16":"6.00","17":"6.20","18":"6.40","19":"6.60","20":"6.80","21":"7.00","22":"7.20","23":"7.40","24":"7.60","25":"7.80","26":"8.00","27":"8.20","28":"8.40","29":"8.60","30":"8.80"},"110.00":{"7":"4.24","8":"4.51","9":"4.78","10":"5.06","11":"5.33","12":"5.61","13":"5.88","14":"6.16","15":"6.38","16":"6.60","17":"6.82","18":"7.04","19":"7.26","20":"7.48","21":"7.70","22":"7.92","23":"8.14","24":"8.36","25":"8.58","26":"8.80","27":"9.02","28":"9.24","29":"9.46","30":"9.68"},"120.00":{"7":"4.62","8":"4.92","9":"5.22","10":"5.52","11":"5.82","12":"6.12","13":"6.42","14":"6.72","15":"6.96","16":"7.20","17":"7.44","18":"7.68","19":"7.92","20":"8.16","21":"8.40","22":"8.64","23":"8.88","24":"9.12","25":"9.36","26":"9.60","27":"9.84","28":"10.08","29":"10.32","30":"10.56"},"130.00":{"7":"5.01","8":"5.33","9":"5.65","10":"5.98","11":"6.30","12":"6.63","13":"6.95","14":"7.28","15":"7.54","16":"7.80","17":"8.06","18":"8.32","19":"8.58","20":"8.84","21":"9.10","22":"9.36","23":"9.62","24":"9.88","25":"10.14","26":"10.40","27":"10.66","28":"10.92","29":"11.18","30":"11.44"},"140.00":{"7":"5.39","8":"5.74","9":"6.09","10":"6.44","11":"6.79","12":"7.14","13":"7.49","14":"7.84","15":"8.12","16":"8.40","17":"8.68","18":"8.96","19":"9.24","20":"9.52","21":"9.80","22":"10.08","23":"10.36","24":"10.64","25":"10.92","26":"11.20","27":"11.48","28":"11.76","29":"12.04","30":"12.32"},"150.00":{"7":"5.78","8":"6.15","9":"6.52","10":"6.90","11":"7.27","12":"7.65","13":"8.02","14":"8.40","15":"8.70","16":"9.00","17":"9.30","18":"9.60","19":"9.90","20":"10.20","21":"10.50","22":"10.80","23":"11.10","24":"11.40","25":"11.70","26":"12.00","27":"12.30","28":"12.60","29":"12.90","30":"13.20"},"160.00":{"7":"6.16","8":"6.56","9":"6.96","10":"7.36","11":"7.76","12":"8.16","13":"8.56","14":"8.96","15":"9.28","16":"9.60","17":"9.92","18":"10.24","19":"10.56","20":"10.88","21":"11.20","22":"11.52","23":"11.84","24":"12.16","25":"12.48","26":"12.80","27":"13.12","28":"13.44","29":"13.76","30":"14.08"},"170.00":{"7":"6.55","8":"6.97","9":"7.39","10":"7.82","11":"8.24","12":"8.67","13":"9.09","14":"9.52","15":"9.86","16":"10.20","17":"10.54","18":"10.88","19":"11.22","20":"11.56","21":"11.90","22":"12.24","23":"12.58","24":"12.92","25":"13.26","26":"13.60","27":"13.94","28":"14.28","29":"14.62","30":"14.96"},"180.00":{"7":"6.93","8":"7.38","9":"7.83","10":"8.28","11":"8.73","12":"9.18","13":"9.63","14":"10.08","15":"10.44","16":"10.80","17":"11.16","18":"11.52","19":"11.88","20":"12.24","21":"12.60","22":"12.96","23":"13.32","24":"13.68","25":"14.04","26":"14.40","27":"14.76","28":"15.12","29":"15.48","30":"15.84"},"190.00":{"7":"7.32","8":"7.79","9":"8.26","10":"8.74","11":"9.21","12":"9.69","13":"10.16","14":"10.64","15":"11.02","16":"11.40","17":"11.78","18":"12.16","19":"12.54","20":"12.92","21":"13.30","22":"13.68","23":"14.06","24":"14.44","25":"14.82","26":"15.20","27":"15.58","28":"15.96","29":"16.34","30":"16.72"},"200.00":{"7":"7.70","8":"8.20","9":"8.70","10":"9.20","11":"9.70","12":"10.20","13":"10.70","14":"11.20","15":"11.60","16":"12.00","17":"12.40","18":"12.80","19":"13.20","20":"13.60","21":"14.00","22":"14.40","23":"14.80","24":"15.20","25":"15.60","26":"16.00","27":"16.40","28":"16.80","29":"17.20","30":"17.60"},"210.00":{"7":"8.09","8":"8.61","9":"9.13","10":"9.66","11":"10.18","12":"10.71","13":"11.23","14":"11.76","15":"12.18","16":"12.60","17":"13.02","18":"13.44","19":"13.86","20":"14.28","21":"14.70","22":"15.12","23":"15.54","24":"15.96","25":"16.38","26":"16.80","27":"17.22","28":"17.64","29":"18.06","30":"18.48"},"220.00":{"7":"8.47","8":"9.02","9":"9.57","10":"10.12","11":"10.67","12":"11.22","13":"11.77","14":"12.32","15":"12.76","16":"13.20","17":"13.64","18":"14.08","19":"14.52","20":"14.96","21":"15.40","22":"15.84","23":"16.28","24":"16.72","25":"17.16","26":"17.60","27":"18.04","28":"18.48","29":"18.92","30":"19.36"},"230.00":{"7":"8.86","8":"9.43","9":"10.00","10":"10.58","11":"11.15","12":"11.73","13":"12.30","14":"12.88","15":"13.34","16":"13.80","17":"14.26","18":"14.72","19":"15.18","20":"15.64","21":"16.10","22":"16.56","23":"17.02","24":"17.48","25":"17.94","26":"18.40","27":"18.86","28":"19.32","29":"19.78","30":"20.24"},"240.00":{"7":"9.24","8":"9.84","9":"10.44","10":"11.04","11":"11.64","12":"12.24","13":"12.84","14":"13.44","15":"13.92","16":"14.40","17":"14.88","18":"15.36","19":"15.84","20":"16.32","21":"16.80","22":"17.28","23":"17.76","24":"18.24","25":"18.72","26":"19.20","27":"19.68","28":"20.16","29":"20.64","30":"21.12"},"250.00":{"7":"9.63","8":"10.25","9":"10.87","10":"11.50","11":"12.12","12":"12.75","13":"13.37","14":"14.00","15":"14.50","16":"15.00","17":"15.50","18":"16.00","19":"16.50","20":"17.00","21":"17.50","22":"18.00","23":"18.50","24":"19.00","25":"19.50","26":"20.00","27":"20.50","28":"21.00","29":"21.50","30":"22.00"},"260.00":{"7":"10.01","8":"10.66","9":"11.31","10":"11.96","11":"12.61","12":"13.26","13":"13.91","14":"14.56","15":"15.08","16":"15.60","17":"16.12","18":"16.64","19":"17.16","20":"17.68","21":"18.20","22":"18.72","23":"19.24","24":"19.76","25":"20.28","26":"20.80","27":"21.32","28":"21.84","29":"22.36","30":"22.88"},"270.00":{"7":"10.40","8":"11.07","9":"11.74","10":"12.42","11":"13.09","12":"13.77","13":"14.44","14":"15.12","15":"15.66","16":"16.20","17":"16.74","18":"17.28","19":"17.82","20":"18.36","21":"18.90","22":"19.44","23":"19.98","24":"20.52","25":"21.06","26":"21.60","27":"22.14","28":"22.68","29":"23.22","30":"23.76"},"280.00":{"7":"10.78","8":"11.48","9":"12.18","10":"12.88","11":"13.58","12":"14.28","13":"14.98","14":"15.68","15":"16.24","16":"16.80","17":"17.36","18":"17.92","19":"18.48","20":"19.04","21":"19.60","22":"20.16","23":"20.72","24":"21.28","25":"21.84","26":"22.40","27":"22.96","28":"23.52","29":"24.08","30":"24.64"},"290.00":{"7":"11.17","8":"11.89","9":"12.61","10":"13.34","11":"14.06","12":"14.79","13":"15.51","14":"16.24","15":"16.82","16":"17.40","17":"17.98","18":"18.56","19":"19.14","20":"19.72","21":"20.30","22":"20.88","23":"21.46","24":"22.04","25":"22.62","26":"23.20","27":"23.78","28":"24.36","29":"24.94","30":"25.52"},"300.00":{"7":"11.55","8":"12.30","9":"13.05","10":"13.80","11":"14.55","12":"15.30","13":"16.05","14":"16.80","15":"17.40","16":"18.00","17":"18.60","18":"19.20","19":"19.80","20":"20.40","21":"21.00","22":"21.60","23":"22.20","24":"22.80","25":"23.40","26":"24.00","27":"24.60","28":"25.20","29":"25.80","30":"26.40"},"310.00":{"7":"11.94","8":"12.71","9":"13.48","10":"14.26","11":"15.03","12":"15.81","13":"16.58","14":"17.36","15":"17.98","16":"18.60","17":"19.22","18":"19.84","19":"20.46","20":"21.08","21":"21.70","22":"22.32","23":"22.94","24":"23.56","25":"24.18","26":"24.80","27":"25.42","28":"26.04","29":"26.66","30":"27.28"},"320.00":{"7":"12.32","8":"13.12","9":"13.92","10":"14.72","11":"15.52","12":"16.32","13":"17.12","14":"17.92","15":"18.56","16":"19.20","17":"19.84","18":"20.48","19":"21.12","20":"21.76","21":"22.40","22":"23.04","23":"23.68","24":"24.32","25":"24.96","26":"25.60","27":"26.24","28":"26.88","29":"27.52","30":"28.16"},"330.00":{"7":"12.71","8":"13.53","9":"14.35","10":"15.18","11":"16.00","12":"16.83","13":"17.65","14":"18.48","15":"19.14","16":"19.80","17":"20.46","18":"21.12","19":"21.78","20":"22.44","21":"23.10","22":"23.76","23":"24.42","24":"25.08","25":"25.74","26":"26.40","27":"27.06","28":"27.72","29":"28.38","30":"29.04"},"340.00":{"7":"13.09","8":"13.94","9":"14.79","10":"15.64","11":"16.49","12":"17.34","13":"18.19","14":"19.04","15":"19.72","16":"20.40","17":"21.08","18":"21.76","19":"22.44","20":"23.12","21":"23.80","22":"24.48","23":"25.16","24":"25.84","25":"26.52","26":"27.20","27":"27.88","28":"28.56","29":"29.24","30":"29.92"},"350.00":{"7":"13.48","8":"14.35","9":"15.22","10":"16.10","11":"16.97","12":"17.85","13":"18.72","14":"19.60","15":"20.30","16":"21.00","17":"21.70","18":"22.40","19":"23.10","20":"23.80","21":"24.50","22":"25.20","23":"25.90","24":"26.60","25":"27.30","26":"28.00","27":"28.70","28":"29.40","29":"30.10","30":"30.80"},"360.00":{"7":"13.86","8":"14.76","9":"15.66","10":"16.56","11":"17.46","12":"18.36","13":"19.26","14":"20.16","15":"20.88","16":"21.60","17":"22.32","18":"23.04","19":"23.76","20":"24.48","21":"25.20","22":"25.92","23":"26.64","24":"27.36","25":"28.08","26":"28.80","27":"29.52","28":"30.24","29":"30.97","30":"31.68"},"370.00":{"7":"14.25","8":"15.17","9":"16.09","10":"17.02","11":"17.94","12":"18.87","13":"19.79","14":"20.72","15":"21.46","16":"22.20","17":"22.94","18":"23.68","19":"24.42","20":"25.16","21":"25.90","22":"26.64","23":"27.38","24":"28.12","25":"28.86","26":"29.60","27":"30.34","28":"31.08","29":"31.83","30":"32.56"},"380.00":{"7":"14.63","8":"15.58","9":"16.53","10":"17.48","11":"18.43","12":"19.38","13":"20.33","14":"21.28","15":"22.04","16":"22.80","17":"23.56","18":"24.32","19":"25.08","20":"25.84","21":"26.60","22":"27.36","23":"28.12","24":"28.88","25":"29.64","26":"30.40","27":"31.16","28":"31.92","29":"32.69","30":"33.44"},"390.00":{"7":"15.02","8":"15.99","9":"16.96","10":"17.94","11":"18.91","12":"19.89","13":"20.86","14":"21.84","15":"22.62","16":"23.40","17":"24.18","18":"24.96","19":"25.74","20":"26.52","21":"27.30","22":"28.08","23":"28.86","24":"29.64","25":"30.42","26":"31.20","27":"31.98","28":"32.76","29":"33.55","30":"34.32"},"400.00":{"7":"15.40","8":"16.40","9":"17.40","10":"18.40","11":"19.40","12":"20.40","13":"21.40","14":"22.40","15":"23.20","16":"24.00","17":"24.80","18":"25.60","19":"26.40","20":"27.20","21":"28.00","22":"28.80","23":"29.60","24":"30.40","25":"31.20","26":"32.00","27":"32.80","28":"33.60","29":"34.41","30":"35.20"},"410.00":{"7":"15.79","8":"16.81","9":"17.83","10":"18.86","11":"19.88","12":"20.91","13":"21.93","14":"22.96","15":"23.78","16":"24.60","17":"25.42","18":"26.24","19":"27.06","20":"27.88","21":"28.70","22":"29.52","23":"30.34","24":"31.16","25":"31.98","26":"32.80","27":"33.62","28":"34.44","29":"35.27","30":"36.08"},"420.00":{"7":"16.17","8":"17.22","9":"18.27","10":"19.32","11":"20.37","12":"21.42","13":"22.47","14":"23.52","15":"24.36","16":"25.20","17":"26.04","18":"26.88","19":"27.72","20":"28.56","21":"29.40","22":"30.24","23":"31.08","24":"31.92","25":"32.76","26":"33.60","27":"34.44","28":"35.28","29":"36.13","30":"36.96"},"430.00":{"7":"16.56","8":"17.63","9":"18.70","10":"19.78","11":"20.85","12":"21.93","13":"23.00","14":"24.08","15":"24.94","16":"25.80","17":"26.66","18":"27.52","19":"28.38","20":"29.24","21":"30.10","22":"30.96","23":"31.82","24":"32.68","25":"33.54","26":"34.40","27":"35.26","28":"36.12","29":"36.99","30":"37.84"},"440.00":{"7":"16.94","8":"18.04","9":"19.14","10":"20.24","11":"21.34","12":"22.44","13":"23.54","14":"24.64","15":"25.52","16":"26.40","17":"27.28","18":"28.16","19":"29.04","20":"29.92","21":"30.80","22":"31.68","23":"32.56","24":"33.44","25":"34.32","26":"35.20","27":"36.08","28":"36.96","29":"37.85","30":"38.72"},"450.00":{"7":"17.33","8":"18.45","9":"19.57","10":"20.70","11":"21.82","12":"22.95","13":"24.07","14":"25.20","15":"26.10","16":"27.00","17":"27.90","18":"28.80","19":"29.70","20":"30.60","21":"31.50","22":"32.40","23":"33.30","24":"34.20","25":"35.10","26":"36.00","27":"36.90","28":"37.80","29":"38.71","30":"39.60"},"460.00":{"7":"17.71","8":"18.86","9":"20.01","10":"21.16","11":"22.31","12":"23.46","13":"24.61","14":"25.76","15":"26.68","16":"27.60","17":"28.52","18":"29.44","19":"30.36","20":"31.28","21":"32.20","22":"33.12","23":"34.04","24":"34.96","25":"35.88","26":"36.80","27":"37.72","28":"38.64","29":"39.57","30":"40.48"},"470.00":{"7":"18.10","8":"19.27","9":"20.44","10":"21.62","11":"22.79","12":"23.97","13":"25.14","14":"26.32","15":"27.26","16":"28.20","17":"29.14","18":"30.08","19":"31.02","20":"31.96","21":"32.90","22":"33.84","23":"34.78","24":"35.72","25":"36.66","26":"37.60","27":"38.54","28":"39.48","29":"40.43","30":"41.36"},"480.00":{"7":"18.48","8":"19.68","9":"20.88","10":"22.08","11":"23.28","12":"24.48","13":"25.68","14":"26.88","15":"27.84","16":"28.80","17":"29.76","18":"30.72","19":"31.68","20":"32.64","21":"33.60","22":"34.56","23":"35.52","24":"36.48","25":"37.44","26":"38.40","27":"39.36","28":"40.32","29":"41.29","30":"42.24"},"490.00":{"7":"18.87","8":"20.09","9":"21.31","10":"22.54","11":"23.76","12":"24.99","13":"26.21","14":"27.44","15":"28.42","16":"29.40","17":"30.38","18":"31.36","19":"32.34","20":"33.32","21":"34.30","22":"35.28","23":"36.26","24":"37.24","25":"38.22","26":"39.20","27":"40.18","28":"41.16","29":"42.15","30":"43.12"},"500.00":{"7":"19.25","8":"20.50","9":"21.75","10":"23.00","11":"24.25","12":"25.50","13":"26.75","14":"28.00","15":"29.00","16":"30.00","17":"31.00","18":"32.00","19":"33.00","20":"34.00","21":"35.00","22":"36.00","23":"37.00","24":"38.00","25":"39.00","26":"40.00","27":"41.00","28":"42.00","29":"43.01","30":"44.00"}} }, fee_types: { 77: 'loan_fee', 78: 'principal', 79: 'extension_fee', 80: 'penalty', 81: 'late_fee', 82: 'payout_fee', 83: 'interest_fee', 87: 'cash_fee', 258: 'late_fee_delay', 260: 'late_fee_delay', 262: 'late_fee_delay', 333: 'court_fee', 359: 'sale_fee', 460: 'late_fee_delay', 465: 'court_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: Stage (function () { // Group: Income Verify Limit if ((bag[799] == 0 && (toDate(bag[6054]).getTime() < (1714205011131-7776000000) || [4].indexOf(parseInt(bag[2538])) === -1))) { bag['setIncomeVerifyLimit'](1); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if (bag[799] > 0) { bag['setIncomeVerifyLimit'](1); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if (bag[799] >= 0) { bag['setAvailableLimit'](500); bag['setVisibleLimit'](500); 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] = 237; } 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[77].buildDimension(bag); this.pricelists[78].buildDimension(bag); this.pricelists[79].buildDimension(bag); for (var i = 0; i < this.pricelists[78].amounts.length; i++) { var amn = this.pricelists[78].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[78].terms.length; i++) { var day = this.pricelists[78].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[79].terms.length; i++) { var day = this.pricelists[79].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 } }, prolongPermissionRules: { 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: Prohibit prolonging If all products open (function () { // Group: Group if ((bag[3251] == 1 && (bag[3252] == 1 && bag['3253'] == 1))) { bag['prohibitProlong']('Has all products open'); bag['prohibitProlongToInstallment']('Has all products open'); bag['exit'](); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; control.skip = false; // Stage: OLD VERSION OF PROLONGING=>HAS OPEN PD PROLONG TO CRL (function () { // Group: PD --> CRL FL BEFORE DUEDATE if (([1,3,4].indexOf(parseInt(bag[460])) !== -1 && ((((bag[802] < 31 && bag[2788] >= 50) && [1].indexOf(bag[2438] ? 1 : 0) !== -1) && bag[3251] == 1) && bag[3252] == 0))) { bag['prohibitProlong'](null); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: PD --> CRL FL AFTER DUEDATE if (([1,3,4].indexOf(parseInt(bag[460])) !== -1 && (((((bag[802] > 0 && bag[802] < 31) && bag[2788] >= 50) && [1].indexOf(bag[2438] ? 1 : 0) !== -1) && bag[3251] == 1) && bag[3252] == 0))) { bag['prohibitProlong'](null); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: PD --> CRL CL BEFORE DUEDATE if (([1,3,4].indexOf(parseInt(bag[460])) !== -1 && ((((bag[802] < 31 && bag[3252] == 0) && bag[2788] >= 50) && [0].indexOf(bag[2438] ? 1 : 0) !== -1) && bag[3251] == 1))) { bag['prohibitProlong'](null); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; control.skip = false; // Stage: NEW VERSION OF PROLONGING to IL (function () { // Group: PDL if ((bag['3141'] == 1 && ([1,3,4].indexOf(parseInt(bag['460'])) !== -1 && (bag['2788'] >= 50 && bag[802] < 31)))) { bag['prohibitProlongToInstallment'](null); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; control.skip = false; // Stage: NEW VERSION OF PROLONGING IL -> IL (function () { // Group: IL if ((bag['3141'] == 2 && ([1].indexOf(parseInt(bag[460])) !== -1 && (bag['2788'] >= 50 && bag[802] < 31)))) { bag['prohibitProlongToInstallment'](null); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; control.skip = false; // Stage: NEW VERSION OF PROLONGING - TO CRL -> IL (function () { // Group: CRL if ((bag['3141'] == 3 && ([17,1,4].indexOf(parseInt(bag[460])) !== -1 && (bag['2788'] >= 50 && bag['3353'] < 31)))) { bag['prohibitProlongToInstallment'](null); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; return control; } }, 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 = this.prolongPermissionRules.evaluate(ibag); return { 'loan-prolong-permitted': ibag._loanProlongPermitted, 'loan-prolong-permitted-to-payday': ibag._loanProlongPermittedToPayday, 'loan-prolong-permitted-to-installment': ibag._loanProlongPermittedToInstallment, 'loan-prolong-permitted-to-creditline': ibag._loanProlongPermittedToCreditline, 'loan-prolong-error': ibag._errorMessage, 'loan-prolong-to-payday-error': ibag._errorMessageProlongToPayday, 'loan-prolong-to-installment-error': ibag._errorMessageProlongToInstallment, 'loan-prolong-to-creditline-error': ibag._errorMessageProlongToCreditline, 'prolong-loan-setting-id': ibag._loanSettingId, 'loan-feetype-mapping': ibag._feetypes } }, 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: SET-3547 Allow ext for all (function () { // Group: Group if (bag['1217'] !== null) { bag['allowExtension'](); bag['exit'](); if (control.canceled) return; } else { bag['allowExtension'](); bag['exit'](); if (control.canceled) return; } if (control.canceled || control.skip) return; })(); if (control.canceled) return control; control.skip = false; // Stage: Stage (function () { // Group: Prohibit for bailiff status if ((([73,74,75,76,79,431,432,433,434,435,439,440,614,615,616].indexOf(parseInt(bag[2593])) !== -1 || [7].indexOf(parseInt(bag[460])) !== -1) || bag[802] > 60)) { bag['prohibitExtension']('Max delay reached'); bag['exit'](); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if (true) { vars['diff'] = (bag[423] - (bag[4190] + bag[-11])); if (control.canceled) return; } if (control.canceled || control.skip) return; // Group: Group if (vars['diff'] < 0) { bag['prohibitExtension']('Fee limit reached'); bag['exit'](); if (control.canceled) return; } else { bag['allowExtension'](); 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 != 79 ) { 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); 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); 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)); 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[79]; 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[79], 'loan-extension-duedate': calc_duedate, 'loan-extension-permitted': ibag._loanExtensionPermitted, 'loan-extension-error': ibag._errorMessage, 'loan-original-extension-fees': values.prices[79], 'loan-free-extension': values.total_extension_term, details: details2, }; }, 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); 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]; if (!values.feeRates[83]) { values.feeRates[83] = 0; } if (!values.feeRates[77]) { values.feeRates[77] = 0; } var daysPerYear = 366; var interestRate = values.feeRates[83]; var loanFeeRate = values.feeRates[77]; var interest = calculateInterestFee(amount, interestRate, daysPerYear, term); var loanFee = calculateLoanFee(amount, loanFeeRate, daysPerYear, term); values.prices[83] = interest; values.prices[77] = loanFee; values.feeRates['interest-fee-rate'] = interestRate; values.feeRates['loan-fee-rate'] = loanFeeRate; if (0.000 > 0) { values.prices_without_tax[83] = (values.prices[83] / (1 + parseFloat(0.000))).toFixed(2); } if (0.000 > 0) { values.prices_without_tax[77] = (values.prices[77] / (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 == 83) { name = 'interest-fee'; } else if (id == 77) { 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 == 83) { values.discounted_without_tax[id] = parseFloat((values.discounted[id] / (1 + parseFloat(0.000))).toFixed(2)); } else if (id == 77) { 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[78] + values.discounted[77]; if (values.discounted[83]) { loan_total += values.discounted[83]; } var loan_total_without_tax = (values.discounted_without_tax[78] || values.discounted[78]) + (values.discounted_without_tax[77] || values.discounted[77]); if (values.discounted_without_tax[83] || values.discounted[83]) { loan_total_without_tax += (values.discounted_without_tax[83] || values.discounted[83]); } 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 == 83) { name = 'interest-fee'; } else if (id == 77) { 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[78], 'loan-total': loan_total, 'loan-free-extension': values.total_extension_term, 'annual-loan-fee-rate': (values.feeRates[77]/ 100).toFixed(10), details: details2, }; var total_fee_cost_values = 0; if (values.prices['77']) { ret['loan-fees'] = values.discounted[77]; ret['loan-original-fees'] = values.prices[77]; total_fee_cost_values += values.discounted[77]; } if (values.prices['83']) { ret['loan-interest-fees'] = values.discounted['83']; ret['loan-original-interest-fees'] = values.prices['83']; ret['loan-interest-fee-type-id'] = 83; total_fee_cost_values += values.discounted[83]; } ret['loan-total-fee-cost'] = total_fee_cost_values; if (values.customer_data) { ret['customer-data'] = values.customer_data; } if (values.feeRates) { ret['fee-rates'] = values.feeRates; } var loanFee = 77; var interestFee = 83; var preparationFee = null; var principalFee = 78; 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: { 423: 'Amount', 460: 'Status', 799: 'Loan count', 802: 'Delay', 1217: 'Id', 2438: 'Is First Loan', 2538: 'Verification Mode', 2593: 'Dc Category', 2788: 'Open principal', 2789: 'Original loan fees', 3141: 'Product', 3251: 'Has Open Payday Loan', 3252: 'Has Open Creditline', 3253: 'Has Open Installment Loan', 3256: 'Next installment remaining debt', 3353: 'Installment Delay', 3626: 'Extension Count', 4054: 'Cost loan fees', 4102: 'Late installment remaining debt (w/o LF)', 4190: 'Fee sum without principal', 5266: 'is active early settlement', 6054: 'Last Instantor Succesful Verification', 7560: 'is_force_settled', }, 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: [], 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; }, }, }, }; })(); ;