From 66d2b33db5def9c56b6f1d1765a94379e82b7323 Mon Sep 17 00:00:00 2001 From: wangyu <823267011@qq.com> Date: Sat, 27 Jun 2026 10:40:54 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E9=87=87=E8=B4=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/purchase-plan-ddl.sql | 60 +++- .../controller/ErpPurchasePlanController.java | 22 +- .../ErpSupplierTransactionController.java | 67 ++++ .../com/klp/erp/domain/ErpPurchasePlan.java | 8 +- .../erp/domain/ErpPurchasePlanDelivery.java | 6 + .../domain/ErpPurchasePlanDeliveryBatch.java | 50 +++ .../erp/domain/ErpSupplierTransaction.java | 50 +++ .../domain/bo/ErpSupplierTransactionBo.java | 39 ++ .../vo/ErpPurchasePlanDeliveryBatchVo.java | 38 ++ .../domain/vo/ErpPurchasePlanDeliveryVo.java | 5 + .../klp/erp/domain/vo/ErpPurchasePlanVo.java | 11 +- .../domain/vo/ErpSupplierTransactionVo.java | 43 +++ .../ErpPurchasePlanDeliveryBatchMapper.java | 13 + .../klp/erp/mapper/ErpPurchasePlanMapper.java | 7 + .../mapper/ErpSupplierTransactionMapper.java | 13 + .../erp/service/IErpPurchasePlanService.java | 14 +- .../IErpSupplierTransactionService.java | 28 ++ .../impl/ErpPurchasePlanServiceImpl.java | 246 +++++++------ .../ErpSupplierTransactionServiceImpl.java | 118 +++++++ .../mapper/erp/ErpPurchasePlanMapper.xml | 11 + klp-ui/src/api/erp/purchase.js | 40 +++ klp-ui/src/api/erp/purchasePlan.js | 25 ++ .../src/views/erp/purchaseDelivery/index.vue | 332 ++++++++++++++++++ klp-ui/src/views/erp/purchasePlan/index.vue | 98 +----- klp-ui/src/views/erp/supplier/index.vue | 144 +++++++- 25 files changed, 1261 insertions(+), 227 deletions(-) create mode 100644 klp-erp/src/main/java/com/klp/erp/controller/ErpSupplierTransactionController.java create mode 100644 klp-erp/src/main/java/com/klp/erp/domain/ErpPurchasePlanDeliveryBatch.java create mode 100644 klp-erp/src/main/java/com/klp/erp/domain/ErpSupplierTransaction.java create mode 100644 klp-erp/src/main/java/com/klp/erp/domain/bo/ErpSupplierTransactionBo.java create mode 100644 klp-erp/src/main/java/com/klp/erp/domain/vo/ErpPurchasePlanDeliveryBatchVo.java create mode 100644 klp-erp/src/main/java/com/klp/erp/domain/vo/ErpSupplierTransactionVo.java create mode 100644 klp-erp/src/main/java/com/klp/erp/mapper/ErpPurchasePlanDeliveryBatchMapper.java create mode 100644 klp-erp/src/main/java/com/klp/erp/mapper/ErpSupplierTransactionMapper.java create mode 100644 klp-erp/src/main/java/com/klp/erp/service/IErpSupplierTransactionService.java create mode 100644 klp-erp/src/main/java/com/klp/erp/service/impl/ErpSupplierTransactionServiceImpl.java create mode 100644 klp-ui/src/views/erp/purchaseDelivery/index.vue diff --git a/docs/purchase-plan-ddl.sql b/docs/purchase-plan-ddl.sql index 7e247894d..8303a3db3 100644 --- a/docs/purchase-plan-ddl.sql +++ b/docs/purchase-plan-ddl.sql @@ -92,6 +92,62 @@ CREATE TABLE IF NOT EXISTS `erp_purchase_plan_delivery` ( KEY `idx_coil_no` (`coil_no`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='采购计划到货明细'; +-- 5. 到货上传批次(每上传一份到货表格记一条,便于随时回看每一次上传) +CREATE TABLE IF NOT EXISTS `erp_purchase_plan_delivery_batch` ( + `batch_id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '批次ID', + `plan_id` BIGINT NOT NULL COMMENT '关联计划ID', + `file_name` VARCHAR(255) DEFAULT NULL COMMENT '文件名', + `row_count` INT DEFAULT 0 COMMENT '本次到货行数', + `matched_count` INT DEFAULT 0 COMMENT '本次匹配上明细卷号的卷数', + `arrived_percent` DECIMAL(6,2) DEFAULT 0 COMMENT '上传后到货百分比快照', + `del_flag` CHAR(1) NOT NULL DEFAULT '0' COMMENT '删除标志', + `create_by` VARCHAR(64) DEFAULT NULL, + `create_time` DATETIME DEFAULT NULL, + `update_by` VARCHAR(64) DEFAULT NULL, + `update_time` DATETIME DEFAULT NULL, + `remark` VARCHAR(512) DEFAULT NULL, + PRIMARY KEY (`batch_id`), + KEY `idx_plan_id` (`plan_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='采购计划到货上传批次'; + +-- 6. 供应商往来流水台账 +CREATE TABLE IF NOT EXISTS `erp_supplier_transaction` ( + `txn_id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '往来流水ID', + `supplier_id` BIGINT NOT NULL COMMENT '关联供应商ID', + `txn_date` DATE DEFAULT NULL COMMENT '发生日期', + `txn_type` CHAR(1) NOT NULL DEFAULT '1' COMMENT '类型: 1-采购应付 2-付款 3-退货 4-其他', + `amount` DECIMAL(16,2) NOT NULL DEFAULT 0 COMMENT '金额', + `doc_no` VARCHAR(64) DEFAULT NULL COMMENT '单据号', + `del_flag` CHAR(1) NOT NULL DEFAULT '0' COMMENT '删除标志', + `create_by` VARCHAR(64) DEFAULT NULL, + `create_time` DATETIME DEFAULT NULL, + `update_by` VARCHAR(64) DEFAULT NULL, + `update_time` DATETIME DEFAULT NULL, + `remark` VARCHAR(512) DEFAULT NULL, + PRIMARY KEY (`txn_id`), + KEY `idx_supplier_id` (`supplier_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='供应商往来流水台账'; + +-- ====== 既有表增列(幂等:列不存在才加) ====== +SET @c := (SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'erp_purchase_plan_delivery' AND COLUMN_NAME = 'batch_id'); +SET @s := IF(@c = 0, 'ALTER TABLE erp_purchase_plan_delivery ADD COLUMN batch_id BIGINT NULL COMMENT ''关联上传批次ID'' AFTER plan_id', 'SELECT 1'); +PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +-- 到货行是否 WMS 钢卷表已确认到货(supplier_coil_no 存在且 data_type<>10) +SET @c := (SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'erp_purchase_plan_delivery' AND COLUMN_NAME = 'arrived'); +SET @s := IF(@c = 0, 'ALTER TABLE erp_purchase_plan_delivery ADD COLUMN arrived TINYINT NOT NULL DEFAULT 0 COMMENT ''是否WMS确认到货:1是0否'' AFTER batch_id', 'SELECT 1'); +PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +-- 计划要求总数量(卷/件) = Σ明细数量 +SET @c := (SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'erp_purchase_plan' AND COLUMN_NAME = 'plan_qty'); +SET @s := IF(@c = 0, 'ALTER TABLE erp_purchase_plan ADD COLUMN plan_qty INT NOT NULL DEFAULT 0 COMMENT ''计划要求总数量(卷/件)'' AFTER arrived_weight', 'SELECT 1'); +PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +-- 已到货卷数 = WMS确认到货的上传卷数 +SET @c := (SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'erp_purchase_plan' AND COLUMN_NAME = 'arrived_count'); +SET @s := IF(@c = 0, 'ALTER TABLE erp_purchase_plan ADD COLUMN arrived_count INT NOT NULL DEFAULT 0 COMMENT ''已到货卷数'' AFTER plan_qty', 'SELECT 1'); +PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt; + -- ============ 菜单 ============ -- 采购目录挂在「生产辅助」(path=helper) 下;父 id 按 path 动态解析,兼容不同环境的不同 menu_id。 @@ -126,6 +182,6 @@ REPLACE INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component (2100000000000000010, '采购审核', @purchase_id, 3, 'purchaseAudit', 'erp/purchaseAudit/index', '', 1, 0, 'C', '0', '0', 'erp:purchasePlan:auditList', 'validCode', 'admin', sysdate(), '采购审核菜单'), (2100000000000000011, '审核操作', 2100000000000000010, 1, '', '', '', 1, 0, 'F', '0', '0', 'erp:purchasePlan:audit', '#', 'admin', sysdate(), ''); --- 7) 采购进度(计划级到货进度总览) +-- 7) 到货记录(审核通过的计划上传到货表格、按卷号比对、到货百分比) REPLACE INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark) VALUES -(2100000000000000020, '采购进度', @purchase_id, 4, 'purchaseProgress', 'erp/purchaseProgress/index', '', 1, 0, 'C', '0', '0', 'erp:purchasePlan:list', 'data-line', 'admin', sysdate(), '采购进度总览'); +(2100000000000000020, '到货记录', @purchase_id, 4, 'purchaseDelivery', 'erp/purchaseDelivery/index', '', 1, 0, 'C', '0', '0', 'erp:purchasePlan:list', 'truck', 'admin', sysdate(), '到货记录与卷号比对'); diff --git a/klp-erp/src/main/java/com/klp/erp/controller/ErpPurchasePlanController.java b/klp-erp/src/main/java/com/klp/erp/controller/ErpPurchasePlanController.java index 65c37af9e..ef3437367 100644 --- a/klp-erp/src/main/java/com/klp/erp/controller/ErpPurchasePlanController.java +++ b/klp-erp/src/main/java/com/klp/erp/controller/ErpPurchasePlanController.java @@ -13,6 +13,7 @@ import com.klp.common.utils.poi.ExcelUtil; import com.klp.erp.domain.bo.ErpPurchasePlanAuditBo; import com.klp.erp.domain.bo.ErpPurchasePlanBo; import com.klp.erp.domain.vo.ErpContractOptionVo; +import com.klp.erp.domain.vo.ErpPurchasePlanDeliveryBatchVo; import com.klp.erp.domain.vo.ErpPurchasePlanDeliveryVo; import com.klp.erp.domain.vo.ErpPurchasePlanItemVo; import com.klp.erp.domain.vo.ErpPurchasePlanVo; @@ -131,7 +132,7 @@ public class ErpPurchasePlanController extends BaseController { @PostMapping(value = "/{planId}/importDelivery", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public R> importDelivery(@PathVariable Long planId, @RequestPart("file") MultipartFile file) throws Exception { - Map result = iErpPurchasePlanService.importDelivery(planId, file.getInputStream(), getUsername()); + Map result = iErpPurchasePlanService.importDelivery(planId, file.getInputStream(), file.getOriginalFilename(), getUsername()); return R.ok(String.valueOf(result.get("message")), result); } @@ -141,6 +142,25 @@ public class ErpPurchasePlanController extends BaseController { return R.ok(iErpPurchasePlanService.queryDeliveryList(planId)); } + /** 某计划的到货上传批次(每次上传一条,可回看) */ + @GetMapping("/{planId}/deliveryBatches") + public R> deliveryBatches(@PathVariable Long planId) { + return R.ok(iErpPurchasePlanService.queryDeliveryBatches(planId)); + } + + /** 某批次的到货明细 */ + @GetMapping("/deliveryBatch/{batchId}") + public R> deliveryByBatch(@PathVariable Long batchId) { + return R.ok(iErpPurchasePlanService.queryDeliveryListByBatch(batchId)); + } + + /** 到货记录页左侧:审核通过的计划分页 */ + @GetMapping("/deliveryPlans") + public TableDataInfo deliveryPlans(@RequestParam(value = "keyword", required = false) String keyword, + PageQuery pageQuery) { + return iErpPurchasePlanService.queryDeliveryPlanPage(keyword, pageQuery); + } + /** 删除到货明细 */ @Log(title = "采购计划-到货", businessType = BusinessType.DELETE) @DeleteMapping("/delivery/{deliveryId}") diff --git a/klp-erp/src/main/java/com/klp/erp/controller/ErpSupplierTransactionController.java b/klp-erp/src/main/java/com/klp/erp/controller/ErpSupplierTransactionController.java new file mode 100644 index 000000000..19822f5c2 --- /dev/null +++ b/klp-erp/src/main/java/com/klp/erp/controller/ErpSupplierTransactionController.java @@ -0,0 +1,67 @@ +package com.klp.erp.controller; + +import com.klp.common.annotation.Log; +import com.klp.common.annotation.RepeatSubmit; +import com.klp.common.core.controller.BaseController; +import com.klp.common.core.domain.R; +import com.klp.common.core.validate.AddGroup; +import com.klp.common.core.validate.EditGroup; +import com.klp.common.enums.BusinessType; +import com.klp.erp.domain.bo.ErpSupplierTransactionBo; +import com.klp.erp.domain.vo.ErpSupplierTransactionVo; +import com.klp.erp.service.IErpSupplierTransactionService; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * 供应商往来流水 + * + * @author klp + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/erp/supplierTxn") +public class ErpSupplierTransactionController extends BaseController { + + private final IErpSupplierTransactionService txnService; + + /** 某供应商的往来流水(含逐笔余额) */ + @GetMapping("/list") + public R> list(@RequestParam("supplierId") Long supplierId) { + return R.ok(txnService.queryBySupplier(supplierId)); + } + + /** 某供应商的往来汇总 */ + @GetMapping("/summary") + public R> summary(@RequestParam("supplierId") Long supplierId) { + return R.ok(txnService.summary(supplierId)); + } + + @Log(title = "供应商往来", businessType = BusinessType.INSERT) + @RepeatSubmit + @PostMapping + public R add(@Validated(AddGroup.class) @RequestBody ErpSupplierTransactionBo bo) { + return toAjax(txnService.insertByBo(bo)); + } + + @Log(title = "供应商往来", businessType = BusinessType.UPDATE) + @RepeatSubmit + @PutMapping + public R edit(@Validated(EditGroup.class) @RequestBody ErpSupplierTransactionBo bo) { + return toAjax(txnService.updateByBo(bo)); + } + + @Log(title = "供应商往来", businessType = BusinessType.DELETE) + @DeleteMapping("/{txnIds}") + public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] txnIds) { + return toAjax(txnService.deleteByIds(Arrays.asList(txnIds))); + } +} diff --git a/klp-erp/src/main/java/com/klp/erp/domain/ErpPurchasePlan.java b/klp-erp/src/main/java/com/klp/erp/domain/ErpPurchasePlan.java index 21119fb5a..066002d47 100644 --- a/klp-erp/src/main/java/com/klp/erp/domain/ErpPurchasePlan.java +++ b/klp-erp/src/main/java/com/klp/erp/domain/ErpPurchasePlan.java @@ -54,9 +54,15 @@ public class ErpPurchasePlan extends BaseEntity { /** 计划总重量(T) */ private BigDecimal planWeight; - /** 已到货重量(T) */ + /** 已到货重量(T) = Σ到货卷(WMS已确认)单卷重量 */ private BigDecimal arrivedWeight; + /** 计划要求总数量(卷/件) = Σ明细数量 */ + private Integer planQty; + + /** 已到货卷数 = WMS确认到货的上传卷数 */ + private Integer arrivedCount; + /** 删除标志 */ @TableLogic private String delFlag; diff --git a/klp-erp/src/main/java/com/klp/erp/domain/ErpPurchasePlanDelivery.java b/klp-erp/src/main/java/com/klp/erp/domain/ErpPurchasePlanDelivery.java index 27b65be77..54e304fc3 100644 --- a/klp-erp/src/main/java/com/klp/erp/domain/ErpPurchasePlanDelivery.java +++ b/klp-erp/src/main/java/com/klp/erp/domain/ErpPurchasePlanDelivery.java @@ -30,6 +30,12 @@ public class ErpPurchasePlanDelivery extends BaseEntity { /** 关联计划ID */ private Long planId; + /** 关联上传批次ID */ + private Long batchId; + + /** 是否WMS已确认到货: 1-是 0-否(卷号在钢卷表查不到或为占位类型) */ + private Integer arrived; + /** 日期 */ private Date arrivalDate; diff --git a/klp-erp/src/main/java/com/klp/erp/domain/ErpPurchasePlanDeliveryBatch.java b/klp-erp/src/main/java/com/klp/erp/domain/ErpPurchasePlanDeliveryBatch.java new file mode 100644 index 000000000..1a6381805 --- /dev/null +++ b/klp-erp/src/main/java/com/klp/erp/domain/ErpPurchasePlanDeliveryBatch.java @@ -0,0 +1,50 @@ +package com.klp.erp.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import com.klp.common.core.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.math.BigDecimal; + +/** + * 到货上传批次对象 erp_purchase_plan_delivery_batch + * 每上传一份到货 Excel 记一条批次,便于随时回看每一次上传。 + * + * @author klp + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("erp_purchase_plan_delivery_batch") +public class ErpPurchasePlanDeliveryBatch extends BaseEntity { + + private static final long serialVersionUID = 1L; + + /** 批次ID */ + @TableId(value = "batch_id") + private Long batchId; + + /** 关联计划ID */ + private Long planId; + + /** 文件名 */ + private String fileName; + + /** 本次导入到货行数 */ + private Integer rowCount; + + /** 本次与明细卷号匹配上的卷数 */ + private Integer matchedCount; + + /** 上传后计划到货百分比快照(0-100) */ + private BigDecimal arrivedPercent; + + /** 删除标志 */ + @TableLogic + private String delFlag; + + /** 备注 */ + private String remark; +} diff --git a/klp-erp/src/main/java/com/klp/erp/domain/ErpSupplierTransaction.java b/klp-erp/src/main/java/com/klp/erp/domain/ErpSupplierTransaction.java new file mode 100644 index 000000000..2af1a7fb0 --- /dev/null +++ b/klp-erp/src/main/java/com/klp/erp/domain/ErpSupplierTransaction.java @@ -0,0 +1,50 @@ +package com.klp.erp.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import com.klp.common.core.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 供应商往来流水对象 erp_supplier_transaction + * + * @author klp + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("erp_supplier_transaction") +public class ErpSupplierTransaction extends BaseEntity { + + private static final long serialVersionUID = 1L; + + /** 往来流水ID */ + @TableId(value = "txn_id") + private Long txnId; + + /** 关联供应商ID */ + private Long supplierId; + + /** 发生日期 */ + private Date txnDate; + + /** 类型: 1-采购应付 2-付款 3-退货 4-其他 */ + private String txnType; + + /** 金额 */ + private BigDecimal amount; + + /** 单据号 */ + private String docNo; + + /** 删除标志 */ + @TableLogic + private String delFlag; + + /** 备注 */ + private String remark; +} diff --git a/klp-erp/src/main/java/com/klp/erp/domain/bo/ErpSupplierTransactionBo.java b/klp-erp/src/main/java/com/klp/erp/domain/bo/ErpSupplierTransactionBo.java new file mode 100644 index 000000000..142713baf --- /dev/null +++ b/klp-erp/src/main/java/com/klp/erp/domain/bo/ErpSupplierTransactionBo.java @@ -0,0 +1,39 @@ +package com.klp.erp.domain.bo; + +import com.klp.common.core.domain.BaseEntity; +import com.klp.common.core.validate.AddGroup; +import com.klp.common.core.validate.EditGroup; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.validation.constraints.NotNull; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 供应商往来流水业务对象 + * + * @author klp + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class ErpSupplierTransactionBo extends BaseEntity { + + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long txnId; + + @NotNull(message = "供应商不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long supplierId; + + private Date txnDate; + + @NotNull(message = "类型不能为空", groups = {AddGroup.class, EditGroup.class}) + private String txnType; + + @NotNull(message = "金额不能为空", groups = {AddGroup.class, EditGroup.class}) + private BigDecimal amount; + + private String docNo; + + private String remark; +} diff --git a/klp-erp/src/main/java/com/klp/erp/domain/vo/ErpPurchasePlanDeliveryBatchVo.java b/klp-erp/src/main/java/com/klp/erp/domain/vo/ErpPurchasePlanDeliveryBatchVo.java new file mode 100644 index 000000000..9e37604fb --- /dev/null +++ b/klp-erp/src/main/java/com/klp/erp/domain/vo/ErpPurchasePlanDeliveryBatchVo.java @@ -0,0 +1,38 @@ +package com.klp.erp.domain.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 到货上传批次视图对象 + * + * @author klp + */ +@Data +public class ErpPurchasePlanDeliveryBatchVo implements Serializable { + + private static final long serialVersionUID = 1L; + + private Long batchId; + + private Long planId; + + private String fileName; + + private Integer rowCount; + + private Integer matchedCount; + + private BigDecimal arrivedPercent; + + private String createBy; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + private String remark; +} diff --git a/klp-erp/src/main/java/com/klp/erp/domain/vo/ErpPurchasePlanDeliveryVo.java b/klp-erp/src/main/java/com/klp/erp/domain/vo/ErpPurchasePlanDeliveryVo.java index 2d2cb3cd9..2e0d3e459 100644 --- a/klp-erp/src/main/java/com/klp/erp/domain/vo/ErpPurchasePlanDeliveryVo.java +++ b/klp-erp/src/main/java/com/klp/erp/domain/vo/ErpPurchasePlanDeliveryVo.java @@ -26,6 +26,11 @@ public class ErpPurchasePlanDeliveryVo implements Serializable { private Long planId; + private Long batchId; + + /** 是否WMS已确认到货: 1-是 0-否 */ + private Integer arrived; + @ExcelProperty(value = "日期") @JsonFormat(pattern = "yyyy-MM-dd") private Date arrivalDate; diff --git a/klp-erp/src/main/java/com/klp/erp/domain/vo/ErpPurchasePlanVo.java b/klp-erp/src/main/java/com/klp/erp/domain/vo/ErpPurchasePlanVo.java index eefb6a076..c9ef4f786 100644 --- a/klp-erp/src/main/java/com/klp/erp/domain/vo/ErpPurchasePlanVo.java +++ b/klp-erp/src/main/java/com/klp/erp/domain/vo/ErpPurchasePlanVo.java @@ -59,12 +59,21 @@ public class ErpPurchasePlanVo implements Serializable { @ExcelProperty(value = "已到货重量(T)") private BigDecimal arrivedWeight; + /** 计划要求总数量(卷/件) */ + private Integer planQty; + + /** 已到货卷数 */ + private Integer arrivedCount; + @ExcelProperty(value = "备注") private String remark; - /** 到货进度百分比(0-100) */ + /** 到货进度-按重量(0-100) = 已到货重量/计划要求总重量 */ private BigDecimal progress; + /** 到货进度-按卷数(0-100) = 已到货卷数/计划要求总数量 */ + private BigDecimal progressQty; + /** 明细行 */ private List items; diff --git a/klp-erp/src/main/java/com/klp/erp/domain/vo/ErpSupplierTransactionVo.java b/klp-erp/src/main/java/com/klp/erp/domain/vo/ErpSupplierTransactionVo.java new file mode 100644 index 000000000..73906260a --- /dev/null +++ b/klp-erp/src/main/java/com/klp/erp/domain/vo/ErpSupplierTransactionVo.java @@ -0,0 +1,43 @@ +package com.klp.erp.domain.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 供应商往来流水视图对象 + * + * @author klp + */ +@Data +public class ErpSupplierTransactionVo implements Serializable { + + private static final long serialVersionUID = 1L; + + private Long txnId; + + private Long supplierId; + + @JsonFormat(pattern = "yyyy-MM-dd") + private Date txnDate; + + /** 类型: 1-采购应付 2-付款 3-退货 4-其他 */ + private String txnType; + + private BigDecimal amount; + + private String docNo; + + private String createBy; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + private String remark; + + /** 计算字段:截至该笔的应付余额(正数=欠供应商) */ + private BigDecimal balance; +} diff --git a/klp-erp/src/main/java/com/klp/erp/mapper/ErpPurchasePlanDeliveryBatchMapper.java b/klp-erp/src/main/java/com/klp/erp/mapper/ErpPurchasePlanDeliveryBatchMapper.java new file mode 100644 index 000000000..4ed9480e7 --- /dev/null +++ b/klp-erp/src/main/java/com/klp/erp/mapper/ErpPurchasePlanDeliveryBatchMapper.java @@ -0,0 +1,13 @@ +package com.klp.erp.mapper; + +import com.klp.common.core.mapper.BaseMapperPlus; +import com.klp.erp.domain.ErpPurchasePlanDeliveryBatch; +import com.klp.erp.domain.vo.ErpPurchasePlanDeliveryBatchVo; + +/** + * 到货上传批次Mapper接口 + * + * @author klp + */ +public interface ErpPurchasePlanDeliveryBatchMapper extends BaseMapperPlus { +} diff --git a/klp-erp/src/main/java/com/klp/erp/mapper/ErpPurchasePlanMapper.java b/klp-erp/src/main/java/com/klp/erp/mapper/ErpPurchasePlanMapper.java index 71c18b8e9..a556fcc7b 100644 --- a/klp-erp/src/main/java/com/klp/erp/mapper/ErpPurchasePlanMapper.java +++ b/klp-erp/src/main/java/com/klp/erp/mapper/ErpPurchasePlanMapper.java @@ -9,6 +9,7 @@ import com.klp.erp.domain.vo.ErpPurchasePlanItemVo; import com.klp.erp.domain.vo.ErpPurchasePlanVo; import org.apache.ibatis.annotations.Param; +import java.util.Collection; import java.util.List; /** @@ -44,4 +45,10 @@ public interface ErpPurchasePlanMapper extends BaseMapperPlus selectPlanIdsByContractKeyword(@Param("kw") String kw); + + /** + * 在 WMS 钢卷表(wms_material_coil)中查出已存在且非占位(data_type<>10)的厂家卷号集合, + * 用于判定上传到货行的卷号是否已实际到货入库。 + */ + List selectExistingSupplierCoilNos(@Param("coilNos") Collection coilNos); } diff --git a/klp-erp/src/main/java/com/klp/erp/mapper/ErpSupplierTransactionMapper.java b/klp-erp/src/main/java/com/klp/erp/mapper/ErpSupplierTransactionMapper.java new file mode 100644 index 000000000..192fa0e5d --- /dev/null +++ b/klp-erp/src/main/java/com/klp/erp/mapper/ErpSupplierTransactionMapper.java @@ -0,0 +1,13 @@ +package com.klp.erp.mapper; + +import com.klp.common.core.mapper.BaseMapperPlus; +import com.klp.erp.domain.ErpSupplierTransaction; +import com.klp.erp.domain.vo.ErpSupplierTransactionVo; + +/** + * 供应商往来流水Mapper接口 + * + * @author klp + */ +public interface ErpSupplierTransactionMapper extends BaseMapperPlus { +} diff --git a/klp-erp/src/main/java/com/klp/erp/service/IErpPurchasePlanService.java b/klp-erp/src/main/java/com/klp/erp/service/IErpPurchasePlanService.java index 9b3a586ab..f40d531ce 100644 --- a/klp-erp/src/main/java/com/klp/erp/service/IErpPurchasePlanService.java +++ b/klp-erp/src/main/java/com/klp/erp/service/IErpPurchasePlanService.java @@ -6,6 +6,7 @@ import com.klp.erp.domain.bo.ErpPurchasePlanAuditBo; import com.klp.erp.domain.bo.ErpPurchasePlanBo; import com.klp.erp.domain.vo.ErpContractOptionVo; import com.klp.erp.domain.vo.ErpPurchasePlanAuditLogVo; +import com.klp.erp.domain.vo.ErpPurchasePlanDeliveryBatchVo; import com.klp.erp.domain.vo.ErpPurchasePlanDeliveryVo; import com.klp.erp.domain.vo.ErpPurchasePlanItemVo; import com.klp.erp.domain.vo.ErpPurchasePlanVo; @@ -63,11 +64,20 @@ public interface IErpPurchasePlanService { * 导入到货 Excel:校验列/数值、kg→t 单位纠正,写入并刷新进度归档。 * 返回 {count: 入库条数, message: 回执文案, kgConverted: 是否做了单位换算} */ - Map importDelivery(Long planId, InputStream inputStream, String operator); + Map importDelivery(Long planId, InputStream inputStream, String fileName, String operator); - /** 查询某计划的到货明细 */ + /** 查询某计划的到货明细(matched 标记是否与明细卷号匹配) */ List queryDeliveryList(Long planId); + /** 某计划下指定批次的到货明细 */ + List queryDeliveryListByBatch(Long batchId); + + /** 某计划的到货上传批次(最新在前) */ + List queryDeliveryBatches(Long planId); + + /** 到货记录页左侧:审核通过的计划分页(含合同号、明细数、进度) */ + TableDataInfo queryDeliveryPlanPage(String keyword, PageQuery pageQuery); + /** 删除到货明细,并刷新进度 */ Boolean deleteDelivery(Long deliveryId); diff --git a/klp-erp/src/main/java/com/klp/erp/service/IErpSupplierTransactionService.java b/klp-erp/src/main/java/com/klp/erp/service/IErpSupplierTransactionService.java new file mode 100644 index 000000000..e9b9e777c --- /dev/null +++ b/klp-erp/src/main/java/com/klp/erp/service/IErpSupplierTransactionService.java @@ -0,0 +1,28 @@ +package com.klp.erp.service; + +import com.klp.erp.domain.bo.ErpSupplierTransactionBo; +import com.klp.erp.domain.vo.ErpSupplierTransactionVo; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 供应商往来流水Service接口 + * + * @author klp + */ +public interface IErpSupplierTransactionService { + + /** 某供应商的往来流水(含逐笔累计余额,最新在前) */ + List queryBySupplier(Long supplierId); + + /** 某供应商的往来汇总:应付合计/付款合计/退货合计/当前余额 */ + Map summary(Long supplierId); + + Boolean insertByBo(ErpSupplierTransactionBo bo); + + Boolean updateByBo(ErpSupplierTransactionBo bo); + + Boolean deleteByIds(Collection ids); +} diff --git a/klp-erp/src/main/java/com/klp/erp/service/impl/ErpPurchasePlanServiceImpl.java b/klp-erp/src/main/java/com/klp/erp/service/impl/ErpPurchasePlanServiceImpl.java index 3e7d34f1c..4e094e501 100644 --- a/klp-erp/src/main/java/com/klp/erp/service/impl/ErpPurchasePlanServiceImpl.java +++ b/klp-erp/src/main/java/com/klp/erp/service/impl/ErpPurchasePlanServiceImpl.java @@ -13,6 +13,7 @@ import com.klp.common.utils.StringUtils; import com.klp.erp.domain.ErpPurchasePlan; import com.klp.erp.domain.ErpPurchasePlanContractRel; import com.klp.erp.domain.ErpPurchasePlanDelivery; +import com.klp.erp.domain.ErpPurchasePlanDeliveryBatch; import com.klp.erp.domain.ErpPurchasePlanItem; import com.klp.erp.domain.ErpPurchasePlanAuditLog; import com.klp.erp.domain.bo.ErpPurchasePlanAuditBo; @@ -20,11 +21,13 @@ import com.klp.erp.domain.bo.ErpPurchasePlanBo; import com.klp.erp.domain.bo.ErpPurchasePlanItemBo; import com.klp.erp.domain.vo.ErpContractOptionVo; import com.klp.erp.domain.vo.ErpPurchasePlanAuditLogVo; +import com.klp.erp.domain.vo.ErpPurchasePlanDeliveryBatchVo; import com.klp.erp.domain.vo.ErpPurchasePlanDeliveryImportVo; import com.klp.erp.domain.vo.ErpPurchasePlanDeliveryVo; import com.klp.erp.domain.vo.ErpPurchasePlanItemVo; import com.klp.erp.domain.vo.ErpPurchasePlanVo; import com.klp.erp.mapper.ErpPurchasePlanContractRelMapper; +import com.klp.erp.mapper.ErpPurchasePlanDeliveryBatchMapper; import com.klp.erp.mapper.ErpPurchasePlanDeliveryMapper; import com.klp.erp.mapper.ErpPurchasePlanItemMapper; import com.klp.erp.mapper.ErpPurchasePlanAuditLogMapper; @@ -42,9 +45,10 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashMap; -import java.util.LinkedHashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; /** @@ -63,14 +67,12 @@ public class ErpPurchasePlanServiceImpl implements IErpPurchasePlanService { private static final String AUDIT_PASS = "1"; private static final String AUDIT_REJECT = "2"; private static final String AUDIT_DRAFT = "3"; - private static final String ITEM_NOT_ARRIVED = "0"; - private static final String ITEM_PARTIAL = "1"; - private static final String ITEM_ARRIVED = "2"; private final ErpPurchasePlanMapper baseMapper; private final ErpPurchasePlanItemMapper itemMapper; private final ErpPurchasePlanContractRelMapper relMapper; private final ErpPurchasePlanDeliveryMapper deliveryMapper; + private final ErpPurchasePlanDeliveryBatchMapper deliveryBatchMapper; private final ErpPurchasePlanAuditLogMapper auditLogMapper; @Override @@ -90,7 +92,7 @@ public class ErpPurchasePlanServiceImpl implements IErpPurchasePlanService { if (!orderIds.isEmpty()) { vo.setContractCodes(baseMapper.selectOrderCodes(orderIds)); } - vo.setProgress(calcProgress(vo.getArrivedWeight(), vo.getPlanWeight())); + fillProgress(vo); // 审核历史(含每次驳回理由) vo.setAuditLogs(queryAuditLogs(planId)); return vo; @@ -107,14 +109,14 @@ public class ErpPurchasePlanServiceImpl implements IErpPurchasePlanService { @Override public TableDataInfo queryPageList(ErpPurchasePlanBo bo, PageQuery pageQuery) { Page result = baseMapper.selectVoPage(pageQuery.build(), buildQueryWrapper(bo)); - result.getRecords().forEach(v -> v.setProgress(calcProgress(v.getArrivedWeight(), v.getPlanWeight()))); + result.getRecords().forEach(this::fillProgress); return TableDataInfo.build(result); } @Override public List queryList(ErpPurchasePlanBo bo) { List list = baseMapper.selectVoList(buildQueryWrapper(bo)); - list.forEach(v -> v.setProgress(calcProgress(v.getArrivedWeight(), v.getPlanWeight()))); + list.forEach(this::fillProgress); return list; } @@ -127,7 +129,7 @@ public class ErpPurchasePlanServiceImpl implements IErpPurchasePlanService { @Override public List queryPlansByContract(Long orderId) { List list = baseMapper.selectPlansByContract(orderId); - list.forEach(v -> v.setProgress(calcProgress(v.getArrivedWeight(), v.getPlanWeight()))); + list.forEach(this::fillProgress); return list; } @@ -164,6 +166,8 @@ public class ErpPurchasePlanServiceImpl implements IErpPurchasePlanService { add.setAuditStatus(AUDIT_DRAFT); // 新建为「待送审」,需手动送审后才进入审核池 add.setArrivedWeight(BigDecimal.ZERO); add.setPlanWeight(sumItemWeight(bo.getItems())); + add.setPlanQty(sumItemQty(bo.getItems())); + add.setArrivedCount(0); if (baseMapper.insert(add) <= 0) { return false; } @@ -187,6 +191,7 @@ public class ErpPurchasePlanServiceImpl implements IErpPurchasePlanService { update.setPlanNo(bo.getPlanNo()); } update.setPlanWeight(sumItemWeight(bo.getItems())); + update.setPlanQty(sumItemQty(bo.getItems())); baseMapper.updateById(update); // 覆盖式重写明细与合同关联 itemMapper.delete(Wrappers.lambdaQuery(ErpPurchasePlanItem.class) @@ -195,6 +200,8 @@ public class ErpPurchasePlanServiceImpl implements IErpPurchasePlanService { relMapper.delete(Wrappers.lambdaQuery(ErpPurchasePlanContractRel.class) .eq(ErpPurchasePlanContractRel::getPlanId, bo.getPlanId())); saveContractRels(bo.getPlanId(), bo.getOrderIds()); + // 卷号匹配可能变化,刷新到货进度 + refreshProgress(bo.getPlanId()); return true; } @@ -234,6 +241,13 @@ public class ErpPurchasePlanServiceImpl implements IErpPurchasePlanService { .reduce(BigDecimal.ZERO, BigDecimal::add); } + private int sumItemQty(List items) { + if (items == null) { + return 0; + } + return items.stream().mapToInt(i -> i.getQuantity() == null ? 0 : i.getQuantity()).sum(); + } + @Override @Transactional(rollbackFor = Exception.class) public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { @@ -298,7 +312,7 @@ public class ErpPurchasePlanServiceImpl implements IErpPurchasePlanService { @Override @Transactional(rollbackFor = Exception.class) - public Map importDelivery(Long planId, InputStream inputStream, String operator) { + public Map importDelivery(Long planId, InputStream inputStream, String fileName, String operator) { ErpPurchasePlan plan = baseMapper.selectById(planId); if (plan == null) { throw new ServiceException("采购计划不存在"); @@ -334,11 +348,28 @@ public class ErpPurchasePlanServiceImpl implements IErpPurchasePlanService { .orElse(BigDecimal.ZERO); boolean kgConverted = maxCoil.compareTo(KG_THRESHOLD) > 0; - // 3) 合并单元格向下填充 + 单位换算 + 落库 + // 3) 卷号去 WMS 钢卷表校验:存在且 data_type<>10 即视为已实际到货入库 + List uploadCoilNos = valid.stream() + .map(ErpPurchasePlanDeliveryImportVo::getCoilNo) + .filter(StringUtils::isNotBlank) + .map(String::trim) + .collect(Collectors.toList()); + Set wmsArrivedSet = uploadCoilNos.isEmpty() ? new HashSet<>() + : baseMapper.selectExistingSupplierCoilNos(uploadCoilNos).stream() + .map(this::normCoil).collect(Collectors.toSet()); + + // 4) 先建批次(每次上传存档,可随时回看) + ErpPurchasePlanDeliveryBatch batch = new ErpPurchasePlanDeliveryBatch(); + batch.setPlanId(planId); + batch.setFileName(StringUtils.isNotBlank(fileName) ? fileName : "到货表"); + deliveryBatchMapper.insert(batch); + + // 5) 合并单元格向下填充 + 单位换算 + 落库 + WMS到货标记 String lastTruckNo = null; BigDecimal lastTruckWeight = null; Integer lastPieceCount = null; int count = 0; + int matched = 0; for (ErpPurchasePlanDeliveryImportVo row : rows) { if (StringUtils.isNotBlank(row.getTruckNo())) { lastTruckNo = row.getTruckNo(); @@ -348,9 +379,12 @@ public class ErpPurchasePlanServiceImpl implements IErpPurchasePlanService { if (StringUtils.isBlank(row.getCoilNo()) && row.getCoilWeight() == null) { continue; } + boolean arrived = StringUtils.isNotBlank(row.getCoilNo()) && wmsArrivedSet.contains(normCoil(row.getCoilNo())); BigDecimal truckWeight = row.getTruckWeight() != null ? row.getTruckWeight() : lastTruckWeight; ErpPurchasePlanDelivery d = new ErpPurchasePlanDelivery(); d.setPlanId(planId); + d.setBatchId(batch.getBatchId()); + d.setArrived(arrived ? 1 : 0); d.setArrivalDate(parseDate(row.getArrivalDate())); d.setGrade(row.getGrade()); d.setSpec(row.getSpec()); @@ -363,17 +397,29 @@ public class ErpPurchasePlanServiceImpl implements IErpPurchasePlanService { d.setArrivalStation(row.getArrivalStation()); deliveryMapper.insert(d); count++; + if (arrived) { + matched++; + } } + // 6) 汇总刷新进度 refreshProgress(planId); - StringBuilder msg = new StringBuilder("成功导入 " + count + " 条到货记录"); + // 7) 回填批次统计(行数、WMS确认到货卷数、上传后进度快照) + ErpPurchasePlan after = baseMapper.selectById(planId); + batch.setRowCount(count); + batch.setMatchedCount(matched); + batch.setArrivedPercent(calcPct(after.getArrivedWeight(), after.getPlanWeight())); + deliveryBatchMapper.updateById(batch); + + StringBuilder msg = new StringBuilder("成功导入 " + count + " 条到货记录,其中 " + matched + " 卷在钢卷表确认到货"); if (kgConverted) { msg.append(";检测到重量疑似按 kg 录入(最大单卷 ") .append(maxCoil.stripTrailingZeros().toPlainString()) .append("),已自动 ÷1000 换算为吨"); } - Map result = new HashMap<>(4); + Map result = new HashMap<>(6); result.put("count", count); + result.put("matched", matched); result.put("kgConverted", kgConverted); result.put("message", msg.toString()); return result; @@ -391,7 +437,40 @@ public class ErpPurchasePlanServiceImpl implements IErpPurchasePlanService { public List queryDeliveryList(Long planId) { return deliveryMapper.selectVoList(Wrappers.lambdaQuery(ErpPurchasePlanDelivery.class) .eq(ErpPurchasePlanDelivery::getPlanId, planId) - .orderByAsc(ErpPurchasePlanDelivery::getTruckNo, ErpPurchasePlanDelivery::getDeliveryId)); + .orderByDesc(ErpPurchasePlanDelivery::getDeliveryId)); + } + + @Override + public List queryDeliveryListByBatch(Long batchId) { + return deliveryMapper.selectVoList(Wrappers.lambdaQuery(ErpPurchasePlanDelivery.class) + .eq(ErpPurchasePlanDelivery::getBatchId, batchId) + .orderByDesc(ErpPurchasePlanDelivery::getDeliveryId)); + } + + @Override + public List queryDeliveryBatches(Long planId) { + return deliveryBatchMapper.selectVoList(Wrappers.lambdaQuery(ErpPurchasePlanDeliveryBatch.class) + .eq(ErpPurchasePlanDeliveryBatch::getPlanId, planId) + .orderByDesc(ErpPurchasePlanDeliveryBatch::getBatchId)); + } + + @Override + public TableDataInfo queryDeliveryPlanPage(String keyword, PageQuery pageQuery) { + ErpPurchasePlanBo bo = new ErpPurchasePlanBo(); + bo.setKeyword(keyword); + LambdaQueryWrapper lqw = buildQueryWrapper(bo) + .eq(ErpPurchasePlan::getAuditStatus, AUDIT_PASS); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + for (ErpPurchasePlanVo v : result.getRecords()) { + fillProgress(v); + List orderIds = relMapper.selectList(Wrappers.lambdaQuery(ErpPurchasePlanContractRel.class) + .eq(ErpPurchasePlanContractRel::getPlanId, v.getPlanId())).stream() + .map(ErpPurchasePlanContractRel::getOrderId).collect(Collectors.toList()); + if (!orderIds.isEmpty()) { + v.setContractCodes(baseMapper.selectOrderCodes(orderIds)); + } + } + return TableDataInfo.build(result); } @Override @@ -413,124 +492,29 @@ public class ErpPurchasePlanServiceImpl implements IErpPurchasePlanService { if (plan == null) { return; } - // 1. 计划总到货 = Σ单卷重量 - BigDecimal arrived = deliveryMapper.sumCoilWeightByPlan(planId); - if (arrived == null) { - arrived = BigDecimal.ZERO; - } - plan.setArrivedWeight(arrived); - - // 2. 明细级回填:到货行按 牌号+规格(厚×宽) 聚合,再顺序分配到匹配明细 - List items = itemMapper.selectList(Wrappers.lambdaQuery(ErpPurchasePlanItem.class) - .eq(ErpPurchasePlanItem::getPlanId, planId)); + // 到货 = 累计所有上传批次中 WMS 已确认到货(arrived=1)的卷 List deliveries = deliveryMapper.selectList(Wrappers.lambdaQuery(ErpPurchasePlanDelivery.class) .eq(ErpPurchasePlanDelivery::getPlanId, planId)); - - Map arrivedByKey = new HashMap<>(); + int arrivedCount = 0; + BigDecimal arrivedWeight = BigDecimal.ZERO; for (ErpPurchasePlanDelivery d : deliveries) { - String key = specKey(d.getGrade(), d.getSpec()); - if (key == null) { - continue; - } - BigDecimal w = d.getCoilWeight() == null ? BigDecimal.ZERO : d.getCoilWeight(); - arrivedByKey.merge(key, w, BigDecimal::add); - } - - // 明细按 key 分组(保持原顺序),便于同规格多行顺序分配 - Map> itemsByKey = new LinkedHashMap<>(); - for (ErpPurchasePlanItem it : items) { - it.setArrivedWeight(BigDecimal.ZERO); - it.setItemStatus(ITEM_NOT_ARRIVED); - String key = itemSpecKey(it.getGrade(), it.getThickness(), it.getWidth()); - if (key != null) { - itemsByKey.computeIfAbsent(key, k -> new ArrayList<>()).add(it); + if (d.getArrived() != null && d.getArrived() == 1) { + arrivedCount++; + arrivedWeight = arrivedWeight.add(d.getCoilWeight() == null ? BigDecimal.ZERO : d.getCoilWeight()); } } - for (Map.Entry> e : itemsByKey.entrySet()) { - BigDecimal remaining = arrivedByKey.getOrDefault(e.getKey(), BigDecimal.ZERO); - List group = e.getValue(); - for (int i = 0; i < group.size(); i++) { - ErpPurchasePlanItem it = group.get(i); - BigDecimal planned = it.getWeight() == null ? BigDecimal.ZERO : it.getWeight(); - // 末项吃掉剩余(含富余),其余按计划量封顶 - BigDecimal give = (i == group.size() - 1) ? remaining : remaining.min(planned); - if (give.compareTo(BigDecimal.ZERO) < 0) { - give = BigDecimal.ZERO; - } - it.setArrivedWeight(give); - remaining = remaining.subtract(give); - if (remaining.compareTo(BigDecimal.ZERO) < 0) { - remaining = BigDecimal.ZERO; - } - } - } - - // 3. 明细状态 + 是否全部到货 - boolean allArrived = !items.isEmpty(); - for (ErpPurchasePlanItem it : items) { - BigDecimal planned = it.getWeight() == null ? BigDecimal.ZERO : it.getWeight(); - BigDecimal aw = it.getArrivedWeight() == null ? BigDecimal.ZERO : it.getArrivedWeight(); - if (aw.compareTo(BigDecimal.ZERO) <= 0) { - it.setItemStatus(ITEM_NOT_ARRIVED); - } else if (planned.compareTo(BigDecimal.ZERO) > 0 && aw.compareTo(planned) >= 0) { - it.setItemStatus(ITEM_ARRIVED); - } else { - it.setItemStatus(ITEM_PARTIAL); - } - if (!ITEM_ARRIVED.equals(it.getItemStatus())) { - allArrived = false; - } - itemMapper.updateById(it); - } - - // 4. 计划状态:所有明细到货 或 总量达标 → 自动归档 + plan.setArrivedCount(arrivedCount); + plan.setArrivedWeight(arrivedWeight); + // 到货重量达到计划要求总重量 → 自动归档 BigDecimal planWeight = plan.getPlanWeight() == null ? BigDecimal.ZERO : plan.getPlanWeight(); - boolean weightDone = planWeight.compareTo(BigDecimal.ZERO) > 0 && arrived.compareTo(planWeight) >= 0; - plan.setPlanStatus((allArrived || weightDone) ? PLAN_STATUS_ARCHIVED : PLAN_STATUS_ONGOING); + boolean done = planWeight.compareTo(BigDecimal.ZERO) > 0 && arrivedWeight.compareTo(planWeight) >= 0; + plan.setPlanStatus(done ? PLAN_STATUS_ARCHIVED : PLAN_STATUS_ONGOING); baseMapper.updateById(plan); } - /** 到货行规格 key:牌号 + 厚×宽(规格形如 "3.00×1230") */ - private String specKey(String grade, String spec) { - if (StringUtils.isBlank(spec)) { - return null; - } - String[] parts = spec.split("[×xX*]"); - if (parts.length < 2) { - return null; - } - String t = normNum(parts[0]); - String w = normNum(parts[1]); - if (t == null || w == null) { - return null; - } - return normGrade(grade) + "|" + t + "×" + w; - } - - /** 明细规格 key:牌号 + 厚×宽 */ - private String itemSpecKey(String grade, String thickness, String width) { - String t = normNum(thickness); - String w = normNum(width); - if (t == null || w == null) { - return null; - } - return normGrade(grade) + "|" + t + "×" + w; - } - - private String normGrade(String g) { - return g == null ? "" : g.trim().toUpperCase(); - } - - /** 数字归一:去尾零,无法解析(区间文本等)返回 null 不参与匹配 */ - private String normNum(String s) { - if (s == null) { - return null; - } - try { - return new BigDecimal(s.trim()).stripTrailingZeros().toPlainString(); - } catch (Exception e) { - return null; - } + /** 卷号归一:去空白转大写,便于匹配(DB 校对集已大小写不敏感,这里再兜底) */ + private String normCoil(String s) { + return s == null ? "" : s.trim().toUpperCase(); } @Override @@ -545,13 +529,21 @@ public class ErpPurchasePlanServiceImpl implements IErpPurchasePlanService { return map; } - /** 进度百分比(0-100,保留2位) */ - private BigDecimal calcProgress(BigDecimal arrived, BigDecimal planWeight) { - if (planWeight == null || planWeight.compareTo(BigDecimal.ZERO) <= 0) { + /** 同时填充按重量、按卷数两个到货百分比 */ + private void fillProgress(ErpPurchasePlanVo v) { + v.setProgress(calcPct(v.getArrivedWeight(), v.getPlanWeight())); + v.setProgressQty(calcPct( + v.getArrivedCount() == null ? null : BigDecimal.valueOf(v.getArrivedCount()), + v.getPlanQty() == null ? null : BigDecimal.valueOf(v.getPlanQty()))); + } + + /** 百分比(0-100,保留2位) = num/den,封顶100 */ + private BigDecimal calcPct(BigDecimal num, BigDecimal den) { + if (den == null || den.compareTo(BigDecimal.ZERO) <= 0) { return BigDecimal.ZERO; } - BigDecimal a = arrived == null ? BigDecimal.ZERO : arrived; - BigDecimal pct = a.multiply(BigDecimal.valueOf(100)).divide(planWeight, 2, RoundingMode.HALF_UP); + BigDecimal n = num == null ? BigDecimal.ZERO : num; + BigDecimal pct = n.multiply(BigDecimal.valueOf(100)).divide(den, 2, RoundingMode.HALF_UP); return pct.min(BigDecimal.valueOf(100)); } diff --git a/klp-erp/src/main/java/com/klp/erp/service/impl/ErpSupplierTransactionServiceImpl.java b/klp-erp/src/main/java/com/klp/erp/service/impl/ErpSupplierTransactionServiceImpl.java new file mode 100644 index 000000000..2b4543426 --- /dev/null +++ b/klp-erp/src/main/java/com/klp/erp/service/impl/ErpSupplierTransactionServiceImpl.java @@ -0,0 +1,118 @@ +package com.klp.erp.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.klp.erp.domain.ErpSupplierTransaction; +import com.klp.erp.domain.bo.ErpSupplierTransactionBo; +import com.klp.erp.domain.vo.ErpSupplierTransactionVo; +import com.klp.erp.mapper.ErpSupplierTransactionMapper; +import com.klp.erp.service.IErpSupplierTransactionService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 供应商往来流水Service业务层处理 + * + * @author klp + */ +@RequiredArgsConstructor +@Service +public class ErpSupplierTransactionServiceImpl implements IErpSupplierTransactionService { + + /** 类型常量 */ + private static final String TYPE_PAYABLE = "1"; // 采购应付 + private static final String TYPE_PAY = "2"; // 付款 + private static final String TYPE_RETURN = "3"; // 退货 + private static final String TYPE_OTHER = "4"; // 其他 + + private final ErpSupplierTransactionMapper baseMapper; + + @Override + public List queryBySupplier(Long supplierId) { + if (supplierId == null) { + return new ArrayList<>(); + } + // 按发生时间升序计算逐笔累计余额,再倒序返回(最新在前) + List asc = baseMapper.selectVoList(Wrappers.lambdaQuery(ErpSupplierTransaction.class) + .eq(ErpSupplierTransaction::getSupplierId, supplierId) + .orderByAsc(ErpSupplierTransaction::getTxnDate) + .orderByAsc(ErpSupplierTransaction::getTxnId)); + BigDecimal running = BigDecimal.ZERO; + for (ErpSupplierTransactionVo v : asc) { + running = running.add(signedAmount(v.getTxnType(), v.getAmount())); + v.setBalance(running); + } + Collections.reverse(asc); + return asc; + } + + @Override + public Map summary(Long supplierId) { + Map map = new HashMap<>(8); + BigDecimal payable = BigDecimal.ZERO; + BigDecimal paid = BigDecimal.ZERO; + BigDecimal returned = BigDecimal.ZERO; + BigDecimal other = BigDecimal.ZERO; + BigDecimal balance = BigDecimal.ZERO; + if (supplierId != null) { + List list = baseMapper.selectVoList(Wrappers.lambdaQuery(ErpSupplierTransaction.class) + .eq(ErpSupplierTransaction::getSupplierId, supplierId)); + for (ErpSupplierTransactionVo v : list) { + BigDecimal amt = v.getAmount() == null ? BigDecimal.ZERO : v.getAmount(); + if (TYPE_PAYABLE.equals(v.getTxnType())) { + payable = payable.add(amt); + } else if (TYPE_PAY.equals(v.getTxnType())) { + paid = paid.add(amt); + } else if (TYPE_RETURN.equals(v.getTxnType())) { + returned = returned.add(amt); + } else { + other = other.add(amt); + } + balance = balance.add(signedAmount(v.getTxnType(), amt)); + } + } + map.put("payable", payable); + map.put("paid", paid); + map.put("returned", returned); + map.put("other", other); + map.put("balance", balance); + return map; + } + + /** 付款/退货为负方向,其余为正方向 */ + private BigDecimal signedAmount(String type, BigDecimal amount) { + BigDecimal amt = amount == null ? BigDecimal.ZERO : amount; + if (TYPE_PAY.equals(type) || TYPE_RETURN.equals(type)) { + return amt.negate(); + } + return amt; + } + + @Override + public Boolean insertByBo(ErpSupplierTransactionBo bo) { + ErpSupplierTransaction add = BeanUtil.toBean(bo, ErpSupplierTransaction.class); + return baseMapper.insert(add) > 0; + } + + @Override + public Boolean updateByBo(ErpSupplierTransactionBo bo) { + ErpSupplierTransaction update = BeanUtil.toBean(bo, ErpSupplierTransaction.class); + return baseMapper.updateById(update) > 0; + } + + @Override + public Boolean deleteByIds(Collection ids) { + if (ids == null || ids.isEmpty()) { + return false; + } + return baseMapper.deleteBatchIds(ids) > 0; + } +} diff --git a/klp-erp/src/main/resources/mapper/erp/ErpPurchasePlanMapper.xml b/klp-erp/src/main/resources/mapper/erp/ErpPurchasePlanMapper.xml index 42c805b26..2fbeaed64 100644 --- a/klp-erp/src/main/resources/mapper/erp/ErpPurchasePlanMapper.xml +++ b/klp-erp/src/main/resources/mapper/erp/ErpPurchasePlanMapper.xml @@ -66,6 +66,17 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" ORDER BY p.plan_id DESC + + +