第2-4-8章 規則引擎Drools實戰(1)-個人所得稅計算器
9. Drools實戰
9.1 個人所得稅計算器
本小節我們需要通過Drools規則引擎來根據規則計算個人所得稅,最終資料效果如下:
9.1.1 名詞解釋
稅前月收入:即稅前工資,指交納個人所得稅之前的總工資
應納稅所得額:指按照稅法規定確定納稅人在一定期間所獲得的所有應稅收入減除在該納稅期間依法允許減除的各種支出後的餘額
稅率:是對徵稅物件的徵收比例或徵收額度
速算扣除數:指為解決超額累進稅率分級計算稅額的複雜技術問題,而預先計算出的一個數據,可以簡化計算過程
扣稅額:是指實際繳納的稅額
稅後工資:是指扣完稅後實際到手的工資收入
9.1.2 計算規則
要實現個人所得稅計算器,需要了解2019個稅扣繳變化:
應納稅所得額=(月收入-五險一金-起徵點-依法確定的其他扣除-專項附加扣除)*適用稅率
9.1.2.1 新稅制主要有哪些變化?
- 扣繳義務人在支付居民個人工資、薪金所得時,需按照“累計預扣法”核算預扣個人所得稅。較之前按月的稅率表變化成按年計稅,適用個人所得稅預扣率表
- 首次設立了子女教育、繼續教育、大病醫療、住房貸款利息或住房租金、贍養老人六項專項附加扣除。(在稅前工資扣除相應額度後核算個稅)
9.1.2.2 資較高人員本次個稅較少,可能到年底扣稅增加?
〔例1〕某職員2015年入職,2019年每月應發工資均為10000元,每月減除費用5000元,“三險一金”等專項扣除為1500元,從1月起享受子女教育專項附加扣除1000元,沒有減免收入及減免稅額等情況,以前三個月為例,應當按照以下方法計算預扣預繳稅額:
1月份:(10000-5000-1500-1000)×3% =75元;
2月份:(10000×2-5000×2-1500×2-1000×2)×3%-75 =75元;
3月份:(10000×3-5000×3-1500×3-1000×3)×3%-75-75 =75元;
進一步計算可知,該納稅人全年累計預扣預繳應納稅所得額為30000元,一直適用3%的稅率,因此各月應預扣預繳的稅款相同
〔例2〕某職員2015年入職,2019年每月應發工資均為30000元,每月減除費用5000元,“三險一金”等專項扣除為4500元,享受子女教育、贍養老人兩項專項附加扣除共計2000元,沒有減免收入及減免稅額等情況,以前三個月為例,應當按照以下方法計算各月應預扣預繳稅額:
1月份:(30000–5000-4500-2000)×3% = 555元;
2月份:(30000×2-5000×2-4500×2-2000×2)×10% -2520 -555 =625元;
3月份:(30000×3-5000×3-4500×3-2000×3)×10% -2520 -555-625 =1850元;
按照上述演算法,每一個月應納個稅額:
專案 | 1月 | 2月 | 3月 | 4月 | 5月 | 6月 | 7月 | 8月 | 9月 | 10月 | 11月 | 12月 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
每月稅前工資 | 30000 | 30000 | 30000 | 30000 | 30000 | 30000 | 30000 | 30000 | 30000 | 30000 | 30000 | 30000 |
扣除各項減免額後應對稅率 | 3% | 10% | 10% | 10% | 10% | 10% | 10% | 20% | 20% | 20% | 20% | 20% |
月應預扣預繳個稅 | 555 | 625 | 1850 | 1850 | 1850 | 1850 | 1850 | 2250 | 3700 | 3700 | 3700 | 3700 |
上述計算結果表明,由於2月份累計預扣預繳應納稅所得額為37000元,已適用10%的稅率,因此2月份和3月份應預扣預繳有所增高。8月份已適用於20%的稅率。根據上述計算結果表明,雖每月工資薪金額和相關扣除額相同,年度內越往後,應納稅所得額越大適用稅率越高,則預扣預繳稅款可能會越大,會出現類似“12月取得工資10000元預扣預繳的個稅稅款,比1月份取得工資薪金30000元預扣預繳的個稅稅款多的”現象。
〔例3〕某職員2022年每月應發工資均為15000元,每月減除費用5000元,養老保險個人繳納8%,為15000×8%=1200元,失業保險個人繳納0.5%,為15000×0.5%=75元,醫療保險個人繳納2%+3元,為15000×2%+3=303元,住房公積金個人繳納比例12%,為15000×12%=1800元,享受住房租金專項附加扣除1500元,沒有減免收入及減免稅額等情況,以前三個月的計算為例,應當按照以下方法計算各月應預扣預繳稅額:
應納稅所得額=月收入-五險一金-起徵點-依法確定的其他扣除-專項附加扣除
1月應納稅所得額=15000–5000-1200-75-303-1800-1500=5122元
1月份:1月應納稅所得額 × 3% = 153.66元;
2月份:1月應納稅所得額 × 2 × 3% -153.66 =153.66元;
3月份:1月應納稅所得額 × 3 × 3% -153.66 - 153.66=153.66元;
按照上述演算法,每一個月應納個稅額:
專案 | 1月 | 2月 | 3月 | 4月 | 5月 | 6月 | 7月 | 8月 | 9月 | 10月 | 11月 | 12月 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
每月稅前工資 | 15000 | 15000 | 15000 | 15000 | 15000 | 15000 | 15000 | 15000 | 15000 | 15000 | 15000 | 15000 |
扣除各項減免額後應對稅率 | 3% | 3% | 3% | 3% | 3% | 3% | 3% | 10% | 10% | 10% | 10% | 10% |
月應預扣預繳個稅 | 153.66 | 153.66 | 153.66 | 153.66 | 153.66 | 153.66 | 153.66 | 501.98 | 512.2 | 512.2 | 512.2 | 512.2 |
上述計算結果表明,由於8月份累計預扣預繳應納稅所得額為40976元,已適用10%的稅率,因此8月份應預扣預繳有所增高。
9.1.2.3 關於年度彙算清繳
一般來講,有以下情形之一的,您可以選擇在次年3月1日至6月30日內,自行向匯繳地主管稅務機關辦理彙算清繳申報時進行專項附加扣除,稅款多退少補;(1)不願意通過單位辦理扣除,未將相關專項附加扣除資訊報送給任職受僱單位的;
(2)在同一納稅年度涉及有兩處工資薪金所得或涉及勞務報酬所得的;
(3)有大病醫療支出專案的;
(4)納稅年度內未享受或未足額享受專項附加扣除等情形。
9.1.2.4 個人所得稅預扣率表(居民個人工資、薪金所得預扣預繳適用)
級數 | 全年累計預扣預繳應納稅所得額 | 稅率(%) | 速算扣除數 |
---|---|---|---|
1 | 不超過36,000元的部分 | 3 | 0 |
2 | 超過36,000元至144,000元的部分 | 10 | 2520 |
3 | 超過144,000元至300,000元的部分 | 20 | 16920 |
4 | 超過300,000元至420,000元的部分 | 25 | 31920 |
5 | 超過420,000元至660,000元的部分 | 30 | 52920 |
6 | 超過660,000元至960,000元的部分 | 35 | 85920 |
7 | 超過960,000元的部分 | 45 | 181920 |
9.1.2.5 五險一金繳費比例
五險一金繳費比例:養老保險單位21%,個人8%;醫療保險單位9%,個人2%+3元;失業保險單位0.5%,個人0.5%;工傷保險和生育保險由單位繳納,勞動者不用繳;住房公積金根據企業的實際情況,選擇住房公積金繳費比例,下限為5%,最高不超12%。
9.1.3 實現步驟
本實戰案例我們基於Spring Boot整合Drools的方式來實現。
目錄結構
9.1.3.1 建立maven工程calculation並配置pom.xml檔案
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starters</artifactId>
<version>2.0.6.RELEASE</version>
</parent>
<groupId>cn.itcast</groupId>
<artifactId>calculation</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<!--drools規則引擎-->
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-core</artifactId>
<version>7.6.0.Final</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-compiler</artifactId>
<version>7.6.0.Final</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-templates</artifactId>
<version>7.6.0.Final</version>
</dependency>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-api</artifactId>
<version>7.6.0.Final</version>
</dependency>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-spring</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</exclusion>
</exclusions>
<version>7.6.0.Final</version>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
9.1.3.2 建立/resources/application.yml檔案
server:
port: 8080
spring:
application:
name: calculation
9.1.3.3 編寫配置類DroolsConfig
package com.guohaowei.drools.config;
import cn.hutool.core.date.DatePattern;
import org.kie.api.KieBase;
import org.kie.api.KieServices;
import org.kie.api.builder.KieBuilder;
import org.kie.api.builder.KieFileSystem;
import org.kie.api.builder.KieRepository;
import org.kie.api.runtime.KieContainer;
import org.kie.internal.io.ResourceFactory;
import org.kie.spring.KModuleBeanFactoryPostProcessor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import java.io.IOException;
/**
* 規則引擎配置類
*/
@Configuration
public class DroolsConfig {
//指定規則檔案存放的目錄
private static final String RULES_PATH = "rules/";
private final KieServices kieServices = KieServices.Factory.get();
@Bean
@ConditionalOnMissingBean
public KieFileSystem kieFileSystem() throws IOException {
System.setProperty("drools.dateformat", DatePattern.NORM_DATE_PATTERN);
KieFileSystem kieFileSystem = kieServices.newKieFileSystem();
ResourcePatternResolver resourcePatternResolver =
new PathMatchingResourcePatternResolver();
Resource[] files =
resourcePatternResolver.getResources("classpath*:" + RULES_PATH + "*.*");
String path;
for (Resource file : files) {
path = RULES_PATH + file.getFilename();
kieFileSystem.write(ResourceFactory.newClassPathResource(path, "UTF-8"));
}
return kieFileSystem;
}
@Bean
@ConditionalOnMissingBean
public KieContainer kieContainer() throws IOException {
KieRepository kieRepository = kieServices.getRepository();
kieRepository.addKieModule(kieRepository::getDefaultReleaseId);
KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem());
kieBuilder.buildAll();
return kieServices.newKieContainer(kieRepository.getDefaultReleaseId());
}
@Bean
@ConditionalOnMissingBean
public KieBase kieBase() throws IOException {
return kieContainer().getKieBase();
}
@Bean
@ConditionalOnMissingBean
public KModuleBeanFactoryPostProcessor kiePostProcessor() {
return new KModuleBeanFactoryPostProcessor();
}
}
9.1.3.4 編寫各種實體類
- 編寫實體類CalculationDto
package com.guohaowei.drools.domain.dto;
import com.guohaowei.drools.domain.vo.CalculationVo;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
/**
* @author guohaowei
*/
@Data
public class CalculationDto {
/**
* 12個月的工資繳稅額等資訊
*/
private List<CalculationVo> calculationVoList;
//--------------------------------重複欄位用於計算start--------------------------------------------------------
/**
* 稅前工資
*/
private BigDecimal wageBeforeTax;
/**
* 預扣預繳應納稅所得額
*/
private BigDecimal taxableIncome;
/**
* 累計預扣預繳應納稅所得額
*/
private BigDecimal taxableIncomeCount;
/**
* 月稅後工資
*/
private BigDecimal wageDeductedTax;
//--------------------------------重複欄位用於計算end----------------------------------------------------------
/**
* 個人所得稅起徵點
*/
private BigDecimal threshold;
/**
* 養老保險個人繳納比例,預設8%
*/
private BigDecimal oldAgeInsuranceRatio;
/**
* 失業保險個人繳納比例,預設0.5%
*/
private BigDecimal unemploymentInsuranceRatio;
/**
* 醫療保險個人繳納比例,預設2% + 3元
*/
private BigDecimal medicalInsuranceRatio = BigDecimal.valueOf(0.02);
/**
* 醫療保險個人繳納比例,預設2% + 3元
*/
private BigDecimal medicalInsuranceAdd = BigDecimal.valueOf(3);
/**
* 住房公積金個人繳納比例,預設12%
*/
private BigDecimal housingFundRatio;
/**
* 住房租金專項附加扣除,預設1500元
* 其他專項附加扣除同理,此處省略
*/
private BigDecimal specialAdditionalDeductionRent;
}
- 編寫實體類CalculationVo
package com.guohaowei.drools.domain.vo;
import lombok.Builder;
import lombok.Data;
import java.math.BigDecimal;
/**
* @author guohaowei
*/
@Data
@Builder
public class CalculationVo {
/**
* 月稅前工資
*/
private BigDecimal wageBeforeTax;
/**
* 月預扣預繳應納稅所得額
*/
private BigDecimal taxableIncome;
/**
* 月稅率
*/
private BigDecimal taxRate;
/**
* 月速算扣除數
*/
private BigDecimal quickDeduction;
/**
* 月扣稅額
*/
private BigDecimal taxRebate;
/**
* 月稅後工資
*/
private BigDecimal wageDeductedTax;
/**
* 月份
*/
private Integer month;
}
- 編寫列舉類WithholdingTaxRateEnum
package com.guohaowei.drools.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.math.BigDecimal;
/**
* @Author: 郭浩偉 qq:912161367
* @Date: 2022/11/17 0017 7:18
* @Description: 個人所得稅預扣率表(居民個人工資、薪金所得預扣預繳適用)
*/
@AllArgsConstructor
@Getter
public enum WithholdingTaxRateEnum {
/**
* 級數
*/
one(36_000, "0.03", 0),
two(144_000, "0.10", 2520),
three(300_000, "0.20", 16920),
four(420_000, "0.25", 31920),
five(660_000, "0.30", 52920),
six(960_000, "0.35", 85920),
seven(960_000, "0.45", 181920),
;
/**
* 全年累計預扣預繳應納稅所得額
*/
private Integer taxableIncome;
/**
* 稅率,如0.03
*/
private String taxRate;
/**
* 速算扣除數
*/
private Integer quickDeduction;
/**
* 根據【應納稅所得額】得到含對應的【稅率】和【速算扣除數】的列舉值
*
* @param actualTaxableIncome
* @return
*/
public static WithholdingTaxRateEnum getInfoByIncome(BigDecimal actualTaxableIncome) {
if(null == actualTaxableIncome){
return null;
}
for (WithholdingTaxRateEnum value : WithholdingTaxRateEnum.values()) {
if(actualTaxableIncome.compareTo(BigDecimal.valueOf(value.getTaxableIncome())) <= 0){
return value;
}
}
return seven;
}
}
- 編寫實體類CalculationReq
package com.guohaowei.drools.domain.req;
import lombok.Data;
import java.math.BigDecimal;
/**
* @Author: 郭浩偉 qq:912161367
* @Date: 2022/11/16 0016 23:31
* @Description:
*/
@Data
public class CalculationReq {
/**
* 稅前工資
*/
private BigDecimal wageBeforeTax;
/**
* 個人所得稅起徵點,預設5000元
*/
private BigDecimal threshold = BigDecimal.valueOf(5000);
/**
* 養老保險個人繳納比例,預設8%
*/
private BigDecimal oldAgeInsuranceRatio = BigDecimal.valueOf(0.08);
/**
* 失業保險個人繳納比例,預設0.5%
*/
private BigDecimal unemploymentInsuranceRatio = BigDecimal.valueOf(0.005);
/**
* 醫療保險個人繳納比例,預設2% + 3元
*/
private BigDecimal medicalInsuranceRatio = BigDecimal.valueOf(0.02);
/**
* 醫療保險個人繳納比例,預設2% + 3元
*/
private BigDecimal medicalInsuranceAdd = BigDecimal.valueOf(3);
/**
* 住房公積金個人繳納比例,預設12%
*/
private BigDecimal housingFundRatio = BigDecimal.valueOf(0.12);
/**
* 住房租金專項附加扣除,預設1500元
* 其他專項附加扣除同理,此處省略
*/
private BigDecimal specialAdditionalDeductionRent = BigDecimal.valueOf(1500);
}
9.1.3.5 在resources/rules下建立規則檔案calculation.drl檔案
//當前規則檔案用於計算個人所得稅
package calculation
import java.math.BigDecimal
import com.guohaowei.drools.domain.dto.CalculationDto
import com.guohaowei.drools.enums.WithholdingTaxRateEnum
import java.util.ArrayList
import com.guohaowei.drools.domain.vo.CalculationVo
import java.util.List
global com.guohaowei.drools.domain.dto.CalculationDto calculationGlobalDto
/**
當前規則檔案中的規則主要分為三類
1、計算應納稅所得額有1個規則
2、設定稅率、速算扣除數有7個規則
3、計算稅後工資有1個規則
**/
//計算應納稅所得額
rule "計算應納稅所得額-減去個稅起徵點"
salience 100
date-effective "2019-01-01"
no-loop true
when
then
//稅前工資
BigDecimal wageBeforeTax = calculationGlobalDto.getWageBeforeTax();
BigDecimal taxableIncome = wageBeforeTax.subtract(calculationGlobalDto.getThreshold());
calculationGlobalDto.setWageBeforeTax(wageBeforeTax);
calculationGlobalDto.setWageDeductedTax(wageBeforeTax);
calculationGlobalDto.setTaxableIncome(BigDecimal.ZERO);
if(taxableIncome.compareTo(BigDecimal.valueOf(0)) > 0){
calculationGlobalDto.setTaxableIncome(taxableIncome);
}
end
rule "計算應納稅所得額-減去養老保險個人繳納金額"
salience 99
date-effective "2019-01-01"
no-loop true
when
then
//養老保險個人繳納額
BigDecimal oldAgeInsurance = calculationGlobalDto.getWageBeforeTax().multiply(calculationGlobalDto.getOldAgeInsuranceRatio());
//預扣預繳應納稅所得額
BigDecimal taxableIncome = calculationGlobalDto.getTaxableIncome();
//應納稅所得額如果小於0即不需要繳稅也就不需要計算了
if(taxableIncome.compareTo(BigDecimal.valueOf(0)) > 0){
taxableIncome = taxableIncome.subtract(oldAgeInsurance);
calculationGlobalDto.setTaxableIncome(taxableIncome);
}
//稅後工資
calculationGlobalDto.setWageDeductedTax(calculationGlobalDto.getWageBeforeTax().subtract(oldAgeInsurance));
end
rule "計算應納稅所得額-減去失業保險個人繳納金額"
salience 98
date-effective "2019-01-01"
no-loop true
when
then
//失業保險個人繳納額
BigDecimal unemploymentInsurance = calculationGlobalDto.getWageBeforeTax().multiply(calculationGlobalDto.getUnemploymentInsuranceRatio());
//應納稅所得額如果小於0即不需要繳稅也就不需要計算了
if(calculationGlobalDto.getTaxableIncome().compareTo(BigDecimal.valueOf(0)) > 0){
BigDecimal taxableIncome = calculationGlobalDto.getTaxableIncome().subtract(unemploymentInsurance);
calculationGlobalDto.setTaxableIncome(taxableIncome);
}
//稅後工資
calculationGlobalDto.setWageDeductedTax(calculationGlobalDto.getWageDeductedTax().subtract(unemploymentInsurance));
end
rule "計算應納稅所得額-減去醫療保險個人繳納金額"
salience 97
date-effective "2019-01-01"
no-loop true
when
then
//醫療保險個人繳納額
BigDecimal medicalInsurance = calculationGlobalDto.getWageBeforeTax().multiply(calculationGlobalDto.getMedicalInsuranceRatio()).add(calculationGlobalDto.getMedicalInsuranceAdd());
//應納稅所得額如果小於0即不需要繳稅也就不需要計算了
if(calculationGlobalDto.getTaxableIncome().compareTo(BigDecimal.valueOf(0)) > 0){
BigDecimal taxableIncome = calculationGlobalDto.getTaxableIncome().subtract(medicalInsurance);
calculationGlobalDto.setTaxableIncome(taxableIncome);
}
//稅後工資
calculationGlobalDto.setWageDeductedTax(calculationGlobalDto.getWageDeductedTax().subtract(medicalInsurance));
end
rule "計算應納稅所得額-減去住房公積金個人繳納金額"
salience 96
date-effective "2019-01-01"
no-loop true
when
then
//住房公積金個人繳納額
BigDecimal housingFund = calculationGlobalDto.getWageBeforeTax().multiply(calculationGlobalDto.getHousingFundRatio());
//應納稅所得額如果小於0即不需要繳稅也就不需要計算了
if(calculationGlobalDto.getTaxableIncome().compareTo(BigDecimal.valueOf(0)) > 0){
BigDecimal taxableIncome = calculationGlobalDto.getTaxableIncome().subtract(housingFund);
calculationGlobalDto.setTaxableIncome(taxableIncome);
}
//稅後工資
calculationGlobalDto.setWageDeductedTax(calculationGlobalDto.getWageDeductedTax().subtract(housingFund));
end
rule "計算應納稅所得額-減去住房租金專項附加扣除金額"
salience 95
date-effective "2019-01-01"
no-loop true
when
then
//應納稅所得額如果小於0即不需要繳稅也就不需要計算了
if(calculationGlobalDto.getTaxableIncome().compareTo(BigDecimal.valueOf(0)) > 0){
BigDecimal taxableIncome = calculationGlobalDto.getTaxableIncome().subtract(calculationGlobalDto.getSpecialAdditionalDeductionRent());
calculationGlobalDto.setTaxableIncome(taxableIncome);
}
end
rule "計算出最終的統計結果"
salience 94
date-effective "2019-01-01"
no-loop true
when
then
List<CalculationVo> calculationVoList = new ArrayList<>();
BigDecimal taxRebateCount = BigDecimal.ZERO;
//當月扣稅額=應納稅所得額x月份數x稅率-速算扣除數-累計扣稅額
for(int i = 1; i <= 12 ; i++) {
//累計應納稅所得額
BigDecimal taxableIncomeCount = calculationGlobalDto.getTaxableIncome().multiply(BigDecimal.valueOf(i));
WithholdingTaxRateEnum withholdingTaxRateEnum = WithholdingTaxRateEnum.getInfoByIncome(taxableIncomeCount);
//月稅率
BigDecimal taxRate = new BigDecimal(withholdingTaxRateEnum.getTaxRate());
//月速算扣除數
BigDecimal quickDeduction = BigDecimal.valueOf(withholdingTaxRateEnum.getQuickDeduction());
//月扣稅額
BigDecimal taxRebate =taxableIncomeCount.multiply(taxRate).subtract(quickDeduction).subtract(taxRebateCount);
//累計扣稅額
taxRebateCount = taxRebateCount.add(taxRebate);
//組裝vo
CalculationVo calculationVo = CalculationVo.builder().wageBeforeTax(calculationGlobalDto.getWageBeforeTax()).taxableIncome(calculationGlobalDto.getTaxableIncome()).
taxRate(taxRate).quickDeduction(quickDeduction).taxRebate(taxRebate).wageDeductedTax(calculationGlobalDto.getWageDeductedTax().subtract(taxRebate)).month(i).build();
calculationVoList.add(calculationVo);
}
calculationGlobalDto.setCalculationVoList(calculationVoList);
end
9.1.3.6 建立RuleServiceImpl
package com.guohaowei.drools.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.guohaowei.drools.domain.dto.CalculationDto;
import com.guohaowei.drools.domain.req.CalculationReq;
import com.guohaowei.drools.domain.vo.CalculationVo;
import com.guohaowei.drools.service.RuleService;
import org.kie.api.KieBase;
import org.kie.api.runtime.KieSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @Author: 郭浩偉 qq:912161367
* @Date: 2022/11/18 0018 19:41
* @Description:
*/
@Service
public class RuleServiceImpl implements RuleService {
@Autowired
private KieBase kieBase;
/**
* 呼叫Drools規則引擎實現個人所得稅計算
*
* @param req
* @return
*/
@Override
public List<CalculationVo> calculate(CalculationReq req) {
KieSession session = kieBase.newKieSession();
CalculationDto calculationDto = BeanUtil.copyProperties(req, CalculationDto.class);
//初始化稅後工資=稅前工資
calculationDto.setWageDeductedTax(calculationDto.getWageBeforeTax());
//設定全域性變數,名稱和型別必須和規則檔案中定義的全域性變數名稱對應
session.setGlobal("calculationGlobalDto",calculationDto);
session.fireAllRules();
session.dispose();
return calculationDto.getCalculationVoList();
}
}
9.1.3.7 建立RuleController
package com.guohaowei.drools.controller;
import com.guohaowei.drools.domain.req.CalculationReq;
import com.guohaowei.drools.domain.vo.CalculationVo;
import com.guohaowei.drools.service.impl.RuleServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/rule")
public class RuleController {
@Autowired
private RuleServiceImpl ruleService;
@RequestMapping("/calculate")
public List<CalculationVo> calculate(@RequestBody CalculationReq req){
return ruleService.calculate(req);
}
}
9.1.3.8 建立啟動類DroolsApplication
package com.guohaowei.drools;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DroolsApplication {
public static void main(String[] args) {
SpringApplication.run(DroolsApplication.class);
}
}