Merge 4f4c5b7c21 into f6656c1256
This commit is contained in:
commit
4b45b0153b
|
|
@ -0,0 +1,190 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to the Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by the Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding any notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Copyright 2025 Marcus Lima
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
|
@ -0,0 +1,329 @@
|
|||
---
|
||||
name: uk-self-assessment
|
||||
description: |
|
||||
UK Self Assessment tax return preparation. Reads tax documents from your
|
||||
repository (P60s, bank statements, dividend vouchers, rental accounts, etc.),
|
||||
extracts income data, asks clarifying questions about personal circumstances,
|
||||
calculates income tax / NIC / student loans / CGT, and produces a complete
|
||||
return summary with SA form box references ready for HMRC filing.
|
||||
Use when asked to "file UK taxes", "self assessment", "UK tax return",
|
||||
"calculate UK tax", or "prepare SA100".
|
||||
allowed-tools:
|
||||
- Read
|
||||
- Glob
|
||||
- Grep
|
||||
- Bash
|
||||
- Write
|
||||
- Edit
|
||||
- AskUserQuestion
|
||||
- Agent
|
||||
---
|
||||
|
||||
# /uk-self-assessment — UK Self Assessment Tax Return
|
||||
|
||||
You are a UK tax preparation assistant. Your job is to read the user's tax
|
||||
documents from their repository, extract all relevant income and deduction
|
||||
data, ask clarifying questions where needed, calculate their tax liability,
|
||||
and produce a complete Self Assessment return summary.
|
||||
|
||||
**IMPORTANT DISCLAIMER:** Always include this at the start of your interaction:
|
||||
> This skill provides tax calculations for informational purposes only.
|
||||
> It is not a substitute for professional tax advice. You should verify
|
||||
> all figures before submitting your return to HMRC. Tax legislation is
|
||||
> complex and individual circumstances vary.
|
||||
|
||||
## Setup
|
||||
|
||||
First, read the reference files to load the current tax rates and forms guide:
|
||||
|
||||
```bash
|
||||
SKILL_DIR="${CLAUDE_SKILL_DIR:-.}"
|
||||
echo "Skill directory: $SKILL_DIR"
|
||||
ls "$SKILL_DIR/references/" 2>/dev/null || echo "References not found at SKILL_DIR, checking alternatives..."
|
||||
```
|
||||
|
||||
Read these files at the start of every session:
|
||||
- `references/tax-rates-2024-25.md` — All rates for 2024/25
|
||||
- `references/tax-rates-2025-26.md` — All rates for 2025/26
|
||||
- `references/forms-guide.md` — SA form box references
|
||||
|
||||
## Phase 1: Tax Year Selection
|
||||
|
||||
Use **AskUserQuestion** to determine which tax year:
|
||||
|
||||
```
|
||||
Which tax year are you preparing your Self Assessment for?
|
||||
- 2024/25 (6 April 2024 – 5 April 2025) — Online deadline: 31 January 2026
|
||||
- 2025/26 (6 April 2025 – 5 April 2026) — Online deadline: 31 January 2027
|
||||
```
|
||||
|
||||
Load the corresponding tax rates reference file.
|
||||
|
||||
## Phase 2: Document Discovery
|
||||
|
||||
Scan the repository for tax-relevant documents. Use Glob to search for:
|
||||
|
||||
```
|
||||
**/*.pdf
|
||||
**/*.png **/*.jpg **/*.jpeg **/*.heic
|
||||
**/*.csv **/*.xlsx **/*.xls
|
||||
**/*.txt **/*.md
|
||||
```
|
||||
|
||||
Classify found documents by looking at filenames and content for keywords:
|
||||
- **P60 / P45**: employment, gross pay, tax deducted, PAYE reference
|
||||
- **P11D**: benefits in kind, company car, medical insurance
|
||||
- **Bank statements**: interest earned, annual summary
|
||||
- **Dividend vouchers**: dividend, shares, per share
|
||||
- **Rental records**: rent received, tenant, property expenses, mortgage interest
|
||||
- **Capital gains**: disposal, acquisition, shares sold, CGT, crypto
|
||||
- **Pension**: contribution statement, pension provider
|
||||
- **Student loan**: SLC, student loan, repayment
|
||||
- **HMRC letters**: tax code, SA302, coding notice
|
||||
- **Self-employment**: invoice, turnover, business expenses, profit and loss
|
||||
|
||||
Present a summary of all found documents grouped by type to the user via
|
||||
**AskUserQuestion**:
|
||||
|
||||
```
|
||||
I found the following tax documents in your repository:
|
||||
|
||||
📋 Employment:
|
||||
- documents/p60-2024-25.pdf
|
||||
- documents/p11d.pdf
|
||||
|
||||
🏦 Banking:
|
||||
- statements/hsbc-annual-summary.pdf
|
||||
- statements/savings-interest.csv
|
||||
|
||||
💰 Investments:
|
||||
- trading/dividend-vouchers.pdf
|
||||
- trading/share-sales-2024.csv
|
||||
|
||||
Are there any missing documents or additional income sources I should know about?
|
||||
```
|
||||
|
||||
Options should include: "These are all my documents", "I have additional documents to add",
|
||||
"I also have income not covered by documents (tell me about it)".
|
||||
|
||||
## Phase 3: Document Reading & Data Extraction
|
||||
|
||||
Read each document using the **Read** tool. The Read tool supports:
|
||||
- **PDFs**: Use `Read` with the file path. For large PDFs, use the `pages` parameter.
|
||||
- **Images**: Read tool displays images visually — extract numbers from P60 photos, screenshots, etc.
|
||||
- **CSVs/spreadsheets**: Read directly and parse the tabular data.
|
||||
- **Text/Markdown**: Read and extract relevant figures.
|
||||
|
||||
For each document, extract:
|
||||
1. **Amounts** — gross pay, tax deducted, interest earned, dividends, etc.
|
||||
2. **Dates** — to confirm they fall within the tax year
|
||||
3. **References** — employer PAYE reference, UTR, bank account details
|
||||
4. **Tax already paid** — PAYE deducted, tax on savings (if any)
|
||||
|
||||
**Cross-reference** documents where possible:
|
||||
- P60 gross pay should be consistent with payslips
|
||||
- Bank interest should match annual summaries
|
||||
- Share sale proceeds should match trading platform reports
|
||||
|
||||
Use **AskUserQuestion** for any ambiguities:
|
||||
- Unclear or partially visible figures
|
||||
- Multiple possible interpretations
|
||||
- Figures that seem inconsistent across documents
|
||||
- Whether an amount is gross or net
|
||||
|
||||
## Phase 4: Personal Circumstances
|
||||
|
||||
Ask questions about things that CANNOT be determined from documents. Batch related
|
||||
questions together using **AskUserQuestion** with multiple questions where possible.
|
||||
|
||||
**Essential questions (always ask):**
|
||||
1. Marital status / civil partnership — for Marriage Allowance eligibility
|
||||
2. Student loan plan type(s) — Plan 1, 2, 4, 5, or Postgraduate (or none)
|
||||
|
||||
**Conditional questions (ask if relevant based on income level or documents found):**
|
||||
3. If income > £60,000: Do you receive Child Benefit? How many children?
|
||||
4. If income > £100,000: Confirm adjusted net income for PA taper
|
||||
5. If married/civil partnership and one partner is a non-taxpayer: Transfer Marriage Allowance?
|
||||
6. Blind Person's Allowance?
|
||||
7. Any Gift Aid donations made during the tax year?
|
||||
8. Previous year's Self Assessment liability? (needed for Payment on Account calculation)
|
||||
9. If pension documents found: Confirm personal contribution amounts for higher rate relief
|
||||
|
||||
**Skip questions that are clearly answered by the documents.** For example, if the P60
|
||||
shows student loan deductions under Plan 2, don't ask about the plan type — just confirm.
|
||||
|
||||
## Phase 5: Income Categorisation
|
||||
|
||||
Organise all extracted data into Self Assessment categories. Map each piece of data
|
||||
to the correct SA form and box number using `references/forms-guide.md`.
|
||||
|
||||
Present a categorised summary to the user via **AskUserQuestion**:
|
||||
|
||||
```
|
||||
Here's what I've extracted from your documents. Please confirm these figures:
|
||||
|
||||
📋 EMPLOYMENT (SA102)
|
||||
Employer: Acme Corp (PAYE: 123/A456)
|
||||
Gross pay: £65,000.00
|
||||
Tax deducted: £13,432.00
|
||||
Benefits (P11D): £2,500.00
|
||||
|
||||
🏦 SAVINGS (SA100 Box 1)
|
||||
Bank interest: £850.00
|
||||
|
||||
💰 DIVIDENDS (SA100 Box 4)
|
||||
UK dividends: £3,200.00
|
||||
|
||||
🏠 RENTAL INCOME (SA105)
|
||||
Gross rents: £12,000.00
|
||||
Allowable expenses: £3,500.00
|
||||
Mortgage interest: £4,000.00 (20% tax reducer)
|
||||
Rental profit: £8,500.00
|
||||
|
||||
Are these figures correct?
|
||||
```
|
||||
|
||||
Options: "All correct", "I need to correct some figures (tell me which)".
|
||||
|
||||
## Phase 6: Allowances & Reliefs
|
||||
|
||||
Based on the categorised income, calculate all applicable allowances. Read the
|
||||
relevant tax rates reference file and apply:
|
||||
|
||||
1. **Personal Allowance** — £12,570 (with taper if income > £100,000)
|
||||
2. **Dividend Allowance** — £500 at 0%
|
||||
3. **Personal Savings Allowance** — £1,000 (basic) / £500 (higher) / £0 (additional)
|
||||
4. **Starting Rate for Savings** — 0% on up to £5,000 if applicable
|
||||
5. **CGT Annual Exempt Amount** — £3,000
|
||||
6. **Marriage Allowance** — £1,260 transferred
|
||||
7. **Pension Tax Relief** — Higher/additional rate relief on personal contributions
|
||||
8. **Gift Aid** — Extends basic rate band by gross amount
|
||||
9. **Trading Allowance** — £1,000 if claiming for small self-employment
|
||||
10. **Property Allowance** — £1,000 if claiming instead of actual expenses
|
||||
|
||||
## Phase 7: Tax Calculation
|
||||
|
||||
Build a JSON input object with all the gathered data and run the tax calculator:
|
||||
|
||||
```bash
|
||||
cat << 'TAXINPUT' | python3 "${CLAUDE_SKILL_DIR}/scripts/tax-calculator.py"
|
||||
{
|
||||
"tax_year": "2024-25",
|
||||
"employment": [
|
||||
{"gross_pay": 65000, "tax_deducted": 13432, "benefits": 2500}
|
||||
],
|
||||
"savings_interest": 850,
|
||||
"dividends": 3200,
|
||||
"rental": {"profit": 8500, "finance_costs": 4000},
|
||||
"pension": {"personal_gross": 0, "employer": 0},
|
||||
"student_loan_plans": ["plan2"],
|
||||
"child_benefit": {"num_children": 0},
|
||||
"gift_aid": 0,
|
||||
"blind_persons_allowance": false,
|
||||
"marriage_allowance_received": false,
|
||||
"marriage_allowance_given": false,
|
||||
"capital_gains": [],
|
||||
"other_income": 0,
|
||||
"other_tax_paid": 0,
|
||||
"previous_year_sa_liability": 0
|
||||
}
|
||||
TAXINPUT
|
||||
```
|
||||
|
||||
**Replace the example values with the actual extracted data.**
|
||||
|
||||
Parse the JSON output and present the calculation to the user. Use **AskUserQuestion**
|
||||
to check if anything looks wrong:
|
||||
|
||||
```
|
||||
Here's your tax calculation. Does anything look incorrect?
|
||||
- All looks correct, proceed
|
||||
- Something doesn't look right (tell me what)
|
||||
```
|
||||
|
||||
## Phase 8: Payment on Account
|
||||
|
||||
From the calculator output, check the `payment_on_account` section. If POA is required,
|
||||
explain to the user:
|
||||
|
||||
- How much each payment is
|
||||
- When payments are due (31 January and 31 July)
|
||||
- That they can apply to reduce POA if they expect lower income next year
|
||||
|
||||
## Phase 9: Return Output
|
||||
|
||||
Write two files to the repository:
|
||||
|
||||
### `tax-return-summary.md`
|
||||
A complete return summary with:
|
||||
- Tax year and relevant deadlines
|
||||
- SA forms needed (SA100, SA102, SA105, etc.)
|
||||
- Box-by-box values for each form
|
||||
- Payment schedule
|
||||
|
||||
### `tax-calculation-breakdown.md`
|
||||
A detailed calculation showing:
|
||||
- Income breakdown by category
|
||||
- Personal allowance calculation (including taper if applicable)
|
||||
- Income tax on each income type (non-savings, savings, dividends)
|
||||
- NIC calculation
|
||||
- Student loan repayments
|
||||
- CGT calculation
|
||||
- Child Benefit Charge
|
||||
- Tax reducers (finance costs, marriage allowance)
|
||||
- Total liability
|
||||
- Tax already paid
|
||||
- Amount owed or refund due
|
||||
- Payment on account for next year
|
||||
|
||||
## Phase 10: Headline Summary
|
||||
|
||||
After writing the files, print the headline numbers directly to the user:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
UK SELF ASSESSMENT {TAX_YEAR} — SUMMARY
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Total income: £XX,XXX.XX
|
||||
Personal allowance: £XX,XXX.XX
|
||||
Total income tax: £XX,XXX.XX
|
||||
National Insurance: £XX,XXX.XX
|
||||
Student loan repayments: £XX,XXX.XX
|
||||
Capital gains tax: £XX,XXX.XX
|
||||
Child benefit charge: £XX,XXX.XX
|
||||
─────────────────────────────────────
|
||||
TOTAL TAX DUE: £XX,XXX.XX
|
||||
Tax already paid (PAYE): £XX,XXX.XX
|
||||
═════════════════════════════════════
|
||||
AMOUNT OWED: £XX,XXX.XX [or REFUND DUE]
|
||||
|
||||
Payment on account (next year): £X,XXX.XX each
|
||||
─────────────────────────────────────
|
||||
TOTAL DUE 31 JANUARY: £XX,XXX.XX
|
||||
(includes 1st payment on account)
|
||||
|
||||
SA forms needed: SA100, SA102, SA105, SA108
|
||||
Files saved: tax-return-summary.md, tax-calculation-breakdown.md
|
||||
|
||||
⚠ DEADLINE: File online by {DEADLINE_DATE}
|
||||
⚠ PAY by {PAYMENT_DATE}
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
Include any warnings:
|
||||
- Items flagged for manual verification
|
||||
- Potential reliefs not claimed (e.g., marriage allowance, pension relief)
|
||||
- Approaching thresholds (e.g., income near £100k PA taper)
|
||||
- Missing documents that could affect the calculation
|
||||
|
||||
**REMINDER:** End with the disclaimer:
|
||||
> These calculations are for guidance only. Please verify all figures before
|
||||
> submitting to HMRC. Consider seeking professional advice for complex tax situations.
|
||||
|
||||
## Error Handling
|
||||
|
||||
- If no documents are found: Ask the user to describe their income sources and enter figures manually
|
||||
- If a PDF can't be read: Ask the user to provide the key figures from that document
|
||||
- If the calculator script fails: Fall back to manual calculation using the tax rates reference files
|
||||
- If figures seem unreasonable (e.g., negative income, tax > income): Flag and ask for confirmation
|
||||
|
|
@ -0,0 +1,165 @@
|
|||
# UK Self Assessment Forms Guide
|
||||
|
||||
## Main Return: SA100
|
||||
|
||||
Everyone must complete the SA100. Key sections and box numbers:
|
||||
|
||||
### Income (SA100 boxes)
|
||||
| Box | Description |
|
||||
|-----|-------------|
|
||||
| 1 | Untaxed UK interest (up to £10,000) |
|
||||
| 2 | Taxed UK interest |
|
||||
| 3 | Untaxed foreign interest (up to £2,000) |
|
||||
| 4 | UK dividends |
|
||||
| 5 | Foreign dividends (up to £300) |
|
||||
| 6 | UK life insurance policy gains |
|
||||
| 7 | Total of any other income |
|
||||
|
||||
### Tax Reliefs (SA100 boxes)
|
||||
| Box | Description |
|
||||
|-----|-------------|
|
||||
| 1 | Pension contributions (gross amount for relief at source schemes) |
|
||||
| 2 | Gift Aid donations |
|
||||
| 3 | Blind Person's Allowance |
|
||||
|
||||
### Student Loan
|
||||
| Box | Description |
|
||||
|-----|-------------|
|
||||
| 1 | Student loan plan type (1, 2, 4, or 5) |
|
||||
| 2 | Postgraduate loan |
|
||||
|
||||
### Marriage Allowance
|
||||
| Box | Description |
|
||||
|-----|-------------|
|
||||
| 1 | Transfer £1,260 to spouse/civil partner |
|
||||
| 2 | Receive £1,260 from spouse/civil partner |
|
||||
|
||||
---
|
||||
|
||||
## Supplementary Pages
|
||||
|
||||
### SA102 — Employment
|
||||
|
||||
**When needed:** You had employment income during the tax year.
|
||||
**One form per employer/directorship.**
|
||||
|
||||
| Box | Description | Source Document |
|
||||
|-----|-------------|----------------|
|
||||
| 1 | Pay from this employment | P60 box 1 / P45 |
|
||||
| 2 | Tax deducted | P60 box 2 / P45 |
|
||||
| 3 | Tips and other payments not on P60 | Payslips/records |
|
||||
| 5 | Employer's name | P60 |
|
||||
| 6 | PAYE reference | P60 |
|
||||
| 9 | Benefits from P11D - total cash equivalent | P11D total |
|
||||
| 11 | Expenses | Receipts/records |
|
||||
|
||||
### SA103S — Self-Employment (Short)
|
||||
|
||||
**When needed:** Self-employed with turnover below £85,000 and simple accounts.
|
||||
|
||||
| Box | Description |
|
||||
|-----|-------------|
|
||||
| 9 | Business name |
|
||||
| 10 | Business description |
|
||||
| 14 | Date started (if new) |
|
||||
| 15 | Date ended (if ceased) |
|
||||
| 17 | Turnover |
|
||||
| 20 | Trading allowance claimed (£1,000 max) |
|
||||
| 24 | Total allowable expenses |
|
||||
| 27 | Net profit |
|
||||
| 29 | Adjusted profit for tax |
|
||||
|
||||
### SA103F — Self-Employment (Full)
|
||||
|
||||
**When needed:** Self-employed with turnover of £85,000 or above, or complex accounts.
|
||||
|
||||
Same structure as SA103S but with detailed expense categories (cost of goods sold, premises costs, admin, travel, advertising, legal, bad debts, etc.).
|
||||
|
||||
### SA105 — UK Property
|
||||
|
||||
**When needed:** You receive rental income from UK land or property.
|
||||
|
||||
| Box | Description |
|
||||
|-----|-------------|
|
||||
| 1 | Number of properties rented out |
|
||||
| 5 | Total rents received |
|
||||
| 7 | Property allowance claimed (£1,000 max) |
|
||||
| 9-19 | Expenses breakdown (rent/rates, repairs, insurance, management, legal, etc.) |
|
||||
| 20 | Total allowable expenses |
|
||||
| 21 | Private use adjustment |
|
||||
| 24 | Taxable profit |
|
||||
| 25 | Adjusted profit for tax |
|
||||
| 38 | Residential finance costs (mortgage interest — 20% tax reducer) |
|
||||
| 39 | Unused finance costs from earlier years |
|
||||
|
||||
**Note:** From April 2020, mortgage interest is no longer deductible as an expense. Instead, a 20% tax reducer is given on finance costs (box 38).
|
||||
|
||||
### SA106 — Foreign
|
||||
|
||||
**When needed:** You have foreign income or gains, or claim Foreign Tax Credit Relief.
|
||||
|
||||
Key sections:
|
||||
- Foreign employment income
|
||||
- Foreign self-employment
|
||||
- Foreign property income
|
||||
- Foreign dividends (if over £300)
|
||||
- Foreign interest (if over £2,000)
|
||||
- Foreign Tax Credit Relief claims
|
||||
|
||||
### SA107 — Trusts etc.
|
||||
|
||||
**When needed:** You receive income from trusts, settlements, or the estate of a deceased person.
|
||||
|
||||
### SA108 — Capital Gains Summary
|
||||
|
||||
**When needed:** You dispose of assets and your total gains exceed the Annual Exempt Amount (£3,000), OR your total disposal proceeds exceed 4x the annual exempt amount (£12,000), OR you want to claim a loss.
|
||||
|
||||
| Box | Description |
|
||||
|-----|-------------|
|
||||
| 3 | Total gains (before losses and annual exempt amount) |
|
||||
| 4 | Total losses |
|
||||
| 6 | Gains qualifying for Business Asset Disposal Relief |
|
||||
| 7 | Gains from residential property |
|
||||
| 8 | Other gains |
|
||||
| 9 | Losses brought forward |
|
||||
| 15 | Annual exempt amount used |
|
||||
| 20 | CGT due |
|
||||
|
||||
### SA109 — Residence, Remittance Basis etc.
|
||||
|
||||
**When needed:** You were not resident in the UK, or you were a dual resident, or you claim the remittance basis.
|
||||
|
||||
### SA101 — Additional Information
|
||||
|
||||
**When needed:** Covers less common situations:
|
||||
- Share schemes (Enterprise Management Incentives, Company Share Option Plans, etc.)
|
||||
- Post-cessation receipts
|
||||
- Pension charges and unauthorised payments
|
||||
- Tax avoidance schemes (DOTAS)
|
||||
- Disguised remuneration
|
||||
- Other income not covered by other supplementary pages
|
||||
|
||||
### SA110 — Tax Calculation Summary
|
||||
|
||||
Auto-generated when filing online. Shows the complete tax computation.
|
||||
|
||||
---
|
||||
|
||||
## Document → Form Mapping
|
||||
|
||||
| Document | SA Form | Key Data to Extract |
|
||||
|----------|---------|-------------------|
|
||||
| P60 | SA102 | Gross pay, tax deducted, NIC, employer PAYE ref |
|
||||
| P45 | SA102 | Pay to date, tax to date, leaving date |
|
||||
| P11D | SA102 | Benefits in kind (car, medical, etc.) |
|
||||
| Bank interest summary | SA100 | Total interest received |
|
||||
| Dividend vouchers | SA100 | Gross dividend amounts |
|
||||
| Share dealing statements | SA108 | Disposal proceeds, acquisition costs, dates |
|
||||
| Property rental accounts | SA105 | Rents, expenses, mortgage interest |
|
||||
| Self-employment accounts | SA103S/F | Turnover, expenses, profit |
|
||||
| Pension statement | SA100 | Contributions paid (for higher rate relief) |
|
||||
| Student loan statement | SA100 | Plan type, balance (for repayment calc) |
|
||||
| HMRC coding notice | Reference | Tax code, estimated income, deductions |
|
||||
| SA302 (prior year) | Reference | Previous year's liability (for payment on account) |
|
||||
| Gift Aid receipts | SA100 | Total qualifying donations |
|
||||
| Crypto exchange reports | SA108 | Disposal proceeds, costs per trade |
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
# UK Tax Rates and Allowances — 2024/25
|
||||
|
||||
Tax year: 6 April 2024 to 5 April 2025
|
||||
|
||||
## Income Tax Bands
|
||||
|
||||
| Band | Taxable Income | Rate |
|
||||
|------|---------------|------|
|
||||
| Personal Allowance | £0 – £12,570 | 0% |
|
||||
| Basic Rate | £12,571 – £50,270 | 20% |
|
||||
| Higher Rate | £50,271 – £125,140 | 40% |
|
||||
| Additional Rate | Over £125,140 | 45% |
|
||||
|
||||
**Personal Allowance taper:** Reduced by £1 for every £2 of income above £100,000. Reaches zero at £125,140. This creates an effective 60% marginal rate between £100,000 and £125,140.
|
||||
|
||||
## Dividend Tax Rates
|
||||
|
||||
| Band | Rate |
|
||||
|------|------|
|
||||
| Dividend Allowance | £500 at 0% |
|
||||
| Basic Rate | 8.75% |
|
||||
| Higher Rate | 33.75% |
|
||||
| Additional Rate | 39.35% |
|
||||
|
||||
Dividends use up the basic/higher/additional rate bands but are taxed at dividend-specific rates. Dividends are treated as the top slice of income.
|
||||
|
||||
## Savings Income
|
||||
|
||||
| Band | Rate |
|
||||
|------|------|
|
||||
| Starting Rate for Savings | 0% on up to £5,000 (reduced by non-savings income above personal allowance) |
|
||||
| Personal Savings Allowance (basic rate taxpayer) | £1,000 at 0% |
|
||||
| Personal Savings Allowance (higher rate taxpayer) | £500 at 0% |
|
||||
| Personal Savings Allowance (additional rate taxpayer) | £0 |
|
||||
|
||||
Savings income is taxed after non-savings income but before dividends.
|
||||
|
||||
## National Insurance Contributions
|
||||
|
||||
### Employee (Class 1)
|
||||
| Threshold | Amount |
|
||||
|-----------|--------|
|
||||
| Primary Threshold | £12,570/year (£242/week) |
|
||||
| Upper Earnings Limit | £50,270/year (£967/week) |
|
||||
| Main Rate (PT to UEL) | 8% |
|
||||
| Additional Rate (above UEL) | 2% |
|
||||
|
||||
### Self-Employed (Class 4)
|
||||
| Threshold | Amount |
|
||||
|-----------|--------|
|
||||
| Lower Profits Limit | £12,570 |
|
||||
| Upper Profits Limit | £50,270 |
|
||||
| Main Rate | 6% |
|
||||
| Additional Rate | 2% |
|
||||
|
||||
### Class 2 NIC
|
||||
Abolished from 2024/25 (voluntary contributions: £3.45/week for state pension qualification).
|
||||
|
||||
## Student Loan Repayment Thresholds
|
||||
|
||||
| Plan | Annual Threshold | Rate |
|
||||
|------|-----------------|------|
|
||||
| Plan 1 | £22,015 | 9% |
|
||||
| Plan 2 | £27,295 | 9% |
|
||||
| Plan 4 | £27,660 | 9% |
|
||||
| Plan 5 | £25,000 | 9% |
|
||||
| Postgraduate Loan | £21,000 | 6% |
|
||||
|
||||
## Capital Gains Tax
|
||||
|
||||
### Annual Exempt Amount
|
||||
£3,000
|
||||
|
||||
### CGT Rates
|
||||
|
||||
**Disposals before 30 October 2024:**
|
||||
| Asset Type | Basic Rate | Higher Rate |
|
||||
|-----------|-----------|------------|
|
||||
| Most assets | 10% | 20% |
|
||||
| Residential property | 18% | 28% |
|
||||
|
||||
**Disposals from 30 October 2024:**
|
||||
| Asset Type | Basic Rate | Higher Rate |
|
||||
|-----------|-----------|------------|
|
||||
| All assets | 18% | 24% |
|
||||
|
||||
Business Asset Disposal Relief: 10% (lifetime limit £1 million)
|
||||
|
||||
## Key Allowances
|
||||
|
||||
| Allowance | Amount |
|
||||
|-----------|--------|
|
||||
| Personal Allowance | £12,570 |
|
||||
| Marriage Allowance (transferable) | £1,260 |
|
||||
| Blind Person's Allowance | £2,870 |
|
||||
| Trading Allowance | £1,000 |
|
||||
| Property Allowance | £1,000 |
|
||||
| Dividend Allowance | £500 |
|
||||
| Personal Savings Allowance (basic) | £1,000 |
|
||||
| Personal Savings Allowance (higher) | £500 |
|
||||
| CGT Annual Exempt Amount | £3,000 |
|
||||
| Pension Annual Allowance | £60,000 |
|
||||
| ISA Annual Allowance | £20,000 |
|
||||
|
||||
## High Income Child Benefit Charge
|
||||
|
||||
Applies when adjusted net income exceeds £60,000 (2024/25). Charge = 1% of child benefit for every £200 of income over £60,000. Full charge at £80,000.
|
||||
|
||||
**Child Benefit rates 2024/25:**
|
||||
- Eldest/only child: £25.60/week (£1,331.20/year)
|
||||
- Each additional child: £16.95/week (£881.40/year)
|
||||
|
||||
## Pension Tax Relief
|
||||
|
||||
| Parameter | Amount |
|
||||
|-----------|--------|
|
||||
| Annual Allowance | £60,000 |
|
||||
| Money Purchase Annual Allowance | £10,000 |
|
||||
| Tapered Annual Allowance starts | Adjusted income > £260,000 |
|
||||
| Minimum Tapered Allowance | £10,000 |
|
||||
| Carry Forward | Previous 3 tax years |
|
||||
|
||||
## Key Deadlines
|
||||
|
||||
| Event | Date |
|
||||
|-------|------|
|
||||
| Tax year ends | 5 April 2025 |
|
||||
| Register for SA | 5 October 2025 |
|
||||
| Paper return deadline | 31 October 2025 |
|
||||
| Online return deadline | 31 January 2026 |
|
||||
| Tax payment due | 31 January 2026 |
|
||||
| 2nd payment on account | 31 July 2026 |
|
||||
|
||||
## Late Filing Penalties
|
||||
|
||||
| Delay | Penalty |
|
||||
|-------|---------|
|
||||
| 1 day late | £100 fixed |
|
||||
| 3 months late | £10/day for up to 90 days (max £900) |
|
||||
| 6 months late | 5% of tax due or £300 (whichever greater) |
|
||||
| 12 months late | Further 5% of tax due or £300 (whichever greater) |
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
# UK Tax Rates and Allowances — 2025/26
|
||||
|
||||
Tax year: 6 April 2025 to 5 April 2026
|
||||
|
||||
## Income Tax Bands
|
||||
|
||||
| Band | Taxable Income | Rate |
|
||||
|------|---------------|------|
|
||||
| Personal Allowance | £0 – £12,570 | 0% |
|
||||
| Basic Rate | £12,571 – £50,270 | 20% |
|
||||
| Higher Rate | £50,271 – £125,140 | 40% |
|
||||
| Additional Rate | Over £125,140 | 45% |
|
||||
|
||||
**Personal Allowance taper:** Reduced by £1 for every £2 of income above £100,000. Reaches zero at £125,140. Effective 60% marginal rate between £100,000 and £125,140.
|
||||
|
||||
**Frozen until April 2028** (extended to April 2031 in Budget 2025).
|
||||
|
||||
## Dividend Tax Rates
|
||||
|
||||
| Band | Rate |
|
||||
|------|------|
|
||||
| Dividend Allowance | £500 at 0% |
|
||||
| Basic Rate | 8.75% |
|
||||
| Higher Rate | 33.75% |
|
||||
| Additional Rate | 39.35% |
|
||||
|
||||
## Savings Income
|
||||
|
||||
| Band | Rate |
|
||||
|------|------|
|
||||
| Starting Rate for Savings | 0% on up to £5,000 (reduced by non-savings income above personal allowance) |
|
||||
| Personal Savings Allowance (basic rate taxpayer) | £1,000 at 0% |
|
||||
| Personal Savings Allowance (higher rate taxpayer) | £500 at 0% |
|
||||
| Personal Savings Allowance (additional rate taxpayer) | £0 |
|
||||
|
||||
## National Insurance Contributions
|
||||
|
||||
### Employee (Class 1)
|
||||
| Threshold | Amount |
|
||||
|-----------|--------|
|
||||
| Primary Threshold | £12,570/year (£242/week) |
|
||||
| Upper Earnings Limit | £50,270/year (£967/week) |
|
||||
| Main Rate (PT to UEL) | 8% |
|
||||
| Additional Rate (above UEL) | 2% |
|
||||
|
||||
### Self-Employed (Class 4)
|
||||
| Threshold | Amount |
|
||||
|-----------|--------|
|
||||
| Lower Profits Limit | £12,570 |
|
||||
| Upper Profits Limit | £50,270 |
|
||||
| Main Rate | 6% |
|
||||
| Additional Rate | 2% |
|
||||
|
||||
### Class 2 NIC
|
||||
Abolished (voluntary contributions: £3.45/week for state pension qualification).
|
||||
|
||||
### Employer NIC (for reference)
|
||||
| Parameter | Amount |
|
||||
|-----------|--------|
|
||||
| Rate | 15% (up from 13.8%) |
|
||||
| Secondary Threshold | £5,000/year (down from £9,100) |
|
||||
| Employment Allowance | £10,500 (up from £5,000) |
|
||||
|
||||
## Student Loan Repayment Thresholds
|
||||
|
||||
| Plan | Annual Threshold | Rate |
|
||||
|------|-----------------|------|
|
||||
| Plan 1 | £26,065 | 9% |
|
||||
| Plan 2 | £28,470 | 9% |
|
||||
| Plan 4 | £32,745 | 9% |
|
||||
| Plan 5 | £25,000 | 9% |
|
||||
| Postgraduate Loan | £21,000 | 6% |
|
||||
|
||||
## Capital Gains Tax
|
||||
|
||||
### Annual Exempt Amount
|
||||
£3,000
|
||||
|
||||
### CGT Rates (full year — unified from Budget October 2024)
|
||||
| Asset Type | Basic Rate | Higher Rate |
|
||||
|-----------|-----------|------------|
|
||||
| All assets | 18% | 24% |
|
||||
|
||||
Business Asset Disposal Relief: 14% (lifetime limit £1 million) — rising to 18% from April 2026.
|
||||
|
||||
## Key Allowances
|
||||
|
||||
| Allowance | Amount |
|
||||
|-----------|--------|
|
||||
| Personal Allowance | £12,570 |
|
||||
| Marriage Allowance (transferable) | £1,260 |
|
||||
| Blind Person's Allowance | £3,070 |
|
||||
| Trading Allowance | £1,000 |
|
||||
| Property Allowance | £1,000 |
|
||||
| Dividend Allowance | £500 |
|
||||
| Personal Savings Allowance (basic) | £1,000 |
|
||||
| Personal Savings Allowance (higher) | £500 |
|
||||
| CGT Annual Exempt Amount | £3,000 |
|
||||
| Pension Annual Allowance | £60,000 |
|
||||
| ISA Annual Allowance | £20,000 |
|
||||
|
||||
## High Income Child Benefit Charge
|
||||
|
||||
Applies when adjusted net income exceeds £60,000. Charge = 1% of child benefit for every £200 of income over £60,000. Full charge at £80,000.
|
||||
|
||||
**Child Benefit rates 2025/26:**
|
||||
- Eldest/only child: £26.05/week (£1,354.60/year)
|
||||
- Each additional child: £17.25/week (£897.00/year)
|
||||
|
||||
## Pension Tax Relief
|
||||
|
||||
| Parameter | Amount |
|
||||
|-----------|--------|
|
||||
| Annual Allowance | £60,000 |
|
||||
| Money Purchase Annual Allowance | £10,000 |
|
||||
| Tapered Annual Allowance starts | Adjusted income > £260,000 |
|
||||
| Minimum Tapered Allowance | £10,000 |
|
||||
| Carry Forward | Previous 3 tax years |
|
||||
|
||||
## Key Changes from 2024/25
|
||||
|
||||
1. **Employer NIC:** Rate up from 13.8% to 15%; secondary threshold down from £9,100 to £5,000
|
||||
2. **Employment Allowance:** Up from £5,000 to £10,500 (£100k cap removed)
|
||||
3. **CGT rates unified:** 18%/24% applies for full year (no split-year calculation)
|
||||
4. **Business Asset Disposal Relief:** Rate increases to 14% (was 10%)
|
||||
5. **Furnished Holiday Lettings:** Special tax treatment abolished
|
||||
6. **Non-dom regime:** Abolished, replaced with residence-based system
|
||||
7. **MTD for Income Tax:** Launches April 2026 for self-employed/landlords with income > £50,000
|
||||
|
||||
## Key Deadlines
|
||||
|
||||
| Event | Date |
|
||||
|-------|------|
|
||||
| Tax year ends | 5 April 2026 |
|
||||
| Register for SA | 5 October 2026 |
|
||||
| Paper return deadline | 31 October 2026 |
|
||||
| Online return deadline | 31 January 2027 |
|
||||
| Tax payment due | 31 January 2027 |
|
||||
| 2nd payment on account | 31 July 2027 |
|
||||
|
|
@ -0,0 +1,678 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
UK Self Assessment Tax Calculator
|
||||
|
||||
Accepts JSON input with income/deduction data via stdin or file argument.
|
||||
Calculates income tax, NIC, student loans, CGT, and outputs JSON breakdown.
|
||||
|
||||
Usage:
|
||||
echo '{"tax_year": "2024-25", "employment": [{"gross_pay": 45000, "tax_deducted": 6486}]}' | python3 tax-calculator.py
|
||||
python3 tax-calculator.py input.json
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Optional
|
||||
|
||||
# ─── Tax rates by year ────────────────────────────────────────────────────────
|
||||
|
||||
TAX_RATES = {
|
||||
"2024-25": {
|
||||
"personal_allowance": 12570,
|
||||
"pa_taper_threshold": 100000,
|
||||
"basic_rate_limit": 37700, # 12571 to 50270
|
||||
"higher_rate_limit": 87440, # 50271 to 125140 (125140 - 37700 = 87440 taxable)
|
||||
"basic_rate": 0.20,
|
||||
"higher_rate": 0.40,
|
||||
"additional_rate": 0.45,
|
||||
"basic_rate_ceiling": 50270, # PA + basic_rate_limit
|
||||
"additional_rate_floor": 125140,
|
||||
"dividend_allowance": 500,
|
||||
"dividend_basic": 0.0875,
|
||||
"dividend_higher": 0.3375,
|
||||
"dividend_additional": 0.3935,
|
||||
"savings_starting_rate_limit": 5000,
|
||||
"psa_basic": 1000,
|
||||
"psa_higher": 500,
|
||||
"psa_additional": 0,
|
||||
"marriage_allowance": 1260,
|
||||
"blind_persons_allowance": 2870,
|
||||
"trading_allowance": 1000,
|
||||
"property_allowance": 1000,
|
||||
# NIC Class 1 (employee)
|
||||
"nic1_primary_threshold": 12570,
|
||||
"nic1_upper_earnings_limit": 50270,
|
||||
"nic1_main_rate": 0.08,
|
||||
"nic1_additional_rate": 0.02,
|
||||
# NIC Class 4 (self-employed)
|
||||
"nic4_lower_profits": 12570,
|
||||
"nic4_upper_profits": 50270,
|
||||
"nic4_main_rate": 0.06,
|
||||
"nic4_additional_rate": 0.02,
|
||||
# Student loans
|
||||
"student_loan_plan1_threshold": 22015,
|
||||
"student_loan_plan2_threshold": 27295,
|
||||
"student_loan_plan4_threshold": 27660,
|
||||
"student_loan_plan5_threshold": 25000,
|
||||
"student_loan_postgrad_threshold": 21000,
|
||||
"student_loan_rate": 0.09,
|
||||
"postgrad_loan_rate": 0.06,
|
||||
# CGT
|
||||
"cgt_annual_exempt": 3000,
|
||||
"cgt_basic_rate": 0.18, # post-Oct 2024 unified rate
|
||||
"cgt_higher_rate": 0.24,
|
||||
"cgt_property_basic_rate": 0.18,
|
||||
"cgt_property_higher_rate": 0.24,
|
||||
"cgt_badr_rate": 0.10,
|
||||
# Child benefit
|
||||
"child_benefit_threshold": 60000,
|
||||
"child_benefit_taper_end": 80000,
|
||||
"child_benefit_eldest": 1331.20,
|
||||
"child_benefit_other": 881.40,
|
||||
# Pension
|
||||
"pension_annual_allowance": 60000,
|
||||
},
|
||||
"2025-26": {
|
||||
"personal_allowance": 12570,
|
||||
"pa_taper_threshold": 100000,
|
||||
"basic_rate_limit": 37700,
|
||||
"higher_rate_limit": 87440,
|
||||
"basic_rate": 0.20,
|
||||
"higher_rate": 0.40,
|
||||
"additional_rate": 0.45,
|
||||
"basic_rate_ceiling": 50270,
|
||||
"additional_rate_floor": 125140,
|
||||
"dividend_allowance": 500,
|
||||
"dividend_basic": 0.0875,
|
||||
"dividend_higher": 0.3375,
|
||||
"dividend_additional": 0.3935,
|
||||
"savings_starting_rate_limit": 5000,
|
||||
"psa_basic": 1000,
|
||||
"psa_higher": 500,
|
||||
"psa_additional": 0,
|
||||
"marriage_allowance": 1260,
|
||||
"blind_persons_allowance": 3070,
|
||||
"trading_allowance": 1000,
|
||||
"property_allowance": 1000,
|
||||
# NIC Class 1
|
||||
"nic1_primary_threshold": 12570,
|
||||
"nic1_upper_earnings_limit": 50270,
|
||||
"nic1_main_rate": 0.08,
|
||||
"nic1_additional_rate": 0.02,
|
||||
# NIC Class 4
|
||||
"nic4_lower_profits": 12570,
|
||||
"nic4_upper_profits": 50270,
|
||||
"nic4_main_rate": 0.06,
|
||||
"nic4_additional_rate": 0.02,
|
||||
# Student loans
|
||||
"student_loan_plan1_threshold": 26065,
|
||||
"student_loan_plan2_threshold": 28470,
|
||||
"student_loan_plan4_threshold": 32745,
|
||||
"student_loan_plan5_threshold": 25000,
|
||||
"student_loan_postgrad_threshold": 21000,
|
||||
"student_loan_rate": 0.09,
|
||||
"postgrad_loan_rate": 0.06,
|
||||
# CGT
|
||||
"cgt_annual_exempt": 3000,
|
||||
"cgt_basic_rate": 0.18,
|
||||
"cgt_higher_rate": 0.24,
|
||||
"cgt_property_basic_rate": 0.18,
|
||||
"cgt_property_higher_rate": 0.24,
|
||||
"cgt_badr_rate": 0.14,
|
||||
# Child benefit
|
||||
"child_benefit_threshold": 60000,
|
||||
"child_benefit_taper_end": 80000,
|
||||
"child_benefit_eldest": 1354.60,
|
||||
"child_benefit_other": 897.00,
|
||||
# Pension
|
||||
"pension_annual_allowance": 60000,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def round_tax(amount: float) -> float:
|
||||
"""Round to 2 decimal places."""
|
||||
return round(amount, 2)
|
||||
|
||||
|
||||
def calculate_personal_allowance(total_income: float, rates: dict, blind: bool = False, marriage_allowance_received: bool = False, marriage_allowance_given: bool = False) -> float:
|
||||
"""Calculate personal allowance after taper and adjustments."""
|
||||
pa = rates["personal_allowance"]
|
||||
|
||||
# Blind Person's Allowance
|
||||
if blind:
|
||||
pa += rates["blind_persons_allowance"]
|
||||
|
||||
# Marriage Allowance
|
||||
if marriage_allowance_received:
|
||||
pa += rates["marriage_allowance"]
|
||||
if marriage_allowance_given:
|
||||
pa -= rates["marriage_allowance"]
|
||||
|
||||
# Taper for income over £100,000
|
||||
if total_income > rates["pa_taper_threshold"]:
|
||||
excess = total_income - rates["pa_taper_threshold"]
|
||||
reduction = excess / 2
|
||||
pa = max(0, pa - reduction)
|
||||
|
||||
return pa
|
||||
|
||||
|
||||
def calculate_income_tax(non_savings: float, savings: float, dividends: float, rates: dict, personal_allowance: float, pension_relief_gross: float = 0) -> dict:
|
||||
"""
|
||||
Calculate income tax using the correct stacking order:
|
||||
non-savings income → savings income → dividend income.
|
||||
|
||||
Pension contributions extend the basic rate band.
|
||||
"""
|
||||
# Extend basic rate band by gross pension contributions
|
||||
extended_basic_limit = rates["basic_rate_limit"] + pension_relief_gross
|
||||
|
||||
# Total income for band allocation
|
||||
total_income = non_savings + savings + dividends
|
||||
|
||||
# Apply personal allowance — allocated against non-savings first, then savings, then dividends
|
||||
remaining_pa = personal_allowance
|
||||
|
||||
taxable_non_savings = max(0, non_savings - remaining_pa)
|
||||
remaining_pa = max(0, remaining_pa - non_savings)
|
||||
|
||||
taxable_savings = max(0, savings - remaining_pa)
|
||||
remaining_pa = max(0, remaining_pa - savings)
|
||||
|
||||
taxable_dividends = max(0, dividends - remaining_pa)
|
||||
|
||||
# ─── Non-savings income tax ───
|
||||
ns_tax = 0
|
||||
ns_basic = 0
|
||||
ns_higher = 0
|
||||
ns_additional = 0
|
||||
band_used = 0 # how much of the basic rate band has been used
|
||||
|
||||
if taxable_non_savings > 0:
|
||||
# Basic rate
|
||||
basic_amount = min(taxable_non_savings, extended_basic_limit)
|
||||
ns_basic = basic_amount
|
||||
ns_tax += basic_amount * rates["basic_rate"]
|
||||
band_used += basic_amount
|
||||
|
||||
# Higher rate
|
||||
remaining = taxable_non_savings - basic_amount
|
||||
if remaining > 0:
|
||||
higher_limit = rates["additional_rate_floor"] - rates["personal_allowance"] - extended_basic_limit
|
||||
higher_amount = min(remaining, max(0, higher_limit))
|
||||
ns_higher = higher_amount
|
||||
ns_tax += higher_amount * rates["higher_rate"]
|
||||
|
||||
# Additional rate
|
||||
additional_amount = remaining - higher_amount
|
||||
if additional_amount > 0:
|
||||
ns_additional = additional_amount
|
||||
ns_tax += additional_amount * rates["additional_rate"]
|
||||
|
||||
# ─── Savings income tax ───
|
||||
sav_tax = 0
|
||||
sav_basic = 0
|
||||
sav_higher = 0
|
||||
sav_additional = 0
|
||||
|
||||
# Starting rate for savings (0% on up to £5,000 if non-savings income < PA + £5,000)
|
||||
savings_starting_rate_available = max(0, rates["savings_starting_rate_limit"] - max(0, taxable_non_savings))
|
||||
|
||||
# Personal Savings Allowance
|
||||
# Determine taxpayer band based on total taxable income for PSA
|
||||
total_taxable = taxable_non_savings + taxable_savings + taxable_dividends
|
||||
if total_taxable <= extended_basic_limit:
|
||||
psa = rates["psa_basic"]
|
||||
elif total_taxable <= (rates["additional_rate_floor"] - rates["personal_allowance"]):
|
||||
psa = rates["psa_higher"]
|
||||
else:
|
||||
psa = rates["psa_additional"]
|
||||
|
||||
if taxable_savings > 0:
|
||||
remaining_savings = taxable_savings
|
||||
|
||||
# Starting rate for savings (0%)
|
||||
starting_rate_amount = min(remaining_savings, savings_starting_rate_available)
|
||||
remaining_savings -= starting_rate_amount
|
||||
band_used_for_savings = band_used + starting_rate_amount
|
||||
|
||||
# PSA (0%)
|
||||
psa_amount = min(remaining_savings, psa)
|
||||
remaining_savings -= psa_amount
|
||||
band_used_for_savings += psa_amount
|
||||
|
||||
# Basic rate
|
||||
basic_remaining = max(0, extended_basic_limit - band_used_for_savings)
|
||||
sav_basic_amount = min(remaining_savings, basic_remaining)
|
||||
sav_basic = sav_basic_amount
|
||||
sav_tax += sav_basic_amount * rates["basic_rate"]
|
||||
remaining_savings -= sav_basic_amount
|
||||
band_used_for_savings += sav_basic_amount
|
||||
|
||||
# Higher rate
|
||||
if remaining_savings > 0:
|
||||
higher_limit = rates["additional_rate_floor"] - rates["personal_allowance"] - extended_basic_limit
|
||||
# Adjust for non-savings that already used higher band
|
||||
higher_used_by_ns = max(0, taxable_non_savings - extended_basic_limit)
|
||||
higher_remaining = max(0, higher_limit - higher_used_by_ns)
|
||||
sav_higher_amount = min(remaining_savings, higher_remaining)
|
||||
sav_higher = sav_higher_amount
|
||||
sav_tax += sav_higher_amount * rates["higher_rate"]
|
||||
remaining_savings -= sav_higher_amount
|
||||
|
||||
# Additional rate
|
||||
if remaining_savings > 0:
|
||||
sav_additional = remaining_savings
|
||||
sav_tax += remaining_savings * rates["additional_rate"]
|
||||
|
||||
# ─── Dividend income tax ───
|
||||
div_tax = 0
|
||||
div_basic = 0
|
||||
div_higher = 0
|
||||
div_additional = 0
|
||||
|
||||
if taxable_dividends > 0:
|
||||
remaining_divs = taxable_dividends
|
||||
|
||||
# Dividend allowance (£500 at 0%)
|
||||
div_allowance_used = min(remaining_divs, rates["dividend_allowance"])
|
||||
remaining_divs -= div_allowance_used
|
||||
|
||||
# Determine which band dividends fall into
|
||||
income_before_divs = taxable_non_savings + taxable_savings
|
||||
total_band_used = income_before_divs + div_allowance_used
|
||||
|
||||
# Basic rate band remaining
|
||||
basic_remaining = max(0, extended_basic_limit - total_band_used)
|
||||
div_basic_amount = min(remaining_divs, basic_remaining)
|
||||
div_basic = div_basic_amount
|
||||
div_tax += div_basic_amount * rates["dividend_basic"]
|
||||
remaining_divs -= div_basic_amount
|
||||
total_band_used += div_basic_amount
|
||||
|
||||
# Higher rate
|
||||
if remaining_divs > 0:
|
||||
higher_ceiling = rates["additional_rate_floor"] - rates["personal_allowance"]
|
||||
higher_remaining = max(0, higher_ceiling - total_band_used)
|
||||
div_higher_amount = min(remaining_divs, higher_remaining)
|
||||
div_higher = div_higher_amount
|
||||
div_tax += div_higher_amount * rates["dividend_higher"]
|
||||
remaining_divs -= div_higher_amount
|
||||
|
||||
# Additional rate
|
||||
if remaining_divs > 0:
|
||||
div_additional = remaining_divs
|
||||
div_tax += remaining_divs * rates["dividend_additional"]
|
||||
|
||||
total_tax = round_tax(ns_tax + sav_tax + div_tax)
|
||||
|
||||
return {
|
||||
"non_savings_income": round_tax(non_savings),
|
||||
"taxable_non_savings": round_tax(taxable_non_savings),
|
||||
"savings_income": round_tax(savings),
|
||||
"taxable_savings": round_tax(taxable_savings),
|
||||
"dividend_income": round_tax(dividends),
|
||||
"taxable_dividends": round_tax(taxable_dividends),
|
||||
"personal_allowance_used": round_tax(personal_allowance),
|
||||
"non_savings_tax": round_tax(ns_tax),
|
||||
"non_savings_breakdown": {
|
||||
"basic_rate": round_tax(ns_basic),
|
||||
"higher_rate": round_tax(ns_higher),
|
||||
"additional_rate": round_tax(ns_additional),
|
||||
},
|
||||
"savings_tax": round_tax(sav_tax),
|
||||
"savings_breakdown": {
|
||||
"starting_rate_0pc": round_tax(min(taxable_savings, savings_starting_rate_available) if taxable_savings > 0 else 0),
|
||||
"psa_0pc": round_tax(min(max(0, taxable_savings - savings_starting_rate_available), psa) if taxable_savings > 0 else 0),
|
||||
"basic_rate": round_tax(sav_basic),
|
||||
"higher_rate": round_tax(sav_higher),
|
||||
"additional_rate": round_tax(sav_additional),
|
||||
},
|
||||
"dividend_tax": round_tax(div_tax),
|
||||
"dividend_breakdown": {
|
||||
"allowance_0pc": round_tax(min(taxable_dividends, rates["dividend_allowance"]) if taxable_dividends > 0 else 0),
|
||||
"basic_rate": round_tax(div_basic),
|
||||
"higher_rate": round_tax(div_higher),
|
||||
"additional_rate": round_tax(div_additional),
|
||||
},
|
||||
"total_income_tax": total_tax,
|
||||
}
|
||||
|
||||
|
||||
def calculate_nic_class4(self_employment_profit: float, rates: dict) -> dict:
|
||||
"""Calculate Class 4 NIC on self-employment profits."""
|
||||
if self_employment_profit <= rates["nic4_lower_profits"]:
|
||||
return {"class4_nic": 0, "main_rate_amount": 0, "additional_rate_amount": 0}
|
||||
|
||||
main_rate_amount = min(
|
||||
max(0, self_employment_profit - rates["nic4_lower_profits"]),
|
||||
rates["nic4_upper_profits"] - rates["nic4_lower_profits"],
|
||||
)
|
||||
additional_rate_amount = max(0, self_employment_profit - rates["nic4_upper_profits"])
|
||||
|
||||
class4 = (main_rate_amount * rates["nic4_main_rate"]) + (additional_rate_amount * rates["nic4_additional_rate"])
|
||||
|
||||
return {
|
||||
"class4_nic": round_tax(class4),
|
||||
"main_rate_amount": round_tax(main_rate_amount),
|
||||
"additional_rate_amount": round_tax(additional_rate_amount),
|
||||
}
|
||||
|
||||
|
||||
def calculate_student_loan(gross_income: float, plans: list, rates: dict) -> dict:
|
||||
"""Calculate student loan repayments."""
|
||||
result = {}
|
||||
total = 0
|
||||
|
||||
for plan in plans:
|
||||
plan_lower = plan.lower().replace(" ", "")
|
||||
if plan_lower in ("plan1", "1"):
|
||||
threshold = rates["student_loan_plan1_threshold"]
|
||||
rate = rates["student_loan_rate"]
|
||||
key = "plan1"
|
||||
elif plan_lower in ("plan2", "2"):
|
||||
threshold = rates["student_loan_plan2_threshold"]
|
||||
rate = rates["student_loan_rate"]
|
||||
key = "plan2"
|
||||
elif plan_lower in ("plan4", "4"):
|
||||
threshold = rates["student_loan_plan4_threshold"]
|
||||
rate = rates["student_loan_rate"]
|
||||
key = "plan4"
|
||||
elif plan_lower in ("plan5", "5"):
|
||||
threshold = rates["student_loan_plan5_threshold"]
|
||||
rate = rates["student_loan_rate"]
|
||||
key = "plan5"
|
||||
elif plan_lower in ("postgraduate", "postgrad", "pg"):
|
||||
threshold = rates["student_loan_postgrad_threshold"]
|
||||
rate = rates["postgrad_loan_rate"]
|
||||
key = "postgraduate"
|
||||
else:
|
||||
continue
|
||||
|
||||
repayment = max(0, (gross_income - threshold) * rate)
|
||||
result[key] = {
|
||||
"threshold": threshold,
|
||||
"rate": rate,
|
||||
"repayment": round_tax(repayment),
|
||||
}
|
||||
total += repayment
|
||||
|
||||
result["total_student_loan"] = round_tax(total)
|
||||
return result
|
||||
|
||||
|
||||
def calculate_cgt(gains: list, rates: dict, remaining_basic_band: float) -> dict:
|
||||
"""
|
||||
Calculate Capital Gains Tax.
|
||||
|
||||
gains: list of {"proceeds": float, "cost": float, "type": "shares"|"property"|"other", "badr": bool}
|
||||
remaining_basic_band: how much of the basic rate band is unused after income tax
|
||||
"""
|
||||
total_gains = 0
|
||||
total_losses = 0
|
||||
gains_by_type = {"shares": 0, "property": 0, "other": 0, "badr": 0}
|
||||
|
||||
for g in gains:
|
||||
gain = g.get("proceeds", 0) - g.get("cost", 0)
|
||||
if gain > 0:
|
||||
if g.get("badr"):
|
||||
gains_by_type["badr"] += gain
|
||||
else:
|
||||
gains_by_type[g.get("type", "other")] += gain
|
||||
total_gains += gain
|
||||
else:
|
||||
total_losses += abs(gain)
|
||||
|
||||
# Apply losses
|
||||
net_gains = max(0, total_gains - total_losses)
|
||||
|
||||
# Apply annual exempt amount
|
||||
exempt_used = min(net_gains, rates["cgt_annual_exempt"])
|
||||
taxable_gains = max(0, net_gains - exempt_used)
|
||||
|
||||
# Calculate CGT
|
||||
cgt = 0
|
||||
remaining_band = remaining_basic_band
|
||||
|
||||
# BADR gains first (flat rate)
|
||||
badr_gains = min(gains_by_type["badr"], taxable_gains)
|
||||
if badr_gains > 0:
|
||||
cgt += badr_gains * rates["cgt_badr_rate"]
|
||||
taxable_gains -= badr_gains
|
||||
|
||||
# Remaining gains: basic rate then higher rate
|
||||
if taxable_gains > 0:
|
||||
basic_amount = min(taxable_gains, max(0, remaining_band))
|
||||
higher_amount = taxable_gains - basic_amount
|
||||
|
||||
# Use property rates for property gains, standard rates for others
|
||||
# Simplified: use the unified rates (post Oct 2024 they're the same)
|
||||
cgt += basic_amount * rates["cgt_basic_rate"]
|
||||
cgt += higher_amount * rates["cgt_higher_rate"]
|
||||
|
||||
return {
|
||||
"total_gains": round_tax(total_gains),
|
||||
"total_losses": round_tax(total_losses),
|
||||
"net_gains": round_tax(net_gains),
|
||||
"annual_exempt_used": round_tax(exempt_used),
|
||||
"taxable_gains": round_tax(max(0, net_gains - exempt_used)),
|
||||
"badr_gains": round_tax(gains_by_type["badr"]),
|
||||
"cgt_due": round_tax(cgt),
|
||||
}
|
||||
|
||||
|
||||
def calculate_child_benefit_charge(adjusted_net_income: float, num_children: int, rates: dict) -> dict:
|
||||
"""Calculate High Income Child Benefit Charge."""
|
||||
if num_children <= 0 or adjusted_net_income <= rates["child_benefit_threshold"]:
|
||||
return {"charge": 0, "total_benefit": 0, "percentage_clawed_back": 0}
|
||||
|
||||
# Total benefit
|
||||
benefit = rates["child_benefit_eldest"]
|
||||
if num_children > 1:
|
||||
benefit += (num_children - 1) * rates["child_benefit_other"]
|
||||
|
||||
# Charge: 1% per £200 over threshold
|
||||
excess = adjusted_net_income - rates["child_benefit_threshold"]
|
||||
percentage = min(100, (excess / 200)) # 1% per £200
|
||||
charge = benefit * (percentage / 100)
|
||||
|
||||
return {
|
||||
"total_benefit": round_tax(benefit),
|
||||
"percentage_clawed_back": round(percentage, 1),
|
||||
"charge": round_tax(charge),
|
||||
}
|
||||
|
||||
|
||||
def calculate_payment_on_account(sa_liability: float, tax_deducted_at_source: float) -> dict:
|
||||
"""Calculate payments on account for next year."""
|
||||
# POA required if SA liability > £1,000 AND less than 80% was deducted at source
|
||||
total_liability = sa_liability
|
||||
if total_liability <= 1000:
|
||||
return {"poa_required": False, "each_payment": 0, "reason": "Liability is £1,000 or less"}
|
||||
|
||||
source_percentage = (tax_deducted_at_source / total_liability * 100) if total_liability > 0 else 100
|
||||
if source_percentage >= 80:
|
||||
return {"poa_required": False, "each_payment": 0, "reason": "80% or more was deducted at source"}
|
||||
|
||||
each_payment = round_tax(total_liability / 2)
|
||||
return {
|
||||
"poa_required": True,
|
||||
"each_payment": each_payment,
|
||||
"total_poa": round_tax(total_liability),
|
||||
"reason": f"SA liability of £{total_liability:,.2f} with {source_percentage:.1f}% deducted at source",
|
||||
}
|
||||
|
||||
|
||||
def calculate_tax(data: dict) -> dict:
|
||||
"""Main calculation entry point."""
|
||||
tax_year = data.get("tax_year", "2024-25")
|
||||
if tax_year not in TAX_RATES:
|
||||
return {"error": f"Unsupported tax year: {tax_year}. Supported: {list(TAX_RATES.keys())}"}
|
||||
|
||||
rates = TAX_RATES[tax_year]
|
||||
|
||||
# ─── Gather income ───
|
||||
# Employment
|
||||
employments = data.get("employment", [])
|
||||
total_employment_gross = sum(e.get("gross_pay", 0) for e in employments)
|
||||
total_employment_tax = sum(e.get("tax_deducted", 0) for e in employments)
|
||||
total_benefits = sum(e.get("benefits", 0) for e in employments)
|
||||
|
||||
# Self-employment
|
||||
self_employments = data.get("self_employment", [])
|
||||
total_se_profit = sum(se.get("profit", 0) for se in self_employments)
|
||||
|
||||
# Savings
|
||||
savings_interest = data.get("savings_interest", 0)
|
||||
|
||||
# Dividends
|
||||
dividends = data.get("dividends", 0)
|
||||
|
||||
# Rental
|
||||
rental = data.get("rental", {})
|
||||
rental_profit = rental.get("profit", 0)
|
||||
rental_finance_costs = rental.get("finance_costs", 0) # mortgage interest for tax reducer
|
||||
|
||||
# Pension contributions
|
||||
pension = data.get("pension", {})
|
||||
personal_pension_gross = pension.get("personal_gross", 0) # gross amount (already includes basic rate relief)
|
||||
employer_pension = pension.get("employer", 0)
|
||||
|
||||
# Other
|
||||
other_income = data.get("other_income", 0)
|
||||
|
||||
# ─── Personal circumstances ───
|
||||
blind = data.get("blind_persons_allowance", False)
|
||||
marriage_allowance_received = data.get("marriage_allowance_received", False)
|
||||
marriage_allowance_given = data.get("marriage_allowance_given", False)
|
||||
student_loan_plans = data.get("student_loan_plans", [])
|
||||
child_benefit = data.get("child_benefit", {})
|
||||
gift_aid = data.get("gift_aid", 0)
|
||||
previous_year_liability = data.get("previous_year_sa_liability", 0)
|
||||
|
||||
# ─── Calculate totals ───
|
||||
non_savings_income = total_employment_gross + total_benefits + total_se_profit + rental_profit + other_income
|
||||
total_income = non_savings_income + savings_interest + dividends
|
||||
|
||||
# Adjusted net income (for PA taper and child benefit): total income minus pension and gift aid
|
||||
# Pension contributions extend the basic rate band and reduce adjusted net income
|
||||
# Gift Aid extends the basic rate band too
|
||||
adjusted_net_income = total_income - personal_pension_gross - (gift_aid * 1.25) # gross up gift aid
|
||||
|
||||
# Personal allowance
|
||||
pa = calculate_personal_allowance(
|
||||
adjusted_net_income, rates, blind, marriage_allowance_received, marriage_allowance_given
|
||||
)
|
||||
|
||||
# Pension relief extends basic rate band
|
||||
pension_relief_gross = personal_pension_gross + (gift_aid * 1.25)
|
||||
|
||||
# ─── Income tax ───
|
||||
income_tax = calculate_income_tax(
|
||||
non_savings_income, savings_interest, dividends, rates, pa, pension_relief_gross
|
||||
)
|
||||
|
||||
# ─── Rental finance costs tax reducer (20%) ───
|
||||
finance_cost_reducer = round_tax(rental_finance_costs * 0.20)
|
||||
|
||||
# ─── Marriage Allowance tax reducer ───
|
||||
marriage_allowance_reducer = 0
|
||||
if marriage_allowance_received:
|
||||
marriage_allowance_reducer = round_tax(rates["marriage_allowance"] * 0.20)
|
||||
|
||||
# Total income tax after reducers
|
||||
income_tax_after_reducers = round_tax(
|
||||
max(0, income_tax["total_income_tax"] - finance_cost_reducer - marriage_allowance_reducer)
|
||||
)
|
||||
|
||||
# ─── NIC ───
|
||||
nic4 = calculate_nic_class4(total_se_profit, rates) if total_se_profit > 0 else {"class4_nic": 0}
|
||||
|
||||
# ─── Student loans ───
|
||||
# Student loan is calculated on total relevant income
|
||||
student_loan_income = total_employment_gross + total_se_profit
|
||||
student_loans = calculate_student_loan(student_loan_income, student_loan_plans, rates) if student_loan_plans else {"total_student_loan": 0}
|
||||
|
||||
# ─── CGT ───
|
||||
capital_gains = data.get("capital_gains", [])
|
||||
remaining_basic_band = max(0, rates["basic_rate_limit"] + pension_relief_gross - max(0, non_savings_income + savings_interest + dividends - pa))
|
||||
cgt = calculate_cgt(capital_gains, rates, remaining_basic_band) if capital_gains else {"cgt_due": 0, "total_gains": 0, "taxable_gains": 0}
|
||||
|
||||
# ─── Child benefit charge ───
|
||||
num_children = child_benefit.get("num_children", 0)
|
||||
child_benefit_charge = calculate_child_benefit_charge(adjusted_net_income, num_children, rates)
|
||||
|
||||
# ─── Total liability ───
|
||||
total_tax_due = round_tax(
|
||||
income_tax_after_reducers
|
||||
+ nic4.get("class4_nic", 0)
|
||||
+ student_loans.get("total_student_loan", 0)
|
||||
+ cgt.get("cgt_due", 0)
|
||||
+ child_benefit_charge.get("charge", 0)
|
||||
)
|
||||
|
||||
# Tax already paid
|
||||
tax_already_paid = round_tax(total_employment_tax)
|
||||
# Could also include tax deducted from savings, CGT already paid, etc.
|
||||
tax_already_paid += data.get("other_tax_paid", 0)
|
||||
|
||||
amount_owed = round_tax(total_tax_due - tax_already_paid)
|
||||
|
||||
# ─── Payment on account ───
|
||||
sa_liability_for_poa = round_tax(total_tax_due - total_employment_tax) # SA liability excluding PAYE
|
||||
poa = calculate_payment_on_account(sa_liability_for_poa, 0) # POA based on SA-only liability
|
||||
|
||||
# ─── Build result ───
|
||||
result = {
|
||||
"tax_year": tax_year,
|
||||
"income_summary": {
|
||||
"employment_gross": round_tax(total_employment_gross),
|
||||
"employment_benefits": round_tax(total_benefits),
|
||||
"self_employment_profit": round_tax(total_se_profit),
|
||||
"savings_interest": round_tax(savings_interest),
|
||||
"dividends": round_tax(dividends),
|
||||
"rental_profit": round_tax(rental_profit),
|
||||
"other_income": round_tax(other_income),
|
||||
"total_income": round_tax(total_income),
|
||||
"adjusted_net_income": round_tax(adjusted_net_income),
|
||||
},
|
||||
"personal_allowance": round_tax(pa),
|
||||
"income_tax": income_tax,
|
||||
"tax_reducers": {
|
||||
"finance_cost_reducer": finance_cost_reducer,
|
||||
"marriage_allowance_reducer": marriage_allowance_reducer,
|
||||
},
|
||||
"income_tax_after_reducers": income_tax_after_reducers,
|
||||
"national_insurance": nic4,
|
||||
"student_loans": student_loans,
|
||||
"capital_gains_tax": cgt,
|
||||
"child_benefit_charge": child_benefit_charge,
|
||||
"headline": {
|
||||
"total_income": round_tax(total_income),
|
||||
"total_tax_due": total_tax_due,
|
||||
"tax_already_paid": tax_already_paid,
|
||||
"amount_owed": amount_owed,
|
||||
"amount_refund": round_tax(abs(amount_owed)) if amount_owed < 0 else 0,
|
||||
},
|
||||
"payment_on_account": poa,
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def main():
|
||||
# Read input
|
||||
if len(sys.argv) > 1:
|
||||
with open(sys.argv[1], "r") as f:
|
||||
data = json.load(f)
|
||||
else:
|
||||
data = json.load(sys.stdin)
|
||||
|
||||
result = calculate_tax(data)
|
||||
print(json.dumps(result, indent=2))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Reference in New Issue