feat(expense): 添加花销统计记录管理功能

- 新增 ExpenseRecordController 控制器,提供花销记录的增删改查接口
- 实现 ExpenseRecordServiceI 接口及其实现类 ExpenseRecordServiceImpl
- 添加 ExpenseRecordCreateCmd、ExpenseRecordUpdateCmd 等命令对象
- 添加 ExpenseRecordListQry、ExpenseRecordShowQry 查询对象
- 定义 ExpenseRecordVO 用于返回花销统计记录数据
- 在业务模块中新增对应的 assembler、executor、gateway 和 convert 类
- 扩展 CostTypeEnum 枚举,增加 EXPENSE_TYPE 类型
- 更新 Dealer 相关实体和 DO,支持 includeFreightFlag 字段
- 添加 ExpenseCostDO 和 ExpenseProvisionDO 数据库实体映射
- 修改 PurchaseOrderFinalApproveCmdExe,集成花销计提逻辑
- 为 ExpenseRecordDO 添加 totalVehicleCount 等字段并忽略部分映射
- 在 CostListQry 中添加 name 查询字段
- 新增 ExpenseRecord 表及相关字段定义
This commit is contained in:
shenyifei 2025-12-19 17:29:47 +08:00
parent b7c04008ce
commit 1ba1d3ddd1
43 changed files with 1540 additions and 5 deletions

View File

@ -0,0 +1,62 @@
package com.xunhong.erp.turbo.admin.controller;
import cn.dev33.satoken.annotation.SaCheckLogin;
import com.alibaba.cola.dto.MultiResponse;
import com.alibaba.cola.dto.SingleResponse;
import com.xunhong.erp.turbo.api.biz.api.ExpenseRecordServiceI;
import com.xunhong.erp.turbo.api.biz.dto.cmd.ExpenseRecordCreateCmd;
import com.xunhong.erp.turbo.api.biz.dto.cmd.ExpenseRecordUpdateCmd;
import com.xunhong.erp.turbo.api.biz.dto.qry.ExpenseRecordListQry;
import com.xunhong.erp.turbo.api.biz.dto.qry.ExpenseRecordShowQry;
import com.xunhong.erp.turbo.api.biz.dto.vo.ExpenseRecordVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
/**
* @author shenyifei
*/
@Tag(name = "ExpenseRecord", description = "花销统计记录管理")
@RestController("operationExpenseRecordController")
@RequestMapping(value = "/operation")
@RequiredArgsConstructor
public class ExpenseRecordController {
@DubboReference(version = "1.0.0")
private final ExpenseRecordServiceI expenseRecordService;
@SaCheckLogin
// @SaCheckPermission(value = {PermissionConstant.MDB_BUSINESS_EXPENSE_RECORD_VIEW})
@GetMapping("listExpenseRecord")
@Operation(summary = "花销统计记录列表", method = "GET")
public MultiResponse<ExpenseRecordVO> listExpenseRecord(@ModelAttribute @Validated ExpenseRecordListQry expenseRecordListQry) {
return MultiResponse.of(expenseRecordService.list(expenseRecordListQry));
}
@SaCheckLogin
// @SaCheckPermission(value = {PermissionConstant.MDB_BUSINESS_EXPENSE_RECORD_CREATE})
@PostMapping("createExpenseRecord")
@Operation(summary = "创建花销统计记录", method = "POST")
public SingleResponse<ExpenseRecordVO> createExpenseRecord(@RequestBody @Validated ExpenseRecordCreateCmd expenseRecordCreateCmd) {
return SingleResponse.of(expenseRecordService.create(expenseRecordCreateCmd));
}
@SaCheckLogin
// @SaCheckPermission(value = {PermissionConstant.MDB_BUSINESS_EXPENSE_RECORD_VIEW})
@GetMapping("showExpenseRecord")
@Operation(summary = "花销统计记录详情", method = "GET")
public SingleResponse<ExpenseRecordVO> showExpenseRecord(@ModelAttribute @Validated ExpenseRecordShowQry expenseRecordShowQry) {
return SingleResponse.of(expenseRecordService.show(expenseRecordShowQry));
}
@SaCheckLogin
// @SaCheckPermission(value = {PermissionConstant.MDB_BUSINESS_EXPENSE_RECORD_UPDATE})
@RequestMapping(value = "updateExpenseRecord", method = {RequestMethod.PATCH, RequestMethod.PUT})
@Operation(summary = "花销统计记录更新", method = "PATCH")
public SingleResponse<ExpenseRecordVO> updateExpenseRecord(@RequestBody @Validated ExpenseRecordUpdateCmd expenseRecordUpdateCmd) {
return SingleResponse.of(expenseRecordService.update(expenseRecordUpdateCmd));
}
}

View File

@ -0,0 +1,15 @@
package com.xunhong.erp.turbo.biz.app.assembler;
import com.xunhong.erp.turbo.api.biz.dto.vo.ExpenseRecordVO;
import com.xunhong.erp.turbo.biz.domain.entity.ExpenseRecord;
import org.mapstruct.Mapper;
import org.mapstruct.NullValueCheckStrategy;
/**
* @author shenyifei
*/
@Mapper(componentModel = "spring", nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS)
public interface ExpenseRecordAssembler {
ExpenseRecordVO toExpenseRecordVO(ExpenseRecord expenseRecord);
}

View File

@ -0,0 +1,29 @@
package com.xunhong.erp.turbo.biz.app.executor.cmd;
import com.xunhong.erp.turbo.api.biz.dto.cmd.ExpenseRecordCreateCmd;
import com.xunhong.erp.turbo.api.biz.dto.vo.ExpenseRecordVO;
import com.xunhong.erp.turbo.biz.app.assembler.ExpenseRecordAssembler;
import com.xunhong.erp.turbo.biz.domain.entity.ExpenseRecord;
import com.xunhong.erp.turbo.biz.domain.gateway.ExpenseRecordGateway;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* @author shenyifei
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class ExpenseRecordCreateCmdExe {
private final ExpenseRecordAssembler expenseRecordAssembler;
private final ExpenseRecordGateway expenseRecordGateway;
public ExpenseRecordVO execute(ExpenseRecordCreateCmd expenseRecordCreateCmd) {
ExpenseRecord expenseRecord = expenseRecordGateway.save(expenseRecordCreateCmd);
return expenseRecordAssembler.toExpenseRecordVO(expenseRecord);
}
}

View File

@ -0,0 +1,27 @@
package com.xunhong.erp.turbo.biz.app.executor.cmd;
import com.xunhong.erp.turbo.api.biz.dto.cmd.ExpenseRecordUpdateCmd;
import com.xunhong.erp.turbo.api.biz.dto.vo.ExpenseRecordVO;
import com.xunhong.erp.turbo.biz.app.assembler.ExpenseRecordAssembler;
import com.xunhong.erp.turbo.biz.domain.entity.ExpenseRecord;
import com.xunhong.erp.turbo.biz.domain.gateway.ExpenseRecordGateway;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* @author shenyifei
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class ExpenseRecordUpdateCmdExe {
private final ExpenseRecordAssembler expenseRecordAssembler;
private final ExpenseRecordGateway expenseRecordGateway;
public ExpenseRecordVO execute(ExpenseRecordUpdateCmd expenseRecordUpdateCmd) {
ExpenseRecord expenseRecord = expenseRecordGateway.update(expenseRecordUpdateCmd);
return expenseRecordAssembler.toExpenseRecordVO(expenseRecord);
}
}

View File

@ -1,10 +1,12 @@
package com.xunhong.erp.turbo.biz.app.executor.cmd;
import com.xunhong.erp.turbo.api.biz.dto.cmd.PurchaseOrderFinalApproveCmd;
import com.xunhong.erp.turbo.biz.domain.gateway.ExpenseRecordGateway;
import com.xunhong.erp.turbo.biz.domain.gateway.PurchaseOrderGateway;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
/**
* @author shenyifei
@ -16,7 +18,13 @@ public class PurchaseOrderFinalApproveCmdExe {
private final PurchaseOrderGateway purchaseOrderGateway;
private final ExpenseRecordGateway expenseRecordGateway;
@Transactional
public void execute(PurchaseOrderFinalApproveCmd purchaseOrderFinalApproveCmd) {
purchaseOrderGateway.finalApprove(purchaseOrderFinalApproveCmd);
// 计提费计入发货日期的花销
expenseRecordGateway.addProvision(purchaseOrderFinalApproveCmd.getOrderId());
}
}

View File

@ -0,0 +1,30 @@
package com.xunhong.erp.turbo.biz.app.executor.query;
import com.xunhong.erp.turbo.api.biz.dto.qry.ExpenseRecordListQry;
import com.xunhong.erp.turbo.api.biz.dto.vo.ExpenseRecordVO;
import com.xunhong.erp.turbo.biz.app.assembler.ExpenseRecordAssembler;
import com.xunhong.erp.turbo.biz.domain.entity.ExpenseRecord;
import com.xunhong.erp.turbo.biz.domain.gateway.ExpenseRecordGateway;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* @author shenyifei
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class ExpenseRecordListQryExe {
private final ExpenseRecordGateway expenseRecordGateway;
private final ExpenseRecordAssembler expenseRecordAssembler;
public List<ExpenseRecordVO> execute(ExpenseRecordListQry expenseRecordListQry) {
List<ExpenseRecord> expenseRecordList = expenseRecordGateway.list(expenseRecordListQry);
return expenseRecordList.stream().map(expenseRecordAssembler::toExpenseRecordVO).toList();
}
}

View File

@ -0,0 +1,29 @@
package com.xunhong.erp.turbo.biz.app.executor.query;
import com.xunhong.erp.turbo.api.biz.dto.qry.ExpenseRecordShowQry;
import com.xunhong.erp.turbo.api.biz.dto.vo.ExpenseRecordVO;
import com.xunhong.erp.turbo.biz.app.assembler.ExpenseRecordAssembler;
import com.xunhong.erp.turbo.biz.domain.entity.ExpenseRecord;
import com.xunhong.erp.turbo.biz.domain.gateway.ExpenseRecordGateway;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* @author shenyifei
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class ExpenseRecordShowQryExe {
private final ExpenseRecordAssembler expenseRecordAssembler;
private final ExpenseRecordGateway expenseRecordGateway;
public ExpenseRecordVO execute(ExpenseRecordShowQry expenseRecordShowQry) {
ExpenseRecord expenseRecord = expenseRecordGateway.show(expenseRecordShowQry);
return expenseRecordAssembler.toExpenseRecordVO(expenseRecord);
}
}

View File

@ -0,0 +1,54 @@
package com.xunhong.erp.turbo.biz.app.service;
import com.xunhong.erp.turbo.api.biz.api.ExpenseRecordServiceI;
import com.xunhong.erp.turbo.api.biz.dto.cmd.ExpenseRecordCreateCmd;
import com.xunhong.erp.turbo.api.biz.dto.cmd.ExpenseRecordUpdateCmd;
import com.xunhong.erp.turbo.api.biz.dto.qry.ExpenseRecordListQry;
import com.xunhong.erp.turbo.api.biz.dto.qry.ExpenseRecordShowQry;
import com.xunhong.erp.turbo.api.biz.dto.vo.ExpenseRecordVO;
import com.xunhong.erp.turbo.biz.app.executor.cmd.ExpenseRecordCreateCmdExe;
import com.xunhong.erp.turbo.biz.app.executor.cmd.ExpenseRecordUpdateCmdExe;
import com.xunhong.erp.turbo.biz.app.executor.query.ExpenseRecordListQryExe;
import com.xunhong.erp.turbo.biz.app.executor.query.ExpenseRecordShowQryExe;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @author shenyifei
*/
@Slf4j
@Service
@DubboService(interfaceClass = ExpenseRecordServiceI.class, version = "1.0.0")
@RequiredArgsConstructor
public class ExpenseRecordServiceImpl implements ExpenseRecordServiceI {
private final ExpenseRecordCreateCmdExe expenseRecordCreateCmdExe;
private final ExpenseRecordUpdateCmdExe expenseRecordUpdateCmdExe;
private final ExpenseRecordListQryExe expenseRecordListQryExe;
private final ExpenseRecordShowQryExe expenseRecordShowQryExe;
@Override
public ExpenseRecordVO create(ExpenseRecordCreateCmd expenseRecordCreateCmd) {
return expenseRecordCreateCmdExe.execute(expenseRecordCreateCmd);
}
@Override
public List<ExpenseRecordVO> list(ExpenseRecordListQry expenseRecordListQry) {
return expenseRecordListQryExe.execute(expenseRecordListQry);
}
@Override
public ExpenseRecordVO update(ExpenseRecordUpdateCmd expenseRecordUpdateCmd) {
return expenseRecordUpdateCmdExe.execute(expenseRecordUpdateCmd);
}
@Override
public ExpenseRecordVO show(ExpenseRecordShowQry expenseRecordShowQry) {
return expenseRecordShowQryExe.execute(expenseRecordShowQry);
}
}

View File

@ -68,6 +68,11 @@ public class Dealer extends DTO {
*/
private Boolean includePackingFlag;
/**
* 发货单合计金额是否含包运费
*/
private Boolean includeFreightFlag;
/**
* 应收金额
*/

View File

@ -0,0 +1,69 @@
package com.xunhong.erp.turbo.biz.domain.entity;
import com.alibaba.cola.domain.Entity;
import com.alibaba.cola.dto.DTO;
import com.xunhong.erp.turbo.api.biz.dto.common.ExpenseCost;
import com.xunhong.erp.turbo.api.biz.dto.common.ExpenseProvision;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
/**
* @author shenyifei
*/
@Data
@Entity
@EqualsAndHashCode(callSuper = true)
public class ExpenseRecord extends DTO {
/**
* 记录ID
*/
private Long expenseRecordId;
/**
* 记录日期YYYY-MM-DD
*/
private LocalDate recordDate;
/**
* 计提车次数量
*/
private Integer totalVehicleCount;
/**
* 计提总金额
*/
private BigDecimal totalProvision;
/**
* 花销总额
*/
private BigDecimal totalExpense;
/**
* 日常利润
*/
private BigDecimal dailyProfit;
/**
* 花销计提明细
*/
private List<ExpenseProvision> expenseProvisionList;
/**
* 花销费用明细
*/
private List<ExpenseCost> expenseCostList;
/**
* 创建时间
*/
private LocalDateTime createdAt;
}

View File

@ -0,0 +1,25 @@
package com.xunhong.erp.turbo.biz.domain.gateway;
import com.xunhong.erp.turbo.api.biz.dto.cmd.ExpenseRecordCreateCmd;
import com.xunhong.erp.turbo.api.biz.dto.cmd.ExpenseRecordUpdateCmd;
import com.xunhong.erp.turbo.api.biz.dto.qry.ExpenseRecordListQry;
import com.xunhong.erp.turbo.api.biz.dto.qry.ExpenseRecordShowQry;
import com.xunhong.erp.turbo.biz.domain.entity.ExpenseRecord;
import java.util.List;
/**
* @author shenyifei
*/
public interface ExpenseRecordGateway {
ExpenseRecord save(ExpenseRecordCreateCmd expenseRecordCreateCmd);
List<ExpenseRecord> list(ExpenseRecordListQry expenseRecordListQry);
ExpenseRecord update(ExpenseRecordUpdateCmd expenseRecordUpdateCmd);
ExpenseRecord show(ExpenseRecordShowQry expenseRecordShowQry);
void addProvision(Long orderId);
}

View File

@ -0,0 +1,47 @@
package com.xunhong.erp.turbo.biz.infrastructure.convert;
import com.xunhong.erp.turbo.api.biz.dto.cmd.ExpenseRecordCreateCmd;
import com.xunhong.erp.turbo.api.biz.dto.cmd.ExpenseRecordUpdateCmd;
import com.xunhong.erp.turbo.biz.domain.entity.ExpenseRecord;
import com.xunhong.erp.turbo.biz.infrastructure.entity.ExpenseRecordDO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.NullValueCheckStrategy;
/**
* @author shenyifei
*/
@Mapper(componentModel = "spring", nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS)
public interface ExpenseRecordConvert {
@Mapping(target = "expenseProvisionList", source = "expenseProvisionDOList")
@Mapping(target = "expenseCostList", source = "expenseCostDOList")
ExpenseRecord toExpenseRecord(ExpenseRecordDO expenseRecordDO);
@Mapping(target = "totalVehicleCount", ignore = true)
@Mapping(target = "totalProvision", ignore = true)
@Mapping(target = "totalExpense", ignore = true)
@Mapping(target = "expenseRecordId", ignore = true)
@Mapping(target = "expenseProvisionDOList", ignore = true)
@Mapping(target = "expenseCostDOList", ignore = true)
@Mapping(target = "dailyProfit", ignore = true)
@Mapping(target = "version", ignore = true)
@Mapping(target = "updatedAt", ignore = true)
@Mapping(target = "isDelete", ignore = true)
@Mapping(target = "createdAt", ignore = true)
ExpenseRecordDO toExpenseRecordDO(ExpenseRecordCreateCmd expenseRecordCreateCmd);
@Mapping(target = "totalVehicleCount", ignore = true)
@Mapping(target = "totalProvision", ignore = true)
@Mapping(target = "totalExpense", ignore = true)
@Mapping(target = "expenseProvisionDOList", ignore = true)
@Mapping(target = "expenseCostDOList", ignore = true)
@Mapping(target = "dailyProfit", ignore = true)
@Mapping(target = "version", ignore = true)
@Mapping(target = "updatedAt", ignore = true)
@Mapping(target = "isDelete", ignore = true)
@Mapping(target = "createdAt", ignore = true)
void toExpenseRecordDO(@MappingTarget ExpenseRecordDO expenseRecordDO, ExpenseRecordUpdateCmd expenseRecordUpdateCmd);
}

View File

@ -74,6 +74,12 @@ public class DealerDO extends BaseDO<DealerDO> {
@TableField(value = "include_packing_flag")
private Boolean includePackingFlag;
/**
* 发货单合计金额是否含包运费
*/
@TableField(value = "include_freight_flag")
private Boolean includeFreightFlag;
/**
* 应收金额
*/

View File

@ -0,0 +1,65 @@
package com.xunhong.erp.turbo.biz.infrastructure.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.xunhong.erp.turbo.datasource.domain.entity.BaseDO;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* @author shenyifei
*/
@Data
@TableName(value = "expense_cost")
@EqualsAndHashCode(callSuper = true)
public class ExpenseCostDO extends BaseDO<ExpenseCostDO> {
/**
* 明细ID
*/
@TableId(value = "expense_cost_id", type = IdType.ASSIGN_ID)
private Long expenseCostId;
/**
* 花销统计记录ID
*/
@TableField(value = "expense_record_id")
private Long expenseRecordId;
/**
* 费用类型ID
*/
@TableField(value = "cost_id")
private Long costId;
/**
* 费用类型名称
*/
@TableField(value = "cost_name")
private String costName;
/**
* 花销金额
*/
@TableField(value = "expense_amount")
private BigDecimal expenseAmount;
/**
* 备注可填员工事由等
*/
@TableField(value = "remark")
private String remark;
/**
* 创建时间
*/
@TableField(value = "created_at")
private LocalDateTime createdAt;
}

View File

@ -0,0 +1,58 @@
package com.xunhong.erp.turbo.biz.infrastructure.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.xunhong.erp.turbo.datasource.domain.entity.BaseDO;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
/**
* @author shenyifei
*/
@Data
@TableName(value = "expense_provision")
@EqualsAndHashCode(callSuper = true)
public class ExpenseProvisionDO extends BaseDO<ExpenseProvisionDO> {
/**
* 明细ID
*/
@TableId(value = "expense_provision_id", type = IdType.ASSIGN_ID)
private Long expenseProvisionId;
/**
* 花销统计记录ID
*/
@TableField(value = "expense_record_id")
private Long expenseRecordId;
/**
* 经销商名称
*/
@TableField(value = "dealer_name")
private String dealerName;
/**
* 车次号
*/
@TableField(value = "vehicle_no")
private String vehicleNo;
/**
* 计提金额
*/
@TableField(value = "provision_amount")
private BigDecimal provisionAmount;
/**
* 采购单ID
*/
@TableField(value = "order_id")
private Long orderId;
}

View File

@ -0,0 +1,65 @@
package com.xunhong.erp.turbo.biz.infrastructure.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.xunhong.erp.turbo.datasource.domain.entity.BaseDO;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
/**
* @author shenyifei
*/
@Data
@TableName(value = "expense_record")
@EqualsAndHashCode(callSuper = true)
public class ExpenseRecordDO extends BaseDO<ExpenseRecordDO> {
/**
* 记录ID
*/
@TableId(value = "expense_record_id", type = IdType.ASSIGN_ID)
private Long expenseRecordId;
/**
* 记录日期YYYY-MM-DD
*/
@TableField(value = "record_date")
private LocalDate recordDate;
/**
* 计提车次数量
*/
@TableField(value = "total_vehicle_count")
private Integer totalVehicleCount;
/**
* 计提总金额
*/
@TableField(value = "total_provision")
private BigDecimal totalProvision;
/**
* 花销总额
*/
@TableField(value = "total_expense")
private BigDecimal totalExpense;
/**
* 日常利润
*/
@TableField(value = "daily_profit")
private BigDecimal dailyProfit;
@TableField(exist = false)
private List<ExpenseProvisionDO> expenseProvisionDOList;
@TableField(exist = false)
private List<ExpenseCostDO> expenseCostDOList;
}

View File

@ -80,6 +80,12 @@ public class OrderDealerDO extends BaseDO<OrderDealerDO> {
@TableField(value = "include_packing_flag")
private Boolean includePackingFlag;
/**
* 发货单合计金额是否含包运费
*/
@TableField(value = "include_freight_flag")
private Boolean includeFreightFlag;
/**
* 是否开启计提税金
*/
@ -146,4 +152,11 @@ public class OrderDealerDO extends BaseDO<OrderDealerDO> {
@TableField(value = "loss_amount")
private BigDecimal lossAmount;
/**
* 兼容性字段标记是否已迁移到新的JSON格式
* true: 已迁移false: 未迁移
*/
@TableField(value = "migrated")
private Boolean migrated;
}

View File

@ -86,6 +86,8 @@ public class CostGatewayImpl implements CostGateway {
queryWrapper.eq(Objects.nonNull(costListQry.getType()), CostDO::getType, costListQry.getType());
queryWrapper.eq(Objects.nonNull(costListQry.getBelong()), CostDO::getBelong, costListQry.getBelong());
queryWrapper.eq(Objects.nonNull(costListQry.getCostId()), CostDO::getCostId, costListQry.getCostId());
queryWrapper.eq(Objects.nonNull(costListQry.getStatus()), CostDO::getStatus, costListQry.getStatus());
queryWrapper.like(Objects.nonNull(costListQry.getName()), CostDO::getName, costListQry.getName());
queryWrapper.orderByAsc(CostDO::getType);
queryWrapper.orderByAsc(CostDO::getSort);

View File

@ -0,0 +1,443 @@
package com.xunhong.erp.turbo.biz.infrastructure.gateway;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.xunhong.erp.turbo.api.biz.dto.cmd.ExpenseRecordCreateCmd;
import com.xunhong.erp.turbo.api.biz.dto.cmd.ExpenseRecordUpdateCmd;
import com.xunhong.erp.turbo.api.biz.dto.common.ExpenseCost;
import com.xunhong.erp.turbo.api.biz.dto.common.ExpenseProvision;
import com.xunhong.erp.turbo.api.biz.dto.qry.ExpenseRecordListQry;
import com.xunhong.erp.turbo.api.biz.dto.qry.ExpenseRecordShowQry;
import com.xunhong.erp.turbo.biz.domain.entity.ExpenseRecord;
import com.xunhong.erp.turbo.biz.domain.gateway.ExpenseRecordGateway;
import com.xunhong.erp.turbo.biz.infrastructure.convert.ExpenseRecordConvert;
import com.xunhong.erp.turbo.biz.infrastructure.entity.*;
import com.xunhong.erp.turbo.biz.infrastructure.mapper.ExpenseCostMapper;
import com.xunhong.erp.turbo.biz.infrastructure.mapper.ExpenseProvisionMapper;
import com.xunhong.erp.turbo.biz.infrastructure.mapper.ExpenseRecordMapper;
import com.xunhong.erp.turbo.biz.infrastructure.mapper.OrderCostMapper;
import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
/**
* @author shenyifei
*/
@Repository
@RequiredArgsConstructor
public class ExpenseRecordGatewayImpl implements ExpenseRecordGateway {
private final ExpenseRecordMapper expenseRecordMapper;
private final ExpenseRecordConvert expenseRecordConvert;
private final ExpenseProvisionMapper expenseProvisionMapper;
private final ExpenseCostMapper expenseCostMapper;
private final OrderCostMapper orderCostMapper;
@NotNull
private static ExpenseRecordDO getExpenseRecordDO(ExpenseRecordCreateCmd expenseRecordCreateCmd, LocalDate recordDate) {
ExpenseRecordDO expenseRecordDO = new ExpenseRecordDO();
expenseRecordDO.setRecordDate(recordDate);
// 计算总计金额
BigDecimal totalProvision = BigDecimal.ZERO;
BigDecimal totalExpense = BigDecimal.ZERO;
Integer totalVehicleCount = 0;
// 处理计提明细
if (expenseRecordCreateCmd.getExpenseProvisionList() != null) {
totalVehicleCount = expenseRecordCreateCmd.getExpenseProvisionList().size();
for (ExpenseProvision provision : expenseRecordCreateCmd.getExpenseProvisionList()) {
if (provision.getProvisionAmount() != null) {
totalProvision = totalProvision.add(provision.getProvisionAmount());
}
}
}
// 处理费用明细
if (expenseRecordCreateCmd.getExpenseCostList() != null) {
for (ExpenseCost cost : expenseRecordCreateCmd.getExpenseCostList()) {
if (cost.getExpenseAmount() != null) {
totalExpense = totalExpense.add(cost.getExpenseAmount());
}
}
}
expenseRecordDO.setTotalProvision(totalProvision);
expenseRecordDO.setTotalExpense(totalExpense);
expenseRecordDO.setDailyProfit(totalProvision.subtract(totalExpense));
expenseRecordDO.setTotalVehicleCount(totalVehicleCount);
return expenseRecordDO;
}
@Override
@Transactional(rollbackFor = Exception.class)
public ExpenseRecord save(ExpenseRecordCreateCmd expenseRecordCreateCmd) {
// 1. 检查当日是否已存在费用记录
LocalDate recordDate = expenseRecordCreateCmd.getRecordDate();
if (recordDate == null) {
recordDate = LocalDate.now();
}
ExpenseRecordDO existingRecord = expenseRecordMapper.selectOne(
Wrappers.lambdaQuery(ExpenseRecordDO.class)
.eq(ExpenseRecordDO::getRecordDate, recordDate)
.last("limit 1")
);
// 2. 如果已存在记录调用update方法
if (existingRecord != null) {
ExpenseRecordUpdateCmd updateCmd = new ExpenseRecordUpdateCmd();
updateCmd.setExpenseRecordId(existingRecord.getExpenseRecordId());
updateCmd.setRecordDate(recordDate);
updateCmd.setExpenseCostList(expenseRecordCreateCmd.getExpenseCostList());
updateCmd.setExpenseProvisionList(expenseRecordCreateCmd.getExpenseProvisionList());
return update(updateCmd);
}
// 3. 创建新的费用记录
ExpenseRecordDO expenseRecordDO = getExpenseRecordDO(expenseRecordCreateCmd, recordDate);
// 插入主记录
expenseRecordMapper.insert(expenseRecordDO);
// 4. 插入计提明细
if (expenseRecordCreateCmd.getExpenseProvisionList() != null) {
for (ExpenseProvision provision : expenseRecordCreateCmd.getExpenseProvisionList()) {
ExpenseProvisionDO provisionDO = convertToExpenseProvisionDO(provision);
provisionDO.setExpenseRecordId(expenseRecordDO.getExpenseRecordId());
expenseProvisionMapper.insert(provisionDO);
}
}
// 5. 插入费用明细
if (expenseRecordCreateCmd.getExpenseCostList() != null) {
for (ExpenseCost cost : expenseRecordCreateCmd.getExpenseCostList()) {
ExpenseCostDO costDO = convertToExpenseCostDO(cost);
costDO.setExpenseRecordId(expenseRecordDO.getExpenseRecordId());
costDO.setCreatedAt(LocalDateTime.now());
expenseCostMapper.insert(costDO);
}
}
return expenseRecordConvert.toExpenseRecord(expenseRecordDO);
}
@Override
public List<ExpenseRecord> list(ExpenseRecordListQry expenseRecordListQry) {
LambdaQueryWrapper<ExpenseRecordDO> queryWrapper = Wrappers.lambdaQuery(ExpenseRecordDO.class);
queryWrapper.gt(Objects.nonNull(expenseRecordListQry.getStartDate()), ExpenseRecordDO::getRecordDate, expenseRecordListQry.getStartDate());
queryWrapper.lt(Objects.nonNull(expenseRecordListQry.getEndDate()), ExpenseRecordDO::getRecordDate, expenseRecordListQry.getEndDate());
List<ExpenseRecordDO> expenseRecordDOList = expenseRecordMapper.selectList(queryWrapper);
List<Long> expenseRecordIdList = expenseRecordDOList.stream().map(ExpenseRecordDO::getExpenseRecordId).toList();
if (CollUtil.isNotEmpty(expenseRecordIdList)) {
LambdaQueryWrapper<ExpenseProvisionDO> queryWrapper1 = Wrappers.lambdaQuery(ExpenseProvisionDO.class);
queryWrapper1.in(ExpenseProvisionDO::getExpenseRecordId, expenseRecordIdList);
List<ExpenseProvisionDO> expenseProvisionDOList = expenseProvisionMapper.selectList(queryWrapper1);
expenseRecordDOList.forEach(expenseRecordDO -> {
expenseRecordDO.setExpenseProvisionDOList(expenseProvisionDOList.stream()
.filter(expenseProvisionDO -> expenseProvisionDO.getExpenseRecordId().equals(expenseRecordDO.getExpenseRecordId()))
.toList());
});
LambdaQueryWrapper<ExpenseCostDO> queryWrapper2 = Wrappers.lambdaQuery(ExpenseCostDO.class);
queryWrapper2.in(ExpenseCostDO::getExpenseRecordId, expenseRecordIdList);
List<ExpenseCostDO> expenseCostDOList = expenseCostMapper.selectList(queryWrapper2);
expenseRecordDOList.forEach(expenseRecordDO -> {
expenseRecordDO.setExpenseCostDOList(expenseCostDOList.stream()
.filter(expenseCostDO -> expenseCostDO.getExpenseRecordId().equals(expenseRecordDO.getExpenseRecordId()))
.toList());
});
}
return expenseRecordDOList.stream().map(expenseRecordConvert::toExpenseRecord).toList();
}
@Override
@Transactional(rollbackFor = Exception.class)
public ExpenseRecord update(ExpenseRecordUpdateCmd expenseRecordUpdateCmd) {
// 1. 查询现有记录
ExpenseRecordDO expenseRecordDO = expenseRecordMapper.selectById(expenseRecordUpdateCmd.getExpenseRecordId());
if (expenseRecordDO == null) {
throw new RuntimeException("费用记录不存在ID: " + expenseRecordUpdateCmd.getExpenseRecordId());
}
// 2. 获取现有的明细记录
List<ExpenseProvisionDO> existingProvisions = expenseProvisionMapper.selectList(
Wrappers.lambdaQuery(ExpenseProvisionDO.class)
.eq(ExpenseProvisionDO::getExpenseRecordId, expenseRecordUpdateCmd.getExpenseRecordId())
);
List<ExpenseCostDO> existingCosts = expenseCostMapper.selectList(
Wrappers.lambdaQuery(ExpenseCostDO.class)
.eq(ExpenseCostDO::getExpenseRecordId, expenseRecordUpdateCmd.getExpenseRecordId())
);
// 3. 精细化更新计提明细
if (expenseRecordUpdateCmd.getExpenseProvisionList() != null) {
updateExpenseProvisions(expenseRecordUpdateCmd.getExpenseRecordId(),
expenseRecordUpdateCmd.getExpenseProvisionList(), existingProvisions);
}
// 4. 精细化更新费用明细
if (expenseRecordUpdateCmd.getExpenseCostList() != null) {
updateExpenseCosts(expenseRecordUpdateCmd.getExpenseRecordId(),
expenseRecordUpdateCmd.getExpenseCostList(), existingCosts);
}
// 5. 重新计算并更新主记录的总计金额
recalculateAndUpdateExpenseRecord(expenseRecordUpdateCmd.getExpenseRecordId());
// 6. 返回更新后的记录
ExpenseRecordDO updatedRecord = expenseRecordMapper.selectById(expenseRecordUpdateCmd.getExpenseRecordId());
return expenseRecordConvert.toExpenseRecord(updatedRecord);
}
@Override
public ExpenseRecord show(ExpenseRecordShowQry expenseRecordShowQry) {
LambdaQueryWrapper<ExpenseRecordDO> queryWrapper = Wrappers.lambdaQuery(ExpenseRecordDO.class);
queryWrapper.eq(Objects.nonNull(expenseRecordShowQry.getExpenseRecordId()), ExpenseRecordDO::getExpenseRecordId, expenseRecordShowQry.getExpenseRecordId());
queryWrapper.eq(Objects.nonNull(expenseRecordShowQry.getRecordDate()), ExpenseRecordDO::getRecordDate, expenseRecordShowQry.getRecordDate());
queryWrapper.last("limit 1");
ExpenseRecordDO expenseRecordDO = expenseRecordMapper.selectOne(queryWrapper);
if (Objects.nonNull(expenseRecordDO)) {
expenseRecordDO.setExpenseProvisionDOList(expenseProvisionMapper.selectList(
Wrappers.lambdaQuery(ExpenseProvisionDO.class)
.eq(ExpenseProvisionDO::getExpenseRecordId, expenseRecordDO.getExpenseRecordId())
));
expenseRecordDO.setExpenseCostDOList(expenseCostMapper.selectList(
Wrappers.lambdaQuery(ExpenseCostDO.class)
.eq(ExpenseCostDO::getExpenseRecordId, expenseRecordDO.getExpenseRecordId())
));
}
return expenseRecordConvert.toExpenseRecord(expenseRecordDO);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void addProvision(Long orderId) {
// 1. 查询订单的计提费用信息
OrderCostDO orderCostDO = orderCostMapper.selectByOrderId(orderId, "计提费");
if (orderCostDO == null) {
return;
}
// 2. 查询车辆信息获取车次号
OrderVehicleDO orderVehicle = orderCostDO.getOrderVehicleDO();
// 3. 创建或更新费用记录
LocalDate today = orderVehicle.getDeliveryTime();
ExpenseRecordDO expenseRecord = expenseRecordMapper.selectOne(
Wrappers.lambdaQuery(ExpenseRecordDO.class)
.eq(ExpenseRecordDO::getRecordDate, today)
.last("limit 1")
);
if (expenseRecord == null) {
// 创建新的费用记录
expenseRecord = new ExpenseRecordDO();
expenseRecord.setRecordDate(today);
expenseRecord.setTotalProvision(orderCostDO.getPrice());
expenseRecord.setTotalExpense(BigDecimal.ZERO);
expenseRecord.setDailyProfit(orderCostDO.getPrice());
expenseRecord.setCreatedAt(LocalDateTime.now());
expenseRecordMapper.insert(expenseRecord);
} else {
// 更新现有费用记录
BigDecimal newTotalProvision = expenseRecord.getTotalProvision().add(orderCostDO.getPrice());
BigDecimal newDailyProfit = newTotalProvision.subtract(expenseRecord.getTotalExpense());
expenseRecord.setTotalProvision(newTotalProvision);
expenseRecord.setDailyProfit(newDailyProfit);
expenseRecordMapper.updateById(expenseRecord);
}
// 4. 创建费用计提明细记录
ExpenseProvisionDO expenseProvision = new ExpenseProvisionDO();
expenseProvision.setExpenseRecordId(expenseRecord.getExpenseRecordId());
expenseProvision.setOrderId(orderId);
expenseProvision.setProvisionAmount(orderCostDO.getPrice());
// 设置经销商名称
expenseProvision.setDealerName(orderVehicle.getDealerName());
// 设置车次号
expenseProvision.setVehicleNo(orderVehicle.getVehicleNo());
// 插入费用计提明细
expenseProvisionMapper.insert(expenseProvision);
}
/**
* 精细化更新费用计提明细
*/
private void updateExpenseProvisions(Long expenseRecordId, List<ExpenseProvision> newProvisions,
List<ExpenseProvisionDO> existingProvisions) {
// 记录需要保留的现有记录ID
Set<Long> existingIdsToKeep = new HashSet<>();
// 处理新的计提明细
for (ExpenseProvision provision : newProvisions) {
// 更新现有记录
ExpenseProvisionDO existingDO = existingProvisions.stream()
.filter(p -> p.getExpenseProvisionId().equals(provision.getExpenseProvisionId()))
.findFirst()
.orElse(null);
if (existingDO != null) {
// 更新现有记录
updateExpenseProvisionDO(existingDO, provision);
expenseProvisionMapper.updateById(existingDO);
existingIdsToKeep.add(provision.getExpenseProvisionId());
} else {
// 插入新记录
ExpenseProvisionDO newDO = convertToExpenseProvisionDO(provision);
newDO.setExpenseRecordId(expenseRecordId);
expenseProvisionMapper.insert(newDO);
}
}
// 删除不再存在的记录
for (ExpenseProvisionDO existing : existingProvisions) {
if (!existingIdsToKeep.contains(existing.getExpenseProvisionId())) {
expenseProvisionMapper.deleteById(existing.getExpenseProvisionId());
}
}
}
/**
* 精细化更新费用明细
*/
private void updateExpenseCosts(Long expenseRecordId, List<ExpenseCost> newCosts,
List<ExpenseCostDO> existingCosts) {
// 记录需要保留的现有记录ID
Set<Long> existingIdsToKeep = new HashSet<>();
// 处理新的费用明细
for (ExpenseCost cost : newCosts) {
// 更新现有记录
ExpenseCostDO existingDO = existingCosts.stream()
.filter(c -> c.getExpenseCostId().equals(cost.getExpenseCostId()))
.findFirst()
.orElse(null);
if (existingDO != null) {
// 更新现有记录
updateExpenseCostDO(existingDO, cost);
expenseCostMapper.updateById(existingDO);
existingIdsToKeep.add(cost.getExpenseCostId());
} else {
// 插入新记录
ExpenseCostDO newDO = convertToExpenseCostDO(cost);
newDO.setExpenseRecordId(expenseRecordId);
newDO.setCreatedAt(LocalDateTime.now());
expenseCostMapper.insert(newDO);
}
}
// 删除不再存在的记录
for (ExpenseCostDO existing : existingCosts) {
if (!existingIdsToKeep.contains(existing.getExpenseCostId())) {
expenseCostMapper.deleteById(existing.getExpenseCostId());
}
}
}
/**
* 重新计算并更新费用记录的总计金额
*/
private void recalculateAndUpdateExpenseRecord(Long expenseRecordId) {
// 重新计算计提总金额和车次数量
List<ExpenseProvisionDO> provisions = expenseProvisionMapper.selectList(
Wrappers.lambdaQuery(ExpenseProvisionDO.class)
.eq(ExpenseProvisionDO::getExpenseRecordId, expenseRecordId)
);
BigDecimal totalProvision = provisions.stream()
.map(ExpenseProvisionDO::getProvisionAmount)
.filter(Objects::nonNull)
.reduce(BigDecimal.ZERO, BigDecimal::add);
int totalVehicleCount = provisions.size();
// 重新计算费用总额
List<ExpenseCostDO> costs = expenseCostMapper.selectList(
Wrappers.lambdaQuery(ExpenseCostDO.class)
.eq(ExpenseCostDO::getExpenseRecordId, expenseRecordId)
);
BigDecimal totalExpense = costs.stream()
.map(ExpenseCostDO::getExpenseAmount)
.filter(Objects::nonNull)
.reduce(BigDecimal.ZERO, BigDecimal::add);
// 更新主记录
ExpenseRecordDO updateRecord = new ExpenseRecordDO();
updateRecord.setExpenseRecordId(expenseRecordId);
updateRecord.setTotalProvision(totalProvision);
updateRecord.setTotalExpense(totalExpense);
updateRecord.setDailyProfit(totalProvision.subtract(totalExpense));
updateRecord.setTotalVehicleCount(totalVehicleCount);
expenseRecordMapper.updateById(updateRecord);
}
/**
* 转换ExpenseProvision到ExpenseProvisionDO
*/
private ExpenseProvisionDO convertToExpenseProvisionDO(ExpenseProvision provision) {
ExpenseProvisionDO provisionDO = new ExpenseProvisionDO();
provisionDO.setDealerName(provision.getDealerName());
provisionDO.setVehicleNo(provision.getVehicleNo());
provisionDO.setProvisionAmount(provision.getProvisionAmount());
provisionDO.setOrderId(provision.getOrderId());
return provisionDO;
}
/**
* 转换ExpenseCost到ExpenseCostDO
*/
private ExpenseCostDO convertToExpenseCostDO(ExpenseCost cost) {
ExpenseCostDO costDO = new ExpenseCostDO();
costDO.setCostId(cost.getCostId());
costDO.setCostName(cost.getCostName());
costDO.setExpenseAmount(cost.getExpenseAmount());
costDO.setRemark(cost.getRemark());
return costDO;
}
/**
* 更新ExpenseProvisionDO
*/
private void updateExpenseProvisionDO(ExpenseProvisionDO existingDO, ExpenseProvision provision) {
existingDO.setDealerName(provision.getDealerName());
existingDO.setVehicleNo(provision.getVehicleNo());
existingDO.setProvisionAmount(provision.getProvisionAmount());
existingDO.setOrderId(provision.getOrderId());
}
/**
* 更新ExpenseCostDO
*/
private void updateExpenseCostDO(ExpenseCostDO existingDO, ExpenseCost cost) {
existingDO.setCostId(cost.getCostId());
existingDO.setCostName(cost.getCostName());
existingDO.setExpenseAmount(cost.getExpenseAmount());
existingDO.setRemark(cost.getRemark());
}
}

View File

@ -43,10 +43,8 @@ public class PurchaseOrderGatewayImpl implements PurchaseOrderGateway {
private final OrderVehicleConvert orderVehicleConvert;
private final OrderSupplierMapper orderSupplierMapper;
private final OrderSupplierConvert orderSupplierConvert;
private final OrderCostMapper orderCostMapper;
private final OrderCostConvert orderCostConvert;
private final OrderCostItemMapper orderCostItemMapper;
private final OrderCostItemConvert orderCostItemConvert;
@ -646,7 +644,6 @@ public class PurchaseOrderGatewayImpl implements PurchaseOrderGateway {
}
@Override
@Transactional
public void finalApprove(PurchaseOrderFinalApproveCmd purchaseOrderFinalApproveCmd) {
LambdaQueryWrapper<PurchaseOrderDO> queryWrapper = Wrappers.lambdaQuery(PurchaseOrderDO.class);
queryWrapper.eq(PurchaseOrderDO::getOrderId, purchaseOrderFinalApproveCmd.getOrderId());

View File

@ -0,0 +1,13 @@
package com.xunhong.erp.turbo.biz.infrastructure.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xunhong.erp.turbo.biz.infrastructure.entity.ExpenseCostDO;
import org.apache.ibatis.annotations.Mapper;
/**
* @author shenyifei
*/
@Mapper
public interface ExpenseCostMapper extends BaseMapper<ExpenseCostDO> {
}

View File

@ -0,0 +1,13 @@
package com.xunhong.erp.turbo.biz.infrastructure.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xunhong.erp.turbo.biz.infrastructure.entity.ExpenseProvisionDO;
import org.apache.ibatis.annotations.Mapper;
/**
* @author shenyifei
*/
@Mapper
public interface ExpenseProvisionMapper extends BaseMapper<ExpenseProvisionDO> {
}

View File

@ -0,0 +1,13 @@
package com.xunhong.erp.turbo.biz.infrastructure.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xunhong.erp.turbo.biz.infrastructure.entity.ExpenseRecordDO;
import org.apache.ibatis.annotations.Mapper;
/**
* @author shenyifei
*/
@Mapper
public interface ExpenseRecordMapper extends BaseMapper<ExpenseRecordDO> {
}

View File

@ -16,5 +16,6 @@ import org.apache.ibatis.annotations.Param;
public interface OrderCostMapper extends BaseMapper<OrderCostDO> {
IPage<OrderCostDO> selectPage(IPage<OrderCostDO> page, @Param(Constants.WRAPPER) LambdaQueryWrapper<OrderCostDO> queryWrapper, @Param("query") OrderCostPageQry orderCostPageQry);
OrderCostDO selectByOrderId(@Param("orderId") Long orderId, @Param("name") String name);
}

View File

@ -14,6 +14,7 @@
<result property="freightCostFlag" column="freight_cost_flag"/>
<result property="strawMatCostFlag" column="straw_mat_cost_flag"/>
<result property="includePackingFlag" column="include_packing_flag"/>
<result property="includeFreightFlag" column="include_freight_flag"/>
<result property="enableAccrualTax" column="enable_accrual_tax"/>
<result property="accrualTaxRatio" column="accrual_tax_ratio"/>
<result property="enableCompanyRebate" column="enable_company_rebate"/>

View File

@ -0,0 +1,18 @@
<?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.xunhong.erp.turbo.biz.infrastructure.mapper.ExpenseCostMapper">
<resultMap id="BaseResultMap" type="com.xunhong.erp.turbo.biz.infrastructure.entity.ExpenseCostDO">
<result property="expenseCostId" column="expense_cost_id"/>
<result property="expenseRecordId" column="expense_record_id"/>
<result property="costId" column="cost_id"/>
<result property="costName" column="cost_name"/>
<result property="expenseAmount" column="expense_amount"/>
<result property="remark" column="remark"/>
<result property="createdAt" column="created_at"/>
<result property="createdAt" column="created_at"/>
<result property="updatedAt" column="updated_at"/>
<result property="isDelete" column="is_delete"/>
<result property="version" column="version"/>
</resultMap>
</mapper>

View File

@ -0,0 +1,18 @@
<?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.xunhong.erp.turbo.biz.infrastructure.mapper.ExpenseProvisionMapper">
<resultMap id="BaseResultMap" type="com.xunhong.erp.turbo.biz.infrastructure.entity.ExpenseProvisionDO">
<result property="expenseProvisionId" column="expense_provision_id"/>
<result property="expenseRecordId" column="expense_record_id"/>
<result property="dealerName" column="dealer_name"/>
<result property="vehicleNo" column="vehicle_no"/>
<result property="provisionAmount" column="provision_amount"/>
<result property="orderId" column="order_id"/>
<result property="createdAt" column="created_at"/>
<result property="createdAt" column="created_at"/>
<result property="updatedAt" column="updated_at"/>
<result property="isDelete" column="is_delete"/>
<result property="version" column="version"/>
</resultMap>
</mapper>

View File

@ -0,0 +1,17 @@
<?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.xunhong.erp.turbo.biz.infrastructure.mapper.ExpenseRecordMapper">
<resultMap id="BaseResultMap" type="com.xunhong.erp.turbo.biz.infrastructure.entity.ExpenseRecordDO">
<result property="expenseRecordId" column="expense_record_id"/>
<result property="recordDate" column="record_date"/>
<result property="totalProvision" column="total_provision"/>
<result property="totalExpense" column="total_expense"/>
<result property="dailyProfit" column="daily_profit"/>
<result property="createdAt" column="created_at"/>
<result property="createdAt" column="created_at"/>
<result property="updatedAt" column="updated_at"/>
<result property="isDelete" column="is_delete"/>
<result property="version" column="version"/>
</resultMap>
</mapper>

View File

@ -20,11 +20,32 @@
<result property="isPaid" column="is_paid"/>
<result property="poState" column="po_state"/>
<result property="createdAt" column="created_at"/>
<result property="createdAt" column="created_at"/>
<result property="updatedAt" column="updated_at"/>
<result property="isDelete" column="is_delete"/>
<result property="version" column="version"/>
</resultMap>
<resultMap id="SelectByOrderIdResultMap"
type="com.xunhong.erp.turbo.biz.infrastructure.entity.OrderCostDO">
<result property="orderCostId" column="order_cost_id"/>
<result property="orderId" column="order_id"/>
<result property="costId" column="cost_id"/>
<result property="name" column="name"/>
<result property="price" column="price"/>
<result property="unit" column="unit"/>
<result property="count" column="count"/>
<result property="belong" column="belong"/>
<result property="principal" column="principal"/>
<result property="type" column="type"/>
<result property="isPaid" column="is_paid"/>
<result property="poState" column="po_state"/>
<association property="orderVehicleDO"
javaType="com.xunhong.erp.turbo.biz.infrastructure.entity.OrderVehicleDO">
<result property="vehicleId" column="vehicle_id"/>
<result property="vehicleNo" column="vehicle_no"/>
<result property="dealerId" column="dealer_id"/>
<result property="dealerName" column="dealer_name"/>
</association>
</resultMap>
<select id="selectPage"
resultType="com.xunhong.erp.turbo.biz.infrastructure.entity.OrderCostDO"
resultMap="BaseResultMap">
@ -59,5 +80,23 @@
</if>
</where>
</select>
<select id="selectByOrderId"
resultType="com.xunhong.erp.turbo.biz.infrastructure.entity.OrderCostDO"
resultMap="SelectByOrderIdResultMap">
SELECT
oc.*,
po.state as po_state,
ov.vehicle_id,
ov.vehicle_no,
ov.dealer_id,
ov.dealer_name
FROM order_cost oc
LEFT JOIN order_vehicle ov ON oc.order_id = ov.order_id AND ov.is_delete = 0
LEFT JOIN purchase_order po ON oc.order_id = po.order_id AND
po.is_delete = 0
WHERE oc.order_id = #{orderId}
AND oc.name = #{name}
AND oc.is_delete = 0
</select>
</mapper>

View File

@ -15,6 +15,7 @@
<result property="freightCostFlag" column="freight_cost_flag"/>
<result property="strawMatCostFlag" column="straw_mat_cost_flag"/>
<result property="includePackingFlag" column="include_packing_flag"/>
<result property="includeFreightFlag" column="include_freight_flag"/>
<result property="enableAccrualTax" column="enable_accrual_tax"/>
<result property="accrualTaxRatio" column="accrual_tax_ratio"/>
<result property="enableCompanyRebate" column="enable_company_rebate"/>

View File

@ -0,0 +1,23 @@
package com.xunhong.erp.turbo.api.biz.api;
import com.xunhong.erp.turbo.api.biz.dto.cmd.ExpenseRecordCreateCmd;
import com.xunhong.erp.turbo.api.biz.dto.cmd.ExpenseRecordUpdateCmd;
import com.xunhong.erp.turbo.api.biz.dto.qry.ExpenseRecordListQry;
import com.xunhong.erp.turbo.api.biz.dto.qry.ExpenseRecordShowQry;
import com.xunhong.erp.turbo.api.biz.dto.vo.ExpenseRecordVO;
import java.util.List;
/**
* @author shenyifei
*/
public interface ExpenseRecordServiceI {
ExpenseRecordVO create(ExpenseRecordCreateCmd expenseRecordCreateCmd);
List<ExpenseRecordVO> list(ExpenseRecordListQry expenseRecordListQry);
ExpenseRecordVO update(ExpenseRecordUpdateCmd expenseRecordUpdateCmd);
ExpenseRecordVO show(ExpenseRecordShowQry expenseRecordShowQry);
}

View File

@ -70,6 +70,12 @@ public class DealerCreateCmd extends Command {
@Schema(title = "发货单合计金额是否含包装费")
private Boolean includePackingFlag;
/**
* 发货单合计金额是否含包运费
*/
@Schema(title = "发货单合计金额是否含包运费")
private Boolean includeFreightFlag;
/**
* 应收金额
*/

View File

@ -0,0 +1,40 @@
package com.xunhong.erp.turbo.api.biz.dto.cmd;
import com.xunhong.erp.turbo.api.biz.dto.common.ExpenseCost;
import com.xunhong.erp.turbo.api.biz.dto.common.ExpenseProvision;
import com.xunhong.erp.turbo.base.dto.Command;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDate;
import java.util.List;
/**
* @author shenyifei
*/
@Data
@Schema(title = "花销统计记录创建")
@EqualsAndHashCode(callSuper = true)
public class ExpenseRecordCreateCmd extends Command {
/**
* 记录日期YYYY-MM-DD
*/
@Schema(title = "记录日期YYYY-MM-DD")
private LocalDate recordDate;
/**
* 花销费用明细
*/
@Schema(title = "花销费用明细")
private List<ExpenseCost> expenseCostList;
/**
* 花销计提明细
*/
@Schema(title = "花销计提明细")
private List<ExpenseProvision> expenseProvisionList;
}

View File

@ -0,0 +1,18 @@
package com.xunhong.erp.turbo.api.biz.dto.cmd;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* @author shenyifei
*/
@Data
@Schema(title = "花销统计记录更新")
@EqualsAndHashCode(callSuper = true)
public class ExpenseRecordUpdateCmd extends ExpenseRecordCreateCmd {
@Schema(title = "花销统计记录ID", requiredMode = Schema.RequiredMode.REQUIRED, type = "string")
private Long expenseRecordId;
}

View File

@ -0,0 +1,56 @@
package com.xunhong.erp.turbo.api.biz.dto.common;
import com.alibaba.cola.dto.Command;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
/**
* @author shenyifei
*/
@Data
@Schema(title = "花销费用明细")
@EqualsAndHashCode(callSuper = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ExpenseCost extends Command {
/**
* 明细ID
*/
@Schema(title = "明细ID", requiredMode = Schema.RequiredMode.REQUIRED, type = "string")
private Long expenseCostId;
/**
* 花销统计记录ID
*/
@Schema(title = "花销统计记录ID", requiredMode = Schema.RequiredMode.REQUIRED, type = "string")
private Long expenseRecordId;
/**
* 费用类型ID
*/
@Schema(title = "费用类型ID", requiredMode = Schema.RequiredMode.REQUIRED, type = "string")
private Long costId;
/**
* 费用类型名称
*/
@Schema(title = "费用类型名称", requiredMode = Schema.RequiredMode.REQUIRED, type = "string")
private String costName;
/**
* 花销金额
*/
@Schema(title = "花销金额", requiredMode = Schema.RequiredMode.REQUIRED, type = "string")
private BigDecimal expenseAmount;
/**
* 备注可填员工事由等
*/
@Schema(title = "备注(可填员工、事由等)", type = "string")
private String remark;
}

View File

@ -0,0 +1,56 @@
package com.xunhong.erp.turbo.api.biz.dto.common;
import com.alibaba.cola.dto.Command;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
/**
* @author shenyifei
*/
@Data
@Schema(title = "花销计提明细")
@EqualsAndHashCode(callSuper = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ExpenseProvision extends Command {
/**
* 明细ID
*/
@Schema(title = "明细ID", type = "string", requiredMode = Schema.RequiredMode.REQUIRED)
private Long expenseProvisionId;
/**
* 花销统计记录ID
*/
@Schema(title = "花销统计记录ID", type = "string", requiredMode = Schema.RequiredMode.REQUIRED)
private Long expenseRecordId;
/**
* 经销商名称
*/
@Schema(title = "经销商名称", type = "string")
private String dealerName;
/**
* 车次号
*/
@Schema(title = "车次号", type = "string")
private String vehicleNo;
/**
* 计提金额
*/
@Schema(title = "计提金额", requiredMode = Schema.RequiredMode.REQUIRED)
private BigDecimal provisionAmount;
/**
* 采购单ID
*/
@Schema(title = "采购单ID", type = "string")
private Long orderId;
}

View File

@ -76,6 +76,12 @@ public class OrderDealer extends Command {
@Schema(title = "发货单合计金额是否含包装费")
private Boolean includePackingFlag;
/**
* 发货单合计金额是否含包运费
*/
@Schema(title = "发货单合计金额是否含包运费")
private Boolean includeFreightFlag;
/**
* 是否开启计提税金
*/

View File

@ -8,13 +8,14 @@ import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public enum CostTypeEnum {
/**
* 费用类型1_辅料类型2_人工类型3_产地类型4_其他类型5_物流类型5_物流类型
* 费用类型1_辅料类型2_人工类型3_产地类型4_其他类型5_物流类型5_物流类型6_花销类型
*/
MATERIAL_TYPE(1, "辅料类型"),
ARTIFICIAL_TYPE(2, "人工类型"),
PRODUCTION_TYPE(3, "产地类型"),
OTHER_TYPE(4, "其他类型"),
LOGISTICS_TYPE(5, "物流类型"),
EXPENSE_TYPE(6, "花销类型"),
;
@EnumValue

View File

@ -29,5 +29,11 @@ public class CostListQry extends Query {
*/
@Schema(title = "费用归属0_无归属1_工头2_产地3_司机")
private CostBelongEnum belong;
/**
* 费用名称
*/
@Schema(title = "费用名称")
private String name;
}

View File

@ -0,0 +1,33 @@
package com.xunhong.erp.turbo.api.biz.dto.qry;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.xunhong.erp.turbo.base.dto.Query;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDate;
/**
* @author shenyifei
*/
@Data
@Schema(title = "花销统计记录列表查询")
@EqualsAndHashCode(callSuper = true)
public class ExpenseRecordListQry extends Query {
/**
* 开始时间
*/
@Schema(title = "开始日期", type = "string")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private LocalDate startDate;
/**
* 结束时间
*/
@Schema(title = "结束日期", type = "string")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private LocalDate endDate;
}

View File

@ -0,0 +1,27 @@
package com.xunhong.erp.turbo.api.biz.dto.qry;
import com.xunhong.erp.turbo.base.dto.Query;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDate;
/**
* @author shenyifei
*/
@Data
@Schema(title = "花销统计记录查询")
@EqualsAndHashCode(callSuper = true)
public class ExpenseRecordShowQry extends Query {
@Schema(title = "花销统计记录ID", type = "string")
private Long expenseRecordId;
/**
* 记录日期YYYY-MM-DD
*/
@Schema(title = "记录日期YYYY-MM-DD")
private LocalDate recordDate;
}

View File

@ -72,6 +72,12 @@ public class DealerVO extends DTO {
@Schema(title = "发货单合计金额是否含包装费")
private Boolean includePackingFlag;
/**
* 发货单合计金额是否含包运费
*/
@Schema(title = "发货单合计金额是否含包运费")
private Boolean includeFreightFlag;
/**
* 应收金额
*/

View File

@ -0,0 +1,74 @@
package com.xunhong.erp.turbo.api.biz.dto.vo;
import com.alibaba.cola.dto.DTO;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.xunhong.erp.turbo.api.biz.dto.common.ExpenseCost;
import com.xunhong.erp.turbo.api.biz.dto.common.ExpenseProvision;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
/**
* @author shenyifei
*/
@Data
@Schema(title = "花销统计记录")
@EqualsAndHashCode(callSuper = true)
public class ExpenseRecordVO extends DTO {
/**
* 记录ID
*/
@Schema(title = "记录ID", type = "string", requiredMode = Schema.RequiredMode.REQUIRED)
private Long expenseRecordId;
/**
* 记录日期YYYY-MM-DD
*/
@Schema(title = "记录日期YYYY-MM-DD")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private LocalDate recordDate;
/**
* 计提车次数量
*/
@Schema(title = "计提车次数量")
private Integer totalVehicleCount;
/**
* 计提总金额
*/
@Schema(title = "计提总金额")
private BigDecimal totalProvision;
/**
* 花销总额
*/
@Schema(title = "花销总额")
private BigDecimal totalExpense;
/**
* 日常利润
*/
@Schema(title = "日常利润")
private BigDecimal dailyProfit;
@Schema(title = "花销计提明细")
private List<ExpenseProvision> expenseProvisionList;
@Schema(title = "花销费用明细")
private List<ExpenseCost> expenseCostList;
/**
* 创建时间
*/
@Schema(title = "创建时间")
private LocalDateTime createdAt;
}