diff --git a/bms-back/bms-admin/src/main/java/com/bms/web/controller/data/PayPostcodeController.java b/bms-back/bms-admin/src/main/java/com/bms/web/controller/data/PayPostcodeController.java index 5954a667..fe81bfad 100644 --- a/bms-back/bms-admin/src/main/java/com/bms/web/controller/data/PayPostcodeController.java +++ b/bms-back/bms-admin/src/main/java/com/bms/web/controller/data/PayPostcodeController.java @@ -19,7 +19,7 @@ import com.bms.common.core.controller.BaseController; import com.bms.common.core.domain.AjaxResult; import com.bms.common.core.page.TableDataInfo; import com.bms.common.enums.BusinessType; -import com.bms.common.exception.ServiceException; +import com.bms.common.enums.PaymentMesEnum; import com.bms.price.domain.PayPostcodeGroup; import com.bms.price.domain.PayPostcodeLine; import com.bms.price.service.IPayPostcodeService; @@ -74,12 +74,12 @@ public class PayPostcodeController extends BaseController { if (file == null || file.isEmpty()) { - throw new ServiceException("请选择 Excel 文件"); + throw PaymentMesEnum.BMS_PAY_PICK_XLS.exception(); } String name = file.getOriginalFilename() == null ? "" : file.getOriginalFilename().toLowerCase(Locale.ROOT); if (!name.endsWith(".xlsx")) { - throw new ServiceException("只支持 xlsx 文件"); + throw PaymentMesEnum.BMS_PAY_XLSX_ONLY.exception(); } return success(payPostcodeService.importExcel(file.getBytes(), file.getOriginalFilename(), getUsername())); } diff --git a/bms-back/bms-admin/src/main/java/com/bms/web/controller/price/AirFeeRoleController.java b/bms-back/bms-admin/src/main/java/com/bms/web/controller/price/AirFeeRoleController.java new file mode 100644 index 00000000..d3c39312 --- /dev/null +++ b/bms-back/bms-admin/src/main/java/com/bms/web/controller/price/AirFeeRoleController.java @@ -0,0 +1,126 @@ +package com.bms.web.controller.price; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import com.bms.common.annotation.Log; +import com.bms.common.core.controller.BaseController; +import com.bms.common.core.domain.AjaxResult; +import com.bms.common.enums.BusinessType; +import com.bms.common.utils.poi.ExcelUtil; +import com.bms.price.airbill.domain.SrvBillAirFeeImportRow; +import com.bms.price.airbill.domain.SrvBillAirRoleFeeCommand; +import com.bms.price.airbill.domain.SrvBillAirRoleFeeQuery; +import com.bms.price.domain.vo.SrvBillAirQuery; +import com.bms.price.service.ISrvBillAirFeeService; +import com.bms.price.service.ISrvBillAirService; + +/** + * 空运、报关、清关三个角色往提单上添加费用。 + */ +@RestController +@RequestMapping("/price/airFee") +public class AirFeeRoleController extends BaseController +{ + private static final int MAX_BILLS = 50; + + @Autowired + private ISrvBillAirFeeService srvBillAirFeeService; + + @Autowired + private ISrvBillAirService srvBillAirService; + + /** 按提单号查出可绑定的提单,支持一次多选。 */ + @PreAuthorize("@ss.hasAnyPermi('price:airFee:air,price:airFee:declare,price:airFee:clear')") + @GetMapping("/bills") + public AjaxResult bills(@RequestParam(value = "billCodes", required = false) String billCodes) + { + List codes = splitCodes(billCodes); + if (codes.isEmpty()) + { + return success(List.of()); + } + if (codes.size() > MAX_BILLS) + { + return error("单次最多选择" + MAX_BILLS + "个提单"); + } + SrvBillAirQuery query = new SrvBillAirQuery(); + query.setBillCodes(codes); + return success(srvBillAirService.selectSrvBillAirList(query)); + } + + /** 查询已绑定到这些提单、且属于当前入口的费用。 */ + @PreAuthorize("@ss.hasAnyPermi('price:airFee:air,price:airFee:declare,price:airFee:clear')") + @GetMapping("/list") + public AjaxResult list(@RequestParam("scene") String scene, @RequestParam(value = "billCodes", required = false) String billCodes) + { + SrvBillAirRoleFeeQuery query = new SrvBillAirRoleFeeQuery(); + query.setScene(scene); + query.setBillCodes(splitCodes(billCodes)); + return success(srvBillAirFeeService.listRoleFees(query)); + } + + /** 保存当前入口的费用。空运写入 b 段,报关和清关写入 c 段。 */ + @PreAuthorize("@ss.hasAnyPermi('price:airFee:air,price:airFee:declare,price:airFee:clear')") + @Log(title = "提单角色费用", businessType = BusinessType.UPDATE) + @PostMapping("/save") + public AjaxResult save(@RequestBody SrvBillAirRoleFeeCommand command) + { + return success(srvBillAirFeeService.saveRole(command)); + } + + /** 下载导入模板,模板中带一行示例。 */ + @PreAuthorize("@ss.hasAnyPermi('price:airFee:air,price:airFee:declare,price:airFee:clear')") + @PostMapping("/importTemplate") + public void importTemplate(HttpServletResponse response) + { + SrvBillAirFeeImportRow example = new SrvBillAirFeeImportRow(); + example.setFeeName("燃油附加费"); + example.setPrice(new BigDecimal("1000")); + example.setCurrency("EUR"); + example.setRemark("导入时需清空这行示例数据"); + List rows = new ArrayList<>(); + rows.add(example); + new ExcelUtil<>(SrvBillAirFeeImportRow.class).exportExcel(response, rows, "提单费用导入模板"); + } + + /** 解析导入文件,不直接落库。示例行会被跳过。 */ + @PreAuthorize("@ss.hasAnyPermi('price:airFee:air,price:airFee:declare,price:airFee:clear')") + @Log(title = "提单角色费用导入", businessType = BusinessType.IMPORT) + @PostMapping("/import") + public AjaxResult importFee(MultipartFile file, @RequestParam("scene") String scene) throws Exception + { + if (file == null || file.isEmpty()) + { + return error("请选择导入文件"); + } + return success(srvBillAirFeeService.parseImport(scene, file.getInputStream())); + } + + private static List splitCodes(String billCodes) + { + LinkedHashSet codes = new LinkedHashSet<>(); + if (billCodes != null) + { + for (String part : billCodes.split("[,,\\s]+")) + { + if (part != null && !part.isBlank()) + { + codes.add(part.trim()); + } + } + } + return new ArrayList<>(codes); + } +} diff --git a/bms-back/bms-admin/src/main/java/com/bms/web/controller/price/PayProductController.java b/bms-back/bms-admin/src/main/java/com/bms/web/controller/price/PayProductController.java index 8805dafc..93b78133 100644 --- a/bms-back/bms-admin/src/main/java/com/bms/web/controller/price/PayProductController.java +++ b/bms-back/bms-admin/src/main/java/com/bms/web/controller/price/PayProductController.java @@ -22,7 +22,7 @@ import com.bms.common.core.controller.BaseController; import com.bms.common.core.domain.AjaxResult; import com.bms.common.core.page.TableDataInfo; import com.bms.common.enums.BusinessType; -import com.bms.common.exception.ServiceException; +import com.bms.common.enums.PaymentMesEnum; import com.bms.common.utils.file.FileUtils; import com.bms.common.utils.poi.ExcelUtil; import com.bms.price.domain.ChannelProductMap; @@ -86,7 +86,8 @@ public class PayProductController extends BaseController @PostMapping("/order/{billId}") public AjaxResult generateOrderCost(@PathVariable String billId) { - return success(payOrderCostService.generate(billId)); + payOrderCostService.generate(billId); + return success("操作成功"); } /** 下载某一版导入时上传的源文件 */ @@ -260,16 +261,16 @@ public class PayProductController extends BaseController { if (file == null || file.isEmpty()) { - throw new ServiceException("请选择价卡文件"); + throw PaymentMesEnum.BMS_PAY_PICK_FILE.exception(); } String filename = file.getOriginalFilename(); if (filename == null || !filename.toLowerCase(Locale.ROOT).endsWith(".xlsx")) { - throw new ServiceException("请上传xlsx价卡"); + throw PaymentMesEnum.BMS_PAY_XLSX.exception(); } if (file.getSize() > 30L * 1024 * 1024) { - throw new ServiceException("价卡文件不能超过30MB"); + throw PaymentMesEnum.BMS_PAY_FILE_30M.exception(); } } diff --git a/bms-back/bms-common/src/main/java/com/bms/common/enums/PaymentMesEnum.java b/bms-back/bms-common/src/main/java/com/bms/common/enums/PaymentMesEnum.java new file mode 100644 index 00000000..115be0eb --- /dev/null +++ b/bms-back/bms-common/src/main/java/com/bms/common/enums/PaymentMesEnum.java @@ -0,0 +1,194 @@ +package com.bms.common.enums; + +import java.util.HashMap; +import java.util.Map; +import com.bms.common.exception.ServiceException; + +/** + * 应付报错。只收没有拼接变量的固定提示。 + * code 以 BMS_PAY 开头,name 是这条提示用在哪一步,cnMes 是弹出的中文,enMes 先留空。 + */ +public enum PaymentMesEnum +{ + BMS_PAY_PICK_ORDER("BMS_PAY_PICK_ORDER", "生成应付校验订单", "请选择订单"), + BMS_PAY_ORDER_MISS("BMS_PAY_ORDER_MISS", "生成应付校验订单", "订单不存在"), + BMS_PAY_NO_COST("BMS_PAY_NO_COST", "生成应付计算结果", "没有算出应付费用"), + BMS_PAY_NO_ZONE("BMS_PAY_NO_ZONE", "生成应付匹配邮编", "邮编没有对上价卡地区"), + BMS_PAY_NO_CHANNEL("BMS_PAY_NO_CHANNEL", "生成应付匹配渠道", "订单没有渠道服务商"), + BMS_PAY_CH_SYNC("BMS_PAY_CH_SYNC", "生成应付匹配渠道", "订单没有渠道服务商,或渠道尚未同步"), + BMS_PAY_NO_PRODUCT("BMS_PAY_NO_PRODUCT", "应付计费匹配产品", "没有应付产品"), + BMS_PAY_NO_WEIGHT("BMS_PAY_NO_WEIGHT", "订单判断重量", "订单没有计费重"), + + BMS_PAY_PICK_PROD("BMS_PAY_PICK_PROD", "应付产品保存", "请先勾选要保存的产品"), + BMS_PAY_PROD_LIMIT("BMS_PAY_PROD_LIMIT", "应付产品保存", "一次最多保存80个产品"), + BMS_PAY_PROD_FIELD("BMS_PAY_PROD_FIELD", "应付产品保存", "产品名称、工作表和文件摘要不能为空"), + BMS_PAY_PICK_EDIT("BMS_PAY_PICK_EDIT", "应付产品修改", "请选择要修改的产品"), + BMS_PAY_PROD_MISS("BMS_PAY_PROD_MISS", "应付产品修改", "产品不存在或已删除"), + BMS_PAY_TPL_ONLY("BMS_PAY_TPL_ONLY", "应付产品修改", "渠道价格请在渠道页面保存,这里只改公共模板"), + BMS_PAY_SAVE_CH("BMS_PAY_SAVE_CH", "渠道应付价格", "请先保存渠道,再维护价格"), + BMS_PAY_NO_PRICE("BMS_PAY_NO_PRICE", "渠道应付价格", "没有可保存的价格"), + BMS_PAY_REIMPORT("BMS_PAY_REIMPORT", "应付价卡导入", "重新导入时请使用单个产品文件,或先选定渠道上的应付产品"), + BMS_PAY_PICK_IMP("BMS_PAY_PICK_IMP", "应付价卡导入", "请先选择一个已导入的应付产品"), + BMS_PAY_DICT_EMPTY("BMS_PAY_DICT_EMPTY", "应付产品字典", "应付产品字典还没有数据"), + BMS_PAY_TERM_START("BMS_PAY_TERM_START", "应付有效期", "请填写生效开始"), + BMS_PAY_TERM_END("BMS_PAY_TERM_END", "应付有效期", "生效结束不能早于生效开始"), + BMS_PAY_PICK_VER("BMS_PAY_PICK_VER", "应付有效期", "请选择要修改的版本"), + BMS_PAY_TERM_MISS("BMS_PAY_TERM_MISS", "应付有效期", "有效期不存在"), + BMS_PAY_TERM_STAT("BMS_PAY_TERM_STAT", "应付有效期", "状态只能是待生效、生效或已失效"), + BMS_PAY_FILE_DOWN("BMS_PAY_FILE_DOWN", "应付价卡源文件", "源文件下载失败"), + BMS_PAY_NO_SOURCE("BMS_PAY_NO_SOURCE", "应付价卡源文件", "这一版没有可下载的源文件"), + BMS_PAY_FILE_UP("BMS_PAY_FILE_UP", "应付价卡源文件", "价卡文件上传失败"), + BMS_PAY_FILE_WAIT("BMS_PAY_FILE_WAIT", "应付价卡源文件", "源文件还没有上传成功,请重新选择价卡后再保存"), + BMS_PAY_CH_FIRST("BMS_PAY_CH_FIRST", "渠道绑定产品", "请先保存渠道,再选择产品"), + BMS_PAY_CH_MISS("BMS_PAY_CH_MISS", "渠道绑定产品", "渠道不存在或渠道编码为空"), + BMS_PAY_PICK_CH("BMS_PAY_PICK_CH", "渠道绑定产品", "请先勾选渠道"), + BMS_PAY_PICK_SIDE("BMS_PAY_PICK_SIDE", "渠道绑定产品", "请至少选择应付或应收产品"), + BMS_PAY_BIND_NONE("BMS_PAY_BIND_NONE", "渠道绑定产品", "没有可绑定的渠道"), + BMS_PAY_CH_PROD("BMS_PAY_CH_PROD", "渠道应付产品", "渠道还没有选择应付产品"), + BMS_PAY_OUT_TERM("BMS_PAY_OUT_TERM", "应付价格有效期", "订单时间没有落在价格有效期内"), + BMS_PAY_TWO_VER("BMS_PAY_TWO_VER", "应付价格对比", "请选择两个价格版本"), + BMS_PAY_AR_AP("BMS_PAY_AR_AP", "收付类型", "收付类型只能是应收AR或应付AP"), + BMS_PAY_AP_MISS("BMS_PAY_AP_MISS", "应付产品查询", "应付产品不存在或已删除"), + + BMS_PAY_PICK_FILE("BMS_PAY_PICK_FILE", "应付价卡文件", "请选择价卡文件"), + BMS_PAY_XLSX("BMS_PAY_XLSX", "应付价卡文件", "请上传xlsx价卡"), + BMS_PAY_FILE_30M("BMS_PAY_FILE_30M", "应付价卡文件", "价卡文件不能超过30MB"), + BMS_PAY_NO_SHEET("BMS_PAY_NO_SHEET", "应付价卡解析", "文件里没有工作表"), + BMS_PAY_NO_QUOTE("BMS_PAY_NO_QUOTE", "应付价卡解析", "目录里没有找到产品报价表"), + BMS_PAY_BAD_XLS("BMS_PAY_BAD_XLS", "应付价卡解析", "价卡无法读取,请确认是 Excel 报价文件"), + + BMS_PAY_PICK_XLS("BMS_PAY_PICK_XLS", "应付邮编导入", "请选择 Excel 文件"), + BMS_PAY_XLS_30M("BMS_PAY_XLS_30M", "应付邮编导入", "文件不能超过30MB"), + BMS_PAY_ZIP_READ("BMS_PAY_ZIP_READ", "应付邮编导入", "邮编表无法读取,请确认是 Excel 文件"), + BMS_PAY_XLSX_ONLY("BMS_PAY_XLSX_ONLY", "应付邮编导入", "只支持 xlsx 文件"), + BMS_PAY_ZIP_EDIT("BMS_PAY_ZIP_EDIT", "应付邮编配置", "请选择要修改的邮编配置"), + BMS_PAY_ZIP_MISS("BMS_PAY_ZIP_MISS", "应付邮编配置", "邮编配置不存在或已删除"), + BMS_PAY_ZIP_TYPE("BMS_PAY_ZIP_TYPE", "应付邮编配置", "类型只能是附加费或邮编分区"), + BMS_PAY_ZIP_HEAD("BMS_PAY_ZIP_HEAD", "应付邮编导入", "没有识别到邮编或附加费邮编,请确认表头里有邮编、ZIP 或附加费"), + BMS_PAY_ZIP_ATTR("BMS_PAY_ZIP_ATTR", "应付邮编配置", "邮编属性只能是可达、不可达或偏远"), + + BMS_PAY_BILL_MISS("BMS_PAY_BILL_MISS", "应付账单查询", "应付账单不存在"), + BMS_PAY_PICK_SUP("BMS_PAY_PICK_SUP", "应付账单生成", "请选择供应商"), + BMS_PAY_SUP_MISS("BMS_PAY_SUP_MISS", "应付账单生成", "供应商不存在"), + BMS_PAY_NO_GEN("BMS_PAY_NO_GEN", "应付账单生成", "没有可生成的订单"), + BMS_PAY_BILL_FEE("BMS_PAY_BILL_FEE", "应付账单生成", "订单没有应付费用"), + BMS_PAY_CURRENCY("BMS_PAY_CURRENCY", "应付账单生成", "所选订单币种不一致,请按币种分开生成"), + BMS_PAY_DRAFT("BMS_PAY_DRAFT", "应付账单确认", "只有草稿可以确认"), + BMS_PAY_CONFIRM("BMS_PAY_CONFIRM", "应付账单付款", "请先确认账单,再登记付款"), + BMS_PAY_PICK_BILL("BMS_PAY_PICK_BILL", "应付账单作废", "请选择应付账单"), + BMS_PAY_VOID_WHY("BMS_PAY_VOID_WHY", "应付账单作废", "请填写作废原因"), + BMS_PAY_VOIDED("BMS_PAY_VOIDED", "应付账单作废", "账单已经作废"), + BMS_PAY_PAID("BMS_PAY_PAID", "应付账单作废", "已付款的账单不能直接作废,请先冲回付款"), + BMS_PAY_ORDER_NO("BMS_PAY_ORDER_NO", "应付账单查询", "请填写订单号"), + BMS_PAY_BIZ_DATE("BMS_PAY_BIZ_DATE", "应付账单查询", "请选择计费日起止"), + BMS_PAY_LADING_NO("BMS_PAY_LADING_NO", "应付账单查询", "请填写提单号"), + BMS_PAY_QUERY("BMS_PAY_QUERY", "应付账单查询", "请选择查询方式"), + + BMS_PAY_NO_ALLOC("BMS_PAY_NO_ALLOC", "提单费用分摊", "所选提单没有需要分摊的费用"), + BMS_PAY_ALLOC_ST("BMS_PAY_ALLOC_ST", "提单费用分摊", "费用状态已变化,请重新打开后再分摊"), + BMS_PAY_PICK_LAD("BMS_PAY_PICK_LAD", "提单费用分摊", "请勾选提单"), + BMS_PAY_LAD_LIMIT("BMS_PAY_LAD_LIMIT", "提单费用分摊", "单次最多处理5条提单"), + BMS_PAY_NO_CHANGE("BMS_PAY_NO_CHANGE", "提单费用保存", "没有需要保存的变更"), + BMS_PAY_IMP_EMPTY("BMS_PAY_IMP_EMPTY", "提单费用导入", "导入文件没有费用数据"), + BMS_PAY_IMP_DEMO("BMS_PAY_IMP_DEMO", "提单费用导入", "请删除示例行后再导入有效费用"), + BMS_PAY_FEE_BODY("BMS_PAY_FEE_BODY", "提单费用保存", "请提交要保存的费用"), + BMS_PAY_DEL_ID("BMS_PAY_DEL_ID", "提单费用删除", "要删除的费用缺少编号"), + BMS_PAY_FEE_DUP("BMS_PAY_FEE_DUP", "提单费用保存", "同一条费用不能重复提交"), + BMS_PAY_FEE_GONE("BMS_PAY_FEE_GONE", "提单费用保存", "费用已删除或不属于该提单"), + BMS_PAY_FEE_LOCK("BMS_PAY_FEE_LOCK", "提单费用删除", "已分摊或已确定的费用不能删除,请使用作废"), + BMS_PAY_EDIT_ID("BMS_PAY_EDIT_ID", "提单费用修改", "要修改的费用缺少编号"), + BMS_PAY_FEE_EDIT("BMS_PAY_FEE_EDIT", "提单费用修改", "已分摊或已确定的费用不能修改"), + BMS_PAY_PICK_OK("BMS_PAY_PICK_OK", "提单费用确定", "请选择要确定的费用"), + BMS_PAY_OK_STAT("BMS_PAY_OK_STAT", "提单费用确定", "只有已分摊的费用可以确定"), + BMS_PAY_OK_CHG("BMS_PAY_OK_CHG", "提单费用确定", "费用状态已变化,请刷新后再确定"), + BMS_PAY_PICK_VOID("BMS_PAY_PICK_VOID", "提单费用作废", "请选择要作废的费用"), + BMS_PAY_VOID_STAT("BMS_PAY_VOID_STAT", "提单费用作废", "只有已分摊或已确定的费用可以作废"), + BMS_PAY_VOID_CHG("BMS_PAY_VOID_CHG", "提单费用作废", "费用状态已变化,请刷新后再作废"), + BMS_PAY_NO_PERM("BMS_PAY_NO_PERM", "提单费用权限", "没有权限修改该费用"), + BMS_PAY_FEE_ENTRY("BMS_PAY_FEE_ENTRY", "提单费用保存", "费用入口不正确"), + BMS_PAY_FORBIDDEN("BMS_PAY_FORBIDDEN", "提单费用权限", "没有权限"), + BMS_PAY_PICK_ITEM("BMS_PAY_PICK_ITEM", "提单费用校验", "请选择费用项"), + BMS_PAY_ITEM_MISS("BMS_PAY_ITEM_MISS", "提单费用校验", "费用项不存在"), + BMS_PAY_LEVEL("BMS_PAY_LEVEL", "提单费用校验", "费用阶段不在字典中"), + BMS_PAY_PRICE("BMS_PAY_PRICE", "提单费用校验价格", "请填写价格"), + BMS_PAY_PRICE_NEG("BMS_PAY_PRICE_NEG", "提单费用校验价格", "价格不能小于0"), + BMS_PAY_PRICE_LEN("BMS_PAY_PRICE_LEN", "提单费用校验价格", "价格整数部分不能超过16位"), + BMS_PAY_CUR_PICK("BMS_PAY_CUR_PICK", "提单费用校验币种", "请选择收费币种"), + BMS_PAY_CUR_DICT("BMS_PAY_CUR_DICT", "提单费用校验币种", "收费币种不在币种字典中"), + BMS_PAY_REMARK("BMS_PAY_REMARK", "提单费用校验备注", "备注不能超过500字"), + BMS_PAY_LAD_BLANK("BMS_PAY_LAD_BLANK", "提单费用校验提单", "提单号不能为空"), + BMS_PAY_LAD_MISS("BMS_PAY_LAD_MISS", "提单费用校验提单", "提单不存在"); + + private static final Map BY_CODE = new HashMap<>(); + + static + { + for (PaymentMesEnum item : values()) + { + BY_CODE.put(item.code, item); + } + } + + private final String code; + + private final String name; + + private final String cnMes; + + private final String enMes; + + PaymentMesEnum(String code, String name, String cnMes) + { + this(code, name, cnMes, ""); + } + + PaymentMesEnum(String code, String name, String cnMes, String enMes) + { + this.code = code; + this.name = name; + this.cnMes = cnMes; + this.enMes = enMes == null ? "" : enMes; + } + + public String getCode() + { + return code; + } + + public String getName() + { + return name; + } + + public String getCnMes() + { + return cnMes; + } + + public String getEnMes() + { + return enMes; + } + + /** 按编码取出提示。没有对应编码时返回 null。 */ + public static PaymentMesEnum of(String code) + { + if (code == null || code.isBlank()) + { + return null; + } + return BY_CODE.get(code.trim()); + } + + /** 弹出给用户的中文提示。英文未配置时仍返回中文。 */ + public String text() + { + return cnMes == null || cnMes.isBlank() ? enMes : cnMes; + } + + /** 转成业务异常,页面只看到中文提示。 */ + public ServiceException exception() + { + return new ServiceException(text()).setDetailMessage(code); + } +} diff --git a/bms-back/bms-system/src/main/java/com/bms/price/airbill/domain/SrvBillAirAllocFee.java b/bms-back/bms-system/src/main/java/com/bms/price/airbill/domain/SrvBillAirAllocFee.java index f367124d..87370984 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/airbill/domain/SrvBillAirAllocFee.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/airbill/domain/SrvBillAirAllocFee.java @@ -1,7 +1,6 @@ package com.bms.price.airbill.domain; import java.math.BigDecimal; -import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Data; /** 待分摊的一条提单费用,供费用处理弹框展示。 */ @@ -30,14 +29,6 @@ public class SrvBillAirAllocFee /** 分摊维度:1订单 2重量 */ private String allocDim; - /** 提单实际重量。只在按重量分摊时按提单号填入,不返回页面 */ - @JsonIgnore - private BigDecimal actualWeight; - - /** 干线打板重量。只在按重量分摊时按提单号填入,不返回页面 */ - @JsonIgnore - private BigDecimal bandBoxWeight; - private Integer orderCount; /** 不能分摊时的原因。空表示可以分摊 */ diff --git a/bms-back/bms-system/src/main/java/com/bms/price/airbill/domain/SrvBillAirFee.java b/bms-back/bms-system/src/main/java/com/bms/price/airbill/domain/SrvBillAirFee.java index efed20f2..4a6de461 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/airbill/domain/SrvBillAirFee.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/airbill/domain/SrvBillAirFee.java @@ -21,9 +21,12 @@ public class SrvBillAirFee /** 附加费名称,仅展示,不入库 */ private String feeName; - /** 费用阶段,字典 bms_fee_level */ + /** 费用阶段,字典 bms_fee_level。空运为 2(b段),报关和清关为 3(c段) */ private String feeLevel; + /** 费用入口:AIR 空运,DECLARE 报关,CLEAR 清关。页面不展示 */ + private String feeScene; + /** 提单费用状态,字典 bill_fee_status。只随分摊、作废操作变化 */ private String billFeeStatus; diff --git a/bms-back/bms-system/src/main/java/com/bms/price/airbill/domain/SrvBillAirFeeImportRow.java b/bms-back/bms-system/src/main/java/com/bms/price/airbill/domain/SrvBillAirFeeImportRow.java new file mode 100644 index 00000000..7e544388 --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/price/airbill/domain/SrvBillAirFeeImportRow.java @@ -0,0 +1,22 @@ +package com.bms.price.airbill.domain; + +import java.math.BigDecimal; +import com.bms.common.annotation.Excel; +import lombok.Data; + +/** 空运、报关、清关费用导入行。 */ +@Data +public class SrvBillAirFeeImportRow +{ + @Excel(name = "费用名称") + private String feeName; + + @Excel(name = "价格") + private BigDecimal price; + + @Excel(name = "币种") + private String currency; + + @Excel(name = "备注") + private String remark; +} diff --git a/bms-back/bms-system/src/main/java/com/bms/price/airbill/domain/SrvBillAirRoleFeeCommand.java b/bms-back/bms-system/src/main/java/com/bms/price/airbill/domain/SrvBillAirRoleFeeCommand.java new file mode 100644 index 00000000..c102bb73 --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/price/airbill/domain/SrvBillAirRoleFeeCommand.java @@ -0,0 +1,27 @@ +package com.bms.price.airbill.domain; + +import java.util.ArrayList; +import java.util.List; +import com.bms.common.core.domain.BaseEntity; +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 空运、报关、清关页面保存费用。 + * scene:air 空运,declare 报关,clear 清关。数据权限写在 params.dataScope。 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class SrvBillAirRoleFeeCommand extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + private String scene; + + /** 入库入口,仅服务端填写 */ + @JsonIgnore + private String feeScene; + + private List bills = new ArrayList<>(); +} diff --git a/bms-back/bms-system/src/main/java/com/bms/price/airbill/domain/SrvBillAirRoleFeeQuery.java b/bms-back/bms-system/src/main/java/com/bms/price/airbill/domain/SrvBillAirRoleFeeQuery.java new file mode 100644 index 00000000..7ec37628 --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/price/airbill/domain/SrvBillAirRoleFeeQuery.java @@ -0,0 +1,25 @@ +package com.bms.price.airbill.domain; + +import java.util.ArrayList; +import java.util.List; +import com.bms.common.core.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 空运、报关、清关费用查询。数据权限写在 params.dataScope。 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class SrvBillAirRoleFeeQuery extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** air 空运,declare 报关,clear 清关 */ + private String scene; + + /** 入库入口:AIR、DECLARE、CLEAR */ + private String feeScene; + + private List billCodes = new ArrayList<>(); +} diff --git a/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/SrvBillAirVo.java b/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/SrvBillAirVo.java index fea3218b..32a759c0 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/SrvBillAirVo.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/SrvBillAirVo.java @@ -75,6 +75,12 @@ public class SrvBillAirVo @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date firstLandingTime; + /** 板号 */ + private String boardNo; + + /** 打板重量 */ + private BigDecimal bandBoxWeight; + /** 航班号 */ private String airNo; @@ -96,6 +102,9 @@ public class SrvBillAirVo /** 清关代理 */ private String qingguanAgent; + /** 清关操作人工号 */ + private String clearanceOpCode; + /** 出仓仓库地址 */ private String stockAddr; diff --git a/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcOrderBillMapper.java b/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcOrderBillMapper.java index a188a472..d052959a 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcOrderBillMapper.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcOrderBillMapper.java @@ -138,6 +138,9 @@ public interface PrcOrderBillMapper /** 只更新应付合计,不改应收。 */ int updatePayTotal(@Param("billId") String billId, @Param("payTotal") BigDecimal payTotal); + /** 按未删除应付明细合计回写订单上的应付总额。单次不超过 1000 个订单。 */ + int refreshPayTotalFromFees(@Param("billIds") List billIds); + /** 统计引用该销售套餐版本的订单账单数。 */ int countByPsvId(@Param("psvId") Long psvId); } diff --git a/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcPayBillFeeMapper.java b/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcPayBillFeeMapper.java index 87c2b135..505ceb68 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcPayBillFeeMapper.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcPayBillFeeMapper.java @@ -47,6 +47,12 @@ public interface PrcPayBillFeeMapper */ public int markLadingFeeDeleted(@Param("feeId") Long feeId); + /** 这条提单费用已经分摊到的订单。作废前取出,用来重算应付总额。 */ + public List selectBillIdsByLadingFee(@Param("feeId") Long feeId); + + /** 明细还没挂上应付主表时,按订单补上主表 ID。单次不超过 1000 个订单。 */ + public int bindPayBillId(@Param("billIds") List billIds); + /** * 这条提单费用分摊到的订单里,取一个已经生成应付账单的订单号。 * 是否出账看 prc_order_bill_ext.is_meet:1 已生成。 diff --git a/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcPayBillMapper.java b/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcPayBillMapper.java index 14300d4f..1b1b880a 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcPayBillMapper.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcPayBillMapper.java @@ -1,5 +1,7 @@ package com.bms.price.mapper; +import java.util.List; +import org.apache.ibatis.annotations.Param; import com.bms.price.domain.PrcPayBill; /** 订单应付主表 prc_pay_bill。一票计费订单对应一行。 */ @@ -52,4 +54,15 @@ public interface PrcPayBillMapper * @return 影响行数 */ int deleteByBillId(String billId); + + /** + * 按未删除的应付明细合计,回写这些订单的应付总额。 + * + * @param billIds 计费订单 ID,单次不超过 1000 个 + * @return 影响行数 + */ + int refreshPayTotal(@Param("billIds") List billIds); + + /** 订单还没有应付主表时补一行,总额随后按明细重算。单次不超过 1000 个订单。 */ + int insertMissingPayBills(@Param("billIds") List billIds, @Param("operator") String operator); } diff --git a/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcSurchargeMapper.java b/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcSurchargeMapper.java index 5f8f7b73..98e65abe 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcSurchargeMapper.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcSurchargeMapper.java @@ -15,6 +15,9 @@ public interface PrcSurchargeMapper { public PrcSurcharge selectPrcSurchargeById(Long scId); + /** 按费用名称精确匹配未删除的附加费。 */ + public List selectByExactName(@Param("scName") String scName); + public List selectPrcSurchargeList(PrcSurcharge surcharge); public int insertPrcSurcharge(PrcSurcharge surcharge); diff --git a/bms-back/bms-system/src/main/java/com/bms/price/mapper/SrvBillAirAllocMapper.java b/bms-back/bms-system/src/main/java/com/bms/price/mapper/SrvBillAirAllocMapper.java index 611049fc..fb9d5186 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/mapper/SrvBillAirAllocMapper.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/mapper/SrvBillAirAllocMapper.java @@ -20,14 +20,6 @@ public interface SrvBillAirAllocMapper */ List selectPendingFees(@Param("billCodes") List billCodes); - /** - * 按提单号取出实际重量和打板重量,只供按重量分摊计算。 - * - * @param billCodes 提单号 - * @return 提单号、实际重量、打板重量 - */ - List selectLadingWeight(@Param("billCodes") List billCodes); - /** * 按提单号查出关联订单。订单号在扩展表,计费单号和重量在订单表。 * diff --git a/bms-back/bms-system/src/main/java/com/bms/price/mapper/SrvBillAirFeeMapper.java b/bms-back/bms-system/src/main/java/com/bms/price/mapper/SrvBillAirFeeMapper.java index b0df69fa..02e56e5d 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/mapper/SrvBillAirFeeMapper.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/mapper/SrvBillAirFeeMapper.java @@ -4,6 +4,8 @@ import java.util.List; import org.apache.ibatis.annotations.Param; import com.bms.price.airbill.domain.SrvBillAirFee; import com.bms.price.airbill.domain.SrvBillAirFeeLog; +import com.bms.price.airbill.domain.SrvBillAirRoleFeeCommand; +import com.bms.price.airbill.domain.SrvBillAirRoleFeeQuery; /** * 空运提单费用及操作日志。 @@ -12,6 +14,13 @@ public interface SrvBillAirFeeMapper { List selectByBillCode(String billCode); + /** 按提单号和费用入口查询仍有效的费用,并套用数据权限。 */ + List selectRoleFees(SrvBillAirRoleFeeQuery query); + + /** 当前数据权限下仍可修改的费用。看不到时返回空。 */ + SrvBillAirFee selectRoleFeeVisible(@Param("id") Long id, @Param("billCode") String billCode, + @Param("query") SrvBillAirRoleFeeCommand query); + /** 只取这一票提单上仍有效的费用,供修改和删除前核对。 */ SrvBillAirFee selectLive(@Param("id") Long id, @Param("billCode") String billCode); diff --git a/bms-back/bms-system/src/main/java/com/bms/price/pay/PayCostCalculator.java b/bms-back/bms-system/src/main/java/com/bms/price/pay/PayCostCalculator.java index b1366cbd..2b73980c 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/pay/PayCostCalculator.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/pay/PayCostCalculator.java @@ -10,6 +10,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; import java.util.Set; +import com.bms.common.enums.PaymentMesEnum; import com.bms.price.domain.PayCostLine; import com.bms.price.domain.PayOrderCostResult; import com.bms.price.domain.PayProduct; @@ -93,14 +94,14 @@ public final class PayCostCalculator result.setSkipped(new ArrayList<>()); if (product == null) { - result.setMessage("没有应付产品"); + result.setMessage(PaymentMesEnum.BMS_PAY_NO_PRODUCT.getCnMes()); return result; } result.setProductId(product.getProductId()); result.setProductName(product.getProductName()); if (weight == null || weight.compareTo(BigDecimal.ZERO) <= 0) { - result.setMessage("订单没有计费重"); + result.setMessage(PaymentMesEnum.BMS_PAY_NO_WEIGHT.getCnMes()); return result; } List usable = usableBands(product.getWeightBands()); @@ -390,7 +391,7 @@ public final class PayCostCalculator } else if (!name.isEmpty() && !name.contains("报价说明")) { - result.getSkipped().add(name + ":价卡没有可计算的条件,需人工确认"); + result.getSkipped().add(name + ":价卡没有可计算的条件,暂不计费"); } } result.getFees().addAll(keepHighestByName(sized)); diff --git a/bms-back/bms-system/src/main/java/com/bms/price/pay/PayPostcodeImporter.java b/bms-back/bms-system/src/main/java/com/bms/price/pay/PayPostcodeImporter.java index 02e50cd8..cc2c0515 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/pay/PayPostcodeImporter.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/pay/PayPostcodeImporter.java @@ -14,6 +14,7 @@ import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.WorkbookFactory; import org.springframework.util.DigestUtils; +import com.bms.common.enums.PaymentMesEnum; import com.bms.common.exception.ServiceException; import com.bms.common.utils.poi.ExcelGrid; import com.bms.price.domain.PayPostcodeGroup; @@ -78,11 +79,11 @@ public class PayPostcodeImporter { if (bytes == null || bytes.length == 0) { - throw new ServiceException("请选择 Excel 文件"); + throw PaymentMesEnum.BMS_PAY_PICK_XLS.exception(); } if (bytes.length > MAX_BYTES) { - throw new ServiceException("文件不能超过30MB"); + throw PaymentMesEnum.BMS_PAY_XLS_30M.exception(); } String safeName = filename == null ? "邮编.xlsx" : filename; String md5 = DigestUtils.md5DigestAsHex(bytes); @@ -115,7 +116,7 @@ public class PayPostcodeImporter } catch (Exception ex) { - throw new ServiceException("邮编表无法读取,请确认是 Excel 文件"); + throw PaymentMesEnum.BMS_PAY_ZIP_READ.exception(); } } diff --git a/bms-back/bms-system/src/main/java/com/bms/price/pay/PaySurchargeRules.java b/bms-back/bms-system/src/main/java/com/bms/price/pay/PaySurchargeRules.java index c99b9c3c..83e99a93 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/pay/PaySurchargeRules.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/pay/PaySurchargeRules.java @@ -10,7 +10,7 @@ import java.util.regex.Pattern; import com.bms.price.domain.PaySurcharge; /** - * 把附加费说明收成可计算的条件。认不出的保持人工,避免把签名、重复单号当成每票必收。 + * 把附加费说明收成可计算的条件。认不出的标为暂不计费,避免把签名、重复单号当成每票必收。 */ public final class PaySurchargeRules { diff --git a/bms-back/bms-system/src/main/java/com/bms/price/pay/PayWorkbookParser.java b/bms-back/bms-system/src/main/java/com/bms/price/pay/PayWorkbookParser.java index 7314acfe..717b486a 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/pay/PayWorkbookParser.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/pay/PayWorkbookParser.java @@ -17,6 +17,7 @@ import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.WorkbookFactory; import org.springframework.util.DigestUtils; +import com.bms.common.enums.PaymentMesEnum; import com.bms.common.exception.ServiceException; import com.bms.common.utils.poi.ExcelGrid; import com.bms.price.domain.PayExtraLine; @@ -64,25 +65,25 @@ public class PayWorkbookParser { if (bytes == null || bytes.length == 0) { - throw new ServiceException("请选择价卡文件"); + throw PaymentMesEnum.BMS_PAY_PICK_FILE.exception(); } if (bytes.length > MAX_BYTES) { - throw new ServiceException("价卡文件不能超过30MB"); + throw PaymentMesEnum.BMS_PAY_FILE_30M.exception(); } String safeName = filename == null ? "价卡.xlsx" : filename; try (Workbook workbook = WorkbookFactory.create(new ByteArrayInputStream(bytes))) { if (workbook.getNumberOfSheets() == 0) { - throw new ServiceException("文件里没有工作表"); + throw PaymentMesEnum.BMS_PAY_NO_SHEET.exception(); } ExcelGrid catalog = ExcelGrid.read(workbook.getSheetAt(0)); FileMeta meta = fileMeta(safeName); List items = readCatalog(catalog, workbook, meta); if (items.isEmpty()) { - throw new ServiceException("目录里没有找到产品报价表"); + throw PaymentMesEnum.BMS_PAY_NO_QUOTE.exception(); } PayPreview preview = new PayPreview(); preview.setSourceFile(safeName); @@ -103,7 +104,7 @@ public class PayWorkbookParser } catch (Exception ex) { - throw new ServiceException("价卡无法读取,请确认是 Excel 报价文件"); + throw PaymentMesEnum.BMS_PAY_BAD_XLS.exception(); } } @@ -124,16 +125,16 @@ public class PayWorkbookParser { if (bytes == null || bytes.length == 0) { - throw new ServiceException("请选择价卡文件"); + throw PaymentMesEnum.BMS_PAY_PICK_FILE.exception(); } if (bytes.length > MAX_BYTES) { - throw new ServiceException("价卡文件不能超过30MB"); + throw PaymentMesEnum.BMS_PAY_FILE_30M.exception(); } Map dict = dictIndex(dictValues); if (dict.isEmpty()) { - throw new ServiceException("应付产品字典还没有数据"); + throw PaymentMesEnum.BMS_PAY_DICT_EMPTY.exception(); } String safeName = filename == null ? "价卡.xlsx" : filename; String wanted = norm(selected); @@ -142,7 +143,7 @@ public class PayWorkbookParser { if (workbook.getNumberOfSheets() == 0) { - throw new ServiceException("文件里没有工作表"); + throw PaymentMesEnum.BMS_PAY_NO_SHEET.exception(); } FileMeta meta = fileMeta(safeName); PayPreview preview = new PayPreview(); @@ -208,7 +209,7 @@ public class PayWorkbookParser } catch (Exception ex) { - throw new ServiceException("价卡无法读取,请确认是 Excel 报价文件"); + throw PaymentMesEnum.BMS_PAY_BAD_XLS.exception(); } } diff --git a/bms-back/bms-system/src/main/java/com/bms/price/service/IPrcPayBillFeeService.java b/bms-back/bms-system/src/main/java/com/bms/price/service/IPrcPayBillFeeService.java new file mode 100644 index 00000000..e4c250cf --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/price/service/IPrcPayBillFeeService.java @@ -0,0 +1,25 @@ +package com.bms.price.service; + +import java.util.List; +import com.bms.price.domain.PrcPayBillFee; + +/** + * 订单应付明细。写入明细后,按未删除金额合计回写应付主表。 + */ +public interface IPrcPayBillFeeService +{ + /** + * 分批写入应付明细,并回写这些订单的应付总额。 + * 明细每批最多 1000 条。总额是该订单未删除明细的金额之和,同时写入 prc_pay_bill.pay_total 和订单应付总额。 + * + * @param fees 本次新增的应付明细 + */ + void insertBatchAndRefreshTotals(List fees); + + /** + * 按未删除应付明细重算应付总额。订单每批最多 1000 个。 + * + * @param billIds 计费订单 ID + */ + void refreshTotals(List billIds); +} diff --git a/bms-back/bms-system/src/main/java/com/bms/price/service/ISrvBillAirFeeService.java b/bms-back/bms-system/src/main/java/com/bms/price/service/ISrvBillAirFeeService.java index e4445133..6b504bbd 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/service/ISrvBillAirFeeService.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/service/ISrvBillAirFeeService.java @@ -1,9 +1,12 @@ package com.bms.price.service; +import java.io.InputStream; import java.util.List; import com.bms.price.airbill.domain.SrvBillAirFee; import com.bms.price.airbill.domain.SrvBillAirFeeLog; import com.bms.price.airbill.domain.SrvBillAirFeeSave; +import com.bms.price.airbill.domain.SrvBillAirRoleFeeCommand; +import com.bms.price.airbill.domain.SrvBillAirRoleFeeQuery; /** * 空运提单费用维护。 @@ -12,6 +15,18 @@ public interface ISrvBillAirFeeService { List listFees(String billCode); + /** + * 按费用入口和提单号查询仍有效的费用。入口:air 空运,declare 报关,clear 清关。 + * 按角色数据权限过滤:本部门看本部门创建的费用,仅本人看自己创建的费用,超级管理员和全部数据权限看全部。 + */ + List listRoleFees(SrvBillAirRoleFeeQuery query); + + /** 把空运、报关或清关页面提交的费用写入提单。费用阶段按入口写入字典值,分摊固定为重量。 */ + String saveRole(SrvBillAirRoleFeeCommand command); + + /** 解析导入文件。示例行不入库,费用名称按附加费名称匹配。 */ + List parseImport(String scene, InputStream input) throws Exception; + List listLogs(String billCode); /** 只写入有变动的行,并按行记录修改前后。返回给页面的提示。 */ diff --git a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PayBillServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PayBillServiceImpl.java index 2910286c..0b979ef8 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PayBillServiceImpl.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PayBillServiceImpl.java @@ -14,6 +14,7 @@ import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import com.bms.common.enums.PaymentMesEnum; import com.bms.common.exception.ServiceException; import com.bms.common.utils.SecurityUtils; import com.bms.common.utils.uuid.IdUtils; @@ -72,7 +73,7 @@ public class PayBillServiceImpl implements IPayBillService PayBill bill = payBillMapper.selectPayBillById(billId); if (bill == null) { - throw new ServiceException("应付账单不存在"); + throw PaymentMesEnum.BMS_PAY_BILL_MISS.exception(); } bill.setLines(payBillMapper.selectLines(billId)); bill.setLogs(payBillMapper.selectLogs(billId)); @@ -104,18 +105,18 @@ public class PayBillServiceImpl implements IPayBillService { if (command.getSupplierId() == null) { - throw new ServiceException("请选择供应商"); + throw PaymentMesEnum.BMS_PAY_PICK_SUP.exception(); } BmsSupplier supplier = supplierMapper.selectBmsSupplierBySupplierId(command.getSupplierId()); if (supplier == null) { - throw new ServiceException("供应商不存在"); + throw PaymentMesEnum.BMS_PAY_SUP_MISS.exception(); } PayBillPreview preview = collect(command, true); List orders = freeOrders(preview); if (orders.isEmpty()) { - throw new ServiceException(preview.getMissingNos().isEmpty() ? "没有可生成的订单" : "订单号没有对上:" + String.join("、", preview.getMissingNos())); + throw new ServiceException(preview.getMissingNos().isEmpty() ? PaymentMesEnum.BMS_PAY_NO_GEN.getCnMes() : "订单号没有对上:" + String.join("、", preview.getMissingNos())); } String currency = currencyOf(orders); Map versions = new HashMap<>(); @@ -155,7 +156,7 @@ public class PayBillServiceImpl implements IPayBillService } if (lines.isEmpty()) { - throw new ServiceException("订单没有应付费用"); + throw PaymentMesEnum.BMS_PAY_BILL_FEE.exception(); } BigDecimal total = BigDecimal.ZERO; for (PayBillLine line : lines) @@ -163,7 +164,7 @@ public class PayBillServiceImpl implements IPayBillService String lineCurrency = line.getCurrency() == null ? "" : line.getCurrency(); if (!currency.equals(lineCurrency)) { - throw new ServiceException("所选订单币种不一致,请按币种分开生成"); + throw PaymentMesEnum.BMS_PAY_CURRENCY.exception(); } total = total.add(line.getAmount() == null ? BigDecimal.ZERO : line.getAmount()); } @@ -203,7 +204,7 @@ public class PayBillServiceImpl implements IPayBillService PayBill bill = required(billId); if (!"DRAFT".equals(bill.getStatus())) { - throw new ServiceException("只有草稿可以确认"); + throw PaymentMesEnum.BMS_PAY_DRAFT.exception(); } String operator = SecurityUtils.getUsername(); bill.setStatus("CONFIRMED"); @@ -226,7 +227,7 @@ public class PayBillServiceImpl implements IPayBillService PayBill bill = required(billId); if (!"CONFIRMED".equals(bill.getStatus())) { - throw new ServiceException("请先确认账单,再登记付款"); + throw PaymentMesEnum.BMS_PAY_CONFIRM.exception(); } String operator = SecurityUtils.getUsername(); bill.setStatus("PAID"); @@ -248,20 +249,20 @@ public class PayBillServiceImpl implements IPayBillService { if (command == null || command.getBillId() == null) { - throw new ServiceException("请选择应付账单"); + throw PaymentMesEnum.BMS_PAY_PICK_BILL.exception(); } if (command.getVoidReason() == null || command.getVoidReason().isBlank()) { - throw new ServiceException("请填写作废原因"); + throw PaymentMesEnum.BMS_PAY_VOID_WHY.exception(); } PayBill bill = required(command.getBillId()); if ("VOID".equals(bill.getStatus())) { - throw new ServiceException("账单已经作废"); + throw PaymentMesEnum.BMS_PAY_VOIDED.exception(); } if ("PAID".equals(bill.getStatus())) { - throw new ServiceException("已付款的账单不能直接作废,请先冲回付款"); + throw PaymentMesEnum.BMS_PAY_PAID.exception(); } String operator = SecurityUtils.getUsername(); String from = bill.getStatus(); @@ -289,16 +290,16 @@ public class PayBillServiceImpl implements IPayBillService List ladingNos = "LADING".equals(mode) ? tokens(command.getLadingText()) : new ArrayList<>(); if ("ORDER".equals(mode) && orderNos.isEmpty()) { - throw new ServiceException("请填写订单号"); + throw PaymentMesEnum.BMS_PAY_ORDER_NO.exception(); } if ("TIME".equals(mode) && (command.getBeginDate() == null || command.getBeginDate().isBlank() || command.getEndDate() == null || command.getEndDate().isBlank())) { - throw new ServiceException("请选择计费日起止"); + throw PaymentMesEnum.BMS_PAY_BIZ_DATE.exception(); } if ("LADING".equals(mode) && ladingNos.isEmpty()) { - throw new ServiceException("请填写提单号"); + throw PaymentMesEnum.BMS_PAY_LADING_NO.exception(); } List found = payBillMapper.selectCandidateOrders( orderNos.isEmpty() ? null : orderNos, @@ -412,7 +413,7 @@ public class PayBillServiceImpl implements IPayBillService PayBill bill = payBillMapper.selectPayBillById(billId); if (bill == null) { - throw new ServiceException("应付账单不存在"); + throw PaymentMesEnum.BMS_PAY_BILL_MISS.exception(); } return bill; } @@ -476,7 +477,7 @@ public class PayBillServiceImpl implements IPayBillService { if (command == null || command.getSourceType() == null || command.getSourceType().isBlank()) { - throw new ServiceException("请选择查询方式"); + throw PaymentMesEnum.BMS_PAY_QUERY.exception(); } return command.getSourceType().trim().toUpperCase(Locale.ROOT); } diff --git a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PayOrderCostServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PayOrderCostServiceImpl.java index d891f113..b7736d7c 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PayOrderCostServiceImpl.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PayOrderCostServiceImpl.java @@ -6,6 +6,7 @@ import java.util.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import com.bms.common.enums.PaymentMesEnum; import com.bms.common.exception.ServiceException; import com.bms.common.utils.SecurityUtils; import com.bms.masterdata.domain.ServiceChannel; @@ -32,6 +33,7 @@ import com.bms.price.pay.PaySurchargeRules; import com.bms.price.pay.PayZoneMatcher; import com.bms.price.service.IPayOrderCostService; import com.bms.price.service.IPayProductService; +import com.bms.price.service.IPrcPayBillFeeService; /** * 订单找到渠道,渠道找到当前应付产品,再按重量、尺寸、邮编计算应付并落库。 @@ -57,6 +59,9 @@ public class PayOrderCostServiceImpl implements IPayOrderCostService @Autowired private PrcPayBillFeeMapper payBillFeeMapper; + @Autowired + private IPrcPayBillFeeService payBillFeeService; + @Autowired private PayPostcodeMapper payPostcodeMapper; @@ -73,12 +78,12 @@ public class PayOrderCostServiceImpl implements IPayOrderCostService { if (billId == null || billId.isBlank()) { - throw new ServiceException("请选择订单"); + throw PaymentMesEnum.BMS_PAY_PICK_ORDER.exception(); } PrcOrderBill bill = orderBillMapper.selectPrcOrderBillById(billId.trim()); if (bill == null) { - throw new ServiceException("订单不存在"); + throw PaymentMesEnum.BMS_PAY_ORDER_MISS.exception(); } PrcOrderBillExt ext = orderBillExtMapper.selectByBillId(bill.getBillId()); ServiceChannel channel = resolveChannel(ext); @@ -98,12 +103,11 @@ public class PayOrderCostServiceImpl implements IPayOrderCostService result.setVersionNo(product.getVersionNo()); if (result.getFees() == null || result.getFees().isEmpty()) { - throw new ServiceException(result.getMessage() == null ? "没有算出应付费用" : result.getMessage()); + throw new ServiceException(result.getMessage() == null ? PaymentMesEnum.BMS_PAY_NO_COST.getCnMes() : result.getMessage()); } String operator = SecurityUtils.getUsername(); PrcPayBill payBill = saveBill(bill, result, operator); saveFees(bill.getBillId(), payBill.getPayBillId(), result.getFees()); - orderBillMapper.updatePayTotal(bill.getBillId(), result.getPayTotal()); String channelName = channel.getChannelName() == null ? channel.getChannelCode() : channel.getChannelName(); String end = product.getTermEnd() == null ? "不限" : day(product.getTermEnd()); result.setMessage("渠道「" + channelName + "」产品编号 " + product.getOriginId() + " 第 " + product.getVersionNo() @@ -273,7 +277,7 @@ public class PayOrderCostServiceImpl implements IPayOrderCostService } if (decision.getZoneName() == null) { - throw new ServiceException("邮编没有对上价卡地区"); + throw PaymentMesEnum.BMS_PAY_NO_ZONE.exception(); } return decision.getZoneName(); } @@ -379,7 +383,7 @@ public class PayOrderCostServiceImpl implements IPayOrderCostService { if (ext == null) { - throw new ServiceException("订单没有渠道服务商"); + throw PaymentMesEnum.BMS_PAY_NO_CHANNEL.exception(); } ServiceChannel channel = blank(ext.getProductChannelId()) ? null : serviceChannelMapper.selectServiceChannelById(ext.getProductChannelId().trim()); if (channel == null && !blank(ext.getProductFloorCode())) @@ -397,13 +401,13 @@ public class PayOrderCostServiceImpl implements IPayOrderCostService } if (channel == null) { - throw new ServiceException("订单没有渠道服务商,或渠道尚未同步"); + throw PaymentMesEnum.BMS_PAY_CH_SYNC.exception(); } return channel; } /** - * 保存订单应付主表。已有记录则更新合计,没有则新建。合计只写本次尾程结果。 + * 保存订单应付主表。已有记录则更新,没有则新建。成本价写本次尾程结果,应付总额随后按全部未删除明细重算。 * * @param bill 计费订单 * @param result 本次尾程计算结果 @@ -473,7 +477,11 @@ public class PayOrderCostServiceImpl implements IPayOrderCostService } if (!rows.isEmpty()) { - payBillFeeMapper.insertBatch(rows); + payBillFeeService.insertBatchAndRefreshTotals(rows); + } + else + { + payBillFeeService.refreshTotals(java.util.List.of(billId)); } } diff --git a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PayPostcodeServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PayPostcodeServiceImpl.java index 104b9d3d..3f07be82 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PayPostcodeServiceImpl.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PayPostcodeServiceImpl.java @@ -7,7 +7,7 @@ import java.util.Set; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import com.bms.common.exception.ServiceException; +import com.bms.common.enums.PaymentMesEnum; import com.bms.price.domain.PayPostcodeGroup; import com.bms.price.domain.PayPostcodeImportResult; import com.bms.price.domain.PayPostcodeLine; @@ -45,11 +45,11 @@ public class PayPostcodeServiceImpl implements IPayPostcodeService { if (group == null || group.getGroupId() == null) { - throw new ServiceException("请选择要修改的邮编配置"); + throw PaymentMesEnum.BMS_PAY_ZIP_EDIT.exception(); } if (payPostcodeMapper.selectGroupById(group.getGroupId()) == null) { - throw new ServiceException("邮编配置不存在或已删除"); + throw PaymentMesEnum.BMS_PAY_ZIP_MISS.exception(); } group.setCoverage(normalizeCoverage(group.getCoverage())); group.setChannelName(trimToEmpty(group.getChannelName())); @@ -65,7 +65,7 @@ public class PayPostcodeServiceImpl implements IPayPostcodeService group.setRemark(trimToEmpty(group.getRemark())); if (!"FEE".equals(group.getConfigKind()) && !"ZONE".equals(group.getConfigKind())) { - throw new ServiceException("类型只能是附加费或邮编分区"); + throw PaymentMesEnum.BMS_PAY_ZIP_TYPE.exception(); } return payPostcodeMapper.updateGroup(group); } @@ -83,7 +83,7 @@ public class PayPostcodeServiceImpl implements IPayPostcodeService List groups = importer.parse(bytes, filename); if (groups.isEmpty()) { - throw new ServiceException("没有识别到邮编或附加费邮编,请确认表头里有邮编、ZIP 或附加费"); + throw PaymentMesEnum.BMS_PAY_ZIP_HEAD.exception(); } Set sheets = new LinkedHashSet<>(); for (PayPostcodeGroup group : groups) @@ -172,6 +172,6 @@ public class PayPostcodeServiceImpl implements IPayPostcodeService { return coverage; } - throw new ServiceException("邮编属性只能是可达、不可达或偏远"); + throw PaymentMesEnum.BMS_PAY_ZIP_ATTR.exception(); } } diff --git a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PayProductServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PayProductServiceImpl.java index 6441db80..b9e60317 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PayProductServiceImpl.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PayProductServiceImpl.java @@ -21,6 +21,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.bms.common.core.domain.entity.SysDictData; +import com.bms.common.enums.PaymentMesEnum; import com.bms.common.exception.ServiceException; import com.bms.common.utils.DateUtils; import com.bms.common.utils.SecurityUtils; @@ -247,11 +248,11 @@ public class PayProductServiceImpl implements IPayProductService { if (command == null || command.getProducts() == null || command.getProducts().isEmpty()) { - throw new ServiceException("请先勾选要保存的产品"); + throw PaymentMesEnum.BMS_PAY_PICK_PROD.exception(); } if (command.getProducts().size() > 80) { - throw new ServiceException("一次最多保存80个产品"); + throw PaymentMesEnum.BMS_PAY_PROD_LIMIT.exception(); } String operator = SecurityUtils.getUsername(); int saved = 0; @@ -261,7 +262,7 @@ public class PayProductServiceImpl implements IPayProductService { if (product == null || isBlank(product.getProductName()) || isBlank(product.getSheetName()) || isBlank(product.getFileMd5())) { - throw new ServiceException("产品名称、工作表和文件摘要不能为空"); + throw PaymentMesEnum.BMS_PAY_PROD_FIELD.exception(); } Long existsId = templateId(product); if (existsId != null && !command.isOverwrite()) @@ -303,16 +304,16 @@ public class PayProductServiceImpl implements IPayProductService { if (product == null || product.getProductId() == null) { - throw new ServiceException("请选择要修改的产品"); + throw PaymentMesEnum.BMS_PAY_PICK_EDIT.exception(); } PayProduct existing = payProductMapper.selectPayProductById(product.getProductId()); if (existing == null) { - throw new ServiceException("产品不存在或已删除"); + throw PaymentMesEnum.BMS_PAY_PROD_MISS.exception(); } if (!isBlank(existing.getChannelId())) { - throw new ServiceException("渠道价格请在渠道页面保存,这里只改公共模板"); + throw PaymentMesEnum.BMS_PAY_TPL_ONLY.exception(); } product.setDictValue(blankTo(product.getDictValue(), existing.getDictValue())); product.setOriginId(existing.getOriginId()); @@ -364,11 +365,11 @@ public class PayProductServiceImpl implements IPayProductService { if (isBlank(channelId)) { - throw new ServiceException("请先保存渠道,再维护价格"); + throw PaymentMesEnum.BMS_PAY_SAVE_CH.exception(); } if (edited == null) { - throw new ServiceException("没有可保存的价格"); + throw PaymentMesEnum.BMS_PAY_NO_PRICE.exception(); } Long root = resolveRoot(edited.getOriginId() != null ? edited.getOriginId() : edited.getProductId()); String operator = SecurityUtils.getUsername(); @@ -387,7 +388,7 @@ public class PayProductServiceImpl implements IPayProductService PayPreview preview = preview(bytes, filename, dictValue); if (preview.getProducts().size() != 1) { - throw new ServiceException("重新导入时请使用单个产品文件,或先选定渠道上的应付产品"); + throw PaymentMesEnum.BMS_PAY_REIMPORT.exception(); } PayProduct parsed = preview.getProducts().get(0); if (root == null) @@ -396,7 +397,7 @@ public class PayProductServiceImpl implements IPayProductService } if (root == null) { - throw new ServiceException("请先选择一个已导入的应付产品"); + throw PaymentMesEnum.BMS_PAY_PICK_IMP.exception(); } PayProduct saved = appendVersion(root, parsed, SecurityUtils.getUsername()); saved.setOriginId(root); @@ -424,7 +425,7 @@ public class PayProductServiceImpl implements IPayProductService } if (values.isEmpty()) { - throw new ServiceException("应付产品字典还没有数据"); + throw PaymentMesEnum.BMS_PAY_DICT_EMPTY.exception(); } return values; } @@ -544,7 +545,7 @@ public class PayProductServiceImpl implements IPayProductService PayProduct product = productId == null ? null : payProductMapper.selectPayProductById(productId); if (product == null) { - throw new ServiceException("产品不存在或已删除"); + throw PaymentMesEnum.BMS_PAY_PROD_MISS.exception(); } return payProductTermMapper.selectByDict(termKey(product)); } @@ -555,23 +556,23 @@ public class PayProductServiceImpl implements IPayProductService { if (term == null || term.getEffectiveStart() == null) { - throw new ServiceException("请填写生效开始"); + throw PaymentMesEnum.BMS_PAY_TERM_START.exception(); } if (term.getEffectiveEnd() != null && term.getEffectiveEnd().before(term.getEffectiveStart())) { - throw new ServiceException("生效结束不能早于生效开始"); + throw PaymentMesEnum.BMS_PAY_TERM_END.exception(); } String operator = SecurityUtils.getUsername(); if (term.getTermId() == null) { if (term.getProductId() == null) { - throw new ServiceException("请选择要修改的版本"); + throw PaymentMesEnum.BMS_PAY_PICK_VER.exception(); } PayProduct product = payProductMapper.selectPayProductById(term.getProductId()); if (product == null) { - throw new ServiceException("产品不存在或已删除"); + throw PaymentMesEnum.BMS_PAY_PROD_MISS.exception(); } PayProductTerm exists = payProductTermMapper.selectByProductId(product.getProductId()); if (exists != null) @@ -592,7 +593,7 @@ public class PayProductServiceImpl implements IPayProductService PayProductTerm existing = payProductTermMapper.selectById(term.getTermId()); if (existing == null) { - throw new ServiceException("有效期不存在"); + throw PaymentMesEnum.BMS_PAY_TERM_MISS.exception(); } term.setProductId(existing.getProductId()); term.setUpdateBy(operator); @@ -609,7 +610,7 @@ public class PayProductServiceImpl implements IPayProductService if (term == null || (!TERM_PENDING.equals(term.getStatus()) && !TERM_EFFECT.equals(term.getStatus()) && !TERM_EXPIRED.equals(term.getStatus()))) { - throw new ServiceException("状态只能是待生效、生效或已失效"); + throw PaymentMesEnum.BMS_PAY_TERM_STAT.exception(); } if (term.getTermId() == null && term.getProductId() != null) { @@ -621,7 +622,7 @@ public class PayProductServiceImpl implements IPayProductService } if (term.getTermId() == null) { - throw new ServiceException("请选择要修改的版本"); + throw PaymentMesEnum.BMS_PAY_PICK_VER.exception(); } term.setUpdateBy(SecurityUtils.getUsername()); return payProductTermMapper.updateStatus(term); @@ -706,7 +707,7 @@ public class PayProductServiceImpl implements IPayProductService } catch (IOException ex) { - throw new ServiceException("源文件下载失败"); + throw PaymentMesEnum.BMS_PAY_FILE_DOWN.exception(); } } @@ -715,12 +716,12 @@ public class PayProductServiceImpl implements IPayProductService PayProduct product = productId == null ? null : payProductMapper.selectPayProductById(productId); if (product == null) { - throw new ServiceException("产品不存在或已删除"); + throw PaymentMesEnum.BMS_PAY_PROD_MISS.exception(); } String path = sourcePath(product.getFilePath()); if (path == null) { - throw new ServiceException("这一版没有可下载的源文件"); + throw PaymentMesEnum.BMS_PAY_NO_SOURCE.exception(); } product.setFilePath(path); if (isBlank(product.getSourceFile())) @@ -754,7 +755,7 @@ public class PayProductServiceImpl implements IPayProductService } catch (IOException ex) { - throw new ServiceException("价卡文件上传失败"); + throw PaymentMesEnum.BMS_PAY_FILE_UP.exception(); } } @@ -768,7 +769,7 @@ public class PayProductServiceImpl implements IPayProductService } if (path == null) { - throw new ServiceException("源文件还没有上传成功,请重新选择价卡后再保存"); + throw PaymentMesEnum.BMS_PAY_FILE_WAIT.exception(); } return path; } @@ -883,7 +884,7 @@ public class PayProductServiceImpl implements IPayProductService { if (command == null || command.getProducts() == null || command.getProducts().isEmpty()) { - throw new ServiceException("请先勾选要保存的产品"); + throw PaymentMesEnum.BMS_PAY_PICK_PROD.exception(); } Long priceSetId = ensureLabelPriceSet(command); String operator = SecurityUtils.getUsername(); @@ -912,7 +913,7 @@ public class PayProductServiceImpl implements IPayProductService { if (product == null || isBlank(product.getProductName()) || isBlank(product.getSheetName()) || isBlank(product.getFileMd5())) { - throw new ServiceException("产品名称、工作表和文件摘要不能为空"); + throw PaymentMesEnum.BMS_PAY_PROD_FIELD.exception(); } product.setProductType(PAY_TYPE_AR); String dict = blankTo(product.getDictValue(), product.getProductName()); @@ -1740,12 +1741,12 @@ public class PayProductServiceImpl implements IPayProductService { if (isBlank(channelId) || productId == null) { - throw new ServiceException("请先保存渠道,再选择产品"); + throw PaymentMesEnum.BMS_PAY_CH_FIRST.exception(); } ServiceChannel channel = serviceChannelMapper.selectServiceChannelById(channelId); if (channel == null || isBlank(channel.getChannelCode())) { - throw new ServiceException("渠道不存在或渠道编码为空"); + throw PaymentMesEnum.BMS_PAY_CH_MISS.exception(); } String type = payTypeOf(payType); Long storedId = resolveRoot(productId); @@ -1766,11 +1767,11 @@ public class PayProductServiceImpl implements IPayProductService { if (channelIds == null || channelIds.isEmpty()) { - throw new ServiceException("请先勾选渠道"); + throw PaymentMesEnum.BMS_PAY_PICK_CH.exception(); } if (apProductId == null && arProductId == null) { - throw new ServiceException("请至少选择应付或应收产品"); + throw PaymentMesEnum.BMS_PAY_PICK_SIDE.exception(); } int n = 0; for (String channelId : channelIds) @@ -1791,7 +1792,7 @@ public class PayProductServiceImpl implements IPayProductService } if (n <= 0) { - throw new ServiceException("没有可绑定的渠道"); + throw PaymentMesEnum.BMS_PAY_BIND_NONE.exception(); } return "已为 " + n + " 个渠道绑定产品"; } @@ -1859,18 +1860,18 @@ public class PayProductServiceImpl implements IPayProductService { if (isBlank(channelId)) { - throw new ServiceException("订单没有渠道服务商"); + throw PaymentMesEnum.BMS_PAY_NO_CHANNEL.exception(); } Long root = channelProductRoot(channelId); if (root == null) { - throw new ServiceException("渠道还没有选择应付产品"); + throw PaymentMesEnum.BMS_PAY_CH_PROD.exception(); } Date day = bizDate == null ? today() : bizDate; PayProduct version = loadVersion(root, day, true); if (version == null) { - throw new ServiceException("订单时间没有落在价格有效期内"); + throw PaymentMesEnum.BMS_PAY_OUT_TERM.exception(); } return version; } @@ -1912,7 +1913,7 @@ public class PayProductServiceImpl implements IPayProductService PayProduct right = selectPayProductDetail(rightId); if (left == null || right == null) { - throw new ServiceException("请选择两个价格版本"); + throw PaymentMesEnum.BMS_PAY_TWO_VER.exception(); } int leftNo = left.getVersionNo() == null ? 1 : left.getVersionNo(); int rightNo = right.getVersionNo() == null ? 1 : right.getVersionNo(); @@ -1991,7 +1992,7 @@ public class PayProductServiceImpl implements IPayProductService String type = payType.trim().toUpperCase(); if (!PAY_TYPE_AP.equals(type) && !PAY_TYPE_AR.equals(type)) { - throw new ServiceException("收付类型只能是应收AR或应付AP"); + throw PaymentMesEnum.BMS_PAY_AR_AP.exception(); } return type; } @@ -2000,7 +2001,7 @@ public class PayProductServiceImpl implements IPayProductService { if (productId == null) { - throw new ServiceException("请先选择一个已导入的应付产品"); + throw PaymentMesEnum.BMS_PAY_PICK_IMP.exception(); } return stableId(productId); } @@ -2010,7 +2011,7 @@ public class PayProductServiceImpl implements IPayProductService PayProduct product = payProductMapper.selectPayProductById(productId); if (product == null) { - throw new ServiceException("应付产品不存在或已删除"); + throw PaymentMesEnum.BMS_PAY_AP_MISS.exception(); } return product.getOriginId() == null ? product.getProductId() : product.getOriginId(); } diff --git a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcPayBillFeeServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcPayBillFeeServiceImpl.java new file mode 100644 index 00000000..49ef88a3 --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcPayBillFeeServiceImpl.java @@ -0,0 +1,96 @@ +package com.bms.price.service.impl; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import com.bms.common.utils.SecurityUtils; +import com.bms.price.domain.PrcPayBillFee; +import com.bms.price.mapper.PrcOrderBillMapper; +import com.bms.price.mapper.PrcPayBillFeeMapper; +import com.bms.price.mapper.PrcPayBillMapper; +import com.bms.price.service.IPrcPayBillFeeService; + +/** + * 应付明细批量落库,并把合计回写到应付主表。 + */ +@Service +public class PrcPayBillFeeServiceImpl implements IPrcPayBillFeeService +{ + private static final int BATCH_SIZE = 1000; + + @Autowired + private PrcPayBillFeeMapper payBillFeeMapper; + + @Autowired + private PrcPayBillMapper payBillMapper; + + @Autowired + private PrcOrderBillMapper orderBillMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + public void insertBatchAndRefreshTotals(List fees) + { + if (fees == null || fees.isEmpty()) + { + return; + } + LinkedHashSet billIds = new LinkedHashSet<>(); + for (int from = 0; from < fees.size(); from += BATCH_SIZE) + { + List chunk = fees.subList(from, Math.min(from + BATCH_SIZE, fees.size())); + payBillFeeMapper.insertBatch(chunk); + for (PrcPayBillFee fee : chunk) + { + if (fee != null && fee.getBillId() != null && !fee.getBillId().isBlank()) + { + billIds.add(fee.getBillId().trim()); + } + } + } + refreshTotals(new ArrayList<>(billIds)); + } + + @Override + public void refreshTotals(List billIds) + { + if (billIds == null || billIds.isEmpty()) + { + return; + } + LinkedHashSet unique = new LinkedHashSet<>(); + for (String billId : billIds) + { + if (billId != null && !billId.isBlank()) + { + unique.add(billId.trim()); + } + } + List ids = new ArrayList<>(unique); + String operator = currentOperator(); + for (int from = 0; from < ids.size(); from += BATCH_SIZE) + { + List chunk = ids.subList(from, Math.min(from + BATCH_SIZE, ids.size())); + payBillMapper.insertMissingPayBills(chunk, operator); + payBillFeeMapper.bindPayBillId(chunk); + payBillMapper.refreshPayTotal(chunk); + orderBillMapper.refreshPayTotalFromFees(chunk); + } + } + + private static String currentOperator() + { + try + { + String name = SecurityUtils.getUsername(); + return name == null || name.isBlank() ? "system" : name; + } + catch (Exception ex) + { + return "system"; + } + } +} diff --git a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/SrvBillAirAllocServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/SrvBillAirAllocServiceImpl.java index 3e142acc..60cbb67f 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/SrvBillAirAllocServiceImpl.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/SrvBillAirAllocServiceImpl.java @@ -14,6 +14,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.alibaba.fastjson2.JSON; import com.bms.common.core.domain.entity.SysUser; +import com.bms.common.enums.PaymentMesEnum; import com.bms.common.exception.ServiceException; import com.bms.common.utils.DateUtils; import com.bms.common.utils.SecurityUtils; @@ -25,7 +26,7 @@ import com.bms.price.domain.PrcPayBillFee; import com.bms.price.mapper.SrvBillAirAllocMapper; import com.bms.price.mapper.SrvBillAirFeeMapper; import com.bms.price.mapper.SrvBillAirMapper; -import com.bms.price.mapper.PrcPayBillFeeMapper; +import com.bms.price.service.IPrcPayBillFeeService; import com.bms.price.service.ISrvBillAirAllocService; /** @@ -42,8 +43,6 @@ public class SrvBillAirAllocServiceImpl implements ISrvBillAirAllocService private static final String DIM_WEIGHT = "2"; - private static final String FEE_TYPE = "LADING"; - private static final String FEE_SOURCE = "LADING"; private static final BigDecimal ZERO = BigDecimal.ZERO; @@ -58,14 +57,13 @@ public class SrvBillAirAllocServiceImpl implements ISrvBillAirAllocService private SrvBillAirFeeMapper feeMapper; @Autowired - private PrcPayBillFeeMapper payBillFeeMapper; + private IPrcPayBillFeeService payBillFeeService; @Override public List preview(List billCodes) { List codes = normalize(billCodes); List fees = dedupe(allocMapper.selectPendingFees(codes)); - fillWeight(fees); Map> orders = ordersOf(codes); for (SrvBillAirAllocFee fee : fees) { @@ -82,10 +80,9 @@ public class SrvBillAirAllocServiceImpl implements ISrvBillAirAllocService { List codes = normalize(billCodes); List fees = dedupe(allocMapper.selectPendingFees(codes)); - fillWeight(fees); if (fees.isEmpty()) { - throw new ServiceException("所选提单没有需要分摊的费用"); + throw PaymentMesEnum.BMS_PAY_NO_ALLOC.exception(); } Map> orders = ordersOf(codes); for (SrvBillAirAllocFee fee : fees) @@ -105,8 +102,7 @@ public class SrvBillAirAllocServiceImpl implements ISrvBillAirAllocService { List rows = orders.get(fee.getBillCode()); fee.setOrderCount(rows.size()); - boolean byWeight = DIM_WEIGHT.equals(fee.getAllocDim()); - List shares = split(byWeight ? weightAmount(fee) : fee.getPrice(), rows, byWeight); + List shares = split(fee.getPrice(), rows, true); for (int i = 0; i < rows.size(); i++) { batch.add(toOrderFee(fee, rows.get(i), shares.get(i), payBillIds)); @@ -122,54 +118,12 @@ public class SrvBillAirAllocServiceImpl implements ISrvBillAirAllocService int updated = allocMapper.markAllocated(feeIds(fees), operator, now); if (updated != fees.size()) { - throw new ServiceException("费用状态已变化,请重新打开后再分摊"); + throw PaymentMesEnum.BMS_PAY_ALLOC_ST.exception(); } writeLogs(fees, operator, now); return "已将 " + fees.size() + " 条费用分摊为 " + lineCount + " 笔订单费用"; } - /** - * 按重量分摊的费用,按提单号补上实际重量和打板重量。按订单数量分摊的不查、不填。 - * 这两个值只参与计算,不返回给页面。 - */ - private void fillWeight(List fees) - { - List codes = new ArrayList<>(); - for (SrvBillAirAllocFee fee : fees) - { - if (fee != null && DIM_WEIGHT.equals(fee.getAllocDim()) && fee.getBillCode() != null && !codes.contains(fee.getBillCode())) - { - codes.add(fee.getBillCode()); - } - } - if (codes.isEmpty()) - { - return; - } - Map weights = new LinkedHashMap<>(); - for (SrvBillAirAllocFee row : allocMapper.selectLadingWeight(codes)) - { - if (row != null && row.getBillCode() != null) - { - weights.put(row.getBillCode(), row); - } - } - for (SrvBillAirAllocFee fee : fees) - { - if (fee == null || !DIM_WEIGHT.equals(fee.getAllocDim())) - { - continue; - } - SrvBillAirAllocFee weight = weights.get(fee.getBillCode()); - if (weight == null) - { - continue; - } - fee.setActualWeight(weight.getActualWeight()); - fee.setBandBoxWeight(weight.getBandBoxWeight()); - } - } - /** 提单号去重、去空白,并限制单次 5 条。 */ private List normalize(List billCodes) { @@ -193,11 +147,11 @@ public class SrvBillAirAllocServiceImpl implements ISrvBillAirAllocService } if (codes.isEmpty()) { - throw new ServiceException("请勾选提单"); + throw PaymentMesEnum.BMS_PAY_PICK_LAD.exception(); } if (codes.size() > MAX_BILLS) { - throw new ServiceException("单次最多处理5条提单"); + throw PaymentMesEnum.BMS_PAY_LAD_LIMIT.exception(); } for (String code : codes) { @@ -266,28 +220,13 @@ public class SrvBillAirAllocServiceImpl implements ISrvBillAirAllocService { return "提单缺少主键"; } - if (!DIM_ORDER.equals(fee.getAllocDim()) && !DIM_WEIGHT.equals(fee.getAllocDim())) - { - return "未选择分摊维度"; - } if (orders == null || orders.isEmpty()) { return "提单下没有关联订单"; } - if (DIM_WEIGHT.equals(fee.getAllocDim())) + if (totalWeight(orders).signum() <= 0) { - if (fee.getActualWeight() == null || fee.getActualWeight().signum() <= 0) - { - return "提单没有实际重量,无法按重量分摊"; - } - if (fee.getBandBoxWeight() == null || fee.getBandBoxWeight().signum() <= 0) - { - return "干线没有打板重量,无法按重量分摊"; - } - if (totalWeight(orders).signum() <= 0) - { - return "订单没有可用重量,无法按重量分摊"; - } + return "订单没有可用重量,无法按重量分摊"; } return ""; } @@ -298,8 +237,9 @@ public class SrvBillAirAllocServiceImpl implements ISrvBillAirAllocService row.setFeeId(UUID.randomUUID().toString()); row.setBillId(order.getBillId()); row.setPayBillId(payBillIds.get(order.getBillId())); - row.setFeeType(FEE_TYPE); - row.setFeeName(blankName(fee)); + String name = blankName(fee); + row.setFeeType(name.length() <= 30 ? name : name.substring(0, 30)); + row.setFeeName(name); row.setScId(fee.getId() == null ? 0L : fee.getId()); row.setFeeLevel(fee.getFeeLevel()); row.setAmount(amount); @@ -319,7 +259,7 @@ public class SrvBillAirAllocServiceImpl implements ISrvBillAirAllocService return 0; } int rows = batch.size(); - payBillFeeMapper.insertBatch(batch); + payBillFeeService.insertBatchAndRefreshTotals(new ArrayList<>(batch)); batch.clear(); return rows; } @@ -345,28 +285,12 @@ public class SrvBillAirAllocServiceImpl implements ISrvBillAirAllocService } /** - * 按重量分摊时,先算出这一票提单实际可分摊的金额。 - * 百分比 = 提单实际重量 srv_bill_air.actual_weight / 干线打板重量 srv_bill_air_flight.band_box_weight。 - * 可分摊金额 = 费用价格 × 百分比,保留两位,四舍五入。 - * 调用前须已确认实际重量和打板重量都大于 0。 - * - * @param fee 待分摊的提单费用,带有实际重量和打板重量 - * @return 按百分比算出的可分摊金额 - */ - private static BigDecimal weightAmount(SrvBillAirAllocFee fee) - { - BigDecimal price = fee.getPrice().setScale(2, RoundingMode.HALF_UP); - return price.multiply(fee.getActualWeight()).divide(fee.getBandBoxWeight(), 2, RoundingMode.HALF_UP); - } - - /** - * 把金额拆到每一票订单,结果保留两位。 - * 按订单数量:每一票先向下取整,差额补在最后一票,合计等于费用价格。 - * 按重量:传入的金额已是「费用价格 × 提单实际重量 / 干线打板重量」。 - * 再按订单重量占这些订单总重量的比例分摊;前面的订单向下取整,差额补在最后一票有重量的订单上,合计等于这笔金额。 + * 把费用价格拆到每一票订单,结果保留两位,合计等于费用价格。 + * 按订单数量:每一票先向下取整,差额补在最后一票。 + * 按重量:按订单重量占这些订单总重量的比例分摊。前面的订单向下取整,差额补在最后一票有重量的订单上。 * 订单重量优先取计费重,没有计费重时取实际重量,再没有取收货重量。 * - * @param price 本次要拆开的金额。按重量时是百分比计算后的可分摊金额 + * @param price 费用价格,全额分摊 * @param orders 该提单下的订单 * @param byWeight true 按订单重量占比,false 按订单数量均分 * @return 与订单顺序一致的分摊金额 diff --git a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/SrvBillAirFeeServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/SrvBillAirFeeServiceImpl.java index fa957dda..e5783b1f 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/SrvBillAirFeeServiceImpl.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/SrvBillAirFeeServiceImpl.java @@ -1,5 +1,6 @@ package com.bms.price.service.impl; +import java.io.InputStream; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Date; @@ -12,19 +13,27 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.alibaba.fastjson2.JSON; +import com.bms.common.annotation.DataScope; import com.bms.common.core.domain.entity.SysDictData; import com.bms.common.core.domain.entity.SysUser; +import com.bms.common.enums.PaymentMesEnum; import com.bms.common.exception.ServiceException; import com.bms.common.utils.DateUtils; import com.bms.common.utils.PageUtils; import com.bms.common.utils.SecurityUtils; +import com.bms.common.utils.poi.ExcelUtil; import com.bms.price.airbill.domain.SrvBillAirFee; +import com.bms.price.airbill.domain.SrvBillAirFeeImportRow; +import com.bms.price.airbill.domain.SrvBillAirRoleFeeCommand; +import com.bms.price.airbill.domain.SrvBillAirRoleFeeQuery; import com.bms.price.airbill.domain.SrvBillAirFeeLog; import com.bms.price.airbill.domain.SrvBillAirFeeSave; import com.bms.price.domain.PrcSurcharge; import com.bms.price.mapper.PrcPayBillFeeMapper; +import com.bms.price.mapper.PrcSurchargeMapper; import com.bms.price.mapper.SrvBillAirFeeMapper; import com.bms.price.mapper.SrvBillAirMapper; +import com.bms.price.service.IPrcPayBillFeeService; import com.bms.price.service.IPrcSurchargeService; import com.bms.price.service.ISrvBillAirFeeService; import com.bms.system.service.ISysDictTypeService; @@ -49,15 +58,24 @@ public class SrvBillAirFeeServiceImpl implements ISrvBillAirFeeService private static final String CURRENCY_DICT = "bms_currency"; + /** 三个费用页的权限字符,用来匹配角色上的数据范围 */ + private static final String ROLE_FEE_PERMS = "price:airFee:air,price:airFee:declare,price:airFee:clear"; + @Autowired private SrvBillAirFeeMapper feeMapper; @Autowired private PrcPayBillFeeMapper payBillFeeMapper; + @Autowired + private IPrcPayBillFeeService payBillFeeService; + @Autowired private IPrcSurchargeService surchargeService; + @Autowired + private PrcSurchargeMapper surchargeMapper; + @Autowired private SrvBillAirMapper billMapper; @@ -71,6 +89,142 @@ public class SrvBillAirFeeServiceImpl implements ISrvBillAirFeeService return feeMapper.selectByBillCode(billCode.trim()); } + @Override + @DataScope(deptAlias = "su", deptField = "dept_id", userAlias = "su", userField = "user_id", permission = ROLE_FEE_PERMS) + public List listRoleFees(SrvBillAirRoleFeeQuery query) + { + if (query == null) + { + return List.of(); + } + String normalized = normalizeScene(query.getScene()); + requireScenePerm(normalized); + if (query.getBillCodes() == null || query.getBillCodes().isEmpty()) + { + return List.of(); + } + query.setFeeScene(sceneCode(normalized)); + return feeMapper.selectRoleFees(query); + } + + @Override + @DataScope(deptAlias = "su", deptField = "dept_id", userAlias = "su", userField = "user_id", permission = ROLE_FEE_PERMS) + @Transactional(rollbackFor = Exception.class) + public String saveRole(SrvBillAirRoleFeeCommand command) + { + if (command == null || command.getBills() == null || command.getBills().isEmpty()) + { + throw PaymentMesEnum.BMS_PAY_NO_CHANGE.exception(); + } + String scene = normalizeScene(command.getScene()); + requireScenePerm(scene); + String level = levelOf(scene); + String code = sceneCode(scene); + command.setFeeScene(code); + int saved = 0; + for (SrvBillAirFeeSave bill : command.getBills()) + { + if (bill == null) + { + continue; + } + requireVisible(command, bill); + stamp(bill.getInserts(), level, code); + stamp(bill.getUpdates(), level, code); + save(bill); + saved++; + } + if (saved == 0) + { + throw PaymentMesEnum.BMS_PAY_NO_CHANGE.exception(); + } + return "已保存 " + saved + " 个提单的费用"; + } + + @Override + public List parseImport(String scene, InputStream input) throws Exception + { + String normalized = normalizeScene(scene); + requireScenePerm(normalized); + String level = levelOf(normalized); + ExcelUtil excel = new ExcelUtil<>(SrvBillAirFeeImportRow.class); + List rows = excel.importExcel(input); + if (rows == null || rows.isEmpty()) + { + throw PaymentMesEnum.BMS_PAY_IMP_EMPTY.exception(); + } + Map currencies = dictLabels(CURRENCY_DICT, "币种"); + List fees = new ArrayList<>(); + int line = 1; + for (SrvBillAirFeeImportRow row : rows) + { + line++; + if (row == null || exampleRow(row) || blankImport(row)) + { + continue; + } + String name = trim(row.getFeeName()); + if (name.isEmpty()) + { + throw new ServiceException("第" + line + "行请填写费用名称"); + } + List found = surchargeMapper.selectByExactName(name); + if (found == null || found.isEmpty()) + { + throw new ServiceException("第" + line + "行费用名称不存在:" + name); + } + PrcSurcharge chosen = null; + int enabled = 0; + for (PrcSurcharge item : found) + { + if (item != null && "on".equalsIgnoreCase(trim(item.getSsCode()))) + { + enabled++; + chosen = item; + } + } + if (enabled > 1) + { + throw new ServiceException("第" + line + "行费用名称对应多条附加费:" + name); + } + if (chosen == null) + { + chosen = found.get(0); + } + if (row.getPrice() == null) + { + throw new ServiceException("第" + line + "行请填写价格"); + } + if (row.getPrice().signum() < 0) + { + throw new ServiceException("第" + line + "行价格不能小于0"); + } + String currency = trim(row.getCurrency()); + if (currency.isEmpty() || !currencies.containsKey(currency)) + { + throw new ServiceException("第" + line + "行币种不在币种字典中"); + } + String remark = trim(row.getRemark()); + if (remark.length() > 500) + { + throw new ServiceException("第" + line + "行备注不能超过500字"); + } + SrvBillAirFee fee = new SrvBillAirFee(); + fee.setFeeType(String.valueOf(chosen.getScId())); + fee.setFeeName(chosen.getScName()); + fee.setFeeLevel(level); + fee.setPrice(row.getPrice()); + fee.setCurrency(currency); + fee.setRemark(remark); + fees.add(fee); + } + if (fees.isEmpty()) + { + throw PaymentMesEnum.BMS_PAY_IMP_DEMO.exception(); + } + return fees; + } + @Override public List listLogs(String billCode) { @@ -93,7 +247,7 @@ public class SrvBillAirFeeServiceImpl implements ISrvBillAirFeeService { if (command == null) { - throw new ServiceException("请提交要保存的费用"); + throw PaymentMesEnum.BMS_PAY_FEE_BODY.exception(); } String billCode = requireBill(command.getBillCode()); List inserts = command.getInserts() == null ? List.of() : command.getInserts(); @@ -101,7 +255,7 @@ public class SrvBillAirFeeServiceImpl implements ISrvBillAirFeeService List deletes = command.getDeletes() == null ? List.of() : command.getDeletes(); if (inserts.isEmpty() && updates.isEmpty() && deletes.isEmpty()) { - throw new ServiceException("没有需要保存的变更"); + throw PaymentMesEnum.BMS_PAY_NO_CHANGE.exception(); } Map levelLabels = dictLabels(LEVEL_DICT, "费用阶段"); Map allocLabels = dictLabels(ALLOC_DICT, "是否分摊"); @@ -118,20 +272,20 @@ public class SrvBillAirFeeServiceImpl implements ISrvBillAirFeeService { if (id == null) { - throw new ServiceException("要删除的费用缺少编号"); + throw PaymentMesEnum.BMS_PAY_DEL_ID.exception(); } if (!seen.add(id)) { - throw new ServiceException("同一条费用不能重复提交"); + throw PaymentMesEnum.BMS_PAY_FEE_DUP.exception(); } SrvBillAirFee current = liveFee(id, billCode); if (current == null) { - throw new ServiceException("费用已删除或不属于该提单"); + throw PaymentMesEnum.BMS_PAY_FEE_GONE.exception(); } if (locked(current)) { - throw new ServiceException("已分摊或已确定的费用不能删除,请使用作废"); + throw PaymentMesEnum.BMS_PAY_FEE_LOCK.exception(); } int rows = feeMapper.deleteFee(current.getId(), billCode, operator.account, now); if (rows > 0) @@ -144,20 +298,24 @@ public class SrvBillAirFeeServiceImpl implements ISrvBillAirFeeService { if (incoming == null || incoming.getId() == null) { - throw new ServiceException("要修改的费用缺少编号"); + throw PaymentMesEnum.BMS_PAY_EDIT_ID.exception(); } if (!seen.add(incoming.getId())) { - throw new ServiceException("同一条费用不能重复提交"); + throw PaymentMesEnum.BMS_PAY_FEE_DUP.exception(); } SrvBillAirFee current = liveFee(incoming.getId(), billCode); if (current == null) { - throw new ServiceException("费用已删除或不属于该提单"); + throw PaymentMesEnum.BMS_PAY_FEE_GONE.exception(); } if (locked(current)) { - throw new ServiceException("已分摊或已确定的费用不能修改"); + throw PaymentMesEnum.BMS_PAY_FEE_EDIT.exception(); + } + if (blank(incoming.getFeeLevel())) + { + incoming.setFeeLevel(current.getFeeLevel()); } SrvBillAirFee next = normalize(incoming, levelLabels, allocLabels, dimLabels, currencyLabels); next.setBillFeeStatus(current.getBillFeeStatus() == null || current.getBillFeeStatus().isBlank() ? "0" : current.getBillFeeStatus()); @@ -198,7 +356,7 @@ public class SrvBillAirFeeServiceImpl implements ISrvBillAirFeeService } if (inserted + updated + removed == 0) { - throw new ServiceException("没有需要保存的变更"); + throw PaymentMesEnum.BMS_PAY_NO_CHANGE.exception(); } return "已保存:新增 " + inserted + " 条,修改 " + updated + " 条,删除 " + removed + " 条"; } @@ -210,22 +368,22 @@ public class SrvBillAirFeeServiceImpl implements ISrvBillAirFeeService String code = requireBill(billCode); if (feeId == null) { - throw new ServiceException("请选择要确定的费用"); + throw PaymentMesEnum.BMS_PAY_PICK_OK.exception(); } SrvBillAirFee current = liveFee(feeId, code); if (current == null) { - throw new ServiceException("费用已删除或不属于该提单"); + throw PaymentMesEnum.BMS_PAY_FEE_GONE.exception(); } if (!STATUS_ALLOCATED.equals(current.getBillFeeStatus())) { - throw new ServiceException("只有已分摊的费用可以确定"); + throw PaymentMesEnum.BMS_PAY_OK_STAT.exception(); } Operator operator = operator(); Date now = DateUtils.getNowDate(); if (feeMapper.confirmFee(feeId, code, operator.account, now) != 1) { - throw new ServiceException("费用状态已变化,请刷新后再确定"); + throw PaymentMesEnum.BMS_PAY_OK_CHG.exception(); } writePlainLog(code, feeId, SrvBillAirFeeLog.CONFIRM, "确定费用", operator, now); return "费用已确定"; @@ -238,33 +396,25 @@ public class SrvBillAirFeeServiceImpl implements ISrvBillAirFeeService String code = requireBill(billCode); if (feeId == null) { - throw new ServiceException("请选择要作废的费用"); + throw PaymentMesEnum.BMS_PAY_PICK_VOID.exception(); } SrvBillAirFee current = liveFee(feeId, code); if (current == null) { - throw new ServiceException("费用已删除或不属于该提单"); + throw PaymentMesEnum.BMS_PAY_FEE_GONE.exception(); } if (!locked(current)) { - throw new ServiceException("只有已分摊或已确定的费用可以作废"); - } - String billedOrderNo = payBillFeeMapper.selectBilledOrderNo(feeId); - if (billedOrderNo != null) - { - String orderNo = billedOrderNo.trim(); - if (orderNo.isEmpty()) - { - orderNo = "未知订单"; - } - throw new ServiceException("该费用存在单笔或多笔订单已出账单-如:" + orderNo + ",不可作废"); + throw PaymentMesEnum.BMS_PAY_VOID_STAT.exception(); } Operator operator = operator(); Date now = DateUtils.getNowDate(); + List affectedBills = payBillFeeMapper.selectBillIdsByLadingFee(feeId); payBillFeeMapper.markLadingFeeDeleted(feeId); + payBillFeeService.refreshTotals(affectedBills); if (feeMapper.voidFee(feeId, code, operator.account, now) != 1) { - throw new ServiceException("费用状态已变化,请刷新后再作废"); + throw PaymentMesEnum.BMS_PAY_VOID_CHG.exception(); } Map levelLabels = dictLabels(LEVEL_DICT, "费用阶段"); Map allocLabels = dictLabels(ALLOC_DICT, "是否分摊"); @@ -276,6 +426,118 @@ public class SrvBillAirFeeServiceImpl implements ISrvBillAirFeeService return "费用已作废"; } + private static void stamp(List rows, String feeLevel, String feeScene) + { + if (rows == null) + { + return; + } + for (SrvBillAirFee row : rows) + { + if (row == null) + { + continue; + } + row.setFeeLevel(feeLevel); + row.setFeeScene(feeScene); + row.setAllocFlag("1"); + row.setAllocDim("2"); + } + } + + /** 修改和删除必须落在当前角色的数据权限内。新增由当前用户创建,不在这里拦截。 */ + private void requireVisible(SrvBillAirRoleFeeCommand command, SrvBillAirFeeSave bill) + { + Object rawScope = command.getParams().get("dataScope"); + if (rawScope == null || String.valueOf(rawScope).isBlank()) + { + return; + } + if (bill.getBillCode() == null || bill.getBillCode().isBlank()) + { + return; + } + List ids = new ArrayList<>(); + if (bill.getDeletes() != null) + { + ids.addAll(bill.getDeletes()); + } + if (bill.getUpdates() != null) + { + for (SrvBillAirFee fee : bill.getUpdates()) + { + if (fee != null) + { + ids.add(fee.getId()); + } + } + } + for (Long id : ids) + { + if (id == null) + { + continue; + } + if (feeMapper.selectRoleFeeVisible(id, bill.getBillCode(), command) == null) + { + throw PaymentMesEnum.BMS_PAY_NO_PERM.exception(); + } + } + } + + /** 空运对应字典 b 段,报关和清关对应字典 c 段。 */ + private static String levelOf(String scene) + { + return "air".equals(scene) ? "2" : "3"; + } + + private static String sceneCode(String scene) + { + if ("air".equals(scene)) + { + return "AIR"; + } + if ("declare".equals(scene)) + { + return "DECLARE"; + } + return "CLEAR"; + } + + private static String normalizeScene(String scene) + { + String value = scene == null ? "" : scene.trim().toLowerCase(); + if (!"air".equals(value) && !"declare".equals(value) && !"clear".equals(value)) + { + throw PaymentMesEnum.BMS_PAY_FEE_ENTRY.exception(); + } + return value; + } + + private static void requireScenePerm(String scene) + { + String perm = "air".equals(scene) ? "price:airFee:air" + : "declare".equals(scene) ? "price:airFee:declare" : "price:airFee:clear"; + if (!SecurityUtils.hasPermi(perm)) + { + throw PaymentMesEnum.BMS_PAY_FORBIDDEN.exception(); + } + } + + private static boolean exampleRow(SrvBillAirFeeImportRow row) + { + return "燃油附加费".equals(trim(row.getFeeName())) + && row.getPrice() != null + && row.getPrice().compareTo(new BigDecimal("1000")) == 0 + && "EUR".equalsIgnoreCase(trim(row.getCurrency())) + && "导入时需清空这行示例数据".equals(trim(row.getRemark())); + } + + private static boolean blankImport(SrvBillAirFeeImportRow row) + { + return blank(row.getFeeName()) && row.getPrice() == null && blank(row.getCurrency()) && blank(row.getRemark()); + } + private SrvBillAirFee liveFee(Long id, String billCode) { return feeMapper.selectLive(id, billCode); @@ -292,73 +554,53 @@ public class SrvBillAirFeeServiceImpl implements ISrvBillAirFeeService { String feeType = trim(raw.getFeeType()); String feeLevel = trim(raw.getFeeLevel()); - String allocFlag = trim(raw.getAllocFlag()); - String allocDim = trim(raw.getAllocDim()); String currency = trim(raw.getCurrency()); String remark = trim(raw.getRemark()); Long scId = parseScId(feeType); if (scId == null) { - throw new ServiceException("请选择费用项"); + throw PaymentMesEnum.BMS_PAY_PICK_ITEM.exception(); } PrcSurcharge surcharge = surchargeService.selectPrcSurchargeById(scId); if (surcharge == null) { - throw new ServiceException("费用项不存在"); + throw PaymentMesEnum.BMS_PAY_ITEM_MISS.exception(); } feeType = String.valueOf(scId); - if (allocFlag.isEmpty() || !allocLabels.containsKey(allocFlag)) - { - throw new ServiceException("请选择是否分摊"); - } if (!feeLevel.isEmpty() && (feeLevel.length() > 32 || !levelLabels.containsKey(feeLevel))) { - throw new ServiceException("费用阶段不在字典中"); - } - if ("1".equals(allocFlag) && feeLevel.isEmpty()) - { - throw new ServiceException("如需分摊费用至订单,请选择费用所属阶段"); - } - if ("1".equals(allocFlag)) - { - if (allocDim.isEmpty() || !dimLabels.containsKey(allocDim)) - { - throw new ServiceException("如需分摊费用至订单,请选择分摊维度"); - } - } - else - { - allocDim = ""; + throw PaymentMesEnum.BMS_PAY_LEVEL.exception(); } if (raw.getPrice() == null) { - throw new ServiceException("请填写价格"); + throw PaymentMesEnum.BMS_PAY_PRICE.exception(); } if (raw.getPrice().signum() < 0) { - throw new ServiceException("价格不能小于0"); + throw PaymentMesEnum.BMS_PAY_PRICE_NEG.exception(); } if (raw.getPrice().precision() - raw.getPrice().scale() > 16) { - throw new ServiceException("价格整数部分不能超过16位"); + throw PaymentMesEnum.BMS_PAY_PRICE_LEN.exception(); } if (currency.isEmpty()) { - throw new ServiceException("请选择收费币种"); + throw PaymentMesEnum.BMS_PAY_CUR_PICK.exception(); } if (currency.length() > 16 || !currencyLabels.containsKey(currency)) { - throw new ServiceException("收费币种不在币种字典中"); + throw PaymentMesEnum.BMS_PAY_CUR_DICT.exception(); } if (remark.length() > 500) { - throw new ServiceException("备注不能超过500字"); + throw PaymentMesEnum.BMS_PAY_REMARK.exception(); } SrvBillAirFee fee = new SrvBillAirFee(); fee.setFeeType(feeType); fee.setFeeLevel(feeLevel); - fee.setAllocFlag(allocFlag); - fee.setAllocDim(allocDim); + fee.setFeeScene(trim(raw.getFeeScene())); + fee.setAllocFlag("1"); + fee.setAllocDim("2"); fee.setPrice(raw.getPrice()); fee.setCurrency(currency); fee.setRemark(remark); @@ -420,14 +662,6 @@ public class SrvBillAirFeeServiceImpl implements ISrvBillAirFeeService List changes = new ArrayList<>(); addChange(changes, "feeType", "费用项", label(feeLabels, value(before, SrvBillAirFee::getFeeType)), label(feeLabels, value(after, SrvBillAirFee::getFeeType))); - addChange(changes, "feeLevel", "费用阶段", label(levelLabels, value(before, SrvBillAirFee::getFeeLevel)), - label(levelLabels, value(after, SrvBillAirFee::getFeeLevel))); - addChange(changes, "allocFlag", "是否分摊", label(allocLabels, value(before, SrvBillAirFee::getAllocFlag)), - label(allocLabels, value(after, SrvBillAirFee::getAllocFlag))); - addChange(changes, "allocDim", "分摊维度(票)", label(dimLabels, value(before, SrvBillAirFee::getAllocDim)), - label(dimLabels, value(after, SrvBillAirFee::getAllocDim))); - addChange(changes, "billFeeStatus", "费用状态", label(statusLabels, value(before, SrvBillAirFee::getBillFeeStatus)), - label(statusLabels, value(after, SrvBillAirFee::getBillFeeStatus))); addChange(changes, "price", "价格", priceText(before), priceText(after)); addChange(changes, "currency", "收费币种", label(currencyLabels, value(before, SrvBillAirFee::getCurrency)), label(currencyLabels, value(after, SrvBillAirFee::getCurrency))); @@ -486,12 +720,12 @@ public class SrvBillAirFeeServiceImpl implements ISrvBillAirFeeService { if (billCode == null || billCode.isBlank()) { - throw new ServiceException("提单号不能为空"); + throw PaymentMesEnum.BMS_PAY_LAD_BLANK.exception(); } String code = billCode.trim(); if (billMapper.selectSrvBillAirByBillCode(code) == null) { - throw new ServiceException("提单不存在"); + throw PaymentMesEnum.BMS_PAY_LAD_MISS.exception(); } return code; } diff --git a/bms-back/bms-system/src/main/resources/mapper/price/PrcOrderBillMapper.xml b/bms-back/bms-system/src/main/resources/mapper/price/PrcOrderBillMapper.xml index 15036583..a7083686 100644 --- a/bms-back/bms-system/src/main/resources/mapper/price/PrcOrderBillMapper.xml +++ b/bms-back/bms-system/src/main/resources/mapper/price/PrcOrderBillMapper.xml @@ -949,6 +949,22 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" where bill_id = #{billId} + + update prc_order_bill o + left join ( + select bill_id, ifnull(sum(amount), 0) as total + from prc_pay_bill_fee + where ifnull(is_deleted, '0') = '0' + and bill_id in + #{billId} + group by bill_id + ) s on s.bill_id = o.bill_id + set o.pay_total = ifnull(s.total, 0), + o.update_time = sysdate() + where o.bill_id in + #{billId} + + update prc_order_bill set surcharge_amount = #{surchargeAmount}, diff --git a/bms-back/bms-system/src/main/resources/mapper/price/PrcPayBillFeeMapper.xml b/bms-back/bms-system/src/main/resources/mapper/price/PrcPayBillFeeMapper.xml index adf50145..626bfd7b 100644 --- a/bms-back/bms-system/src/main/resources/mapper/price/PrcPayBillFeeMapper.xml +++ b/bms-back/bms-system/src/main/resources/mapper/price/PrcPayBillFeeMapper.xml @@ -65,6 +65,25 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" limit 1 + + update prc_pay_bill_fee f + inner join prc_pay_bill b on b.bill_id = f.bill_id + set f.pay_bill_id = b.pay_bill_id + where (f.pay_bill_id is null or f.pay_bill_id = '') + and f.bill_id in + #{billId} + + + + update prc_pay_bill_fee set is_deleted = '1' diff --git a/bms-back/bms-system/src/main/resources/mapper/price/PrcPayBillMapper.xml b/bms-back/bms-system/src/main/resources/mapper/price/PrcPayBillMapper.xml index 6902c4b6..60d134be 100644 --- a/bms-back/bms-system/src/main/resources/mapper/price/PrcPayBillMapper.xml +++ b/bms-back/bms-system/src/main/resources/mapper/price/PrcPayBillMapper.xml @@ -96,6 +96,39 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" update_time = sysdate() + + insert into prc_pay_bill ( + pay_bill_id, bill_id, currency, pay_total, + belong_saler, belong_saler_code, settlement_man, settlement_man_code, + create_by, create_time, update_by, update_time + ) + select replace(uuid(), '-', ''), o.bill_id, ifnull(nullif(o.currency, ''), 'CNY'), 0, + o.belong_saler, o.belong_saler_code, o.settlement_man, o.settlement_man_code, + #{operator}, sysdate(), #{operator}, sysdate() + from prc_order_bill o + where o.bill_id in + #{billId} + and not exists ( + select 1 from prc_pay_bill b where b.bill_id = o.bill_id + ) + + + + update prc_pay_bill b + left join ( + select bill_id, ifnull(sum(amount), 0) as total + from prc_pay_bill_fee + where ifnull(is_deleted, '0') = '0' + and bill_id in + #{billId} + group by bill_id + ) s on s.bill_id = b.bill_id + set b.pay_total = ifnull(s.total, 0), + b.update_time = sysdate() + where b.bill_id in + #{billId} + + delete from prc_pay_bill where bill_id = #{billId} diff --git a/bms-back/bms-system/src/main/resources/mapper/price/PrcSurchargeMapper.xml b/bms-back/bms-system/src/main/resources/mapper/price/PrcSurchargeMapper.xml index 1799d99b..d7787673 100644 --- a/bms-back/bms-system/src/main/resources/mapper/price/PrcSurchargeMapper.xml +++ b/bms-back/bms-system/src/main/resources/mapper/price/PrcSurchargeMapper.xml @@ -35,6 +35,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" and ifnull(is_del, '0') = '0' + + - - + + + AND EXISTS ( + SELECT 1 FROM sys_user su + WHERE su.del_flag = '0' + AND su.user_name COLLATE utf8mb4_general_ci = f.create_by COLLATE utf8mb4_general_ci + ${params.dataScope} + ) + + + + + + +