销售发货
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
package com.gear.oa.controller;
|
||||
|
||||
import com.gear.common.annotation.Log;
|
||||
import com.gear.common.annotation.RepeatSubmit;
|
||||
import com.gear.common.core.controller.BaseController;
|
||||
import com.gear.common.core.domain.PageQuery;
|
||||
import com.gear.common.core.domain.R;
|
||||
import com.gear.common.core.page.TableDataInfo;
|
||||
import com.gear.common.core.validate.EditGroup;
|
||||
import com.gear.common.enums.BusinessType;
|
||||
import com.gear.oa.domain.bo.GearOrderProductionBo;
|
||||
import com.gear.oa.domain.vo.GearOrderProductionVo;
|
||||
import com.gear.oa.service.IGearOrderProductionService;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 订单生产记录
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/oa/orderProduction")
|
||||
public class GearOrderProductionController extends BaseController {
|
||||
|
||||
private final IGearOrderProductionService iGearOrderProductionService;
|
||||
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<GearOrderProductionVo> list(GearOrderProductionBo bo, PageQuery pageQuery) {
|
||||
return iGearOrderProductionService.queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
@GetMapping("/{productionId}")
|
||||
public R<GearOrderProductionVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long productionId) {
|
||||
return R.ok(iGearOrderProductionService.queryById(productionId));
|
||||
}
|
||||
|
||||
@Log(title = "订单生产记录", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping()
|
||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody GearOrderProductionBo bo) {
|
||||
return toAjax(iGearOrderProductionService.updateByBo(bo));
|
||||
}
|
||||
|
||||
@Log(title = "订单生产记录", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{productionIds}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] productionIds) {
|
||||
return toAjax(iGearOrderProductionService.deleteWithValidByIds(Arrays.asList(productionIds), true));
|
||||
}
|
||||
|
||||
@Log(title = "订单生产记录", businessType = BusinessType.OTHER)
|
||||
@RepeatSubmit()
|
||||
@PostMapping("/init/{orderId}")
|
||||
public R<Void> init(@NotNull(message = "订单ID不能为空") @PathVariable Long orderId) {
|
||||
return toAjax(iGearOrderProductionService.initByOrderId(orderId));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
package com.gear.oa.controller;
|
||||
|
||||
import com.gear.common.annotation.Log;
|
||||
import com.gear.common.annotation.RepeatSubmit;
|
||||
import com.gear.common.core.controller.BaseController;
|
||||
import com.gear.common.core.domain.PageQuery;
|
||||
import com.gear.common.core.domain.R;
|
||||
import com.gear.common.core.page.TableDataInfo;
|
||||
import com.gear.common.core.validate.AddGroup;
|
||||
import com.gear.common.core.validate.EditGroup;
|
||||
import com.gear.common.enums.BusinessType;
|
||||
import com.gear.common.utils.poi.ExcelUtil;
|
||||
import com.gear.oa.domain.bo.GearSalesmanBo;
|
||||
import com.gear.oa.domain.vo.GearCustomerVo;
|
||||
import com.gear.oa.domain.vo.GearSalesmanVo;
|
||||
import com.gear.oa.service.IGearSalesmanService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 销售员管理
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/oa/salesman")
|
||||
public class GearSalesmanController extends BaseController {
|
||||
|
||||
private final IGearSalesmanService iGearSalesmanService;
|
||||
|
||||
/**
|
||||
* 查询销售员列表
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<GearSalesmanVo> list(GearSalesmanBo bo, PageQuery pageQuery) {
|
||||
return iGearSalesmanService.queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出销售员列表
|
||||
*/
|
||||
@Log(title = "销售员", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(GearSalesmanBo bo, HttpServletResponse response) {
|
||||
List<GearSalesmanVo> list = iGearSalesmanService.queryList(bo);
|
||||
ExcelUtil.exportExcel(list, "销售员", GearSalesmanVo.class, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取销售员详情
|
||||
*/
|
||||
@GetMapping("/{salesmanId}")
|
||||
public R<GearSalesmanVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long salesmanId) {
|
||||
return R.ok(iGearSalesmanService.queryById(salesmanId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增销售员
|
||||
*/
|
||||
@Log(title = "销售员", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping()
|
||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody GearSalesmanBo bo) {
|
||||
return toAjax(iGearSalesmanService.insertByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改销售员
|
||||
*/
|
||||
@Log(title = "销售员", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping()
|
||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody GearSalesmanBo bo) {
|
||||
return toAjax(iGearSalesmanService.updateByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除销售员
|
||||
*/
|
||||
@Log(title = "销售员", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{salesmanIds}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] salesmanIds) {
|
||||
return toAjax(iGearSalesmanService.deleteWithValidByIds(Arrays.asList(salesmanIds), true));
|
||||
}
|
||||
|
||||
/**
|
||||
* 跟进客户:按销售员反查客户列表
|
||||
*/
|
||||
@GetMapping("/{salesmanId}/customers")
|
||||
public TableDataInfo<GearCustomerVo> customers(@NotNull(message = "主键不能为空") @PathVariable Long salesmanId,
|
||||
PageQuery pageQuery) {
|
||||
return iGearSalesmanService.queryFollowCustomers(salesmanId, pageQuery);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
package com.gear.oa.controller;
|
||||
|
||||
import com.gear.common.annotation.Log;
|
||||
import com.gear.common.annotation.RepeatSubmit;
|
||||
import com.gear.common.core.controller.BaseController;
|
||||
import com.gear.common.core.domain.PageQuery;
|
||||
import com.gear.common.core.domain.R;
|
||||
import com.gear.common.core.page.TableDataInfo;
|
||||
import com.gear.common.core.validate.AddGroup;
|
||||
import com.gear.common.core.validate.EditGroup;
|
||||
import com.gear.common.enums.BusinessType;
|
||||
import com.gear.common.utils.poi.ExcelUtil;
|
||||
import com.gear.oa.domain.bo.GearShippingOrderBo;
|
||||
import com.gear.oa.domain.vo.GearShippingOrderVo;
|
||||
import com.gear.oa.service.IGearShippingOrderService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 发货单据
|
||||
*
|
||||
* 说明:
|
||||
* - 本模块写入 gear_shipping_order(独立表)
|
||||
* - 用于订单发货/物流信息记录,不与出入库/WMS 绑定
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/oa/shippingOrder")
|
||||
public class GearShippingOrderController extends BaseController {
|
||||
|
||||
private final IGearShippingOrderService iGearShippingOrderService;
|
||||
|
||||
/**
|
||||
* 查询发货单据列表
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<GearShippingOrderVo> list(GearShippingOrderBo bo, PageQuery pageQuery) {
|
||||
return iGearShippingOrderService.queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据订单ID查询发货单据列表
|
||||
*/
|
||||
@GetMapping("/listByOrderId/{orderId}")
|
||||
public R<List<GearShippingOrderVo>> listByOrderId(@PathVariable Long orderId) {
|
||||
return R.ok(iGearShippingOrderService.queryListByOrderId(orderId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出发货单据列表
|
||||
*/
|
||||
@Log(title = "发货单据", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(GearShippingOrderBo bo, HttpServletResponse response) {
|
||||
List<GearShippingOrderVo> list = iGearShippingOrderService.queryList(bo);
|
||||
ExcelUtil.exportExcel(list, "发货单据", GearShippingOrderVo.class, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取发货单据详细信息
|
||||
*/
|
||||
@GetMapping("/{shippingId}")
|
||||
public R<GearShippingOrderVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long shippingId) {
|
||||
return R.ok(iGearShippingOrderService.queryById(shippingId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增发货单据
|
||||
*/
|
||||
@Log(title = "发货单据", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping()
|
||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody GearShippingOrderBo bo) {
|
||||
return toAjax(iGearShippingOrderService.insertByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改发货单据
|
||||
*/
|
||||
@Log(title = "发货单据", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping()
|
||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody GearShippingOrderBo bo) {
|
||||
return toAjax(iGearShippingOrderService.updateByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除发货单据
|
||||
*/
|
||||
@Log(title = "发货单据", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{shippingIds}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] shippingIds) {
|
||||
return toAjax(iGearShippingOrderService.deleteWithValidByIds(Arrays.asList(shippingIds), true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.gear.oa.controller;
|
||||
|
||||
import com.gear.common.annotation.Log;
|
||||
import com.gear.common.annotation.RepeatSubmit;
|
||||
import com.gear.common.core.controller.BaseController;
|
||||
import com.gear.common.core.domain.PageQuery;
|
||||
import com.gear.common.core.domain.R;
|
||||
import com.gear.common.core.page.TableDataInfo;
|
||||
import com.gear.common.core.validate.AddGroup;
|
||||
import com.gear.common.core.validate.EditGroup;
|
||||
import com.gear.common.enums.BusinessType;
|
||||
import com.gear.common.utils.poi.ExcelUtil;
|
||||
import com.gear.oa.domain.bo.GearShippingPlanBo;
|
||||
import com.gear.oa.domain.vo.GearShippingPlanVo;
|
||||
import com.gear.oa.service.IGearShippingPlanService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 发货计划
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/oa/shippingPlan")
|
||||
public class GearShippingPlanController extends BaseController {
|
||||
|
||||
private final IGearShippingPlanService iGearShippingPlanService;
|
||||
|
||||
/**
|
||||
* 查询发货计划列表
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<GearShippingPlanVo> list(GearShippingPlanBo bo, PageQuery pageQuery) {
|
||||
return iGearShippingPlanService.queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询发货计划列表(带单据数)
|
||||
*/
|
||||
@GetMapping("/listWithCount")
|
||||
public R<List<GearShippingPlanVo>> listWithCount(GearShippingPlanBo bo) {
|
||||
return R.ok(iGearShippingPlanService.queryListWithCount(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出发货计划列表
|
||||
*/
|
||||
@Log(title = "发货计划", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(GearShippingPlanBo bo, HttpServletResponse response) {
|
||||
List<GearShippingPlanVo> list = iGearShippingPlanService.queryList(bo);
|
||||
ExcelUtil.exportExcel(list, "发货计划", GearShippingPlanVo.class, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取发货计划详细信息
|
||||
*/
|
||||
@GetMapping("/{planId}")
|
||||
public R<GearShippingPlanVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long planId) {
|
||||
return R.ok(iGearShippingPlanService.queryById(planId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增发货计划
|
||||
*/
|
||||
@Log(title = "发货计划", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping()
|
||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody GearShippingPlanBo bo) {
|
||||
return toAjax(iGearShippingPlanService.insertByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改发货计划
|
||||
*/
|
||||
@Log(title = "发货计划", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping()
|
||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody GearShippingPlanBo bo) {
|
||||
return toAjax(iGearShippingPlanService.updateByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除发货计划
|
||||
*/
|
||||
@Log(title = "发货计划", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{planIds}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] planIds) {
|
||||
return toAjax(iGearShippingPlanService.deleteWithValidByIds(Arrays.asList(planIds), true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 库存:原材料/产品与库区/库位的存放关系
|
||||
@@ -45,6 +47,12 @@ public class GearStockController extends BaseController {
|
||||
return iGearStockService.queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
@PostMapping("/sumQuantityByItemIds")
|
||||
public R<Map<Long, BigDecimal>> sumQuantityByItemIds(@RequestParam(defaultValue = "product") String itemType,
|
||||
@RequestBody List<Long> itemIds) {
|
||||
return R.ok(iGearStockService.sumQuantityByItemIds(itemType, itemIds));
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出库存:原材料/产品与库区/库位的存放关系列表
|
||||
*/
|
||||
|
||||
@@ -44,6 +44,12 @@ public class GearOrder extends BaseEntity {
|
||||
* 销售经理
|
||||
*/
|
||||
private String salesManager;
|
||||
|
||||
/**
|
||||
* 销售员ID(挂接销售员管理)
|
||||
*/
|
||||
@TableField("salesman_id")
|
||||
private Long salesmanId;
|
||||
/**
|
||||
* 订单状态(0=新建,1=生产中,2=已完成,3=已取消)
|
||||
*/
|
||||
@@ -70,4 +76,10 @@ public class GearOrder extends BaseEntity {
|
||||
*/
|
||||
private BigDecimal noTaxAmount;
|
||||
|
||||
/**
|
||||
* 合同Excel附件ossId列表(逗号分隔)
|
||||
*/
|
||||
@TableField("contract_excel_oss_ids")
|
||||
private String contractExcelOssIds;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.gear.oa.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.gear.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 订单生产记录对象 gear_order_production
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("gear_order_production")
|
||||
public class GearOrderProduction extends BaseEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(value = "production_id")
|
||||
private Long productionId;
|
||||
|
||||
private Long orderId;
|
||||
|
||||
private Long orderDetailId;
|
||||
|
||||
private Long productId;
|
||||
|
||||
private BigDecimal planQty;
|
||||
|
||||
private BigDecimal finishedQty;
|
||||
|
||||
private BigDecimal badQty;
|
||||
|
||||
private String unit;
|
||||
|
||||
private String remark;
|
||||
|
||||
@TableLogic(value = "0", delval = "2")
|
||||
private String delFlag;
|
||||
}
|
||||
|
||||
52
gear-oa/src/main/java/com/gear/oa/domain/GearSalesman.java
Normal file
52
gear-oa/src/main/java/com/gear/oa/domain/GearSalesman.java
Normal file
@@ -0,0 +1,52 @@
|
||||
package com.gear.oa.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.gear.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 销售员对象 gear_salesman
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("gear_salesman")
|
||||
public class GearSalesman extends BaseEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 销售员ID
|
||||
*/
|
||||
@TableId(value = "salesman_id")
|
||||
private Long salesmanId;
|
||||
|
||||
/**
|
||||
* 销售员姓名
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 联系电话
|
||||
*/
|
||||
private String mobile;
|
||||
|
||||
/**
|
||||
* 状态(0正常 1停用)
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 删除标志(0=正常,1=已删除)
|
||||
*/
|
||||
@TableLogic
|
||||
private Integer delFlag;
|
||||
}
|
||||
|
||||
117
gear-oa/src/main/java/com/gear/oa/domain/GearShippingOrder.java
Normal file
117
gear-oa/src/main/java/com/gear/oa/domain/GearShippingOrder.java
Normal file
@@ -0,0 +1,117 @@
|
||||
package com.gear.oa.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.gear.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 发货单据对象 gear_shipping_order
|
||||
*
|
||||
* 说明:
|
||||
* - 用于记录订单发货信息(物流公司/运单号/收货信息等)
|
||||
* - 与出入库/WMS 单据无关,避免与库存模块耦合
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("gear_shipping_order")
|
||||
public class GearShippingOrder extends BaseEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 发货单据ID
|
||||
*/
|
||||
@TableId(value = "shipping_id")
|
||||
private Long shippingId;
|
||||
|
||||
/**
|
||||
* 发货单号(唯一)
|
||||
*/
|
||||
private String shippingNo;
|
||||
|
||||
/**
|
||||
* 发货单名称(展示用)
|
||||
*/
|
||||
private String shippingName;
|
||||
|
||||
/**
|
||||
* 发货计划ID
|
||||
*/
|
||||
private Long planId;
|
||||
|
||||
/**
|
||||
* 关联订单ID
|
||||
*/
|
||||
private Long orderId;
|
||||
|
||||
/**
|
||||
* 订单编号快照
|
||||
*/
|
||||
private String orderCode;
|
||||
|
||||
/**
|
||||
* 收货单位
|
||||
*/
|
||||
private String receiverCompany;
|
||||
|
||||
/**
|
||||
* 收货客户ID(真实归属客户)
|
||||
*/
|
||||
private Long receiverCustomerId;
|
||||
|
||||
/**
|
||||
* 发货时间
|
||||
*/
|
||||
private Date shipTime;
|
||||
|
||||
/**
|
||||
* 负责人
|
||||
*/
|
||||
private String responsibleName;
|
||||
|
||||
/**
|
||||
* 物流公司
|
||||
*/
|
||||
private String logisticsCompany;
|
||||
|
||||
/**
|
||||
* 运单号
|
||||
*/
|
||||
private String logisticsNo;
|
||||
|
||||
/**
|
||||
* 收货人
|
||||
*/
|
||||
private String receiverName;
|
||||
|
||||
/**
|
||||
* 收货电话
|
||||
*/
|
||||
private String receiverPhone;
|
||||
|
||||
/**
|
||||
* 收货地址
|
||||
*/
|
||||
private String receiverAddress;
|
||||
|
||||
/**
|
||||
* 完成状态(0未发货 1已打印 2已发货 3已完成)
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 删除标志(0存在 2删除)
|
||||
*/
|
||||
@TableLogic(value = "0", delval = "2")
|
||||
private String delFlag;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.gear.oa.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.gear.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 发货计划对象 gear_shipping_plan
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("gear_shipping_plan")
|
||||
public class GearShippingPlan extends BaseEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 发货计划ID
|
||||
*/
|
||||
@TableId(value = "plan_id")
|
||||
private Long planId;
|
||||
|
||||
/**
|
||||
* 计划名称
|
||||
*/
|
||||
private String planName;
|
||||
|
||||
/**
|
||||
* 计划日期
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
|
||||
private Date planDate;
|
||||
|
||||
/**
|
||||
* 状态(0未完成 1已完成)
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 删除标志(0存在 2删除)
|
||||
*/
|
||||
@TableLogic(value = "0", delval = "2")
|
||||
private String delFlag;
|
||||
}
|
||||
@@ -48,6 +48,11 @@ public class GearOrderBo extends BaseEntity {
|
||||
*/
|
||||
private String salesManager;
|
||||
|
||||
/**
|
||||
* 销售员ID(挂接销售员管理)
|
||||
*/
|
||||
private Long salesmanId;
|
||||
|
||||
/**
|
||||
* 订单状态(0=新建,1=生产中,2=已完成,3=已取消)
|
||||
*/
|
||||
@@ -73,5 +78,9 @@ public class GearOrderBo extends BaseEntity {
|
||||
*/
|
||||
private BigDecimal noTaxAmount;
|
||||
|
||||
/**
|
||||
* 合同Excel附件ossId列表(逗号分隔)
|
||||
*/
|
||||
private String contractExcelOssIds;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.gear.oa.domain.bo;
|
||||
|
||||
import com.gear.common.core.domain.BaseEntity;
|
||||
import com.gear.common.core.validate.EditGroup;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 订单生产记录业务对象 gear_order_production
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class GearOrderProductionBo extends BaseEntity {
|
||||
|
||||
private Long productionId;
|
||||
|
||||
private Long orderId;
|
||||
|
||||
private Long orderDetailId;
|
||||
|
||||
private Long productId;
|
||||
|
||||
private BigDecimal planQty;
|
||||
|
||||
@NotNull(message = "完成数量不能为空", groups = {EditGroup.class})
|
||||
private BigDecimal finishedQty;
|
||||
|
||||
private BigDecimal badQty;
|
||||
|
||||
private String unit;
|
||||
|
||||
private String remark;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.gear.oa.domain.bo;
|
||||
|
||||
import com.gear.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 销售员业务对象 gear_salesman
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class GearSalesmanBo extends BaseEntity {
|
||||
|
||||
/**
|
||||
* 销售员ID
|
||||
*/
|
||||
private Long salesmanId;
|
||||
|
||||
/**
|
||||
* 销售员姓名
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 联系电话
|
||||
*/
|
||||
private String mobile;
|
||||
|
||||
/**
|
||||
* 状态(0正常 1停用)
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
package com.gear.oa.domain.bo;
|
||||
|
||||
import com.gear.common.core.domain.BaseEntity;
|
||||
import com.gear.common.core.validate.AddGroup;
|
||||
import com.gear.common.core.validate.EditGroup;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Date;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
/**
|
||||
* 发货单据业务对象 gear_shipping_order
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class GearShippingOrderBo extends BaseEntity {
|
||||
|
||||
/**
|
||||
* 发货单据ID
|
||||
*/
|
||||
private Long shippingId;
|
||||
|
||||
/**
|
||||
* 发货单号(唯一)
|
||||
*/
|
||||
private String shippingNo;
|
||||
|
||||
/**
|
||||
* 发货单名称(展示用)
|
||||
*/
|
||||
private String shippingName;
|
||||
|
||||
/**
|
||||
* 发货计划ID
|
||||
*/
|
||||
private Long planId;
|
||||
|
||||
/**
|
||||
* 关联订单ID
|
||||
*
|
||||
* 说明:
|
||||
* - 发货单据必须绑定订单(订单是发货的业务来源)
|
||||
* - 前端存在“只修改完成状态”的场景(仅提交 shippingId/status),这里仍要求携带 orderId,
|
||||
* 便于做订单状态联动与数据归属校验
|
||||
*/
|
||||
@NotNull(message = "订单ID不能为空", groups = {AddGroup.class, EditGroup.class})
|
||||
private Long orderId;
|
||||
|
||||
/**
|
||||
* 客户ID(用于按客户维度查询发货单据:通过订单反查)
|
||||
*
|
||||
* 说明:发货单据表本身不存 customerId,这里是查询条件,后端会先查出订单ID集合再过滤发货单据
|
||||
*/
|
||||
private Long customerId;
|
||||
|
||||
/**
|
||||
* 销售员ID(用于按销售员维度查询发货单据:通过订单反查)
|
||||
*
|
||||
* 说明:发货单据表本身不存 salesmanId,这里是查询条件,后端会先查出订单ID集合再过滤发货单据
|
||||
*/
|
||||
private Long salesmanId;
|
||||
|
||||
/**
|
||||
* 订单编号快照
|
||||
*/
|
||||
private String orderCode;
|
||||
|
||||
/**
|
||||
* 收货单位
|
||||
*/
|
||||
private String receiverCompany;
|
||||
|
||||
/**
|
||||
* 收货客户ID(真实归属客户)
|
||||
*
|
||||
* 说明:用于客户维度归属统计/查询(客户管理页按此字段精确查询“该客户实际收货”的发货单据)
|
||||
*/
|
||||
private Long receiverCustomerId;
|
||||
|
||||
/**
|
||||
* 发货时间
|
||||
*/
|
||||
private Date shipTime;
|
||||
|
||||
/**
|
||||
* 发货时间范围(开始)
|
||||
*/
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date shipTimeStart;
|
||||
|
||||
/**
|
||||
* 发货时间范围(结束)
|
||||
*/
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date shipTimeEnd;
|
||||
|
||||
/**
|
||||
* 负责人
|
||||
*/
|
||||
private String responsibleName;
|
||||
|
||||
/**
|
||||
* 物流公司
|
||||
*/
|
||||
private String logisticsCompany;
|
||||
|
||||
/**
|
||||
* 运单号
|
||||
*/
|
||||
private String logisticsNo;
|
||||
|
||||
/**
|
||||
* 收货人
|
||||
*/
|
||||
private String receiverName;
|
||||
|
||||
/**
|
||||
* 收货电话
|
||||
*/
|
||||
private String receiverPhone;
|
||||
|
||||
/**
|
||||
* 收货地址
|
||||
*/
|
||||
private String receiverAddress;
|
||||
|
||||
/**
|
||||
* 完成状态(0未发货 1已打印 2已发货 3已完成)
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.gear.oa.domain.bo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.gear.common.core.domain.BaseEntity;
|
||||
import com.gear.common.core.validate.AddGroup;
|
||||
import com.gear.common.core.validate.EditGroup;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 发货计划业务对象 gear_shipping_plan
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class GearShippingPlanBo extends BaseEntity {
|
||||
|
||||
/**
|
||||
* 发货计划ID
|
||||
*/
|
||||
private Long planId;
|
||||
|
||||
/**
|
||||
* 计划名称
|
||||
*/
|
||||
@NotBlank(message = "计划名称不能为空", groups = {AddGroup.class, EditGroup.class})
|
||||
private String planName;
|
||||
|
||||
/**
|
||||
* 计划日期
|
||||
*/
|
||||
@NotNull(message = "计划日期不能为空", groups = {AddGroup.class, EditGroup.class})
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
|
||||
private Date planDate;
|
||||
|
||||
/**
|
||||
* 状态(0未完成 1已完成)
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
}
|
||||
@@ -76,4 +76,25 @@ public class GearOrderDetailVo {
|
||||
private String productName;
|
||||
private String productCode;
|
||||
|
||||
/**
|
||||
* 产品类型(product=产品/成品,semi=半成品,raw=原料)
|
||||
* 说明:用于“订单明细”页过滤,只允许成品进入发货
|
||||
*/
|
||||
private String productType;
|
||||
|
||||
/**
|
||||
* 产品规格(来自产品管理 mat_product)
|
||||
*/
|
||||
private String spec;
|
||||
|
||||
/**
|
||||
* 产品型号(来自产品管理 mat_product)
|
||||
*/
|
||||
private String model;
|
||||
|
||||
/**
|
||||
* 产品单价(来自产品管理 mat_product;用于订单明细快捷填写)
|
||||
*/
|
||||
private BigDecimal unitPrice;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.gear.oa.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 订单生产记录视图对象 gear_order_production
|
||||
*/
|
||||
@Data
|
||||
public class GearOrderProductionVo {
|
||||
|
||||
private Long productionId;
|
||||
|
||||
private Long orderId;
|
||||
|
||||
private Long orderDetailId;
|
||||
|
||||
private Long productId;
|
||||
|
||||
private BigDecimal planQty;
|
||||
|
||||
private BigDecimal finishedQty;
|
||||
|
||||
private BigDecimal badQty;
|
||||
|
||||
private String unit;
|
||||
|
||||
private String remark;
|
||||
|
||||
private Date createTime;
|
||||
|
||||
private Date updateTime;
|
||||
|
||||
private String productName;
|
||||
|
||||
private String productCode;
|
||||
|
||||
private String productType;
|
||||
|
||||
private String spec;
|
||||
|
||||
private String model;
|
||||
}
|
||||
|
||||
@@ -50,11 +50,16 @@ public class GearOrderVo extends BaseEntity {
|
||||
private Integer company;
|
||||
|
||||
/**
|
||||
* 销售经理
|
||||
* 销售员(展示用:从 gear_salesman.name 联查得到)
|
||||
*/
|
||||
@ExcelProperty(value = "销售经理")
|
||||
@ExcelProperty(value = "销售员")
|
||||
private String salesManager;
|
||||
|
||||
/**
|
||||
* 销售员ID(挂接销售员管理)
|
||||
*/
|
||||
private Long salesmanId;
|
||||
|
||||
/**
|
||||
* 订单状态(0=新建,1=生产中,2=已完成,3=已取消)
|
||||
*/
|
||||
@@ -86,6 +91,18 @@ public class GearOrderVo extends BaseEntity {
|
||||
@ExcelProperty(value = "无税金额")
|
||||
private BigDecimal noTaxAmount;
|
||||
|
||||
/**
|
||||
* 合同Excel附件ossId列表(逗号分隔)
|
||||
*/
|
||||
private String contractExcelOssIds;
|
||||
|
||||
private String customerName;
|
||||
|
||||
private Integer shippingMaxStatus;
|
||||
|
||||
private Integer shippedFlag;
|
||||
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date lastShipTime;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.gear.oa.domain.vo;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 销售员视图对象 gear_salesman
|
||||
*/
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
public class GearSalesmanVo {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ExcelProperty(value = "销售员ID")
|
||||
private Long salesmanId;
|
||||
|
||||
@ExcelProperty(value = "销售员姓名")
|
||||
private String name;
|
||||
|
||||
@ExcelProperty(value = "联系电话")
|
||||
private String mobile;
|
||||
|
||||
@ExcelProperty(value = "状态(0正常 1停用)")
|
||||
private Integer status;
|
||||
|
||||
@ExcelProperty(value = "备注")
|
||||
private String remark;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.gear.oa.domain.vo;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import com.gear.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 发货单据视图对象 gear_shipping_order
|
||||
*/
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
public class GearShippingOrderVo extends BaseEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ExcelProperty(value = "发货单据ID")
|
||||
private Long shippingId;
|
||||
|
||||
@ExcelProperty(value = "发货单号")
|
||||
private String shippingNo;
|
||||
|
||||
@ExcelProperty(value = "发货单名称")
|
||||
private String shippingName;
|
||||
|
||||
@ExcelProperty(value = "发货计划ID")
|
||||
private Long planId;
|
||||
|
||||
@ExcelProperty(value = "订单ID")
|
||||
private Long orderId;
|
||||
|
||||
@ExcelProperty(value = "订单编号")
|
||||
private String orderCode;
|
||||
|
||||
@ExcelProperty(value = "收货单位")
|
||||
private String receiverCompany;
|
||||
|
||||
@ExcelProperty(value = "收货客户ID")
|
||||
private Long receiverCustomerId;
|
||||
|
||||
@ExcelProperty(value = "发货时间")
|
||||
private Date shipTime;
|
||||
|
||||
@ExcelProperty(value = "负责人")
|
||||
private String responsibleName;
|
||||
|
||||
@ExcelProperty(value = "物流公司")
|
||||
private String logisticsCompany;
|
||||
|
||||
@ExcelProperty(value = "运单号")
|
||||
private String logisticsNo;
|
||||
|
||||
@ExcelProperty(value = "收货人")
|
||||
private String receiverName;
|
||||
|
||||
@ExcelProperty(value = "收货电话")
|
||||
private String receiverPhone;
|
||||
|
||||
@ExcelProperty(value = "收货地址")
|
||||
private String receiverAddress;
|
||||
|
||||
@ExcelProperty(value = "完成状态")
|
||||
private String status;
|
||||
|
||||
@ExcelProperty(value = "备注")
|
||||
private String remark;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.gear.oa.domain.vo;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import com.gear.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 发货计划视图对象 gear_shipping_plan
|
||||
*/
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
public class GearShippingPlanVo extends BaseEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ExcelProperty(value = "计划ID")
|
||||
private Long planId;
|
||||
|
||||
@ExcelProperty(value = "计划名称")
|
||||
private String planName;
|
||||
|
||||
@ExcelProperty(value = "计划日期")
|
||||
private Date planDate;
|
||||
|
||||
@ExcelProperty(value = "状态")
|
||||
private String status;
|
||||
|
||||
@ExcelProperty(value = "备注")
|
||||
private String remark;
|
||||
|
||||
@ExcelProperty(value = "单据数")
|
||||
private Long shippingCount;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.gear.oa.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.gear.common.core.mapper.BaseMapperPlus;
|
||||
import com.gear.oa.domain.GearOrderProduction;
|
||||
import com.gear.oa.domain.vo.GearOrderProductionVo;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 订单生产记录Mapper接口
|
||||
*/
|
||||
public interface GearOrderProductionMapper extends BaseMapperPlus<GearOrderProductionMapper, GearOrderProduction, GearOrderProductionVo> {
|
||||
|
||||
Page<GearOrderProductionVo> selectVoPagePlus(Page<?> page, @Param("ew") Wrapper<GearOrderProduction> wrapper);
|
||||
|
||||
List<GearOrderProductionVo> selectVoListPlus(@Param("ew") Wrapper<GearOrderProduction> wrapper);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.gear.oa.mapper;
|
||||
|
||||
import com.gear.common.core.mapper.BaseMapperPlus;
|
||||
import com.gear.oa.domain.GearSalesman;
|
||||
import com.gear.oa.domain.vo.GearSalesmanVo;
|
||||
|
||||
/**
|
||||
* 销售员Mapper接口
|
||||
*/
|
||||
public interface GearSalesmanMapper extends BaseMapperPlus<GearSalesmanMapper, GearSalesman, GearSalesmanVo> {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.gear.oa.mapper;
|
||||
|
||||
import com.gear.common.core.mapper.BaseMapperPlus;
|
||||
import com.gear.oa.domain.GearShippingOrder;
|
||||
import com.gear.oa.domain.vo.GearShippingOrderVo;
|
||||
|
||||
/**
|
||||
* 发货单据Mapper
|
||||
*/
|
||||
public interface GearShippingOrderMapper extends BaseMapperPlus<GearShippingOrderMapper, GearShippingOrder, GearShippingOrderVo> {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.gear.oa.mapper;
|
||||
|
||||
import com.gear.common.core.mapper.BaseMapperPlus;
|
||||
import com.gear.oa.domain.GearShippingPlan;
|
||||
import com.gear.oa.domain.bo.GearShippingPlanBo;
|
||||
import com.gear.oa.domain.vo.GearShippingPlanVo;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 发货计划Mapper
|
||||
*/
|
||||
public interface GearShippingPlanMapper extends BaseMapperPlus<GearShippingPlanMapper, GearShippingPlan, GearShippingPlanVo> {
|
||||
|
||||
@Select({
|
||||
"<script>",
|
||||
"SELECT",
|
||||
" p.plan_id,",
|
||||
" p.plan_name,",
|
||||
" p.plan_date,",
|
||||
" p.status,",
|
||||
" p.remark,",
|
||||
" p.create_by,",
|
||||
" p.create_time,",
|
||||
" p.update_by,",
|
||||
" p.update_time,",
|
||||
" COUNT(o.shipping_id) AS shipping_count",
|
||||
"FROM gear_shipping_plan p",
|
||||
"LEFT JOIN gear_shipping_order o",
|
||||
" ON o.plan_id = p.plan_id",
|
||||
" AND o.del_flag = '0'",
|
||||
"WHERE p.del_flag = '0'",
|
||||
"<if test='bo != null and bo.planName != null and bo.planName != \"\"'>",
|
||||
" AND p.plan_name LIKE CONCAT('%', #{bo.planName}, '%')",
|
||||
"</if>",
|
||||
"<if test='bo != null and bo.status != null and bo.status != \"\"'>",
|
||||
" AND p.status = #{bo.status}",
|
||||
"</if>",
|
||||
"<if test='bo != null and bo.planDate != null'>",
|
||||
" AND p.plan_date = #{bo.planDate}",
|
||||
"</if>",
|
||||
"GROUP BY p.plan_id",
|
||||
"ORDER BY p.plan_date DESC, p.create_time DESC",
|
||||
"</script>"
|
||||
})
|
||||
List<GearShippingPlanVo> selectVoListWithCount(@Param("bo") GearShippingPlanBo bo);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ import com.gear.oa.domain.vo.GearStockVo;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 库存:原材料/产品与库区/库位的存放关系Mapper接口
|
||||
@@ -19,6 +21,8 @@ public interface GearStockMapper extends BaseMapperPlus<GearStockMapper, GearSto
|
||||
|
||||
BigDecimal getStockByItemId(Long rawMaterialId);
|
||||
|
||||
List<Map<String, Object>> selectSumQuantityByItemIds(@Param("itemType") String itemType, @Param("itemIds") List<Long> itemIds);
|
||||
|
||||
/**
|
||||
* 分页联查物品名称和编码,支持Wrapper动态条件,返回Page<GearStockVo>
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.gear.oa.service;
|
||||
|
||||
import com.gear.common.core.domain.PageQuery;
|
||||
import com.gear.common.core.page.TableDataInfo;
|
||||
import com.gear.oa.domain.bo.GearOrderProductionBo;
|
||||
import com.gear.oa.domain.vo.GearOrderProductionVo;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 订单生产记录Service接口
|
||||
*/
|
||||
public interface IGearOrderProductionService {
|
||||
|
||||
GearOrderProductionVo queryById(Long productionId);
|
||||
|
||||
TableDataInfo<GearOrderProductionVo> queryPageList(GearOrderProductionBo bo, PageQuery pageQuery);
|
||||
|
||||
List<GearOrderProductionVo> queryList(GearOrderProductionBo bo);
|
||||
|
||||
Boolean updateByBo(GearOrderProductionBo bo);
|
||||
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
|
||||
/**
|
||||
* 初始化/同步:按订单明细生成生产记录,并同步计划数量
|
||||
*/
|
||||
Boolean initByOrderId(Long orderId);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.gear.oa.service;
|
||||
|
||||
import com.gear.common.core.domain.PageQuery;
|
||||
import com.gear.common.core.page.TableDataInfo;
|
||||
import com.gear.oa.domain.bo.GearSalesmanBo;
|
||||
import com.gear.oa.domain.vo.GearCustomerVo;
|
||||
import com.gear.oa.domain.vo.GearSalesmanVo;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 销售员Service接口
|
||||
*/
|
||||
public interface IGearSalesmanService {
|
||||
|
||||
GearSalesmanVo queryById(Long salesmanId);
|
||||
|
||||
TableDataInfo<GearSalesmanVo> queryPageList(GearSalesmanBo bo, PageQuery pageQuery);
|
||||
|
||||
List<GearSalesmanVo> queryList(GearSalesmanBo bo);
|
||||
|
||||
Boolean insertByBo(GearSalesmanBo bo);
|
||||
|
||||
Boolean updateByBo(GearSalesmanBo bo);
|
||||
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
|
||||
/**
|
||||
* 查询销售员的跟进客户(从订单中反查:salesManager=销售员姓名 -> customerId -> 客户信息)
|
||||
*/
|
||||
TableDataInfo<GearCustomerVo> queryFollowCustomers(Long salesmanId, PageQuery pageQuery);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.gear.oa.service;
|
||||
|
||||
import com.gear.common.core.domain.PageQuery;
|
||||
import com.gear.common.core.page.TableDataInfo;
|
||||
import com.gear.oa.domain.bo.GearShippingOrderBo;
|
||||
import com.gear.oa.domain.vo.GearShippingOrderVo;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 发货单据Service接口
|
||||
*/
|
||||
public interface IGearShippingOrderService {
|
||||
|
||||
/**
|
||||
* 查询发货单据
|
||||
*/
|
||||
GearShippingOrderVo queryById(Long shippingId);
|
||||
|
||||
/**
|
||||
* 查询发货单据列表(分页)
|
||||
*/
|
||||
TableDataInfo<GearShippingOrderVo> queryPageList(GearShippingOrderBo bo, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 查询发货单据列表(不分页)
|
||||
*/
|
||||
List<GearShippingOrderVo> queryList(GearShippingOrderBo bo);
|
||||
|
||||
/**
|
||||
* 新增发货单据
|
||||
*/
|
||||
Boolean insertByBo(GearShippingOrderBo bo);
|
||||
|
||||
/**
|
||||
* 修改发货单据
|
||||
*/
|
||||
Boolean updateByBo(GearShippingOrderBo bo);
|
||||
|
||||
/**
|
||||
* 删除发货单据
|
||||
*/
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
|
||||
/**
|
||||
* 根据订单ID查询发货单据
|
||||
*/
|
||||
List<GearShippingOrderVo> queryListByOrderId(Long orderId);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.gear.oa.service;
|
||||
|
||||
import com.gear.common.core.domain.PageQuery;
|
||||
import com.gear.common.core.page.TableDataInfo;
|
||||
import com.gear.oa.domain.bo.GearShippingPlanBo;
|
||||
import com.gear.oa.domain.vo.GearShippingPlanVo;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 发货计划Service接口
|
||||
*/
|
||||
public interface IGearShippingPlanService {
|
||||
|
||||
GearShippingPlanVo queryById(Long planId);
|
||||
|
||||
TableDataInfo<GearShippingPlanVo> queryPageList(GearShippingPlanBo bo, PageQuery pageQuery);
|
||||
|
||||
List<GearShippingPlanVo> queryList(GearShippingPlanBo bo);
|
||||
|
||||
List<GearShippingPlanVo> queryListWithCount(GearShippingPlanBo bo);
|
||||
|
||||
Boolean insertByBo(GearShippingPlanBo bo);
|
||||
|
||||
Boolean updateByBo(GearShippingPlanBo bo);
|
||||
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import com.gear.oa.domain.vo.GearStockVo;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 库存:原材料/产品与库区/库位的存放关系Service接口
|
||||
@@ -47,6 +48,8 @@ public interface IGearStockService {
|
||||
*/
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
|
||||
Map<Long, BigDecimal> sumQuantityByItemIds(String itemType, List<Long> itemIds);
|
||||
|
||||
// /**
|
||||
// * 根据原材料ID获取库存数量 (用于生成推荐采购计划)
|
||||
// */
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
package com.gear.oa.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.gear.common.core.domain.PageQuery;
|
||||
import com.gear.common.core.page.TableDataInfo;
|
||||
import com.gear.common.utils.StringUtils;
|
||||
import com.gear.oa.domain.GearOrderProduction;
|
||||
import com.gear.oa.domain.bo.GearOrderProductionBo;
|
||||
import com.gear.oa.domain.vo.GearOrderDetailVo;
|
||||
import com.gear.oa.domain.vo.GearOrderProductionVo;
|
||||
import com.gear.oa.mapper.GearOrderDetailMapper;
|
||||
import com.gear.oa.mapper.GearOrderProductionMapper;
|
||||
import com.gear.oa.service.IGearOrderProductionService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 订单生产记录Service业务层处理
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class GearOrderProductionServiceImpl implements IGearOrderProductionService {
|
||||
|
||||
private final GearOrderProductionMapper baseMapper;
|
||||
private final GearOrderDetailMapper orderDetailMapper;
|
||||
|
||||
@Override
|
||||
public GearOrderProductionVo queryById(Long productionId) {
|
||||
return baseMapper.selectVoById(productionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableDataInfo<GearOrderProductionVo> queryPageList(GearOrderProductionBo bo, PageQuery pageQuery) {
|
||||
QueryWrapper<GearOrderProduction> qw = buildQueryWrapperPlus(bo);
|
||||
Page<GearOrderProductionVo> result = baseMapper.selectVoPagePlus(pageQuery.build(), qw);
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GearOrderProductionVo> queryList(GearOrderProductionBo bo) {
|
||||
QueryWrapper<GearOrderProduction> qw = buildQueryWrapperPlus(bo);
|
||||
return baseMapper.selectVoListPlus(qw);
|
||||
}
|
||||
|
||||
private QueryWrapper<GearOrderProduction> buildQueryWrapperPlus(GearOrderProductionBo bo) {
|
||||
Map<String, Object> params = bo.getParams();
|
||||
QueryWrapper<GearOrderProduction> qw = Wrappers.query();
|
||||
qw.eq("p.del_flag", "0");
|
||||
qw.eq(bo.getProductionId() != null, "p.production_id", bo.getProductionId());
|
||||
qw.eq(bo.getOrderId() != null, "p.order_id", bo.getOrderId());
|
||||
qw.eq(bo.getOrderDetailId() != null, "p.order_detail_id", bo.getOrderDetailId());
|
||||
qw.eq(bo.getProductId() != null, "p.product_id", bo.getProductId());
|
||||
qw.orderByAsc("p.order_detail_id");
|
||||
qw.orderByDesc("p.update_time");
|
||||
qw.orderByDesc("p.create_time");
|
||||
return qw;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean updateByBo(GearOrderProductionBo bo) {
|
||||
GearOrderProduction update = BeanUtil.toBean(bo, GearOrderProduction.class);
|
||||
validEntityBeforeSave(update);
|
||||
return baseMapper.updateById(update) > 0;
|
||||
}
|
||||
|
||||
private void validEntityBeforeSave(GearOrderProduction entity) {
|
||||
if (entity == null) return;
|
||||
if (entity.getProductionId() == null) {
|
||||
entity.setProductionId(IdUtil.getSnowflakeNextId());
|
||||
}
|
||||
if (entity.getFinishedQty() == null) {
|
||||
entity.setFinishedQty(BigDecimal.ZERO);
|
||||
}
|
||||
if (entity.getBadQty() == null) {
|
||||
entity.setBadQty(BigDecimal.ZERO);
|
||||
}
|
||||
if (StringUtils.isBlank(entity.getDelFlag())) {
|
||||
entity.setDelFlag("0");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||||
if (isValid) {
|
||||
}
|
||||
return baseMapper.deleteBatchIds(ids) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean initByOrderId(Long orderId) {
|
||||
if (orderId == null) return false;
|
||||
List<GearOrderDetailVo> details = orderDetailMapper.selectVoListByOrderId(orderId);
|
||||
if (details == null || details.isEmpty()) return true;
|
||||
|
||||
List<Long> detailIds = details.stream().map(GearOrderDetailVo::getDetailId).filter(Objects::nonNull).distinct().collect(Collectors.toList());
|
||||
if (detailIds.isEmpty()) return true;
|
||||
|
||||
List<GearOrderProduction> existing = baseMapper.selectList(Wrappers.<GearOrderProduction>lambdaQuery()
|
||||
.in(GearOrderProduction::getOrderDetailId, detailIds));
|
||||
Map<Long, GearOrderProduction> existMap = new HashMap<>();
|
||||
if (existing != null) {
|
||||
for (GearOrderProduction e : existing) {
|
||||
if (e != null && e.getOrderDetailId() != null) {
|
||||
existMap.put(e.getOrderDetailId(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (GearOrderDetailVo d : details) {
|
||||
if (d == null || d.getDetailId() == null) continue;
|
||||
GearOrderProduction hit = existMap.get(d.getDetailId());
|
||||
BigDecimal planQty = d.getQuantity() == null ? BigDecimal.ZERO : BigDecimal.valueOf(d.getQuantity());
|
||||
if (hit == null) {
|
||||
GearOrderProduction add = new GearOrderProduction();
|
||||
add.setProductionId(IdUtil.getSnowflakeNextId());
|
||||
add.setOrderId(orderId);
|
||||
add.setOrderDetailId(d.getDetailId());
|
||||
add.setProductId(d.getProductId());
|
||||
add.setPlanQty(planQty);
|
||||
add.setFinishedQty(BigDecimal.ZERO);
|
||||
add.setBadQty(BigDecimal.ZERO);
|
||||
add.setUnit(d.getUnit() == null ? "" : d.getUnit());
|
||||
add.setDelFlag("0");
|
||||
baseMapper.insert(add);
|
||||
} else {
|
||||
GearOrderProduction upd = new GearOrderProduction();
|
||||
upd.setProductionId(hit.getProductionId());
|
||||
upd.setOrderId(orderId);
|
||||
upd.setOrderDetailId(d.getDetailId());
|
||||
upd.setProductId(d.getProductId());
|
||||
upd.setPlanQty(planQty);
|
||||
upd.setUnit(d.getUnit() == null ? "" : d.getUnit());
|
||||
baseMapper.updateById(upd);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,9 @@ import org.springframework.stereotype.Service;
|
||||
import com.gear.oa.domain.bo.GearOrderBo;
|
||||
import com.gear.oa.domain.vo.GearOrderVo;
|
||||
import com.gear.oa.domain.GearOrder;
|
||||
import com.gear.oa.domain.GearShippingOrder;
|
||||
import com.gear.oa.mapper.GearOrderMapper;
|
||||
import com.gear.oa.mapper.GearShippingOrderMapper;
|
||||
import com.gear.oa.service.IGearOrderService;
|
||||
|
||||
import java.util.List;
|
||||
@@ -31,13 +33,51 @@ import java.util.Collection;
|
||||
public class GearOrderServiceImpl implements IGearOrderService {
|
||||
|
||||
private final GearOrderMapper baseMapper;
|
||||
private final GearShippingOrderMapper shippingOrderMapper;
|
||||
|
||||
/**
|
||||
* 查询订单主
|
||||
*/
|
||||
@Override
|
||||
public GearOrderVo queryById(Long orderId){
|
||||
return baseMapper.selectVoById(orderId);
|
||||
GearOrderVo vo = baseMapper.selectVoById(orderId);
|
||||
if (vo == null) {
|
||||
return null;
|
||||
}
|
||||
Integer derived = deriveOrderStatusByShipping(orderId, vo.getOrderStatus());
|
||||
if (derived != null && vo.getOrderStatus() != null && !derived.equals(vo.getOrderStatus())) {
|
||||
baseMapper.update(null, Wrappers.<GearOrder>lambdaUpdate()
|
||||
.eq(GearOrder::getOrderId, orderId)
|
||||
.ne(GearOrder::getOrderStatus, 3)
|
||||
.in(GearOrder::getOrderStatus, 0, 1)
|
||||
.set(GearOrder::getOrderStatus, derived));
|
||||
vo.setOrderStatus(derived);
|
||||
}
|
||||
return vo;
|
||||
}
|
||||
|
||||
private Integer deriveOrderStatusByShipping(Long orderId, Integer currentStatus) {
|
||||
if (orderId == null) return currentStatus;
|
||||
if (currentStatus != null && currentStatus == 3) return 3;
|
||||
|
||||
QueryWrapper<GearShippingOrder> qw = new QueryWrapper<>();
|
||||
qw.select("MAX(CAST(status AS SIGNED))");
|
||||
qw.eq("order_id", orderId);
|
||||
qw.eq("del_flag", "0");
|
||||
List<Object> objs = shippingOrderMapper.selectObjs(qw);
|
||||
Integer maxStatus = null;
|
||||
if (objs != null && !objs.isEmpty() && objs.get(0) != null) {
|
||||
try {
|
||||
maxStatus = Integer.parseInt(String.valueOf(objs.get(0)));
|
||||
} catch (Exception ignored) {
|
||||
maxStatus = null;
|
||||
}
|
||||
}
|
||||
|
||||
int st = currentStatus == null ? 0 : currentStatus;
|
||||
if (maxStatus != null && maxStatus >= 3 && (st == 0 || st == 1)) return 2;
|
||||
if (maxStatus != null && maxStatus >= 2 && st == 0) return 1;
|
||||
return currentStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -55,8 +95,11 @@ public class GearOrderServiceImpl implements IGearOrderService {
|
||||
//表别名的方式进行联查
|
||||
lqw.like(StringUtils.isNotBlank(bo.getOrderCode()), "o.order_code", bo.getOrderCode());
|
||||
lqw.eq(bo.getCustomerId() != null, "o.customer_id", bo.getCustomerId());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getSalesManager()), "o.sales_manager", bo.getSalesManager());
|
||||
lqw.eq(bo.getSalesmanId() != null, "o.salesman_id", bo.getSalesmanId());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getSalesManager()), "s.name", bo.getSalesManager());
|
||||
lqw.eq(bo.getOrderStatus() != null, "o.order_status", bo.getOrderStatus());
|
||||
lqw.apply(bo.getOrderStatus() != null && bo.getOrderStatus() == 0,
|
||||
"NOT EXISTS (SELECT 1 FROM gear_shipping_order so WHERE so.order_id = o.order_id AND so.del_flag = '0' AND CAST(so.status AS SIGNED) >= 2)");
|
||||
lqw.eq(bo.getTradeType() != null, "o.trade_type", bo.getTradeType());
|
||||
lqw.eq(bo.getTaxAmount() != null, "o.tax_amount", bo.getTaxAmount());
|
||||
lqw.eq(bo.getNoTaxAmount() != null, "o.no_tax_amount", bo.getNoTaxAmount());
|
||||
@@ -78,6 +121,7 @@ public class GearOrderServiceImpl implements IGearOrderService {
|
||||
LambdaQueryWrapper<GearOrder> lqw = Wrappers.lambdaQuery();
|
||||
lqw.like(StringUtils.isNotBlank(bo.getOrderCode()), GearOrder::getOrderCode, bo.getOrderCode());
|
||||
lqw.eq(bo.getCustomerId() != null, GearOrder::getCustomerId, bo.getCustomerId());
|
||||
lqw.eq(bo.getSalesmanId() != null, GearOrder::getSalesmanId, bo.getSalesmanId());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getSalesManager()), GearOrder::getSalesManager, bo.getSalesManager());
|
||||
lqw.eq(bo.getOrderStatus() != null, GearOrder::getOrderStatus, bo.getOrderStatus());
|
||||
lqw.eq(bo.getTradeType() != null, GearOrder::getTradeType, bo.getTradeType());
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
package com.gear.oa.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.gear.common.core.domain.PageQuery;
|
||||
import com.gear.common.core.page.TableDataInfo;
|
||||
import com.gear.common.utils.StringUtils;
|
||||
import com.gear.oa.domain.GearCustomer;
|
||||
import com.gear.oa.domain.GearOrder;
|
||||
import com.gear.oa.domain.GearSalesman;
|
||||
import com.gear.oa.domain.bo.GearSalesmanBo;
|
||||
import com.gear.oa.domain.vo.GearCustomerVo;
|
||||
import com.gear.oa.domain.vo.GearSalesmanVo;
|
||||
import com.gear.oa.mapper.GearCustomerMapper;
|
||||
import com.gear.oa.mapper.GearOrderMapper;
|
||||
import com.gear.oa.mapper.GearSalesmanMapper;
|
||||
import com.gear.oa.service.IGearSalesmanService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 销售员Service业务层处理
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class GearSalesmanServiceImpl implements IGearSalesmanService {
|
||||
|
||||
private final GearSalesmanMapper baseMapper;
|
||||
private final GearOrderMapper gearOrderMapper;
|
||||
private final GearCustomerMapper gearCustomerMapper;
|
||||
|
||||
@Override
|
||||
public GearSalesmanVo queryById(Long salesmanId) {
|
||||
return baseMapper.selectVoById(salesmanId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableDataInfo<GearSalesmanVo> queryPageList(GearSalesmanBo bo, PageQuery pageQuery) {
|
||||
LambdaQueryWrapper<GearSalesman> lqw = buildQueryWrapper(bo);
|
||||
Page<GearSalesmanVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GearSalesmanVo> queryList(GearSalesmanBo bo) {
|
||||
LambdaQueryWrapper<GearSalesman> lqw = buildQueryWrapper(bo);
|
||||
return baseMapper.selectVoList(lqw);
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<GearSalesman> buildQueryWrapper(GearSalesmanBo bo) {
|
||||
Map<String, Object> params = bo.getParams();
|
||||
LambdaQueryWrapper<GearSalesman> lqw = Wrappers.lambdaQuery();
|
||||
lqw.like(StringUtils.isNotBlank(bo.getName()), GearSalesman::getName, bo.getName());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getMobile()), GearSalesman::getMobile, bo.getMobile());
|
||||
lqw.eq(bo.getStatus() != null, GearSalesman::getStatus, bo.getStatus());
|
||||
return lqw;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean insertByBo(GearSalesmanBo bo) {
|
||||
GearSalesman add = BeanUtil.toBean(bo, GearSalesman.class);
|
||||
validEntityBeforeSave(add);
|
||||
boolean flag = baseMapper.insert(add) > 0;
|
||||
if (flag) {
|
||||
bo.setSalesmanId(add.getSalesmanId());
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean updateByBo(GearSalesmanBo bo) {
|
||||
GearSalesman update = BeanUtil.toBean(bo, GearSalesman.class);
|
||||
validEntityBeforeSave(update);
|
||||
return baseMapper.updateById(update) > 0;
|
||||
}
|
||||
|
||||
private void validEntityBeforeSave(GearSalesman entity) {
|
||||
// TODO 可在这里增加“姓名唯一”等约束校验
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||||
if (isValid) {
|
||||
// TODO 可在这里增加“存在关联订单/客户则不允许删除”等业务校验
|
||||
}
|
||||
return baseMapper.deleteBatchIds(ids) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableDataInfo<GearCustomerVo> queryFollowCustomers(Long salesmanId, PageQuery pageQuery) {
|
||||
GearSalesman salesman = baseMapper.selectById(salesmanId);
|
||||
if (salesman == null) {
|
||||
return TableDataInfo.build(new Page<>());
|
||||
}
|
||||
|
||||
// 说明:销售经理已挂接销售员,这里通过 salesman_id 反查客户
|
||||
LambdaQueryWrapper<GearOrder> orderQw = Wrappers.lambdaQuery();
|
||||
orderQw.select(GearOrder::getCustomerId);
|
||||
orderQw.eq(GearOrder::getSalesmanId, salesmanId);
|
||||
orderQw.eq(GearOrder::getDelFlag, 0);
|
||||
orderQw.isNotNull(GearOrder::getCustomerId);
|
||||
orderQw.groupBy(GearOrder::getCustomerId);
|
||||
List<GearOrder> orders = gearOrderMapper.selectList(orderQw);
|
||||
|
||||
Set<Long> customerIds = orders.stream()
|
||||
.map(GearOrder::getCustomerId)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
if (customerIds.isEmpty()) {
|
||||
return TableDataInfo.build(new Page<>());
|
||||
}
|
||||
|
||||
// 说明:客户表有逻辑删除标志,这里仅取未删除的数据
|
||||
LambdaQueryWrapper<GearCustomer> customerQw = Wrappers.lambdaQuery();
|
||||
customerQw.in(GearCustomer::getCustomerId, customerIds);
|
||||
customerQw.eq(GearCustomer::getDelFlag, 0);
|
||||
|
||||
Page<GearCustomerVo> page = gearCustomerMapper.selectVoPage(pageQuery.build(), customerQw);
|
||||
return TableDataInfo.build(page);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
package com.gear.oa.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.gear.common.core.domain.PageQuery;
|
||||
import com.gear.common.core.page.TableDataInfo;
|
||||
import com.gear.common.utils.StringUtils;
|
||||
import com.gear.oa.domain.GearOrder;
|
||||
import com.gear.oa.domain.GearShippingOrder;
|
||||
import com.gear.oa.domain.bo.GearShippingOrderBo;
|
||||
import com.gear.oa.domain.vo.GearShippingOrderVo;
|
||||
import com.gear.oa.mapper.GearOrderMapper;
|
||||
import com.gear.oa.mapper.GearShippingOrderMapper;
|
||||
import com.gear.oa.service.IGearShippingOrderService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 发货单据Service业务层处理
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class GearShippingOrderServiceImpl implements IGearShippingOrderService {
|
||||
|
||||
private final GearShippingOrderMapper baseMapper;
|
||||
private final GearOrderMapper orderMapper;
|
||||
|
||||
@Override
|
||||
public GearShippingOrderVo queryById(Long shippingId) {
|
||||
return baseMapper.selectVoById(shippingId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableDataInfo<GearShippingOrderVo> queryPageList(GearShippingOrderBo bo, PageQuery pageQuery) {
|
||||
LambdaQueryWrapper<GearShippingOrder> lqw = buildQueryWrapper(bo);
|
||||
Page<GearShippingOrderVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GearShippingOrderVo> queryList(GearShippingOrderBo bo) {
|
||||
LambdaQueryWrapper<GearShippingOrder> lqw = buildQueryWrapper(bo);
|
||||
return baseMapper.selectVoList(lqw);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GearShippingOrderVo> queryListByOrderId(Long orderId) {
|
||||
GearShippingOrderBo bo = new GearShippingOrderBo();
|
||||
bo.setOrderId(orderId);
|
||||
return queryList(bo);
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<GearShippingOrder> buildQueryWrapper(GearShippingOrderBo bo) {
|
||||
Map<String, Object> params = bo.getParams();
|
||||
LambdaQueryWrapper<GearShippingOrder> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(bo.getShippingId() != null, GearShippingOrder::getShippingId, bo.getShippingId());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getShippingNo()), GearShippingOrder::getShippingNo, bo.getShippingNo());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getShippingName()), GearShippingOrder::getShippingName, bo.getShippingName());
|
||||
lqw.eq(bo.getPlanId() != null, GearShippingOrder::getPlanId, bo.getPlanId());
|
||||
// receiverCustomerId 是“真实收货客户归属”,用于客户管理页按收货客户精确查询发货单据
|
||||
lqw.eq(bo.getReceiverCustomerId() != null, GearShippingOrder::getReceiverCustomerId, bo.getReceiverCustomerId());
|
||||
if (bo.getOrderId() != null) {
|
||||
// 精确查某订单的发货单据
|
||||
lqw.eq(GearShippingOrder::getOrderId, bo.getOrderId());
|
||||
} else if (bo.getCustomerId() != null || bo.getSalesmanId() != null) {
|
||||
// 按客户/销售员维度查发货单据:先通过订单表反查订单ID集合,再过滤发货单据
|
||||
List<Long> orderIds = queryOrderIdsByCustomerOrSalesman(bo.getCustomerId(), bo.getSalesmanId());
|
||||
if (orderIds.isEmpty()) {
|
||||
lqw.eq(GearShippingOrder::getShippingId, -1L);
|
||||
} else {
|
||||
lqw.in(GearShippingOrder::getOrderId, orderIds);
|
||||
}
|
||||
}
|
||||
lqw.like(StringUtils.isNotBlank(bo.getOrderCode()), GearShippingOrder::getOrderCode, bo.getOrderCode());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getReceiverCompany()), GearShippingOrder::getReceiverCompany, bo.getReceiverCompany());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getResponsibleName()), GearShippingOrder::getResponsibleName, bo.getResponsibleName());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getLogisticsNo()), GearShippingOrder::getLogisticsNo, bo.getLogisticsNo());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getStatus()), GearShippingOrder::getStatus, bo.getStatus());
|
||||
lqw.ge(bo.getShipTimeStart() != null, GearShippingOrder::getShipTime, bo.getShipTimeStart());
|
||||
lqw.le(bo.getShipTimeEnd() != null, GearShippingOrder::getShipTime, bo.getShipTimeEnd());
|
||||
lqw.orderByDesc(GearShippingOrder::getShipTime);
|
||||
lqw.orderByDesc(GearShippingOrder::getCreateTime);
|
||||
return lqw;
|
||||
}
|
||||
|
||||
private List<Long> queryOrderIdsByCustomerOrSalesman(Long customerId, Long salesmanId) {
|
||||
LambdaQueryWrapper<GearOrder> lqw = Wrappers.lambdaQuery();
|
||||
lqw.select(GearOrder::getOrderId);
|
||||
lqw.eq(customerId != null, GearOrder::getCustomerId, customerId);
|
||||
lqw.eq(salesmanId != null, GearOrder::getSalesmanId, salesmanId);
|
||||
lqw.eq(GearOrder::getDelFlag, 0);
|
||||
List<GearOrder> list = orderMapper.selectList(lqw);
|
||||
if (list == null || list.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return list.stream().map(GearOrder::getOrderId).filter(id -> id != null).distinct().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean insertByBo(GearShippingOrderBo bo) {
|
||||
GearShippingOrder add = BeanUtil.toBean(bo, GearShippingOrder.class);
|
||||
validEntityBeforeSave(add);
|
||||
boolean flag = baseMapper.insert(add) > 0;
|
||||
if (flag) {
|
||||
bo.setShippingId(add.getShippingId());
|
||||
syncOrderStatusIfShipped(add);
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean updateByBo(GearShippingOrderBo bo) {
|
||||
GearShippingOrder update = BeanUtil.toBean(bo, GearShippingOrder.class);
|
||||
validEntityBeforeSave(update);
|
||||
boolean ok = baseMapper.updateById(update) > 0;
|
||||
if (ok) {
|
||||
syncOrderStatusIfShipped(update);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
private void syncOrderStatusIfShipped(GearShippingOrder entity) {
|
||||
if (entity == null || entity.getOrderId() == null) return;
|
||||
Integer statusNum = null;
|
||||
try {
|
||||
if (StringUtils.isNotBlank(entity.getStatus())) {
|
||||
statusNum = Integer.parseInt(entity.getStatus());
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
statusNum = null;
|
||||
}
|
||||
if (statusNum == null) return;
|
||||
// 发货单据状态与订单状态联动:
|
||||
// - 发货单据 >=2(已发货):若订单仍为预订单(0),推进到进行中(1)
|
||||
// - 发货单据 >=3(已完成):若订单为预订单(0)/进行中(1),推进到已完成(2)
|
||||
// - 订单若已取消(3),不做推进
|
||||
if (statusNum >= 3) {
|
||||
orderMapper.update(null, Wrappers.<GearOrder>lambdaUpdate()
|
||||
.eq(GearOrder::getOrderId, entity.getOrderId())
|
||||
.ne(GearOrder::getOrderStatus, 3)
|
||||
.in(GearOrder::getOrderStatus, 0, 1)
|
||||
.set(GearOrder::getOrderStatus, 2));
|
||||
return;
|
||||
}
|
||||
if (statusNum >= 2) {
|
||||
orderMapper.update(null, Wrappers.<GearOrder>lambdaUpdate()
|
||||
.eq(GearOrder::getOrderId, entity.getOrderId())
|
||||
.eq(GearOrder::getOrderStatus, 0)
|
||||
.set(GearOrder::getOrderStatus, 1));
|
||||
}
|
||||
}
|
||||
|
||||
private void validEntityBeforeSave(GearShippingOrder entity) {
|
||||
boolean isInsert = entity.getShippingId() == null;
|
||||
// 发货单据ID:项目内大部分业务表使用雪花ID,这里保持一致
|
||||
if (entity.getShippingId() == null) {
|
||||
entity.setShippingId(IdUtil.getSnowflakeNextId());
|
||||
}
|
||||
if (isInsert) {
|
||||
// 发货单号:为空则自动生成
|
||||
if (StringUtils.isBlank(entity.getShippingNo())) {
|
||||
entity.setShippingNo("SHIP_" + IdUtil.getSnowflakeNextIdStr());
|
||||
}
|
||||
// 发货单名称:为空则默认等于发货单号(便于列表展示)
|
||||
if (StringUtils.isBlank(entity.getShippingName())) {
|
||||
entity.setShippingName(entity.getShippingNo());
|
||||
}
|
||||
// 默认状态:0正常
|
||||
if (StringUtils.isBlank(entity.getStatus())) {
|
||||
entity.setStatus("0");
|
||||
}
|
||||
// 默认逻辑删除标志:0存在(TableLogic 会在删除时写 2)
|
||||
if (StringUtils.isBlank(entity.getDelFlag())) {
|
||||
entity.setDelFlag("0");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||||
if (isValid) {
|
||||
// 可在此处加入业务校验:例如已对账/已开票的发货单据不允许删除
|
||||
}
|
||||
return baseMapper.deleteBatchIds(ids) > 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package com.gear.oa.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.gear.common.core.domain.PageQuery;
|
||||
import com.gear.common.core.page.TableDataInfo;
|
||||
import com.gear.common.exception.ServiceException;
|
||||
import com.gear.common.utils.StringUtils;
|
||||
import com.gear.oa.domain.GearShippingOrder;
|
||||
import com.gear.oa.domain.GearShippingPlan;
|
||||
import com.gear.oa.domain.bo.GearShippingPlanBo;
|
||||
import com.gear.oa.domain.vo.GearShippingPlanVo;
|
||||
import com.gear.oa.mapper.GearShippingOrderMapper;
|
||||
import com.gear.oa.mapper.GearShippingPlanMapper;
|
||||
import com.gear.oa.service.IGearShippingPlanService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 发货计划Service业务层处理
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class GearShippingPlanServiceImpl implements IGearShippingPlanService {
|
||||
|
||||
private final GearShippingPlanMapper baseMapper;
|
||||
private final GearShippingOrderMapper shippingOrderMapper;
|
||||
|
||||
@Override
|
||||
public GearShippingPlanVo queryById(Long planId) {
|
||||
return baseMapper.selectVoById(planId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableDataInfo<GearShippingPlanVo> queryPageList(GearShippingPlanBo bo, PageQuery pageQuery) {
|
||||
LambdaQueryWrapper<GearShippingPlan> lqw = buildQueryWrapper(bo);
|
||||
Page<GearShippingPlanVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GearShippingPlanVo> queryList(GearShippingPlanBo bo) {
|
||||
LambdaQueryWrapper<GearShippingPlan> lqw = buildQueryWrapper(bo);
|
||||
return baseMapper.selectVoList(lqw);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GearShippingPlanVo> queryListWithCount(GearShippingPlanBo bo) {
|
||||
return baseMapper.selectVoListWithCount(bo);
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<GearShippingPlan> buildQueryWrapper(GearShippingPlanBo bo) {
|
||||
LambdaQueryWrapper<GearShippingPlan> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(bo.getPlanId() != null, GearShippingPlan::getPlanId, bo.getPlanId());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getPlanName()), GearShippingPlan::getPlanName, bo.getPlanName());
|
||||
lqw.eq(bo.getPlanDate() != null, GearShippingPlan::getPlanDate, bo.getPlanDate());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getStatus()), GearShippingPlan::getStatus, bo.getStatus());
|
||||
lqw.orderByDesc(GearShippingPlan::getPlanDate);
|
||||
lqw.orderByDesc(GearShippingPlan::getCreateTime);
|
||||
return lqw;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean insertByBo(GearShippingPlanBo bo) {
|
||||
GearShippingPlan add = BeanUtil.toBean(bo, GearShippingPlan.class);
|
||||
validEntityBeforeSave(add);
|
||||
boolean flag = baseMapper.insert(add) > 0;
|
||||
if (flag) {
|
||||
bo.setPlanId(add.getPlanId());
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean updateByBo(GearShippingPlanBo bo) {
|
||||
GearShippingPlan update = BeanUtil.toBean(bo, GearShippingPlan.class);
|
||||
validEntityBeforeSave(update);
|
||||
return baseMapper.updateById(update) > 0;
|
||||
}
|
||||
|
||||
private void validEntityBeforeSave(GearShippingPlan entity) {
|
||||
if (entity.getPlanId() == null) {
|
||||
entity.setPlanId(IdUtil.getSnowflakeNextId());
|
||||
}
|
||||
if (StringUtils.isBlank(entity.getStatus())) {
|
||||
entity.setStatus("0");
|
||||
}
|
||||
if (StringUtils.isBlank(entity.getDelFlag())) {
|
||||
entity.setDelFlag("0");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||||
if (isValid && ids != null && !ids.isEmpty()) {
|
||||
Long cnt = shippingOrderMapper.selectCount(Wrappers.lambdaQuery(GearShippingOrder.class)
|
||||
.in(GearShippingOrder::getPlanId, ids));
|
||||
if (cnt != null && cnt > 0) {
|
||||
throw new ServiceException("该计划下存在发货单据,无法删除");
|
||||
}
|
||||
}
|
||||
return baseMapper.deleteBatchIds(ids) > 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -176,6 +177,49 @@ public class GearStockServiceImpl implements IGearStockService {
|
||||
return baseMapper.deleteBatchIds(ids) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Long, BigDecimal> sumQuantityByItemIds(String itemType, List<Long> itemIds) {
|
||||
if (StringUtils.isBlank(itemType) || itemIds == null || itemIds.isEmpty()) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
List<Map<String, Object>> rows = baseMapper.selectSumQuantityByItemIds(itemType, itemIds);
|
||||
Map<Long, BigDecimal> result = new HashMap<>();
|
||||
if (rows == null || rows.isEmpty()) {
|
||||
return result;
|
||||
}
|
||||
for (Map<String, Object> r : rows) {
|
||||
if (r == null) continue;
|
||||
Object idObj = r.get("itemId");
|
||||
Object qtyObj = r.get("quantity");
|
||||
if (idObj == null) continue;
|
||||
Long id = null;
|
||||
if (idObj instanceof Number) {
|
||||
id = ((Number) idObj).longValue();
|
||||
} else {
|
||||
try {
|
||||
id = Long.valueOf(String.valueOf(idObj));
|
||||
} catch (Exception ignored) {
|
||||
id = null;
|
||||
}
|
||||
}
|
||||
if (id == null) continue;
|
||||
BigDecimal qty = BigDecimal.ZERO;
|
||||
if (qtyObj instanceof BigDecimal) {
|
||||
qty = (BigDecimal) qtyObj;
|
||||
} else if (qtyObj instanceof Number) {
|
||||
qty = BigDecimal.valueOf(((Number) qtyObj).doubleValue());
|
||||
} else if (qtyObj != null) {
|
||||
try {
|
||||
qty = new BigDecimal(String.valueOf(qtyObj));
|
||||
} catch (Exception ignored) {
|
||||
qty = BigDecimal.ZERO;
|
||||
}
|
||||
}
|
||||
result.put(id, qty);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public BigDecimal getStockByItemId(Long rawMaterialId) {
|
||||
// return baseMapper.getStockByItemId(rawMaterialId);
|
||||
|
||||
@@ -20,15 +20,31 @@
|
||||
<result property="noTaxPrice" column="no_tax_price"/>
|
||||
</resultMap>
|
||||
<select id="selectVoListByOrderId" resultType="com.gear.oa.domain.vo.GearOrderDetailVo">
|
||||
SELECT * FROM gear_order_detail WHERE order_id = #{orderId} AND del_flag = 0
|
||||
SELECT
|
||||
d.*,
|
||||
COALESCE(p.product_name, mp.product_name) AS productName,
|
||||
COALESCE(p.product_code, CAST(mp.product_id AS CHAR)) AS productCode,
|
||||
COALESCE(p.type, mp.product_type) AS productType,
|
||||
mp.spec AS spec,
|
||||
mp.model AS model,
|
||||
mp.unit_price AS unitPrice
|
||||
FROM gear_order_detail d
|
||||
LEFT JOIN gear_product p ON d.product_id = p.product_id
|
||||
LEFT JOIN mat_product mp ON d.product_id = mp.product_id
|
||||
WHERE d.order_id = #{orderId} AND d.del_flag = 0
|
||||
</select>
|
||||
<select id="selectVoPagePlus" resultType="com.gear.oa.domain.vo.GearOrderDetailVo">
|
||||
SELECT
|
||||
d.*,
|
||||
p.product_name AS productName,
|
||||
p.product_code AS productCode
|
||||
COALESCE(p.product_name, mp.product_name) AS productName,
|
||||
COALESCE(p.product_code, CAST(mp.product_id AS CHAR)) AS productCode,
|
||||
COALESCE(p.type, mp.product_type) AS productType,
|
||||
mp.spec AS spec,
|
||||
mp.model AS model,
|
||||
mp.unit_price AS unitPrice
|
||||
FROM gear_order_detail d
|
||||
LEFT JOIN gear_product p ON d.product_id = p.product_id
|
||||
LEFT JOIN mat_product mp ON d.product_id = mp.product_id
|
||||
${ew.customSqlSegment}
|
||||
</select>
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="customerId" column="customer_id"/>
|
||||
<result property="company" column="company"/>
|
||||
<result property="salesManager" column="sales_manager"/>
|
||||
<result property="salesmanId" column="salesman_id"/>
|
||||
<result property="orderStatus" column="order_status"/>
|
||||
<result property="tradeType" column="trade_type"/>
|
||||
<result property="remark" column="remark"/>
|
||||
@@ -20,14 +21,29 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="updateBy" column="update_by"/>
|
||||
<result property="taxAmount" column="tax_amount"/>
|
||||
<result property="noTaxAmount" column="no_tax_amount"/>
|
||||
<result property="contractExcelOssIds" column="contract_excel_oss_ids"/>
|
||||
</resultMap>
|
||||
<select id="selectVoPlusPage" resultType="com.gear.oa.domain.vo.GearOrderVo">
|
||||
select o.order_id,
|
||||
o.order_code,
|
||||
o.customer_id,
|
||||
o.company,
|
||||
o.sales_manager,
|
||||
o.order_status,
|
||||
o.salesman_id,
|
||||
s.name as sales_manager,
|
||||
(CASE
|
||||
WHEN o.order_status = 3 THEN 3
|
||||
WHEN EXISTS (SELECT 1
|
||||
FROM gear_shipping_order so
|
||||
WHERE so.order_id = o.order_id
|
||||
AND so.del_flag = '0'
|
||||
AND CAST(so.status AS SIGNED) >= 3) THEN 2
|
||||
WHEN EXISTS (SELECT 1
|
||||
FROM gear_shipping_order so
|
||||
WHERE so.order_id = o.order_id
|
||||
AND so.del_flag = '0'
|
||||
AND CAST(so.status AS SIGNED) >= 2) THEN 1
|
||||
ELSE o.order_status
|
||||
END) AS order_status,
|
||||
o.remark,
|
||||
o.del_flag,
|
||||
o.create_time,
|
||||
@@ -36,9 +52,28 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
o.update_by,
|
||||
o.tax_amount,
|
||||
o.no_tax_amount,
|
||||
c.name as customerName
|
||||
o.contract_excel_oss_ids as contractExcelOssIds,
|
||||
c.name as customerName,
|
||||
(SELECT MAX(CAST(so.status AS SIGNED))
|
||||
FROM gear_shipping_order so
|
||||
WHERE so.order_id = o.order_id
|
||||
AND so.del_flag = '0') AS shippingMaxStatus,
|
||||
(CASE
|
||||
WHEN EXISTS (SELECT 1
|
||||
FROM gear_shipping_order so
|
||||
WHERE so.order_id = o.order_id
|
||||
AND so.del_flag = '0'
|
||||
AND CAST(so.status AS SIGNED) >= 2) THEN 1
|
||||
ELSE 0
|
||||
END) AS shippedFlag,
|
||||
(SELECT MAX(so.ship_time)
|
||||
FROM gear_shipping_order so
|
||||
WHERE so.order_id = o.order_id
|
||||
AND so.del_flag = '0'
|
||||
AND CAST(so.status AS SIGNED) >= 2) AS lastShipTime
|
||||
from gear_order o
|
||||
left join gear_customer c on o.customer_id = c.customer_id and c.del_flag = 0
|
||||
left join gear_salesman s on o.salesman_id = s.salesman_id and s.del_flag = 0
|
||||
${ew.customSqlSegment}
|
||||
</select>
|
||||
<select id="selectVoPagePlus" resultType="com.gear.oa.domain.vo.GearOrderVo">
|
||||
@@ -46,8 +81,22 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
o.order_code,
|
||||
o.customer_id,
|
||||
o.company,
|
||||
o.sales_manager,
|
||||
o.order_status,
|
||||
o.salesman_id,
|
||||
s.name as sales_manager,
|
||||
(CASE
|
||||
WHEN o.order_status = 3 THEN 3
|
||||
WHEN EXISTS (SELECT 1
|
||||
FROM gear_shipping_order so
|
||||
WHERE so.order_id = o.order_id
|
||||
AND so.del_flag = '0'
|
||||
AND CAST(so.status AS SIGNED) >= 3) THEN 2
|
||||
WHEN EXISTS (SELECT 1
|
||||
FROM gear_shipping_order so
|
||||
WHERE so.order_id = o.order_id
|
||||
AND so.del_flag = '0'
|
||||
AND CAST(so.status AS SIGNED) >= 2) THEN 1
|
||||
ELSE o.order_status
|
||||
END) AS order_status,
|
||||
o.remark,
|
||||
o.del_flag,
|
||||
o.create_time,
|
||||
@@ -56,9 +105,28 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
o.update_by,
|
||||
o.tax_amount,
|
||||
o.no_tax_amount,
|
||||
c.name as customerName
|
||||
o.contract_excel_oss_ids as contractExcelOssIds,
|
||||
c.name as customerName,
|
||||
(SELECT MAX(CAST(so.status AS SIGNED))
|
||||
FROM gear_shipping_order so
|
||||
WHERE so.order_id = o.order_id
|
||||
AND so.del_flag = '0') AS shippingMaxStatus,
|
||||
(CASE
|
||||
WHEN EXISTS (SELECT 1
|
||||
FROM gear_shipping_order so
|
||||
WHERE so.order_id = o.order_id
|
||||
AND so.del_flag = '0'
|
||||
AND CAST(so.status AS SIGNED) >= 2) THEN 1
|
||||
ELSE 0
|
||||
END) AS shippedFlag,
|
||||
(SELECT MAX(so.ship_time)
|
||||
FROM gear_shipping_order so
|
||||
WHERE so.order_id = o.order_id
|
||||
AND so.del_flag = '0'
|
||||
AND CAST(so.status AS SIGNED) >= 2) AS lastShipTime
|
||||
from gear_order o
|
||||
left join gear_customer c on o.customer_id = c.customer_id and c.del_flag = 0
|
||||
left join gear_salesman s on o.salesman_id = s.salesman_id and s.del_flag = 0
|
||||
${ew.customSqlSegment}
|
||||
</select>
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.gear.oa.mapper.GearOrderProductionMapper">
|
||||
|
||||
<resultMap type="com.gear.oa.domain.GearOrderProduction" id="GearOrderProductionResult">
|
||||
<result property="productionId" column="production_id"/>
|
||||
<result property="orderId" column="order_id"/>
|
||||
<result property="orderDetailId" column="order_detail_id"/>
|
||||
<result property="productId" column="product_id"/>
|
||||
<result property="planQty" column="plan_qty"/>
|
||||
<result property="finishedQty" column="finished_qty"/>
|
||||
<result property="badQty" column="bad_qty"/>
|
||||
<result property="unit" column="unit"/>
|
||||
<result property="remark" column="remark"/>
|
||||
<result property="delFlag" column="del_flag"/>
|
||||
<result property="createTime" column="create_time"/>
|
||||
<result property="createBy" column="create_by"/>
|
||||
<result property="updateTime" column="update_time"/>
|
||||
<result property="updateBy" column="update_by"/>
|
||||
</resultMap>
|
||||
|
||||
<select id="selectVoPagePlus" resultType="com.gear.oa.domain.vo.GearOrderProductionVo">
|
||||
SELECT
|
||||
p.*,
|
||||
COALESCE(gp.product_name, mp.product_name) AS productName,
|
||||
COALESCE(gp.product_code, CAST(mp.product_id AS CHAR)) AS productCode,
|
||||
COALESCE(gp.type, mp.product_type) AS productType,
|
||||
mp.spec AS spec,
|
||||
mp.model AS model
|
||||
FROM gear_order_production p
|
||||
LEFT JOIN gear_order_detail d ON p.order_detail_id = d.detail_id
|
||||
LEFT JOIN gear_product gp ON d.product_id = gp.product_id
|
||||
LEFT JOIN mat_product mp ON d.product_id = mp.product_id
|
||||
${ew.customSqlSegment}
|
||||
</select>
|
||||
|
||||
<select id="selectVoListPlus" resultType="com.gear.oa.domain.vo.GearOrderProductionVo">
|
||||
SELECT
|
||||
p.*,
|
||||
COALESCE(gp.product_name, mp.product_name) AS productName,
|
||||
COALESCE(gp.product_code, CAST(mp.product_id AS CHAR)) AS productCode,
|
||||
COALESCE(gp.type, mp.product_type) AS productType,
|
||||
mp.spec AS spec,
|
||||
mp.model AS model
|
||||
FROM gear_order_production p
|
||||
LEFT JOIN gear_order_detail d ON p.order_detail_id = d.detail_id
|
||||
LEFT JOIN gear_product gp ON d.product_id = gp.product_id
|
||||
LEFT JOIN mat_product mp ON d.product_id = mp.product_id
|
||||
${ew.customSqlSegment}
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -25,6 +25,18 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
where item_id = #{rawMaterialId} and item_type = 'raw_material' and del_flag = 0
|
||||
</select>
|
||||
|
||||
<select id="selectSumQuantityByItemIds" resultType="java.util.HashMap">
|
||||
SELECT item_id AS itemId, SUM(quantity) AS quantity
|
||||
FROM gear_stock
|
||||
WHERE del_flag = 0
|
||||
AND item_type = #{itemType}
|
||||
AND item_id IN
|
||||
<foreach collection="itemIds" item="id" open="(" close=")" separator=",">
|
||||
#{id}
|
||||
</foreach>
|
||||
GROUP BY item_id
|
||||
</select>
|
||||
|
||||
<!-- 分页联查物品名称和编码,支持Wrapper动态条件,返回Page<GearStockVo> -->
|
||||
<select id="selectVoPagePlus" resultType="com.gear.oa.domain.vo.GearStockVo">
|
||||
SELECT
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "2.3.1",
|
||||
"@tailwindcss/vite": "^4.1.11",
|
||||
"@vue-office/docx": "^1.6.3",
|
||||
"@vue-office/excel": "^1.7.14",
|
||||
"@vueup/vue-quill": "1.2.0",
|
||||
"@vueuse/core": "13.3.0",
|
||||
"axios": "1.9.0",
|
||||
@@ -27,6 +29,7 @@
|
||||
"element-plus": "2.9.9",
|
||||
"file-saver": "2.0.5",
|
||||
"fuse.js": "6.6.2",
|
||||
"html2canvas": "^1.4.1",
|
||||
"i": "^0.3.7",
|
||||
"js-beautify": "1.14.11",
|
||||
"js-cookie": "3.0.5",
|
||||
|
||||
@@ -69,3 +69,15 @@ export function importProductData(data, updateSupport) {
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 查询产品附加属性(按产品ID集合返回 Map<productId, attrs>)
|
||||
export function listProductAdditionByProductIds(productIds) {
|
||||
return request({
|
||||
url: '/api/mat/productAddition/listByProductIds',
|
||||
method: 'post',
|
||||
data: productIds,
|
||||
headers: {
|
||||
repeatSubmit: false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -47,6 +47,9 @@ export function listProductAdditionByProductIds(productIds) {
|
||||
return request({
|
||||
url: '/api/mat/productAddition/listByProductIds',
|
||||
method: 'post',
|
||||
data: productIds
|
||||
data: productIds,
|
||||
headers: {
|
||||
repeatSubmit: false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
27
gear-ui3/src/api/oms/orderProduction.js
Normal file
27
gear-ui3/src/api/oms/orderProduction.js
Normal file
@@ -0,0 +1,27 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 订单生产记录(gear_order_production)
|
||||
|
||||
export function listOrderProduction(query) {
|
||||
return request({
|
||||
url: '/oa/orderProduction/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function initOrderProduction(orderId) {
|
||||
return request({
|
||||
url: '/oa/orderProduction/init/' + orderId,
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
||||
export function updateOrderProduction(data) {
|
||||
return request({
|
||||
url: '/oa/orderProduction',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
54
gear-ui3/src/api/oms/salesman.js
Normal file
54
gear-ui3/src/api/oms/salesman.js
Normal file
@@ -0,0 +1,54 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询销售员列表
|
||||
export function listSalesman(query) {
|
||||
return request({
|
||||
url: '/oa/salesman/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询销售员详情
|
||||
export function getSalesman(salesmanId) {
|
||||
return request({
|
||||
url: '/oa/salesman/' + salesmanId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增销售员
|
||||
export function addSalesman(data) {
|
||||
return request({
|
||||
url: '/oa/salesman',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改销售员
|
||||
export function updateSalesman(data) {
|
||||
return request({
|
||||
url: '/oa/salesman',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除销售员
|
||||
export function delSalesman(salesmanIds) {
|
||||
return request({
|
||||
url: '/oa/salesman/' + salesmanIds,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询销售员跟进客户
|
||||
export function listSalesmanCustomers(salesmanId, query) {
|
||||
return request({
|
||||
url: '/oa/salesman/' + salesmanId + '/customers',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
111
gear-ui3/src/api/oms/shippingOrder.js
Normal file
111
gear-ui3/src/api/oms/shippingOrder.js
Normal file
@@ -0,0 +1,111 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 发货单据(独立表 gear_shipping_order)
|
||||
// 说明:用于订单发货/物流信息记录,不与出入库/WMS 单据绑定
|
||||
|
||||
// 查询发货单据列表
|
||||
export function listShippingOrder(query) {
|
||||
return request({
|
||||
url: '/oa/shippingOrder/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 根据订单ID查询发货单据列表
|
||||
export function listShippingOrderByOrderId(orderId) {
|
||||
return request({
|
||||
url: '/oa/shippingOrder/listByOrderId/' + orderId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询发货单据详细
|
||||
export function getShippingOrder(shippingId) {
|
||||
return request({
|
||||
url: '/oa/shippingOrder/' + shippingId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增发货单据
|
||||
export function addShippingOrder(data) {
|
||||
return request({
|
||||
url: '/oa/shippingOrder',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改发货单据
|
||||
export function updateShippingOrder(data) {
|
||||
return request({
|
||||
url: '/oa/shippingOrder',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除发货单据
|
||||
export function delShippingOrder(shippingId) {
|
||||
return request({
|
||||
url: '/oa/shippingOrder/' + shippingId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// ================================
|
||||
// 发货计划(独立表 gear_shipping_plan)
|
||||
// ================================
|
||||
|
||||
// 查询发货计划列表(分页)
|
||||
export function listShippingPlan(query) {
|
||||
return request({
|
||||
url: '/oa/shippingPlan/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询发货计划列表(带单据数,左侧列表使用)
|
||||
export function listShippingPlanWithCount(query) {
|
||||
return request({
|
||||
url: '/oa/shippingPlan/listWithCount',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询发货计划详细
|
||||
export function getShippingPlan(planId) {
|
||||
return request({
|
||||
url: '/oa/shippingPlan/' + planId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增发货计划
|
||||
export function addShippingPlan(data) {
|
||||
return request({
|
||||
url: '/oa/shippingPlan',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改发货计划
|
||||
export function updateShippingPlan(data) {
|
||||
return request({
|
||||
url: '/oa/shippingPlan',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除发货计划
|
||||
export function delShippingPlan(planId) {
|
||||
return request({
|
||||
url: '/oa/shippingPlan/' + planId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
@@ -53,3 +53,17 @@ export function getStockTrace(batchNo) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function sumStockQuantityByItemIds(itemType, itemIds) {
|
||||
return request({
|
||||
url: '/gear/stock/sumQuantityByItemIds',
|
||||
method: 'post',
|
||||
params: {
|
||||
itemType: itemType || 'product'
|
||||
},
|
||||
data: itemIds || [],
|
||||
headers: {
|
||||
repeatSubmit: false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -88,13 +88,13 @@ const renderChart = () => {
|
||||
if (!productStats[productName]) {
|
||||
productStats[productName] = {
|
||||
quantity: 0, // 销量
|
||||
amount: 0 // 销售额(销量*含税单价)
|
||||
amount: 0 // 销售额(销量*单价)
|
||||
};
|
||||
}
|
||||
// 累加销量
|
||||
productStats[productName].quantity += Number(detail.quantity || 0);
|
||||
// 累加销售额
|
||||
const unitPrice = Number(detail.taxPrice || 0);
|
||||
const unitPrice = Number(detail.unitPrice || 0);
|
||||
productStats[productName].amount += unitPrice * Number(detail.quantity || 0);
|
||||
});
|
||||
|
||||
@@ -200,4 +200,4 @@ const handleResize = () => {
|
||||
width: 100%;
|
||||
height: calc(100% - 40px);
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
626
gear-ui3/src/views/mat/sales/order/index.vue
Normal file
626
gear-ui3/src/views/mat/sales/order/index.vue
Normal file
@@ -0,0 +1,626 @@
|
||||
<template>
|
||||
<div class="app-container sales-order-page">
|
||||
<!-- 左右两栏:左侧合同列表 + 右侧详情 -->
|
||||
<el-row :gutter="16">
|
||||
<!-- 左侧:列表区 -->
|
||||
<el-col :span="6" class="left-pane">
|
||||
<!-- 顶部:搜索 + 操作 -->
|
||||
<div class="left-toolbar">
|
||||
<el-input
|
||||
v-model="keyword"
|
||||
clearable
|
||||
placeholder="请输入关键字"
|
||||
@clear="handleQuery"
|
||||
@keyup.enter="handleQuery"
|
||||
>
|
||||
<template #append>
|
||||
<el-button icon="Search" @click="handleQuery" />
|
||||
</template>
|
||||
</el-input>
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 列表:使用现有 GearList 组件实现“卡片式” -->
|
||||
<klp-list
|
||||
:list-data="filteredList"
|
||||
list-key="contractId"
|
||||
:loading="loading"
|
||||
info1-field="contractTitle"
|
||||
info1-max-percent="70"
|
||||
info5-field="contractCode"
|
||||
@item-click="handleSelect"
|
||||
>
|
||||
<!-- 主标题:合同名称 + 合同编号 -->
|
||||
<template #info1="{ item }">
|
||||
<span class="list-title">{{ item.contractTitle }}</span>
|
||||
<span class="list-code">{{ item.contractCode }}</span>
|
||||
</template>
|
||||
|
||||
<!-- 副标题:甲/乙方 -->
|
||||
<template #info2="{ item }">
|
||||
<div class="list-sub">
|
||||
<span>供方:{{ item.sellerName }}</span>
|
||||
<span class="ml-8">需方:{{ item.buyerName }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 状态 -->
|
||||
<template #info3="{ item }">
|
||||
<el-tag :type="item.status === '已生效' ? 'success' : 'info'" size="small">
|
||||
{{ item.status }}
|
||||
</el-tag>
|
||||
</template>
|
||||
|
||||
<!-- 时间信息 -->
|
||||
<template #info4="{ item }">
|
||||
<div class="list-sub">
|
||||
<span>签订时间:{{ item.signDate }}</span>
|
||||
<span class="ml-8">交货日期:{{ item.deliveryDate }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 右侧动作:置顶/导出/修改/删除(雏形,只做 UI) -->
|
||||
<template #actions="{ item }">
|
||||
<div class="list-actions">
|
||||
<el-button link type="primary" icon="Top" @click.stop="handlePin(item)">置顶</el-button>
|
||||
<el-button link type="primary" icon="Download" @click.stop="handleExport(item)">导出</el-button>
|
||||
<el-button link type="primary" icon="Edit" @click.stop="handleEdit(item)">修改</el-button>
|
||||
<el-button link type="danger" icon="Delete" @click.stop="handleDelete(item)">删除</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</klp-list>
|
||||
</el-col>
|
||||
|
||||
<!-- 右侧:详情区 -->
|
||||
<el-col :span="18" class="right-pane">
|
||||
<!-- 未选中时提示 -->
|
||||
<div v-if="!selected" class="empty-tip">
|
||||
<el-empty description="请选择左侧一个订单查看详情" />
|
||||
</div>
|
||||
|
||||
<!-- 选中后显示详情 -->
|
||||
<div v-else>
|
||||
<!-- 顶部:标题 + 状态选择(雏形) -->
|
||||
<div class="detail-header">
|
||||
<div class="detail-header__title">
|
||||
<span class="detail-title">产品销售合同</span>
|
||||
<span class="detail-code">{{ selected.contractCode }}</span>
|
||||
</div>
|
||||
<el-select v-model="selected.contractStatus" size="small" style="width: 140px">
|
||||
<el-option label="草稿" value="草稿" />
|
||||
<el-option label="已生效" value="已生效" />
|
||||
<el-option label="已作废" value="已作废" />
|
||||
</el-select>
|
||||
</div>
|
||||
|
||||
<!-- 合同基础信息:用“预览Excel表”风格展示(对齐产品管理-说明书预览的观感:边框+圆角+表格单元格) -->
|
||||
<div class="contract-excel-preview">
|
||||
<div class="contract-excel-title">
|
||||
<span>合同基础信息</span>
|
||||
<div class="contract-excel-title__actions">
|
||||
<!-- 上传合同Excel:雏形直接保存ossId串到 selected.contractExcelOssIds,后续可落库到订单主表 -->
|
||||
<file-upload
|
||||
v-model="selected.contractExcelOssIds"
|
||||
:limit="1"
|
||||
:file-size="20"
|
||||
:file-type="['xls', 'xlsx']"
|
||||
:is-show-tip="false"
|
||||
@success="handleContractExcelUploaded"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="contract-excel-body">
|
||||
<table class="excel-table">
|
||||
<colgroup>
|
||||
<col style="width: 14%" />
|
||||
<col style="width: 36%" />
|
||||
<col style="width: 14%" />
|
||||
<col style="width: 36%" />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="excel-cell excel-cell--label">合同编号</td>
|
||||
<td class="excel-cell">{{ selected.contractCode }}</td>
|
||||
<td class="excel-cell excel-cell--label">合同状态</td>
|
||||
<td class="excel-cell">{{ selected.contractStatus }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="excel-cell excel-cell--label">供方</td>
|
||||
<td class="excel-cell">{{ selected.sellerName }}</td>
|
||||
<td class="excel-cell excel-cell--label">需方</td>
|
||||
<td class="excel-cell">{{ selected.buyerName }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="excel-cell excel-cell--label">签订时间</td>
|
||||
<td class="excel-cell">{{ selected.signDate }}</td>
|
||||
<td class="excel-cell excel-cell--label">交货日期</td>
|
||||
<td class="excel-cell">{{ selected.deliveryDate }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="excel-cell excel-cell--label">签订地点</td>
|
||||
<td class="excel-cell">{{ selected.signPlace }}</td>
|
||||
<td class="excel-cell excel-cell--label">备注</td>
|
||||
<td class="excel-cell">{{ selected.remark || '—' }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 产品内容:产品名称 + 生产厂家 -->
|
||||
<div class="section">
|
||||
<div class="section-title">产品内容</div>
|
||||
<div class="section-body">
|
||||
<div class="product-top">
|
||||
<div class="product-top__left">
|
||||
<span class="label">产品名称:</span>
|
||||
<el-select v-model="selected.productName" filterable clearable style="width: 240px">
|
||||
<el-option v-for="name in productNameOptions" :key="name" :label="name" :value="name" />
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="product-top__right">
|
||||
<span class="label">生产厂家:</span>
|
||||
<span class="value">{{ selected.factoryName }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 产品明细:雏形表格,支持行内编辑与汇总 -->
|
||||
<el-table
|
||||
:data="selected.items"
|
||||
border
|
||||
size="small"
|
||||
class="detail-table"
|
||||
show-summary
|
||||
:summary-method="summaryMethod"
|
||||
>
|
||||
<el-table-column type="index" width="60" label="序号" align="center" />
|
||||
<el-table-column label="规格(mm)" min-width="160">
|
||||
<template #default="{ row }">
|
||||
<el-input v-model="row.spec" size="small" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="材质" width="120">
|
||||
<template #default="{ row }">
|
||||
<el-input v-model="row.material" size="small" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="数量(吨)" width="140" align="right">
|
||||
<template #default="{ row }">
|
||||
<el-input-number v-model="row.qty" :controls="false" :min="0" size="small" style="width: 120px" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" min-width="160">
|
||||
<template #default="{ row }">
|
||||
<el-input v-model="row.remark" size="small" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 业务 Tab:雏形占位 -->
|
||||
<el-tabs v-model="activeTab" type="border-card" class="mt-12">
|
||||
<el-tab-pane label="订单信息" name="orderInfo">
|
||||
<el-empty description="订单信息(待完善)" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="财务状态" name="finance">
|
||||
<div class="section">
|
||||
<div class="section-title">财务状态</div>
|
||||
<div class="finance-strip">
|
||||
<div class="finance-item">
|
||||
<div class="finance-item__label">订单金额</div>
|
||||
<div class="finance-item__value">{{ formatMoney(financeSummary.orderAmount) }}</div>
|
||||
</div>
|
||||
<div class="finance-item">
|
||||
<div class="finance-item__label">已收款金额</div>
|
||||
<div class="finance-item__value">{{ formatMoney(financeSummary.receivedAmount) }}</div>
|
||||
</div>
|
||||
<div class="finance-item">
|
||||
<div class="finance-item__label">未收款金额</div>
|
||||
<div class="finance-item__value">{{ formatMoney(financeSummary.unreceivedAmount) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section-title mt-12">收款明细</div>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" size="small" @click="handleAddPayment">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" plain icon="Edit" size="small" :disabled="true">修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="Delete" size="small" :disabled="true">删除</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="Download" size="small" @click="handleExportPayments">导出</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-table :data="paymentList" border size="small">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="收款日期" prop="payDate" width="160" />
|
||||
<el-table-column label="收款金额" prop="amount" width="160" align="right">
|
||||
<template #default="{ row }">
|
||||
{{ formatMoney(row.amount) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" prop="remark" />
|
||||
<el-table-column label="操作" width="140" align="center">
|
||||
<template #default>
|
||||
<el-button link type="primary" size="small" icon="Edit">修改</el-button>
|
||||
<el-button link type="danger" size="small" icon="Delete">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="生产成本" name="cost">
|
||||
<el-empty description="生产成本(待完善)" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="发货流程" name="ship">
|
||||
<el-empty description="发货流程(待完善)" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="合同附件" name="files">
|
||||
<el-empty description="合同附件(待完善)" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="操作记录" name="logs">
|
||||
<el-empty description="操作记录(待完善)" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import klpList from "@/components/GearList/index.vue";
|
||||
import FileUpload from "@/components/FileUpload/index.vue";
|
||||
|
||||
export default {
|
||||
name: "MatSalesOrder",
|
||||
components: { klpList, FileUpload },
|
||||
data() {
|
||||
return {
|
||||
// 页面加载态:雏形直接用本地数据,保留 loading 方便后续接接口
|
||||
loading: false,
|
||||
// 左侧搜索关键字
|
||||
keyword: "",
|
||||
// 当前选中的合同/订单
|
||||
selected: null,
|
||||
// 右侧 Tab 当前激活项
|
||||
activeTab: "finance",
|
||||
// 产品名称下拉:雏形静态配置,后续可接产品字典/接口
|
||||
productNameOptions: ["冷轧钢卷", "热轧钢卷", "镀锌卷", "不锈钢卷"],
|
||||
// 左侧列表:雏形静态数据,后续替换为接口分页
|
||||
list: [
|
||||
{
|
||||
contractId: 1,
|
||||
contractTitle: "产品销售合同",
|
||||
contractCode: "KLPYX260420-14",
|
||||
sellerName: "嘉祥科伦管理有限公司",
|
||||
buyerName: "洛阳一石科技有限公司",
|
||||
signDate: "2026-05-12",
|
||||
deliveryDate: "2026-06-15",
|
||||
signPlace: "嘉祥",
|
||||
status: "草稿",
|
||||
contractStatus: "草稿",
|
||||
productName: "冷轧钢卷",
|
||||
factoryName: "嘉祥科伦管理有限公司",
|
||||
remark: "",
|
||||
// 合同Excel附件(ossId串,逗号分隔)
|
||||
contractExcelOssIds: "",
|
||||
items: [
|
||||
{ spec: "0.38*1200", material: "SPCC", qty: 10, remark: "净边料 角包 卷" }
|
||||
]
|
||||
},
|
||||
{
|
||||
contractId: 2,
|
||||
contractTitle: "产品销售合同",
|
||||
contractCode: "KLPYX260420-06",
|
||||
sellerName: "嘉祥科伦管理有限公司",
|
||||
buyerName: "湖北广化工贸有限公司",
|
||||
signDate: "2026-04-20",
|
||||
deliveryDate: "2026-05-25",
|
||||
signPlace: "嘉祥",
|
||||
status: "已生效",
|
||||
contractStatus: "已生效",
|
||||
productName: "热轧钢卷",
|
||||
factoryName: "嘉祥科伦管理有限公司",
|
||||
remark: "",
|
||||
// 合同Excel附件(ossId串,逗号分隔)
|
||||
contractExcelOssIds: "",
|
||||
items: [
|
||||
{ spec: "2.0*1250", material: "Q235B", qty: 5, remark: "" }
|
||||
]
|
||||
}
|
||||
],
|
||||
// 财务状态:雏形数据
|
||||
financeSummary: {
|
||||
orderAmount: 30000,
|
||||
receivedAmount: 0,
|
||||
unreceivedAmount: 30000
|
||||
},
|
||||
// 收款明细:雏形为空数据
|
||||
paymentList: []
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// 左侧列表过滤:关键字匹配合同号/甲乙方
|
||||
filteredList() {
|
||||
const kw = (this.keyword || "").trim();
|
||||
if (!kw) return this.list;
|
||||
return this.list.filter((it) => {
|
||||
return (
|
||||
(it.contractCode || "").includes(kw) ||
|
||||
(it.sellerName || "").includes(kw) ||
|
||||
(it.buyerName || "").includes(kw)
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 搜索:雏形仅触发计算属性刷新,后续可接接口
|
||||
handleQuery() {},
|
||||
// 左侧选中:加载详情(雏形直接赋值)
|
||||
handleSelect(item) {
|
||||
this.selected = item || null;
|
||||
this.activeTab = "finance";
|
||||
this.syncFinanceSummary();
|
||||
},
|
||||
// 新增:雏形占位,后续可弹窗录入合同/订单信息
|
||||
handleAdd() {
|
||||
this.$modal && this.$modal.msgInfo && this.$modal.msgInfo("新增(雏形)");
|
||||
},
|
||||
// 置顶:雏形占位
|
||||
handlePin() {},
|
||||
// 导出:雏形占位
|
||||
handleExport() {},
|
||||
// 修改:雏形占位
|
||||
handleEdit() {},
|
||||
// 删除:雏形占位
|
||||
handleDelete() {},
|
||||
// 财务:新增收款明细(雏形占位)
|
||||
handleAddPayment() {
|
||||
this.$modal && this.$modal.msgInfo && this.$modal.msgInfo("新增收款明细(雏形)");
|
||||
},
|
||||
// 财务:导出收款明细(雏形占位)
|
||||
handleExportPayments() {},
|
||||
// 金额格式化:雏形使用 toFixed
|
||||
formatMoney(val) {
|
||||
const n = Number(val || 0);
|
||||
if (Number.isNaN(n)) return "0.00";
|
||||
return n.toFixed(2);
|
||||
},
|
||||
// 表格汇总:合计数量/金额(雏形)
|
||||
summaryMethod({ columns, data }) {
|
||||
const sums = [];
|
||||
const totalQty = data.reduce((acc, it) => acc + Number(it.qty || 0), 0);
|
||||
|
||||
columns.forEach((col, index) => {
|
||||
if (index === 0) {
|
||||
sums[index] = "合计";
|
||||
return;
|
||||
}
|
||||
if (col.label === "数量(吨)") {
|
||||
sums[index] = totalQty;
|
||||
return;
|
||||
}
|
||||
sums[index] = "";
|
||||
});
|
||||
|
||||
return sums;
|
||||
},
|
||||
// 根据明细同步财务汇总(雏形:用含税总额当订单金额)
|
||||
syncFinanceSummary() {
|
||||
if (!this.selected) return;
|
||||
const orderAmount = 0;
|
||||
const receivedAmount = (this.paymentList || []).reduce((acc, it) => acc + Number(it.amount || 0), 0);
|
||||
this.financeSummary.orderAmount = orderAmount;
|
||||
this.financeSummary.receivedAmount = receivedAmount;
|
||||
this.financeSummary.unreceivedAmount = orderAmount - receivedAmount;
|
||||
},
|
||||
// 合同Excel上传完成:雏形仅做提示,后续可在这里调用接口保存到数据库
|
||||
handleContractExcelUploaded() {
|
||||
this.$modal && this.$modal.msgSuccess && this.$modal.msgSuccess("合同Excel已上传");
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 默认选中第一条,方便看到右侧雏形效果
|
||||
if (this.list.length > 0) {
|
||||
this.handleSelect(this.list[0]);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.sales-order-page {
|
||||
height: calc(100vh - 120px);
|
||||
}
|
||||
|
||||
.left-pane {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.left-toolbar {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.list-title {
|
||||
font-weight: 600;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.list-code {
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.list-sub {
|
||||
color: #606266;
|
||||
font-size: 12px;
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.ml-8 {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.list-actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.right-pane {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.empty-tip {
|
||||
height: 520px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.detail-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.detail-header__title {
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.detail-title {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.detail-code {
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.contract-excel-preview {
|
||||
border: 1px solid var(--el-border-color-light);
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.contract-excel-title {
|
||||
padding: 8px 10px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
background: var(--el-fill-color-lighter);
|
||||
border-bottom: 1px solid var(--el-border-color-light);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.contract-excel-title__actions :deep(.el-button) {
|
||||
height: 28px;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.contract-excel-title__actions :deep(.upload-file-list) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.contract-excel-body {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.excel-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
.excel-cell {
|
||||
border: 1px solid var(--el-border-color-light);
|
||||
padding: 8px 10px;
|
||||
font-size: 12px;
|
||||
color: #303133;
|
||||
word-break: break-word;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.excel-cell--label {
|
||||
background: var(--el-fill-color-light);
|
||||
color: #606266;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.section-body {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.product-top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.label {
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.detail-table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.mt-12 {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.finance-strip {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.finance-item {
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 6px;
|
||||
padding: 10px 12px;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.finance-item__label {
|
||||
color: #909399;
|
||||
font-size: 12px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.finance-item__value {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -26,17 +26,6 @@
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card class="stat-card">
|
||||
<div class="flex justify-between items-center">
|
||||
<div>
|
||||
<p class="text-gray-500 text-sm">总销售额</p>
|
||||
<h3 class="text-2xl font-bold">¥{{ totalSales.toFixed(2) }}</h3>
|
||||
</div>
|
||||
<el-icon class="text-warning text-xl"><Money /></el-icon>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card class="stat-card">
|
||||
<div class="flex justify-between items-center">
|
||||
@@ -74,7 +63,6 @@
|
||||
<span>产品销售排行</span>
|
||||
<el-select v-model="productRankType" style="width: 100px;" placeholder="统计类型" size="small" @change="renderProductRankChart">
|
||||
<el-option label="销量" value="quantity" />
|
||||
<el-option label="销售额" value="amount" />
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
@@ -119,12 +107,6 @@
|
||||
<el-table-column prop="orderCode" label="订单编号" />
|
||||
<el-table-column prop="customerName" label="客户名称" />
|
||||
<el-table-column prop="salesManager" label="销售经理" />
|
||||
<el-table-column
|
||||
prop="taxAmount"
|
||||
label="订单金额"
|
||||
align="right"
|
||||
:formatter="(row) => `¥${Number(row.taxAmount).toFixed(2)}`"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="orderStatus"
|
||||
label="订单状态"
|
||||
@@ -155,7 +137,7 @@
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import * as echarts from 'echarts'
|
||||
import {
|
||||
UserFilled, ShoppingCart, Money, RefreshRight
|
||||
UserFilled, ShoppingCart, RefreshRight
|
||||
} from '@element-plus/icons-vue'
|
||||
import { listOrder } from '@/api/oms/order'
|
||||
import { listOrderDetail } from '@/api/oms/orderDetail'
|
||||
@@ -194,12 +176,6 @@ const followUpStatusLabel = {
|
||||
// 指标计算(computed确保响应式)
|
||||
const totalCustomers = computed(() => customers.value.length)
|
||||
const totalOrders = computed(() => orders.value.length)
|
||||
const totalSales = computed(() => {
|
||||
return orders.value.reduce(
|
||||
(sum, order) => sum + Number(order.taxAmount || 0),
|
||||
0
|
||||
)
|
||||
})
|
||||
const returnExchangeRate = computed(() => {
|
||||
if (totalOrders.value === 0) return 0
|
||||
return (returnExchanges.value.length / totalOrders.value) * 100
|
||||
@@ -250,7 +226,7 @@ onMounted(async () => {
|
||||
|
||||
// ---------- 图表渲染函数 ----------
|
||||
|
||||
// 订单趋势图(双轴:订单数+销售额)
|
||||
// 订单趋势图(订单数)
|
||||
const renderOrderTrendChart = () => {
|
||||
const chartDom = orderTrendChart.value
|
||||
if (!chartDom) return
|
||||
@@ -266,19 +242,16 @@ const renderOrderTrendChart = () => {
|
||||
dates.push(date.toLocaleDateString())
|
||||
}
|
||||
|
||||
// 按日期统计订单数和销售额
|
||||
// 按日期统计订单数
|
||||
const orderCountMap = {}
|
||||
const salesMap = {}
|
||||
dates.forEach(date => {
|
||||
orderCountMap[date] = 0
|
||||
salesMap[date] = 0
|
||||
})
|
||||
orders.value.forEach(order => {
|
||||
// 假设订单有createTime字段,需与dates格式匹配
|
||||
const orderDate = new Date(order.createTime).toLocaleDateString()
|
||||
if (orderCountMap[orderDate] !== undefined) {
|
||||
orderCountMap[orderDate]++
|
||||
salesMap[orderDate] += Number(order.taxAmount || 0)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -287,35 +260,19 @@ const renderOrderTrendChart = () => {
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'cross' }
|
||||
},
|
||||
legend: { data: ['订单数', '销售额'] },
|
||||
legend: { data: ['订单数'] },
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: dates,
|
||||
axisLabel: { rotate: 30, fontSize: 12 }
|
||||
},
|
||||
yAxis: [
|
||||
{ type: 'value', name: '订单数', min: 0 },
|
||||
{
|
||||
type: 'value',
|
||||
name: '销售额',
|
||||
min: 0,
|
||||
axisLabel: { formatter: '¥{value}' }
|
||||
}
|
||||
],
|
||||
yAxis: { type: 'value', name: '订单数', min: 0 },
|
||||
series: [
|
||||
{
|
||||
name: '订单数',
|
||||
type: 'bar',
|
||||
data: dates.map(date => orderCountMap[date]),
|
||||
barWidth: '40%'
|
||||
},
|
||||
{
|
||||
name: '销售额',
|
||||
type: 'line',
|
||||
yAxisIndex: 1,
|
||||
data: dates.map(date => salesMap[date]),
|
||||
smooth: true,
|
||||
lineStyle: { width: 2 }
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -340,7 +297,7 @@ const renderProductRankChart = () => {
|
||||
}
|
||||
productStats[productName].quantity += Number(detail.quantity || 0)
|
||||
productStats[productName].amount +=
|
||||
Number(detail.quantity || 0) * Number(detail.taxPrice || 0)
|
||||
Number(detail.quantity || 0) * Number(detail.unitPrice || 0)
|
||||
})
|
||||
|
||||
// 转换为图表数据(取Top10)
|
||||
@@ -478,4 +435,4 @@ const viewOrderDetail = (row) => {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -16,26 +16,6 @@
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="Download" size="small" @click="handleExport">导出</el-button>
|
||||
</el-col>
|
||||
|
||||
<!-- 含税部分 -->
|
||||
<el-col :span="8">
|
||||
<span>含税金额:</span>
|
||||
<span style="margin-right: 10px;">{{ orderInfo?.taxAmount }}</span>
|
||||
<span style="margin-right: 10px;">-</span>
|
||||
<span style="margin-right: 10px;">{{ actualAmount }}</span>
|
||||
<span style="margin-right: 10px;">=</span>
|
||||
<span style="color: red; margin-right: 10px;">{{ amountDifference }}</span>
|
||||
</el-col>
|
||||
|
||||
<!-- 无税部分 -->
|
||||
<el-col :span="8">
|
||||
<span>无税金额:</span>
|
||||
<span style="margin-right: 10px;">{{ orderInfo?.noTaxAmount }}</span>
|
||||
<span style="margin-right: 10px;">-</span>
|
||||
<span style="margin-right: 10px;">{{ noTaxAmount }}</span>
|
||||
<span style="margin-right: 10px;">=</span>
|
||||
<span style="color: red; margin-right: 10px;">{{ noTaxAmountDifference }}</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="orderDetailList">
|
||||
@@ -56,8 +36,6 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="产品数量" align="center" prop="quantity" />
|
||||
<el-table-column label="单位" align="center" prop="unit" />
|
||||
<el-table-column label="含税单价" align="center" prop="taxPrice" />
|
||||
<el-table-column label="无税单价" align="center" prop="noTaxPrice" />
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column label="操作" align="center" width="200">
|
||||
<template #default="scope">
|
||||
@@ -81,13 +59,6 @@
|
||||
<el-form-item label="单位" prop="unit">
|
||||
<el-input v-model="form.unit" placeholder="单位" />
|
||||
</el-form-item>
|
||||
<el-form-item label="含税单价" prop="taxPrice">
|
||||
<el-input-number :controls=false controls-position="right" v-model="form.taxPrice" placeholder="请输入含税单价" />
|
||||
</el-form-item>
|
||||
<el-form-item label="无税单价" prop="noTaxPrice">
|
||||
<el-input-number :controls=false controls-position="right" v-model="form.noTaxPrice" placeholder="请输入无税单价"
|
||||
:min="0" :max="form.taxPrice" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
@@ -165,22 +136,6 @@ export default {
|
||||
// 是否可以编辑(订单状态为新建时才能编辑)
|
||||
canEdit() {
|
||||
return this.orderInfo && this.orderInfo.orderStatus === EOrderStatus.NEW;
|
||||
},
|
||||
actualAmount() {
|
||||
return this.orderDetailList?.reduce((total, item) => {
|
||||
return total + (item.taxPrice || 0) * (item.quantity || 0);
|
||||
}, 0) || 0;
|
||||
},
|
||||
amountDifference() {
|
||||
return ((this.orderInfo?.taxAmount || 0) - this.actualAmount);
|
||||
},
|
||||
noTaxAmount() {
|
||||
return this.orderDetailList?.reduce((total, item) => {
|
||||
return total + (item.noTaxPrice || 0) * (item.quantity || 0);
|
||||
}, 0) || 0;
|
||||
},
|
||||
noTaxAmountDifference() {
|
||||
return ((this.orderInfo?.noTaxAmount || 0) - this.noTaxAmount);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -224,8 +179,6 @@ export default {
|
||||
orderId: this.orderId,
|
||||
productId: undefined,
|
||||
quantity: undefined,
|
||||
taxPrice: undefined,
|
||||
noTaxPrice: undefined,
|
||||
unit: undefined,
|
||||
remark: undefined,
|
||||
delFlag: undefined,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -94,8 +94,9 @@
|
||||
<!-- 添加或修改应收款管理(宽松版)对话框 -->
|
||||
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="客户ID" prop="customerId">
|
||||
<CustomerSelect v-model="form.customerId" />
|
||||
<el-form-item label="客户" prop="customerId">
|
||||
<el-input v-if="fixedCustomer" :model-value="fixedCustomerLabel" disabled />
|
||||
<CustomerSelect v-else v-model="form.customerId" />
|
||||
</el-form-item>
|
||||
<el-form-item label="到期日" prop="dueDate">
|
||||
<el-date-picker clearable
|
||||
@@ -141,6 +142,7 @@
|
||||
<script>
|
||||
import { listReceivable, getReceivable, delReceivable, addReceivable, updateReceivable, updatePaidAmount } from "@/api/finance/receivable";
|
||||
import CustomerSelect from '@/components/CustomerSelect/index.vue';
|
||||
import { getCustomer } from "@/api/oms/customer";
|
||||
|
||||
export default {
|
||||
name: "Receivable",
|
||||
@@ -151,6 +153,25 @@ export default {
|
||||
orderId: {
|
||||
type: [String, Number],
|
||||
required: true
|
||||
},
|
||||
customerId: {
|
||||
type: [String, Number],
|
||||
required: false,
|
||||
default: undefined
|
||||
},
|
||||
customerName: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: ""
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
fixedCustomer() {
|
||||
return this.customerId != null && String(this.customerId) !== ""
|
||||
},
|
||||
fixedCustomerLabel() {
|
||||
const name = String(this.customerName || this.fixedCustomerName || "").trim()
|
||||
return name || "—"
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -179,7 +200,7 @@ export default {
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 20,
|
||||
customerId: undefined,
|
||||
customerId: this.customerId != null && String(this.customerId) !== "" ? this.customerId : undefined,
|
||||
orderId: this.orderId,
|
||||
dueDate: undefined,
|
||||
amount: undefined,
|
||||
@@ -195,7 +216,8 @@ export default {
|
||||
// 收款表单参数
|
||||
receiveForm: {},
|
||||
// 是否显示收款弹出层
|
||||
receiveOpen: false
|
||||
receiveOpen: false,
|
||||
fixedCustomerName: ""
|
||||
};
|
||||
},
|
||||
created() {
|
||||
@@ -205,9 +227,35 @@ export default {
|
||||
orderId(newVal) {
|
||||
this.queryParams.orderId = newVal;
|
||||
this.getList();
|
||||
},
|
||||
customerId(newVal) {
|
||||
this.queryParams.customerId = newVal != null && String(newVal) !== "" ? newVal : undefined
|
||||
this.loadFixedCustomerName()
|
||||
},
|
||||
customerName() {
|
||||
this.loadFixedCustomerName()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
loadFixedCustomerName() {
|
||||
if (!this.fixedCustomer) {
|
||||
this.fixedCustomerName = ""
|
||||
return
|
||||
}
|
||||
const name = String(this.customerName || "").trim()
|
||||
if (name) {
|
||||
this.fixedCustomerName = name
|
||||
return
|
||||
}
|
||||
const id = this.customerId
|
||||
if (id == null || String(id) === "") return
|
||||
getCustomer(id).then(res => {
|
||||
const d = res && res.data ? res.data : null
|
||||
this.fixedCustomerName = (d && d.name) ? String(d.name) : ""
|
||||
}).catch(() => {
|
||||
this.fixedCustomerName = ""
|
||||
})
|
||||
},
|
||||
/** 查询应收款管理(宽松版)列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
@@ -226,7 +274,7 @@ export default {
|
||||
reset() {
|
||||
this.form = {
|
||||
receivableId: undefined,
|
||||
customerId: undefined,
|
||||
customerId: this.fixedCustomer ? this.customerId : undefined,
|
||||
orderId: this.orderId,
|
||||
dueDate: undefined,
|
||||
amount: undefined,
|
||||
@@ -263,6 +311,7 @@ export default {
|
||||
this.reset();
|
||||
this.open = true;
|
||||
this.title = "添加应收款管理(宽松版)";
|
||||
this.loadFixedCustomerName()
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleUpdate(row) {
|
||||
@@ -291,7 +340,11 @@ export default {
|
||||
this.buttonLoading = false;
|
||||
});
|
||||
} else {
|
||||
addReceivable(this.form).then(response => {
|
||||
const payload = Object.assign({}, this.form, {
|
||||
customerId: this.fixedCustomer ? this.customerId : this.form.customerId,
|
||||
orderId: this.orderId
|
||||
})
|
||||
addReceivable(payload).then(response => {
|
||||
this.$modal.msgSuccess("新增成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
|
||||
761
gear-ui3/src/views/oms/salesman/index.vue
Normal file
761
gear-ui3/src/views/oms/salesman/index.vue
Normal file
@@ -0,0 +1,761 @@
|
||||
<template>
|
||||
<div class="app-container salesman-page">
|
||||
<el-row :gutter="16">
|
||||
<!-- 左侧:销售员列表(对齐示意图的“左列表 + 右明细”布局) -->
|
||||
<el-col :span="6" class="salesman-left">
|
||||
<el-form :model="queryParams" ref="queryRef" label-width="56px" size="small" class="left-filter">
|
||||
<el-form-item label="姓名">
|
||||
<el-input
|
||||
v-model="queryParams.name"
|
||||
clearable
|
||||
placeholder="请输入销售员名称"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="queryParams.status" clearable placeholder="数据状态">
|
||||
<el-option label="正常" :value="0" />
|
||||
<el-option label="停用" :value="1" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<div class="left-actions">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
|
||||
<el-button plain icon="Refresh" @click="getSalesmanList">刷新</el-button>
|
||||
</div>
|
||||
|
||||
<div class="left-list" v-loading="leftLoading">
|
||||
<el-empty v-if="!salesmanList.length && !leftLoading" description="暂无销售员" />
|
||||
<el-scrollbar v-else max-height="calc(100vh - 290px)">
|
||||
<div
|
||||
v-for="item in salesmanList"
|
||||
:key="item.salesmanId"
|
||||
class="salesman-item"
|
||||
:class="{ active: item.salesmanId === selectedSalesmanId }"
|
||||
@click="handleSelect(item)"
|
||||
>
|
||||
<div class="salesman-item__left">
|
||||
<el-tag v-if="item.status === 0" size="small" type="success">正常</el-tag>
|
||||
<el-tag v-else size="small" type="danger">停用</el-tag>
|
||||
<span class="salesman-item__name">{{ item.name }}</span>
|
||||
</div>
|
||||
<div class="salesman-item__actions">
|
||||
<el-button link type="primary" icon="Edit" @click.stop="handleUpdate(item)">修改</el-button>
|
||||
<el-button link type="danger" icon="Delete" @click.stop="handleDelete(item)">删除</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<!-- 右侧:跟进信息(Tab 结构对齐示意图) -->
|
||||
<el-col :span="18" class="salesman-right">
|
||||
<el-card shadow="never">
|
||||
<div class="right-title">
|
||||
<span>销售员:</span>
|
||||
<span class="right-title__name">{{ selectedSalesmanName || "未选择" }}</span>
|
||||
</div>
|
||||
|
||||
<el-tabs v-model="activeTab" class="right-tabs" @tab-change="handleTabChange">
|
||||
<el-tab-pane label="跟进客户" name="followCustomer" />
|
||||
<el-tab-pane label="跟进合同" name="followContract" />
|
||||
<el-tab-pane label="发货单据" name="shippingDocs" />
|
||||
<el-tab-pane label="生产成果" name="production" />
|
||||
<el-tab-pane label="计划发货" name="planShipping" />
|
||||
</el-tabs>
|
||||
|
||||
<div class="right-body">
|
||||
<el-empty v-if="!selectedSalesmanId" description="请先从左侧选择销售员" />
|
||||
|
||||
<template v-else>
|
||||
<div v-show="activeTab === 'followCustomer'">
|
||||
<!-- 跟进客户:从订单反查客户(后端接口 /oa/salesman/{id}/customers) -->
|
||||
<el-table v-loading="customerLoading" :data="customerList">
|
||||
<el-table-column label="公司名称" prop="name" min-width="220" />
|
||||
<el-table-column label="联系方式" min-width="160">
|
||||
<template #default="scope">
|
||||
{{ scope.row.mobile || scope.row.telephone || "-" }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="税号" min-width="160">
|
||||
<template #default>
|
||||
-
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="行业" prop="industryId" width="120" />
|
||||
<el-table-column label="客户等级" prop="level" width="120" />
|
||||
<el-table-column label="地址" prop="detailAddress" min-width="260" show-overflow-tooltip />
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="customerTotal > 0"
|
||||
:total="customerTotal"
|
||||
v-model:page="customerQuery.pageNum"
|
||||
v-model:limit="customerQuery.pageSize"
|
||||
@pagination="loadCustomers"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-show="activeTab === 'followContract'">
|
||||
<!-- 跟进合同:复用现有订单接口(/oa/order/list),以销售经理名称过滤 -->
|
||||
<el-table v-loading="orderLoading" :data="orderList">
|
||||
<el-table-column label="订单编号" prop="orderCode" min-width="160" />
|
||||
<el-table-column label="客户" prop="customerName" min-width="200" />
|
||||
<el-table-column label="状态" prop="orderStatus" width="120" />
|
||||
<el-table-column label="创建时间" prop="createTime" width="180" />
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="orderTotal > 0"
|
||||
:total="orderTotal"
|
||||
v-model:page="orderQuery.pageNum"
|
||||
v-model:limit="orderQuery.pageSize"
|
||||
@pagination="loadOrders"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-show="activeTab === 'shippingDocs'">
|
||||
<el-table v-loading="shippingLoading" :data="shippingList">
|
||||
<el-table-column label="发货单号" prop="shippingNo" min-width="180" />
|
||||
<el-table-column label="订单编号" prop="orderCode" min-width="160" />
|
||||
<el-table-column label="收货单位" prop="receiverCompany" min-width="220" />
|
||||
<el-table-column label="发货时间" prop="shipTime" width="180" />
|
||||
<el-table-column label="物流公司" prop="logisticsCompany" min-width="140" />
|
||||
<el-table-column label="运单号" prop="logisticsNo" min-width="180" />
|
||||
<el-table-column label="状态" prop="status" width="120" />
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<div v-show="activeTab === 'production'">
|
||||
<div class="production-wrap">
|
||||
<div class="production-summary">
|
||||
<div class="production-summary__item">订单数:{{ productionSummary.orderCount }}</div>
|
||||
<div class="production-summary__item">计划总量:{{ formatQty(productionSummary.planQty) }}</div>
|
||||
<div class="production-summary__item">已完成总量:{{ formatQty(productionSummary.finishedQty) }}</div>
|
||||
<div class="production-summary__item">完成率:{{ productionSummary.rateText }}</div>
|
||||
</div>
|
||||
<el-table v-loading="productionLoading" :data="productionOrderSummaryList" size="small" border>
|
||||
<el-table-column label="订单编号" prop="orderCode" min-width="160" />
|
||||
<el-table-column label="客户" prop="customerName" min-width="180" show-overflow-tooltip />
|
||||
<el-table-column label="计划" prop="planQty" width="120" align="center" />
|
||||
<el-table-column label="完成" prop="finishedQty" width="120" align="center" />
|
||||
<el-table-column label="完成率" width="120" align="center">
|
||||
<template #default="scope">
|
||||
{{ formatRate(scope.row.finishedQty, scope.row.planQty) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="最近更新" prop="lastUpdateTime" min-width="160" />
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-show="activeTab === 'planShipping'">
|
||||
<el-card shadow="never">
|
||||
<template #header>
|
||||
<div class="plan-shipping__header">
|
||||
计划发货
|
||||
<span class="plan-shipping__sub">产品明细(按该销售员全部订单汇总)</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="plan-shipping__summary">
|
||||
<div class="plan-shipping__summary-item">总条数:{{ planDetailSummary.totalLines }}</div>
|
||||
<div class="plan-shipping__summary-item">总数量:{{ planDetailSummary.totalQty }}</div>
|
||||
<div class="plan-shipping__summary-item">订单数:{{ planDetailSummary.totalOrders }}</div>
|
||||
<div class="plan-shipping__summary-item">已发货订单:{{ planDetailSummary.shippedOrders }}</div>
|
||||
</div>
|
||||
<el-table
|
||||
v-loading="planDetailLoading"
|
||||
:data="planOrderDetailList"
|
||||
size="small"
|
||||
border
|
||||
>
|
||||
<el-table-column label="订单编号" prop="orderCode" min-width="160" />
|
||||
<el-table-column label="客户" prop="customerName" min-width="160" show-overflow-tooltip />
|
||||
<el-table-column label="产品编号" min-width="140">
|
||||
<template #default="scope">
|
||||
<el-popover placement="right" trigger="hover" width="460">
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="订单编号">{{ scope.row.orderCode || "-" }}</el-descriptions-item>
|
||||
<el-descriptions-item label="客户">{{ scope.row.customerName || "-" }}</el-descriptions-item>
|
||||
<el-descriptions-item label="产品编号">{{ scope.row.productCode || "-" }}</el-descriptions-item>
|
||||
<el-descriptions-item label="产品名称">{{ scope.row.productName || "-" }}</el-descriptions-item>
|
||||
<el-descriptions-item label="规格">{{ scope.row.spec || "-" }}</el-descriptions-item>
|
||||
<el-descriptions-item label="型号">{{ scope.row.model || "-" }}</el-descriptions-item>
|
||||
<el-descriptions-item label="单位">{{ scope.row.unit || "-" }}</el-descriptions-item>
|
||||
<el-descriptions-item label="数量">{{ scope.row.quantity ?? "-" }}</el-descriptions-item>
|
||||
<el-descriptions-item label="备注" :span="2">{{ scope.row.remark || "-" }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<template #reference>
|
||||
<el-link type="primary" :underline="false">{{ scope.row.productCode || "-" }}</el-link>
|
||||
</template>
|
||||
</el-popover>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="产品名称" prop="productName" min-width="180" show-overflow-tooltip />
|
||||
<el-table-column label="规格" prop="spec" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column label="型号" prop="model" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column label="单位" prop="unit" width="90" />
|
||||
<el-table-column label="数量" prop="quantity" width="90" />
|
||||
<el-table-column label="备注" prop="remark" min-width="160" show-overflow-tooltip />
|
||||
<el-table-column label="发货状态" width="110">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.shippedFlag" type="success" size="small">已发货</el-tag>
|
||||
<el-tag v-else type="info" size="small">未发货</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 新增/修改销售员对话框 -->
|
||||
<el-dialog :title="dialogTitle" v-model="dialogOpen" width="520px" append-to-body>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="90px">
|
||||
<el-form-item label="姓名" prop="name">
|
||||
<el-input v-model="form.name" placeholder="请输入销售员姓名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="手机" prop="mobile">
|
||||
<el-input v-model="form.mobile" placeholder="请输入联系电话" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-radio-group v-model="form.status">
|
||||
<el-radio :value="0">正常</el-radio>
|
||||
<el-radio :value="1">停用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button :loading="submitLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="dialogOpen = false">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listSalesman, addSalesman, updateSalesman, delSalesman, listSalesmanCustomers } from "@/api/oms/salesman";
|
||||
import { listOrder } from "@/api/oms/order";
|
||||
import { listShippingOrder } from "@/api/oms/shippingOrder";
|
||||
import { listOrderDetail } from "@/api/oms/orderDetail";
|
||||
import { listOrderProduction } from "@/api/oms/orderProduction";
|
||||
|
||||
export default {
|
||||
name: "Salesman",
|
||||
data() {
|
||||
return {
|
||||
leftLoading: false,
|
||||
salesmanList: [],
|
||||
selectedSalesmanId: undefined,
|
||||
selectedSalesmanName: "",
|
||||
activeTab: "followCustomer",
|
||||
|
||||
// 左侧查询条件(用于筛选销售员列表)
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 9999,
|
||||
name: undefined,
|
||||
status: undefined
|
||||
},
|
||||
|
||||
// 跟进客户
|
||||
customerLoading: false,
|
||||
customerList: [],
|
||||
customerTotal: 0,
|
||||
customerQuery: {
|
||||
pageNum: 1,
|
||||
pageSize: 20
|
||||
},
|
||||
|
||||
// 跟进合同(订单列表)
|
||||
orderLoading: false,
|
||||
orderList: [],
|
||||
orderTotal: 0,
|
||||
orderQuery: {
|
||||
pageNum: 1,
|
||||
pageSize: 20
|
||||
},
|
||||
|
||||
// 发货单据
|
||||
shippingLoading: false,
|
||||
shippingList: [],
|
||||
|
||||
// 生产成果:按订单汇总(只读)
|
||||
productionLoading: false,
|
||||
productionOrderSummaryList: [],
|
||||
|
||||
// 计划发货:产品明细(按销售员全部订单汇总)
|
||||
planDetailLoading: false,
|
||||
planOrderDetailList: [],
|
||||
|
||||
// 新增/修改弹窗
|
||||
dialogOpen: false,
|
||||
dialogTitle: "",
|
||||
submitLoading: false,
|
||||
form: {},
|
||||
rules: {
|
||||
name: [{ required: true, message: "请输入销售员姓名", trigger: "blur" }]
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
planDetailSummary() {
|
||||
const rows = this.planOrderDetailList || [];
|
||||
const totalLines = rows.length;
|
||||
const totalQty = rows.reduce((sum, r) => sum + Number(r && r.quantity != null ? r.quantity : 0), 0);
|
||||
const totalOrders = new Set(rows.map(r => r && r.orderId).filter(v => v != null)).size;
|
||||
const shippedOrders = new Set(rows.filter(r => r && r.shippedFlag).map(r => r.orderId).filter(v => v != null)).size;
|
||||
return {
|
||||
totalLines,
|
||||
totalQty,
|
||||
totalOrders,
|
||||
shippedOrders
|
||||
};
|
||||
},
|
||||
productionSummary() {
|
||||
const list = Array.isArray(this.productionOrderSummaryList) ? this.productionOrderSummaryList : [];
|
||||
const orderCount = list.length;
|
||||
const planQty = list.reduce((sum, r) => sum + Number(r && r.planQty != null ? r.planQty : 0), 0);
|
||||
const finishedQty = list.reduce((sum, r) => sum + Number(r && r.finishedQty != null ? r.finishedQty : 0), 0);
|
||||
const rateText = planQty > 0 ? `${((finishedQty / planQty) * 100).toFixed(1)}%` : "0%";
|
||||
return { orderCount, planQty, finishedQty, rateText };
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getSalesmanList();
|
||||
},
|
||||
methods: {
|
||||
// 查询销售员列表
|
||||
getSalesmanList() {
|
||||
this.leftLoading = true;
|
||||
listSalesman(this.queryParams)
|
||||
.then(res => {
|
||||
this.salesmanList = (res && res.rows) ? res.rows : [];
|
||||
// 列表刷新后:若未选中,则默认选中第一条,方便右侧直接看到内容
|
||||
if (!this.selectedSalesmanId && this.salesmanList.length) {
|
||||
this.handleSelect(this.salesmanList[0]);
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.leftLoading = false;
|
||||
});
|
||||
},
|
||||
|
||||
// 搜索
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getSalesmanList();
|
||||
},
|
||||
|
||||
// 重置
|
||||
resetQuery() {
|
||||
this.queryParams.name = undefined;
|
||||
this.queryParams.status = undefined;
|
||||
this.handleQuery();
|
||||
},
|
||||
|
||||
// 选中销售员:刷新右侧数据
|
||||
handleSelect(item) {
|
||||
if (!item || !item.salesmanId) return;
|
||||
this.selectedSalesmanId = item.salesmanId;
|
||||
this.selectedSalesmanName = item.name;
|
||||
this.customerQuery.pageNum = 1;
|
||||
this.orderQuery.pageNum = 1;
|
||||
this.loadCustomers();
|
||||
if (this.activeTab === "followContract") {
|
||||
this.loadOrders();
|
||||
}
|
||||
if (this.activeTab === "shippingDocs") {
|
||||
this.loadShippingOrders();
|
||||
}
|
||||
if (this.activeTab === "production") {
|
||||
this.loadProductionSummary();
|
||||
}
|
||||
if (this.activeTab === "planShipping") {
|
||||
this.loadPlanShipping();
|
||||
}
|
||||
},
|
||||
|
||||
// Tab切换:按需加载(避免每次都拉所有数据)
|
||||
handleTabChange() {
|
||||
if (!this.selectedSalesmanId) return;
|
||||
if (this.activeTab === "followCustomer") {
|
||||
this.loadCustomers();
|
||||
}
|
||||
if (this.activeTab === "followContract") {
|
||||
this.loadOrders();
|
||||
}
|
||||
if (this.activeTab === "shippingDocs") {
|
||||
this.loadShippingOrders();
|
||||
}
|
||||
if (this.activeTab === "production") {
|
||||
this.loadProductionSummary();
|
||||
}
|
||||
if (this.activeTab === "planShipping") {
|
||||
this.loadPlanShipping();
|
||||
}
|
||||
},
|
||||
|
||||
// 跟进客户:后端反查客户
|
||||
loadCustomers() {
|
||||
if (!this.selectedSalesmanId) return;
|
||||
this.customerLoading = true;
|
||||
listSalesmanCustomers(this.selectedSalesmanId, this.customerQuery)
|
||||
.then(res => {
|
||||
this.customerList = (res && res.rows) ? res.rows : [];
|
||||
this.customerTotal = res && typeof res.total === "number" ? res.total : 0;
|
||||
})
|
||||
.finally(() => {
|
||||
this.customerLoading = false;
|
||||
});
|
||||
},
|
||||
|
||||
// 跟进合同:按 salesManager(姓名)过滤订单
|
||||
loadOrders() {
|
||||
if (!this.selectedSalesmanId) return;
|
||||
this.orderLoading = true;
|
||||
listOrder({
|
||||
...this.orderQuery,
|
||||
salesmanId: this.selectedSalesmanId
|
||||
})
|
||||
.then(res => {
|
||||
this.orderList = (res && res.rows) ? res.rows : [];
|
||||
this.orderTotal = res && typeof res.total === "number" ? res.total : 0;
|
||||
})
|
||||
.finally(() => {
|
||||
this.orderLoading = false;
|
||||
});
|
||||
},
|
||||
|
||||
loadShippingOrders() {
|
||||
if (!this.selectedSalesmanId) return;
|
||||
this.shippingLoading = true;
|
||||
listShippingOrder({ salesmanId: this.selectedSalesmanId, pageNum: 1, pageSize: 9999 })
|
||||
.then(res => {
|
||||
this.shippingList = (res && res.rows) ? res.rows : [];
|
||||
})
|
||||
.finally(() => {
|
||||
this.shippingLoading = false;
|
||||
});
|
||||
},
|
||||
|
||||
async loadProductionSummary() {
|
||||
if (!this.selectedSalesmanId) return;
|
||||
this.productionLoading = true;
|
||||
try {
|
||||
const orderRes = await listOrder({ salesmanId: this.selectedSalesmanId, pageNum: 1, pageSize: 9999 });
|
||||
const orders = (orderRes && orderRes.rows) ? orderRes.rows : [];
|
||||
const promises = orders
|
||||
.filter(o => o && o.orderId != null)
|
||||
.map(async (o) => {
|
||||
const prodRes = await listOrderProduction({ orderId: o.orderId, pageNum: 1, pageSize: 9999 });
|
||||
const rows = (prodRes && prodRes.rows) ? prodRes.rows : [];
|
||||
const planQty = rows.reduce((sum, r) => sum + Number(r && r.planQty != null ? r.planQty : 0), 0);
|
||||
const finishedQty = rows.reduce((sum, r) => sum + Number(r && r.finishedQty != null ? r.finishedQty : 0), 0);
|
||||
let lastUpdateTime = "";
|
||||
rows.forEach(r => {
|
||||
const t = r && r.updateTime ? String(r.updateTime) : "";
|
||||
if (t && (!lastUpdateTime || new Date(t).getTime() > new Date(lastUpdateTime).getTime())) {
|
||||
lastUpdateTime = t;
|
||||
}
|
||||
});
|
||||
return {
|
||||
orderId: o.orderId,
|
||||
orderCode: o.orderCode,
|
||||
customerName: o.customerName,
|
||||
planQty,
|
||||
finishedQty,
|
||||
lastUpdateTime
|
||||
};
|
||||
});
|
||||
|
||||
this.productionOrderSummaryList = await Promise.all(promises);
|
||||
} finally {
|
||||
this.productionLoading = false;
|
||||
}
|
||||
},
|
||||
|
||||
loadPlanShipping() {
|
||||
if (!this.selectedSalesmanId) return;
|
||||
this.planDetailLoading = true;
|
||||
Promise.all([
|
||||
listOrder({ salesmanId: this.selectedSalesmanId, pageNum: 1, pageSize: 9999 }),
|
||||
listShippingOrder({ salesmanId: this.selectedSalesmanId, pageNum: 1, pageSize: 9999 })
|
||||
])
|
||||
.then(async ([orderRes, shipRes]) => {
|
||||
const orders = (orderRes && orderRes.rows) ? orderRes.rows : [];
|
||||
const shipping = (shipRes && shipRes.rows) ? shipRes.rows : [];
|
||||
|
||||
const orderMap = new Map();
|
||||
orders.forEach(o => {
|
||||
if (o && o.orderId != null) orderMap.set(o.orderId, o);
|
||||
});
|
||||
|
||||
const shipStatusMap = new Map();
|
||||
shipping.forEach(s => {
|
||||
const orderId = s && s.orderId != null ? s.orderId : null;
|
||||
if (orderId == null) return;
|
||||
const n = Number(s && s.status != null ? s.status : 0);
|
||||
const prev = shipStatusMap.has(orderId) ? shipStatusMap.get(orderId) : 0;
|
||||
shipStatusMap.set(orderId, Number.isFinite(n) ? Math.max(prev, n) : prev);
|
||||
});
|
||||
|
||||
const detailPromises = orders
|
||||
.filter(o => o && o.orderId)
|
||||
.map(o =>
|
||||
listOrderDetail({ orderId: o.orderId, pageNum: 1, pageSize: 9999 })
|
||||
.then(res => ({
|
||||
order: o,
|
||||
rows: (res && res.rows) ? res.rows : []
|
||||
}))
|
||||
);
|
||||
|
||||
const detailResults = await Promise.all(detailPromises);
|
||||
const merged = [];
|
||||
detailResults.forEach(({ order, rows }) => {
|
||||
const maxStatus = shipStatusMap.has(order.orderId) ? shipStatusMap.get(order.orderId) : 0;
|
||||
const shippedFlag = Number(maxStatus) >= 2;
|
||||
rows
|
||||
.filter(r => String(r && r.productType ? r.productType : "").toLowerCase() === "product")
|
||||
.forEach(r => {
|
||||
merged.push({
|
||||
...r,
|
||||
orderId: order.orderId,
|
||||
orderCode: order.orderCode,
|
||||
customerName: order.customerName,
|
||||
shippedFlag
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
this.planOrderDetailList = merged;
|
||||
})
|
||||
.finally(() => {
|
||||
this.planDetailLoading = false;
|
||||
});
|
||||
},
|
||||
|
||||
formatQty(v) {
|
||||
const n = Number(v);
|
||||
const val = Number.isFinite(n) ? n : 0;
|
||||
return val.toFixed(2);
|
||||
},
|
||||
|
||||
formatRate(finished, plan) {
|
||||
const a = Number(finished);
|
||||
const b = Number(plan);
|
||||
const fa = Number.isFinite(a) ? a : 0;
|
||||
const fb = Number.isFinite(b) ? b : 0;
|
||||
if (fb <= 0) return "0%";
|
||||
return `${((fa / fb) * 100).toFixed(1)}%`;
|
||||
},
|
||||
|
||||
// 新增
|
||||
handleAdd() {
|
||||
this.dialogTitle = "新增销售员";
|
||||
this.form = {
|
||||
salesmanId: undefined,
|
||||
name: "",
|
||||
mobile: "",
|
||||
status: 0,
|
||||
remark: ""
|
||||
};
|
||||
this.dialogOpen = true;
|
||||
},
|
||||
|
||||
// 修改
|
||||
handleUpdate(row) {
|
||||
this.dialogTitle = "修改销售员";
|
||||
this.form = { ...row };
|
||||
if (this.form.status === undefined || this.form.status === null) {
|
||||
this.form.status = 0;
|
||||
}
|
||||
this.dialogOpen = true;
|
||||
},
|
||||
|
||||
// 删除
|
||||
handleDelete(row) {
|
||||
this.$modal
|
||||
.confirm(`是否确认删除销售员"${row.name}"?`)
|
||||
.then(() => delSalesman([row.salesmanId]))
|
||||
.then(() => {
|
||||
this.$modal.msgSuccess("删除成功");
|
||||
// 删除后清空选中,避免右侧仍显示已删除人员的数据
|
||||
if (this.selectedSalesmanId === row.salesmanId) {
|
||||
this.selectedSalesmanId = undefined;
|
||||
this.selectedSalesmanName = "";
|
||||
}
|
||||
this.getSalesmanList();
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
|
||||
// 提交新增/修改
|
||||
submitForm() {
|
||||
this.$refs["formRef"].validate(valid => {
|
||||
if (!valid) return;
|
||||
this.submitLoading = true;
|
||||
const req = this.form.salesmanId ? updateSalesman(this.form) : addSalesman(this.form);
|
||||
req
|
||||
.then(() => {
|
||||
this.$modal.msgSuccess("保存成功");
|
||||
this.dialogOpen = false;
|
||||
this.getSalesmanList();
|
||||
})
|
||||
.finally(() => {
|
||||
this.submitLoading = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.salesman-left {
|
||||
height: calc(100vh - 110px);
|
||||
}
|
||||
|
||||
.left-filter {
|
||||
padding: 10px 10px 0 10px;
|
||||
border: 1px solid var(--el-border-color-light);
|
||||
border-radius: 6px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.left-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.left-list {
|
||||
border: 1px solid var(--el-border-color-light);
|
||||
border-radius: 6px;
|
||||
background: #fff;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.salesman-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border: 1px solid var(--el-border-color-light);
|
||||
border-radius: 6px;
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.salesman-item.active {
|
||||
border-color: #409eff;
|
||||
background: var(--el-color-primary-light-9);
|
||||
}
|
||||
|
||||
.salesman-item__left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.salesman-item__name {
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.salesman-item__actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.right-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid var(--el-border-color-light);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.right-title__name {
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.right-body {
|
||||
padding-top: 14px;
|
||||
}
|
||||
|
||||
:deep(.right-tabs .el-tabs__content) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:deep(.right-tabs .el-tabs__header) {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.plan-shipping__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.plan-shipping__sub {
|
||||
font-weight: 400;
|
||||
color: #909399;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.plan-shipping__summary {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
padding: 10px 12px;
|
||||
margin-bottom: 10px;
|
||||
background: #f5f7fa;
|
||||
border: 1px solid var(--el-border-color-light);
|
||||
border-radius: 6px;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.plan-shipping__summary-item {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.production-wrap {
|
||||
padding-top: 6px;
|
||||
}
|
||||
|
||||
.production-summary {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
padding: 10px 12px;
|
||||
margin-bottom: 10px;
|
||||
background: #f5f7fa;
|
||||
border: 1px solid var(--el-border-color-light);
|
||||
border-radius: 6px;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.production-summary__item {
|
||||
font-size: 13px;
|
||||
}
|
||||
</style>
|
||||
1479
gear-ui3/src/views/shipping/order/index.vue
Normal file
1479
gear-ui3/src/views/shipping/order/index.vue
Normal file
File diff suppressed because it is too large
Load Diff
30
script/sql/mysql/item/gear_order_production.sql
Normal file
30
script/sql/mysql/item/gear_order_production.sql
Normal file
@@ -0,0 +1,30 @@
|
||||
-- ================================
|
||||
-- 订单生产记录(按订单明细行维度)
|
||||
-- 目标:用于“订单管理 -> 生产成果”记录每个订单明细行的计划/完成/不良等生产数据
|
||||
-- 说明:
|
||||
-- 1) 与 gear_order_detail 通过 order_detail_id 关联;与订单通过 order_id 关联
|
||||
-- 2) init 接口会按订单明细自动生成缺失记录,并同步计划数量 plan_qty
|
||||
-- 3) 逻辑删除使用 del_flag(0存在 2删除)
|
||||
-- ================================
|
||||
|
||||
DROP TABLE IF EXISTS gear_order_production;
|
||||
CREATE TABLE gear_order_production (
|
||||
production_id bigint(20) NOT NULL COMMENT '生产记录ID',
|
||||
order_id bigint(20) NOT NULL COMMENT '订单ID gear_order.order_id',
|
||||
order_detail_id bigint(20) NOT NULL COMMENT '订单明细ID gear_order_detail.detail_id',
|
||||
product_id bigint(20) DEFAULT NULL COMMENT '产品ID(快照,便于展示)',
|
||||
plan_qty decimal(16,4) NOT NULL DEFAULT 0 COMMENT '计划生产数量(通常=订单明细数量)',
|
||||
finished_qty decimal(16,4) NOT NULL DEFAULT 0 COMMENT '累计完成数量',
|
||||
bad_qty decimal(16,4) NOT NULL DEFAULT 0 COMMENT '不良数量',
|
||||
unit varchar(32) DEFAULT '' COMMENT '单位',
|
||||
remark varchar(500) DEFAULT NULL COMMENT '备注',
|
||||
del_flag char(1) NOT NULL DEFAULT '0' COMMENT '删除标志(0存在 2删除)',
|
||||
create_by varchar(64) DEFAULT '' COMMENT '创建者',
|
||||
create_time datetime COMMENT '创建时间',
|
||||
update_by varchar(64) DEFAULT '' COMMENT '更新者',
|
||||
update_time datetime COMMENT '更新时间',
|
||||
PRIMARY KEY (production_id),
|
||||
UNIQUE KEY uk_order_detail_id_del (order_detail_id, del_flag),
|
||||
KEY idx_order_id (order_id),
|
||||
KEY idx_product_id (product_id)
|
||||
) ENGINE=InnoDB COMMENT='订单生产记录表';
|
||||
68
script/sql/mysql/item/gear_shipping_order.sql
Normal file
68
script/sql/mysql/item/gear_shipping_order.sql
Normal file
@@ -0,0 +1,68 @@
|
||||
-- ================================
|
||||
-- 发货单据(独立于出入库/WMS 单据)
|
||||
-- 目标:用于“订单管理 -> 发货/发货单据”记录发货信息(发货单名称/收货单位/负责人/物流等)
|
||||
-- 说明:
|
||||
-- 1) 一张表存储发货单据主信息;与订单通过 order_id 关联
|
||||
-- 2) 发货单号 shipping_no 后端自动生成(也可手填/改造)
|
||||
-- 3) 逻辑删除使用 del_flag(0存在 2删除)
|
||||
-- ================================
|
||||
|
||||
DROP TABLE IF EXISTS gear_shipping_order;
|
||||
CREATE TABLE gear_shipping_order (
|
||||
shipping_id bigint(20) NOT NULL COMMENT '发货单据ID',
|
||||
shipping_no varchar(64) NOT NULL COMMENT '发货单号(唯一)',
|
||||
shipping_name varchar(128) DEFAULT '' COMMENT '发货单名称(展示用)',
|
||||
plan_id bigint(20) DEFAULT NULL COMMENT '发货计划ID gear_shipping_plan.plan_id',
|
||||
order_id bigint(20) NOT NULL COMMENT '关联订单ID gear_order.order_id',
|
||||
order_code varchar(64) DEFAULT '' COMMENT '订单编号快照',
|
||||
receiver_company varchar(128) DEFAULT '' COMMENT '收货单位',
|
||||
receiver_customer_id bigint(20) DEFAULT NULL COMMENT '收货客户ID gear_customer.customer_id',
|
||||
ship_time datetime COMMENT '发货时间',
|
||||
responsible_name varchar(64) DEFAULT '' COMMENT '负责人',
|
||||
logistics_company varchar(64) DEFAULT '' COMMENT '物流公司',
|
||||
logistics_no varchar(64) DEFAULT '' COMMENT '运单号',
|
||||
receiver_name varchar(64) DEFAULT '' COMMENT '收货人',
|
||||
receiver_phone varchar(32) DEFAULT '' COMMENT '收货电话',
|
||||
receiver_address varchar(255) DEFAULT '' COMMENT '收货地址',
|
||||
status char(1) NOT NULL DEFAULT '0' COMMENT '完成状态(0未发货 1已打印 2已发货 3已完成)',
|
||||
remark varchar(500) DEFAULT NULL COMMENT '备注',
|
||||
del_flag char(1) NOT NULL DEFAULT '0' COMMENT '删除标志(0存在 2删除)',
|
||||
create_by varchar(64) DEFAULT '' COMMENT '创建者',
|
||||
create_time datetime COMMENT '创建时间',
|
||||
update_by varchar(64) DEFAULT '' COMMENT '更新者',
|
||||
update_time datetime COMMENT '更新时间',
|
||||
PRIMARY KEY (shipping_id),
|
||||
UNIQUE KEY uk_shipping_no (shipping_no),
|
||||
KEY idx_plan_id (plan_id),
|
||||
KEY idx_order_id (order_id),
|
||||
KEY idx_shipping_name (shipping_name),
|
||||
KEY idx_receiver_company (receiver_company),
|
||||
KEY idx_receiver_customer_id (receiver_customer_id),
|
||||
KEY idx_logistics_no (logistics_no),
|
||||
KEY idx_ship_time (ship_time)
|
||||
) ENGINE=InnoDB COMMENT='发货单据表';
|
||||
|
||||
-- ================================
|
||||
-- 发货计划(独立表)
|
||||
-- 目标:用于“发货单据”左侧计划列表,计划下可关联多张发货单据(gear_shipping_order.plan_id)
|
||||
-- 说明:
|
||||
-- 1) 一张表存储计划主信息(计划名称/计划日期/状态)
|
||||
-- 2) 逻辑删除使用 del_flag(0存在 2删除)
|
||||
-- ================================
|
||||
|
||||
DROP TABLE IF EXISTS gear_shipping_plan;
|
||||
CREATE TABLE gear_shipping_plan (
|
||||
plan_id bigint(20) NOT NULL COMMENT '发货计划ID',
|
||||
plan_name varchar(128) NOT NULL COMMENT '计划名称',
|
||||
plan_date date NOT NULL COMMENT '计划日期',
|
||||
status char(1) NOT NULL DEFAULT '0' COMMENT '状态(0未完成 1已完成)',
|
||||
remark varchar(500) DEFAULT NULL COMMENT '备注',
|
||||
del_flag char(1) NOT NULL DEFAULT '0' COMMENT '删除标志(0存在 2删除)',
|
||||
create_by varchar(64) DEFAULT '' COMMENT '创建者',
|
||||
create_time datetime COMMENT '创建时间',
|
||||
update_by varchar(64) DEFAULT '' COMMENT '更新者',
|
||||
update_time datetime COMMENT '更新时间',
|
||||
PRIMARY KEY (plan_id),
|
||||
KEY idx_plan_date (plan_date),
|
||||
KEY idx_plan_name (plan_name)
|
||||
) ENGINE=InnoDB COMMENT='发货计划表';
|
||||
Reference in New Issue
Block a user