-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathFinancialPlanningGenerator_v2.html
More file actions
112 lines (112 loc) · 18.6 KB
/
FinancialPlanningGenerator_v2.html
File metadata and controls
112 lines (112 loc) · 18.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Financial Planning Generator</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: Arial, sans-serif; min-height: 100vh; background: #f5f5f5; color: #333; }
.app-header { background: #5e4965; color: white; padding: 16px 24px; display: flex; align-items: center; justify-content: space-between; }
.app-header h1 { font-size: 20px; font-weight: 600; }
.app-header .version { font-size: 12px; opacity: 0.8; }
.main-container { display: grid; grid-template-columns: 420px 1fr; gap: 0; height: calc(100vh - 56px); }
.left-panel { background: white; border-right: 1px solid #ddd; overflow-y: auto; padding: 20px; }
.section { margin-bottom: 24px; border: 1px solid #e0e0e0; border-radius: 4px; overflow: hidden; }
.section-header { background: #f8f8f8; padding: 10px 14px; font-weight: 600; font-size: 13px; color: #5e4965; border-bottom: 1px solid #e0e0e0; text-transform: uppercase; letter-spacing: 0.5px; }
.section-body { padding: 16px; }
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 12px; }
.form-group { display: flex; flex-direction: column; }
.form-group label { font-size: 12px; font-weight: 600; color: #555; margin-bottom: 4px; }
.form-group input, .form-group select, .form-group textarea { padding: 8px 10px; border: 1px solid #ccc; border-radius: 3px; font-size: 13px; font-family: Arial, sans-serif; background: white; }
.form-group input:focus, .form-group select:focus, .form-group textarea:focus { outline: none; border-color: #5e4965; box-shadow: 0 0 0 2px rgba(94,73,101,0.1); }
.form-group textarea { min-height: 100px; font-family: Consolas, monospace; font-size: 12px; resize: vertical; }
.form-group .help-text { font-size: 11px; color: #888; margin-top: 3px; }
.wbs-gl-container { max-height: 200px; overflow-y: auto; border: 1px solid #e0e0e0; border-radius: 3px; }
.wbs-gl-row { display: grid; grid-template-columns: 1fr 140px; gap: 8px; padding: 6px 8px; border-bottom: 1px solid #f0f0f0; align-items: center; font-size: 12px; }
.wbs-gl-row.header { background: #f8f8f8; font-weight: 600; color: #555; position: sticky; top: 0; }
.wbs-gl-row .wbs-code { font-family: Consolas, monospace; color: #333; }
.btn-group { display: flex; gap: 10px; margin-top: 16px; }
.btn { padding: 10px 20px; border: none; border-radius: 3px; font-size: 13px; font-weight: 600; font-family: Arial, sans-serif; cursor: pointer; transition: background 0.2s; }
.btn-primary { background: #5e4965; color: white; }
.btn-primary:hover { background: #4a3a51; }
.btn-success { background: #90d303; color: #333; }
.btn-success:hover { background: #7fb800; }
.btn-secondary { background: #e0e0e0; color: #555; }
.btn-secondary:hover { background: #d0d0d0; }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
.instructions { background: #fafafa; border: 1px solid #e0e0e0; border-radius: 4px; padding: 14px; margin-bottom: 20px; font-size: 12px; line-height: 1.6; }
.instructions h3 { font-size: 13px; color: #5e4965; margin-bottom: 8px; }
.instructions ol { margin-left: 18px; color: #555; }
.instructions li { margin: 3px 0; }
.status { margin-top: 14px; padding: 10px 12px; border-radius: 3px; font-size: 12px; }
.status.success { background: #e8f5e9; color: #2e7d32; border: 1px solid #a5d6a7; }
.status.error { background: #ffebee; color: #c62828; border: 1px solid #ef9a9a; }
.status.info { background: #e3f2fd; color: #1565c0; border: 1px solid #90caf9; }
.right-panel { display: flex; flex-direction: column; background: #fafafa; overflow: hidden; }
.right-panel-header { background: white; padding: 14px 20px; border-bottom: 1px solid #ddd; display: flex; justify-content: space-between; align-items: center; }
.right-panel-header h2 { font-size: 15px; color: #333; font-weight: 600; }
.summary-panel { background: white; border-bottom: 1px solid #ddd; padding: 14px 20px; }
.summary-title { font-size: 13px; font-weight: 600; color: #5e4965; margin-bottom: 10px; }
.summary-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px; }
.summary-card { background: #f8f8f8; border: 1px solid #e0e0e0; border-radius: 4px; padding: 10px 12px; }
.summary-card .label { font-size: 11px; color: #777; margin-bottom: 2px; }
.summary-card .value { font-size: 16px; font-weight: 600; color: #333; font-family: Consolas, monospace; }
.summary-card.total { background: #5e4965; border-color: #5e4965; }
.summary-card.total .label { color: rgba(255,255,255,0.7); }
.summary-card.total .value { color: white; }
.subtotals-container { background: white; border-bottom: 1px solid #ddd; max-height: 200px; overflow-y: auto; }
.subtotals-table { width: 100%; border-collapse: collapse; font-size: 12px; }
.subtotals-table th { background: #f0f0f0; padding: 8px 12px; text-align: left; font-weight: 600; color: #555; position: sticky; top: 0; border-bottom: 1px solid #ddd; }
.subtotals-table td { padding: 6px 12px; border-bottom: 1px solid #f0f0f0; }
.subtotals-table .wbs-col { font-family: Consolas, monospace; }
.subtotals-table .amount-col { text-align: right; font-family: Consolas, monospace; font-weight: 600; }
.table-wrapper { flex: 1; overflow: auto; padding: 0; }
.data-table { width: 100%; border-collapse: collapse; font-size: 12px; background: white; }
.data-table th { background: #5e4965; color: white; padding: 10px 8px; text-align: left; font-weight: 600; font-size: 11px; position: sticky; top: 0; z-index: 10; white-space: nowrap; }
.data-table td { padding: 8px; border-bottom: 1px solid #eee; font-family: Consolas, monospace; font-size: 11px; }
.data-table tr:nth-child(even) { background: #fafafa; }
.data-table tr:hover { background: #f5f0f7; }
.data-table .amount-input { width: 100px; padding: 4px 6px; border: 1px solid #ddd; border-radius: 2px; text-align: right; font-family: Consolas, monospace; font-size: 11px; }
.data-table .amount-input:focus { border-color: #5e4965; outline: none; }
.data-table .gl-name { font-family: Arial, sans-serif; font-size: 10px; color: #666; }
.row-count { background: white; padding: 10px 20px; border-top: 1px solid #ddd; font-size: 12px; color: #666; text-align: right; }
.empty-state { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 300px; color: #999; font-size: 14px; }
</style>
</head>
<body>
<header class="app-header"><h1>Financial Planning Generator</h1><span class="version">v2.0</span></header>
<div class="main-container">
<div class="left-panel">
<div class="instructions"><h3>How to Use</h3><ol><li>Enter planning details (start year, end year, version, company code, currency).</li><li>Paste WBS codes (one per line).</li><li>Assign G/L Account to each WBS element.</li><li>Select planning type (Yearly or Monthly).</li><li>Click <strong>Generate Planning Table</strong> to create rows.</li><li>Enter amounts in the table.</li><li>Click <strong>Export to CSV</strong> to download your file.</li></ol></div>
<div class="section"><div class="section-header">General Settings</div><div class="section-body"><div class="form-row"><div class="form-group"><label for="startYear">Planning Start Year</label><input type="number" id="startYear" placeholder="2025" min="2020" max="2050"></div><div class="form-group"><label for="finishYear">Planning End Year</label><input type="number" id="finishYear" placeholder="2027" min="2020" max="2050"></div></div><div class="form-row"><div class="form-group"><label for="planVersion">Plan Version</label><select id="planVersion"><option value="Forecast">Forecast</option><option value="Budget">Budget Breakdown</option></select></div><div class="form-group"><label for="planningType">Planning Type</label><select id="planningType"><option value="Yearly">Yearly</option><option value="Monthly">Monthly</option></select><span class="help-text">Yearly = 1 row/year (December), Monthly = 12 rows/year</span></div></div></div></div>
<div class="section"><div class="section-header">Company Info</div><div class="section-body"><div class="form-row"><div class="form-group"><label for="companyCode">Company Code</label><input type="text" id="companyCode" placeholder="12345"></div><div class="form-group"><label for="currency">Currency</label><select id="currency"><option value="EUR">EUR</option><option value="USD">USD</option><option value="PLN">PLN</option><option value="GBP">GBP</option><option value="CHF">CHF</option></select></div></div></div></div>
<div class="section"><div class="section-header">WBS Elements</div><div class="section-body"><div class="form-group"><label for="wbsElements">WBS Codes (one per line)</label><textarea id="wbsElements" placeholder="ABCDE-123456-A12 ABCDE-123456-A13 ABCDE-123456-A14"></textarea></div><button class="btn btn-secondary" onclick="parseWbsElements()" style="margin-top:8px;">Assign G/L Accounts</button><div id="wbsGlAssignment" style="margin-top:12px;display:none;"><label style="font-size:12px;font-weight:600;color:#555;margin-bottom:6px;display:block;">G/L Account Assignment (1 G/L per WBS)</label><div class="wbs-gl-container"><div class="wbs-gl-row header"><span>WBS Element</span><span>G/L Account</span></div><div id="wbsGlRows"></div></div></div></div></div>
<div class="btn-group"><button class="btn btn-primary" onclick="generateRows()">Generate Planning Table</button><button class="btn btn-secondary" onclick="resetForm()">Reset Form</button></div>
<div id="status" class="status" style="display:none;"></div>
</div>
<div class="right-panel">
<div class="right-panel-header"><h2>Data Preview</h2><button class="btn btn-success" onclick="downloadCSV()" id="btnDownload" disabled>Export to CSV</button></div>
<div class="summary-panel" id="summaryPanel" style="display:none;"><div class="summary-title">Summary</div><div class="summary-grid" id="summaryGrid"></div></div>
<div class="subtotals-container" id="subtotalsContainer" style="display:none;"><table class="subtotals-table"><thead><tr><th>WBS Element</th><th>Project ID</th><th>G/L Account</th><th style="text-align:right;">Subtotal</th></tr></thead><tbody id="subtotalsBody"></tbody></table></div>
<div class="table-wrapper"><table class="data-table" id="dataTable"><thead><tr><th>Plan Category</th><th>Fiscal Year</th><th>Period</th><th>Company Code</th><th>Project ID</th><th>WBS Element</th><th>G/L Account</th><th>Account Name</th><th id="amtHeader">Amount</th><th id="curHeader">Currency</th></tr></thead><tbody id="tableBody"><tr><td colspan="10"><div class="empty-state">Click "Generate Planning Table" to preview data</div></td></tr></tbody></table></div>
<div class="row-count" id="rowCount">Rows: 0</div>
</div>
</div>
<script>
const glAccountNames={'4655001001':'External Services','4671001000':'Travel Expenses','4711033000':'Other Operating Costs'};
let generatedData=[],wbsGlMapping={};
function showStatus(m,t){const s=document.getElementById('status');s.textContent=m;s.className='status '+t;s.style.display='block';}
function hideStatus(){document.getElementById('status').style.display='none';}
function getWbsList(){return document.getElementById('wbsElements').value.split('\n').map(w=>w.trim()).filter(w=>w.length>0);}
function parseWbsElements(){const wbsList=getWbsList();if(wbsList.length===0){showStatus('Please enter at least one WBS element.','error');return;}const rowsDiv=document.getElementById('wbsGlRows');rowsDiv.innerHTML=wbsList.map((wbs,i)=>`<div class="wbs-gl-row"><span class="wbs-code">${wbs}</span><select id="gl_${i}" onchange="wbsGlMapping['${wbs}']=this.value"><option value="4655001001">4655001001</option><option value="4671001000">4671001000</option><option value="4711033000">4711033000</option></select></div>`).join('');wbsList.forEach(wbs=>{if(!wbsGlMapping[wbs])wbsGlMapping[wbs]='4655001001';});document.getElementById('wbsGlAssignment').style.display='block';hideStatus();}
function validateInputs(){const sY=document.getElementById('startYear').value,fY=document.getElementById('finishYear').value,cc=document.getElementById('companyCode').value.trim(),wbs=getWbsList();if(!sY||!fY){showStatus('Please enter a valid year range (e.g., 2020-2035).','error');return false;}if(parseInt(sY)>parseInt(fY)){showStatus('Start Year cannot be greater than End Year.','error');return false;}if(!cc){showStatus('Company Code is required.','error');return false;}if(wbs.length===0){showStatus('Please enter at least one WBS element.','error');return false;}return true;}
function generateRows(){hideStatus();if(!validateInputs())return;const wbsList=getWbsList();wbsList.forEach(wbs=>{if(!wbsGlMapping[wbs])wbsGlMapping[wbs]='4655001001';});const sY=parseInt(document.getElementById('startYear').value),fY=parseInt(document.getElementById('finishYear').value),pV=document.getElementById('planVersion').value,pT=document.getElementById('planningType').value,cc=document.getElementById('companyCode').value.trim(),cur=document.getElementById('currency').value,cat=pV==='Forecast'?'PROJ_FRCST':'PROJ_BDGT2',monthly=pT==='Monthly';generatedData=[];for(const wbs of wbsList){const pid=wbs.length>=12?wbs.substring(0,12):wbs,gl=wbsGlMapping[wbs]||'4655001001';for(let y=sY;y<=fY;y++){if(monthly){for(let m=1;m<=12;m++)generatedData.push({category:cat,year:y,period:m,companyCode:cc,projectId:pid,wbsElement:wbs,glAccount:gl,glName:glAccountNames[gl]||'',amount:0,currency:cur});}else{generatedData.push({category:cat,year:y,period:12,companyCode:cc,projectId:pid,wbsElement:wbs,glAccount:gl,glName:glAccountNames[gl]||'',amount:0,currency:cur});}}}renderTable();updateSummary();document.getElementById('rowCount').textContent=`Rows: ${generatedData.length}`;document.getElementById('btnDownload').disabled=false;showStatus(`${generatedData.length} rows generated successfully.`,'success');}
function renderTable(){const tbody=document.getElementById('tableBody');if(generatedData.length===0){tbody.innerHTML='<tr><td colspan="10"><div class="empty-state">Click "Generate Planning Table" to preview data</div></td></tr>';return;}tbody.innerHTML=generatedData.map((r,i)=>`<tr><td>${r.category}</td><td>${r.year}</td><td>${r.period}</td><td>${r.companyCode}</td><td>${r.projectId}</td><td>${r.wbsElement}</td><td>${r.glAccount}</td><td class="gl-name">${r.glName}</td><td><input type="number" class="amount-input" value="${r.amount}" data-index="${i}" onchange="updateAmount(${i},this.value)" step="0.01"></td><td>${r.currency}</td></tr>`).join('');}
function updateAmount(i,v){generatedData[i].amount=parseFloat(v)||0;updateSummary();}
function updateSummary(){if(generatedData.length===0){document.getElementById('summaryPanel').style.display='none';document.getElementById('subtotalsContainer').style.display='none';return;}document.querySelectorAll('.amount-input').forEach(inp=>{generatedData[parseInt(inp.dataset.index)].amount=parseFloat(inp.value)||0;});const cur=generatedData[0]?.currency||'EUR',total=generatedData.reduce((s,r)=>s+r.amount,0),wbsSub={};generatedData.forEach(r=>{if(!wbsSub[r.wbsElement])wbsSub[r.wbsElement]={projectId:r.projectId,glAccount:r.glAccount,total:0};wbsSub[r.wbsElement].total+=r.amount;});document.getElementById('summaryGrid').innerHTML=`<div class="summary-card total"><div class="label">Grand Total</div><div class="value">${total.toLocaleString('en-US',{minimumFractionDigits:2})} ${cur}</div></div><div class="summary-card"><div class="label">Total Rows</div><div class="value">${generatedData.length}</div></div><div class="summary-card"><div class="label">WBS Elements</div><div class="value">${Object.keys(wbsSub).length}</div></div>`;document.getElementById('summaryPanel').style.display='block';document.getElementById('subtotalsBody').innerHTML=Object.entries(wbsSub).map(([wbs,d])=>`<tr><td class="wbs-col">${wbs}</td><td class="wbs-col">${d.projectId}</td><td>${d.glAccount}</td><td class="amount-col">${d.total.toLocaleString('en-US',{minimumFractionDigits:2})} ${cur}</td></tr>`).join('');document.getElementById('subtotalsContainer').style.display='block';}
function downloadCSV(){if(generatedData.length===0){showStatus('Please generate the planning table first.','error');return;}document.querySelectorAll('.amount-input').forEach(inp=>{generatedData[parseInt(inp.dataset.index)].amount=parseFloat(inp.value)||0;});const cur=generatedData[0]?.currency||'EUR',amtCol=cur==='EUR'?'KSL':'HSL',curCol=cur==='EUR'?'RKCUR':'RHCUR';let csv=`CATEGORY;RYEAR;POPER;RBUKRS;PS_PSPID;PS_POSID;RACCT;${amtCol};${curCol}\nPlan Category;General Ledger Fiscal Year;Posting Period;Company Code;Project definition;Work Breakdown Structure Element (WBS Element);Account Number;Amount in Local Currency;Local Currency\nX;X;X;X;X;;X;X;X\n`;for(const r of generatedData)csv+=`${r.category};${r.year};${r.period};${r.companyCode};${r.projectId};${r.wbsElement};${r.glAccount};${r.amount};${r.currency}\n`;const blob=new Blob(['\ufeff'+csv],{type:'text/csv;charset=utf-8;'}),url=URL.createObjectURL(blob),link=document.createElement('a');link.href=url;const now=new Date(),ds=now.getFullYear().toString()+String(now.getMonth()+1).padStart(2,'0')+String(now.getDate()).padStart(2,'0'),ts=String(now.getHours()).padStart(2,'0')+String(now.getMinutes()).padStart(2,'0');link.download=`Financial_Planning_${ds}_${ts}.csv`;document.body.appendChild(link);link.click();document.body.removeChild(link);URL.revokeObjectURL(url);const total=generatedData.reduce((s,r)=>s+r.amount,0);showStatus(`CSV exported successfully. Total: ${total.toLocaleString('en-US',{minimumFractionDigits:2})} ${cur}`,'success');}
function resetForm(){['startYear','finishYear','companyCode','wbsElements'].forEach(id=>document.getElementById(id).value='');['planVersion','planningType','currency'].forEach(id=>document.getElementById(id).selectedIndex=0);document.getElementById('wbsGlAssignment').style.display='none';document.getElementById('wbsGlRows').innerHTML='';generatedData=[];wbsGlMapping={};renderTable();document.getElementById('summaryPanel').style.display='none';document.getElementById('subtotalsContainer').style.display='none';document.getElementById('rowCount').textContent='Rows: 0';document.getElementById('btnDownload').disabled=true;showStatus('Form has been reset.','info');setTimeout(hideStatus,2000);}
</script>
</body>
</html>