{#- Zakat Dashboard -- Fava extension report template. This is a FRAGMENT. Fava 1.30 renders it and injects the result into its own _layout.html, so it must not `extends` anything and must not emit ,
or . CSS is inlined because Fava exposes no static-asset route for extensions; behaviour lives in ZakatDashboard.js, loaded through has_js_module. -#} {% set ctx = extension.report() %} {% set report = ctx.report %} {% set cur = ctx.currency %} {% set dp = ctx.places %} {% macro m(value, places=None) %}{{ extension.money(value, cur, dp if places is none else places) }}{% endmacro %} {% macro n(value, places=None) %}{{ extension.number(value, dp if places is none else places) }}{% endmacro %}{{ ctx.error }}
{{ w.message }}
{% if w.detail %}{{ w.detail }}{% endif %}
{{ w.message }}
{% if w.detail %}{{ w.detail }}{% endif %}
{{ w.message }}
No account carries the beancount_zakat metadata, or none of the tagged accounts has any postings on or before {{ report.as_of }}. See the About Zakat & Methodology tab for how to tag your accounts.
{{ extension.balance(b.remaining_or_excess, cur, dp) }}
{% if b.status == 'outstanding' %} ● Outstanding {% elif b.status == 'excess' %} ✓ Paid in excess {% else %} — Settled {% endif %} {% if b.qualifies_now %} ✓ Sahib-e-nisab today {% endif %}
Only accounts carrying beancount_zakat metadata take part. Everything else in your ledger is ignored.
| Role | Accounts |
|---|---|
| {{ label }} |
{% if accounts %}
|
A reporting year runs from 1 Muharram to the day before the next 1 Muharram. Where a holding period spans more than one year its liability is split across those years pro rata by days, so these rows sum exactly to the cumulative totals shown on the Overview tab.
Balance columns are running and signed: a negative figure means more has been paid than was owed at that point.
| Hijri year | Gregorian range | Gold liability | Silver liability | Payments | Gold balance | Silver balance |
|---|---|---|---|---|---|---|
| {{ row.label }} | {{ row.start }} – {{ row.end }} | {{ n(row.gold_liability) }} | {{ n(row.silver_liability) }} | {{ n(row.payments) }} | {{ n(row.gold_balance) }} | {{ n(row.silver_balance) }} |
| No reporting years yet. | ||||||
| Total | {{ n(gold.cumulative_liability) }} | {{ n(silver.cumulative_liability) }} | {{ n(report.payments_total) }} | {{ n(gold.remaining_or_excess) }} | {{ n(silver.remaining_or_excess) }} | |
Each tagged account is a band. Anything held stacks upwards from the zero line and anything owed hangs below it, so the gap between the two fronts is net zakatable wealth — the same figure the calculation used, drawn as its parts. The horizontal axis is proportional to real elapsed time, so a quiet stretch looks like a quiet stretch, and every edge is stepped because a balance holds its value until something changes it. The nisab is deliberately absent: a stack front is a gross figure, and the threshold applies to the net.
{% set stack = ctx.stack %} {% if stack.is_empty %}The same net figure the stack adds up to, now on its own against both moving thresholds. Gold and silver are alternative bases, never added together: each line is a separate answer to “was there enough to owe anything?”
{% set chart = ctx.chart %} {% if chart.is_empty %}Every date on which either threshold moved, with the price behind it and the date that price was quoted. A day with no price of its own reuses the last known price, so a row stands until the next one replaces it.
| In force from | Gold price | Quoted | Gold nisab | Silver price | Quoted | Silver nisab |
|---|---|---|---|---|---|---|
| {{ row.when }} | {{ n(row.gold.price) if row.gold.price is not none else 'n/a' }} {{ row.gold.commodity or '' }} | {{ row.gold.price_date or '—'|safe }}{% if row.gold_stale %} {{ row.gold.stale_days }}d old{% endif %} | {{ n(row.gold.nisab) if row.gold.nisab is not none else 'n/a' }} | {{ n(row.silver.price) if row.silver.price is not none else 'n/a' }} {{ row.silver.commodity or '' }} | {{ row.silver.price_date or '—'|safe }}{% if row.silver_stale %} {{ row.silver.stale_days }}d old{% endif %} | {{ n(row.silver.nisab) if row.silver.nisab is not none else 'n/a' }} |
| No metal prices found, so no nisab could be determined. | ||||||
Every date on which wealth changed — whether from a posting or purely from a price movement. Holdings in a non-operating commodity are re-valued whenever their price moves, not frozen at what they cost.
| Date | Cause | Assets | Liabilities | Net wealth | Gold nisab | Silver nisab |
|---|---|---|---|---|---|---|
| {{ point.when }} | {{ 'price movement' if point.price_driven else 'posting' }} | {{ n(point.assets) }} | {{ n(point.liabilities) }} | {{ n(point.net) }} | {{ n(gp.nisab) if gp.nisab is not none else 'n/a' }} | {{ n(sp.nisab) if sp.nisab is not none else 'n/a' }} |
| No wealth events recorded. | ||||||
Values in {{ cur }}. Liabilities keep their natural negative sign, so the figures add up rather than needing to be stacked by hand.
| Account | Role | Value |
|---|---|---|
| {{ account }} | {{ 'liability' if account in report.liability_accounts else 'asset' }} | {{ n(value) }} |
| Net | {{ n(last.net) }} | |
| No accounts with balances. | ||
Each distinct level your net wealth has reached becomes a marginal slice with its own independent hawl. A slice’s period runs only while net wealth stays at or above that slice’s level and total net wealth stays at or above the nisab.
The two bases are shown separately.
The chart plots net zakatable wealth over time against the {{ sec.basis.value }} nisab alone. Shaded spans mark the stretches in which wealth sat below that threshold.
{% set ch = sec.chart %} {% if ch.is_empty %}One band per marginal slice, on the same time axis as the chart above. A band is green while its hawl is complete, amber while it is running but short of a lunar year, and grey where wealth fell below the nisab and the clock was reset. {% if st.truncated %}Showing the {{ st.rows|length }} largest slices; {{ st.truncated }} smaller ones are omitted from this view but are all in the table below.{% endif %}
| Level | Marginal amount | Start | End | Days | Lunar years counted | {{ sec.basis.label }} nisab in force | Above nisab | Hawl | Zakat due | Why |
|---|---|---|---|---|---|---|---|---|---|---|
| {{ n(row.level) }} | {{ n(row.marginal) }} | {% endif %}{{ p.start }} | {{ p.end }} | {{ p.days }} | {{ extension.years(p.lunar_years) }} | {{ extension.nisab_span(row.nisab) }} | {% if p.above_nisab %}✓ yes {% else %}— no{% endif %} | {% if p.hawl.value == 'complete' %}✓ complete {% elif p.hawl.value == 'incomplete' %}— incomplete {% else %}∅ reset (below nisab){% endif %} | {{ n(p.zakat_due) }} | {{ p.reason }} |
| No positive wealth levels were reached. | ||||||||||
| Total {{ sec.basis.label|lower }} liability | {{ n(b.cumulative_liability) }} | |||||||||
Every posting to an account tagged beancount_zakat: "expense". Signs are preserved: a negative posting is a refund or a correcting reversal and reduces the total paid.
| Date | Account | Payee / narration | As posted | Rate | Amount | Kind | Running total |
|---|---|---|---|---|---|---|---|
| {{ p.when }} | {{ p.account }} | {{ [p.payee, p.narration]|select|join(' — ')|safe }} | {{ n(p.original_amount) }} {{ p.original_currency }} | {{ n(p.rate, 6) if p.rate is not none else '—'|safe }} | {{ n(p.amount) }} | {% if p.is_reversal %}← refund {% else %}✓ payment{% endif %} | {{ n(ns.running) }} |
| No zakat payments recorded. Tag the account you pay zakat from with beancount_zakat: "expense" so payments are tracked here. | |||||||
| Total paid (net of refunds) | {{ n(report.payments_total) }} | {{ n(report.payments_total) }} | |||||
{{ extension.balance(b.remaining_or_excess, cur, dp) }}