@jjlmoya/utils-finance 1.45.0 → 1.46.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.
Files changed (37) hide show
  1. package/package.json +1 -1
  2. package/src/entries.ts +2 -1
  3. package/src/tests/locale_completeness.test.ts +2 -2
  4. package/src/tests/mfe_assets_contract.test.ts +48 -0
  5. package/src/tests/tool_validation.test.ts +2 -2
  6. package/src/tool/renterInsuranceCoverageGapCalculator/bibliography.astro +11 -0
  7. package/src/tool/renterInsuranceCoverageGapCalculator/bibliography.ts +6 -0
  8. package/src/tool/renterInsuranceCoverageGapCalculator/component.astro +80 -0
  9. package/src/tool/renterInsuranceCoverageGapCalculator/controller.ts +102 -0
  10. package/src/tool/renterInsuranceCoverageGapCalculator/currency.test.ts +17 -0
  11. package/src/tool/renterInsuranceCoverageGapCalculator/currency.ts +69 -0
  12. package/src/tool/renterInsuranceCoverageGapCalculator/dom-views.ts +68 -0
  13. package/src/tool/renterInsuranceCoverageGapCalculator/entry.ts +28 -0
  14. package/src/tool/renterInsuranceCoverageGapCalculator/evaluator.ts +17 -0
  15. package/src/tool/renterInsuranceCoverageGapCalculator/i18n/de.ts +40 -0
  16. package/src/tool/renterInsuranceCoverageGapCalculator/i18n/en.ts +98 -0
  17. package/src/tool/renterInsuranceCoverageGapCalculator/i18n/es.ts +38 -0
  18. package/src/tool/renterInsuranceCoverageGapCalculator/i18n/fr.ts +38 -0
  19. package/src/tool/renterInsuranceCoverageGapCalculator/i18n/id.ts +38 -0
  20. package/src/tool/renterInsuranceCoverageGapCalculator/i18n/it.ts +38 -0
  21. package/src/tool/renterInsuranceCoverageGapCalculator/i18n/ja.ts +38 -0
  22. package/src/tool/renterInsuranceCoverageGapCalculator/i18n/ko.ts +38 -0
  23. package/src/tool/renterInsuranceCoverageGapCalculator/i18n/nl.ts +38 -0
  24. package/src/tool/renterInsuranceCoverageGapCalculator/i18n/pl.ts +38 -0
  25. package/src/tool/renterInsuranceCoverageGapCalculator/i18n/pt.ts +38 -0
  26. package/src/tool/renterInsuranceCoverageGapCalculator/i18n/ru.ts +38 -0
  27. package/src/tool/renterInsuranceCoverageGapCalculator/i18n/sv.ts +38 -0
  28. package/src/tool/renterInsuranceCoverageGapCalculator/i18n/tr.ts +38 -0
  29. package/src/tool/renterInsuranceCoverageGapCalculator/i18n/zh.ts +38 -0
  30. package/src/tool/renterInsuranceCoverageGapCalculator/index.ts +11 -0
  31. package/src/tool/renterInsuranceCoverageGapCalculator/logic.test.ts +52 -0
  32. package/src/tool/renterInsuranceCoverageGapCalculator/logic.ts +108 -0
  33. package/src/tool/renterInsuranceCoverageGapCalculator/renter-insurance-coverage-gap-calculator.css +462 -0
  34. package/src/tool/renterInsuranceCoverageGapCalculator/seo.astro +11 -0
  35. package/src/tool/renterInsuranceCoverageGapCalculator/storage.ts +23 -0
  36. package/src/tool/renterInsuranceCoverageGapCalculator/ui.ts +48 -0
  37. package/src/tools.ts +2 -1
@@ -0,0 +1,38 @@
1
+ import type { ToolLocaleContent } from '../../../types';
2
+ import { bibliography } from '../bibliography';
3
+ import type { RenterInsuranceCoverageGapCalculatorUI } from '../ui';
4
+ export const ui: RenterInsuranceCoverageGapCalculatorUI = {
5
+ labelPolicyLimit: 'Limite do recheio segurado', labelDeductible: 'Franquia', labelSampleLoss: 'Perda ilustrativa', labelCurrency: 'Moeda', labelApproximateRates: 'A conversão usa fatores aproximados de planeamento, não taxas de câmbio em tempo real.', labelPolicyBoundary: 'Limite da apólice', labelSetLimit: 'Defina o limite', labelPolicyBoundaryHint: 'Use os valores do recheio indicados na apólice e teste depois uma possível perda.', labelInventory: 'Inventário de reposição', labelInventoryHint: 'Estime por categoria quanto custaria substituir tudo hoje.', labelPolicyLimitHelp: 'Use o limite do recheio da apólice, não o limite do edifício.', labelDeductibleHelp: 'Introduza a franquia aplicável a um sinistro do recheio.', labelSampleLossHelp: 'Serve apenas para mostrar um cenário de perda ilustrativo depois de encontrar a lacuna.', labelTotalContents: 'Valor estimado do recheio', labelCoverageGap: 'Possível lacuna de cobertura', labelCoverageRatio: 'Rácio de cobertura estimado', labelEstimatedPayout: 'Indemnização ilustrativa', labelOutOfPocket: 'Custo próprio ilustrativo', labelStatus: 'Leitura da cobertura', labelStatusSafe: 'O limite alcança o inventário', labelStatusReview: 'Reveja uma pequena lacuna', labelStatusExposed: 'O recheio está subsegurado', labelStatusSafeText: 'O limite de recheio introduzido é pelo menos tão alto como o inventário de reposição. Verifique separadamente os limites de objetos especiais.', labelStatusReviewText: 'O limite está perto do inventário, mas resta uma pequena lacuna. Reveja objetos caros e compras recentes.', labelStatusExposedText: 'O limite está abaixo do inventário. Reveja a apólice antes de confiar nela para uma perda total.', labelLargestCategories: 'Maiores categorias do inventário', labelCoverageLine: 'Limite da apólice', labelReplacementValue: 'Valor de reposição', labelLimitMarker: 'Marcador do limite', labelLossScenario: 'Cenário de perda', labelIllustrativePayout: 'Apenas ilustrativo: uma cláusula proporcional pode não se aplicar à sua apólice.', labelReset: 'Repor exemplo', labelCopy: 'Copiar resumo', labelCopied: 'Resumo copiado', labelCopyFailed: 'Copie o resultado manualmente', labelInvalidInput: 'Verifique o valor destacado', labelMethodTitle: 'Como funciona esta estimativa', labelMethodText: 'A ferramenta soma as estimativas por categoria e compara-as com o limite do recheio. A lacuna é a diferença positiva entre esses valores. A indemnização ilustrativa aplica o rácio de cobertura à perda e subtrai a franquia; é um modelo de planeamento, não uma previsão de liquidação. A apólice pode ter limites por objeto, regras de valor em novo, exclusões, cobertura a primeiro risco ou outro tratamento do subseguro.', labelPrivacyNote: 'O seu inventário fica neste navegador. Não é necessária conta, transferência de ficheiros ou operação no servidor.', labelAmountPlaceholder: '0,00', labelCategoryFurniture: 'Mobiliário e decoração', labelCategoryElectronics: 'Eletrónica', labelCategoryClothing: 'Roupa e calçado', labelCategoryKitchen: 'Cozinha e eletrodomésticos', labelCategoryPersonal: 'Objetos pessoais', labelCategoryOther: 'Outro recheio',
6
+ };
7
+ const slug = 'calculadora-lacuna-cobertura-seguro-inquilino';
8
+ const title = 'Calculadora da lacuna de cobertura do seguro de inquilino';
9
+ const description = 'Estime se o limite do recheio do seu seguro de inquilino alcança o valor de reposição dos seus bens. Compare inventário, limite da apólice, franquia e perda ilustrativa no navegador.';
10
+ const faq = [
11
+ { question: 'O que mede esta calculadora de cobertura para inquilinos?', answer: 'Soma os valores de reposição introduzidos para o recheio da casa e compara o total com o limite da apólice. A diferença é uma estimativa de planeamento do valor que pode ficar fora do limite.' },
12
+ { question: 'Que valor devo usar no inventário?', answer: 'Use custos atuais e realistas de reposição para mobiliário, eletrónica, roupa, eletrodomésticos e objetos pessoais. Confirme se a apólice exige valor em novo ou outra base de avaliação.' },
13
+ { question: 'O resultado prova que estou subsegurado?', answer: 'Não. O resultado depende do inventário, das condições da apólice e das hipóteses de avaliação. Sublimites, exclusões e cláusulas de subseguro podem alterar um sinistro.' },
14
+ { question: 'Por que aparece uma indemnização ilustrativa?', answer: 'Mostra o resultado de um modelo proporcional simples para a perda indicada, depois da franquia. Torna a lacuna concreta, mas não prevê o pagamento real da seguradora.' },
15
+ { question: 'Devo incluir instalações do senhorio?', answer: 'Inclua apenas o que pertence à sua cobertura de recheio segundo a apólice. Responsabilidade civil, edifício, instalações e recheio podem ser secções separadas.' },
16
+ ];
17
+ const howTo = [
18
+ { name: 'Crie um inventário de reposição', text: 'Percorra cada divisão e introduza o custo atual de substituir o recheio. Agrupe os bens nas seis categorias para manter visíveis os maiores valores.' },
19
+ { name: 'Introduza o limite do recheio', text: 'Copie a soma segura do recheio das condições da apólice ou da proposta. Não use o limite do edifício nem da responsabilidade civil.' },
20
+ { name: 'Adicione a franquia e teste uma perda', text: 'Introduza a franquia para sinistros do recheio e um valor de perda para ver um cenário ilustrativo de custo próprio.' },
21
+ { name: 'Reveja o limite de decisão', text: 'Se surgir uma lacuna, reveja bens caros, sublimites, valores e compras recentes com a seguradora ou o mediador.' },
22
+ ];
23
+ export const content: ToolLocaleContent<RenterInsuranceCoverageGapCalculatorUI> = { slug, title, description, ui, seo: [
24
+ { type: 'title', text: 'Compare o limite com o valor dos seus bens', level: 2 },
25
+ { type: 'paragraph', html: '<p>O seguro do recheio de uma casa arrendada é facilmente subestimado quando o limite é escolhido como um número redondo em vez de ser calculado divisão a divisão. Esta calculadora transforma as suas estimativas de reposição numa fronteira visível de cobertura.</p>' },
26
+ { type: 'title', text: 'O que reunir antes de calcular', level: 2 },
27
+ { type: 'list', items: ['Uma estimativa de reposição para mobiliário, eletrónica, roupa, eletrodomésticos e objetos pessoais.', 'O limite do recheio da apólice, separado do edifício e da responsabilidade civil.', 'A franquia aplicável a um sinistro do recheio.', 'Uma perda realista se quiser explorar a indemnização ilustrativa.'] },
28
+ { type: 'title', text: 'Como ler a lacuna de cobertura', level: 2 },
29
+ { type: 'paragraph', html: '<p>A lacuna é o valor do recheio acima do limite. Uma lacuna zero significa que os totais coincidem segundo as suas hipóteses, mas não confirma que todos os bens estejam cobertos. Uma lacuna positiva é um convite a verificar a avaliação, os sublimites, as exclusões e qualquer cláusula de subseguro.</p>' },
30
+ { type: 'table', headers: ['Sinal', 'O que indica', 'Próxima ação útil'], rows: [['O limite alcança o inventário', 'O limite introduzido alcança pelo menos o total de reposição.', 'Verifique bens valiosos, sublimites e exclusões.'], ['Reveja uma pequena lacuna', 'O limite está próximo, mas compras recentes ou arredondamentos podem contar.', 'Reveja as categorias maiores e atualize a apólice.'], ['O recheio está subsegurado', 'O limite está claramente abaixo do total de reposição.', 'Peça uma proposta revista ou confirme a regra de subseguro.']] },
31
+ { type: 'title', text: 'Limites da estimativa', level: 2 },
32
+ { type: 'paragraph', html: '<p>Esta ferramenta não lê uma apólice, decide se um dano está coberto nem calcula uma liquidação vinculativa. O cenário usa um modelo proporcional simples e subtrai a franquia. As apólices reais podem ter cláusulas, limites, regras de avaliação, exclusões ou coberturas a primeiro risco diferentes.</p>' },
33
+ { type: 'tip', title: 'Sugestão de prova', html: '<p>Guarde faturas, fotografias e um inventário datado dos bens de maior valor. O Financial Ombudsman Service explica por que não calcular o custo total de reposição pode causar subseguro; a lei espanhola do contrato de seguro define a soma segura como indemnização máxima e prevê uma regra proporcional salvo indicação diferente da apólice.</p>' },
34
+ ], faq, bibliography, howTo, schemas: [
35
+ { '@context': 'https://schema.org', '@type': 'FAQPage', mainEntity: faq.map((item) => ({ '@type': 'Question', name: item.question, acceptedAnswer: { '@type': 'Answer', text: item.answer } })) },
36
+ { '@context': 'https://schema.org', '@type': 'HowTo', name: title, step: howTo.map((step, index) => ({ '@type': 'HowToStep', position: index + 1, name: step.name, text: step.text })) },
37
+ { '@context': 'https://schema.org', '@type': 'SoftwareApplication', name: title, description, applicationCategory: 'FinanceApplication', operatingSystem: 'All', offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' } },
38
+ ] };
@@ -0,0 +1,38 @@
1
+ import type { ToolLocaleContent } from '../../../types';
2
+ import { bibliography } from '../bibliography';
3
+ import type { RenterInsuranceCoverageGapCalculatorUI } from '../ui';
4
+ export const ui: RenterInsuranceCoverageGapCalculatorUI = {
5
+ labelPolicyLimit: 'Лимит страхования имущества', labelDeductible: 'Франшиза', labelSampleLoss: 'Примерный ущерб', labelCurrency: 'Валюта', labelApproximateRates: 'Конвертация использует примерные коэффициенты для планирования, а не текущие курсы.', labelPolicyBoundary: 'Граница полиса', labelSetLimit: 'Задайте лимит', labelPolicyBoundaryHint: 'Используйте сумму имущества из полиса, а затем проверьте возможный ущерб.', labelInventory: 'Опись стоимости замены', labelInventoryHint: 'Оцените по категориям, сколько сегодня стоила бы замена всего имущества.', labelPolicyLimitHelp: 'Укажите лимит имущества из полиса, а не лимит здания.', labelDeductibleHelp: 'Введите франшизу, которая применяется к требованию по имуществу.', labelSampleLossHelp: 'Нужно только для показа примерного сценария после обнаружения нехватки покрытия.', labelTotalContents: 'Оценочная стоимость имущества', labelCoverageGap: 'Возможная нехватка покрытия', labelCoverageRatio: 'Расчётная доля покрытия', labelEstimatedPayout: 'Примерная выплата', labelOutOfPocket: 'Примерные расходы из своего кармана', labelStatus: 'Оценка покрытия', labelStatusSafe: 'Лимит покрывает опись', labelStatusReview: 'Проверьте небольшую нехватку', labelStatusExposed: 'Имущество застраховано недостаточно', labelStatusSafeText: 'Введённый лимит имущества не ниже стоимости замены по описи. Отдельно проверьте лимиты для особых предметов.', labelStatusReviewText: 'Лимит близок к стоимости описи, но небольшая нехватка остаётся. Перепроверьте дорогие вещи и недавние покупки.', labelStatusExposedText: 'Лимит ниже стоимости описи. Изучите полис, прежде чем полагаться на него при полной утрате.', labelLargestCategories: 'Крупнейшие категории описи', labelCoverageLine: 'Лимит полиса', labelReplacementValue: 'Стоимость замены', labelLimitMarker: 'Метка лимита', labelLossScenario: 'Сценарий ущерба', labelIllustrativePayout: 'Только для иллюстрации: пропорциональное правило может не применяться к вашему полису.', labelReset: 'Сбросить пример', labelCopy: 'Скопировать сводку', labelCopied: 'Сводка скопирована', labelCopyFailed: 'Скопируйте результат вручную', labelInvalidInput: 'Проверьте выделенное значение', labelMethodTitle: 'Как работает оценка', labelMethodText: 'Инструмент складывает оценки по категориям и сравнивает их с лимитом имущества. Нехватка является положительной разницей между этими значениями. Примерная выплата применяет долю покрытия к сценарию ущерба, а затем вычитает франшизу. Это модель для планирования, а не прогноз урегулирования. В полисе могут быть лимиты на предметы, правила новой стоимости, исключения, покрытие по первому риску или другой порядок расчёта недострахования.', labelPrivacyNote: 'Опись остаётся в этом браузере. Учётная запись, загрузка и расчёт на сервере не нужны.', labelAmountPlaceholder: '0,00', labelCategoryFurniture: 'Мебель и декор', labelCategoryElectronics: 'Электроника', labelCategoryClothing: 'Одежда и обувь', labelCategoryKitchen: 'Кухня и техника', labelCategoryPersonal: 'Личные вещи', labelCategoryOther: 'Прочее имущество',
6
+ };
7
+ const slug = 'kalkulyator-nedostayushchego-pokrytiya-strakhovaniya-arendatora';
8
+ const title = 'Калькулятор нехватки покрытия страхования имущества арендатора';
9
+ const description = 'Оцените, покрывает ли лимит имущества в страховке арендатора стоимость замены вещей. Сравните опись, лимит полиса, франшизу и примерный ущерб в браузере.';
10
+ const faq = [
11
+ { question: 'Что измеряет этот калькулятор страхования арендатора?', answer: 'Он складывает введённую стоимость замены имущества и сравнивает её с лимитом полиса. Разница показывает оценочную сумму, которая может выходить за пределы покрытия.' },
12
+ { question: 'Какую стоимость указать в описи?', answer: 'Используйте реалистичную текущую стоимость замены мебели, электроники, одежды, техники и личных вещей. Проверьте, какую основу оценки требует полис.' },
13
+ { question: 'Доказывает ли результат недострахование?', answer: 'Нет. Результат зависит от описи, условий полиса и допущений о стоимости. Подлимиты, исключения и оговорки о недостраховании могут изменить выплату.' },
14
+ { question: 'Зачем калькулятор показывает примерную выплату?', answer: 'Она показывает результат простой пропорциональной модели для указанного ущерба после вычета франшизы. Это не прогноз фактической выплаты страховщика.' },
15
+ { question: 'Нужно ли включать оборудование владельца?', answer: 'Включайте только то, что относится к вашему страхованию имущества по полису. Ответственность, здание, встроенные элементы и имущество могут быть разными разделами.' },
16
+ ];
17
+ const howTo = [
18
+ { name: 'Составьте опись стоимости замены', text: 'Пройдите по каждой комнате и укажите сегодняшнюю стоимость замены имущества. Разнесите вещи по шести категориям, чтобы видеть самые крупные суммы.' },
19
+ { name: 'Введите лимит имущества', text: 'Перенесите страховую сумму имущества из полиса или расчёта. Не используйте лимит здания или ответственности.' },
20
+ { name: 'Добавьте франшизу и ущерб', text: 'Укажите франшизу по требованиям об имуществе и примерную сумму ущерба, чтобы увидеть возможные расходы из своего кармана.' },
21
+ { name: 'Проверьте границу решения', text: 'Если появилась нехватка, обсудите с страховщиком или брокером дорогие вещи, подлимиты, ценности и недавние покупки.' },
22
+ ];
23
+ export const content: ToolLocaleContent<RenterInsuranceCoverageGapCalculatorUI> = { slug, title, description, ui, seo: [
24
+ { type: 'title', text: 'Сравните лимит со стоимостью своих вещей', level: 2 },
25
+ { type: 'paragraph', html: '<p>Страхование имущества арендатора легко недооценить, если выбрать круглую сумму вместо описи по комнатам. Калькулятор превращает ваши оценки стоимости замены в видимую границу покрытия до наступления ущерба.</p>' },
26
+ { type: 'title', text: 'Что собрать перед расчётом', level: 2 },
27
+ { type: 'list', items: ['Оценку замены мебели, электроники, одежды, техники и личных вещей.', 'Лимит имущества из полиса отдельно от здания и ответственности.', 'Франшизу, применяемую к требованию по имуществу.', 'Реалистичный пример ущерба для проверки примерной выплаты.'] },
28
+ { type: 'title', text: 'Как читать нехватку покрытия', level: 2 },
29
+ { type: 'paragraph', html: '<p>Нехватка представляет стоимость имущества сверх лимита. Нулевая разница означает совпадение итогов при ваших допущениях, но не подтверждает покрытие каждого предмета. Положительная разница подсказывает проверить оценку, подлимиты, исключения и оговорку о недостраховании.</p>' },
30
+ { type: 'table', headers: ['Сигнал', 'Что он означает', 'Полезное действие'], rows: [['Лимит покрывает опись', 'Введённый лимит не ниже общей стоимости замены.', 'Проверьте ценности, подлимиты и исключения.'], ['Проверьте небольшую нехватку', 'Лимит близок, но новые покупки или округление могут иметь значение.', 'Пересмотрите крупнейшие категории и обновите полис.'], ['Имущество недостраховано', 'Лимит заметно ниже стоимости замены.', 'Запросите новый расчёт или уточните правило недострахования.']] },
31
+ { type: 'title', text: 'Ограничения оценки', level: 2 },
32
+ { type: 'paragraph', html: '<p>Инструмент не читает полис, не решает, покрывается ли ущерб, и не рассчитывает обязательную выплату. Сценарий использует простую пропорциональную модель и вычитает франшизу. В реальном договоре могут быть другие условия, лимиты, правила оценки и исключения.</p>' },
33
+ { type: 'tip', title: 'Совет по подтверждениям', html: '<p>Храните чеки, фотографии и датированную опись дорогих вещей. Financial Ombudsman Service объясняет, почему отсутствие полного расчёта стоимости замены приводит к недострахованию; испанский закон о договоре страхования считает страховую сумму максимальным возмещением и предусматривает пропорциональное правило, если договор не устанавливает иное.</p>' },
34
+ ], faq, bibliography, howTo, schemas: [
35
+ { '@context': 'https://schema.org', '@type': 'FAQPage', mainEntity: faq.map((item) => ({ '@type': 'Question', name: item.question, acceptedAnswer: { '@type': 'Answer', text: item.answer } })) },
36
+ { '@context': 'https://schema.org', '@type': 'HowTo', name: title, step: howTo.map((step, index) => ({ '@type': 'HowToStep', position: index + 1, name: step.name, text: step.text })) },
37
+ { '@context': 'https://schema.org', '@type': 'SoftwareApplication', name: title, description, applicationCategory: 'FinanceApplication', operatingSystem: 'All', offers: { '@type': 'Offer', price: '0', priceCurrency: 'RUB' } },
38
+ ] };
@@ -0,0 +1,38 @@
1
+ import type { ToolLocaleContent } from '../../../types';
2
+ import { bibliography } from '../bibliography';
3
+ import type { RenterInsuranceCoverageGapCalculatorUI } from '../ui';
4
+ export const ui: RenterInsuranceCoverageGapCalculatorUI = {
5
+ labelPolicyLimit: 'Försäkringsbelopp för lösöre', labelDeductible: 'Självrisk', labelSampleLoss: 'Exempel på skada', labelCurrency: 'Valuta', labelApproximateRates: 'Omräkningen använder ungefärliga planeringsfaktorer, inte aktuella växelkurser.', labelPolicyBoundary: 'Försäkringsgräns', labelSetLimit: 'Ange gränsen', labelPolicyBoundaryHint: 'Använd lösöresbeloppet i försäkringen och testa sedan en möjlig skada.', labelInventory: 'Ersättningsinventering', labelInventoryHint: 'Uppskatta per kategori vad det skulle kosta att ersätta allt i dag.', labelPolicyLimitHelp: 'Använd lösöresgränsen i försäkringen, inte byggnadens gräns.', labelDeductibleHelp: 'Ange självrisken som gäller för ett lösöreskrav.', labelSampleLossHelp: 'Används bara för att visa ett illustrativt skadefall efter att gapet hittats.', labelTotalContents: 'Uppskattat lösöresvärde', labelCoverageGap: 'Möjligt försäkringsgap', labelCoverageRatio: 'Uppskattad täckningsgrad', labelEstimatedPayout: 'Illustrativ ersättning', labelOutOfPocket: 'Illustrativ egen kostnad', labelStatus: 'Täckningsbedömning', labelStatusSafe: 'Gränsen täcker inventeringen', labelStatusReview: 'Kontrollera ett litet gap', labelStatusExposed: 'Lösöret är underförsäkrat', labelStatusSafeText: 'Den angivna lösöresgränsen är minst lika hög som ersättningsinventeringen. Kontrollera separata gränser för särskilda föremål.', labelStatusReviewText: 'Gränsen ligger nära inventeringen, men ett litet gap finns kvar. Kontrollera dyra föremål och nya inköp.', labelStatusExposedText: 'Gränsen ligger under inventeringsvärdet. Läs försäkringen innan du litar på den vid en totalskada.', labelLargestCategories: 'Största inventeringskategorierna', labelCoverageLine: 'Försäkringsgräns', labelReplacementValue: 'Ersättningsvärde', labelLimitMarker: 'Gränsmarkör', labelLossScenario: 'Skadescenario', labelIllustrativePayout: 'Endast illustrativt: en proportionell klausul kanske inte gäller för din försäkring.', labelReset: 'Återställ exempel', labelCopy: 'Kopiera sammanfattning', labelCopied: 'Sammanfattningen kopierad', labelCopyFailed: 'Kopiera resultatet manuellt', labelInvalidInput: 'Kontrollera det markerade värdet', labelMethodTitle: 'Så fungerar uppskattningen', labelMethodText: 'Verktyget summerar dina kategoribelopp och jämför dem med lösöresgränsen. Gapet är den positiva skillnaden mellan värdena. Den illustrativa ersättningen tillämpar täckningsgraden på skadescenariot och drar sedan av självrisken. Det är en planeringsmodell, inte en prognos för skadereglering. Försäkringen kan ha föremålsgränser, regler för nyvärde, undantag, förstahandsrisk eller annan behandling av underförsäkring.', labelPrivacyNote: 'Din inventering stannar i den här webbläsaren. Inget konto, ingen uppladdning och ingen serverberäkning krävs.', labelAmountPlaceholder: '0,00', labelCategoryFurniture: 'Möbler och inredning', labelCategoryElectronics: 'Elektronik', labelCategoryClothing: 'Kläder och skor', labelCategoryKitchen: 'Kök och apparater', labelCategoryPersonal: 'Personliga saker', labelCategoryOther: 'Övrigt lösöre',
6
+ };
7
+ const slug = 'kalkylator-underforsakring-hemforsakring-hyresgast';
8
+ const title = 'Kalkylator för försäkringsgap i hyresgästens hemförsäkring';
9
+ const description = 'Uppskatta om lösöresgränsen i din hemförsäkring för hyresgäster når ersättningsvärdet av dina saker. Jämför inventering, gräns, självrisk och exempel på skada i webbläsaren.';
10
+ const faq = [
11
+ { question: 'Vad mäter den här kalkylatorn för hyresgästers försäkring?', answer: 'Den summerar de ersättningsvärden du anger för hushållets lösöre och jämför summan med försäkringsgränsen. Skillnaden är en planeringsuppskattning av belopp som kan hamna utanför gränsen.' },
12
+ { question: 'Vilket värde ska jag använda i inventeringen?', answer: 'Använd realistiska aktuella ersättningskostnader för möbler, elektronik, kläder, apparater och personliga saker. Kontrollera vilken värderingsgrund försäkringen kräver.' },
13
+ { question: 'Bevisar resultatet att jag är underförsäkrad?', answer: 'Nej. Resultatet beror på inventering, villkor och värderingsantaganden. Föremålsgränser, undantag och underförsäkringsklausuler kan ändra en skada.' },
14
+ { question: 'Varför visar kalkylatorn en illustrativ ersättning?', answer: 'Den visar vad en enkel proportionell modell skulle ge för den angivna skadan efter självrisken. Den förutspår inte vad försäkringsbolaget betalar.' },
15
+ { question: 'Ska hyresvärdens fasta utrustning räknas med?', answer: 'Ta bara med sådant som enligt villkoren hör till din lösöresförsäkring. Ansvar, byggnad, fasta delar och lösöre kan vara separata delar.' },
16
+ ];
17
+ const howTo = [
18
+ { name: 'Skapa en ersättningsinventering', text: 'Gå igenom varje rum och ange vad det skulle kosta att ersätta lösöret i dag. Gruppera saker i sex kategorier så att de största värdena syns.' },
19
+ { name: 'Ange lösöresgränsen', text: 'Kopiera försäkringsbeloppet för lösöre från försäkringsbrevet eller offerten. Använd inte byggnadens eller ansvarsförsäkringens gräns.' },
20
+ { name: 'Lägg till självrisk och skada', text: 'Ange självrisken för lösöreskador och ett exempel på skada för att se ett illustrativt scenario med egen kostnad.' },
21
+ { name: 'Granska beslutsgränsen', text: 'Om ett gap syns, kontrollera dyra föremål, föremålsgränser, värdesaker och nya inköp med försäkringsbolaget eller rådgivaren.' },
22
+ ];
23
+ export const content: ToolLocaleContent<RenterInsuranceCoverageGapCalculatorUI> = { slug, title, description, ui, seo: [
24
+ { type: 'title', text: 'Jämför gränsen med värdet av dina saker', level: 2 },
25
+ { type: 'paragraph', html: '<p>Lösöresförsäkring för en hyresbostad underskattas lätt när gränsen väljs som ett runt tal i stället för från en inventering rum för rum. Kalkylatorn gör dina egna ersättningsuppskattningar till en synlig täckningsgräns innan en skada inträffar.</p>' },
26
+ { type: 'title', text: 'Samla detta före beräkningen', level: 2 },
27
+ { type: 'list', items: ['Ersättningsvärden för möbler, elektronik, kläder, apparater och personliga saker.', 'Lösöresgränsen från försäkringen, skild från byggnad och ansvar.', 'Självrisken som gäller för en lösöresskada.', 'En realistisk exempel-skada om du vill undersöka den illustrativa ersättningen.'] },
28
+ { type: 'title', text: 'Så läser du försäkringsgapet', level: 2 },
29
+ { type: 'paragraph', html: '<p>Gapet är lösöresvärdet över gränsen. Ett nollgap betyder att totalsummorna möts enligt dina antaganden, men bekräftar inte att varje sak är täckt. Ett positivt gap är en signal att kontrollera värdering, föremålsgränser, undantag och eventuell underförsäkringsklausul.</p>' },
30
+ { type: 'table', headers: ['Signal', 'Vad den betyder', 'Nästa användbara steg'], rows: [['Gränsen täcker inventeringen', 'Den angivna gränsen når minst den totala ersättningskostnaden.', 'Kontrollera värdesaker, föremålsgränser och undantag.'], ['Kontrollera ett litet gap', 'Gränsen ligger nära, men nya inköp eller avrundning kan spela roll.', 'Se över de största kategorierna och uppdatera försäkringen.'], ['Lösöret är underförsäkrat', 'Gränsen ligger tydligt under ersättningsvärdet.', 'Be om en ny offert eller klargör underförsäkringsregeln.']] },
31
+ { type: 'title', text: 'Uppskattningens begränsningar', level: 2 },
32
+ { type: 'paragraph', html: '<p>Verktyget läser inte försäkringen, avgör inte om en skada ersätts och räknar inte fram en bindande ersättning. Scenariot använder en enkel proportionell modell och drar av självrisken. Riktiga försäkringar kan ha andra klausuler, värderingsregler, undantag eller förstahandsrisk.</p>' },
33
+ { type: 'tip', title: 'Tips om underlag', html: '<p>Spara kvitton, fotografier och en daterad inventering av värdefulla saker. Financial Ombudsman Service förklarar varför ofullständiga ersättningsberäkningar kan leda till underförsäkring; den spanska försäkringsavtalslagen beskriver försäkringsbeloppet som maximal ersättning och anger en proportionell regel om avtalet inte säger annat.</p>' },
34
+ ], faq, bibliography, howTo, schemas: [
35
+ { '@context': 'https://schema.org', '@type': 'FAQPage', mainEntity: faq.map((item) => ({ '@type': 'Question', name: item.question, acceptedAnswer: { '@type': 'Answer', text: item.answer } })) },
36
+ { '@context': 'https://schema.org', '@type': 'HowTo', name: title, step: howTo.map((step, index) => ({ '@type': 'HowToStep', position: index + 1, name: step.name, text: step.text })) },
37
+ { '@context': 'https://schema.org', '@type': 'SoftwareApplication', name: title, description, applicationCategory: 'FinanceApplication', operatingSystem: 'All', offers: { '@type': 'Offer', price: '0', priceCurrency: 'SEK' } },
38
+ ] };
@@ -0,0 +1,38 @@
1
+ import type { ToolLocaleContent } from '../../../types';
2
+ import { bibliography } from '../bibliography';
3
+ import type { RenterInsuranceCoverageGapCalculatorUI } from '../ui';
4
+ export const ui: RenterInsuranceCoverageGapCalculatorUI = {
5
+ labelPolicyLimit: 'Eşya teminat limiti', labelDeductible: 'Muafiyet', labelSampleLoss: 'Örnek hasar', labelCurrency: 'Para birimi', labelApproximateRates: 'Dönüşüm, canlı kurlar yerine yaklaşık planlama katsayılarını kullanır.', labelPolicyBoundary: 'Poliçe sınırı', labelSetLimit: 'Sınırı belirle', labelPolicyBoundaryHint: 'Poliçenizdeki eşya tutarını kullanın ve ardından olası bir kaybı deneyin.', labelInventory: 'Yenileme envanteri', labelInventoryHint: 'Her kategoride her şeyi bugün yenilemenin maliyetini tahmin edin.', labelPolicyLimitHelp: 'Bina limitini değil, poliçedeki eşya teminat limitini kullanın.', labelDeductibleHelp: 'Eşya hasarı için geçerli muafiyeti girin.', labelSampleLossHelp: 'Yalnızca açık bulunduğunda örnek bir hasar senaryosu göstermek için kullanılır.', labelTotalContents: 'Tahmini eşya değeri', labelCoverageGap: 'Olası teminat açığı', labelCoverageRatio: 'Tahmini teminat oranı', labelEstimatedPayout: 'Örnek tazminat', labelOutOfPocket: 'Örnek cepten ödeme', labelStatus: 'Teminat değerlendirmesi', labelStatusSafe: 'Limit envanteri karşılıyor', labelStatusReview: 'Küçük açığı incele', labelStatusExposed: 'Eşyalar eksik sigortalı', labelStatusSafeText: 'Girdiğiniz eşya limiti en az yenileme envanteri kadar. Özel eşyalar için ayrı limitleri kontrol edin.', labelStatusReviewText: 'Limit envantere yakın, ancak küçük bir açık var. Pahalı eşyaları ve yeni alışverişleri yeniden kontrol edin.', labelStatusExposedText: 'Limit envanter tahmininin altında. Tam kayıp durumunda poliçeye güvenmeden önce poliçeyi inceleyin.', labelLargestCategories: 'En büyük envanter kategorileri', labelCoverageLine: 'Poliçe limiti', labelReplacementValue: 'Yenileme değeri', labelLimitMarker: 'Limit işareti', labelLossScenario: 'Kayıp senaryosu', labelIllustrativePayout: 'Yalnızca örnektir: oransal tazminat maddesi poliçeniz için geçerli olmayabilir.', labelReset: 'Örneği sıfırla', labelCopy: 'Özeti kopyala', labelCopied: 'Özet kopyalandı', labelCopyFailed: 'Sonucu elle kopyalayın', labelInvalidInput: 'Vurgulanan değeri kontrol edin', labelMethodTitle: 'Bu tahmin nasıl çalışır', labelMethodText: 'Araç kategori tahminlerinizi toplar ve eşya limitiyle karşılaştırır. Açık, bu değerler arasındaki pozitif farktır. Örnek tazminat, teminat oranını kayıp senaryosuna uygular ve muafiyeti çıkarır. Bu bir planlama modelidir, hasar ödemesi tahmini değildir. Poliçenizde eşya alt limitleri, yeni değer kuralları, istisnalar, ilk risk teminatı veya eksik sigorta için farklı bir uygulama bulunabilir.', labelPrivacyNote: 'Envanteriniz bu tarayıcıda kalır. Hesap, yükleme veya sunucu hesabı gerekmez.', labelAmountPlaceholder: '0,00', labelCategoryFurniture: 'Mobilya ve dekorasyon', labelCategoryElectronics: 'Elektronik', labelCategoryClothing: 'Giyim ve ayakkabı', labelCategoryKitchen: 'Mutfak ve cihazlar', labelCategoryPersonal: 'Kişisel eşyalar', labelCategoryOther: 'Diğer eşyalar',
6
+ };
7
+ const slug = 'kiraci-esya-sigortasi-teminat-acigi-hesaplayici';
8
+ const title = 'Kiracı eşya sigortası teminat açığı hesaplayıcısı';
9
+ const description = 'Kiracı sigortanızdaki eşya limitinin eşyalarınızın yenileme değerine ulaşıp ulaşmadığını tahmin edin. Envanteri, poliçe limitini, muafiyeti ve örnek hasarı tarayıcıda karşılaştırın.';
10
+ const faq = [
11
+ { question: 'Bu kiracı sigortası hesaplayıcısı neyi ölçer?', answer: 'Girdiğiniz ev eşyası yenileme değerlerini toplar ve poliçe eşya limitiyle karşılaştırır. Fark, limit dışında kalabilecek tutarın planlama tahminidir.' },
12
+ { question: 'Envanter için hangi değeri kullanmalıyım?', answer: 'Mobilya, elektronik, giyim, cihaz ve kişisel eşyaların güncel ve gerçekçi yenileme maliyetini kullanın. Poliçenizin istediği değerleme temelini kontrol edin.' },
13
+ { question: 'Sonuç eksik sigortayı kanıtlar mı?', answer: 'Hayır. Sonuç envantere, poliçe metnine ve değerleme varsayımlarına bağlıdır. Alt limitler, istisnalar ve eksik sigorta maddeleri hasarı değiştirebilir.' },
14
+ { question: 'Hesaplayıcı neden örnek tazminat gösteriyor?', answer: 'Girdiğiniz kayıp için basit bir oransal modelin muafiyet sonrasındaki sonucunu gösterir. Sigortacının gerçekte ne ödeyeceğini öngörmez.' },
15
+ { question: 'Ev sahibinin sabit eşyalarını eklemeli miyim?', answer: 'Poliçenizde eşya teminatına girenleri ekleyin. Sorumluluk, bina, sabit donanım ve eşyalar ayrı bölümler olabilir.' },
16
+ ];
17
+ const howTo = [
18
+ { name: 'Yenileme envanteri oluşturun', text: 'Her odayı dolaşın ve eşyaları bugün yenilemenin maliyetini girin. En yüksek değerleri görünür tutmak için altı kategori kullanın.' },
19
+ { name: 'Eşya limitini girin', text: 'Poliçe veya teklifteki eşya sigorta bedelini kopyalayın. Bina ya da sorumluluk limitini kullanmayın.' },
20
+ { name: 'Muafiyet ve kaybı ekleyin', text: 'Eşya hasarlarında geçerli muafiyeti ve örnek kayıp tutarını girerek olası cepten ödemeyi görün.' },
21
+ { name: 'Karar sınırını inceleyin', text: 'Açık oluşursa pahalı eşyaları, alt limitleri, değerli eşyaları ve yeni alışverişleri sigortacı veya broker ile kontrol edin.' },
22
+ ];
23
+ export const content: ToolLocaleContent<RenterInsuranceCoverageGapCalculatorUI> = { slug, title, description, ui, seo: [
24
+ { type: 'title', text: 'Limiti eşyalarınızın değeriyle karşılaştırın', level: 2 },
25
+ { type: 'paragraph', html: '<p>Kiralık ev eşyası sigortası, limit oda oda çıkarılan envanter yerine yuvarlak bir tutarla seçildiğinde kolayca yetersiz kalır. Bu hesaplayıcı kendi yenileme tahminlerinizi görünür bir teminat sınırına dönüştürür.</p>' },
26
+ { type: 'title', text: 'Hesaplamadan önce toplanacaklar', level: 2 },
27
+ { type: 'list', items: ['Mobilya, elektronik, giyim, cihaz ve kişisel eşyalar için yenileme tahmini.', 'Bina ve sorumluluktan ayrı poliçe eşya limiti.', 'Eşya hasarında uygulanan muafiyet.', 'Örnek tazminatı incelemek için gerçekçi bir kayıp tutarı.'] },
28
+ { type: 'title', text: 'Teminat açığını nasıl okuyabilirsiniz', level: 2 },
29
+ { type: 'paragraph', html: '<p>Açık, limitin üzerindeki eşya değeridir. Sıfır açık varsayımlarınıza göre iki toplamın buluştuğunu gösterir; her eşyanın teminat altında olduğunu doğrulamaz. Pozitif açık, değerleme temelini, alt limitleri, istisnaları ve eksik sigorta maddesini incelemek için uyarıdır.</p>' },
30
+ { type: 'table', headers: ['İşaret', 'Ne anlatır', 'Sonraki yararlı adım'], rows: [['Limit envanteri karşılıyor', 'Girilen limit en az yenileme toplamına ulaşıyor.', 'Değerli eşyaları, alt limitleri ve istisnaları kontrol edin.'], ['Küçük açığı incele', 'Limit yakın, ancak yeni alışverişler veya yuvarlama önemli olabilir.', 'En büyük kategorileri yeniden kontrol edin ve poliçeyi güncelleyin.'], ['Eşyalar eksik sigortalı', 'Girilen limit yenileme toplamının belirgin biçimde altında.', 'Yeni teklif isteyin veya eksik sigorta uygulamasını netleştirin.']] },
31
+ { type: 'title', text: 'Tahminin sınırları', level: 2 },
32
+ { type: 'paragraph', html: '<p>Bu araç poliçeyi okumaz, hasarın teminat altında olup olmadığına karar vermez ve bağlayıcı bir tazminat hesaplamaz. Senaryo basit bir oransal model kullanır ve muafiyeti çıkarır. Gerçek poliçelerde farklı maddeler, limitler, değerleme kuralları ve istisnalar olabilir.</p>' },
33
+ { type: 'tip', title: 'Belge saklama önerisi', html: '<p>Değerli eşyalar için faturaları, fotoğrafları ve tarihli bir envanteri saklayın. Financial Ombudsman Service, tam yenileme maliyetinin hesaplanmamasının neden eksik sigortaya yol açabildiğini açıklar; İspanya sigorta sözleşmesi kanunu sigorta bedelini azami tazminat olarak tanımlar ve poliçe aksini söylemiyorsa oransal kural öngörür.</p>' },
34
+ ], faq, bibliography, howTo, schemas: [
35
+ { '@context': 'https://schema.org', '@type': 'FAQPage', mainEntity: faq.map((item) => ({ '@type': 'Question', name: item.question, acceptedAnswer: { '@type': 'Answer', text: item.answer } })) },
36
+ { '@context': 'https://schema.org', '@type': 'HowTo', name: title, step: howTo.map((step, index) => ({ '@type': 'HowToStep', position: index + 1, name: step.name, text: step.text })) },
37
+ { '@context': 'https://schema.org', '@type': 'SoftwareApplication', name: title, description, applicationCategory: 'FinanceApplication', operatingSystem: 'All', offers: { '@type': 'Offer', price: '0', priceCurrency: 'TRY' } },
38
+ ] };
@@ -0,0 +1,38 @@
1
+ import type { ToolLocaleContent } from '../../../types';
2
+ import { bibliography } from '../bibliography';
3
+ import type { RenterInsuranceCoverageGapCalculatorUI } from '../ui';
4
+ export const ui: RenterInsuranceCoverageGapCalculatorUI = {
5
+ labelPolicyLimit: '家庭财产保险限额', labelDeductible: '免赔额', labelSampleLoss: '示例损失', labelCurrency: '货币', labelApproximateRates: '换算使用规划用的近似系数,不是实时汇率。', labelPolicyBoundary: '保单边界', labelSetLimit: '设置限额', labelPolicyBoundaryHint: '使用保单中的家庭财产金额,然后测试一次可能的损失。', labelInventory: '重置成本清单', labelInventoryHint: '按类别估算今天重新购置全部物品需要的费用。', labelPolicyLimitHelp: '使用保单中的家庭财产限额,不要使用建筑物限额。', labelDeductibleHelp: '输入家庭财产理赔适用的免赔额。', labelSampleLossHelp: '仅用于在发现缺口后展示示例理赔情景。', labelTotalContents: '估计财产价值', labelCoverageGap: '潜在保障缺口', labelCoverageRatio: '估计保障比例', labelEstimatedPayout: '示例赔付', labelOutOfPocket: '示例自付金额', labelStatus: '保障评估', labelStatusSafe: '限额覆盖清单', labelStatusReview: '检查小额缺口', labelStatusExposed: '财产保障不足', labelStatusSafeText: '输入的财产限额至少达到清单中的重置成本。高价值物品的单项限额仍需单独检查。', labelStatusReviewText: '限额接近清单总额,但仍有小额缺口。请重新检查贵重物品和最近购买的物品。', labelStatusExposedText: '限额低于清单估值。在依靠保单应对全损之前,请先查看具体条款。', labelLargestCategories: '清单中金额最大的类别', labelCoverageLine: '保单限额', labelReplacementValue: '重置价值', labelLimitMarker: '限额标记', labelLossScenario: '损失情景', labelIllustrativePayout: '仅供参考:比例赔付条款不一定适用于您的保单。', labelReset: '重置示例', labelCopy: '复制摘要', labelCopied: '摘要已复制', labelCopyFailed: '请手动复制结果', labelInvalidInput: '检查突出显示的数值', labelMethodTitle: '估算方式', labelMethodText: '工具会汇总各类别估值,并与家庭财产限额比较。缺口是两者之间的正差值。示例赔付将保障比例应用于损失情景,再扣除免赔额。这是规划模型,不是实际理赔金额预测。保单可能包含单项限额、新价值规则、除外责任、第一风险保障或其他不足额保险处理方式。', labelPrivacyNote: '您的清单保留在此浏览器中。不需要账户、上传或服务器计算。', labelAmountPlaceholder: '0.00', labelCategoryFurniture: '家具和装饰', labelCategoryElectronics: '电子产品', labelCategoryClothing: '衣物和鞋类', labelCategoryKitchen: '厨房和家电', labelCategoryPersonal: '个人物品', labelCategoryOther: '其他财产',
6
+ };
7
+ const slug = 'renter-insurance-coverage-gap-calculator';
8
+ const title = '租房者家庭财产保险保障缺口计算器';
9
+ const description = '估算租房者家庭财产保险限额是否达到物品的重置价值。在浏览器中比较清单、保单限额、免赔额和示例损失。';
10
+ const faq = [
11
+ { question: '这个租房保险计算器衡量什么?', answer: '它会汇总您输入的家庭财产重置价值,并与保单限额比较。差额是可能超出保障限额金额的规划估算。' },
12
+ { question: '清单应使用什么价值?', answer: '请使用家具、电子产品、衣物、家电和个人物品当前合理的重置成本,并确认保单要求的新价值或其他估值方式。' },
13
+ { question: '结果能证明保障不足吗?', answer: '不能。结果取决于清单、保单文字和估值假设。单项限额、除外责任、特别约定和不足额保险条款都可能改变理赔结果。' },
14
+ { question: '为什么显示示例赔付?', answer: '它展示简单比例模型对输入损失额计算的结果,并扣除免赔额。这个数字用于理解缺口,不代表保险公司的实际赔付预测。' },
15
+ { question: '需要计入房东的固定设施吗?', answer: '只计入按保单属于您家庭财产保障的物品。责任、建筑物、固定设施和家庭财产可能属于不同保障部分。' },
16
+ ];
17
+ const howTo = [
18
+ { name: '建立重置成本清单', text: '逐个房间查看物品,输入今天重新购置家庭财产的成本。分成六类后,最大金额来源会更清楚。' },
19
+ { name: '输入家庭财产限额', text: '从保单明细或报价单中抄录家庭财产保险金额,不要使用建筑物或责任限额。' },
20
+ { name: '加入免赔额和损失', text: '输入家庭财产理赔的免赔额和示例损失金额,查看示例自付情景。' },
21
+ { name: '检查决策边界', text: '如果出现缺口,请与保险公司或经纪人核对贵重物品、单项限额、价值物品和最近购买的物品。' },
22
+ ];
23
+ export const content: ToolLocaleContent<RenterInsuranceCoverageGapCalculatorUI> = { slug, title, description, ui, seo: [
24
+ { type: 'title', text: '将保险限额与物品价值比较', level: 2 },
25
+ { type: 'paragraph', html: '<p>租房者家庭财产保险在只选一个整数限额、没有按房间建立清单时容易不足。这款计算器将您自己的重置成本估算变成可见的保障边界,帮助您在需要理赔前发现缺口。</p>' },
26
+ { type: 'title', text: '计算前准备什么', level: 2 },
27
+ { type: 'list', items: ['家具、电子产品、衣物、家电和个人物品的重置成本估算。', '保单中的家庭财产限额,并与建筑物和责任保障分开。', '家庭财产理赔适用的免赔额。', '用于探索示例赔付的现实损失金额。'] },
28
+ { type: 'title', text: '如何理解保障缺口', level: 2 },
29
+ { type: 'paragraph', html: '<p>缺口是超过限额的家庭财产价值。缺口为零表示按您的假设两个总额相符,但不能确认每件物品都受保障。正数缺口提示您检查估值方式、高价物品单项限额、除外责任和不足额保险条款。</p>' },
30
+ { type: 'table', headers: ['提示', '含义', '下一步'], rows: [['限额覆盖清单', '输入的限额至少达到重置成本总额。', '检查价值物品、单项限额和除外责任。'], ['检查小额缺口', '限额接近总额,但新购买或四舍五入可能有影响。', '重新查看最大类别并更新保单。'], ['财产保障不足', '输入限额明显低于重置成本总额。', '索取新报价或确认不足额保险的处理方式。']] },
31
+ { type: 'title', text: '估算的局限', level: 2 },
32
+ { type: 'paragraph', html: '<p>此工具不会读取保单,不判断损失是否承保,也不会计算有约束力的理赔金额。示例情景使用简单比例模型并扣除免赔额。真实保单可能包含不同的条款、限额、估值规则、除外责任或第一风险保障。</p>' },
33
+ { type: 'tip', title: '留存证据的建议', html: '<p>为高价值物品保存收据、照片和带日期的清单。Financial Ombudsman Service 解释了没有计算完整重置成本为何会造成保障不足;西班牙保险合同法将保险金额视为最高赔偿额,除非保单另有约定,还规定了比例规则。</p>' },
34
+ ], faq, bibliography, howTo, schemas: [
35
+ { '@context': 'https://schema.org', '@type': 'FAQPage', mainEntity: faq.map((item) => ({ '@type': 'Question', name: item.question, acceptedAnswer: { '@type': 'Answer', text: item.answer } })) },
36
+ { '@context': 'https://schema.org', '@type': 'HowTo', name: title, step: howTo.map((step, index) => ({ '@type': 'HowToStep', position: index + 1, name: step.name, text: step.text })) },
37
+ { '@context': 'https://schema.org', '@type': 'SoftwareApplication', name: title, description, applicationCategory: 'FinanceApplication', operatingSystem: 'All', offers: { '@type': 'Offer', price: '0', priceCurrency: 'CNY' } },
38
+ ] };
@@ -0,0 +1,11 @@
1
+ import type { ToolDefinition } from '../../types';
2
+ import { renterInsuranceCoverageGapCalculator } from './entry';
3
+
4
+ export * from './entry';
5
+
6
+ export const RENTER_INSURANCE_COVERAGE_GAP_CALCULATOR_TOOL: ToolDefinition = {
7
+ entry: renterInsuranceCoverageGapCalculator,
8
+ Component: () => import('./component.astro'),
9
+ SEOComponent: () => import('./seo.astro'),
10
+ BibliographyComponent: () => import('./bibliography.astro'),
11
+ };
@@ -0,0 +1,52 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { calculateRenterInsuranceCoverageGap, validateRenterInsuranceInput, type RenterInsuranceInput } from './logic';
3
+
4
+ const baseInput: RenterInsuranceInput = {
5
+ currency: 'EUR',
6
+ policyLimit: 18000,
7
+ deductible: 250,
8
+ sampleLoss: 5000,
9
+ inventory: [
10
+ { category: 'furniture', value: 7000 },
11
+ { category: 'electronics', value: 6000 },
12
+ { category: 'clothing', value: 2500 },
13
+ { category: 'kitchen', value: 1500 },
14
+ { category: 'personal', value: 1000 },
15
+ { category: 'other', value: 1000 },
16
+ ],
17
+ };
18
+
19
+ describe('calculateRenterInsuranceCoverageGap', () => {
20
+ it('finds the coverage gap and ranks the largest inventory categories', () => {
21
+ const result = calculateRenterInsuranceCoverageGap(baseInput);
22
+ expect(result).toMatchObject({ totalContents: 19000, coverageGap: 1000, coverageRatio: 18000 / 19000, uncoveredPercent: 5.26, status: 'review' });
23
+ expect(result?.largestCategories.map((item) => item.category)).toEqual(['furniture', 'electronics', 'clothing']);
24
+ });
25
+
26
+ it('estimates the illustrative payout after proportional cover and deductible', () => {
27
+ const result = calculateRenterInsuranceCoverageGap(baseInput);
28
+ expect(result).toMatchObject({ estimatedPayout: 4486.84, estimatedOutOfPocket: 513.16 });
29
+ });
30
+
31
+ it('marks a fully covered inventory as safe', () => {
32
+ const result = calculateRenterInsuranceCoverageGap({ ...baseInput, policyLimit: 25000 });
33
+ expect(result).toMatchObject({ coverageGap: 0, coverageRatio: 1, uncoveredPercent: 0, status: 'safe' });
34
+ });
35
+
36
+ it('marks a large gap as exposed', () => {
37
+ const result = calculateRenterInsuranceCoverageGap({ ...baseInput, policyLimit: 10000 });
38
+ expect(result?.status).toBe('exposed');
39
+ expect(result?.coverageGap).toBe(9000);
40
+ });
41
+
42
+ it('rejects empty inventories and impossible policy values', () => {
43
+ const invalid = { ...baseInput, policyLimit: 0, sampleLoss: 0, inventory: [] };
44
+ expect(Object.keys(validateRenterInsuranceInput(invalid))).toEqual(['policyLimit', 'sampleLoss', 'inventory']);
45
+ expect(calculateRenterInsuranceCoverageGap(invalid)).toBeNull();
46
+ });
47
+
48
+ it('does not let a sample loss exceed the estimated contents', () => {
49
+ const result = calculateRenterInsuranceCoverageGap({ ...baseInput, sampleLoss: 50000, policyLimit: 25000, deductible: 0 });
50
+ expect(result).toMatchObject({ estimatedPayout: 19000, estimatedOutOfPocket: 0 });
51
+ });
52
+ });
@@ -0,0 +1,108 @@
1
+ export const INVENTORY_CATEGORIES = ['furniture', 'electronics', 'clothing', 'kitchen', 'personal', 'other'] as const;
2
+
3
+ export type InventoryCategory = (typeof INVENTORY_CATEGORIES)[number];
4
+
5
+ export interface InventoryItem {
6
+ category: InventoryCategory;
7
+ value: number;
8
+ }
9
+
10
+ export interface RenterInsuranceInput {
11
+ currency: string;
12
+ policyLimit: number;
13
+ deductible: number;
14
+ sampleLoss: number;
15
+ inventory: InventoryItem[];
16
+ }
17
+
18
+ export type CoverageStatus = 'safe' | 'review' | 'exposed';
19
+
20
+ export interface RenterInsuranceValidationErrors {
21
+ currency?: string;
22
+ policyLimit?: string;
23
+ deductible?: string;
24
+ sampleLoss?: string;
25
+ inventory?: string;
26
+ }
27
+
28
+ export interface CoverageCategoryResult {
29
+ category: InventoryCategory;
30
+ value: number;
31
+ share: number;
32
+ }
33
+
34
+ export interface RenterInsuranceResult {
35
+ currency: string;
36
+ policyLimit: number;
37
+ deductible: number;
38
+ sampleLoss: number;
39
+ totalContents: number;
40
+ coverageGap: number;
41
+ coverageRatio: number;
42
+ uncoveredPercent: number;
43
+ estimatedPayout: number;
44
+ estimatedOutOfPocket: number;
45
+ largestCategories: CoverageCategoryResult[];
46
+ status: CoverageStatus;
47
+ }
48
+
49
+ const MAX_AMOUNT = 1_000_000_000;
50
+
51
+ const roundMoney = (value: number): number => Number(value.toFixed(2));
52
+ const isAmount = (value: number): boolean => Number.isFinite(value) && value >= 0 && value <= MAX_AMOUNT;
53
+ const isPositiveAmount = (value: number): boolean => isAmount(value) && value > 0;
54
+ const isCurrency = (value: string): boolean => /^[A-Z]{3}$/.test(value.trim().toUpperCase());
55
+ const isValidInventory = (inventory: unknown): inventory is InventoryItem[] => {
56
+ if (!Array.isArray(inventory) || inventory.length !== INVENTORY_CATEGORIES.length) return false;
57
+ return inventory.every((item) => INVENTORY_CATEGORIES.includes(item.category) && isAmount(item.value));
58
+ };
59
+ const getInventoryTotal = (inventory: InventoryItem[]): number => inventory.reduce((sum, item) => sum + item.value, 0);
60
+
61
+ export function validateRenterInsuranceInput(input: RenterInsuranceInput): RenterInsuranceValidationErrors {
62
+ const errors: RenterInsuranceValidationErrors = {};
63
+ if (!isCurrency(input.currency)) errors.currency = 'currency';
64
+ if (!isPositiveAmount(input.policyLimit)) errors.policyLimit = 'policyLimit';
65
+ if (!isAmount(input.deductible)) errors.deductible = 'deductible';
66
+ if (!isPositiveAmount(input.sampleLoss)) errors.sampleLoss = 'sampleLoss';
67
+ if (!isValidInventory(input.inventory)) errors.inventory = 'inventory';
68
+ if (isValidInventory(input.inventory)) {
69
+ const total = getInventoryTotal(input.inventory);
70
+ if (!isPositiveAmount(total)) errors.inventory = 'inventory';
71
+ }
72
+ return errors;
73
+ }
74
+
75
+ const getStatus = (coverageGap: number, totalContents: number): CoverageStatus => {
76
+ if (coverageGap <= 0) return 'safe';
77
+ return coverageGap / totalContents <= 0.1 ? 'review' : 'exposed';
78
+ };
79
+
80
+ const sortCategories = (inventory: InventoryItem[], totalContents: number): CoverageCategoryResult[] => inventory
81
+ .map((item) => ({ category: item.category, value: roundMoney(item.value), share: item.value / totalContents }))
82
+ .sort((left, right) => right.value - left.value)
83
+ .slice(0, 3);
84
+
85
+ export function calculateRenterInsuranceCoverageGap(input: RenterInsuranceInput): RenterInsuranceResult | null {
86
+ if (Object.keys(validateRenterInsuranceInput(input)).length > 0) return null;
87
+ const currency = input.currency.trim().toUpperCase();
88
+ const totalContents = roundMoney(getInventoryTotal(input.inventory));
89
+ const coverageRatio = Math.min(input.policyLimit / totalContents, 1);
90
+ const coverageGap = roundMoney(Math.max(0, totalContents - input.policyLimit));
91
+ const loss = Math.min(input.sampleLoss, totalContents);
92
+ const estimatedPayout = roundMoney(Math.max(0, loss * coverageRatio - input.deductible));
93
+ const estimatedOutOfPocket = roundMoney(Math.max(0, loss - estimatedPayout));
94
+ return {
95
+ currency,
96
+ policyLimit: roundMoney(input.policyLimit),
97
+ deductible: roundMoney(input.deductible),
98
+ sampleLoss: roundMoney(input.sampleLoss),
99
+ totalContents,
100
+ coverageGap,
101
+ coverageRatio,
102
+ uncoveredPercent: roundMoney(Math.max(0, 1 - coverageRatio) * 100),
103
+ estimatedPayout,
104
+ estimatedOutOfPocket,
105
+ largestCategories: sortCategories(input.inventory, totalContents),
106
+ status: getStatus(coverageGap, totalContents),
107
+ };
108
+ }