很多人理解 AvaTax 集成时,只盯着“过账前调用一次税接口”。但企业版 account_avatax 真正要处理的是一整个外部税生命周期:什么时候提交、什么时候撤销、退款时金额符号怎么表达,以及外部返回没税时 Odoo 怎么收敛。
参考入口:
enterprise/account_avatax/tests/test_avatax.py
一、过账才是外部税真正落地的时点
测试 test_01_odoo_invoice / test_02_odoo_invoice 都体现了这一点:发票在 Odoo 内部先是无税线或待外部计算状态,真正 action_post() 后才把外部税结果合入发票金额。这意味着 AvaTax 不是静态税表,而是 posting 时的外部定税服务。
二、回草稿不是只回 Odoo,本地回滚还要通知外部 uncommit
测试专门 patch 了 _uncommit_external_taxes,并验证发票 button_draft() 时会触发。也就是说,如果你把一张已提交给 AvaTax 的发票拉回草稿,Odoo 不会假装外部世界不存在,而会尝试把那笔外部税事务一起撤销或解绑定。
三、退款发送给 AvaTax 的金额语义必须是负数
test_01_odoo_refund 和 test_02_odoo_refund 都强调:退款文档行发送给 AvaTax 时,正常商品应按负数金额表达;折扣等特殊行可能需要反向判断。这个负号不是格式细节,而是外部税服务识别“这是 refund 而不是 sale”的关键。
四、外部返回 0 税,也不是异常
测试 test_04_odoo_invoice 里,即使 AvaTax 返回 no-tax 结果,Odoo 也要稳定收敛:发票总额保持原未税金额,不因为“没税可加”而崩掉。这说明集成层既要处理正常税额,也要处理合法的零税响应。
五、实战建议
- 排查税额异常时,把 posting 与 draft reset 都纳入链路,不要只看 create/edit。
- 退款场景优先检查发送给外部的金额符号与 line mapping。
- 对“0 税返回”要视为业务结果,不要默认当接口失败。
六、结论
AvaTax 集成的难点从来不是“调到了接口”,而是外部税事务能否和 Odoo 发票状态机保持一致:过账时提交、回草稿时撤销、退款时换语义。这三步少一步,账就可能对不上。
DISCUSSION
评论区