📄《Python 发票系统从 0 到 1》

34 阅读2分钟

① 一句话架构:发票 4 大模型 🏗️

模型字段精度
发票头号码、日期、买方、卖方文本
商品行名称、数量、单价、税率金额 Decimal
价税合计不含税、税额、含税Decimal 2 位
PDF 版式二维码、章、水印版式文件

口诀:“头行税,PDF,Decimal 无误差” 🎵


② 模型层:发票头 + 商品行(Decimal)💰

from decimal import Decimal, ROUND_HALF_UP

class InvoiceItem:
    def __init__(self, name: str, qty: int, price: Decimal, tax_rate: Decimal):
        self.name = name
        self.qty = qty
        self.price = price
        self.tax_rate = tax_rate

    @property
    def amount(self) -> Decimal:
        """不含税金额"""
        return (self.qty * self.price).quantize(Decimal('0.01'), ROUND_HALF_UP)

    @property
    def tax(self) -> Decimal:
        """税额"""
        return (self.amount * self.tax_rate).quantize(Decimal('0.01'), ROUND_HALF_UP)

    @property
    def total(self) -> Decimal:
        """含税金额"""
        return self.amount + self.tax

③ 发票头:自动编号 + 日期 📅

import datetime, uuid

class InvoiceHeader:
    def __init__(self, buyer: str, seller: str):
        self.number = str(uuid.uuid4())[:8]  # 模拟号码
        self.date = datetime.date.today()
        self.buyer = buyer
        self.seller = seller
        self.items: list[InvoiceItem] = []

    def add_item(self, item: InvoiceItem):
        self.items.append(item)

    @property
    def total_amount(self) -> Decimal:
        return sum(i.amount for i in self.items)

    @property
    def total_tax(self) -> Decimal:
        return sum(i.tax for i in self.items)

    @property
    def total_with_tax(self) -> Decimal:
        return self.total_amount + self.total_tax

④ 使用演示:10 行开票 🚀

if __name__ == "__main__":
    inv = InvoiceHeader("买方公司", "卖方公司")
    inv.add_item(InvoiceItem("咖啡", 2, Decimal('35.00'), Decimal('0.13')))  # 13% 税率
    inv.add_item(InvoiceItem("三明治", 1, Decimal('78.00'), Decimal('0.13')))

    print(f"发票号:{inv.number}")
    print(f"不含税:{inv.total_amount}")
    print(f"税额:{inv.total_tax}")
    print(f"含税:{inv.total_with_tax}")

运行:python invoice_demo.py → 输出发票摘要!


⑤ PDF 版式:模板 + 二维码 + 章 🖨️

from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4
import qrcode

def make_pdf(invoice: InvoiceHeader, filename: str):
    c = canvas.Canvas(filename, pagesize=A4)
    width, height = A4

    # 标题
    c.setFont("Helvetica-Bold", 20)
    c.drawString(50, height - 50, "电子发票")

    # 发票头
    c.setFont("Helvetica", 12)
    c.drawString(50, height - 100, f"发票号:{invoice.number}")
    c.drawString(50, height - 120, f"日期:{invoice.date}")
    c.drawString(50, height - 140, f"买方:{invoice.buyer}")
    c.drawString(50, height - 160, f"卖方:{invoice.seller}")

    # 商品行
    y = height - 200
    for i, item in enumerate(invoice.items):
        c.drawString(50, y - i * 20, f"{item.name} ×{item.qty}  {item.amount} + {item.tax} = {item.total}")

    # 价税合计
    c.setFont("Helvetica-Bold", 14)
    c.drawString(50, y - len(invoice.items) * 20 - 40, f"不含税:{invoice.total_amount}")
    c.drawString(50, y - len(invoice.items) * 20 - 60, f"税额:{invoice.total_tax}")
    c.drawString(50, y - len(invoice.items) * 20 - 80, f"含税:{invoice.total_with_tax}")

    # 二维码
    qr = qrcode.make(invoice.number)
    qr.save("qr.png")
    c.drawImage("qr.png", width - 150, height - 200, width=100, height=100)

    c.save()

运行:make_pdf(inv, "invoice.pdf") → 生成正式发票!


⑥ 批量导出:CSV + 彩虹金额 🌈

import pandas as pd

def export_csv(invoice: InvoiceHeader, filename: str):
    df = pd.DataFrame([
        {
            "发票号": invoice.number,
            "商品": item.name,
            "数量": item.qty,
            "单价": item.price,
            "税率": item.tax_rate,
            "不含税": item.amount,
            "税额": item.tax,
            "含税": item.total,
        }
        for item in invoice.items
    ])
    df.to_csv(filename, index=False, float_format="%.2f")
    print(f"✅ 已导出:{filename}")

# 使用
export_csv(inv, "invoice.csv")

⑦ 彩蛋:终端彩虹发票(15 行)🌈

import random, math, os
from decimal import Decimal

def rainbow_invoice(invoice: InvoiceHeader):
    os.system('clear||cls')
    for i, item in enumerate(invoice.items):
        r = int(255 * (1 + math.cos(i * 2 * math.pi / len(invoice.items))) / 2)
        g = int(255 * (1 + math.cos(i * 2 * math.pi / len(invoice.items) + 2)) / 2)
        b = int(255 * (1 + math.cos(i * 2 * math.pi / len(invoice.items) + 4)) / 2)
        print(f"\033[38;2;{r};{g};{b}m{item.name} ×{item.qty} = {item.total}\033[0m")
    print(f"\033[1m\033[36m含税:{invoice.total_with_tax}\033[0m")

# 使用
rainbow_invoice(inv)

运行:python rainbow_invoice.py → 彩虹渐变发票!


⑧ 万能解毒剂:检查清单 ✅

场景解毒法
金额存储int 分厘 或 Decimal
税率计算quantize(Decimal('0.01'), ROUND_HALF_UP)
批量导出pandas.to_csv(float_format='%.2f')
PDF 版式reportlab + qrcode

⑨ 彩蛋:打包 EXE(一键发送)📦

pip install pyinstaller
pyinstaller -F invoice_demo.py
# dist/invoice_demo.exe 直接发