multi-currency support in roi calculator
This commit is contained in:
parent
adc3a6b905
commit
5cc6b779c5
7 changed files with 231 additions and 45 deletions
|
|
@ -131,7 +131,7 @@ class ExportManager {
|
|||
|
||||
doc.setFontSize(12);
|
||||
doc.setTextColor(...colors.dark);
|
||||
doc.text(`Investment Amount: ${this.formatCHF(inputs.investmentAmount)}`, pageWidth/2, boxY + 22, { align: 'center' });
|
||||
doc.text(`Investment Amount: ${this.formatCurrency(inputs.investmentAmount)}`, pageWidth/2, boxY + 22, { align: 'center' });
|
||||
doc.text(`Analysis Period: ${inputs.timeframe} years`, pageWidth/2, boxY + 32, { align: 'center' });
|
||||
|
||||
// Generated date
|
||||
|
|
@ -191,7 +191,7 @@ class ExportManager {
|
|||
doc.setFont('helvetica', 'normal');
|
||||
doc.setFontSize(10);
|
||||
doc.text(`Average ROI: ${this.uiManager.formatPercentage(avgDirectROI)}`, margin + 5, yPos + 16);
|
||||
doc.text(`Average Net Profit: ${this.formatCHF(avgDirectNetPos)}`, margin + 5, yPos + 24);
|
||||
doc.text(`Average Net Profit: ${this.formatCurrency(avgDirectNetPos)}`, margin + 5, yPos + 24);
|
||||
doc.text('Performance-based with bonuses', margin + 5, yPos + 32);
|
||||
|
||||
// Loan Model Summary
|
||||
|
|
@ -208,7 +208,7 @@ class ExportManager {
|
|||
doc.setFont('helvetica', 'normal');
|
||||
doc.setFontSize(10);
|
||||
doc.text(`Average ROI: ${this.uiManager.formatPercentage(avgLoanROI)}`, loanBoxX + 5, yPos + 16);
|
||||
doc.text(`Average Net Profit: ${this.formatCHF(avgLoanNetPos)}`, loanBoxX + 5, yPos + 24);
|
||||
doc.text(`Average Net Profit: ${this.formatCurrency(avgLoanNetPos)}`, loanBoxX + 5, yPos + 24);
|
||||
doc.text('Fixed returns, guaranteed', loanBoxX + 5, yPos + 32);
|
||||
|
||||
yPos += 45;
|
||||
|
|
@ -228,11 +228,11 @@ class ExportManager {
|
|||
|
||||
// Create parameter table
|
||||
const params = [
|
||||
['Investment Amount', this.formatCHF(inputs.investmentAmount)],
|
||||
['Investment Amount', this.formatCurrency(inputs.investmentAmount)],
|
||||
['Investment Timeframe', `${inputs.timeframe} years`],
|
||||
['Service Revenue per Instance', `${this.formatCHF(inputs.revenuePerInstance)} / month`],
|
||||
['Core Service Revenue per Instance', `${this.formatCHF(inputs.coreServiceRevenue)} / month`],
|
||||
['Total Revenue per Instance', `${this.formatCHF(inputs.revenuePerInstance + inputs.coreServiceRevenue)} / month`],
|
||||
['Service Revenue per Instance', `${this.formatCurrency(inputs.revenuePerInstance)} / month`],
|
||||
['Core Service Revenue per Instance', `${this.formatCurrency(inputs.coreServiceRevenue)} / month`],
|
||||
['Total Revenue per Instance', `${this.formatCurrency(inputs.revenuePerInstance + inputs.coreServiceRevenue)} / month`],
|
||||
['Loan Interest Rate', `${(inputs.loanInterestRate * 100).toFixed(1)}%`],
|
||||
['Direct Investment Share', `${(inputs.servalaShare * 100).toFixed(0)}% to Servala`],
|
||||
['Grace Period', `${inputs.gracePeriod} months`]
|
||||
|
|
@ -271,9 +271,9 @@ class ExportManager {
|
|||
if (directResult && loanResult) {
|
||||
tableData.push([
|
||||
scenarioName,
|
||||
this.formatCHF(directResult.netPosition),
|
||||
this.formatCurrency(directResult.netPosition),
|
||||
this.uiManager.formatPercentage(directResult.roi),
|
||||
this.formatCHF(loanResult.netPosition),
|
||||
this.formatCurrency(loanResult.netPosition),
|
||||
this.uiManager.formatPercentage(loanResult.roi)
|
||||
]);
|
||||
}
|
||||
|
|
@ -339,7 +339,7 @@ class ExportManager {
|
|||
tableData.push([
|
||||
result.scenario,
|
||||
result.investmentModel === 'direct' ? 'Direct' : 'Loan',
|
||||
this.formatCHF(result.netPosition),
|
||||
this.formatCurrency(result.netPosition),
|
||||
this.uiManager.formatPercentage(result.roi),
|
||||
result.breakEvenMonth ? `${result.breakEvenMonth} months` : 'N/A'
|
||||
]);
|
||||
|
|
@ -498,17 +498,24 @@ class ExportManager {
|
|||
});
|
||||
}
|
||||
|
||||
formatCHF(amount) {
|
||||
formatCurrency(amount) {
|
||||
try {
|
||||
// Consistent CHF formatting: CHF in front, no decimals for whole numbers
|
||||
return new Intl.NumberFormat('de-CH', {
|
||||
// Get current currency from the page
|
||||
const currencyElement = document.getElementById('currency');
|
||||
const currency = currencyElement ? currencyElement.value : 'CHF';
|
||||
|
||||
// Determine locale based on currency
|
||||
const locale = currency === 'EUR' ? 'de-DE' : 'de-CH';
|
||||
|
||||
// Consistent currency formatting: currency in front, no decimals for whole numbers
|
||||
return new Intl.NumberFormat(locale, {
|
||||
style: 'currency',
|
||||
currency: 'CHF',
|
||||
currency: currency,
|
||||
minimumFractionDigits: 0,
|
||||
maximumFractionDigits: 0
|
||||
}).format(amount);
|
||||
} catch (error) {
|
||||
console.error('Error formatting CHF:', error);
|
||||
console.error('Error formatting currency:', error);
|
||||
return `CHF ${Math.round(amount).toLocaleString()}`;
|
||||
}
|
||||
}
|
||||
|
|
@ -522,15 +529,16 @@ class ExportManager {
|
|||
// Add input parameters
|
||||
csvContent += 'INPUT PARAMETERS\n';
|
||||
const inputs = this.calculator.getInputValues();
|
||||
csvContent += `Currency,${inputs.currency}\n`;
|
||||
csvContent += `Investment Amount,${inputs.investmentAmount}\n`;
|
||||
csvContent += `Timeframe (years),${inputs.timeframe}\n`;
|
||||
csvContent += `Investment Model,${inputs.investmentModel === 'loan' ? 'Loan Model' : 'Direct Investment'}\n`;
|
||||
if (inputs.investmentModel === 'loan') {
|
||||
csvContent += `Loan Interest Rate (%),${(inputs.loanInterestRate * 100).toFixed(1)}\n`;
|
||||
}
|
||||
csvContent += `Service Revenue per Instance,${inputs.revenuePerInstance}\n`;
|
||||
csvContent += `Core Service Revenue per Instance,${inputs.coreServiceRevenue}\n`;
|
||||
csvContent += `Total Revenue per Instance,${inputs.revenuePerInstance + inputs.coreServiceRevenue}\n`;
|
||||
csvContent += `Service Revenue per Instance (${inputs.currency}),${inputs.revenuePerInstance}\n`;
|
||||
csvContent += `Core Service Revenue per Instance (${inputs.currency}),${inputs.coreServiceRevenue}\n`;
|
||||
csvContent += `Total Revenue per Instance (${inputs.currency}),${inputs.revenuePerInstance + inputs.coreServiceRevenue}\n`;
|
||||
if (inputs.investmentModel === 'direct') {
|
||||
csvContent += `Servala Share (%),${(inputs.servalaShare * 100).toFixed(0)}\n`;
|
||||
csvContent += `Grace Period (months),${inputs.gracePeriod}\n`;
|
||||
|
|
@ -539,7 +547,7 @@ class ExportManager {
|
|||
|
||||
// Add scenario summary
|
||||
csvContent += 'SCENARIO SUMMARY\n';
|
||||
csvContent += 'Scenario,Investment Model,Final Instances,Total Revenue,CSP Revenue,Servala Revenue,ROI (%),Break-even (months)\n';
|
||||
csvContent += `Scenario,Investment Model,Final Instances,Total Revenue (${inputs.currency}),CSP Revenue (${inputs.currency}),Servala Revenue (${inputs.currency}),ROI (%),Break-even (months)\n`;
|
||||
|
||||
Object.values(this.calculator.results).forEach(result => {
|
||||
const modelText = result.investmentModel === 'loan' ? 'Loan' : 'Direct';
|
||||
|
|
@ -550,7 +558,7 @@ class ExportManager {
|
|||
|
||||
// Add detailed monthly data
|
||||
csvContent += 'MONTHLY BREAKDOWN\n';
|
||||
csvContent += 'Month,Scenario,New Instances,Churned Instances,Total Instances,Monthly Revenue,CSP Revenue,Servala Revenue,Cumulative CSP Revenue,Cumulative Servala Revenue\n';
|
||||
csvContent += `Month,Scenario,New Instances,Churned Instances,Total Instances,Service Revenue (${inputs.currency}),Core Revenue (${inputs.currency}),Total Revenue (${inputs.currency}),CSP Revenue (${inputs.currency}),Servala Revenue (${inputs.currency}),Cumulative CSP Revenue (${inputs.currency}),Cumulative Servala Revenue (${inputs.currency})\n`;
|
||||
|
||||
// Combine all monthly data
|
||||
const allData = [];
|
||||
|
|
@ -563,7 +571,7 @@ class ExportManager {
|
|||
allData.sort((a, b) => a.month - b.month || a.scenario.localeCompare(b.scenario));
|
||||
|
||||
allData.forEach(data => {
|
||||
csvContent += `${data.month},${data.scenario},${data.newInstances},${data.churnedInstances},${data.totalInstances},${data.monthlyRevenue.toFixed(2)},${data.cspRevenue.toFixed(2)},${data.servalaRevenue.toFixed(2)},${data.cumulativeCSPRevenue.toFixed(2)},${data.cumulativeServalaRevenue.toFixed(2)}\n`;
|
||||
csvContent += `${data.month},${data.scenario},${data.newInstances},${data.churnedInstances},${data.totalInstances},${(data.serviceRevenue || data.monthlyRevenue || 0).toFixed(2)},${(data.coreRevenue || 0).toFixed(2)},${(data.totalRevenue || data.monthlyRevenue || 0).toFixed(2)},${data.cspRevenue.toFixed(2)},${data.servalaRevenue.toFixed(2)},${data.cumulativeCSPRevenue.toFixed(2)},${data.cumulativeServalaRevenue.toFixed(2)}\n`;
|
||||
});
|
||||
|
||||
// Create and download file
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue