{
  "spec": "israeli-tax-reports",
  "audited_at": "2026-06-11",
  "findings": [
    {
      "id": "itr-001",
      "severity": "P0",
      "dimension": "spec",
      "title": "PCN874 input VAT sums full vat_amount \u2014 ignores deduction_pct partial deductibility",
      "location": "packages/db/src/queries/tax-reports.ts:74",
      "evidence": "getPcn874Report input query: `COALESCE(SUM(${expenses.vatAmount}::numeric), 0)` with no deduction_pct weighting. Dependent expenses-module spec requires `vat_amount \u00d7 deduction_pct / 100` for partial rows; sibling vatSummaryPcn874 (packages/db/src/queries/expenses.ts:689-705) implements full+partial split. Overstates input VAT and understates VAT payable when expenses have deduction_pct < 100.",
      "recommendation": "Replace input VAT aggregation with SUM(vat_amount * COALESCE(deduction_pct,100) / 100) for vat_deductible=true rows (mirror vatSummaryPcn874), and scale inputExpenseTotalIls by deduction_pct for display totals.",
      "fix_direction": "code_to_spec"
    },
    {
      "id": "itr-002",
      "severity": "P0",
      "dimension": "spec",
      "title": "PCN874 CSV is aggregate stub \u2014 not ITA fixed per-transaction record layout",
      "location": "apps/zync-api/src/lib/pcn874-csv.ts:25-74",
      "evidence": "buildPcn874Csv emits only summary lines H/O/C/I with period and totals. Spec: 'Fields are not free-form \u2014 the layout is the fixed ITA PCN874 record (header line + per-transaction lines)'. Task 6 requires tenant business tax id, reporting period YYYYMM, vat_period, and per-transaction lines. Function signature accepts only Pcn874Report (no tenant), no invoice/expense detail rows fetched.",
      "recommendation": "Implement the fixed ITA PCN874 record layout with tenant tax id header and per-transaction detail lines sourced from period invoices/expenses; pass tenant context into buildPcn874Csv.",
      "fix_direction": "code_to_spec"
    },
    {
      "id": "itr-003",
      "severity": "P1",
      "dimension": "spec",
      "title": "PCN874 input VAT includes per-diem expenses (isPerDiem not excluded)",
      "location": "packages/db/src/queries/tax-reports.ts:69-85",
      "evidence": "Input VAT query filters vat_deductible=true but never `is_per_diem = false`. expenses-module spec: 'Per-diem expenses are not VAT-deductible\u2026 PCN874 export excludes per-diem rows'. vatSummaryPcn874 adds `eq(expenses.isPerDiem, false)` at packages/db/src/queries/expenses.ts:683.",
      "recommendation": "Add isPerDiem=false filter to getPcn874Report input VAT query.",
      "fix_direction": "code_to_spec"
    },
    {
      "id": "itr-004",
      "severity": "P1",
      "dimension": "spec",
      "title": "PCN874 input VAT includes soft-deleted expenses",
      "location": "packages/db/src/queries/tax-reports.ts:69-85",
      "evidence": "getPcn874Report expense WHERE clause has no deletedAt IS NULL guard. vatSummaryPcn874 uses `isNull(expenses.deletedAt)` (packages/db/src/queries/expenses.ts:679). Deleted expenses can inflate input VAT.",
      "recommendation": "Add deletedAt IS NULL filter to input VAT aggregation.",
      "fix_direction": "code_to_spec"
    },
    {
      "id": "itr-005",
      "severity": "P1",
      "dimension": "spec",
      "title": "vat_period tenant setting not surfaced in PCN874 report or period selector",
      "location": "packages/db/src/queries/tax-reports.ts:30-107; apps/zync-app/src/pages/reports/VatReportPage.tsx:20-28",
      "evidence": "Spec schema adds tenant_settings.vat_period and architecture table says 'VAT period configurable'. getPcn874Report never reads tenant_settings; Pcn874Report DTO has no vatPeriod field. VatReportPage hardcodes bimonthly default via getDefaultPeriod() without fetching /api/settings/tax.",
      "recommendation": "Read vat_period from tenant_settings in getPcn874Report, return it in JSON, and drive VatReportPage period presets from the tenant setting.",
      "fix_direction": "code_to_spec"
    },
    {
      "id": "itr-006",
      "severity": "P1",
      "dimension": "spec",
      "title": "Annual income summary missing mileage and contractor payout deductions",
      "location": "packages/db/src/queries/tax-reports.ts:115-194",
      "evidence": "Spec UI lists 'Mileage deduction' and 'Contractor payouts' as separate deduction rows. getAnnualIncomeSummary only sums expense categories; no mileageEntries or payoutBills queries. Upstream tables and aggregation patterns exist (packages/db/src/queries/mileage.ts, packages/db/src/queries/nii-advances.ts:63-67). Task 5 explicitly requires mileage + contractor payout totals.",
      "recommendation": "Add tenant-level mileage reimbursement sum and PAID payout_bills net sum for the year; expose as separate deduction lines and include in totalDeductionsIls/netTaxableIncomeIls.",
      "fix_direction": "code_to_spec"
    },
    {
      "id": "itr-007",
      "severity": "P1",
      "dimension": "spec",
      "title": "Annual summary xlsx missing supporting detail worksheets",
      "location": "apps/zync-api/src/lib/annual-summary-xlsx.ts:45-81",
      "evidence": "Spec: 'Excel export includes all supporting data: invoice list, expense list by category, mileage log, payout bills.' buildAnnualSummaryXlsx writes only 3 summary sheets (Income Summary, Expense Breakdown, Net Taxable Income) with aggregated figures \u2014 no invoice rows, mileage log, or payout detail.",
      "recommendation": "Extend buildAnnualSummaryXlsx to accept supporting detail queries and add Invoices, Mileage Log, and Contractor Payouts worksheets per task 7.",
      "fix_direction": "code_to_spec"
    },
    {
      "id": "itr-008",
      "severity": "P1",
      "dimension": "spec",
      "title": "Advance tax advancePaymentsYtdIls hardcoded to zero",
      "location": "packages/db/src/queries/tax-reports.ts:334",
      "evidence": "getAdvanceTaxEstimate returns `advancePaymentsYtdIls: '0.00'` always. Spec UI shows 'Advance payments YTD (entered): \u20aa 24,000'. No schema column or settings field stores YTD advance payments; AdvanceTaxPage displays the zero.",
      "recommendation": "Add tenant-stored advance payments YTD (settings column or manual entry) and return real value in getAdvanceTaxEstimate.",
      "fix_direction": "code_to_spec"
    },
    {
      "id": "itr-009",
      "severity": "P1",
      "dimension": "spec",
      "title": "Recommended advance per month formula omits advancePaymentsYtd subtraction",
      "location": "packages/db/src/queries/tax-reports.ts:319-324",
      "evidence": "Implementation: `(estimatedTax * (parseFloat(advanceTaxRatePct) / 100)) / monthsRemaining`. Task 5 formula: `max(0, (estimatedAnnualTax*ratePct/100) - advancePaymentsYtd) / remainingMonths`. Missing subtraction of payments already made.",
      "recommendation": "Apply task formula once advancePaymentsYtd is stored; divide remaining liability by monthsRemaining.",
      "fix_direction": "code_to_spec"
    },
    {
      "id": "itr-010",
      "severity": "P1",
      "dimension": "spec",
      "title": "Advance tax bracket query missing country_code IL filter",
      "location": "packages/db/src/queries/tax-reports.ts:281-295",
      "evidence": "Bracket load uses `tax_type LIKE 'personal_bracket_%' AND effective_from <= :asOfDate` but no `country_code = 'IL'`. Task 5 SQL and tax_rates seed scope rows to IL. Multi-country tax_rates table could return wrong brackets.",
      "recommendation": "Add eq(taxRates.countryCode, 'IL') to bracket query.",
      "fix_direction": "code_to_spec"
    },
    {
      "id": "itr-011",
      "severity": "P1",
      "dimension": "spec",
      "title": "TaxSettingsPage API response shape mismatch (snake_case vs camelCase)",
      "location": "apps/zync-app/src/pages/settings/TaxSettingsPage.tsx:20-62; apps/zync-api/src/routes/settings-tax.ts:44-49",
      "evidence": "API returns `{ vat_period, advance_tax_rate_pct }`. TaxSettingsPage casts to TaxSettings and reads `data.vatPeriod` / `data.advanceTaxRatePct` in useEffect \u2014 always undefined on load. Form defaults to bimonthly/blank instead of saved values.",
      "recommendation": "Map snake_case API fields to camelCase in fetchTaxSettings or change API to emit camelCase matching @zync/types TaxSettings.",
      "fix_direction": "code_to_spec"
    },
    {
      "id": "itr-012",
      "severity": "P1",
      "dimension": "spec",
      "title": "Credit note VAT reversal includes non-final invoice statuses",
      "location": "packages/db/src/queries/tax-reports.ts:53-67",
      "evidence": "Credit-note query filters only `source = 'credit_note'` and date range \u2014 no status exclusion. Output VAT excludes DRAFT/SENT/VOID/BAD_DEBT per spec. DRAFT/VOID credit notes could reduce net output VAT incorrectly.",
      "recommendation": "Apply same status filter as output VAT query to credit-note aggregation.",
      "fix_direction": "code_to_spec"
    },
    {
      "id": "itr-013",
      "severity": "P1",
      "dimension": "spec",
      "title": "Duplicate PCN874 endpoints with divergent VAT calculations",
      "location": "apps/zync-api/src/routes/reports-tax.ts:45-92; apps/zync-api/src/routes/expenses/reports.ts:98-148",
      "evidence": "Canonical spec routes: GET /api/reports/vat (+ /csv) via getPcn874Report. Parallel path GET /api/expenses/reports/vat (+ /xlsx) via vatSummaryPcn874 applies deduction_pct, isPerDiem, deletedAt filters. Same report name, different numbers; expense module UI (VatSummaryTab) still points at /api/expenses/reports/vat.",
      "recommendation": "Consolidate on /api/reports/vat or delegate expenses path to getPcn874Report; deprecate duplicate xlsx export or align calculations.",
      "fix_direction": "code_to_spec"
    },
    {
      "id": "itr-014",
      "severity": "P1",
      "dimension": "spec",
      "title": "Annual expense deductions ignore deduction_pct \u2014 use vat_deductible boolean only",
      "location": "packages/db/src/queries/tax-reports.ts:157",
      "evidence": "Deductible sum: `SUM(CASE WHEN vat_deductible THEN amount ELSE 0 END)`. Expenses with partial deductibility (deduction_pct 25/45/66) should contribute `amount * deduction_pct / 100` to income-tax deductions, not full amount when vat_deductible=true.",
      "recommendation": "Scale deductible expense amounts by deduction_pct in annual summary and advance-tax YTD expense aggregation.",
      "fix_direction": "code_to_spec"
    },
    {
      "id": "itr-015",
      "severity": "P2",
      "dimension": "spec",
      "title": "VAT report UI missing Export PDF button",
      "location": "apps/zync-app/src/pages/reports/VatReportPage.tsx:143-147",
      "evidence": "Spec PCN874 UI wireframe shows '[Export CSV] [Export PDF]'. VatReportPage only renders Export CSV; no PDF export route exists under /api/reports/vat.",
      "recommendation": "Add PDF export endpoint and button, or reconcile spec if CSV-only is intended.",
      "fix_direction": "spec_review"
    },
    {
      "id": "itr-016",
      "severity": "P2",
      "dimension": "constraint",
      "title": "GET /api/settings/tax requires settings:write \u2014 not documented in spec API",
      "location": "apps/zync-api/src/routes/settings-tax.ts:37",
      "evidence": "Spec API lists only PATCH /api/settings/tax with settings:write. GET handler also uses requirePermission('settings:write'), blocking read-only staff from loading current vat_period/rate even for display.",
      "recommendation": "Decide whether GET should use settings:read or reports:read; document in spec.",
      "fix_direction": "spec_review"
    },
    {
      "id": "itr-017",
      "severity": "P2",
      "dimension": "spec",
      "title": "Expense-module PCN874 xlsx export uses reports:read not reports:export",
      "location": "apps/zync-api/src/routes/expenses/reports.ts:122",
      "evidence": "Spec: GET /api/reports/vat/csv requires reports:export. Duplicate GET /api/expenses/reports/vat/xlsx uses requirePermission('reports:read') for a file download.",
      "recommendation": "Align export permission with reports:export on all PCN874 download endpoints.",
      "fix_direction": "code_to_spec"
    },
    {
      "id": "itr-018",
      "severity": "P2",
      "dimension": "quality",
      "title": "No automated tests for tax report queries, CP1255 CSV, or advance tax banding",
      "location": "ABSENT \u2014 no *test* files matching getPcn874Report|buildPcn874Csv|getAdvanceTaxEstimate",
      "evidence": "Grep across worktree finds zero unit/integration tests for packages/db/src/queries/tax-reports.ts or apps/zync-api/src/lib/pcn874-csv.ts. Regression-prone aggregation and encoding paths are unverified.",
      "recommendation": "Add query tests for deduction_pct weighting, credit-note status filter, marginal tax banding, and CP1255 byte output.",
      "fix_direction": "code_to_spec"
    },
    {
      "id": "itr-019",
      "severity": "P2",
      "dimension": "spec",
      "title": "Advance tax YTD net income uses gross expense amounts without deduction_pct",
      "location": "packages/db/src/queries/tax-reports.ts:256-272",
      "evidence": "ytdExpenses = SUM(expenses.amount) for approved COMPLETED expenses with no deduction_pct scaling. Partially deductible expenses overstate expense deductions in YTD net income estimate.",
      "recommendation": "Use SUM(amount * COALESCE(deduction_pct,100) / 100) for YTD expense side of advance tax.",
      "fix_direction": "code_to_spec"
    },
    {
      "id": "itr-020",
      "severity": "P2",
      "dimension": "spec",
      "title": "Bad debt write-off amount uses subtotal not COALESCE(total_ils,total)",
      "location": "packages/db/src/queries/tax-reports.ts:139-141",
      "evidence": "badDebtRow sums invoices.subtotal. Implementation plan task 5 specifies bad debts as `COALESCE(total_ils, total)` for foreign-currency invoices. May under/over-state write-offs for non-ILS invoices.",
      "recommendation": "Align bad debt aggregation with multi-currency snapshot column per task 5.",
      "fix_direction": "spec_review"
    },
    {
      "id": "itr-021",
      "severity": "P2",
      "dimension": "quality",
      "title": "Tax settings save does not invalidate advance-tax query cache",
      "location": "apps/zync-app/src/pages/settings/TaxSettingsPage.tsx:65-71",
      "evidence": "Task 13: 'invalidate the advance-tax query so /reports/advance-tax refreshes'. mutation.onSuccess only updates ['tax-settings'] cache; no queryClient.invalidateQueries(['advance-tax']).",
      "recommendation": "Invalidate advance-tax query key on successful PATCH.",
      "fix_direction": "code_to_spec"
    },
    {
      "id": "itr-022",
      "severity": "P2",
      "dimension": "spec",
      "title": "israeli-tax-reports spec input VAT SQL omits deduction_pct while expenses-module dependency requires it",
      "location": "docs/specs/2026-06-01-israeli-tax-reports.md:54-63 vs docs/specs/2026-05-30-expenses-module.md:317-318",
      "evidence": "Authority spec SQL: `SELECT SUM(vat_amount) AS input_vat \u2026 AND vat_deductible = true`. Dependent expenses-module: partial input VAT = `vat_amount \u00d7 deduction_pct / 100`. Codebase has both interpretations live (getPcn874Report vs vatSummaryPcn874).",
      "recommendation": "Reconcile specs \u2014 update israeli-tax-reports input VAT SQL to document deduction_pct scaling, or explicitly defer to expenses-module aggregation rules.",
      "fix_direction": "spec_review"
    },
    {
      "id": "itr-023",
      "severity": "P2",
      "dimension": "spec",
      "title": "Advance tax estimate partial \u2014 core path delivered but missing YTD payments entry and full deduction model",
      "location": "apps/zync-api/src/routes/reports-tax.ts:148-164; apps/zync-app/src/pages/reports/AdvanceTaxPage.tsx",
      "evidence": "Sibling audit lead CONFIRMED partial: GET /api/reports/advance-tax exists with real YTD invoice/expense aggregation and tax_rates marginal banding. Gaps: advancePaymentsYtdIls stub (itr-008), formula (itr-009), expense deduction_pct (itr-019), no contractor payout adjustment in YTD net income.",
      "recommendation": "Treat advance-tax as partial implementation; complete payment tracking and deduction model per spec UI.",
      "fix_direction": "code_to_spec"
    }
  ]
}
