commerceguys / tax
具有灵活数据模型、预定义税率、强大解析逻辑的税务库。
Requires
- php: >=5.5.0
- commerceguys/addressing: ^1.0
- commerceguys/zone: ^0.8
- doctrine/collections: ^1.0 || ^2.0
Requires (Dev)
- mikey179/vfsstream: ^1.0
- phpunit/phpunit: ^8.0
README
PHP 5.5+ 税务管理库。
功能
- 智能数据模型,适用于税率变化的金额("19% -> 21% 在1月1日")
- 为欧盟国家和瑞士预定义税率。更多即将到来。
- 具有所有主要用例逻辑的税务解析器。
关于库设计的幕后故事可以在 这篇博客文章 中找到。
在数据集中看不到您国家的税种和税率?给我们发一个PR!
数据模型
Zone 1-1 TaxType 1-n TaxRate 1-n TaxRateAmount
每种税种都有一个区域和一个或多个税率。每个税率都有一个或多个税率金额。
示例
- 税种:法国增值税
- 区域:"法国 (增值税)"(包括"法国无科西嘉"和"摩纳哥")
- 税率:标准、中级、减税、超级减税
- 标准税率的税率金额:19.6%(截至2014年1月1日),20%(自2014年1月1日起)
基本接口不强制设置器,因为服务类不需要它们。扩展接口(TaxTypeEntityInterface,(TaxRateEntityInterface,(TaxRateAmountEntityInterface))也提供为此目的,以及匹配的 TaxType, TaxRate 和 TaxRateAmount 类,它们可以用作示例或由Doctrine映射。
税务解析器
找到给定应税对象最适合的税种/税率/金额的过程称为解析。除了 应税对象 之外,还向系统传递一个包含客户和商店信息的 上下文对象。
税务解析在三个步骤中进行
- 解析税种。
- 解析每个解析的税种的税率。
- 为每个解析的税率获取税率金额(通过调用
$rate->getAmount($date)
)。
通过调用注册的解析器(按优先级排序)解析税种和税率,直到其中一个返回结果。
包含的税种解析器
-
CanadaTaxTypeResolver(加拿大特定逻辑)
商店按照客户所在省份/地区的税种来收取税费。
如果从魁北克省的商店向安大略省的客户销售,则应用安大略省的HST(消费税)。
-
EuTaxTypeResolver(欧盟特定逻辑)
一家法语商店销售实体产品(例如T恤)将对欧盟客户收取法国增值税。
自2015年1月1日起,一家法语商店销售数字产品(例如电子书)将应用欧盟客户的税率(例如,德国客户 - 德国增值税等)。
如果欧盟客户提供了增值税号,法语商店将收取0%的社区内部税率。
-
DefaultTaxTypeResolver(适用于大多数国家的逻辑)
如果客户和商店属于同一区域,则返回匹配的税种。
塞尔维亚商店向塞尔维亚客户销售,使用塞尔维亚增值税。
包含的税率解析器
- DefaultTaxRateResolver - 返回税种默认税率。
用户将为以下内容创建自定义解析器
- "纽约州T恤200美元以下不收税"
- "9月1日学校用品免税(美国税收假日)"
- "法国和其他国家的电子书减税率"。
- "返回由$ taxable object引用的税种/税率(明确的供应地,例如"在西班牙提供培训的法国公司")
使用示例
use CommerceGuys\Tax\Repository\TaxTypeRepository; use CommerceGuys\Tax\Resolver\TaxType\ChainTaxTypeResolver; use CommerceGuys\Tax\Resolver\TaxType\CanadaTaxTypeResolver; use CommerceGuys\Tax\Resolver\TaxType\EuTaxTypeResolver; use CommerceGuys\Tax\Resolver\TaxType\DefaultTaxTypeResolver; use CommerceGuys\Tax\Resolver\TaxRate\ChainTaxRateResolver; use CommerceGuys\Tax\Resolver\TaxRate\DefaultTaxRateResolver; use CommerceGuys\Tax\Resolver\TaxResolver; // The repository, and the resolvers are usualy initialized by the // container, this is just a verbose example. $taxTypeRepository = new TaxTypeRepository(); $chainTaxTypeResolver = new ChainTaxTypeResolver(); $chainTaxTypeResolver->addResolver(new CanadaTaxTypeResolver($taxTypeRepository)); $chainTaxTypeResolver->addResolver(new EuTaxTypeResolver($taxTypeRepository)); $chainTaxTypeResolver->addResolver(new DefaultTaxTypeResolver($taxTypeRepository)); $chainTaxRateResolver = new ChainTaxRateResolver(); $chainTaxRateResolver->addResolver(new DefaultTaxRateResolver()); $resolver = new TaxResolver($chainTaxTypeResolver, $chainTaxRateResolver); // You can also provide the customer's tax number (e.g. VAT number needed // to trigger Intra-Community supply rules in EU), list of additional countries // where the store is registered to collect tax, a different calculation date. $context = new Context($customerAddress, $storeAddress); $amounts = $resolver->resolveAmounts($taxable, $context); // More rarely, if only the types or rates are needed: $rates = $resolver->resolveRates($taxable, $context); $types = $resolver->resolveTypes($taxable, $context);