30 lines
3.0 KiB
JavaScript
30 lines
3.0 KiB
JavaScript
// The calculation from the original single-file app ("CALCULATOR CASHBACK SERUBI -
|
|
// UPDATE BUNGA NETO.html"), copied verbatim and wrapped to return the figures it
|
|
// displayed. Used only by `node tests/validate.js --legacy` for the comparison report.
|
|
'use strict';
|
|
const P={gwm:.09,kas:.02,puab:.051,kredit:.09,ohc:.0442,lps:.002,pajak:.20};
|
|
const depo={1:[[1e8,.03],[2.5e8,.0305],[5e8,.031],[1e9,.0325],[Infinity,.034]],3:[[1e8,.031],[2.5e8,.0315],[5e8,.032],[1e9,.0335],[Infinity,.035]],6:[[1e8,.032],[2.5e8,.0325],[5e8,.033],[1e9,.0345],[Infinity,.035]],12:[[1e8,.033],[2.5e8,.0335],[5e8,.034],[1e9,.035],[Infinity,.035]],24:[[1e8,.034],[2.5e8,.0345],[5e8,.035],[1e9,.035],[Infinity,.035]]};
|
|
function productRate(product,n){if(product==='simanja'){if(n<=1e5)return 0;if(n<=1e8)return .0075;if(n<=5e8)return .01;if(n<=1e9)return .02;return .025}if(product==='simpeda'){if(n<=1e5)return 0;if(n<=1e8)return .005;if(n<=5e8)return .0075;if(n<=1e9)return .015;return .02}if(product==='giro'){if(n<=5e7)return 0;if(n<=5e8)return .005;if(n<=1e9)return .005;if(n<=5e10)return .005;return .0075}}
|
|
function depRate(n,t){let a=depo[t]||[];if(n<1e6)return 0;for(const [lim,r] of a)if(n<=lim)return r;return 0}
|
|
function cashbackShare(n,t){if(![3,6,12,24].includes(t))return 0;if(n>=2e9)return .30;if(n>=250000001)return .45;if(n>=5e7)return .50;return 0}
|
|
|
|
module.exports = function legacy(n, t) {
|
|
const managed=n*(1-P.gwm-P.kas); const puabIncome=(managed*.10*P.puab)*(30/365)*t; const creditIncome=(managed*.90*P.kredit)*(t/12); const income=puabIncome+creditIncome;
|
|
function cost(rate){const bunga=Math.floor(((n*rate*30)/365)*t);const ohc=puabIncome*P.ohc;const lps=Math.min(n,2e9)*P.lps;return {bunga,cost:bunga+ohc+lps,spread:income-(bunga+ohc+lps)}}
|
|
const rates={giro:productRate('giro',n),simanja:productRate('simanja',n),simpeda:productRate('simpeda',n)};const avgSpread=(cost(rates.giro).spread+cost(rates.simanja).spread+cost(rates.simpeda).spread)/3;const share=cashbackShare(n,t);const gross=avgSpread*share;const net=gross*(1-P.pajak);const eq=(t>0?net/n*(365/(30*t)):0);
|
|
const ok = share > 0; // Excel shows blank/#VALUE! where the old app showed Rp0
|
|
const bn = k => cost(rates[k]).bunga*(1-P.pajak);
|
|
const dr=depRate(n,t),dg=Math.floor(((n*dr*30)/365)*t),dn=dg*(1-P.pajak);
|
|
return {
|
|
cashbackGross: ok ? gross : null, cashbackNet: ok ? net : null,
|
|
giroNet: bn('giro'), simanjaNet: bn('simanja'), simpedaNet: bn('simpeda'),
|
|
depositoNet: dg, // shown in the "Bunga Neto" column of the DEPOSITO row
|
|
totalDeposito: dn,
|
|
totalGiro: ok ? bn('giro') + net : null, totalSimanja: ok ? bn('simanja') + net : null, totalSimpeda: ok ? bn('simpeda') + net : null,
|
|
rateGiro: rates.giro, rateSimanja: rates.simanja, rateSimpeda: rates.simpeda, rateDeposito: dr,
|
|
equiv: ok ? eq : null,
|
|
totalRateGiro: ok ? rates.giro + eq : null, totalRateSimanja: ok ? rates.simanja + eq : null, totalRateSimpeda: ok ? rates.simpeda + eq : null,
|
|
penaltyRefund: null, penaltyTax: null, penaltyFee: null, penaltyTotal: null // not implemented in the old app
|
|
};
|
|
};
|