财税业务后台需在Yii模型层精准实现强语义校验:金额禁负、税率限档、日期格式强制、税目编码存在性校验;按开票/红冲/作废等场景动态配置safe属性与规则;复杂逻辑(如进销项抵扣、免税标识一致性、电子签章验真)须封装为独立Validator类;动态阈值与数据库依赖校验应置于beforeValidate中。

财税业务后台对数据准确性、合规性、完整性要求极高,比如发票金额不能为负、税率必须匹配税目、进销项抵扣逻辑需闭环、凭证摘要不能为空且长度受限、时间范围不能跨年错误等。Yii 框架本身不内置财税语义规则,但其验证机制足够灵活——关键在于把业务约束精准映射到模型层的 rules()、scenarios、beforeValidate 和自定义验证器中,避免校验逻辑散落在控制器或服务层。
核心字段必须绑定强语义规则
财税字段不是普通表单字段,每个都有明确的会计/税务含义,需用内置验证器组合加固:
-
金额类(如 amount、tax_amount):用
['amount', 'number', 'min' => 0]禁止负值;配合['amount', 'filter', 'filter' => 'floatval']统一类型,防止字符串"100.00"被当整数截断;若需保留两位小数,加['amount', 'number', 'integerOnly' => false, 'min' => 0, 'max' => 999999999.99] -
税率(tax_rate):用
['tax_rate', 'in', 'range' => [0, 0.03, 0.06, 0.09, 0.13, 0.16, 0.25], 'strict' => true]限定法定税率档位,strict防止 "3%" 被转成 3 后误判 -
日期类(invoice_date、period_start):用
['invoice_date', 'date', 'format' => 'php:Y-m-d']强制格式;跨期校验(如 period_start ≤ period_end)必须用内联验证器,不能只靠前端 -
税目编码(tax_code):用
['tax_code', 'exist', 'targetClass' => TaxCategory::class, 'targetAttribute' => 'code', 'message' => '税目编码不存在或已停用'],确保与主数据一致
场景驱动多状态校验(开票/红冲/作废/申报)
同一张发票在不同业务状态下,校验重点完全不同:
- 开票(
scenario = 'issue'):校验seller_name、buyer_tax_id(纳税人识别号必填且符合正则)、line_items非空且每行amount × tax_rate = tax_amount - 红冲(
scenario = 'credit'):强制要求原发票号original_invoice_no存在且状态为“已开票”,且红字内容必须与原票一一反向对应 - 作废(
scenario = 'void'):仅允许当日、未记账、未认证的发票作废,需在beforeValidate()中查库判断
务必在模型中重写 scenarios() 显式声明各场景的 safe 属性,例如:return ['issue' => ['seller_name', 'buyer_tax_id', 'line_items'], 'credit' => ['original_invoice_no', 'reason']];否则 load() 不会填充非 safe 字段,后续验证形同虚设。
复杂财税逻辑必须抽离为独立验证器
以下逻辑绝不能写在 rules() 里硬凑,必须封装为 Validator 类:
-
进销项抵扣匹配验证:新建
VatDeductionValidator,继承yii\validators\Validator,在validateAttribute()中查询进项发票池,比对销项税额是否超可抵扣余额 -
免税/零税率标识一致性:当
tax_rate = 0时,tax_exemption_type必须从预设枚举中选择(如“出口免税”“小微企业免税”),否则报错 -
电子发票签章有效性:接收 PDF 或 OFD 文件后,调用国密 SDK 验签,失败则
$model->addError('file', '数字签名无效,请检查CA证书')
类名必须以 Validator 结尾(如 VatDeductionValidator),并在 rules() 中声明:['line_items', 'VatDeductionValidator']。如需配置参数(如是否启用严格抵扣),定义 public 属性并在 rules 中传入:['line_items', 'VatDeductionValidator', 'strict' => true]。
动态校验与运行时依赖处理
财税规则常依赖上下文,比如“小规模纳税人月销售额≤10万元免征增值税”,该阈值可能随政策调整:
- 不要在
rules()方法里写if ($this->taxpayerType === 'small') { ... }—— rules 是静态方法,$this为 null - 改用
beforeValidate()动态加规则:if ($this->taxpayerType === 'small') { $this->addRule('monthly_sales', 'number', ['max' => Yii::$app->params['vatExemptionThreshold']]); } - 涉及数据库查询的判断(如“同一税号本月已开票金额”),必须在
beforeValidate()或自定义验证方法中执行,避免 rules() 初始化阶段触发查询
注意:addRule() 添加的规则默认只对 default 场景生效,若用于 issue 场景,必须显式指定 ['on' => 'issue'],并在 load() 前设置 $model->scenario = 'issue'。


















