一个具有灵活数据模型、预定义税率、强大解析逻辑的税务库。
Requires
- php: >=5.4.0
- commerceguys/addressing: ~0.5
- commerceguys/enum: ~1.0
- commerceguys/zone: ~0.5
- doctrine/collections: ~1.0
Requires (Dev)
- mikey179/vfsstream: 1.*
- phpunit/phpunit: ~4.0
- symfony/intl: >=2.3
This package is not auto-updated.
Last update: 2024-09-18 19:12:43 UTC
README
PHP 5.4+ 税务管理库。
特性
- 智能数据模型,适用于税率变动的情况(例如:“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对象引用的税率类型/率(明确的供应地,例如“在西班牙提供培训的法国公司”)"
用法示例
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);