Dit is de diepe duik in lange vorm. Voor een snellere, vlot leesbare versie, zie de begeleidende blogpost.
1. Waarom ik dit heb gebouwd
Elk klein bedrijf in Groot-Brittannië met een belastbare omzet boven de VAT-registratiedrempel moet een VAT-aangifte berekenen en indienen (meestal driemaandelijks) via de Making Tax Digital (MTD)-service van HMRC. Negen genummerde dozen. Uitgang VAT minus ingang VAT. Betaal het nettobedrag, of claim de terugbetaling. Makkelijk in theorie; In de praktijk is het straffen als uw facturen in Postgres staan, uw onkostenbewijzen pdf's zijn van twintig verschillende leveranciers, uw bankafschriften CSV van Wise en Revolut zijn en uw regels in vreemde valuta opnieuw moeten worden opgegeven in ponden tegen het juiste maandtarief.
Een paar jaar lang heb ik doorgemodderd met een gigantische spreadsheet, een kalenderherinnering en een kleine sprong in het diepe. Elk kwartaal nam ongeveer een halve dag in beslag, waarvan het grootste deel data-ruzie was: exporteer een CSV uit de database met facturen, plak het in Excel, classificeer een paar honderd rijen met de hand, controleer formules nogmaals, kopieer negen getallen naar een MTD-bridge van derden en klik op Verzenden. Het audittraject was "geloof me, ik had een spreadsheet". Rentewijzigingen halverwege het kwartaal maakten daar een einde aan. De ontvangstbewijzen zijn verdwenen. Eén misplaatste classificatie zou kunnen leiden tot druk Box 5 met honderden ponden af.
Ik heb de pijpleiding dit jaar opnieuw opgebouwd als een kleine Claude Code project dat rechtstreeks met mijn facturen- en onkostendatabase praat, elke regel classificeert, Box 1 naar Box 9 berekent met een deterministische Python-engine, de opbrengst droogvoert als een Markdown-rapport en pas nadat een mens zich heeft afgemeld, zich onderwerpt aan HMRC MTD. Het hele kwartaal kost me nu ongeveer een uur, waarvan het grootste deel het doornemen van de proef met mijn accountant. De audit trail is een git repository. De wiskunde is unit-getest. De classificator is een gedocumenteerd Markdown-bestand. De inzending is afgesloten.
Dit artikel is de lange versie. Prompts, Python, schema, de HMRC OAuth-stroom, de edge-cases. Als je eerst de korte versie wilt, lees dan de metgezel blog.
2. HMRC MTD-primer (en disclaimer)
Making Tax Digital voor VAT is de eis van HMRC dat bij VAT geregistreerde bedrijven digitale gegevens bijhouden en aangiften indienen via compatibele software via een gedocumenteerde API in plaats van cijfers in het gov.uk-webformulier te typen. De API is OAuth2, vereist fraudepreventie-HTTP-headers die het oorspronkelijke apparaat identificeren, en is beschikbaar in een gratis sandbox voordat u ooit aan liveproductie begint.
Het rendement zelf bestaat uit negen vakken: Box 1 is VAT verschuldigd op verkopen en andere outputs; Box 2 is VAT verschuldigd bij verwerving van goederen uit EU-lidstaten (relevant voor Noord-Ierse bedrijven onder het NI-protocol); Box 3 is de totale verschuldigde VAT (Box 1 + Box 2); Box 4 is VAT terugvorderbaar bij aankopen (invoer VAT); Box 5 is de netto VAT die moet worden betaald of teruggevorderd (Box 3 - Box 4); Box 6 is de totale omzet exclusief VAT; Box 7 is het totale aantal aankopen exclusief VAT; Box 8 is de totale waarde van goederen die aan EU-lidstaten worden geleverd (opnieuw alleen NI); Box 9 is de totale waarde van acquisities uit EU-lidstaten (alleen NI). Er zijn regelingsvarianten – forfaitaire regeling, kasboekhouding, jaarrekening – die veranderen Hoe je komt uit op de cijfers, maar niet op de doosvorm.
Vrijwaring. Ik ben een software-ingenieur, geen registeraccountant. Dit artikel is geen belastingadvies. De exacte regels voor de VAT-classificatie, de gedeeltelijke vrijstelling, de forfaitaire regeling, de regels voor de plaats van levering van digitale diensten, de post-Brexit-behandeling van EU-goederen en -diensten en het NI-protocol veranderen allemaal in de loop van de tijd en zijn afhankelijk van uw specifieke omstandigheden. Voordat u zoiets op een echt HMRC-account uitvoert, moet u een in het Verenigd Koninkrijk geregistreerde accountant vragen uw eerste aangifte van begin tot eind te beoordelen. Voor de gezaghebbende regel voor elk specifiek randgeval, zie HMRC VAT Notice 700 en de gerelateerde mededelingen op de website gov.uk. De kosten van een uur van uw accountantstijd zijn veel kleiner dan de kosten van een slechte indiening.
3. Het datamodel
Alles stroomafwaarts hangt af van de vorm van twee tabellen: uitgegeven facturen (verkoop) en geregistreerde uitgaven (aankopen). Ik bewaar de mijne in Postgres, maar dezelfde vorm werkt op MySQL of SQLite. Het minimaal bruikbare schema:
CREATE TABLE invoices (
id BIGSERIAL PRIMARY KEY,
invoice_number TEXT NOT NULL UNIQUE,
issued_at DATE NOT NULL,
customer_name TEXT NOT NULL,
customer_vat_no TEXT,
customer_country CHAR(2) NOT NULL,
net_amount NUMERIC(14,2) NOT NULL,
vat_amount NUMERIC(14,2) NOT NULL,
gross_amount NUMERIC(14,2) NOT NULL,
currency CHAR(3) NOT NULL DEFAULT 'GBP',
fx_rate_to_gbp NUMERIC(18,8) NOT NULL DEFAULT 1,
vat_rate NUMERIC(5,2) NOT NULL,
vat_treatment TEXT NOT NULL,
status TEXT NOT NULL,
paid_at DATE,
notes TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE expenses (
id BIGSERIAL PRIMARY KEY,
supplier_name TEXT NOT NULL,
supplier_vat_no TEXT,
supplier_country CHAR(2) NOT NULL,
incurred_at DATE NOT NULL,
net_amount NUMERIC(14,2) NOT NULL,
vat_amount NUMERIC(14,2) NOT NULL,
gross_amount NUMERIC(14,2) NOT NULL,
currency CHAR(3) NOT NULL DEFAULT 'GBP',
fx_rate_to_gbp NUMERIC(18,8) NOT NULL DEFAULT 1,
vat_rate NUMERIC(5,2) NOT NULL,
vat_treatment TEXT NOT NULL,
category TEXT NOT NULL,
receipt_url TEXT,
notes TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
De niet voor de hand liggende kolommen zijn vat_treatment, fx_rate_to_gbpen de landcodes. Behandeling is een kleine opsomming die zowel door de classificator als door de engine wordt gebruikt; de landcode drijft de logica van verlegging en EU-overname aan; Met fx-koers kunt u facturen in vreemde valuta in hun oorspronkelijke valuta behouden en nog steeds opnieuw vermelden in GBP voor de doostotalen.
| Kolom | Doel | Gebruikt door |
|---|---|---|
vat_treatment | standaard / verlaagd / nul / vrijgesteld / buiten scope / verlegging / eu-goederen / eu-diensten / uitgestelde import | classificator + motor |
vat_rate | 0,00, 5,00, 20,00 (of regelingsspecifiek tarief) | motor + controlesom |
currency + fx_rate_to_gbp | herverklaring van meerdere valuta's | normalisator |
customer_country / supplier_country | verlegging, NI-protocol, plaats van levering | classificator |
paid_at | tak van het kasboekhoudsysteem | motor |
Het aanvullen van deze kolommen in een oude facturentabel is het langzaamste deel van het project. Claude Code kan de migratie genereren; je moet de lastige randgevallen nog steeds met de hand classificeren.
4. Waarom Claude Code (en niet Xero / QuickBooks / FreeAgent)
Voor de meeste kleine bedrijven in Groot-Brittannië is het antwoord – echt – het gebruik van pakketsoftware. Xero, QuickBooks, FreeAgent en Sage hebben allemaal HMRC-gecertificeerde MTD-inzending, fatsoenlijke gebruikersinterfaces en accountants die ze al kennen. U mag uw eigen VAT-systeem niet gebruiken tenzij u daar een duidelijke reden voor heeft.
Mijn redenen waren beperkt maar reëel. Ik gebruik Claude Code al elke dag voor softwarewerk, dus de marginale inspanning om het ook voor VAT te gebruiken was klein. Mijn facturen DB staat op mijn eigen machine; Ik geef er de voorkeur aan dat het niet wordt gespiegeld in een SaaS-tenant. Mijn leveranciersmix is lastig (EU-diensten met verlegging, uitgestelde import VAT, af en toe zero-rated edge cases) en het coderen van de regels in een enkel Markdown-bestand plus een Python-module bleek sneller dan het configureren van een SaaS om ze af te handelen. En de audit trail die ik krijg van een git repo - elke classifier-prompt, elke diff, elke ondertekende periode-snapshot - is precies de audit trail die ik wil.
Claude Code is hiervoor om drie redenen zeer geschikt. Ten eerste werkt het rechtstreeks tegen een project op schijf: het kan mijn schema, de dry-run-rapporten van het voorgaande kwartaal, het regelsbestand en de Python-engine lezen, en bewerkingen in context voorstellen. Ten tweede is het goed in de saaie steigers: migratiescripts, CSV-importeurs, afstemmingsrapporten, pytest-armaturen. Ten derde, als het een regel classificeert, legt het de redenering ervan in gewoon Engels uit, en dat is precies wat ik wil bekijken voordat ik de aangifte onderteken. De beslissingsmatrix onderaan het onderstaande workflowdiagram is de eerlijke versie van deze afweging.
5. Architectuur en workflow
De pijpleiding heeft vijf zwembanen. Bron is alleen-lezen toegang tot de facturen en onkostentabellen plus een vervolgmap voor CSV van Stripe, Wise en bankfeeds. Innemen + normaliseren is een klein door Claude gegenereerd Python-script dat elk bronschema in één canonieke weergave in kaart brengt, vreemde valuta converteert naar GBP tegen het maandelijkse HMRC-tarief, een datumbereikfilter toepast en elke rij stempelt met een idempotent run_key dus herhalingen zijn veilig. Categoriseren + afstemmen is de Claude Code-classificator: deze loopt door elke canonieke rij en kiest a vat_treatment uit de gedocumenteerde enum met behulp van regels in vat-rules.md, stemt facturen af met betalingen en signaleert afwijkingen (negatief netto, ontbrekend VAT-nummer van leverancier, niet-overeenkomende landcode, leveranciers die VAT-geregistreerd zouden moeten zijn maar dat niet zijn). VAT-motor is een pure Python-module die de geclassificeerde rijen leest en deterministisch Box 1 tot Box 9 berekent; de forfaitaire, contante en jaarlijkse schematakken zijn expliciete codepaden. Indienen + controle is de HMRC MTD API-oproep (OAuth2, fraudepreventieheaders, sandbox eerst), achter een menselijke goedkeuringsstap, waarbij de ondertekende momentopname van de periode en het volledige classificatielogboek naar een alleen-toevoegen-auditmap worden geschreven die zes jaar wordt bewaard.
6. Stap-voor-stap bouwen met Claude Code
Dit is de workflow die ik daadwerkelijk heb gebruikt. Vijf aanwijzingen. Ik heb ze opgeruimd voor dit artikel; de originelen zijn slordiger.
Prompt 1 - normalisatie. "Kijk naar de invoices En expenses tafels binnen schema.sql. Genereer een Python-module normalise.py dat beide tabellen leest met behulp van psycopg (alleen-lezen rol vat_reader), beperkt tot het datumbereik 2026-01-01 naar 2026-03-31, en zendt een lijst met canonieke dictaten uit met sleutels: id, source, date, party, gross, net, vat_amount, vat_rate, vat_treatment, country, currency, fx_rate, run_key. Gebruik Decimal, niet zweven. Voeg een docstring en een __main__ blok dat een samenvatting afdrukt."
Claude heeft een schone module teruggestuurd Decimal, geparametriseerde SQL, een build_run_key() die hashes (period_start, period_end, schema, schema_version) in een stabiele hexadecimale tekenreeks en een op klikken gebaseerde CLI. Ik heb twee veldnamen bewerkt en de rest geaccepteerd.
Prompt 2 - classificatie. "Lezen vat-rules.md en schrijf classify.py. Voor elke canonieke rij vanaf normalise.py, set vat_treatment naar een van: standaard, verminderd, nul, vrijstellen, buiten bereik, verlegging, eu-goederen, eu-diensten, uitgestelde import. Als de rij al een niet-lege waarde heeft vat_treatment, laat het met rust en neem het op als bewaard gebleven. Kies anders uit de landcode, het VAT-nummer van de leverancier, de categorie en het bedrag en schrijf een zin van één zin reason op elke rij. Afwijkingen (negatief netto, ontbrekende leverancier VAT voor een Britse leverancier boven de registratiedrempel, landenmismatch) gaan naar een aparte anomalies.json voor menselijke beoordeling. Muteer de bron-DB niet."
De classificator zelf is een kleine, grotendeels op regels gebaseerde functie met één door LLM ondersteunde vertakking voor dubbelzinnige uitgavencategorieën. Cruciaal is dat het nooit naar de bron-DB schrijft; het zendt een geclassificeerd JSON-bestand uit dat de engine leest. Als ik het niet eens ben met een classificatie, bewerk ik de JSON; de volgende droogbeurt pakt het op.
Prompt 3 - motor. "Schrijven engine.py als zuivere functie: def compute_boxes(rows: list[Row], scheme: Scheme) -> BoxTotals. Geen I/O. Geen LLM. BoxTotals is een bevroren dataklasse met Box 1 tot Box 9 als Decimal waarden tot 2 dp. Voeg expliciete vertakkingen toe voor standard, flat_rate, cash, En annual schema's. Standaardregeling is opbouw: omzet VAT draagt bij aan Box 1, aankoop VAT aan Box 4. Verlegde facturen worden toegevoegd aan zowel Box 1 als Box 4. Uitgestelde import VAT voegt zowel Box 1 als Box 4 toe. Overnames in de EU voor NI-bedrijven vinden plaats in Box 2 en Box 9. Rond af op de doostotalen, nooit per rij. Schrijf een docring die elke tak aan zijn regel koppelt."
Prompt 4 - drooglooprapport. "Schrijven dryrun.py dat roept compute_boxes() en verzendt een Markdown-rapport: koptekst met periode en schema, een tabel van Box 1 tot Box 9 met de canonieke labels, een sectie per classificatiebucket met de top 10 rijen op basis van bruto, een sectie met afwijkingen en een voettekst met de run_key en een SHA-256 van de geclassificeerde JSON. Opslaan naar ./audit/<period>/dryrun.md."
Prompt 5 - indiener. "Schrijven submit.py tegen de HMRC MTD VAT API v1.0. Gebruik de OAuth2 autorisatiecodestroom met PKCE. Lees het toegangstoken van ~/.vat/token.json; vernieuwen indien verlopen. Vereiste fraudepreventieheaders zoals gedocumenteerd door HMRC; trek ze eraf fraud_headers.py. Standaard ingesteld op de sandbox https://test-api.service.hmrc.gov.uk. Stappen: GET /organisations/vat/{vrn}/obligations?status=OZoek de overeenkomende periode en vraag de operator om bevestiging door het droogloopoverzicht weer te geven POST /organisations/vat/{vrn}/returns met finalised: true. Druk de receiptId. Bel nooit live tenzij HMRC_ENV=live is ingesteld en de operatortypes INDIENEN."
Vijf aanwijzingen, vier bestanden en een klein document met Markdown-regels. De totale bouwtijd was misschien een week vol gerichte avonden, waarvan het grootste deel de classificatieregels en de HMRC OAuth-dans waren.
7. De VAT-motor
De motor is het opzettelijk saaie onderdeel. Het is een pure functie met unit-tests en geen AI. Dit is de werkelijke vorm:
from dataclasses import dataclass
from decimal import Decimal, ROUND_HALF_UP
D2 = Decimal("0.01")
@dataclass(frozen=True)
class BoxTotals:
box1: Decimal
box2: Decimal
box3: Decimal
box4: Decimal
box5: Decimal
box6: Decimal
box7: Decimal
box8: Decimal
box9: Decimal
def _q(x: Decimal) -> Decimal:
return x.quantize(D2, rounding=ROUND_HALF_UP)
def compute_boxes(rows, scheme="standard", is_ni_business=False) -> BoxTotals:
box1 = box2 = box4 = Decimal(0)
box6 = box7 = box8 = box9 = Decimal(0)
for r in rows:
gbp_net = r.net * r.fx_rate
gbp_vat = r.vat_amount * r.fx_rate
if r.source == "invoice":
box6 += gbp_net
if r.vat_treatment in ("standard", "reduced"):
box1 += gbp_vat
elif r.vat_treatment == "eu-goods" and is_ni_business:
box8 += gbp_net
else:
box7 += gbp_net
if r.vat_treatment in ("standard", "reduced"):
box4 += gbp_vat
elif r.vat_treatment == "reverse-charge":
notional = gbp_net * (r.vat_rate / Decimal(100))
box1 += notional
box4 += notional
elif r.vat_treatment == "postponed-import":
notional = gbp_net * (r.vat_rate / Decimal(100))
box1 += notional
box4 += notional
elif r.vat_treatment == "eu-goods" and is_ni_business:
notional = gbp_net * (r.vat_rate / Decimal(100))
box2 += notional
box4 += notional
box9 += gbp_net
box3 = box1 + box2
box5 = box3 - box4
return BoxTotals(
_q(box1), _q(box2), _q(box3), _q(box4), _q(box5),
_q(box6), _q(box7), _q(box8), _q(box9),
)
Twee dingen om op te merken. Eerst wordt elk boxtotaal aan het eind één keer gekwantificeerd. Afronding per rij lekt centen en HMRC is meedogenloos als het gaat om totalen die niet met elkaar in overeenstemming zijn. Ten tweede is de engine volledig deterministisch: elke keer dezelfde input, dezelfde output. Dat is de eigenschap waardoor ik het in de unit kan testen en kan vertrouwen op de dry-run.
def test_simple_quarter():
rows = [
Row("invoice", net=Decimal("24500.00"), vat_amount=Decimal("4900.00"),
vat_rate=Decimal("20"), vat_treatment="standard", fx_rate=Decimal(1), source="invoice"),
Row("expense", net=Decimal("3000.00"), vat_amount=Decimal("600.00"),
vat_rate=Decimal("20"), vat_treatment="standard", fx_rate=Decimal(1), source="expense"),
]
t = compute_boxes(rows)
assert t.box1 == Decimal("4900.00")
assert t.box4 == Decimal("600.00")
assert t.box5 == Decimal("4300.00")
assert t.box6 == Decimal("24500.00")
assert t.box7 == Decimal("3000.00")
Als dit op zijn plaats is, wordt elke regressie in CI vastgelegd voordat deze ooit in het proefrapport terechtkomt. Claude kan me helpen meer tests te schrijven; het kan de motor niet vervangen zonder dat deze tests zijn geslaagd.
8. Demovideo
Hieronder vindt u een korte uitleg van de pijplijn die gedurende een kwartaal end-to-end wordt uitgevoerd tegen een echte (geanonimiseerde) database met facturen en onkosten: normaliseren, classificeren, dry-runnen en vervolgens een sandbox MTD-indiening. De hele lus duurt een paar minuten.
9. HMRC MTD-inzending
Dit is het deel dat de meeste artikelen verdoezelen. De MTD VAT API zelf is goed gedocumenteerd en gratis, maar er zijn drie dingen die u moet regelen voordat u ooit belt POST returns.
OAuth2 en tokens. Registreer een applicatie in de HMRC-ontwikkelaarshub; u krijgt een client-ID, een clientgeheim en een omleidings-URI. De stroom is autorisatiecode met PKCE: open de browser naar de HMRC-toestemmingspagina, de gebruiker logt in met zijn Government Gateway-inloggegevens en autoriseert uw applicatie op basis van zijn VAT-registratienummer (VRN), HMRC stuurt terug met een autorisatiecode, u wisselt deze in voor een toegangstoken + vernieuwingstoken. Cache ze met verstandige machtigingen op schijf. Het toegangstoken is van korte duur; het vernieuwingstoken is veel langer.
Kopteksten ter voorkoming van fraude. HMRC vereist een gedocumenteerde set HTTP-headers die het oorspronkelijke apparaat identificeren bij elke API-oproep: tijdzone, schermgeometrie, gebruikers-ID's, openbare IP, MAC-adressen, de clientverbindingsmethode, enz. De exacte lijst en het formaat staan in de HMRC-ontwikkelaarsdocumentatie (zie HMRC VAT Notice 700 en de bijbehorende fraudepreventiespecificaties voor de gezaghebbende regel). Begrijp ze verkeerd en je krijgt een 403 met een vage fout. Begrijp ze goed en de oproep komt door. Ik bewaar ze binnen fraud_headers.py en hun aanwezigheid in tests bevestigen.
Eerst zandbak. HMRC biedt een gratis sandbox aan test-api.service.hmrc.gov.uk met stub-VRN's en stub-verplichtingen. Voer uw hele pijplijn van begin tot eind tegen de sandbox aan totdat u een valse aangifte heeft ingediend, een valse ontvangst-ID heeft ontvangen en heeft bevestigd dat uw verplichtingen zijn verplaatst. Pas dan draai je om HMRC_ENV=live. Mijn indiener weigert live te bellen, tenzij de omgevingsvariabele is ingesteld en de operator typt SUBMIT bij de prompt. Er is geen automatisch executiepad om te leven. De kosten van een enkele slechte live-inzending zijn veel hoger dan de kosten van nog een sandbox-iteratie.
De eigenlijke happy-path-oproep is kort: GET obligations met status=O Om de open periode te vinden, geeft u het drooglooprapport weer en vraagt u de operator om bevestiging POST returns met de negen boxnummers, de periodKey van de verplichting, en finalised: true. Sla het antwoord op, schrijf de ontvangst-ID in de auditmap en u bent klaar.
10. Veiligheid, audit en naleving
Drie regels waar ik niet verder op in ga. Eerst, Claude is niet bezig met rekenen. De classificator suggereert behandelingen en de LLM kan Python en regels bewerken, maar de boxtotalen worden geproduceerd door een pure functie met eenheidstests. "De AI heeft het gedaan" is geen verdediging onder MTD. Seconde, elke inzending wordt afgesloten door een mens. Het proefrapport is de bron van waarheid voor ondertekening. Als ik een getal uit de geclassificeerde rijen en de motorcode niet kan verklaren, geef ik niet op. Derde, het audittraject is alleen als bijlage toegevoegd. Elke geclassificeerde JSON, elk testrapport, elke prompt, elke diff en de uiteindelijke HMRC-ontvangst-ID voor de periode worden in ./audit/<period>/ en toegewijd aan een privé git repo. Voor HMRC zijn zes jaar aan gegevens vereist; de mijne zijn ondertekende snapshots.
Nog twee praktijken die ik zou aanraden, zelfs als je de rest niet overneemt. Voer de bron-DB-verbinding uit als een alleen-lezen rol met de minste rechten: nee UPDATE, Nee DELETE, Nee DROP. Houd de HMRC-client geheim en de toegangstokens uit de repository met behulp van een geheime manager of een 0600-bestand in uw thuismap. Het gaat erom ongelukken onmogelijk te maken door middel van constructie, en niet alleen door beleid.
11. Randgevallen
VAT is een gereguleerde belasting die sinds 1973 door elke Britse regering wordt aangepast, met een post-Brexit-overlay en een Noord-Iers protocol erbovenop. De randgevallen zijn echt. Een niet-uitputtende lijst van degenen die mijn motor aankan:
- Brexit en Noord-Ierland. Bedrijven in Groot-Brittannië vullen Box 8 of Box 9 niet meer in. NI-bedrijven doen dit nog steeds voor goederen (geen diensten) onder het NI-protocol. De motor neemt
is_ni_businessals parameter en vertakt zich dienovereenkomstig. Zie HMRC VAT Notice 700 voor de gezaghebbende regel. - Omgekeerde lading. Diensten gekocht bij een leverancier buiten het Verenigd Koninkrijk (en bepaalde binnenlandse diensten in de bouwsector) worden verantwoord onder de verleggingsregeling: u voegt een fictieve output VAT toe aan Box 1 en vordert hetzelfde bedrag terug in Box 4. Het netto-effect op contant geld is nul; Het netto-effect op het rendement is dat twee dozen samen bewegen.
- Import VAT uitgesteld. Voor goederen die van buiten het Verenigd Koninkrijk worden geïmporteerd, stelt u de import VAT uit tot uw volgende retourzending in plaats van deze aan de grens te betalen. De behandeling lijkt op een verlegging bij retourzending.
- Gedeeltelijke vrijstelling. Als u zowel belastbare als vrijgestelde prestaties verricht, kunt u de input VAT alleen terugvorderen voor zover deze betrekking heeft op belastbare prestaties. Er zijn standaard en speciale methoden; dit is de hoek waar het vaakst een accountant nodig is. Probeer geen regels voor gedeeltelijke vrijstelling te coderen in een Markdown-bestand.
- Forfaitaire regeling. U brengt VAT in rekening tegen het standaardtarief, maar betaalt HMRC een vast percentage van de bruto-omzet, en u kunt de input VAT doorgaans niet terugvorderen. De engine heeft hiervoor een aparte tak en negeert het grootste deel van de classificatie per rij.
- Facturen in meerdere valuta. Converteer elke rij naar GBP tegen de koers die geldt op de relevante datum. HMRC publiceert maandelijkse gemiddelde tarieven; U kunt ook het spottarief gebruiken. Wees consistent. De
fx_rate_to_gbpkolom slaat het daadwerkelijk toegepaste tarief per rij op. - Creditnota's. Een creditnota is een negatieve factuur. Het vermindert Box 1 en Box 6 (of Box 4 en Box 7 als het een aankoopkrediet is). Zorg ervoor dat uw
net_amountEnvat_amountnegatief kunnen worden en dat de motor ze correct optelt. - Voorschotten en termijnbetalingen. Het belastingpunt kan zich voordoen op het moment dat de aanbetaling is ontvangen, en niet op het moment dat de eindfactuur wordt uitgereikt. Kasboekhouding kan dit vereenvoudigen.
- Tariefwijzigingen halverwege het kwartaal. Historisch zeldzaam, maar mogelijk. Bewaar de snelheid per rij, niet als een globale constante.
12. Kosten
Eerlijke cijfers. Bouw tijd was ongeveer een week van gerichte avonden – noem het 25 tot 30 uur. Het grootste deel daarvan bestond uit schemanormalisatie, de classificatieregels en het schrijven van de motortests; de HMRC OAuth-dans was het op één na grootste deel. Bedrijfskosten per kwartaal is klein: mijn bestaande Claude Code-abonnement (al betaald voor ander softwarewerk), een paar cent aan classifier-tokengebruik en nul pond voor de HMRC API. De engine en het auditlogboek draaien lokaal op mijn Mac. Reviewertijd per kwartaal is ongeveer een uur, meestal besteed aan het lezen van de proefversie met mijn accountant. Vergelijk dat eens met ongeveer een halve dag spreadsheet-geruzie onder de oude workflow, en de bespaarde tijd betaalt de bouwkosten binnen het eerste jaar terug.
Als je dit zou prijzen als een project waarvoor je iemand anders zou betalen om het te bouwen, zou je in de viercijferige marge zitten wat betreft adviestarieven. Daarom is een pakket SaaS van twintig tot vijftig pond per maand bijna altijd het juiste antwoord voor de meeste bedrijven. De interessante vraag is niet: "is dit goedkoper dan Xero?" - meestal niet - maar "is het nauwkeuriger, beter controleerbaar en gemakkelijker voor mij om erover te redeneren?". Voor mij is het antwoord ja.
13. Wat nu, en conclusie
De volgende stukken die ik wil toevoegen zijn: automatische import van bankfeeds (Open Banking AISP), OCR voor pdf's van leveranciers zodat de ontvangst-URL een daadwerkelijke gescande regel wordt, een geplande cron + Slack-goedkeuringsstroom zodat ik de deadlines niet hoef te onthouden, en een self-billing-extensie voor marktplaatsverkopers. Geen van deze verandert de kernvorm van de pijpleiding. Het zijn allemaal decoraties op hetzelfde vijfbaansdiagram.
De grotere les, in ieder geval voor mij, is hoe goed een klein Claude Code-project omgaat met de saaie helft van een gereguleerde workflow. De saaie helft is het meeste werk: normalisatie van schema's, CSV-importeurs, afstemmingsrapporten, schema's, edge-case-regels, auditlogboekregistratie. Claude is daar uitstekend in. De deterministische helft – de eigenlijke wiskunde, de daadwerkelijke inzending – blijft in eenvoudige Python met unit-tests en een menselijke goedkeuringspoort, precies waar het thuishoort.
Disclaimer, nogmaals. Dit artikel is geen belastingadvies. Voordat u zoiets naar een echt HMRC-account verwijst, moet u een in het Verenigd Koninkrijk geregistreerde accountant vragen uw eerste aangifte van begin tot eind te beoordelen. Ga de strijd aan met de HMRC MTD-sandbox totdat u en uw accountant er allebei vertrouwen in hebben. Voor de gezaghebbende regel over een specifieke VAT-behandeling, zie HMRC VAT Notice 700 en de gerelateerde mededelingen op de website gov.uk. Als je de korte versie van dit alles wilt, kun je de begeleidende blogpost is een leestijd van vijf minuten; anders abonneer je en ik zal blijven schrijven naarmate de stapel volwassener wordt.
SEO-momentopname voor dit artikel
- SEO-titel: Bouw een VAT-retoursysteem met Claude Code
- Metabeschrijving: Gebruik Claude Code om uw facturen en onkostendatabase te lezen, classificeer VAT, bereken HMRC Box 1 tot 9, test het uit en verzend het vervolgens naar MTD - deterministische engine, menselijke goedkeuring.
- Primaire trefwoorden: claude-code, btw, hmrc, mtd, boekhouding, automatisering, python, ai-agent, finops, kleine bedrijven
- Twitter / X (minder dan 280 tekens): Mijn Britse VAT-retour herbouwd als een Claude Code-project: Postgres-facturen + onkosten DB tot HMRC Box 1 tot 9, deterministische Python-engine, sandbox-eerste MTD-indiening, menselijke goedkeuringspoort. Volledig artikel + workflow SVG + demovideo. Geen belastingadvies. #ClaudeCode #VAT #HMRC #MTD
- LinkedIn-bericht: Ik heb mijn driemaandelijkse Britse VAT-rendement dit jaar herbouwd als een klein Claude Code-project. Het leest mijn database met facturen en onkosten alleen-lezen, classificeert elke regel op basis van VAT-behandeling met behulp van een gedocumenteerd regelsbestand, berekent HMRC Box 1 naar 9 met een deterministische Python-engine (geen LLM in de rekenkunde), en pas nadat ik de proefperiode heb afgetekend, wordt het ingediend via de HMRC MTD API - eerst sandbox, headers voor fraudepreventie, de volledige OAuth2-stroom. Het hele kwartier duurt nu ongeveer een uur. Het volledige artikel met aanwijzingen, schema, de motorcode, randgevallen en het eerlijke kostenverhaal staat op Workstation. Geen belastingadvies; laat een accountant uw eerste aangifte beoordelen.
