CalculateTax.java 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. package com.kmall.admin.utils;
  2. import com.kmall.admin.dao.GoodsDao;
  3. import com.kmall.admin.dao.TaxErrorRecordDao;
  4. import com.kmall.admin.dto.GoodsDetailsDto;
  5. import com.kmall.admin.dto.GoodsDto;
  6. import com.kmall.admin.entity.GoodsEntity;
  7. import com.kmall.admin.entity.TaxErrorRecordEntity;
  8. import com.kmall.admin.service.GoodsService;
  9. import com.kmall.manager.manager.express.sf.ServiceException;
  10. import org.apache.commons.lang3.StringUtils;
  11. import org.slf4j.Logger;
  12. import org.slf4j.LoggerFactory;
  13. import java.math.BigDecimal;
  14. import java.math.RoundingMode;
  15. import java.util.*;
  16. import java.util.regex.Matcher;
  17. import java.util.regex.Pattern;
  18. /**
  19. * xwh
  20. * 2020年12月7日14:57:04
  21. */
  22. public class CalculateTax {
  23. private static final Logger logger = LoggerFactory.getLogger(CalculateTax.class);
  24. private static List<String> hsCodeList = new ArrayList<>();
  25. private static Map<String,BigDecimal> hsCodeMap = new HashMap<String,BigDecimal>();
  26. private static BigDecimal consumerGoodsRate = new BigDecimal("0.2306");
  27. private static BigDecimal noConsumerGoodsRate = new BigDecimal("0.091");
  28. /*
  29. cus_goods_code 计税方式
  30. 3304990029 完税价格≥10元/克的,税率为15%
  31. 3304910000 完税价格≥10元/克的,税率为15%
  32. 3303000010 完税价格≥10元/克的,税率为15%
  33. 3303000020 完税价格≥10元/毫升的,税率为15%
  34. 3304100091 完税价格≥10元/克的,税率为15%
  35. 3304100092 完税价格≥10元/毫升的,税率为15%
  36. 3304100093 完税价格≥15元/片(张)的,税率为15%
  37. 3304200091 完税价格≥10元/克的,税率为15%
  38. 3304200092 完税价格≥10元/毫升的,税率为15%
  39. 3304200093 完税价格≥15元/片(张)的,税率为15%
  40. 3304300001 完税价格≥10元/克的,税率为15%
  41. 3304300002 完税价格≥10元/毫升的,税率为15%
  42. 3304300003 完税价格≥15元/片(张)的,税率为15%
  43. 3304990049 完税价格≥15元/片(张)的,税率为15%
  44. 3304990099 完税价格≥10元/克的,税率为15%
  45. 3304990039 完税价格≥10元/克的,税率为15%
  46. 9102190000 进口关税完税价格在10000人民币及以上时,税率为20%
  47. */
  48. static {
  49. hsCodeMap.put("3304990029",new BigDecimal("10"));
  50. hsCodeMap.put("3304910090",new BigDecimal("10"));
  51. hsCodeMap.put("3303000010",new BigDecimal("10"));
  52. hsCodeMap.put("3303000020",new BigDecimal("10"));
  53. hsCodeMap.put("3304100091",new BigDecimal("10"));
  54. hsCodeMap.put("3304100092",new BigDecimal("10"));
  55. hsCodeMap.put("3304100093",new BigDecimal("15"));
  56. hsCodeMap.put("3304200091",new BigDecimal("10"));
  57. hsCodeMap.put("3304200092",new BigDecimal("10"));
  58. hsCodeMap.put("3304200093",new BigDecimal("15"));
  59. hsCodeMap.put("3304300001",new BigDecimal("10"));
  60. hsCodeMap.put("3304300002",new BigDecimal("10"));
  61. hsCodeMap.put("3304300003",new BigDecimal("15"));
  62. hsCodeMap.put("3304990049",new BigDecimal("15"));
  63. hsCodeMap.put("3304990099",new BigDecimal("10"));
  64. hsCodeMap.put("3304990039",new BigDecimal("10"));
  65. hsCodeMap.put("9102190000",new BigDecimal("10000"));
  66. }
  67. /**
  68. * 计算税费,包括消费税和增值税。公式如下:
  69. * 订单该商品总税费 = (商品消费税 + 商品增值税)* 0.7
  70. *
  71. * @param
  72. * @return
  73. */
  74. public static final BigDecimal calculateTax(GoodsEntity goodsEntity, BigDecimal actualPaymentAmount) {
  75. // 该商品消费税
  76. BigDecimal grandConsumerTax = BigDecimal.ZERO;
  77. // 该商品增值税
  78. BigDecimal grandValueAddTax = BigDecimal.ZERO;
  79. String hsCode = goodsEntity.getHsCode();// 海关商品编码
  80. String hsCodeName = goodsEntity.getHsCodeName();// 海关商品编码名称
  81. // 消费税税率
  82. final BigDecimal impConsumTaxRate = goodsEntity.getImpConsumTaxRate();
  83. // 增值税税率
  84. final BigDecimal valueAddedTaxRate = goodsEntity.getValueAddedTaxRate();
  85. try {
  86. // 计算某类产品的消费税
  87. final BigDecimal subConsumerTax = calculateConsumerTax(actualPaymentAmount, impConsumTaxRate, hsCodeName, goodsEntity, actualPaymentAmount);
  88. // 计算某类产品的增值税
  89. final BigDecimal subValueAddTax = calculateValueAddTax(actualPaymentAmount, valueAddedTaxRate, subConsumerTax);
  90. // 计算总的消费税
  91. grandConsumerTax = grandConsumerTax.add(subConsumerTax);
  92. // 计算总的增值税
  93. grandValueAddTax = grandValueAddTax.add(subValueAddTax);
  94. } catch (final RuntimeException e) {
  95. if (logger.isErrorEnabled()) logger.error("计算税费出错!请检查产品的分类数据,以及产品类别的税率数据!", e);
  96. throw new IllegalStateException("计算税费出错!请检查产品的分类数据,以及产品类别的税率数据!", e);
  97. }
  98. // 按照公式,打7折
  99. final BigDecimal tax = grandConsumerTax.add(grandValueAddTax).multiply(new BigDecimal("0.7")).setScale(2, BigDecimal.ROUND_HALF_UP);
  100. return tax;
  101. }
  102. /**
  103. * 计算消费税,公式如下:
  104. * 消费税 = (明细商品总价 / (1-消费税税率)) * 消费税税率
  105. *
  106. * @param productSubTotal 订单中的某类商品总价(单价 * 数量)
  107. * @param impConsumTaxRate 进口从价消费税率,百分数
  108. * @param goodsEntity 商品备案信息
  109. * @param productUnitPrice 产品销售单价
  110. * @return 消费税
  111. */
  112. private static final BigDecimal calculateConsumerTax(final BigDecimal productSubTotal, final BigDecimal impConsumTaxRate, final String cusGoodsName, GoodsEntity goodsEntity, final BigDecimal productUnitPrice) {
  113. // 消费税税率为0,不做计算
  114. if (impConsumTaxRate.compareTo(BigDecimal.ZERO) == 0) {
  115. return BigDecimal.ZERO;
  116. }
  117. BigDecimal taxRate = null;
  118. //计算化妆品, 需要计算单价
  119. BigDecimal unit1Price = null;
  120. BigDecimal unit2Price = null;
  121. BigDecimal qty1 = goodsEntity.getLegalUnit1Qty();
  122. BigDecimal qty2 = goodsEntity.getLegalUnit2Qty();
  123. if (StringUtils.isNotBlank(cusGoodsName) && (cusGoodsName.contains("“片”") || cusGoodsName.contains("“张”") || cusGoodsName.contains("\"片\"") || cusGoodsName.contains("\"张\""))) {
  124. //片、张的单价
  125. if (qty2 != null) {
  126. unit2Price = productUnitPrice.divide(qty2, 5, BigDecimal.ROUND_HALF_UP);
  127. }
  128. } else {
  129. //毫升、克的单价
  130. if (qty1 != null) {
  131. BigDecimal gramOrMl = qty1.multiply(new BigDecimal(1000));
  132. unit1Price = productUnitPrice.divide(gramOrMl, 5, BigDecimal.ROUND_HALF_UP);
  133. }
  134. }
  135. // 每单位价格
  136. // 如果有片、张的单价,按片、张;如果没有则按毫升、克
  137. BigDecimal gramOrMlUnitPrice = (unit2Price != null) ? unit2Price : unit1Price;
  138. //获取化妆品阀值
  139. BigDecimal cosmThresholdValue = goodsEntity.getCosmThresholdValue();
  140. if (cosmThresholdValue != null && cosmThresholdValue.compareTo(BigDecimal.ZERO) > 0) {
  141. if (gramOrMlUnitPrice != null) {
  142. //完税法定单位单价大于阈值采用第一档消费税率,否则消费税率为0
  143. if (gramOrMlUnitPrice.compareTo(cosmThresholdValue) >= 0) {
  144. taxRate = impConsumTaxRate.divide(new BigDecimal("100"), 10, BigDecimal.ROUND_HALF_UP);
  145. } else {
  146. taxRate = BigDecimal.ZERO;
  147. }
  148. } else {
  149. String message = "【" + goodsEntity.getSku() + "】缺少法定单价";
  150. logger.error(message);
  151. throw new ServiceException(message);
  152. }
  153. }
  154. // 每单位价格小于化妆品阀值,不做计算
  155. if (taxRate.compareTo(BigDecimal.ZERO) == 0) {
  156. return BigDecimal.ZERO;
  157. }
  158. final BigDecimal tax = productSubTotal.divide(BigDecimal.ONE.subtract(taxRate), 10, BigDecimal.ROUND_HALF_UP).multiply(taxRate);
  159. return tax;
  160. }
  161. /**
  162. * 计算增值税,公式如下:
  163. * 增值税 = (明细商品总价 + 消费税) * 增值税税率
  164. *
  165. * @param productSubTotal 订单中的某类商品总价(单价 * 数量)
  166. * @param valueAddedTaxRate 增值税率,百分数
  167. * @param consumerTax 订单中的某类商品总的消费税
  168. * @return 增值税
  169. */
  170. private static final BigDecimal calculateValueAddTax(final BigDecimal productSubTotal, final BigDecimal valueAddedTaxRate, final BigDecimal consumerTax) {
  171. final BigDecimal taxRate = valueAddedTaxRate.divide(new BigDecimal("100"), 10, BigDecimal.ROUND_HALF_UP);
  172. // 免税,不做计算
  173. if (taxRate.compareTo(BigDecimal.ZERO) == 0) {
  174. return BigDecimal.ZERO;
  175. }
  176. final BigDecimal tax = productSubTotal.add(consumerTax).multiply(taxRate);
  177. return tax;
  178. }
  179. /**
  180. * 计算税率
  181. *
  182. * @param
  183. * @return
  184. */
  185. public static final BigDecimal calculateGoodsRate(GoodsEntity goodsEntity) {
  186. // 消费税税率
  187. final BigDecimal impConsumTaxRate = goodsEntity.getImpConsumTaxRate();
  188. // 增值税税率
  189. final BigDecimal valueAddedTaxRate = goodsEntity.getValueAddedTaxRate();
  190. // 如果增值税为0,免税
  191. if (BigDecimal.ZERO.compareTo(valueAddedTaxRate)==0){
  192. return valueAddedTaxRate;
  193. }
  194. // 如果有消费税 23.06%=(0.13+0.15)/(1-0.15)*0.7
  195. if (BigDecimal.ZERO.compareTo(impConsumTaxRate)!=0){
  196. return valueAddedTaxRate.add(impConsumTaxRate).
  197. divide(new BigDecimal(100).subtract(impConsumTaxRate),10, BigDecimal.ROUND_HALF_UP)
  198. .multiply(new BigDecimal("0.7")).setScale(4, BigDecimal.ROUND_HALF_UP);
  199. }else {
  200. // 如果没有消费税 9.1%=0.13*0.7
  201. return valueAddedTaxRate.multiply(new BigDecimal("0.7")).divide(new BigDecimal("100"),4,BigDecimal.ROUND_HALF_UP);
  202. }
  203. }
  204. /**
  205. * * 1.2.1 特殊化妆品(如下列举的“特殊化妆品SKU”),计算“消费税成分单价”:
  206. * 1.2.1.1 如果是片、张,优先使用片、张成分单价,没有,则按毫升、克成分单价为算税依据,二者成分单价计算公式如下:
  207. * 如果特殊化妆品内规格为“片”或“张”的化妆品,用第二数量按片、张计算成分单价:成分单价=销售单价/产品备案数据第二数量;
  208. * 其它的化妆品,用第一数量按毫升、克计算成分单价,成分单价=销售单价/(产品备案数据第一数量*1000);
  209. * 1.2.2 根据海关产品分类中“消费税成分单价收税阀值”,如果“消费税成分单价”大于等于“消费税成分单价收税阀值”,按产品分类消费税率作为算税依据,小于,则消费税率为0;无“消费税成分单价收税阀值”的,按产品分类消费税率作为算税依据:
  210. * 1.2.3 总计订单全部明细商品消费税;
  211. * @return
  212. */
  213. public static BigDecimal calculateFinalTax(GoodsEntity goods, BigDecimal retailPrice, GoodsService goodsService){
  214. // 判断商品的海关商品编码是否是在特殊化妆品里面
  215. // 海关商品编码
  216. String hsCode = goods.getHsCode();
  217. // 商品税率
  218. BigDecimal goodsRate = goods.getGoodsRate();
  219. if(hsCodeMap.containsKey(hsCode)){
  220. String cusGoodsName = goods.getHsCodeName();
  221. // 法1
  222. BigDecimal qty1 = goods.getLegalUnit1Qty();
  223. // 法2
  224. BigDecimal qty2 = goods.getLegalUnit2Qty();
  225. // 阈值
  226. BigDecimal cosmThresholdValue = goods.getCosmThresholdValue();
  227. BigDecimal gramOrMlUnitPrice = getUnitPrice(retailPrice, goodsRate, cusGoodsName, qty1, qty2);
  228. //获取化妆品阀值
  229. if (cosmThresholdValue != null && cosmThresholdValue.compareTo(BigDecimal.ZERO) > 0) {
  230. if (gramOrMlUnitPrice != null) {
  231. //完税法定单位单价大于阈值采用第一档消费税率,否则消费税率为0
  232. if (gramOrMlUnitPrice.compareTo(cosmThresholdValue) >= 0) {
  233. // 征收消费税,此时判断税率是否为23.06,如果进入if,则是23.06 如果进了else 则当前税率为9.1
  234. if(goodsRate.compareTo(consumerGoodsRate) == 0){
  235. return getTax(retailPrice, goodsRate);
  236. }else{
  237. // 如果不是。重新用23.06计算一次
  238. gramOrMlUnitPrice = getUnitPrice(retailPrice, consumerGoodsRate, cusGoodsName, qty1, qty2);
  239. if (gramOrMlUnitPrice.compareTo(cosmThresholdValue) >= 0) {
  240. GoodsEntity updateGoods = new GoodsEntity();
  241. updateGoods.setGoodsRate(consumerGoodsRate);
  242. updateGoods.setId(goods.getId());
  243. goodsService.updateByEntity(updateGoods);
  244. // 如果用23.06去计算,还是大于阈值,那就是用23.06去计税
  245. return getTax(retailPrice, consumerGoodsRate);
  246. }else{
  247. BigDecimal tax = getFinalTax(goods, retailPrice, goodsService, cusGoodsName, qty1, qty2);
  248. return tax;
  249. }
  250. }
  251. } else {
  252. // 不征收消费税,判断税率是否是9.1 如果进了if,则是9.1 ,如果进了else,则当前税率是23.06
  253. if(goodsRate.compareTo(noConsumerGoodsRate) == 0){
  254. return getTax(retailPrice, goodsRate);
  255. }else{
  256. // 如果不是,重新用0.091计算一次
  257. gramOrMlUnitPrice = getUnitPrice(retailPrice, noConsumerGoodsRate, cusGoodsName, qty1, qty2);
  258. if (gramOrMlUnitPrice.compareTo(cosmThresholdValue) >= 0) {
  259. BigDecimal tax = getFinalTax(goods, retailPrice, goodsService, cusGoodsName, qty1, qty2);
  260. return tax;
  261. }else{
  262. GoodsEntity updateGoods = new GoodsEntity();
  263. updateGoods.setGoodsRate(noConsumerGoodsRate);
  264. updateGoods.setId(goods.getId());
  265. goodsService.updateByEntity(updateGoods);
  266. return getTax(retailPrice, noConsumerGoodsRate);
  267. }
  268. }
  269. }
  270. } else {
  271. String message = "【" + goods.getSku() + "】缺少法定单价";
  272. logger.error(message);
  273. // 没有法一法二,记录起来
  274. TaxErrorRecordEntity taxErrorRecordEntity = new TaxErrorRecordEntity();
  275. taxErrorRecordEntity.setSku(goods.getGoodsSn());
  276. taxErrorRecordEntity.setMoney(retailPrice.toString());
  277. taxErrorRecordEntity.setHandle("0");
  278. taxErrorRecordEntity.setGoodsRate(goodsRate.toString());
  279. taxErrorRecordEntity.setErrorTime(new Date());
  280. goodsService.insertTaxErrorRecord(taxErrorRecordEntity);
  281. // 一般走不到这里,但是为了做个保险,还是要这个
  282. return getTax(retailPrice, goodsRate);
  283. }
  284. }
  285. // 特殊化妆品但是规格型号符合不上,记录起来
  286. TaxErrorRecordEntity taxErrorRecordEntity = new TaxErrorRecordEntity();
  287. taxErrorRecordEntity.setSku(goods.getGoodsSn());
  288. taxErrorRecordEntity.setMoney(retailPrice.toString());
  289. taxErrorRecordEntity.setHandle("0");
  290. taxErrorRecordEntity.setGoodsRate(goodsRate.toString());
  291. taxErrorRecordEntity.setErrorTime(new Date());
  292. goodsService.insertTaxErrorRecord(taxErrorRecordEntity);
  293. // 一般走不到这里,但是为了做个保险,还是要这个
  294. return getTax(retailPrice, goodsRate);
  295. }else{
  296. // 该商品不是特殊海关商品编码,直接用里面的税率进行计算
  297. return getTax(retailPrice, goodsRate);
  298. }
  299. }
  300. private static BigDecimal getFinalTax(GoodsEntity goods, BigDecimal retailPrice, GoodsService goodsService, String cusGoodsName, BigDecimal qty1, BigDecimal qty2) {
  301. BigDecimal goodsRate;
  302. // 当使用0.091的时候,发现要收消费税。当使用0.2306的时候,不收消费税
  303. // 然后分别用两种计税方式去算税前价,然后算税费,最后去一个更接近税后价的来计算
  304. BigDecimal noConsumerTaxPrice = retailPrice.divide(BigDecimal.ONE.add(noConsumerGoodsRate),10,RoundingMode.HALF_UP).setScale(2,RoundingMode.HALF_UP);
  305. BigDecimal tax1 = noConsumerTaxPrice.multiply(consumerGoodsRate).setScale(10,RoundingMode.HALF_UP);
  306. BigDecimal finalPrice1 = tax1.add(noConsumerTaxPrice);
  307. // 394.78
  308. // 310.29
  309. BigDecimal consumerTaxPrice = retailPrice.divide(BigDecimal.ONE.add(consumerGoodsRate),10,RoundingMode.HALF_UP).setScale(2,RoundingMode.HALF_UP);
  310. BigDecimal tax2 = consumerTaxPrice.multiply(noConsumerGoodsRate).setScale(10,RoundingMode.HALF_UP);
  311. BigDecimal finalPrice2 = tax2.add(consumerTaxPrice);
  312. // 比较两个价格哪个比较接近税后价
  313. BigDecimal abs1 = finalPrice1.subtract(retailPrice).abs();
  314. BigDecimal abs2 = finalPrice2.subtract(retailPrice).abs();
  315. BigDecimal tax ;
  316. if(abs1.compareTo(abs2) > 0){
  317. goodsRate = consumerGoodsRate;
  318. tax = tax2;
  319. }else{
  320. goodsRate = noConsumerGoodsRate;
  321. tax = tax1;
  322. }
  323. // 如果用23.06去计算,小于阈值,则证明当前商品的价格有问题,需要记录起来 当前商品的sku,当前商品税率,异常时间
  324. // 存储异常sku , 并修改税率
  325. TaxErrorRecordEntity taxErrorRecordEntity = new TaxErrorRecordEntity();
  326. taxErrorRecordEntity.setSku(goods.getGoodsSn());
  327. taxErrorRecordEntity.setMoney(retailPrice.toString());
  328. taxErrorRecordEntity.setHandle("0");
  329. taxErrorRecordEntity.setGoodsRate(goodsRate.toString());
  330. taxErrorRecordEntity.setErrorTime(new Date());
  331. goodsService.insertTaxErrorRecord(taxErrorRecordEntity);
  332. GoodsEntity updateGoods = new GoodsEntity();
  333. updateGoods.setGoodsRate(goodsRate);
  334. updateGoods.setId(goods.getId());
  335. goodsService.updateByEntity(updateGoods);
  336. return tax;
  337. }
  338. /**
  339. * 根据税率、税后价,计算出税
  340. * @param retailPrice 税后价
  341. * @param goodsRate 税率
  342. * @return
  343. */
  344. private static BigDecimal getTax(BigDecimal retailPrice, BigDecimal goodsRate) {
  345. return goodsRate.multiply(retailPrice
  346. .divide(new BigDecimal(1).add(goodsRate), 10, RoundingMode.HALF_DOWN))
  347. .setScale(10, RoundingMode.HALF_DOWN);
  348. }
  349. /**
  350. * 计算出特殊化妆品的单价
  351. * @param retailPrice 税后价
  352. * @param goodsRate 税率
  353. * @param cusGoodsName 规格名称
  354. * @param qty1 法一
  355. * @param qty2 法二
  356. * @return
  357. */
  358. private static BigDecimal getUnitPrice(BigDecimal retailPrice, BigDecimal goodsRate, String cusGoodsName, BigDecimal qty1, BigDecimal qty2) {
  359. BigDecimal unit1Price = null;
  360. BigDecimal unit2Price = null;
  361. // 根据税后价计算出税前价
  362. BigDecimal productUnitPrice = retailPrice
  363. .divide(new BigDecimal(1).add(goodsRate),10, RoundingMode.HALF_DOWN) ; // TODO 需要得到公式计算出税前价
  364. if (StringUtils.isNotBlank(cusGoodsName) && (cusGoodsName.contains("“片”") || cusGoodsName.contains("“张”") || cusGoodsName.contains("片") || cusGoodsName.contains("张"))) {
  365. //片、张的单价
  366. if (qty2 != null) {
  367. // 计算税额是否达到阈值
  368. unit2Price = productUnitPrice.divide(qty2, 5, BigDecimal.ROUND_HALF_UP);
  369. }
  370. } else {
  371. //毫升、克的单价
  372. if (qty1 != null) {
  373. BigDecimal gramOrMl = qty1.multiply(new BigDecimal(1000));
  374. unit1Price = productUnitPrice.divide(gramOrMl, 5, BigDecimal.ROUND_HALF_UP);
  375. }
  376. }
  377. // 每单位价格
  378. // 如果有片、张的单价,按片、张;如果没有则按毫升、克
  379. BigDecimal gramOrMlUnitPrice = (unit2Price != null) ? unit2Price : unit1Price;
  380. return gramOrMlUnitPrice;
  381. }
  382. }