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
257 lines
9.1 KiB
Plaintext
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;
|