Files
ai-tax-agent/reasoning/schedule_queries.cypher
harkon b324ff09ef
Some checks failed
CI/CD Pipeline / Code Quality & Linting (push) Has been cancelled
CI/CD Pipeline / Policy Validation (push) Has been cancelled
CI/CD Pipeline / Test Suite (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-coverage) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-extract) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-firm-connectors) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-forms) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-hmrc) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-ingestion) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-kg) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-normalize-map) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-ocr) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-rag-indexer) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-rag-retriever) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-reason) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-rpa) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (ui-review) (push) Has been cancelled
CI/CD Pipeline / Security Scanning (svc-coverage) (push) Has been cancelled
CI/CD Pipeline / Security Scanning (svc-extract) (push) Has been cancelled
CI/CD Pipeline / Security Scanning (svc-kg) (push) Has been cancelled
CI/CD Pipeline / Security Scanning (svc-rag-retriever) (push) Has been cancelled
CI/CD Pipeline / Security Scanning (ui-review) (push) Has been cancelled
CI/CD Pipeline / Generate SBOM (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / Notifications (push) Has been cancelled
Initial commit
2025-10-11 08:41:36 +01:00

257 lines
9.1 KiB
Plaintext

// FILE: reasoning/schedule_queries.cypher
// SA102: Employment Income Calculation
// Box 1: Pay from employment
MATCH (tp:TaxpayerProfile {taxpayer_id: $taxpayer_id})
MATCH (ty:TaxYear {label: $tax_year})
MATCH (ii:IncomeItem {type: 'employment'})
WHERE (tp)-[:HAS_INCOME]->(ii)
AND ii.valid_from >= ty.start_date
AND ii.valid_from <= ty.end_date
AND ii.retracted_at IS NULL
WITH ii, ty
MATCH (ii)-[:DERIVED_FROM]->(e:Evidence)-[:SUPPORTED_BY]->(d:Document)
RETURN
sum(ii.gross) as box_1_total,
collect(DISTINCT {
amount: ii.gross,
source: d.doc_id,
page: e.page,
bbox: e.bbox,
text_hash: e.text_hash,
confidence: e.ocr_confidence
}) as evidence_trail,
count(ii) as income_items_count;
// Box 2: UK tax deducted
MATCH (tp:TaxpayerProfile {taxpayer_id: $taxpayer_id})
MATCH (ty:TaxYear {label: $tax_year})
MATCH (ii:IncomeItem {type: 'employment'})
WHERE (tp)-[:HAS_INCOME]->(ii)
AND ii.valid_from >= ty.start_date
AND ii.valid_from <= ty.end_date
AND ii.retracted_at IS NULL
AND ii.tax_withheld IS NOT NULL
WITH ii, ty
MATCH (ii)-[:DERIVED_FROM]->(e:Evidence)-[:SUPPORTED_BY]->(d:Document)
RETURN
sum(ii.tax_withheld) as box_2_total,
collect(DISTINCT {
amount: ii.tax_withheld,
source: d.doc_id,
page: e.page,
bbox: e.bbox,
text_hash: e.text_hash
}) as evidence_trail;
// SA103: Self-Employment Income Calculation
// Box 12: Turnover
MATCH (tp:TaxpayerProfile {taxpayer_id: $taxpayer_id})
MATCH (ty:TaxYear {label: $tax_year})
MATCH (ba:BusinessActivity)-[:OWNED_BY]->(tp)
MATCH (ii:IncomeItem {type: 'self_employment'})
WHERE (ba)-[:GENERATES]->(ii)
AND ii.valid_from >= ty.start_date
AND ii.valid_from <= ty.end_date
AND ii.retracted_at IS NULL
WITH ii, ba, ty
MATCH (ii)-[:DERIVED_FROM]->(e:Evidence)-[:SUPPORTED_BY]->(d:Document)
RETURN
ba.business_name as business_name,
sum(ii.gross) as box_12_turnover,
collect(DISTINCT {
amount: ii.gross,
description: ii.description,
source: d.doc_id,
page: e.page,
bbox: e.bbox,
text_hash: e.text_hash
}) as evidence_trail;
// Box 31: Total allowable business expenses
MATCH (tp:TaxpayerProfile {taxpayer_id: $taxpayer_id})
MATCH (ty:TaxYear {label: $tax_year})
MATCH (ba:BusinessActivity)-[:OWNED_BY]->(tp)
MATCH (ei:ExpenseItem {type: 'business', allowable: true})
WHERE (ba)-[:INCURS]->(ei)
AND ei.valid_from >= ty.start_date
AND ei.valid_from <= ty.end_date
AND ei.retracted_at IS NULL
WITH ei, ba, ty
MATCH (ei)-[:DERIVED_FROM]->(e:Evidence)-[:SUPPORTED_BY]->(d:Document)
RETURN
ba.business_name as business_name,
sum(ei.amount) as box_31_expenses,
collect(DISTINCT {
amount: ei.amount,
category: ei.category,
description: ei.description,
source: d.doc_id,
page: e.page,
bbox: e.bbox,
text_hash: e.text_hash
}) as evidence_trail;
// SA105: Property Income Calculation
// Box 20: Total rents and other income from property
MATCH (tp:TaxpayerProfile {taxpayer_id: $taxpayer_id})
MATCH (ty:TaxYear {label: $tax_year})
MATCH (pa:PropertyAsset)-[:OWNED_BY]->(tp)
MATCH (ii:IncomeItem {type: 'property'})
WHERE (pa)-[:GENERATES]->(ii)
AND ii.valid_from >= ty.start_date
AND ii.valid_from <= ty.end_date
AND ii.retracted_at IS NULL
WITH ii, pa, ty
MATCH (ii)-[:DERIVED_FROM]->(e:Evidence)-[:SUPPORTED_BY]->(d:Document)
RETURN
pa.address as property_address,
pa.usage as property_usage,
sum(ii.gross) as box_20_rental_income,
collect(DISTINCT {
amount: ii.gross,
period_start: ii.period_start,
period_end: ii.period_end,
source: d.doc_id,
page: e.page,
bbox: e.bbox,
text_hash: e.text_hash
}) as evidence_trail;
// Box 29: Total allowable property expenses
MATCH (tp:TaxpayerProfile {taxpayer_id: $taxpayer_id})
MATCH (ty:TaxYear {label: $tax_year})
MATCH (pa:PropertyAsset)-[:OWNED_BY]->(tp)
MATCH (ei:ExpenseItem {type: 'property', allowable: true})
WHERE (pa)-[:INCURS]->(ei)
AND ei.valid_from >= ty.start_date
AND ei.valid_from <= ty.end_date
AND ei.retracted_at IS NULL
WITH ei, pa, ty
MATCH (ei)-[:DERIVED_FROM]->(e:Evidence)-[:SUPPORTED_BY]->(d:Document)
RETURN
pa.address as property_address,
sum(ei.amount) as box_29_expenses,
collect(DISTINCT {
amount: ei.amount,
category: ei.category,
description: ei.description,
source: d.doc_id,
page: e.page,
bbox: e.bbox,
text_hash: e.text_hash
}) as evidence_trail;
// SA110: Tax Calculation Summary
// Personal allowance calculation with tapering
MATCH (tp:TaxpayerProfile {taxpayer_id: $taxpayer_id})
MATCH (ty:TaxYear {label: $tax_year})
MATCH (r:Rule {rule_id: 'personal_allowance_' + $tax_year})
WITH tp, ty, r,
CASE
WHEN $total_income <= r.taper_threshold
THEN r.full_allowance
WHEN $total_income >= r.taper_threshold + (2 * r.full_allowance)
THEN 0
ELSE r.full_allowance - (($total_income - r.taper_threshold) / 2)
END as personal_allowance
RETURN personal_allowance;
// Income tax calculation with bands
MATCH (ty:TaxYear {label: $tax_year})
MATCH (r:Rule {rule_id: 'income_tax_bands_' + $tax_year})
WITH $taxable_income as income, r
RETURN
CASE
WHEN income <= r.basic_rate_threshold
THEN income * r.basic_rate
WHEN income <= r.higher_rate_threshold
THEN (r.basic_rate_threshold * r.basic_rate) +
((income - r.basic_rate_threshold) * r.higher_rate)
ELSE (r.basic_rate_threshold * r.basic_rate) +
((r.higher_rate_threshold - r.basic_rate_threshold) * r.higher_rate) +
((income - r.higher_rate_threshold) * r.additional_rate)
END as income_tax_due;
// NIC Class 4 calculation for self-employed
MATCH (ty:TaxYear {label: $tax_year})
MATCH (r:Rule {rule_id: 'nic_class4_' + $tax_year})
WITH $self_employment_profit as profit, r
RETURN
CASE
WHEN profit <= r.lower_threshold
THEN 0
WHEN profit <= r.upper_threshold
THEN (profit - r.lower_threshold) * r.main_rate
ELSE ((r.upper_threshold - r.lower_threshold) * r.main_rate) +
((profit - r.upper_threshold) * r.additional_rate)
END as nic_class4_due;
// Property interest restriction (20% credit)
MATCH (tp:TaxpayerProfile {taxpayer_id: $taxpayer_id})
MATCH (ty:TaxYear {label: $tax_year})
MATCH (pa:PropertyAsset)-[:OWNED_BY]->(tp)
MATCH (ei:ExpenseItem {category: 'mortgage_interest'})
WHERE (pa)-[:INCURS]->(ei)
AND ei.valid_from >= ty.start_date
AND ei.valid_from <= ty.end_date
AND ei.retracted_at IS NULL
WITH sum(ei.amount) as total_interest
RETURN
total_interest as mortgage_interest_paid,
total_interest * 0.20 as interest_relief_credit,
total_interest * 0.80 as disallowed_interest;
// Furnished Holiday Lettings (FHL) qualification test
MATCH (tp:TaxpayerProfile {taxpayer_id: $taxpayer_id})
MATCH (ty:TaxYear {label: $tax_year})
MATCH (pa:PropertyAsset {usage: 'furnished_holiday_letting'})-[:OWNED_BY]->(tp)
OPTIONAL MATCH (pa)-[:HAS_METRIC]->(m:PropertyMetric)
WHERE m.metric_type IN ['availability_days', 'letting_days', 'longer_term_days']
AND m.tax_year = $tax_year
WITH pa,
collect(CASE WHEN m.metric_type = 'availability_days' THEN m.value END)[0] as availability,
collect(CASE WHEN m.metric_type = 'letting_days' THEN m.value END)[0] as letting,
collect(CASE WHEN m.metric_type = 'longer_term_days' THEN m.value END)[0] as longer_term
RETURN
pa.address as property_address,
availability >= 210 as availability_test_passed,
letting >= 105 as letting_test_passed,
(longer_term IS NULL OR longer_term <= 155) as longer_term_test_passed,
(availability >= 210 AND letting >= 105 AND (longer_term IS NULL OR longer_term <= 155)) as fhl_qualified;
// Validation query: Income/expense reconciliation
MATCH (tp:TaxpayerProfile {taxpayer_id: $taxpayer_id})
MATCH (ty:TaxYear {label: $tax_year})
OPTIONAL MATCH (ii:IncomeItem)-[:BELONGS_TO]->(tp)
WHERE ii.valid_from >= ty.start_date AND ii.valid_from <= ty.end_date
OPTIONAL MATCH (ei:ExpenseItem)-[:BELONGS_TO]->(tp)
WHERE ei.valid_from >= ty.start_date AND ei.valid_from <= ty.end_date
OPTIONAL MATCH (p:Payment)-[:BELONGS_TO]->(tp)
WHERE p.valid_from >= ty.start_date AND p.valid_from <= ty.end_date
RETURN
sum(ii.gross) as total_income,
sum(ei.amount) as total_expenses,
sum(CASE WHEN p.direction = 'outgoing' THEN p.amount ELSE 0 END) as total_payments_out,
sum(CASE WHEN p.direction = 'incoming' THEN p.amount ELSE 0 END) as total_payments_in,
abs(sum(ii.gross) - sum(CASE WHEN p.direction = 'incoming' THEN p.amount ELSE 0 END)) as income_reconciliation_delta,
abs(sum(ei.amount) - sum(CASE WHEN p.direction = 'outgoing' THEN p.amount ELSE 0 END)) as expense_reconciliation_delta;
// Time-travel query: Get facts as of specific date
CALL temporal.asOf($as_of_date) YIELD node
WHERE node:IncomeItem OR node:ExpenseItem
WITH node
MATCH (node)-[:BELONGS_TO]->(tp:TaxpayerProfile {taxpayer_id: $taxpayer_id})
MATCH (node)-[:DERIVED_FROM]->(e:Evidence)-[:SUPPORTED_BY]->(d:Document)
RETURN
labels(node)[0] as node_type,
node.type as item_type,
CASE WHEN node:IncomeItem THEN node.gross ELSE node.amount END as amount,
node.valid_from as valid_from,
node.valid_to as valid_to,
node.asserted_at as asserted_at,
d.doc_id as source_document,
e.page as source_page,
e.text_hash as evidence_hash
ORDER BY node.valid_from, node.asserted_at;