97a1d2ebe2
Closes the 4th and final gap from the competitive benchmark: the human-in-the-loop professional tier every commercial competitor pairs with their software. Deliberately NOT a marketplace or payment integration — a structured review-request status on each member's Wassiyah (not_requested -> requested -> reviewed, with reviewer name recorded), surfaced on the Mutawalli dashboard so nothing quietly ships as final without the legally-required review being flagged. Paired with a new whole-app jurisdiction setting since 'necessary in certain countries' only makes sense per-jurisdiction: - nf_families.jurisdiction (MY/SG/UK), owner-only to change — first UPDATE policy ever added to nf_families, which previously had none. - New Settings-tab jurisdiction selector (JurisdictionSetting.svelte). - Wassiyah tab and the draft will document now carry jurisdiction- specific legal notes: Wills Act 1959 + state Syariah (Malaysia), Wills Act 1838 + AMLA (Singapore), Wills Act 1837 (UK). Singapore added as a full third jurisdiction option, not just a stub. - Zakat calculator's currency label and Nisab default now follow the family's jurisdiction (RM/SGD/GBP) instead of a hardcoded RM guess. Covered by e2e-jurisdiction-review.cjs (13/13). Full regression: 253/253 across all suites (2 reruns confirmed as pre-existing parallel-load flakes, not regressions).
32 lines
1.5 KiB
JavaScript
32 lines
1.5 KiB
JavaScript
// Shared Zakat calculation core — Nisab check + 2.5% (1/40) on qualifying
|
|
// wealth held above threshold. Nisab is entered as a value, not a live gold
|
|
// price feed — matches the app's "manual entry only" philosophy already
|
|
// established for Asset Registry / Faraid.
|
|
const ZAKAT_RATE = 0.025;
|
|
|
|
// Rough starting points, NOT live prices — the app has no gold/silver price
|
|
// feed (manual entry only, matching the rest of the app). These exist so a
|
|
// new user isn't staring at a blank/zero Nisab field; they must still verify
|
|
// today's actual 85g-gold-equivalent value before relying on the result.
|
|
export const CURRENCY_BY_JURISDICTION = { MY: 'RM', SG: 'SGD', UK: 'GBP' };
|
|
export const DEFAULT_NISAB_BY_JURISDICTION = { MY: 24000, SG: 8000, UK: 5000 };
|
|
|
|
export function zakatableWealth(fields) {
|
|
const gross = (Number(fields.cash) || 0) + (Number(fields.gold) || 0) + (Number(fields.silver) || 0)
|
|
+ (Number(fields.businessAssets) || 0) + (Number(fields.investments) || 0) + (Number(fields.otherZakatable) || 0);
|
|
const deductible = Number(fields.deductibleLiabilities) || 0;
|
|
return Math.max(0, gross - deductible);
|
|
}
|
|
|
|
export function zakatDue(fields) {
|
|
const wealth = zakatableWealth(fields);
|
|
const nisab = Number(fields.nisabThreshold) || 0;
|
|
if (nisab <= 0 || wealth < nisab) return 0;
|
|
return wealth * ZAKAT_RATE;
|
|
}
|
|
|
|
export function meetsNisab(fields) {
|
|
const nisab = Number(fields.nisabThreshold) || 0;
|
|
return nisab > 0 && zakatableWealth(fields) >= nisab;
|
|
}
|