mantenimento-app 2.4.10 → 2.4.12

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 CHANGED
@@ -293,6 +293,8 @@ const defaultExpenseItems = [
293
293
  firstHomeLocativeValueHint: "Valore locativo mensile della casa assegnata, usato per valorizzare il beneficio economico implicito.",
294
294
  firstHomeMortgageAmountLabel: "Rata mutuo mensile ({currency})",
295
295
  firstHomeMortgageAmountHint: "Importo mensile complessivo della rata del mutuo prima casa.",
296
+ firstHomeMortgageExpiryLabel: "Scadenza",
297
+ firstHomeMortgageExpiryHint: "Data prevista di estinzione del mutuo prima casa.",
296
298
  firstHomeAssignedToLabel: "Casa assegnata a",
297
299
  firstHomeAssignedToHint: "Seleziona il coniuge a cui e ceduta la prima casa.",
298
300
  firstHomeAssignedToNone: "Nessuna cessione",
@@ -315,6 +317,7 @@ const defaultExpenseItems = [
315
317
  pdfPrimaryHomeNotDeclared: "Non dichiarato",
316
318
  pdfPrimaryHomeAssignedTo: "Assegnata a",
317
319
  pdfPrimaryHomeMonthlyAmount: "Rata mensile",
320
+ pdfPrimaryHomeExpiryDate: "Scadenza",
318
321
  pdfPrimaryHomeSplit: "Ripartizione mutuo",
319
322
  pdfPrimaryHomeAppliedOnlyColl: "Considerato solo se casa ceduta al collocatario.",
320
323
  pdfExtraordinaryRow: "Spese straordinarie (quota mensile da annuo)",
@@ -661,6 +664,8 @@ const defaultExpenseItems = [
661
664
  firstHomeLocativeValueHint: "Monthly rental value of the assigned home, used to value the implicit economic benefit.",
662
665
  firstHomeMortgageAmountLabel: "Monthly mortgage payment ({currency})",
663
666
  firstHomeMortgageAmountHint: "Total monthly amount of the primary-home mortgage payment.",
667
+ firstHomeMortgageExpiryLabel: "Expiry",
668
+ firstHomeMortgageExpiryHint: "Expected payoff date of the primary-home mortgage.",
664
669
  firstHomeAssignedToLabel: "Home assigned to",
665
670
  firstHomeAssignedToHint: "Select which spouse receives assignment of the primary home.",
666
671
  firstHomeAssignedToNone: "No assignment",
@@ -683,6 +688,7 @@ const defaultExpenseItems = [
683
688
  pdfPrimaryHomeNotDeclared: "Not declared",
684
689
  pdfPrimaryHomeAssignedTo: "Assigned to",
685
690
  pdfPrimaryHomeMonthlyAmount: "Monthly payment",
691
+ pdfPrimaryHomeExpiryDate: "Expiry",
686
692
  pdfPrimaryHomeSplit: "Mortgage split",
687
693
  pdfPrimaryHomeAppliedOnlyColl: "Counted only when the home is assigned to the custodial parent.",
688
694
  pdfExtraordinaryRow: "Extraordinary expenses (monthly share from yearly)",
@@ -1237,6 +1243,8 @@ const defaultExpenseItems = [
1237
1243
  const hintPrimaCasaValoreLocativo = document.getElementById("hintPrimaCasaValoreLocativo");
1238
1244
  const lblPrimaCasaMutuoImporto = document.getElementById("lblPrimaCasaMutuoImporto");
1239
1245
  const hintPrimaCasaMutuoImporto = document.getElementById("hintPrimaCasaMutuoImporto");
1246
+ const lblPrimaCasaMutuoScadenza = document.getElementById("lblPrimaCasaMutuoScadenza");
1247
+ const hintPrimaCasaMutuoScadenza = document.getElementById("hintPrimaCasaMutuoScadenza");
1240
1248
  const lblPrimaCasaAssegnataA = document.getElementById("lblPrimaCasaAssegnataA");
1241
1249
  const hintPrimaCasaAssegnataA = document.getElementById("hintPrimaCasaAssegnataA");
1242
1250
  const lblPrimaCasaMutuoPerc1 = document.getElementById("lblPrimaCasaMutuoPerc1");
@@ -1302,6 +1310,8 @@ const defaultExpenseItems = [
1302
1310
  if (hintPrimaCasaValoreLocativo) hintPrimaCasaValoreLocativo.title = tr("firstHomeLocativeValueHint");
1303
1311
  if (lblPrimaCasaMutuoImporto) lblPrimaCasaMutuoImporto.textContent = msg("firstHomeMortgageAmountLabel", { currency: currentCurrency });
1304
1312
  if (hintPrimaCasaMutuoImporto) hintPrimaCasaMutuoImporto.title = tr("firstHomeMortgageAmountHint");
1313
+ if (lblPrimaCasaMutuoScadenza) lblPrimaCasaMutuoScadenza.textContent = tr("firstHomeMortgageExpiryLabel");
1314
+ if (hintPrimaCasaMutuoScadenza) hintPrimaCasaMutuoScadenza.title = tr("firstHomeMortgageExpiryHint");
1305
1315
  if (lblPrimaCasaAssegnataA) lblPrimaCasaAssegnataA.textContent = tr("firstHomeAssignedToLabel");
1306
1316
  if (hintPrimaCasaAssegnataA) hintPrimaCasaAssegnataA.title = tr("firstHomeAssignedToHint");
1307
1317
  if (lblPrimaCasaMutuoPerc1) lblPrimaCasaMutuoPerc1.textContent = tr("firstHomeSplitLabel");
@@ -1424,6 +1434,18 @@ const defaultExpenseItems = [
1424
1434
  return `${short} ${currentCurrency}`;
1425
1435
  }
1426
1436
 
1437
+ function formatIsoDate(value) {
1438
+ const iso = String(value || "").trim();
1439
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(iso)) return "";
1440
+ const d = new Date(`${iso}T00:00:00`);
1441
+ if (Number.isNaN(d.getTime())) return iso;
1442
+ return new Intl.DateTimeFormat(getCurrentLocale(), {
1443
+ year: "numeric",
1444
+ month: "2-digit",
1445
+ day: "2-digit"
1446
+ }).format(d);
1447
+ }
1448
+
1427
1449
  function escapeHtml(value) {
1428
1450
  return String(value || "")
1429
1451
  .replaceAll("&", "&")
@@ -3762,6 +3784,7 @@ const defaultExpenseItems = [
3762
3784
  primaCasaMutuoEnabled: firstHome.enabled ? 1 : 0,
3763
3785
  primaCasaValoreLocativo: num("primaCasaValoreLocativo"),
3764
3786
  primaCasaMutuoImporto: firstHome.amount,
3787
+ primaCasaMutuoScadenza: firstHome.expiry,
3765
3788
  primaCasaAssegnataA: firstHome.assignedTo,
3766
3789
  primaCasaMutuoPerc1: firstHome.share1,
3767
3790
  straordAnn1: num("straordAnn1"),
@@ -3803,6 +3826,8 @@ const defaultExpenseItems = [
3803
3826
  const aFam2 = Number(payload.aFam2 || 0);
3804
3827
  const primaCasaMutuoEnabled = Number(payload.primaCasaMutuoEnabled || 0) > 0;
3805
3828
  const primaCasaMutuoImporto = Math.max(0, Number(payload.primaCasaMutuoImporto || 0));
3829
+ const primaCasaMutuoScadenzaRaw = String(payload.primaCasaMutuoScadenza || "").trim();
3830
+ const primaCasaMutuoScadenza = /^\d{4}-\d{2}-\d{2}$/.test(primaCasaMutuoScadenzaRaw) ? primaCasaMutuoScadenzaRaw : "";
3806
3831
  const primaCasaAssegnataA = (String(payload.primaCasaAssegnataA || "") === "1" || String(payload.primaCasaAssegnataA || "") === "2")
3807
3832
  ? String(payload.primaCasaAssegnataA)
3808
3833
  : "";
@@ -3942,7 +3967,7 @@ const defaultExpenseItems = [
3942
3967
  quotaDiretta1, quotaDiretta2,
3943
3968
  saldo1, saldo2,
3944
3969
  assegnoBaseDa1a2, assegnoBaseDa2a1,
3945
- primaCasaMutuoEnabled, primaCasaMutuoImporto, primaCasaAssegnataA, primaCasaValoreLocativo,
3970
+ primaCasaMutuoEnabled, primaCasaMutuoImporto, primaCasaMutuoScadenza, primaCasaAssegnataA, primaCasaValoreLocativo,
3946
3971
  primaCasaMutuoPerc1, primaCasaMutuoPerc2,
3947
3972
  primaCasaConsidered, primaCasaTransfer1to2, primaCasaTransfer2to1,
3948
3973
  compensativeBenefits,
@@ -4046,16 +4071,19 @@ const defaultExpenseItems = [
4046
4071
  function getFirstHomeMortgageInput() {
4047
4072
  const enabled = !!document.getElementById("primaCasaMutuoEnabled")?.checked;
4048
4073
  const amount = Math.max(0, num("primaCasaMutuoImporto"));
4074
+ const expiryRaw = String(document.getElementById("primaCasaMutuoScadenza")?.value || "").trim();
4075
+ const expiry = /^\d{4}-\d{2}-\d{2}$/.test(expiryRaw) ? expiryRaw : "";
4049
4076
  const assignedToRaw = String(document.getElementById("primaCasaAssegnataA")?.value || "").trim();
4050
4077
  const assignedTo = (assignedToRaw === "1" || assignedToRaw === "2") ? assignedToRaw : "";
4051
4078
  const share1 = Math.min(100, Math.max(0, num("primaCasaMutuoPerc1")));
4052
4079
  const share2 = 100 - share1;
4053
- return { enabled, amount, assignedTo, share1, share2 };
4080
+ return { enabled, amount, expiry, assignedTo, share1, share2 };
4054
4081
  }
4055
4082
 
4056
4083
  function updateFirstHomeMortgageUi() {
4057
4084
  const enabledEl = document.getElementById("primaCasaMutuoEnabled");
4058
4085
  const amountEl = document.getElementById("primaCasaMutuoImporto");
4086
+ const expiryEl = document.getElementById("primaCasaMutuoScadenza");
4059
4087
  const assignedEl = document.getElementById("primaCasaAssegnataA");
4060
4088
  const shareEl = document.getElementById("primaCasaMutuoPerc1");
4061
4089
  const splitInfoEl = document.getElementById("primaCasaMutuoSplitInfo");
@@ -4071,6 +4099,7 @@ const defaultExpenseItems = [
4071
4099
 
4072
4100
  const isEnabled = !!enabledEl.checked;
4073
4101
  amountEl.disabled = !isEnabled;
4102
+ if (expiryEl) expiryEl.disabled = !isEnabled;
4074
4103
  assignedEl.disabled = !isEnabled;
4075
4104
  shareEl.disabled = !isEnabled;
4076
4105
  if (splitWrapEl) splitWrapEl.classList.toggle("is-disabled", !isEnabled);
@@ -4601,6 +4630,7 @@ const defaultExpenseItems = [
4601
4630
  setVal("assegnoFam2", Number(payload.aFam2 || 0));
4602
4631
  setChecked("primaCasaMutuoEnabled", Number(payload.primaCasaMutuoEnabled || 0) > 0);
4603
4632
  setVal("primaCasaMutuoImporto", Number(payload.primaCasaMutuoImporto || 0));
4633
+ setVal("primaCasaMutuoScadenza", String(payload.primaCasaMutuoScadenza || ""));
4604
4634
  setVal("primaCasaAssegnataA", (String(payload.primaCasaAssegnataA || "") === "1" || String(payload.primaCasaAssegnataA || "") === "2") ? String(payload.primaCasaAssegnataA) : "");
4605
4635
  setVal("primaCasaMutuoPerc1", Math.min(100, Math.max(0, Number((payload.primaCasaMutuoPerc1 === undefined ? 50 : payload.primaCasaMutuoPerc1) || 0))));
4606
4636
  setVal("straordAnn1", Number(payload.straordAnn1 || 0));
@@ -4775,6 +4805,7 @@ const defaultExpenseItems = [
4775
4805
  ? `
4776
4806
  <tr><td>${tr("pdfPrimaryHomeAssignedTo")}</td><td>${primaryHomeAssignedLabel}</td></tr>
4777
4807
  <tr><td>${tr("pdfPrimaryHomeMonthlyAmount")}</td><td>${eur(m.primaCasaMutuoImporto || 0)}</td></tr>
4808
+ <tr><td>${tr("pdfPrimaryHomeExpiryDate")}</td><td>${escapeHtml(formatIsoDate(m.primaCasaMutuoScadenza) || tr("pdfPrimaryHomeNotDeclared"))}</td></tr>
4778
4809
  <tr><td>${tr("pdfPrimaryHomeSplit")}</td><td>${c1NameEsc} ${(m.primaCasaMutuoPerc1 || 0).toFixed(0)}% · ${c2NameEsc} ${(m.primaCasaMutuoPerc2 || 0).toFixed(0)}%</td></tr>
4779
4810
  <tr><td>${tr("pdfPrimaryHomeAppliedOnlyColl")}</td><td>${m.primaCasaConsidered ? "OK" : tr("pdfPrimaryHomeNotDeclared")}</td></tr>`
4780
4811
  : `<tr><td>${tr("pdfPrimaryHomeMortgage")}</td><td>${tr("pdfPrimaryHomeNotDeclared")}</td></tr>`;
@@ -5293,6 +5324,7 @@ const defaultExpenseItems = [
5293
5324
  ? `
5294
5325
  <tr><td>${tr("pdfPrimaryHomeAssignedTo")}</td><td>${primaryHomeAssignedLabel}</td></tr>
5295
5326
  <tr><td>${tr("pdfPrimaryHomeMonthlyAmount")}</td><td>${eur(m.primaCasaMutuoImporto || 0)}</td></tr>
5327
+ <tr><td>${tr("pdfPrimaryHomeExpiryDate")}</td><td>${escapeHtml(formatIsoDate(m.primaCasaMutuoScadenza) || tr("pdfPrimaryHomeNotDeclared"))}</td></tr>
5296
5328
  <tr><td>${tr("pdfPrimaryHomeSplit")}</td><td>${c1NameEsc} ${(m.primaCasaMutuoPerc1 || 0).toFixed(0)}% · ${c2NameEsc} ${(m.primaCasaMutuoPerc2 || 0).toFixed(0)}%</td></tr>
5297
5329
  <tr><td>${tr("pdfPrimaryHomeAppliedOnlyColl")}</td><td>${m.primaCasaConsidered ? "OK" : tr("pdfPrimaryHomeNotDeclared")}</td></tr>`
5298
5330
  : `<tr><td>${tr("pdfPrimaryHomeMortgage")}</td><td>${tr("pdfPrimaryHomeNotDeclared")}</td></tr>`;
@@ -6201,6 +6233,7 @@ ${scenarioLab.length ? `
6201
6233
  primaCasaMutuoEnabled: document.getElementById("primaCasaMutuoEnabled")?.checked ? 1 : 0,
6202
6234
  primaCasaValoreLocativo: num("primaCasaValoreLocativo"),
6203
6235
  primaCasaMutuoImporto: num("primaCasaMutuoImporto"),
6236
+ primaCasaMutuoScadenza: String(document.getElementById("primaCasaMutuoScadenza")?.value || ""),
6204
6237
  primaCasaAssegnataA: String(document.getElementById("primaCasaAssegnataA")?.value || ""),
6205
6238
  primaCasaMutuoPerc1: num("primaCasaMutuoPerc1"),
6206
6239
  straordAnn1: num("straordAnn1"),
@@ -6328,8 +6361,10 @@ ${scenarioLab.length ? `
6328
6361
  });
6329
6362
  const firstHomeEnabled = document.getElementById("primaCasaMutuoEnabled");
6330
6363
  const firstHomeAssigned = document.getElementById("primaCasaAssegnataA");
6364
+ const firstHomeExpiry = document.getElementById("primaCasaMutuoScadenza");
6331
6365
  if (firstHomeEnabled) firstHomeEnabled.checked = !!firstHomeEnabled.defaultChecked;
6332
6366
  if (firstHomeAssigned) firstHomeAssigned.value = "";
6367
+ if (firstHomeExpiry) firstHomeExpiry.value = firstHomeExpiry.defaultValue || "";
6333
6368
  permanenceCalendarState.byMonth = {};
6334
6369
  speseConvivenzaAutoMode = true;
6335
6370
  selectedScenarioIdx = -1;
@@ -6675,7 +6710,7 @@ ${scenarioLab.length ? `
6675
6710
  }
6676
6711
  updateModeUi();
6677
6712
  renderAll();
6678
- } else if (e.target && (e.target.id === "primaCasaMutuoEnabled" || e.target.id === "primaCasaAssegnataA")) {
6713
+ } else if (e.target && (e.target.id === "primaCasaMutuoEnabled" || e.target.id === "primaCasaAssegnataA" || e.target.id === "primaCasaMutuoScadenza")) {
6679
6714
  updateFirstHomeMortgageUi();
6680
6715
  renderAll();
6681
6716
  }
@@ -35,6 +35,8 @@ function calculateModel(input) {
35
35
  const primaCasaMutuoEnabled = toNumber(input.primaCasaMutuoEnabled) > 0;
36
36
  const primaCasaValoreLocativo = Math.max(0, toNumber(input.primaCasaValoreLocativo));
37
37
  const primaCasaMutuoImporto = Math.max(0, toNumber(input.primaCasaMutuoImporto));
38
+ const primaCasaMutuoScadenzaRaw = String(input.primaCasaMutuoScadenza || '').trim();
39
+ const primaCasaMutuoScadenza = /^\d{4}-\d{2}-\d{2}$/.test(primaCasaMutuoScadenzaRaw) ? primaCasaMutuoScadenzaRaw : '';
38
40
  const primaCasaAssegnataA = String(input.primaCasaAssegnataA || '');
39
41
  const rawMutuoPerc1 = input.primaCasaMutuoPerc1 === undefined ? 50 : input.primaCasaMutuoPerc1;
40
42
  const primaCasaMutuoPerc1 = clamp(toNumber(rawMutuoPerc1), 0, 100);
@@ -163,7 +165,7 @@ function calculateModel(input) {
163
165
  quotaDiretta1, quotaDiretta2,
164
166
  saldo1, saldo2,
165
167
  assegnoBaseDa1a2, assegnoBaseDa2a1,
166
- primaCasaMutuoEnabled, primaCasaValoreLocativo, primaCasaMutuoImporto,
168
+ primaCasaMutuoEnabled, primaCasaValoreLocativo, primaCasaMutuoImporto, primaCasaMutuoScadenza,
167
169
  primaCasaAssegnataA: assigned,
168
170
  primaCasaMutuoPerc1, primaCasaMutuoPerc2,
169
171
  primaCasaConsidered, primaCasaTransfer1to2, primaCasaTransfer2to1,
@@ -293,6 +293,8 @@ const defaultExpenseItems = [
293
293
  firstHomeLocativeValueHint: "Valore locativo mensile della casa assegnata, usato per valorizzare il beneficio economico implicito.",
294
294
  firstHomeMortgageAmountLabel: "Rata mutuo mensile ({currency})",
295
295
  firstHomeMortgageAmountHint: "Importo mensile complessivo della rata del mutuo prima casa.",
296
+ firstHomeMortgageExpiryLabel: "Scadenza mutuo",
297
+ firstHomeMortgageExpiryHint: "Data prevista di estinzione del mutuo prima casa.",
296
298
  firstHomeAssignedToLabel: "Casa assegnata a",
297
299
  firstHomeAssignedToHint: "Seleziona il coniuge a cui e ceduta la prima casa.",
298
300
  firstHomeAssignedToNone: "Nessuna cessione",
@@ -315,6 +317,7 @@ const defaultExpenseItems = [
315
317
  pdfPrimaryHomeNotDeclared: "Non dichiarato",
316
318
  pdfPrimaryHomeAssignedTo: "Assegnata a",
317
319
  pdfPrimaryHomeMonthlyAmount: "Rata mensile",
320
+ pdfPrimaryHomeExpiryDate: "Scadenza mutuo",
318
321
  pdfPrimaryHomeSplit: "Ripartizione mutuo",
319
322
  pdfPrimaryHomeAppliedOnlyColl: "Considerato solo se casa ceduta al collocatario.",
320
323
  pdfExtraordinaryRow: "Spese straordinarie (quota mensile da annuo)",
@@ -661,6 +664,8 @@ const defaultExpenseItems = [
661
664
  firstHomeLocativeValueHint: "Monthly rental value of the assigned home, used to value the implicit economic benefit.",
662
665
  firstHomeMortgageAmountLabel: "Monthly mortgage payment ({currency})",
663
666
  firstHomeMortgageAmountHint: "Total monthly amount of the primary-home mortgage payment.",
667
+ firstHomeMortgageExpiryLabel: "Mortgage expiry date",
668
+ firstHomeMortgageExpiryHint: "Expected payoff date of the primary-home mortgage.",
664
669
  firstHomeAssignedToLabel: "Home assigned to",
665
670
  firstHomeAssignedToHint: "Select which spouse receives assignment of the primary home.",
666
671
  firstHomeAssignedToNone: "No assignment",
@@ -683,6 +688,7 @@ const defaultExpenseItems = [
683
688
  pdfPrimaryHomeNotDeclared: "Not declared",
684
689
  pdfPrimaryHomeAssignedTo: "Assigned to",
685
690
  pdfPrimaryHomeMonthlyAmount: "Monthly payment",
691
+ pdfPrimaryHomeExpiryDate: "Mortgage expiry date",
686
692
  pdfPrimaryHomeSplit: "Mortgage split",
687
693
  pdfPrimaryHomeAppliedOnlyColl: "Counted only when the home is assigned to the custodial parent.",
688
694
  pdfExtraordinaryRow: "Extraordinary expenses (monthly share from yearly)",
@@ -1237,6 +1243,8 @@ const defaultExpenseItems = [
1237
1243
  const hintPrimaCasaValoreLocativo = document.getElementById("hintPrimaCasaValoreLocativo");
1238
1244
  const lblPrimaCasaMutuoImporto = document.getElementById("lblPrimaCasaMutuoImporto");
1239
1245
  const hintPrimaCasaMutuoImporto = document.getElementById("hintPrimaCasaMutuoImporto");
1246
+ const lblPrimaCasaMutuoScadenza = document.getElementById("lblPrimaCasaMutuoScadenza");
1247
+ const hintPrimaCasaMutuoScadenza = document.getElementById("hintPrimaCasaMutuoScadenza");
1240
1248
  const lblPrimaCasaAssegnataA = document.getElementById("lblPrimaCasaAssegnataA");
1241
1249
  const hintPrimaCasaAssegnataA = document.getElementById("hintPrimaCasaAssegnataA");
1242
1250
  const lblPrimaCasaMutuoPerc1 = document.getElementById("lblPrimaCasaMutuoPerc1");
@@ -1302,6 +1310,8 @@ const defaultExpenseItems = [
1302
1310
  if (hintPrimaCasaValoreLocativo) hintPrimaCasaValoreLocativo.title = tr("firstHomeLocativeValueHint");
1303
1311
  if (lblPrimaCasaMutuoImporto) lblPrimaCasaMutuoImporto.textContent = msg("firstHomeMortgageAmountLabel", { currency: currentCurrency });
1304
1312
  if (hintPrimaCasaMutuoImporto) hintPrimaCasaMutuoImporto.title = tr("firstHomeMortgageAmountHint");
1313
+ if (lblPrimaCasaMutuoScadenza) lblPrimaCasaMutuoScadenza.textContent = tr("firstHomeMortgageExpiryLabel");
1314
+ if (hintPrimaCasaMutuoScadenza) hintPrimaCasaMutuoScadenza.title = tr("firstHomeMortgageExpiryHint");
1305
1315
  if (lblPrimaCasaAssegnataA) lblPrimaCasaAssegnataA.textContent = tr("firstHomeAssignedToLabel");
1306
1316
  if (hintPrimaCasaAssegnataA) hintPrimaCasaAssegnataA.title = tr("firstHomeAssignedToHint");
1307
1317
  if (lblPrimaCasaMutuoPerc1) lblPrimaCasaMutuoPerc1.textContent = tr("firstHomeSplitLabel");
@@ -1424,6 +1434,18 @@ const defaultExpenseItems = [
1424
1434
  return `${short} ${currentCurrency}`;
1425
1435
  }
1426
1436
 
1437
+ function formatIsoDate(value) {
1438
+ const iso = String(value || "").trim();
1439
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(iso)) return "";
1440
+ const d = new Date(`${iso}T00:00:00`);
1441
+ if (Number.isNaN(d.getTime())) return iso;
1442
+ return new Intl.DateTimeFormat(getCurrentLocale(), {
1443
+ year: "numeric",
1444
+ month: "2-digit",
1445
+ day: "2-digit"
1446
+ }).format(d);
1447
+ }
1448
+
1427
1449
  function escapeHtml(value) {
1428
1450
  return String(value || "")
1429
1451
  .replaceAll("&", "&amp;")
@@ -3762,6 +3784,7 @@ const defaultExpenseItems = [
3762
3784
  primaCasaMutuoEnabled: firstHome.enabled ? 1 : 0,
3763
3785
  primaCasaValoreLocativo: num("primaCasaValoreLocativo"),
3764
3786
  primaCasaMutuoImporto: firstHome.amount,
3787
+ primaCasaMutuoScadenza: firstHome.expiry,
3765
3788
  primaCasaAssegnataA: firstHome.assignedTo,
3766
3789
  primaCasaMutuoPerc1: firstHome.share1,
3767
3790
  straordAnn1: num("straordAnn1"),
@@ -3803,6 +3826,8 @@ const defaultExpenseItems = [
3803
3826
  const aFam2 = Number(payload.aFam2 || 0);
3804
3827
  const primaCasaMutuoEnabled = Number(payload.primaCasaMutuoEnabled || 0) > 0;
3805
3828
  const primaCasaMutuoImporto = Math.max(0, Number(payload.primaCasaMutuoImporto || 0));
3829
+ const primaCasaMutuoScadenzaRaw = String(payload.primaCasaMutuoScadenza || "").trim();
3830
+ const primaCasaMutuoScadenza = /^\d{4}-\d{2}-\d{2}$/.test(primaCasaMutuoScadenzaRaw) ? primaCasaMutuoScadenzaRaw : "";
3806
3831
  const primaCasaAssegnataA = (String(payload.primaCasaAssegnataA || "") === "1" || String(payload.primaCasaAssegnataA || "") === "2")
3807
3832
  ? String(payload.primaCasaAssegnataA)
3808
3833
  : "";
@@ -3942,7 +3967,7 @@ const defaultExpenseItems = [
3942
3967
  quotaDiretta1, quotaDiretta2,
3943
3968
  saldo1, saldo2,
3944
3969
  assegnoBaseDa1a2, assegnoBaseDa2a1,
3945
- primaCasaMutuoEnabled, primaCasaMutuoImporto, primaCasaAssegnataA, primaCasaValoreLocativo,
3970
+ primaCasaMutuoEnabled, primaCasaMutuoImporto, primaCasaMutuoScadenza, primaCasaAssegnataA, primaCasaValoreLocativo,
3946
3971
  primaCasaMutuoPerc1, primaCasaMutuoPerc2,
3947
3972
  primaCasaConsidered, primaCasaTransfer1to2, primaCasaTransfer2to1,
3948
3973
  compensativeBenefits,
@@ -4046,16 +4071,19 @@ const defaultExpenseItems = [
4046
4071
  function getFirstHomeMortgageInput() {
4047
4072
  const enabled = !!document.getElementById("primaCasaMutuoEnabled")?.checked;
4048
4073
  const amount = Math.max(0, num("primaCasaMutuoImporto"));
4074
+ const expiryRaw = String(document.getElementById("primaCasaMutuoScadenza")?.value || "").trim();
4075
+ const expiry = /^\d{4}-\d{2}-\d{2}$/.test(expiryRaw) ? expiryRaw : "";
4049
4076
  const assignedToRaw = String(document.getElementById("primaCasaAssegnataA")?.value || "").trim();
4050
4077
  const assignedTo = (assignedToRaw === "1" || assignedToRaw === "2") ? assignedToRaw : "";
4051
4078
  const share1 = Math.min(100, Math.max(0, num("primaCasaMutuoPerc1")));
4052
4079
  const share2 = 100 - share1;
4053
- return { enabled, amount, assignedTo, share1, share2 };
4080
+ return { enabled, amount, expiry, assignedTo, share1, share2 };
4054
4081
  }
4055
4082
 
4056
4083
  function updateFirstHomeMortgageUi() {
4057
4084
  const enabledEl = document.getElementById("primaCasaMutuoEnabled");
4058
4085
  const amountEl = document.getElementById("primaCasaMutuoImporto");
4086
+ const expiryEl = document.getElementById("primaCasaMutuoScadenza");
4059
4087
  const assignedEl = document.getElementById("primaCasaAssegnataA");
4060
4088
  const shareEl = document.getElementById("primaCasaMutuoPerc1");
4061
4089
  const splitInfoEl = document.getElementById("primaCasaMutuoSplitInfo");
@@ -4071,6 +4099,7 @@ const defaultExpenseItems = [
4071
4099
 
4072
4100
  const isEnabled = !!enabledEl.checked;
4073
4101
  amountEl.disabled = !isEnabled;
4102
+ if (expiryEl) expiryEl.disabled = !isEnabled;
4074
4103
  assignedEl.disabled = !isEnabled;
4075
4104
  shareEl.disabled = !isEnabled;
4076
4105
  if (splitWrapEl) splitWrapEl.classList.toggle("is-disabled", !isEnabled);
@@ -4601,6 +4630,7 @@ const defaultExpenseItems = [
4601
4630
  setVal("assegnoFam2", Number(payload.aFam2 || 0));
4602
4631
  setChecked("primaCasaMutuoEnabled", Number(payload.primaCasaMutuoEnabled || 0) > 0);
4603
4632
  setVal("primaCasaMutuoImporto", Number(payload.primaCasaMutuoImporto || 0));
4633
+ setVal("primaCasaMutuoScadenza", String(payload.primaCasaMutuoScadenza || ""));
4604
4634
  setVal("primaCasaAssegnataA", (String(payload.primaCasaAssegnataA || "") === "1" || String(payload.primaCasaAssegnataA || "") === "2") ? String(payload.primaCasaAssegnataA) : "");
4605
4635
  setVal("primaCasaMutuoPerc1", Math.min(100, Math.max(0, Number((payload.primaCasaMutuoPerc1 === undefined ? 50 : payload.primaCasaMutuoPerc1) || 0))));
4606
4636
  setVal("straordAnn1", Number(payload.straordAnn1 || 0));
@@ -4775,6 +4805,7 @@ const defaultExpenseItems = [
4775
4805
  ? `
4776
4806
  <tr><td>${tr("pdfPrimaryHomeAssignedTo")}</td><td>${primaryHomeAssignedLabel}</td></tr>
4777
4807
  <tr><td>${tr("pdfPrimaryHomeMonthlyAmount")}</td><td>${eur(m.primaCasaMutuoImporto || 0)}</td></tr>
4808
+ <tr><td>${tr("pdfPrimaryHomeExpiryDate")}</td><td>${escapeHtml(formatIsoDate(m.primaCasaMutuoScadenza) || tr("pdfPrimaryHomeNotDeclared"))}</td></tr>
4778
4809
  <tr><td>${tr("pdfPrimaryHomeSplit")}</td><td>${c1NameEsc} ${(m.primaCasaMutuoPerc1 || 0).toFixed(0)}% · ${c2NameEsc} ${(m.primaCasaMutuoPerc2 || 0).toFixed(0)}%</td></tr>
4779
4810
  <tr><td>${tr("pdfPrimaryHomeAppliedOnlyColl")}</td><td>${m.primaCasaConsidered ? "OK" : tr("pdfPrimaryHomeNotDeclared")}</td></tr>`
4780
4811
  : `<tr><td>${tr("pdfPrimaryHomeMortgage")}</td><td>${tr("pdfPrimaryHomeNotDeclared")}</td></tr>`;
@@ -5293,6 +5324,7 @@ const defaultExpenseItems = [
5293
5324
  ? `
5294
5325
  <tr><td>${tr("pdfPrimaryHomeAssignedTo")}</td><td>${primaryHomeAssignedLabel}</td></tr>
5295
5326
  <tr><td>${tr("pdfPrimaryHomeMonthlyAmount")}</td><td>${eur(m.primaCasaMutuoImporto || 0)}</td></tr>
5327
+ <tr><td>${tr("pdfPrimaryHomeExpiryDate")}</td><td>${escapeHtml(formatIsoDate(m.primaCasaMutuoScadenza) || tr("pdfPrimaryHomeNotDeclared"))}</td></tr>
5296
5328
  <tr><td>${tr("pdfPrimaryHomeSplit")}</td><td>${c1NameEsc} ${(m.primaCasaMutuoPerc1 || 0).toFixed(0)}% · ${c2NameEsc} ${(m.primaCasaMutuoPerc2 || 0).toFixed(0)}%</td></tr>
5297
5329
  <tr><td>${tr("pdfPrimaryHomeAppliedOnlyColl")}</td><td>${m.primaCasaConsidered ? "OK" : tr("pdfPrimaryHomeNotDeclared")}</td></tr>`
5298
5330
  : `<tr><td>${tr("pdfPrimaryHomeMortgage")}</td><td>${tr("pdfPrimaryHomeNotDeclared")}</td></tr>`;
@@ -6201,6 +6233,7 @@ ${scenarioLab.length ? `
6201
6233
  primaCasaMutuoEnabled: document.getElementById("primaCasaMutuoEnabled")?.checked ? 1 : 0,
6202
6234
  primaCasaValoreLocativo: num("primaCasaValoreLocativo"),
6203
6235
  primaCasaMutuoImporto: num("primaCasaMutuoImporto"),
6236
+ primaCasaMutuoScadenza: String(document.getElementById("primaCasaMutuoScadenza")?.value || ""),
6204
6237
  primaCasaAssegnataA: String(document.getElementById("primaCasaAssegnataA")?.value || ""),
6205
6238
  primaCasaMutuoPerc1: num("primaCasaMutuoPerc1"),
6206
6239
  straordAnn1: num("straordAnn1"),
@@ -6328,8 +6361,10 @@ ${scenarioLab.length ? `
6328
6361
  });
6329
6362
  const firstHomeEnabled = document.getElementById("primaCasaMutuoEnabled");
6330
6363
  const firstHomeAssigned = document.getElementById("primaCasaAssegnataA");
6364
+ const firstHomeExpiry = document.getElementById("primaCasaMutuoScadenza");
6331
6365
  if (firstHomeEnabled) firstHomeEnabled.checked = !!firstHomeEnabled.defaultChecked;
6332
6366
  if (firstHomeAssigned) firstHomeAssigned.value = "";
6367
+ if (firstHomeExpiry) firstHomeExpiry.value = firstHomeExpiry.defaultValue || "";
6333
6368
  permanenceCalendarState.byMonth = {};
6334
6369
  speseConvivenzaAutoMode = true;
6335
6370
  selectedScenarioIdx = -1;
@@ -6675,7 +6710,7 @@ ${scenarioLab.length ? `
6675
6710
  }
6676
6711
  updateModeUi();
6677
6712
  renderAll();
6678
- } else if (e.target && (e.target.id === "primaCasaMutuoEnabled" || e.target.id === "primaCasaAssegnataA")) {
6713
+ } else if (e.target && (e.target.id === "primaCasaMutuoEnabled" || e.target.id === "primaCasaAssegnataA" || e.target.id === "primaCasaMutuoScadenza")) {
6679
6714
  updateFirstHomeMortgageUi();
6680
6715
  renderAll();
6681
6716
  }
@@ -358,11 +358,37 @@
358
358
  </label>
359
359
  <input id="primaCasaMutuoEnabled" type="checkbox" />
360
360
  </div>
361
- <div class="field">
362
- <label for="primaCasaValoreLocativo" class="label-row"><span id="lblPrimaCasaValoreLocativo">Casa (valore locativo) ({currency})</span>
363
- <span class="hint" id="hintPrimaCasaValoreLocativo" title="Valore locativo mensile della casa assegnata, usato per valorizzare il beneficio economico implicito.">i</span>
364
- </label>
365
- <input id="primaCasaValoreLocativo" type="number" min="0" step="50" value="1000" />
361
+ <div class="field first-home-mortgage-table-field">
362
+ <div class="first-home-mortgage-table-wrap">
363
+ <table class="first-home-mortgage-table" aria-label="Dati mutuo prima casa">
364
+ <thead>
365
+ <tr>
366
+ <th>
367
+ <span class="label-row"><span id="lblPrimaCasaValoreLocativo">Casa (valore locativo) ({currency})</span>
368
+ <span class="hint" id="hintPrimaCasaValoreLocativo" title="Valore locativo mensile della casa assegnata, usato per valorizzare il beneficio economico implicito.">i</span>
369
+ </span>
370
+ </th>
371
+ <th>
372
+ <span class="label-row"><span id="lblPrimaCasaMutuoImporto">Rata mutuo mensile ({currency})</span>
373
+ <span class="hint" id="hintPrimaCasaMutuoImporto" title="Importo mensile complessivo della rata del mutuo prima casa.">i</span>
374
+ </span>
375
+ </th>
376
+ <th>
377
+ <span class="label-row"><span id="lblPrimaCasaMutuoScadenza">Scadenza</span>
378
+ <span class="hint" id="hintPrimaCasaMutuoScadenza" title="Data prevista di estinzione del mutuo prima casa.">i</span>
379
+ </span>
380
+ </th>
381
+ </tr>
382
+ </thead>
383
+ <tbody>
384
+ <tr>
385
+ <td><input id="primaCasaValoreLocativo" type="number" min="0" step="50" value="1000" /></td>
386
+ <td><input id="primaCasaMutuoImporto" type="number" min="0" step="50" value="0" /></td>
387
+ <td><input id="primaCasaMutuoScadenza" type="date" value="" /></td>
388
+ </tr>
389
+ </tbody>
390
+ </table>
391
+ </div>
366
392
  </div>
367
393
  <div class="field">
368
394
  <label for="primaCasaAssegnataA" class="label-row"><span id="lblPrimaCasaAssegnataA">Casa assegnata a</span>
@@ -374,15 +400,9 @@
374
400
  <option value="2">Coniuge 2</option>
375
401
  </select>
376
402
  </div>
377
- <div class="field">
378
- <label for="primaCasaMutuoImporto" class="label-row"><span id="lblPrimaCasaMutuoImporto">Rata mutuo mensile ({currency})</span>
379
- <span class="hint" id="hintPrimaCasaMutuoImporto" title="Importo mensile complessivo della rata del mutuo prima casa.">i</span>
380
- </label>
381
- <input id="primaCasaMutuoImporto" type="number" min="0" step="50" value="0" />
382
- </div>
383
403
  <div class="field first-home-split-field">
384
404
  <label for="primaCasaMutuoPerc1" class="label-row"><span id="lblPrimaCasaMutuoPerc1">Quota mutuo a carico</span>
385
- <span class="hint" id="hintPrimaCasaMutuoPerc1" title="Percentuale della rata mutuo pagata da Coniuge 1. La quota dell'altro coniuge e complementare a 100%.">i</span>
405
+ <span class="hint" id="hintPrimaCasaMutuoPerc1" title="Percentuale della rata mutuo pagata da ciascun Coniuge.">i</span>
386
406
  </label>
387
407
  <div class="mortgage-split-slider" id="primaCasaMutuoSliderWrap">
388
408
  <div class="mortgage-split-side mortgage-split-side-left" id="primaCasaSplitLeft">
@@ -630,7 +650,7 @@
630
650
  <script src="supabase.min.js"></script>
631
651
  <script src="fabric.min.js"></script>
632
652
  <script src="html2pdf.bundle.min.js"></script>
633
- <script src="app.js?v=2.4.10"></script>
653
+ <script src="app.js?v=2.4.12"></script>
634
654
  </body>
635
655
  </html>
636
656
 
@@ -949,6 +949,58 @@
949
949
  grid-column: 1 / -1;
950
950
  }
951
951
 
952
+ .extra-box-first-home .first-home-mortgage-table-field {
953
+ grid-column: 1 / -1;
954
+ }
955
+
956
+ .first-home-mortgage-table-wrap {
957
+ overflow-x: visible;
958
+ padding-bottom: 2px;
959
+ }
960
+
961
+ .first-home-mortgage-table {
962
+ width: 100%;
963
+ min-width: 0;
964
+ border-collapse: collapse;
965
+ table-layout: fixed;
966
+ border: 1px solid rgba(27, 141, 127, 0.16);
967
+ border-radius: 12px;
968
+ overflow: hidden;
969
+ background: rgba(255, 255, 255, 0.55);
970
+ }
971
+
972
+ .first-home-mortgage-table th {
973
+ text-align: left;
974
+ font-size: 0.72rem;
975
+ color: #2a5954;
976
+ font-weight: 800;
977
+ padding: 8px 10px 6px;
978
+ background: linear-gradient(180deg, rgba(232, 246, 243, 0.95), rgba(242, 249, 247, 0.92));
979
+ border-bottom: 1px solid rgba(27, 141, 127, 0.12);
980
+ }
981
+
982
+ .first-home-mortgage-table td {
983
+ padding: 8px;
984
+ border-top: 1px solid rgba(27, 141, 127, 0.08);
985
+ }
986
+
987
+ .first-home-mortgage-table th + th,
988
+ .first-home-mortgage-table td + td {
989
+ border-left: 1px solid rgba(27, 141, 127, 0.08);
990
+ }
991
+
992
+ .first-home-mortgage-table .label-row {
993
+ display: flex;
994
+ align-items: center;
995
+ gap: 5px;
996
+ flex-wrap: wrap;
997
+ line-height: 1.15;
998
+ }
999
+
1000
+ .first-home-mortgage-table input {
1001
+ width: 100%;
1002
+ }
1003
+
952
1004
  .extra-box-first-home .field {
953
1005
  border-radius: 12px;
954
1006
  padding: 8px 9px 7px;
@@ -966,6 +1018,7 @@
966
1018
  }
967
1019
 
968
1020
  .extra-box-first-home .field input[type="number"],
1021
+ .extra-box-first-home .field input[type="date"],
969
1022
  .extra-box-first-home .field select {
970
1023
  border-width: 1.5px;
971
1024
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.8) inset;
@@ -982,6 +1035,7 @@
982
1035
  }
983
1036
 
984
1037
  .extra-box-first-home .field input[type="number"]:focus,
1038
+ .extra-box-first-home .field input[type="date"]:focus,
985
1039
  .extra-box-first-home .field select:focus {
986
1040
  border-color: rgba(27, 141, 127, 0.4);
987
1041
  box-shadow: 0 0 0 3px rgba(27, 141, 127, 0.14), 0 1px 0 rgba(255, 255, 255, 0.85) inset;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mantenimento-app",
3
- "version": "2.4.10",
3
+ "version": "2.4.12",
4
4
  "description": "Frontend + backend architecture for the mantenimento calculator",
5
5
  "type": "commonjs",
6
6
  "main": "backend/calculate-model.js",