นี่คือการเจาะลึกรูปแบบยาว หากต้องการเวอร์ชันที่เร็วกว่าและอ่านแบบสกิมได้ โปรดดูที่ โพสต์บล็อกสหาย.
1. ทำไมฉันถึงสร้างสิ่งนี้
ธุรกิจขนาดเล็กในสหราชอาณาจักรทุกแห่งที่มีผลประกอบการที่ต้องเสียภาษีเกินเกณฑ์การลงทะเบียน VAT จะต้องคำนวณและส่งการคืน VAT ซึ่งโดยปกติจะเป็นรายไตรมาส ผ่านบริการ Making Tax Digital (MTD) ของ HMRC กล่องเก้าหมายเลข เอาต์พุต VAT ลบอินพุต VAT ชำระเงินสุทธิหรือขอรับเงินคืน ง่ายในทางทฤษฎี การลงโทษในทางปฏิบัติเมื่อใบแจ้งหนี้ของคุณอยู่ใน Postgres ใบเสร็จรับเงินค่าใช้จ่ายของคุณเป็น PDF จากซัพพลายเออร์ที่แตกต่างกันยี่สิบราย ใบแจ้งยอดธนาคารของคุณเป็น CSV จาก Wise และ Revolut และบรรทัดสกุลเงินต่างประเทศของคุณจะต้องระบุใหม่เป็นปอนด์ในอัตรารายเดือนที่ถูกต้อง
เป็นเวลาหลายปีที่ฉันยุ่งวุ่นวายกับสเปรดชีตขนาดยักษ์ ปฏิทินเตือนความจำ และศรัทธาเล็กๆ น้อยๆ แต่ละไตรมาสใช้เวลาประมาณครึ่งวัน ซึ่งส่วนใหญ่เป็นการถกเถียงข้อมูล - ส่งออก CSV จากฐานข้อมูลใบแจ้งหนี้ วางลงใน Excel จำแนกแถวสองสามร้อยแถวด้วยมือ ตรวจสอบสูตรอีกครั้ง คัดลอกตัวเลขเก้าหมายเลขลงในบริดจ์ MTD ของบริษัทอื่น คลิกส่ง เส้นทางการตรวจสอบคือ "เชื่อฉันเถอะ ฉันมีสเปรดชีต" การเปลี่ยนแปลงอัตรากลางไตรมาสทำลายมัน ใบเสร็จรับเงินหายไป การจำแนกประเภทที่ใส่ผิดตำแหน่งหนึ่งสามารถผลักดันได้ Box 5 ลดไปหลายร้อยปอนด์
ฉันสร้างท่อใหม่ในปีนี้เป็นขนาดเล็ก Claude Code โปรเจ็กต์ที่พูดคุยกับฐานข้อมูลใบแจ้งหนี้และค่าใช้จ่ายของฉันโดยตรง จำแนกแต่ละบรรทัด คำนวณ Box 1 ถึง Box 9 ด้วยกลไก Python ที่กำหนดไว้ ดำเนินการส่งคืนแบบแห้งเป็นรายงาน Markdown และหลังจากที่มนุษย์ลงนามแล้วเท่านั้นที่จะส่งไปยัง HMRC MTD ตอนนี้ฉันใช้เวลาทั้งไตรมาสประมาณหนึ่งชั่วโมง ซึ่งส่วนใหญ่จะตรวจสอบการทดลองดำเนินการกับนักบัญชีของฉัน เส้นทางการตรวจสอบเป็น repo คอมไพล์ คณิตศาสตร์มีการทดสอบหน่วย ตัวแยกประเภทเป็นไฟล์ Markdown ที่ได้รับการบันทึกไว้ การส่งมีรั้วรอบขอบชิด
บทความนี้เป็นฉบับยาว พรอมต์, Python, สคีมา, โฟลว์ HMRC OAuth, เคส Edge หากคุณต้องการเวอร์ชันสั้นก่อนโปรดอ่าน บล็อกสหาย.
2. ไพรเมอร์ HMRC MTD (และข้อจำกัดความรับผิดชอบ)
Making Tax Digital สำหรับ VAT เป็นข้อกำหนดของ HMRC ที่ว่าธุรกิจที่จดทะเบียน VAT จะเก็บบันทึกดิจิทัลและส่งการส่งคืนผ่านซอฟต์แวร์ที่เข้ากันได้ผ่าน API ที่เป็นเอกสาร แทนที่จะพิมพ์ตัวเลขลงในแบบฟอร์มเว็บ gov.uk API คือ OAuth2 ซึ่งต้องใช้ส่วนหัว HTTP ป้องกันการฉ้อโกงซึ่งระบุอุปกรณ์ต้นทาง และพร้อมใช้งานในแซนด์บ็อกซ์ฟรีก่อนที่คุณจะสัมผัสการผลิตจริง
การส่งคืนคือเก้ากล่อง: Box 1 คือ VAT เนื่องจากยอดขายและเอาต์พุตอื่น ๆ Box 2 คือ VAT เนื่องจากการซื้อสินค้าจากประเทศสมาชิกสหภาพยุโรป (เกี่ยวข้องกับธุรกิจในไอร์แลนด์เหนือภายใต้โปรโตคอล NI) Box 3 คือ VAT ทั้งหมดที่ต้องชำระ (Box 1 + Box 2) Box 4 คือ VAT ที่สามารถเรียกคืนได้เมื่อซื้อ (อินพุต VAT); Box 5 คือ VAT สุทธิที่จะจ่ายหรือเรียกคืน (Box 3 - Box 4) Box 6 คือยอดขายรวมไม่รวม VAT; Box 7 คือยอดซื้อทั้งหมดไม่รวม VAT; Box 8 คือมูลค่ารวมของสินค้าที่จัดหาให้กับประเทศสมาชิกสหภาพยุโรป (เฉพาะ NI เท่านั้น) Box 9 คือมูลค่ารวมของการได้มาจากประเทศสมาชิกสหภาพยุโรป (NI เท่านั้น) มีรูปแบบที่แตกต่างกันออกไป - รูปแบบอัตราคงที่, การบัญชีเงินสด, การบัญชีรายปี - การเปลี่ยนแปลงนั้น ยังไง คุณมาถึงตัวเลขแต่ไม่ใช่รูปร่างกล่อง
ข้อสงวนสิทธิ์ ฉันเป็นวิศวกรซอฟต์แวร์ ไม่ใช่นักบัญชีรับอนุญาต บทความนี้เป็น ไม่ใช่คำแนะนำด้านภาษี. กฎที่แน่นอนสำหรับการจำแนกประเภท VAT การยกเว้นบางส่วน โครงการอัตราคงที่ กฎสถานที่จัดหาสำหรับบริการดิจิทัล การปฏิบัติต่อสินค้าและบริการของสหภาพยุโรปหลัง Brexit และโปรโตคอล NI ล้วนเปลี่ยนแปลงไปตามกาลเวลาและขึ้นอยู่กับสถานการณ์เฉพาะของคุณ ก่อนที่คุณจะดำเนินการในลักษณะนี้กับบัญชี HMRC จริง ให้ขอให้นักบัญชีที่จดทะเบียนในสหราชอาณาจักรตรวจสอบการคืนสินค้าครั้งแรกของคุณตั้งแต่ต้นทางถึงปลายทาง สำหรับกฎที่เชื่อถือได้เกี่ยวกับ Edge Case เฉพาะใดๆ โปรดดู HMRC VAT Notice 700 และประกาศที่เกี่ยวข้องบนเว็บไซต์ gov.uk ต้นทุนเวลาหนึ่งชั่วโมงของนักบัญชีของคุณน้อยกว่าต้นทุนการส่งที่ไม่ถูกต้องมาก
3. แบบจำลองข้อมูล
ทุกอย่างที่ปลายน้ำขึ้นอยู่กับรูปร่างของสองตาราง: ใบแจ้งหนี้ที่ออก (การขาย) และค่าใช้จ่ายที่บันทึกไว้ (การซื้อ) ฉันเก็บของฉันไว้ใน Postgres แต่รูปร่างเดียวกันนั้นใช้ได้กับ MySQL หรือ SQLite สคีมาที่มีประโยชน์ขั้นต่ำ:
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()
);
คอลัมน์ที่ไม่ชัดเจนคือ vat_treatment, fx_rate_to_gbpและรหัสประเทศ การรักษาเป็นแจงนับขนาดเล็กที่ใช้โดยทั้งลักษณนามและเครื่องยนต์ รหัสประเทศขับเคลื่อนการคิดค่าธรรมเนียมย้อนกลับและตรรกะการได้มาของสหภาพยุโรป อัตรา fx ช่วยให้คุณสามารถเก็บใบแจ้งหนี้สกุลเงินต่างประเทศไว้ในสกุลเงินเดิมและยังคงระบุสถานะใหม่เป็น GBP สำหรับผลรวมในกล่อง
| คอลัมน์ | วัตถุประสงค์ | ใช้โดย |
|---|---|---|
vat_treatment | มาตรฐาน / ลดลง / เป็นศูนย์ / ยกเว้น / ขอบเขตภายนอก / ค่าธรรมเนียมย้อนกลับ / สินค้าของสหภาพยุโรป / บริการของสหภาพยุโรป / การนำเข้าที่เลื่อนออกไป | ลักษณนาม + เครื่องยนต์ |
vat_rate | 0.00, 5.00, 20.00 (หรืออัตราเฉพาะโครงการ) | เครื่องยนต์+เช็คซัม |
currency + fx_rate_to_gbp | ใบแจ้งยอดหลายสกุลเงิน | นอร์มัลไลเซอร์ |
customer_country / supplier_country | การชาร์จแบบย้อนกลับ, โปรโตคอล NI, สถานที่จัดหา | ลักษณนาม |
paid_at | สาขาโครงการบัญชีเงินสด | เครื่องยนต์ |
การเติมคอลัมน์เหล่านั้นลงในตารางใบแจ้งหนี้แบบเดิมถือเป็นส่วนที่ช้าที่สุดของโครงการ Claude Code สามารถสร้างการโยกย้าย คุณยังคงต้องจำแนกเคสขอบที่น่าอึดอัดด้วยมือ
4. ทำไมต้อง Claude Code (และไม่ใช่ Xero / QuickBooks / FreeAgent)
สำหรับธุรกิจขนาดเล็กในสหราชอาณาจักรส่วนใหญ่ คำตอบคือ - ใช้ซอฟต์แวร์สำเร็จรูปอย่างแท้จริง Xero, QuickBooks, FreeAgent, Sage ล้วนมีการส่ง MTD ที่ได้รับการรับรอง HMRC, UI ที่เหมาะสม และนักบัญชีที่รู้จักพวกเขาอยู่แล้ว คุณไม่ควรหมุนระบบ VAT ของคุณเอง เว้นแต่คุณจะมีเหตุผลที่ชัดเจน
เหตุผลของฉันนั้นแคบแต่เป็นจริง ฉันใช้ Claude Code ทุกวันสำหรับงานซอฟต์แวร์อยู่แล้ว ดังนั้นความพยายามเพียงเล็กน้อยในการใช้ VAT ด้วยจึงมีเพียงเล็กน้อย ฐานข้อมูลใบแจ้งหนี้ของฉันอยู่บนเครื่องของฉันเอง ฉันไม่อยากให้มันสะท้อนไปยังผู้เช่า SaaS กลุ่มซัพพลายเออร์ของฉันไม่สะดวก (บริการของสหภาพยุโรปที่มีการเรียกเก็บเงินย้อนกลับ, การนำเข้า VAT ที่ถูกเลื่อนออกไป, กรณี Edge ที่ได้รับการจัดอันดับเป็นศูนย์เป็นครั้งคราว) และการเข้ารหัสกฎในไฟล์ Markdown ไฟล์เดียวบวกกับโมดูล Python กลับกลายเป็นว่าเร็วกว่าการกำหนดค่า SaaS เพื่อจัดการพวกเขา และเส้นทางการตรวจสอบที่ฉันได้รับจาก git repo - ทุกพรอมต์ของตัวแยกประเภท ทุกส่วนต่าง ทุกสแนปช็อตช่วงเวลาที่ลงนาม - คือเส้นทางการตรวจสอบที่ฉันต้องการ
Claude Code เหมาะสมกับสิ่งนี้ด้วยเหตุผลสามประการ ประการแรก ทำงานโดยตรงกับโปรเจ็กต์บนดิสก์ โดยสามารถอ่านสคีมาของฉัน รายงานการทดลองใช้งานไตรมาสก่อนหน้า ไฟล์กฎ และกลไก Python และเสนอการแก้ไขในบริบทได้ ประการที่สอง สคริปต์การโยกย้าย ผู้นำเข้า CSV รายงานการกระทบยอด และการติดตั้ง pytest เป็นเรื่องที่ดี ประการที่สาม เมื่อมันแยกบรรทัด มันจะอธิบายเหตุผลเป็นภาษาอังกฤษธรรมดา ซึ่งเป็นสิ่งที่ผมอยากทบทวนก่อนลงนามในการคืนสินค้า เมทริกซ์การตัดสินใจที่ด้านล่างของแผนภาพขั้นตอนการทำงานด้านล่างคือเวอร์ชันที่ตรงไปตรงมาของการแลกเปลี่ยนนี้
5. สถาปัตยกรรมและขั้นตอนการทำงาน
ท่อส่งน้ำมีเลนว่ายน้ำ 5 เลน แหล่งที่มา คือการเข้าถึงตารางใบแจ้งหนี้และค่าใช้จ่ายระดับอ่านอย่างเดียว รวมถึงโฟลเดอร์ดรอปสำหรับ CSV จาก Stripe, Wise และฟีดธนาคาร นำเข้า + ทำให้เป็นมาตรฐาน เป็นสคริปต์ Python ขนาดเล็กที่ Claude สร้างขึ้น ซึ่งจะแมปแต่ละสคีมาแหล่งที่มาเป็นมุมมองเดียวที่ยอมรับได้ แปลงสกุลเงินต่างประเทศเป็น GBP ในอัตรารายเดือน HMRC ใช้ตัวกรองช่วงวันที่ และประทับตราทุกแถวด้วย idempotent run_key ดังนั้นการวิ่งซ้ำจึงปลอดภัย จัดหมวดหมู่ + ปรับยอด คือตัวแยกประเภท Claude Code: มันจะเดินแต่ละแถวตามรูปแบบบัญญัติ เลือก a vat_treatment จากแจงนับเอกสารโดยใช้กฎใน vat-rules.mdกระทบยอดใบแจ้งหนี้กับการชำระเงิน และแจ้งความผิดปกติ (สุทธิติดลบ หมายเลข VAT ของซัพพลายเออร์หายไป รหัสประเทศไม่ตรงกัน ซัพพลายเออร์ที่ควรลงทะเบียน VAT แต่ไม่ได้จดทะเบียน) เครื่องยนต์ VAT เป็นโมดูล Python แท้ที่อ่านแถวที่จัดประเภทและคำนวณ Box 1 ถึง Box 9 ตามที่กำหนด สาขาแบบอัตราคงที่ เงินสด และรายปีเป็นเส้นทางโค้ดที่ชัดเจน ส่ง + ตรวจสอบ คือการเรียก HMRC MTD API (OAuth2, ส่วนหัวป้องกันการฉ้อโกง, แซนด์บ็อกซ์ก่อน) มีการควบคุมตามขั้นตอนการอนุมัติโดยมนุษย์ โดยมีสแนปชอตช่วงเวลาที่ลงนามและบันทึกตัวแยกประเภทแบบเต็มที่เขียนลงในโฟลเดอร์การตรวจสอบแบบผนวกเท่านั้นที่เก็บไว้เป็นเวลาหกปี
6. การสร้างทีละขั้นตอนด้วย Claude Code
นี่คือขั้นตอนการทำงานที่ฉันใช้จริง ห้าแจ้ง ฉันทำความสะอาดพวกเขาสำหรับบทความนี้ ต้นฉบับมีความยุ่งเหยิงมากขึ้น
พรอมต์ 1 - ตัวปรับมาตรฐาน "ดูที่ invoices และ expenses ตารางใน schema.sql. สร้างโมดูล Python normalise.py ที่อ่านทั้งสองตารางโดยใช้ psycopg (บทบาทแบบอ่านอย่างเดียว vat_reader) จำกัดเฉพาะช่วงวันที่ 2026-01-01 ถึง 2026-03-31และส่งรายการคำสั่งตามรูปแบบบัญญัติพร้อมคีย์: id, source, date, party, gross, net, vat_amount, vat_rate, vat_treatment, country, currency, fx_rate, run_key. ใช้ Decimalไม่ลอย. รวมเอกสารและก __main__ บล็อกที่พิมพ์บทสรุป"
คลอดด์กลับโมดูลสะอาดด้วย Decimal, SQL แบบกำหนดพารามิเตอร์, build_run_key() ที่แฮช ( period_start, period_end, Scheme, schema_version) ลงในสตริงฐานสิบหกที่เสถียร และ CLI ตามการคลิก ฉันแก้ไขชื่อฟิลด์สองชื่อและยอมรับส่วนที่เหลือ
พรอมต์ 2 - ลักษณนาม "อ่าน vat-rules.md และเขียน classify.py. สำหรับแต่ละแถวตามรูปแบบบัญญัติจาก normalise.py, ชุด vat_treatment ไปที่หนึ่งใน: มาตรฐาน, ลดลง, ศูนย์, ได้รับการยกเว้น, นอกขอบเขต, ชาร์จย้อนกลับ, สินค้าสหภาพยุโรป, บริการของสหภาพยุโรป, เลื่อน-นำเข้า. หากแถวนั้นมีข้อมูลไม่ว่างอยู่แล้ว vat_treatmentปล่อยให้มันอยู่คนเดียวและบันทึกเป็น เก็บรักษาไว้. หรือเลือกจากรหัสประเทศ หมายเลข VAT ของซัพพลายเออร์ หมวดหมู่ และจำนวน แล้วเขียนหนึ่งประโยค reason ในแต่ละแถว ความผิดปกติ (สุทธิติดลบ ซัพพลายเออร์ VAT หายไปสำหรับซัพพลายเออร์ในสหราชอาณาจักรที่สูงกว่าเกณฑ์การลงทะเบียน ประเทศไม่ตรงกัน) ไปที่แยกต่างหาก anomalies.json สำหรับการตรวจสอบโดยมนุษย์ อย่าเปลี่ยนฐานข้อมูลต้นทาง"
ตัวแยกประเภทนั้นเป็นฟังก์ชันขนาดเล็กที่อิงตามกฎเป็นหลัก โดยมีสาขาที่ได้รับการสนับสนุน LLM หนึ่งสาขาสำหรับหมวดหมู่ค่าใช้จ่ายที่ไม่ชัดเจน ที่สำคัญคือมันไม่เคยเขียนไปยังฐานข้อมูลต้นทาง - มันจะปล่อยไฟล์ JSON ที่จัดประเภทซึ่งกลไกอ่าน หากฉันไม่เห็นด้วยกับการจัดหมวดหมู่ ฉันจะแก้ไข JSON การวิ่งแบบแห้งครั้งถัดไปจะหยิบมันขึ้นมา
พรอมต์ 3 - เครื่องยนต์ "เขียน engine.py เป็นฟังก์ชันบริสุทธิ์: def compute_boxes(rows: list[Row], scheme: Scheme) -> BoxTotals. ไม่มี I/O ไม่มี LLM BoxTotals เป็นคลาสข้อมูลที่ตรึงด้วย Box 1 ถึง Box 9 เป็น Decimal ค่าเป็น 2 dp เพิ่มสาขาที่ชัดเจนสำหรับ standard, flat_rate, cash, และ annual แผนงาน โครงการมาตรฐานเป็นแบบคงค้าง: ยอดขาย VAT มีส่วนช่วย Box 1, ซื้อ VAT ถึง Box 4 ใบแจ้งหนี้ที่เรียกเก็บเงินย้อนกลับจะเพิ่มลงในทั้ง Box 1 และ Box 4 การนำเข้า VAT ที่เลื่อนออกไปจะเพิ่มทั้ง Box 1 และ Box 4 การเข้าซื้อกิจการของสหภาพยุโรปสำหรับธุรกิจ NI อยู่ใน Box 2 และ Box 9 ปัดเศษที่ผลรวมกล่อง ไม่เคยต่อแถว เขียนหลักคำสอนที่เชื่อมโยงแต่ละสาขาเข้ากับกฎเกณฑ์ของมัน”
พรอมต์ 4 - รายงานการทดลองใช้งาน "เขียน dryrun.py ที่โทรมา compute_boxes() และส่งรายงาน Markdown: ส่วนหัวพร้อมจุดและแบบแผน, ตาราง Box 1 ถึง Box 9 พร้อมป้ายกำกับตามรูปแบบบัญญัติ, ส่วนต่อกลุ่มตัวแยกประเภทที่แสดงรายการ 10 แถวแรกตามยอดรวม, ส่วนความผิดปกติ และส่วนท้ายที่มี run_key และ SHA-256 ของ JSON ที่จัดประเภท บันทึกไปที่ ./audit/<period>/dryrun.md."
พรอมต์ 5 - ผู้ส่ง "เขียน submit.py เทียบกับ HMRC MTD VAT API v1.0 ใช้โฟลว์ OAuth2 authorization_code กับ PKCE อ่านโทเค็นการเข้าถึงจาก ~/.vat/token.json; รีเฟรชหากหมดอายุ ส่วนหัวป้องกันการฉ้อโกงที่จำเป็นตามที่จัดทำเอกสารโดย HMRC ดึงพวกเขาออกมา fraud_headers.py. ค่าเริ่มต้นเป็นแซนด์บ็อกซ์ https://test-api.service.hmrc.gov.uk. ขั้นตอน: GET /organisations/vat/{vrn}/obligations?status=Oค้นหาช่วงเวลาที่ตรงกัน แจ้งให้ผู้ปฏิบัติงานยืนยันโดยแสดงสรุปการทดลองเรียกใช้ จากนั้น POST /organisations/vat/{vrn}/returns กับ finalised: true. พิมพ์ receiptId. อย่าโทรสดเว้นแต่ HMRC_ENV=live ถูกตั้งค่าและประเภทตัวดำเนินการ ส่ง."
พร้อมท์ห้ารายการ สี่ไฟล์ และเอกสารกฎ Markdown ขนาดเล็ก เวลาในการสร้างทั้งหมดอาจเป็นหนึ่งสัปดาห์ในช่วงเย็นที่มุ่งเน้น ซึ่งส่วนใหญ่เป็นกฎตัวแยกประเภทและการเต้นรำ HMRC OAuth
7. เครื่องยนต์ VAT
เครื่องยนต์ถือเป็นส่วนที่จงใจน่าเบื่อ มันเป็นฟังก์ชันล้วนๆ ที่มีการทดสอบหน่วยและไม่มี AI นี่คือรูปร่างที่แท้จริง:
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),
)
สองสิ่งที่ควรทราบ ขั้นแรก ยอดรวมทุกกล่องจะถูกนับหนึ่งครั้งในตอนท้าย การปัดเศษแบบต่อแถวการรั่วไหลของเพนนีและ HMRC นั้นไม่น่าให้อภัยเกี่ยวกับผลรวมที่ไม่กระทบยอด ประการที่สอง เครื่องยนต์ถูกกำหนดไว้อย่างสมบูรณ์: อินพุตเดียวกัน เอาต์พุตเดียวกันทุกครั้ง นั่นคือคุณสมบัติที่ช่วยให้ฉันทดสอบหน่วยและเชื่อถือการทดลองเรียกใช้ได้
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")
เมื่อใช้สิ่งนี้ การถดถอยทุกครั้งจะถูกบันทึกไว้ใน CI ก่อนที่จะแตะรายงานการทดลองใช้งาน โคลดสามารถช่วยฉันเขียนแบบทดสอบเพิ่มเติมได้ ไม่สามารถเปลี่ยนเครื่องยนต์ได้หากไม่ผ่านการทดสอบ
8. วิดีโอสาธิต
ด้านล่างนี้เป็นคำแนะนำสั้นๆ ของไปป์ไลน์ที่ทำงานตั้งแต่ต้นทางถึงปลายทางโดยเทียบกับใบแจ้งหนี้และฐานข้อมูลค่าใช้จ่ายจริง (ไม่ระบุชื่อ) สำหรับหนึ่งไตรมาส: ทำให้เป็นมาตรฐาน จำแนกประเภท ทดลองดำเนินการ จากนั้นจึงส่งแซนด์บ็อกซ์ MTD การวนซ้ำทั้งหมดใช้เวลาสองสามนาที
9. การส่ง HMRC MTD
นี่คือส่วนที่เป็นส่วนที่เขียนทับส่วนใหญ่ MTD VAT API นั้นได้รับการบันทึกไว้อย่างดีและฟรี แต่มีสามสิ่งที่คุณต้องทำให้ถูกต้องก่อนที่จะโทร POST returns.
OAuth2 และโทเค็น ลงทะเบียนแอปพลิเคชันในฮับนักพัฒนา HMRC คุณจะได้รับรหัสไคลเอ็นต์ ข้อมูลลับไคลเอ็นต์ และ URI การเปลี่ยนเส้นทาง โฟลว์คือ authorization_code ด้วย PKCE: เปิดเบราว์เซอร์ไปที่หน้ายินยอม HMRC ผู้ใช้เข้าสู่ระบบด้วยข้อมูลรับรอง Government Gateway และให้สิทธิ์แอปพลิเคชันของคุณกับหมายเลขการลงทะเบียน VAT (VRN) HMRC เปลี่ยนเส้นทางกลับด้วยรหัสอนุญาต คุณแลกเปลี่ยนเป็นโทเค็นการเข้าถึง + โทเค็นการรีเฟรช แคชด้วยสิทธิ์ที่เหมาะสมบนดิสก์ โทเค็นการเข้าถึงมีอายุสั้น โทเค็นการรีเฟรชนั้นยาวกว่ามาก
ส่วนหัวของการป้องกันการฉ้อโกง HMRC ต้องใช้ชุดส่วนหัว HTTP ที่เป็นเอกสารซึ่งระบุอุปกรณ์ต้นทางในการเรียก API ทุกครั้ง: โซนเวลา, เรขาคณิตของหน้าจอ, ID ผู้ใช้, IP สาธารณะ, ที่อยู่ MAC, วิธีการเชื่อมต่อไคลเอ็นต์ ฯลฯ รายการและรูปแบบที่แน่นอนอยู่ในเอกสารประกอบของนักพัฒนา HMRC (ดู HMRC VAT Notice 700 และข้อกำหนดการป้องกันการฉ้อโกงที่เกี่ยวข้องสำหรับกฎที่เชื่อถือได้) Get them wrong and you get a 403 with a vague error. ทำให้พวกเขาถูกต้องแล้วการโทรก็จะผ่านไป I keep them in fraud_headers.py และยืนยันการมีอยู่ของพวกเขาในการทดสอบ
แซนด์บ็อกซ์ก่อน HMRC จัดให้มี Sandbox ฟรีที่ test-api.service.hmrc.gov.uk พร้อมด้วย VRN และภาระหน้าที่ของ Stub ดำเนินการไปป์ไลน์ทั้งหมดของคุณตั้งแต่ต้นจนจบโดยเทียบกับ Sandbox จนกว่าคุณจะส่งการคืนสินค้าปลอม ได้รับรหัสใบเสร็จปลอม และยืนยันการย้ายภาระผูกพันของคุณ จากนั้นคุณจะพลิก HMRC_ENV=live. ผู้ส่งของฉันปฏิเสธที่จะโทรสดเว้นแต่จะมีการตั้งค่าตัวแปรสภาพแวดล้อมและประเภทตัวดำเนินการ SUBMIT ที่พร้อมท์ ไม่มีเส้นทางการดำเนินการอัตโนมัติที่จะใช้งานจริง ค่าใช้จ่ายของการส่งข้อมูลสดที่ผิดพลาดเพียงครั้งเดียวนั้นสูงกว่าค่าใช้จ่ายของการทำซ้ำแซนด์บ็อกซ์อีกครั้งหนึ่งมาก
การเรียกเส้นทางแห่งความสุขที่แท้จริงนั้นสั้น: GET obligations กับ status=O หากต้องการค้นหาช่วงเวลาเปิด ให้แสดงรายงานการทดลองใช้งานแล้วให้ผู้ปฏิบัติงานยืนยัน จากนั้น POST returns โดยมีเลขเก้าช่องคือ periodKey จากภาระผูกพันและ finalised: true. บันทึกการตอบกลับ เขียนรหัสใบเสร็จลงในโฟลเดอร์การตรวจสอบ เท่านี้ก็เสร็จสิ้น
10. ความปลอดภัย การตรวจสอบ และการปฏิบัติตามข้อกำหนด
กฎสามข้อที่ฉันจะไม่ก้าวต่อไป อันดับแรก, โคลด์ไม่ได้อยู่ในเลขคณิต. ตัวแยกประเภทจะแนะนำการรักษาและ LLM สามารถแก้ไข Python และกฎได้ แต่ผลรวมของกล่องนั้นถูกสร้างขึ้นโดยฟังก์ชันล้วนๆ พร้อมการทดสอบหน่วย "AI ทำได้" ไม่ใช่การป้องกันภายใต้ MTD ที่สอง, การส่งทุกครั้งถูกควบคุมโดยมนุษย์. รายงานการทดลองดำเนินการเป็นแหล่งที่มาของความจริงในการลงนาม หากฉันไม่สามารถอธิบายตัวเลขจากแถวที่แยกประเภทและรหัสเครื่องยนต์ได้ ฉันก็จะไม่ส่ง ที่สาม, เส้นทางการตรวจสอบเป็นแบบผนวกเท่านั้น. JSON ที่จัดประเภททุกรายการ รายงานการทดลองใช้งานทุกรายการ ทุกการแจ้งเตือน ทุกส่วนต่าง และรหัสการรับ HMRC สุดท้ายสำหรับช่วงเวลานั้นจะถูกเขียนลงใน ./audit/<period>/ และมุ่งมั่นที่จะซื้อคืน git ส่วนตัว HMRC ต้องใช้เวลาบันทึกหกปี ของฉันเป็นสแน็ปช็อตที่ลงนามแล้ว
แนวทางปฏิบัติอีกสองประการที่ฉันอยากจะแนะนำแม้ว่าคุณจะไม่ยอมรับส่วนที่เหลือก็ตาม เรียกใช้การเชื่อมต่อ Source-DB เป็นบทบาทอ่านอย่างเดียวที่มีสิทธิ์น้อยที่สุด: ไม่ UPDATE, เลขที่ DELETE, เลขที่ DROP. เก็บความลับของไคลเอนต์ HMRC และโทเค็นการเข้าถึงออกจาก repo โดยใช้ตัวจัดการความลับหรือไฟล์ 0600 ในโฮมไดเร็กตอรี่ของคุณ ประเด็นคือการทำให้อุบัติเหตุเป็นไปไม่ได้โดยการก่อสร้าง ไม่ใช่แค่นโยบายเท่านั้น
11. เคสขอบ
VAT เป็นภาษีควบคุมที่ได้รับการแก้ไขโดยรัฐบาลสหราชอาณาจักรทุกแห่งตั้งแต่ปี 1973 โดยมีการซ้อนทับหลัง Brexit และโปรโตคอลของไอร์แลนด์เหนืออยู่ด้านบน เคสขอบมีจริง รายการโดยย่อของรายการที่เครื่องยนต์ของฉันจัดการ:
- Brexit และไอร์แลนด์เหนือ ธุรกิจในบริเตนใหญ่ไม่กรอก Box 8 หรือ Box 9 อีกต่อไป ธุรกิจ NI ยังคงทำเพื่อสินค้า (ไม่ใช่บริการ) ภายใต้โปรโตคอล NI เครื่องยนต์ใช้เวลา
is_ni_businessเป็นพารามิเตอร์และแยกส่วนตามลำดับ ดู HMRC VAT Notice 700 สำหรับกฎที่เชื่อถือได้ - ชาร์จย้อนกลับ บริการที่ซื้อจากซัพพลายเออร์นอกสหราชอาณาจักร (และบริการอุตสาหกรรมก่อสร้างในประเทศบางแห่ง) จะถูกคิดค่าใช้จ่ายแบบย้อนกลับ: คุณเพิ่มเอาท์พุตตามสัญญา VAT ให้กับ Box 1 และเรียกคืนจำนวนเท่ากันใน Box 4 ผลกระทบสุทธิต่อเงินสดเป็นศูนย์ ผลกระทบสุทธิต่อการคืนสินค้าคือกล่องสองกล่องที่เคลื่อนตัวมารวมกัน
- เลื่อนการนำเข้า VAT สำหรับสินค้าที่นำเข้าจากนอกสหราชอาณาจักร คุณจะเลื่อนการนำเข้า VAT ไปเป็นการส่งคืนครั้งถัดไป แทนที่จะชำระเงินที่ชายแดน การรักษาจะมีลักษณะคล้ายกับการชาร์จแบบย้อนกลับเมื่อส่งคืน
- ได้รับการยกเว้นบางส่วน หากคุณผลิตทั้งวัสดุที่ต้องเสียภาษีและได้รับการยกเว้น คุณสามารถเรียกคืนอินพุต VAT ได้เฉพาะในขอบเขตที่เกี่ยวข้องกับวัสดุที่ต้องเสียภาษีเท่านั้น มีวิธีมาตรฐานและวิธีพิเศษ นี่คือมุมที่นักบัญชีต้องการบ่อยที่สุด อย่าพยายามเข้ารหัสกฎการยกเว้นบางส่วนในไฟล์ Markdown
- โครงการอัตราคงที่ คุณเรียกเก็บเงิน VAT ในอัตรามาตรฐาน แต่จ่าย HMRC เป็นเปอร์เซ็นต์คงที่ของมูลค่าการซื้อขายรวม และโดยทั่วไปคุณจะไม่สามารถเรียกคืนอินพุต VAT ได้ เครื่องยนต์มีสาขาแยกต่างหากสำหรับสิ่งนี้ และละเว้นการจำแนกต่อแถวเป็นส่วนใหญ่
- ใบแจ้งหนี้หลายสกุลเงิน แปลงแต่ละแถวเป็น GBP ในอัตราที่มีผลในวันที่ที่เกี่ยวข้อง HMRC เผยแพร่อัตราเฉลี่ยรายเดือน คุณยังสามารถใช้อัตราสปอตได้อีกด้วย มีความสม่ำเสมอ ที่
fx_rate_to_gbpคอลัมน์เก็บอัตราจริงที่ใช้ต่อแถว - ใบลดหนี้. ใบลดหนี้เป็นใบแจ้งหนี้ติดลบ จะลด Box 1 และ Box 6 (หรือ Box 4 และ Box 7 หากเป็นเครดิตการซื้อ) ตรวจสอบให้แน่ใจว่าคุณ
net_amountและvat_amountสามารถติดลบได้และเครื่องยนต์จะรวมค่าเหล่านั้นอย่างถูกต้อง - การฝากเงินและการชำระเงินตามขั้นตอน จุดเสียภาษีอาจเป็นเมื่อได้รับเงินมัดจำ ไม่ใช่เมื่อมีการออกใบแจ้งหนี้ครั้งสุดท้าย การบัญชีเงินสดสามารถทำให้สิ่งนี้ง่ายขึ้น
- การเปลี่ยนแปลงอัตรากลางไตรมาส ประวัติศาสตร์หายากแต่เป็นไปได้ เก็บอัตราต่อแถว ไม่ใช่เป็นค่าคงที่โดยรวม
12. ต้นทุน
ตัวเลขที่ซื่อสัตย์ เวลาสร้าง เป็นช่วงเย็นที่มีสมาธิประมาณหนึ่งสัปดาห์ หรือเรียกว่า 25 ถึง 30 ชั่วโมง ส่วนใหญ่เป็นการทำให้สคีมาเป็นมาตรฐาน กฎตัวแยกประเภท และการเขียนการทดสอบเครื่องยนต์ การเต้น HMRC OAuth เป็นกลุ่มที่ใหญ่เป็นอันดับสอง ต้นทุนการดำเนินการต่อไตรมาส มีขนาดเล็ก: การสมัครสมาชิก Claude Code ที่มีอยู่ของฉัน (ชำระเงินสำหรับงานซอฟต์แวร์อื่นแล้ว), เพนนีสองสามเพนนีในการใช้โทเค็นตัวแยกประเภท และศูนย์ปอนด์สำหรับ HMRC API กลไกและบันทึกการตรวจสอบทำงานบนเครื่อง Mac ของฉัน เวลาของผู้ตรวจสอบต่อไตรมาส ใช้เวลาประมาณหนึ่งชั่วโมง ส่วนใหญ่ใช้เวลาอ่านหนังสือทดลองกับนักบัญชีของฉัน เปรียบเทียบกับการถกเถียงกันในสเปรดชีตประมาณครึ่งวันภายใต้เวิร์กโฟลว์แบบเก่า และเวลาที่ประหยัดได้จะจ่ายคืนต้นทุนการก่อสร้างภายในปีแรก
หากคุณตั้งราคานี้เป็นโปรเจ็กต์ที่คุณจ่ายเงินให้คนอื่นสร้าง คุณจะอยู่ในอัตราค่าที่ปรึกษาสี่หลัก ซึ่งเป็นเหตุผลว่าทำไมแพ็คเกจ SaaS ที่ราคา 20 ถึง 50 ปอนด์ต่อเดือนจึงแทบจะเป็นคำตอบที่ถูกต้องสำหรับธุรกิจส่วนใหญ่เสมอไป คำถามที่น่าสนใจไม่ใช่ว่า "ถูกกว่า Xero หรือเปล่า" - โดยปกติจะไม่ใช่ - แต่ "มันแม่นยำกว่า ตรวจสอบได้มากกว่า และง่ายกว่าสำหรับฉันในการให้เหตุผลหรือไม่" สำหรับฉันคำตอบคือใช่
13. อะไรจะเกิดขึ้นต่อไปและบทสรุป
ชิ้นต่อไปที่ฉันต้องการเพิ่มคือ: การนำเข้าฟีดธนาคารอัตโนมัติ (Open Banking AISP), OCR สำหรับ PDF ของซัพพลายเออร์ ดังนั้น URL ใบเสร็จรับเงินจะกลายเป็นบรรทัดที่สแกนจริง, cron ที่กำหนดเวลาไว้ + ขั้นตอนการอนุมัติ Slack ดังนั้นฉันจึงไม่จำเป็นต้องจำกำหนดเวลา และการขยายเวลาการเรียกเก็บเงินด้วยตนเองสำหรับผู้ขายในตลาดกลาง ไม่มีสิ่งใดที่เปลี่ยนรูปร่างแกนกลางของไปป์ไลน์ สิ่งเหล่านี้เป็นของตกแต่งทั้งหมดบนแผนภาพห้าเลนเดียวกัน
อย่างน้อยบทเรียนที่ใหญ่กว่าสำหรับฉันก็คือโปรเจ็กต์ Claude Code ขนาดเล็กสามารถจัดการกับเวิร์กโฟลว์ที่ได้รับการควบคุมที่น่าเบื่อครึ่งหนึ่งได้ดีเพียงใด ส่วนที่น่าเบื่อเป็นงานส่วนใหญ่: การปรับสคีมาให้เป็นมาตรฐาน, ผู้นำเข้า CSV, รายงานการกระทบยอด, โปรแกรมติดตั้ง, กฎ Edge-Case, การบันทึกการตรวจสอบ คลอดด์เก่งมากในเรื่องนั้น ครึ่งหนึ่งของการกำหนด - คณิตศาสตร์จริง, การส่งจริง - ยังคงอยู่ใน Python ธรรมดาพร้อมการทดสอบหน่วยและประตูการอนุมัติโดยมนุษย์ซึ่งอยู่ตรงที่ของมัน
ข้อจำกัดความรับผิดชอบอีกครั้ง บทความนี้ไม่ใช่คำแนะนำด้านภาษี ก่อนที่คุณจะชี้ให้เห็นสิ่งนี้กับบัญชี HMRC จริง ให้ขอให้นักบัญชีที่จดทะเบียนในสหราชอาณาจักรตรวจสอบการคืนสินค้าครั้งแรกของคุณตั้งแต่ต้นทางถึงปลายทาง วิ่งแข่งกับแซนด์บ็อกซ์ HMRC MTD จนกว่าคุณจะและนักบัญชีของคุณมั่นใจ สำหรับกฎที่เชื่อถือได้เกี่ยวกับการรักษา VAT เฉพาะใดๆ โปรดดู HMRC VAT Notice 700 และประกาศที่เกี่ยวข้องบนเว็บไซต์ gov.uk หากคุณต้องการเวอร์ชันสั้นของทั้งหมดนี้ โพสต์บล็อกสหาย ใช้เวลาอ่านห้านาที ไม่เช่นนั้น ให้สมัครสมาชิกแล้วฉันจะเขียนต่อเมื่อสแต็กครบกำหนด
ภาพรวม SEO สำหรับบทความนี้
- ชื่อ SEO: สร้างระบบส่งคืน VAT ด้วย Claude Code
- คำอธิบายเมตา: ใช้ Claude Code เพื่ออ่านฐานข้อมูลใบแจ้งหนี้และค่าใช้จ่ายของคุณ จำแนก VAT คำนวณ HMRC Box 1 เป็น 9 ทดลองดำเนินการ จากนั้นส่งไปยัง MTD - กลไกที่กำหนดขึ้นเอง การอนุมัติโดยมนุษย์
- คำหลักหลัก: claude-code, vat, hmrc, mtd, การบัญชี, ระบบอัตโนมัติ, python, ai-agent, finops, ธุรกิจขนาดเล็ก
- Twitter / X (ไม่เกิน 280 ตัวอักษร): สร้างผลตอบแทน UK VAT ของฉันขึ้นมาใหม่เป็นโครงการ Claude Code: ใบแจ้งหนี้ Postgres + ค่าใช้จ่าย DB ถึง HMRC Box 1 ถึง 9, เอ็นจิ้น Python ที่กำหนด, การส่ง MTD แซนด์บ็อกซ์แรก, ประตูการอนุมัติโดยมนุษย์ การเขียนบทความฉบับเต็ม + SVG เวิร์กโฟลว์ + วิดีโอสาธิต ไม่ใช่คำแนะนำด้านภาษี #โคลดโคด #VAT #HMRC #MTD
- โพสต์ใน LinkedIn: ฉันสร้างผลตอบแทน UK VAT รายไตรมาสขึ้นมาใหม่เป็นโครงการ Claude Code ขนาดเล็กในปีนี้ โดยจะอ่านฐานข้อมูลใบแจ้งหนี้และค่าใช้จ่ายของฉันแบบอ่านอย่างเดียว จำแนกแต่ละบรรทัดตามการจัดการ VAT โดยใช้ไฟล์กฎที่จัดทำเป็นเอกสาร คำนวณ HMRC Box 1 ถึง 9 ด้วยกลไก Python ที่กำหนดไว้ (ไม่มี LLM ในเลขคณิต) และหลังจากที่ฉันลงชื่อออกจากการทดลองเรียกใช้แล้วเท่านั้น ระบบจะส่งผ่าน HMRC MTD API - แซนด์บ็อกซ์ก่อน ส่วนหัวป้องกันการฉ้อโกง โฟลว์ OAuth2 แบบเต็ม ตอนนี้ทั้งไตรมาสใช้เวลาประมาณหนึ่งชั่วโมง การเขียนบทความฉบับเต็มพร้อมข้อความแจ้ง สคีมา รหัสเครื่องยนต์ เคส Edge และเรื่องราวค่าใช้จ่ายที่ตรงไปตรงมาอยู่ที่ Workstation ไม่ใช่คำแนะนำด้านภาษี ให้นักบัญชีตรวจสอบผลตอบแทนครั้งแรกของคุณ
