mantenimento-app 2.1.9 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app.js +113 -5
- package/backend/calculate-model.js +16 -1
- package/frontend/public/app.js +113 -5
- package/frontend/public/index.html +17 -1
- package/frontend/public/styles.css +77 -0
- package/package.json +1 -1
package/app.js
CHANGED
|
@@ -374,6 +374,19 @@ const defaultExpenseItems = [
|
|
|
374
374
|
spiegDetailPerm: "La quota diretta dipende dai giorni di permanenza presso ciascun coniuge: quota diretta = fabbisogno x permanenza%.",
|
|
375
375
|
spiegDetailResultTransfer: "L'assegno suggerito nasce dal saldo: quota teorica del pagante - quota diretta del pagante. Se il saldo e positivo, viene trasferito all'altro coniuge.",
|
|
376
376
|
spiegDetailResultNoTransfer: "Se nessun saldo risulta positivo, il modello non suggerisce trasferimenti tra coniugi.",
|
|
377
|
+
sepCostBoxTitle: "💸 Costo della separazione",
|
|
378
|
+
sepCostBoxNote: "Stima le spese mensili che la coppia avrebbe sostenuto vivendo insieme: il modello calcolerà il costo di duplicazione generato dalla separazione e la perdita economica netta per ciascun coniuge.",
|
|
379
|
+
lblSpeseConvivenza: "Spese mensili stimate in convivenza ({currency})",
|
|
380
|
+
hintSpeseConvivenza: "Stima delle spese mensili totali della coppia se non si fosse separata: affitto/mutuo unico, una sola utenza, una sola auto, ecc. Lascia 0 per non includere questa analisi.",
|
|
381
|
+
sepCostDuplication: "Costo duplicazione mensile",
|
|
382
|
+
sepCostPanelTitle: "💔 Effetto economico della separazione",
|
|
383
|
+
sepCostNetTogether: "Netto combinato se insieme",
|
|
384
|
+
sepCostNetSeparated: "Netto combinato dopo separazione",
|
|
385
|
+
sepCostLossMonthly: "Perdita economica mensile",
|
|
386
|
+
sepCostLossAnnually: "Perdita economica annua",
|
|
387
|
+
sepCostLossSpouse: "Impatto stimato su {spouse}",
|
|
388
|
+
sepCostInlineHint: "Duplicazione mensile stimata: {amount}",
|
|
389
|
+
sepCostWarning: "Inserisci le spese mensili in convivenza nel campo sopra per attivare questa analisi.",
|
|
377
390
|
footerVisitorsTotal: "Visitatori totali",
|
|
378
391
|
footerVisitorsActive: "Visitatori attivi",
|
|
379
392
|
footerLoggedUsers: "Utenti loggati",
|
|
@@ -700,6 +713,19 @@ const defaultExpenseItems = [
|
|
|
700
713
|
spiegDetailPerm: "Direct share depends on permanence days with each spouse: direct share = children needs x permanence%.",
|
|
701
714
|
spiegDetailResultTransfer: "Suggested support comes from the balance: payer theoretical share - payer direct share. If the balance is positive, it is transferred to the other spouse.",
|
|
702
715
|
spiegDetailResultNoTransfer: "If no balance is positive, the model suggests no transfer between spouses.",
|
|
716
|
+
sepCostBoxTitle: "💸 Cost of separation",
|
|
717
|
+
sepCostBoxNote: "Estimate the monthly expenses the couple would have incurred if living together: the model will compute the duplication cost generated by the separation and the net economic loss per spouse.",
|
|
718
|
+
lblSpeseConvivenza: "Estimated monthly expenses when cohabiting ({currency})",
|
|
719
|
+
hintSpeseConvivenza: "Estimated total monthly expenses of the couple if they had not separated: single rent/mortgage, single utilities, single car, etc. Leave 0 to skip this analysis.",
|
|
720
|
+
sepCostDuplication: "Monthly duplication cost",
|
|
721
|
+
sepCostPanelTitle: "💔 Economic effect of separation",
|
|
722
|
+
sepCostNetTogether: "Combined net if together",
|
|
723
|
+
sepCostNetSeparated: "Combined net after separation",
|
|
724
|
+
sepCostLossMonthly: "Monthly economic loss",
|
|
725
|
+
sepCostLossAnnually: "Annual economic loss",
|
|
726
|
+
sepCostLossSpouse: "Estimated impact on {spouse}",
|
|
727
|
+
sepCostInlineHint: "Estimated monthly duplication: {amount}",
|
|
728
|
+
sepCostWarning: "Enter the cohabiting monthly expenses above to activate this analysis.",
|
|
703
729
|
footerVisitorsTotal: "Total visitors",
|
|
704
730
|
footerVisitorsActive: "Active visitors",
|
|
705
731
|
footerLoggedUsers: "Logged users",
|
|
@@ -1184,6 +1210,14 @@ const defaultExpenseItems = [
|
|
|
1184
1210
|
if (permLegendC2) permLegendC2.textContent = c2n();
|
|
1185
1211
|
if (extraBoxTitle) extraBoxTitle.textContent = tr("extraBoxTitle");
|
|
1186
1212
|
if (extraBoxNote) extraBoxNote.textContent = tr("extraBoxNote");
|
|
1213
|
+
const sepCostBoxTitleEl = document.getElementById("sepCostBoxTitle");
|
|
1214
|
+
const sepCostBoxNoteEl = document.getElementById("sepCostBoxNote");
|
|
1215
|
+
const lblSpeseConvivenzaEl = document.getElementById("lblSpeseConvivenza");
|
|
1216
|
+
const hintSpeseConvivenzaEl = document.getElementById("hintSpeseConvivenza");
|
|
1217
|
+
if (sepCostBoxTitleEl) sepCostBoxTitleEl.textContent = tr("sepCostBoxTitle");
|
|
1218
|
+
if (sepCostBoxNoteEl) sepCostBoxNoteEl.textContent = tr("sepCostBoxNote");
|
|
1219
|
+
if (lblSpeseConvivenzaEl) lblSpeseConvivenzaEl.textContent = msg("lblSpeseConvivenza", { currency: currentCurrency });
|
|
1220
|
+
if (hintSpeseConvivenzaEl) hintSpeseConvivenzaEl.title = tr("hintSpeseConvivenza");
|
|
1187
1221
|
if (firstHomeBoxTitle) firstHomeBoxTitle.textContent = tr("firstHomeBoxTitle");
|
|
1188
1222
|
if (firstHomeBoxNote) firstHomeBoxNote.textContent = tr("firstHomeBoxNote");
|
|
1189
1223
|
if (lblPrimaCasaMutuoEnabled) lblPrimaCasaMutuoEnabled.textContent = tr("firstHomeMortgageEnabledLabel");
|
|
@@ -1781,7 +1815,12 @@ const defaultExpenseItems = [
|
|
|
1781
1815
|
}
|
|
1782
1816
|
|
|
1783
1817
|
function isLoggedIn() { return !!authSession.username; }
|
|
1784
|
-
function
|
|
1818
|
+
function isDonationPolicyBypassedUser() {
|
|
1819
|
+
return normalizeUsername(authSession.username) === "favagit";
|
|
1820
|
+
}
|
|
1821
|
+
function isDonorUser() {
|
|
1822
|
+
return !!authSession.isDonor || isDonationPolicyBypassedUser();
|
|
1823
|
+
}
|
|
1785
1824
|
|
|
1786
1825
|
function getScenarioMaxForUser() {
|
|
1787
1826
|
if (!isLoggedIn()) return 0;
|
|
@@ -3263,7 +3302,8 @@ const defaultExpenseItems = [
|
|
|
3263
3302
|
straordAnn1: num("straordAnn1"),
|
|
3264
3303
|
straordAnn2: num("straordAnn2"),
|
|
3265
3304
|
c1SpeseDetails,
|
|
3266
|
-
|
|
3305
|
+
c2SpeseDetails,
|
|
3306
|
+
speseConvivenza: num("speseConvivenza"),
|
|
3267
3307
|
c1SpeseDetailUi,
|
|
3268
3308
|
c2SpeseDetailUi,
|
|
3269
3309
|
c1Spese,
|
|
@@ -3384,7 +3424,18 @@ const defaultExpenseItems = [
|
|
|
3384
3424
|
const post1 = disp1 - assegnoDa1a2 + assegnoDa2a1;
|
|
3385
3425
|
const post2 = disp2 - assegnoDa2a1 + assegnoDa1a2;
|
|
3386
3426
|
|
|
3387
|
-
|
|
3427
|
+
// Separation cost analysis (only active when speseConvivenza > 0)
|
|
3428
|
+
const speseConvivenza = Math.max(0, Number(payload.speseConvivenza || 0));
|
|
3429
|
+
const costoSeparazioneMensile = speseConvivenza > 0 ? speseTot - speseConvivenza : null;
|
|
3430
|
+
const nettoInsiemeCombinato = speseConvivenza > 0 ? (r1 + r2 - speseConvivenza) : null;
|
|
3431
|
+
const nettoSeparatoTotale = post1 + post2;
|
|
3432
|
+
const perditaMensile = nettoInsiemeCombinato !== null ? nettoInsiemeCombinato - nettoSeparatoTotale : null;
|
|
3433
|
+
const perditaAnnua = perditaMensile !== null ? perditaMensile * 12 : null;
|
|
3434
|
+
const totReddito = Math.max(0.001, r1 + r2);
|
|
3435
|
+
const perditaSpouse1 = perditaMensile !== null ? perditaMensile * (r1 / totReddito) : null;
|
|
3436
|
+
const perditaSpouse2 = perditaMensile !== null ? perditaMensile * (r2 / totReddito) : null;
|
|
3437
|
+
|
|
3438
|
+
return {
|
|
3388
3439
|
r1, r2, r1Raw, r2Raw, incomeMode, figli, perm1, perm2,
|
|
3389
3440
|
aPerc1, aPag1, aPerc2, aPag2, aFam1, aFam2,
|
|
3390
3441
|
match12, match21, esternoPag1, esternoPag2,
|
|
@@ -3402,7 +3453,11 @@ const defaultExpenseItems = [
|
|
|
3402
3453
|
primaCasaConsidered, primaCasaTransfer1to2, primaCasaTransfer2to1,
|
|
3403
3454
|
compensativeBenefits,
|
|
3404
3455
|
assegnoDa1a2, assegnoDa2a1,
|
|
3405
|
-
post1, post2
|
|
3456
|
+
post1, post2,
|
|
3457
|
+
speseConvivenza, costoSeparazioneMensile,
|
|
3458
|
+
nettoInsiemeCombinato, nettoSeparatoTotale,
|
|
3459
|
+
perditaMensile, perditaAnnua,
|
|
3460
|
+
perditaSpouse1, perditaSpouse2
|
|
3406
3461
|
};
|
|
3407
3462
|
}
|
|
3408
3463
|
|
|
@@ -4526,6 +4581,57 @@ const defaultExpenseItems = [
|
|
|
4526
4581
|
});
|
|
4527
4582
|
}
|
|
4528
4583
|
|
|
4584
|
+
function renderSeparationCostPanel(m) {
|
|
4585
|
+
const panel = document.getElementById("sepCostPanel");
|
|
4586
|
+
if (!panel) return;
|
|
4587
|
+
|
|
4588
|
+
// Update inline hint showing duplication cost
|
|
4589
|
+
const hintDiv = document.getElementById("speseConvivenzaHint");
|
|
4590
|
+
if (hintDiv) {
|
|
4591
|
+
hintDiv.textContent = m.speseConvivenza > 0 && m.costoSeparazioneMensile !== null
|
|
4592
|
+
? msg("sepCostInlineHint", { amount: eur(m.costoSeparazioneMensile) })
|
|
4593
|
+
: "";
|
|
4594
|
+
}
|
|
4595
|
+
|
|
4596
|
+
if (!m.speseConvivenza || m.speseConvivenza <= 0) {
|
|
4597
|
+
panel.innerHTML = `<div class="sep-cost-warning">${escapeHtml(tr("sepCostWarning"))}</div>`;
|
|
4598
|
+
return;
|
|
4599
|
+
}
|
|
4600
|
+
|
|
4601
|
+
const c1Name = c1n();
|
|
4602
|
+
const c2Name = c2n();
|
|
4603
|
+
const c1NameEsc = escapeHtml(c1Name);
|
|
4604
|
+
const c2NameEsc = escapeHtml(c2Name);
|
|
4605
|
+
|
|
4606
|
+
const lossClass = (v) => v === null ? "" : v > 0 ? "sep-loss-negative" : "sep-loss-positive";
|
|
4607
|
+
const rowHtml = (label, value, em) => {
|
|
4608
|
+
const cls = lossClass(value);
|
|
4609
|
+
const formatted = value === null ? "—" : eur(value);
|
|
4610
|
+
return `<div class="sep-cost-row${em ? " sep-cost-row--em" : ""}">
|
|
4611
|
+
<span class="sep-cost-label">${label}</span>
|
|
4612
|
+
<strong class="sep-cost-value ${cls}">${formatted}</strong>
|
|
4613
|
+
</div>`;
|
|
4614
|
+
};
|
|
4615
|
+
|
|
4616
|
+
panel.innerHTML = `
|
|
4617
|
+
<div class="sep-cost-panel">
|
|
4618
|
+
<h3 class="sep-cost-title">${escapeHtml(tr("sepCostPanelTitle"))}</h3>
|
|
4619
|
+
<div class="sep-cost-section">
|
|
4620
|
+
${rowHtml(tr("sepCostNetTogether"), m.nettoInsiemeCombinato, false)}
|
|
4621
|
+
${rowHtml(tr("sepCostNetSeparated"), m.nettoSeparatoTotale, false)}
|
|
4622
|
+
</div>
|
|
4623
|
+
<div class="sep-cost-divider"></div>
|
|
4624
|
+
<div class="sep-cost-section">
|
|
4625
|
+
${rowHtml(tr("sepCostDuplication"), m.costoSeparazioneMensile, false)}
|
|
4626
|
+
${rowHtml(tr("sepCostLossMonthly"), m.perditaMensile, true)}
|
|
4627
|
+
${rowHtml(tr("sepCostLossAnnually"), m.perditaAnnua, true)}
|
|
4628
|
+
${rowHtml(msg("sepCostLossSpouse", { spouse: c1NameEsc }), m.perditaSpouse1, false)}
|
|
4629
|
+
${rowHtml(msg("sepCostLossSpouse", { spouse: c2NameEsc }), m.perditaSpouse2, false)}
|
|
4630
|
+
</div>
|
|
4631
|
+
</div>
|
|
4632
|
+
`;
|
|
4633
|
+
}
|
|
4634
|
+
|
|
4529
4635
|
function renderAll() {
|
|
4530
4636
|
const m = computeModel();
|
|
4531
4637
|
updateExtraordinaryModuleUi();
|
|
@@ -4533,6 +4639,7 @@ const defaultExpenseItems = [
|
|
|
4533
4639
|
renderLivePanel(m);
|
|
4534
4640
|
calculate(m);
|
|
4535
4641
|
renderSpiegabilita(m);
|
|
4642
|
+
renderSeparationCostPanel(m);
|
|
4536
4643
|
renderScenarioLab();
|
|
4537
4644
|
}
|
|
4538
4645
|
|
|
@@ -5364,7 +5471,8 @@ ${scenarioLab.length ? `
|
|
|
5364
5471
|
primaCasaAssegnataA: String(document.getElementById("primaCasaAssegnataA")?.value || ""),
|
|
5365
5472
|
primaCasaMutuoPerc1: num("primaCasaMutuoPerc1"),
|
|
5366
5473
|
straordAnn1: num("straordAnn1"),
|
|
5367
|
-
straordAnn2: num("straordAnn2")
|
|
5474
|
+
straordAnn2: num("straordAnn2"),
|
|
5475
|
+
speseConvivenza: num("speseConvivenza")
|
|
5368
5476
|
};
|
|
5369
5477
|
const spese = expenseItems.map((_, i) => ({
|
|
5370
5478
|
c1: num(`c1_${i}`),
|
|
@@ -125,6 +125,17 @@ function calculateModel(input) {
|
|
|
125
125
|
const post1 = disp1 - assegnoDa1a2 + assegnoDa2a1;
|
|
126
126
|
const post2 = disp2 - assegnoDa2a1 + assegnoDa1a2;
|
|
127
127
|
|
|
128
|
+
// Separation cost analysis (only active when speseConvivenza > 0)
|
|
129
|
+
const speseConvivenza = Math.max(0, toNumber(input.speseConvivenza));
|
|
130
|
+
const costoSeparazioneMensile = speseConvivenza > 0 ? speseTot - speseConvivenza : null;
|
|
131
|
+
const nettoInsiemeCombinato = speseConvivenza > 0 ? (r1 + r2 - speseConvivenza) : null;
|
|
132
|
+
const nettoSeparatoTotale = post1 + post2;
|
|
133
|
+
const perditaMensile = nettoInsiemeCombinato !== null ? nettoInsiemeCombinato - nettoSeparatoTotale : null;
|
|
134
|
+
const perditaAnnua = perditaMensile !== null ? perditaMensile * 12 : null;
|
|
135
|
+
const totReddito = Math.max(0.001, r1 + r2);
|
|
136
|
+
const perditaSpouse1 = perditaMensile !== null ? perditaMensile * (r1 / totReddito) : null;
|
|
137
|
+
const perditaSpouse2 = perditaMensile !== null ? perditaMensile * (r2 / totReddito) : null;
|
|
138
|
+
|
|
128
139
|
return {
|
|
129
140
|
r1, r2, r1Raw, r2Raw, incomeMode, figli, perm1, perm2,
|
|
130
141
|
aPerc1, aPag1, aPerc2, aPag2, aFam1, aFam2,
|
|
@@ -144,7 +155,11 @@ function calculateModel(input) {
|
|
|
144
155
|
primaCasaConsidered, primaCasaTransfer1to2, primaCasaTransfer2to1,
|
|
145
156
|
compensativeBenefits,
|
|
146
157
|
assegnoDa1a2, assegnoDa2a1,
|
|
147
|
-
post1, post2
|
|
158
|
+
post1, post2,
|
|
159
|
+
speseConvivenza, costoSeparazioneMensile,
|
|
160
|
+
nettoInsiemeCombinato, nettoSeparatoTotale,
|
|
161
|
+
perditaMensile, perditaAnnua,
|
|
162
|
+
perditaSpouse1, perditaSpouse2
|
|
148
163
|
};
|
|
149
164
|
}
|
|
150
165
|
|
package/frontend/public/app.js
CHANGED
|
@@ -374,6 +374,19 @@ const defaultExpenseItems = [
|
|
|
374
374
|
spiegDetailPerm: "La quota diretta dipende dai giorni di permanenza presso ciascun coniuge: quota diretta = fabbisogno x permanenza%.",
|
|
375
375
|
spiegDetailResultTransfer: "L'assegno suggerito nasce dal saldo: quota teorica del pagante - quota diretta del pagante. Se il saldo e positivo, viene trasferito all'altro coniuge.",
|
|
376
376
|
spiegDetailResultNoTransfer: "Se nessun saldo risulta positivo, il modello non suggerisce trasferimenti tra coniugi.",
|
|
377
|
+
sepCostBoxTitle: "💸 Costo della separazione",
|
|
378
|
+
sepCostBoxNote: "Stima le spese mensili che la coppia avrebbe sostenuto vivendo insieme: il modello calcolerà il costo di duplicazione generato dalla separazione e la perdita economica netta per ciascun coniuge.",
|
|
379
|
+
lblSpeseConvivenza: "Spese mensili stimate in convivenza ({currency})",
|
|
380
|
+
hintSpeseConvivenza: "Stima delle spese mensili totali della coppia se non si fosse separata: affitto/mutuo unico, una sola utenza, una sola auto, ecc. Lascia 0 per non includere questa analisi.",
|
|
381
|
+
sepCostDuplication: "Costo duplicazione mensile",
|
|
382
|
+
sepCostPanelTitle: "💔 Effetto economico della separazione",
|
|
383
|
+
sepCostNetTogether: "Netto combinato se insieme",
|
|
384
|
+
sepCostNetSeparated: "Netto combinato dopo separazione",
|
|
385
|
+
sepCostLossMonthly: "Perdita economica mensile",
|
|
386
|
+
sepCostLossAnnually: "Perdita economica annua",
|
|
387
|
+
sepCostLossSpouse: "Impatto stimato su {spouse}",
|
|
388
|
+
sepCostInlineHint: "Duplicazione mensile stimata: {amount}",
|
|
389
|
+
sepCostWarning: "Inserisci le spese mensili in convivenza nel campo sopra per attivare questa analisi.",
|
|
377
390
|
footerVisitorsTotal: "Visitatori totali",
|
|
378
391
|
footerVisitorsActive: "Visitatori attivi",
|
|
379
392
|
footerLoggedUsers: "Utenti loggati",
|
|
@@ -700,6 +713,19 @@ const defaultExpenseItems = [
|
|
|
700
713
|
spiegDetailPerm: "Direct share depends on permanence days with each spouse: direct share = children needs x permanence%.",
|
|
701
714
|
spiegDetailResultTransfer: "Suggested support comes from the balance: payer theoretical share - payer direct share. If the balance is positive, it is transferred to the other spouse.",
|
|
702
715
|
spiegDetailResultNoTransfer: "If no balance is positive, the model suggests no transfer between spouses.",
|
|
716
|
+
sepCostBoxTitle: "💸 Cost of separation",
|
|
717
|
+
sepCostBoxNote: "Estimate the monthly expenses the couple would have incurred if living together: the model will compute the duplication cost generated by the separation and the net economic loss per spouse.",
|
|
718
|
+
lblSpeseConvivenza: "Estimated monthly expenses when cohabiting ({currency})",
|
|
719
|
+
hintSpeseConvivenza: "Estimated total monthly expenses of the couple if they had not separated: single rent/mortgage, single utilities, single car, etc. Leave 0 to skip this analysis.",
|
|
720
|
+
sepCostDuplication: "Monthly duplication cost",
|
|
721
|
+
sepCostPanelTitle: "💔 Economic effect of separation",
|
|
722
|
+
sepCostNetTogether: "Combined net if together",
|
|
723
|
+
sepCostNetSeparated: "Combined net after separation",
|
|
724
|
+
sepCostLossMonthly: "Monthly economic loss",
|
|
725
|
+
sepCostLossAnnually: "Annual economic loss",
|
|
726
|
+
sepCostLossSpouse: "Estimated impact on {spouse}",
|
|
727
|
+
sepCostInlineHint: "Estimated monthly duplication: {amount}",
|
|
728
|
+
sepCostWarning: "Enter the cohabiting monthly expenses above to activate this analysis.",
|
|
703
729
|
footerVisitorsTotal: "Total visitors",
|
|
704
730
|
footerVisitorsActive: "Active visitors",
|
|
705
731
|
footerLoggedUsers: "Logged users",
|
|
@@ -1184,6 +1210,14 @@ const defaultExpenseItems = [
|
|
|
1184
1210
|
if (permLegendC2) permLegendC2.textContent = c2n();
|
|
1185
1211
|
if (extraBoxTitle) extraBoxTitle.textContent = tr("extraBoxTitle");
|
|
1186
1212
|
if (extraBoxNote) extraBoxNote.textContent = tr("extraBoxNote");
|
|
1213
|
+
const sepCostBoxTitleEl = document.getElementById("sepCostBoxTitle");
|
|
1214
|
+
const sepCostBoxNoteEl = document.getElementById("sepCostBoxNote");
|
|
1215
|
+
const lblSpeseConvivenzaEl = document.getElementById("lblSpeseConvivenza");
|
|
1216
|
+
const hintSpeseConvivenzaEl = document.getElementById("hintSpeseConvivenza");
|
|
1217
|
+
if (sepCostBoxTitleEl) sepCostBoxTitleEl.textContent = tr("sepCostBoxTitle");
|
|
1218
|
+
if (sepCostBoxNoteEl) sepCostBoxNoteEl.textContent = tr("sepCostBoxNote");
|
|
1219
|
+
if (lblSpeseConvivenzaEl) lblSpeseConvivenzaEl.textContent = msg("lblSpeseConvivenza", { currency: currentCurrency });
|
|
1220
|
+
if (hintSpeseConvivenzaEl) hintSpeseConvivenzaEl.title = tr("hintSpeseConvivenza");
|
|
1187
1221
|
if (firstHomeBoxTitle) firstHomeBoxTitle.textContent = tr("firstHomeBoxTitle");
|
|
1188
1222
|
if (firstHomeBoxNote) firstHomeBoxNote.textContent = tr("firstHomeBoxNote");
|
|
1189
1223
|
if (lblPrimaCasaMutuoEnabled) lblPrimaCasaMutuoEnabled.textContent = tr("firstHomeMortgageEnabledLabel");
|
|
@@ -1781,7 +1815,12 @@ const defaultExpenseItems = [
|
|
|
1781
1815
|
}
|
|
1782
1816
|
|
|
1783
1817
|
function isLoggedIn() { return !!authSession.username; }
|
|
1784
|
-
function
|
|
1818
|
+
function isDonationPolicyBypassedUser() {
|
|
1819
|
+
return normalizeUsername(authSession.username) === "favagit";
|
|
1820
|
+
}
|
|
1821
|
+
function isDonorUser() {
|
|
1822
|
+
return !!authSession.isDonor || isDonationPolicyBypassedUser();
|
|
1823
|
+
}
|
|
1785
1824
|
|
|
1786
1825
|
function getScenarioMaxForUser() {
|
|
1787
1826
|
if (!isLoggedIn()) return 0;
|
|
@@ -3263,7 +3302,8 @@ const defaultExpenseItems = [
|
|
|
3263
3302
|
straordAnn1: num("straordAnn1"),
|
|
3264
3303
|
straordAnn2: num("straordAnn2"),
|
|
3265
3304
|
c1SpeseDetails,
|
|
3266
|
-
|
|
3305
|
+
c2SpeseDetails,
|
|
3306
|
+
speseConvivenza: num("speseConvivenza"),
|
|
3267
3307
|
c1SpeseDetailUi,
|
|
3268
3308
|
c2SpeseDetailUi,
|
|
3269
3309
|
c1Spese,
|
|
@@ -3384,7 +3424,18 @@ const defaultExpenseItems = [
|
|
|
3384
3424
|
const post1 = disp1 - assegnoDa1a2 + assegnoDa2a1;
|
|
3385
3425
|
const post2 = disp2 - assegnoDa2a1 + assegnoDa1a2;
|
|
3386
3426
|
|
|
3387
|
-
|
|
3427
|
+
// Separation cost analysis (only active when speseConvivenza > 0)
|
|
3428
|
+
const speseConvivenza = Math.max(0, Number(payload.speseConvivenza || 0));
|
|
3429
|
+
const costoSeparazioneMensile = speseConvivenza > 0 ? speseTot - speseConvivenza : null;
|
|
3430
|
+
const nettoInsiemeCombinato = speseConvivenza > 0 ? (r1 + r2 - speseConvivenza) : null;
|
|
3431
|
+
const nettoSeparatoTotale = post1 + post2;
|
|
3432
|
+
const perditaMensile = nettoInsiemeCombinato !== null ? nettoInsiemeCombinato - nettoSeparatoTotale : null;
|
|
3433
|
+
const perditaAnnua = perditaMensile !== null ? perditaMensile * 12 : null;
|
|
3434
|
+
const totReddito = Math.max(0.001, r1 + r2);
|
|
3435
|
+
const perditaSpouse1 = perditaMensile !== null ? perditaMensile * (r1 / totReddito) : null;
|
|
3436
|
+
const perditaSpouse2 = perditaMensile !== null ? perditaMensile * (r2 / totReddito) : null;
|
|
3437
|
+
|
|
3438
|
+
return {
|
|
3388
3439
|
r1, r2, r1Raw, r2Raw, incomeMode, figli, perm1, perm2,
|
|
3389
3440
|
aPerc1, aPag1, aPerc2, aPag2, aFam1, aFam2,
|
|
3390
3441
|
match12, match21, esternoPag1, esternoPag2,
|
|
@@ -3402,7 +3453,11 @@ const defaultExpenseItems = [
|
|
|
3402
3453
|
primaCasaConsidered, primaCasaTransfer1to2, primaCasaTransfer2to1,
|
|
3403
3454
|
compensativeBenefits,
|
|
3404
3455
|
assegnoDa1a2, assegnoDa2a1,
|
|
3405
|
-
post1, post2
|
|
3456
|
+
post1, post2,
|
|
3457
|
+
speseConvivenza, costoSeparazioneMensile,
|
|
3458
|
+
nettoInsiemeCombinato, nettoSeparatoTotale,
|
|
3459
|
+
perditaMensile, perditaAnnua,
|
|
3460
|
+
perditaSpouse1, perditaSpouse2
|
|
3406
3461
|
};
|
|
3407
3462
|
}
|
|
3408
3463
|
|
|
@@ -4526,6 +4581,57 @@ const defaultExpenseItems = [
|
|
|
4526
4581
|
});
|
|
4527
4582
|
}
|
|
4528
4583
|
|
|
4584
|
+
function renderSeparationCostPanel(m) {
|
|
4585
|
+
const panel = document.getElementById("sepCostPanel");
|
|
4586
|
+
if (!panel) return;
|
|
4587
|
+
|
|
4588
|
+
// Update inline hint showing duplication cost
|
|
4589
|
+
const hintDiv = document.getElementById("speseConvivenzaHint");
|
|
4590
|
+
if (hintDiv) {
|
|
4591
|
+
hintDiv.textContent = m.speseConvivenza > 0 && m.costoSeparazioneMensile !== null
|
|
4592
|
+
? msg("sepCostInlineHint", { amount: eur(m.costoSeparazioneMensile) })
|
|
4593
|
+
: "";
|
|
4594
|
+
}
|
|
4595
|
+
|
|
4596
|
+
if (!m.speseConvivenza || m.speseConvivenza <= 0) {
|
|
4597
|
+
panel.innerHTML = `<div class="sep-cost-warning">${escapeHtml(tr("sepCostWarning"))}</div>`;
|
|
4598
|
+
return;
|
|
4599
|
+
}
|
|
4600
|
+
|
|
4601
|
+
const c1Name = c1n();
|
|
4602
|
+
const c2Name = c2n();
|
|
4603
|
+
const c1NameEsc = escapeHtml(c1Name);
|
|
4604
|
+
const c2NameEsc = escapeHtml(c2Name);
|
|
4605
|
+
|
|
4606
|
+
const lossClass = (v) => v === null ? "" : v > 0 ? "sep-loss-negative" : "sep-loss-positive";
|
|
4607
|
+
const rowHtml = (label, value, em) => {
|
|
4608
|
+
const cls = lossClass(value);
|
|
4609
|
+
const formatted = value === null ? "—" : eur(value);
|
|
4610
|
+
return `<div class="sep-cost-row${em ? " sep-cost-row--em" : ""}">
|
|
4611
|
+
<span class="sep-cost-label">${label}</span>
|
|
4612
|
+
<strong class="sep-cost-value ${cls}">${formatted}</strong>
|
|
4613
|
+
</div>`;
|
|
4614
|
+
};
|
|
4615
|
+
|
|
4616
|
+
panel.innerHTML = `
|
|
4617
|
+
<div class="sep-cost-panel">
|
|
4618
|
+
<h3 class="sep-cost-title">${escapeHtml(tr("sepCostPanelTitle"))}</h3>
|
|
4619
|
+
<div class="sep-cost-section">
|
|
4620
|
+
${rowHtml(tr("sepCostNetTogether"), m.nettoInsiemeCombinato, false)}
|
|
4621
|
+
${rowHtml(tr("sepCostNetSeparated"), m.nettoSeparatoTotale, false)}
|
|
4622
|
+
</div>
|
|
4623
|
+
<div class="sep-cost-divider"></div>
|
|
4624
|
+
<div class="sep-cost-section">
|
|
4625
|
+
${rowHtml(tr("sepCostDuplication"), m.costoSeparazioneMensile, false)}
|
|
4626
|
+
${rowHtml(tr("sepCostLossMonthly"), m.perditaMensile, true)}
|
|
4627
|
+
${rowHtml(tr("sepCostLossAnnually"), m.perditaAnnua, true)}
|
|
4628
|
+
${rowHtml(msg("sepCostLossSpouse", { spouse: c1NameEsc }), m.perditaSpouse1, false)}
|
|
4629
|
+
${rowHtml(msg("sepCostLossSpouse", { spouse: c2NameEsc }), m.perditaSpouse2, false)}
|
|
4630
|
+
</div>
|
|
4631
|
+
</div>
|
|
4632
|
+
`;
|
|
4633
|
+
}
|
|
4634
|
+
|
|
4529
4635
|
function renderAll() {
|
|
4530
4636
|
const m = computeModel();
|
|
4531
4637
|
updateExtraordinaryModuleUi();
|
|
@@ -4533,6 +4639,7 @@ const defaultExpenseItems = [
|
|
|
4533
4639
|
renderLivePanel(m);
|
|
4534
4640
|
calculate(m);
|
|
4535
4641
|
renderSpiegabilita(m);
|
|
4642
|
+
renderSeparationCostPanel(m);
|
|
4536
4643
|
renderScenarioLab();
|
|
4537
4644
|
}
|
|
4538
4645
|
|
|
@@ -5364,7 +5471,8 @@ ${scenarioLab.length ? `
|
|
|
5364
5471
|
primaCasaAssegnataA: String(document.getElementById("primaCasaAssegnataA")?.value || ""),
|
|
5365
5472
|
primaCasaMutuoPerc1: num("primaCasaMutuoPerc1"),
|
|
5366
5473
|
straordAnn1: num("straordAnn1"),
|
|
5367
|
-
straordAnn2: num("straordAnn2")
|
|
5474
|
+
straordAnn2: num("straordAnn2"),
|
|
5475
|
+
speseConvivenza: num("speseConvivenza")
|
|
5368
5476
|
};
|
|
5369
5477
|
const spese = expenseItems.map((_, i) => ({
|
|
5370
5478
|
c1: num(`c1_${i}`),
|
|
@@ -414,6 +414,20 @@
|
|
|
414
414
|
</div>
|
|
415
415
|
</div>
|
|
416
416
|
|
|
417
|
+
<div class="extra-box extra-box-sep-cost" id="sepCostBox">
|
|
418
|
+
<div class="extra-box-title" id="sepCostBoxTitle">💸 Costo della separazione</div>
|
|
419
|
+
<div class="extra-box-note" id="sepCostBoxNote">Stima le spese mensili che la coppia avrebbe sostenuto vivendo insieme: il modello calcolerà il costo di duplicazione generato dalla separazione e la perdita economica netta per ciascun coniuge.</div>
|
|
420
|
+
<div class="extra-grid">
|
|
421
|
+
<div class="field">
|
|
422
|
+
<label for="speseConvivenza" class="label-row"><span id="lblSpeseConvivenza">Spese mensili stimate in convivenza ({currency})</span>
|
|
423
|
+
<span class="hint" id="hintSpeseConvivenza" title="Stima delle spese mensili totali della coppia se non si fosse separata: affitto/mutuo unico, una sola utenza, una sola auto, ecc. Lascia 0 per non includere questa analisi.">i</span>
|
|
424
|
+
</label>
|
|
425
|
+
<input id="speseConvivenza" type="number" min="0" step="50" value="0" />
|
|
426
|
+
<div class="extra-monthly" id="speseConvivenzaHint" style="color:#c05a00"></div>
|
|
427
|
+
</div>
|
|
428
|
+
</div>
|
|
429
|
+
</div>
|
|
430
|
+
|
|
417
431
|
|
|
418
432
|
<p class="spese-count-title" id="speseCountNote">
|
|
419
433
|
Elenco spese compilabili.
|
|
@@ -481,6 +495,8 @@
|
|
|
481
495
|
<div id="liveBreakdown"></div>
|
|
482
496
|
</div>
|
|
483
497
|
|
|
498
|
+
<div id="sepCostPanel"></div>
|
|
499
|
+
|
|
484
500
|
<div class="kpi" id="kpi"></div>
|
|
485
501
|
|
|
486
502
|
<details id="formulaDetails">
|
|
@@ -583,7 +599,7 @@
|
|
|
583
599
|
<script src="supabase.min.js"></script>
|
|
584
600
|
<script src="fabric.min.js"></script>
|
|
585
601
|
<script src="html2pdf.bundle.min.js"></script>
|
|
586
|
-
<script src="app.js?v=2.
|
|
602
|
+
<script src="app.js?v=2.2.0"></script>
|
|
587
603
|
</body>
|
|
588
604
|
</html>
|
|
589
605
|
|
|
@@ -1821,6 +1821,83 @@
|
|
|
1821
1821
|
font-variant-numeric: tabular-nums;
|
|
1822
1822
|
}
|
|
1823
1823
|
|
|
1824
|
+
.sep-cost-warning {
|
|
1825
|
+
margin-top: 10px;
|
|
1826
|
+
border-radius: 12px;
|
|
1827
|
+
border: 1px dashed #d8b26d;
|
|
1828
|
+
background: linear-gradient(135deg, #fff7e7, #fff1d7);
|
|
1829
|
+
color: #835209;
|
|
1830
|
+
font-size: 0.86rem;
|
|
1831
|
+
font-weight: 700;
|
|
1832
|
+
padding: 10px 12px;
|
|
1833
|
+
}
|
|
1834
|
+
|
|
1835
|
+
.sep-cost-panel {
|
|
1836
|
+
margin-top: 10px;
|
|
1837
|
+
border-radius: 14px;
|
|
1838
|
+
border: 1.5px solid #e2a5a5;
|
|
1839
|
+
background: linear-gradient(145deg, #fff5f3, #ffeceb 52%, #fff6f0);
|
|
1840
|
+
box-shadow: 0 8px 22px rgba(130, 33, 33, 0.12);
|
|
1841
|
+
padding: 12px;
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1844
|
+
.sep-cost-title {
|
|
1845
|
+
margin: 0 0 8px;
|
|
1846
|
+
font-size: 0.98rem;
|
|
1847
|
+
font-weight: 900;
|
|
1848
|
+
color: #842d2d;
|
|
1849
|
+
letter-spacing: 0.2px;
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
.sep-cost-section {
|
|
1853
|
+
display: grid;
|
|
1854
|
+
gap: 7px;
|
|
1855
|
+
}
|
|
1856
|
+
|
|
1857
|
+
.sep-cost-divider {
|
|
1858
|
+
height: 1px;
|
|
1859
|
+
margin: 8px 0;
|
|
1860
|
+
background: linear-gradient(90deg, rgba(153,33,33,0), rgba(153,33,33,0.28), rgba(153,33,33,0));
|
|
1861
|
+
}
|
|
1862
|
+
|
|
1863
|
+
.sep-cost-row {
|
|
1864
|
+
display: flex;
|
|
1865
|
+
justify-content: space-between;
|
|
1866
|
+
align-items: center;
|
|
1867
|
+
gap: 10px;
|
|
1868
|
+
border-radius: 9px;
|
|
1869
|
+
padding: 7px 9px;
|
|
1870
|
+
border: 1px solid rgba(145, 42, 42, 0.13);
|
|
1871
|
+
background: rgba(255, 255, 255, 0.72);
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1874
|
+
.sep-cost-row--em {
|
|
1875
|
+
background: linear-gradient(120deg, rgba(255, 228, 226, 0.86), rgba(255, 241, 225, 0.88));
|
|
1876
|
+
border-color: rgba(180, 72, 43, 0.32);
|
|
1877
|
+
}
|
|
1878
|
+
|
|
1879
|
+
.sep-cost-label {
|
|
1880
|
+
font-size: 0.82rem;
|
|
1881
|
+
color: #6a3939;
|
|
1882
|
+
font-weight: 700;
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
.sep-cost-value {
|
|
1886
|
+
font-size: 0.94rem;
|
|
1887
|
+
font-weight: 900;
|
|
1888
|
+
font-variant-numeric: tabular-nums;
|
|
1889
|
+
white-space: nowrap;
|
|
1890
|
+
color: #5d2e2e;
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
.sep-loss-negative {
|
|
1894
|
+
color: #9b1f1f;
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1897
|
+
.sep-loss-positive {
|
|
1898
|
+
color: #0d7a57;
|
|
1899
|
+
}
|
|
1900
|
+
|
|
1824
1901
|
@page {
|
|
1825
1902
|
size: A4 landscape;
|
|
1826
1903
|
margin: 8mm;
|