• 第2-4-8章 规则引擎Drools实战(1)-个人所得税计算器


    9. Drools实战

    全套代码及资料全部完整提供,点此处下载

    9.1 个人所得税计算器

    本小节我们需要通过Drools规则引擎来根据规则计算个人所得税,最终数据效果如下:

    在这里插入图片描述

    9.1.1 名词解释

    税前月收入:即税前工资,指交纳个人所得税之前的总工资

    应纳税所得额:指按照税法规定确定纳税人在一定期间所获得的所有应税收入减除在该纳税期间依法允许减除的各种支出后的余额

    税率:是对征税对象的征收比例或征收额度

    速算扣除数:指为解决超额累进税率分级计算税额的复杂技术问题,而预先计算出的一个数据,可以简化计算过程

    扣税额:是指实际缴纳的税额

    税后工资:是指扣完税后实际到手的工资收入

    9.1.2 计算规则

    要实现个人所得税计算器,需要了解2019个税扣缴变化:

    应纳税所得额=(月收入-五险一金-起征点-依法确定的其他扣除-专项附加扣除)*适用税率

    9.1.2.1 新税制主要有哪些变化?
    1. 扣缴义务人在支付居民个人工资、薪金所得时,需按照“累计预扣法”核算预扣个人所得税。较之前按月的税率表变化成按年计税,适用个人所得税预扣率表
    2. 首次设立了子女教育、继续教育、大病医疗、住房贷款利息或住房租金、赡养老人六项专项附加扣除。(在税前工资扣除相应额度后核算个税)
    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月
    每月税前工资300003000030000300003000030000300003000030000300003000030000
    扣除各项减免额后应对税率3%10%10%10%10%10%10%20%20%20%20%20%
    月应预扣预缴个税5556251850185018501850185022503700370037003700

    上述计算结果表明,由于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月
    每月税前工资150001500015000150001500015000150001500015000150001500015000
    扣除各项减免额后应对税率3%3%3%3%3%3%3%10%10%10%10%10%
    月应预扣预缴个税153.66153.66153.66153.66153.66153.66153.66501.98512.2512.2512.2512.2

    上述计算结果表明,由于8月份累计预扣预缴应纳税所得额为40976元,已适用10%的税率,因此8月份应预扣预缴有所增高。

    9.1.2.3 关于年度汇算清缴

    一般来讲,有以下情形之一的,您可以选择在次年3月1日至6月30日内,自行向汇缴地主管税务机关办理汇算清缴申报时进行专项附加扣除,税款多退少补;(1)不愿意通过单位办理扣除,未将相关专项附加扣除信息报送给任职受雇单位的;

    (2)在同一纳税年度涉及有两处工资薪金所得或涉及劳务报酬所得的;

    (3)有大病医疗支出项目的;

    (4)纳税年度内未享受或未足额享受专项附加扣除等情形。

    9.1.2.4 个人所得税预扣率表(居民个人工资、薪金所得预扣预缴适用)
    级数全年累计预扣预缴应纳税所得额税率(%)速算扣除数
    1不超过36,000元的部分30
    2超过36,000元至144,000元的部分102520
    3超过144,000元至300,000元的部分2016920
    4超过300,000元至420,000元的部分2531920
    5超过420,000元至660,000元的部分3052920
    6超过660,000元至960,000元的部分3585920
    7超过960,000元的部分45181920
    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文件
    
    <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.0modelVersion>
        <parent>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-startersartifactId>
            <version>2.0.6.RELEASEversion>
        parent>
        <groupId>cn.itcastgroupId>
        <artifactId>calculationartifactId>
        <version>1.0-SNAPSHOTversion>
        <dependencies>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-webartifactId>
            dependency>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-aopartifactId>
            dependency>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-testartifactId>
            dependency>
            <dependency>
                <groupId>commons-langgroupId>
                <artifactId>commons-langartifactId>
                <version>2.6version>
            dependency>
            
            <dependency>
                <groupId>org.droolsgroupId>
                <artifactId>drools-coreartifactId>
                <version>7.6.0.Finalversion>
            dependency>
            <dependency>
                <groupId>org.droolsgroupId>
                <artifactId>drools-compilerartifactId>
                <version>7.6.0.Finalversion>
            dependency>
            <dependency>
                <groupId>org.droolsgroupId>
                <artifactId>drools-templatesartifactId>
                <version>7.6.0.Finalversion>
            dependency>
            <dependency>
                <groupId>org.kiegroupId>
                <artifactId>kie-apiartifactId>
                <version>7.6.0.Finalversion>
            dependency>
            <dependency>
                <groupId>org.kiegroupId>
                <artifactId>kie-springartifactId>
                <exclusions>
                    <exclusion>
                        <groupId>org.springframeworkgroupId>
                        <artifactId>spring-txartifactId>
                    exclusion>
                    <exclusion>
                        <groupId>org.springframeworkgroupId>
                        <artifactId>spring-beansartifactId>
                    exclusion>
                    <exclusion>
                        <groupId>org.springframeworkgroupId>
                        <artifactId>spring-coreartifactId>
                    exclusion>
                    <exclusion>
                        <groupId>org.springframeworkgroupId>
                        <artifactId>spring-contextartifactId>
                    exclusion>
                exclusions>
                <version>7.6.0.Finalversion>
            dependency>
        dependencies>
        <build>
            <finalName>${project.artifactId}finalName>
            <resources>
                <resource>
                    <directory>src/main/javadirectory>
                    <includes>
                        <include>**/*.xmlinclude>
                    includes>
                    <filtering>falsefiltering>
                resource>
                <resource>
                    <directory>src/main/resourcesdirectory>
                    <includes>
                        <include>**/*.*include>
                    includes>
                    <filtering>falsefiltering>
                resource>
            resources>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.pluginsgroupId>
                    <artifactId>maven-compiler-pluginartifactId>
                    <version>2.3.2version>
                    <configuration>
                        <source>1.8source>
                        <target>1.8target>
                    configuration>
                plugin>
            plugins>
        build>
    project>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    9.1.3.2 创建/resources/application.yml文件
    server:
      port: 8080
    spring:
      application:
        name: calculation
    
    • 1
    • 2
    • 3
    • 4
    • 5
    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();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    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;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 编写实体类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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 编写枚举类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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 编写实体类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);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    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
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    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();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    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);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    全套代码及资料全部完整提供,点此处下载

  • 相关阅读:
    第十一章第一节:JavaString类介绍和常用方法
    封装一个滑块控制灯光组件
    SpringBoot中公共字段的自动填充
    Date对象
    程序员积累的编程知识十年后有多少变得没用?
    【无标题】
    三菱PLC中通过变址寄存器V或Z实现简单跑马灯的程序示例及说明
    小程序之自定义组件 结合案例(会议OA的会议/投票管理及个人中心的搭建)详解 (4)
    为什么要使用消息队列?
    状态模式(state)
  • 原文地址:https://blog.csdn.net/weixin_42208775/article/details/128090740