Merge remote-tracking branch 'upstream/develop' into develop

This commit is contained in:
dhb52 2024-02-16 22:16:44 +08:00
commit 72113bbbc6
35 changed files with 1168 additions and 76 deletions

View File

@ -72,6 +72,9 @@ ErrorCode PURCHASE_ORDER_ITEM_RETURN_FAIL_IN_EXCEED = new ErrorCode(1_030_101_00
ErrorCode SALE_OUT_APPROVE_FAIL = new ErrorCode(1_020_202_003, "审核失败,只有未审核的出库单才能审核");
ErrorCode SALE_OUT_NO_EXISTS = new ErrorCode(1_020_202_004, "生成出库单失败,请重新提交");
ErrorCode SALE_OUT_UPDATE_FAIL_APPROVE = new ErrorCode(1_020_202_005, "销售出库单({})已审核,无法修改");
ErrorCode SALE_OUT_NOT_APPROVE = new ErrorCode(1_020_202_006, "销售出库单未审核,无法操作");
ErrorCode SALE_OUT_FAIL_RECEIPT_PRICE_EXCEED = new ErrorCode(1_020_202_007, "收款金额({})超过销售出库单总金额({})");
ErrorCode SALE_OUT_PROCESS_FAIL_EXISTS_RECEIPT = new ErrorCode(1_020_202_008, "反审核失败,已存在对应的收款单");
// ========== ERP 销售退货1-030-203-000 ==========
ErrorCode SALE_RETURN_NOT_EXISTS = new ErrorCode(1_020_203_000, "销售退货单不存在");
@ -80,6 +83,9 @@ ErrorCode PURCHASE_ORDER_ITEM_RETURN_FAIL_IN_EXCEED = new ErrorCode(1_030_101_00
ErrorCode SALE_RETURN_APPROVE_FAIL = new ErrorCode(1_020_203_003, "审核失败,只有未审核的退货单才能审核");
ErrorCode SALE_RETURN_NO_EXISTS = new ErrorCode(1_020_203_004, "生成退货单失败,请重新提交");
ErrorCode SALE_RETURN_UPDATE_FAIL_APPROVE = new ErrorCode(1_020_203_005, "销售退货单({})已审核,无法修改");
ErrorCode SALE_RETURN_NOT_APPROVE = new ErrorCode(1_020_203_006, "销售退货单未审核,无法操作");
ErrorCode SALE_RETURN_FAIL_REFUND_PRICE_EXCEED = new ErrorCode(1_020_203_007, "退款金额({})超过销售退货单总金额({})");
ErrorCode SALE_RETURN_PROCESS_FAIL_EXISTS_REFUND = new ErrorCode(1_020_203_008, "反审核失败,已存在对应的退款单");
// ========== ERP 仓库 1-030-400-000 ==========
ErrorCode WAREHOUSE_NOT_EXISTS = new ErrorCode(1_030_400_000, "仓库不存在");
@ -151,4 +157,12 @@ ErrorCode PURCHASE_ORDER_ITEM_RETURN_FAIL_IN_EXCEED = new ErrorCode(1_030_101_00
ErrorCode FINANCE_PAYMENT_NO_EXISTS = new ErrorCode(1_030_601_004, "生成付款单号失败,请重新提交");
ErrorCode FINANCE_PAYMENT_UPDATE_FAIL_APPROVE = new ErrorCode(1_030_601_005, "付款单({})已审核,无法修改");
// ========== ERP 收款单 1-030-602-000 ==========
ErrorCode FINANCE_RECEIPT_NOT_EXISTS = new ErrorCode(1_030_602_000, "收款单不存在");
ErrorCode FINANCE_RECEIPT_DELETE_FAIL_APPROVE = new ErrorCode(1_030_602_001, "收款单({})已审核,无法删除");
ErrorCode FINANCE_RECEIPT_PROCESS_FAIL = new ErrorCode(1_030_602_002, "反审核失败,只有已审核的收款单才能反审核");
ErrorCode FINANCE_RECEIPT_APPROVE_FAIL = new ErrorCode(1_030_602_003, "审核失败,只有未审核的收款单才能审核");
ErrorCode FINANCE_RECEIPT_NO_EXISTS = new ErrorCode(1_030_602_004, "生成收款单号失败,请重新提交");
ErrorCode FINANCE_RECEIPT_UPDATE_FAIL_APPROVE = new ErrorCode(1_030_602_005, "收款单({})已审核,无法修改");
}

View File

@ -0,0 +1,153 @@
package cn.iocoder.yudao.module.erp.controller.admin.finance;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.receipt.ErpFinanceReceiptPageReqVO;
import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.receipt.ErpFinanceReceiptRespVO;
import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.receipt.ErpFinanceReceiptSaveReqVO;
import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpAccountDO;
import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinanceReceiptDO;
import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinanceReceiptItemDO;
import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpCustomerDO;
import cn.iocoder.yudao.module.erp.service.finance.ErpAccountService;
import cn.iocoder.yudao.module.erp.service.finance.ErpFinanceReceiptService;
import cn.iocoder.yudao.module.erp.service.sale.ErpCustomerService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
@Tag(name = "管理后台 - ERP 收款单")
@RestController
@RequestMapping("/erp/finance-receipt")
@Validated
public class ErpFinanceReceiptController {
@Resource
private ErpFinanceReceiptService financeReceiptService;
@Resource
private ErpCustomerService customerService;
@Resource
private ErpAccountService accountService;
@Resource
private AdminUserApi adminUserApi;
@PostMapping("/create")
@Operation(summary = "创建收款单")
@PreAuthorize("@ss.hasPermission('erp:finance-receipt:create')")
public CommonResult<Long> createFinanceReceipt(@Valid @RequestBody ErpFinanceReceiptSaveReqVO createReqVO) {
return success(financeReceiptService.createFinanceReceipt(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新收款单")
@PreAuthorize("@ss.hasPermission('erp:finance-receipt:update')")
public CommonResult<Boolean> updateFinanceReceipt(@Valid @RequestBody ErpFinanceReceiptSaveReqVO updateReqVO) {
financeReceiptService.updateFinanceReceipt(updateReqVO);
return success(true);
}
@PutMapping("/update-status")
@Operation(summary = "更新收款单的状态")
@PreAuthorize("@ss.hasPermission('erp:finance-receipt:update-status')")
public CommonResult<Boolean> updateFinanceReceiptStatus(@RequestParam("id") Long id,
@RequestParam("status") Integer status) {
financeReceiptService.updateFinanceReceiptStatus(id, status);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除收款单")
@Parameter(name = "ids", description = "编号数组", required = true)
@PreAuthorize("@ss.hasPermission('erp:finance-receipt:delete')")
public CommonResult<Boolean> deleteFinanceReceipt(@RequestParam("ids") List<Long> ids) {
financeReceiptService.deleteFinanceReceipt(ids);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得收款单")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('erp:finance-receipt:query')")
public CommonResult<ErpFinanceReceiptRespVO> getFinanceReceipt(@RequestParam("id") Long id) {
ErpFinanceReceiptDO receipt = financeReceiptService.getFinanceReceipt(id);
if (receipt == null) {
return success(null);
}
List<ErpFinanceReceiptItemDO> receiptItemList = financeReceiptService.getFinanceReceiptItemListByReceiptId(id);
return success(BeanUtils.toBean(receipt, ErpFinanceReceiptRespVO.class, financeReceiptVO ->
financeReceiptVO.setItems(BeanUtils.toBean(receiptItemList, ErpFinanceReceiptRespVO.Item.class))));
}
@GetMapping("/page")
@Operation(summary = "获得收款单分页")
@PreAuthorize("@ss.hasPermission('erp:finance-receipt:query')")
public CommonResult<PageResult<ErpFinanceReceiptRespVO>> getFinanceReceiptPage(@Valid ErpFinanceReceiptPageReqVO pageReqVO) {
PageResult<ErpFinanceReceiptDO> pageResult = financeReceiptService.getFinanceReceiptPage(pageReqVO);
return success(buildFinanceReceiptVOPageResult(pageResult));
}
@GetMapping("/export-excel")
@Operation(summary = "导出收款单 Excel")
@PreAuthorize("@ss.hasPermission('erp:finance-receipt:export')")
@OperateLog(type = EXPORT)
public void exportFinanceReceiptExcel(@Valid ErpFinanceReceiptPageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<ErpFinanceReceiptRespVO> list = buildFinanceReceiptVOPageResult(financeReceiptService.getFinanceReceiptPage(pageReqVO)).getList();
// 导出 Excel
ExcelUtils.write(response, "收款单.xls", "数据", ErpFinanceReceiptRespVO.class, list);
}
private PageResult<ErpFinanceReceiptRespVO> buildFinanceReceiptVOPageResult(PageResult<ErpFinanceReceiptDO> pageResult) {
if (CollUtil.isEmpty(pageResult.getList())) {
return PageResult.empty(pageResult.getTotal());
}
// 1.1 收款项
List<ErpFinanceReceiptItemDO> receiptItemList = financeReceiptService.getFinanceReceiptItemListByReceiptIds(
convertSet(pageResult.getList(), ErpFinanceReceiptDO::getId));
Map<Long, List<ErpFinanceReceiptItemDO>> financeReceiptItemMap = convertMultiMap(receiptItemList, ErpFinanceReceiptItemDO::getReceiptId);
// 1.2 客户信息
Map<Long, ErpCustomerDO> customerMap = customerService.getCustomerMap(
convertSet(pageResult.getList(), ErpFinanceReceiptDO::getCustomerId));
// 1.3 结算账户信息
Map<Long, ErpAccountDO> accountMap = accountService.getAccountMap(
convertSet(pageResult.getList(), ErpFinanceReceiptDO::getAccountId));
// 1.4 管理员信息
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(pageResult.getList(),
contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getFinanceUserId())));
// 2. 开始拼接
return BeanUtils.toBean(pageResult, ErpFinanceReceiptRespVO.class, receipt -> {
receipt.setItems(BeanUtils.toBean(financeReceiptItemMap.get(receipt.getId()), ErpFinanceReceiptRespVO.Item.class));
MapUtils.findAndThen(customerMap, receipt.getCustomerId(), customer -> receipt.setCustomerName(customer.getName()));
MapUtils.findAndThen(accountMap, receipt.getAccountId(), account -> receipt.setAccountName(account.getName()));
MapUtils.findAndThen(userMap, Long.parseLong(receipt.getCreator()), user -> receipt.setCreatorName(user.getNickname()));
MapUtils.findAndThen(userMap, receipt.getFinanceUserId(), user -> receipt.setFinanceUserName(user.getNickname()));
});
}
}

View File

@ -17,7 +17,7 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
@ToString(callSuper = true)
public class ErpFinancePaymentPageReqVO extends PageParam {
@Schema(description = "采购单编号", example = "XS001")
@Schema(description = "付款单编号", example = "XS001")
private String no;
@Schema(description = "付款时间")

View File

@ -79,10 +79,10 @@ public class ErpFinancePaymentRespVO {
@Schema(description = "业务单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11756")
private String bizNo;
@Schema(description = "应付欠款,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000")
@Schema(description = "应付金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000")
private BigDecimal totalPrice;
@Schema(description = "已付欠款,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000")
@Schema(description = "已付金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000")
private BigDecimal paidPrice;
@Schema(description = "本次付款,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000")

View File

@ -58,8 +58,8 @@ public class ErpFinancePaymentSaveReqVO {
@NotNull(message = "业务编号不能为空")
private Long bizId;
@Schema(description = "已付欠款,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000")
@NotNull(message = "已付欠款不能为空")
@Schema(description = "已付金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000")
@NotNull(message = "已付金额不能为空")
private BigDecimal paidPrice;
@Schema(description = "本次付款,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000")

View File

@ -0,0 +1,48 @@
package cn.iocoder.yudao.module.erp.controller.admin.finance.vo.receipt;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - ERP 收款单分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ErpFinanceReceiptPageReqVO extends PageParam {
@Schema(description = "收款单编号", example = "XS001")
private String no;
@Schema(description = "收款时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] receiptTime;
@Schema(description = "客户编号", example = "1724")
private Long customerId;
@Schema(description = "创建者", example = "666")
private String creator;
@Schema(description = "财务人员编号", example = "888")
private String financeUserId;
@Schema(description = "收款账户编号", example = "31189")
private Long accountId;
@Schema(description = "收款状态", example = "2")
private Integer status;
@Schema(description = "备注", example = "你猜")
private String remark;
@Schema(description = "业务编号", example = "123")
private String bizNo;
}

View File

@ -0,0 +1,97 @@
package cn.iocoder.yudao.module.erp.controller.admin.finance.vo.receipt;
import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - ERP 收款单 Response VO")
@Data
public class ErpFinanceReceiptRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23752")
private Long id;
@Schema(description = "收款单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "FKD888")
private String no;
@Schema(description = "收款状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status;
@Schema(description = "收款时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime receiptTime;
@Schema(description = "财务人员编号", example = "19690")
private Long financeUserId;
@Schema(description = "财务人员名称", example = "张三")
private String financeUserName;
@Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "29399")
private Long customerId;
@Schema(description = "客户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "小番茄公司")
private String customerName;
@Schema(description = "收款账户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "28989")
private Long accountId;
@Schema(description = "收款账户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
private String accountName;
@Schema(description = "合计价格,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "13832")
private BigDecimal totalPrice;
@Schema(description = "优惠金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "11600")
private BigDecimal discountPrice;
@Schema(description = "实际价格,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000")
private BigDecimal receiptPrice;
@Schema(description = "备注", example = "你猜")
private String remark;
@Schema(description = "创建人", example = "芋道")
private String creator;
@Schema(description = "创建人名称", example = "芋道")
private String creatorName;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
@Schema(description = "收款项列表", requiredMode = Schema.RequiredMode.REQUIRED)
private List<Item> items;
@Data
public static class Item {
@Schema(description = "收款项编号", example = "11756")
private Long id;
@Schema(description = "业务类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer bizType;
@Schema(description = "业务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11756")
private Long bizId;
@Schema(description = "业务单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11756")
private String bizNo;
@Schema(description = "应收金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000")
private BigDecimal totalPrice;
@Schema(description = "已收金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000")
private BigDecimal receiptedPrice;
@Schema(description = "本次收款,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000")
@NotNull(message = "本次收款不能为空")
private BigDecimal receiptPrice;
@Schema(description = "备注", example = "随便")
private String remark;
}
}

View File

@ -0,0 +1,74 @@
package cn.iocoder.yudao.module.erp.controller.admin.finance.vo.receipt;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - ERP 收款单新增/修改 Request VO")
@Data
public class ErpFinanceReceiptSaveReqVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23752")
private Long id;
@Schema(description = "收款时间", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "收款时间不能为空")
private LocalDateTime receiptTime;
@Schema(description = "财务人员编号", example = "19690")
private Long financeUserId;
@Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "29399")
@NotNull(message = "客户编号不能为空")
private Long customerId;
@Schema(description = "收款账户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "28989")
@NotNull(message = "收款账户编号不能为空")
private Long accountId;
@Schema(description = "优惠金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "11600")
@NotNull(message = "优惠金额不能为空")
private BigDecimal discountPrice;
@Schema(description = "备注", example = "你猜")
private String remark;
@Schema(description = "收款项列表", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "收款项列表不能为空")
@Valid
private List<Item> items;
@Data
public static class Item {
@Schema(description = "收款项编号", example = "11756")
private Long id;
@Schema(description = "业务类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "业务类型不能为空")
private Integer bizType;
@Schema(description = "业务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11756")
@NotNull(message = "业务编号不能为空")
private Long bizId;
@Schema(description = "已收金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000")
@NotNull(message = "已收金额不能为空")
private BigDecimal receiptedPrice;
@Schema(description = "本次收款,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000")
@NotNull(message = "本次收款不能为空")
private BigDecimal receiptPrice;
@Schema(description = "备注", example = "随便")
private String remark;
}
}

View File

@ -17,6 +17,10 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
@ToString(callSuper = true)
public class ErpSaleOutPageReqVO extends PageParam {
public static final Integer RECEIPT_STATUS_NONE = 0;
public static final Integer RECEIPT_STATUS_PART = 1;
public static final Integer RECEIPT_STATUS_ALL = 2;
@Schema(description = "销售单编号", example = "XS001")
private String no;
@ -45,8 +49,11 @@ public class ErpSaleOutPageReqVO extends PageParam {
@Schema(description = "结算账号编号", example = "1")
private Long accountId;
@Schema(description = "是否欠款", example = "true")
private Boolean debtStatus;
@Schema(description = "收款状态", example = "1")
private Integer receiptStatus;
@Schema(description = "是否可收款", example = "true")
private Boolean receiptEnable; // 对应 receiptStatus = [0, 1]
@Schema(description = "销售单号", example = "1")
private String orderNo;

View File

@ -55,6 +55,8 @@ public class ErpSaleOutRespVO {
@Schema(description = "最终合计价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "24906")
@ExcelProperty("最终合计价格")
private BigDecimal totalPrice;
@Schema(description = "已收款金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127")
private BigDecimal receiptPrice;
@Schema(description = "合计产品价格,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127")
private BigDecimal totalProductPrice;
@ -68,15 +70,9 @@ public class ErpSaleOutRespVO {
@Schema(description = "优惠金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127")
private BigDecimal discountPrice;
@Schema(description = "定金金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127")
@Schema(description = "其它金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127")
private BigDecimal otherPrice;
@Schema(description = "本次收款,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127")
private BigDecimal payPrice;
@Schema(description = "本次欠款,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "666")
private BigDecimal debtPrice;
@Schema(description = "附件地址", example = "https://www.iocoder.cn")
@ExcelProperty("附件地址")
private String fileUrl;

View File

@ -35,10 +35,6 @@ public class ErpSaleOutSaveReqVO {
@Schema(description = "其它金额,单位:元", example = "7127")
private BigDecimal otherPrice;
@Schema(description = "本次收款,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127")
@NotNull(message = "本次收款不能为空")
private BigDecimal payPrice;
@Schema(description = "附件地址", example = "https://www.iocoder.cn")
private String fileUrl;

View File

@ -17,6 +17,10 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
@ToString(callSuper = true)
public class ErpSaleReturnPageReqVO extends PageParam {
public static final Integer REFUND_STATUS_NONE = 0;
public static final Integer REFUND_STATUS_PART = 1;
public static final Integer REFUND_STATUS_ALL = 2;
@Schema(description = "销售单编号", example = "XS001")
private String no;
@ -48,4 +52,10 @@ public class ErpSaleReturnPageReqVO extends PageParam {
@Schema(description = "销售单号", example = "1")
private String orderNo;
@Schema(description = "退款状态", example = "1")
private Integer refundStatus;
@Schema(description = "是否可退款", example = "true")
private Boolean refundEnable; // 对应 refundStatus = [0, 1]
}

View File

@ -55,6 +55,8 @@ public class ErpSaleReturnRespVO {
@Schema(description = "最终合计价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "24906")
@ExcelProperty("最终合计价格")
private BigDecimal totalPrice;
@Schema(description = "已退款金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127")
private BigDecimal refundPrice;
@Schema(description = "合计产品价格,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127")
private BigDecimal totalProductPrice;
@ -68,15 +70,9 @@ public class ErpSaleReturnRespVO {
@Schema(description = "优惠金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127")
private BigDecimal discountPrice;
@Schema(description = "定金金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127")
@Schema(description = "其它金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127")
private BigDecimal otherPrice;
@Schema(description = "本次退款,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127")
private BigDecimal refundPrice;
@Schema(description = "本次欠款,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "666")
private BigDecimal debtPrice;
@Schema(description = "附件地址", example = "https://www.iocoder.cn")
@ExcelProperty("附件地址")
private String fileUrl;

View File

@ -35,10 +35,6 @@ public class ErpSaleReturnSaveReqVO {
@Schema(description = "其它金额,单位:元", example = "7127")
private BigDecimal otherPrice;
@Schema(description = "本次退款,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127")
@NotNull(message = "本次退款不能为空")
private BigDecimal refundPrice;
@Schema(description = "附件地址", example = "https://www.iocoder.cn")
private String fileUrl;

View File

@ -56,11 +56,11 @@ public class ErpFinancePaymentItemDO extends BaseDO {
private String bizNo;
/**
* 应付欠款单位
* 应付金额单位
*/
private BigDecimal totalPrice;
/**
* 已付欠款单位
* 已付金额单位
*/
private BigDecimal paidPrice;
/**

View File

@ -0,0 +1,86 @@
package cn.iocoder.yudao.module.erp.dal.dataobject.finance;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpCustomerDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* ERP 收款单 DO
*
* @author 芋道源码
*/
@TableName("erp_finance_receipt")
@KeySequence("erp_finance_receipt_seq") // 用于 OraclePostgreSQLKingbaseDB2H2 数据库的主键自增如果是 MySQL 等数据库可不写
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ErpFinanceReceiptDO extends BaseDO {
/**
* 编号
*/
@TableId
private Long id;
/**
* 收款单号
*/
private String no;
/**
* 收款状态
*
* 枚举 {@link cn.iocoder.yudao.module.erp.enums.ErpAuditStatus}
*/
private Integer status;
/**
* 收款时间
*/
private LocalDateTime receiptTime;
/**
* 财务人员编号
*
* 关联 AdminUserDO id 字段
*/
private Long financeUserId;
/**
* 客户编号
*
* 关联 {@link ErpCustomerDO#getId()}
*/
private Long customerId;
/**
* 收款账户编号
*
* 关联 {@link ErpAccountDO#getId()}
*/
private Long accountId;
/**
* 合计价格单位
*/
private BigDecimal totalPrice;
/**
* 优惠金额单位
*/
private BigDecimal discountPrice;
/**
* 实付金额单位
*
* receiptPrice = totalPrice - discountPrice
*/
private BigDecimal receiptPrice;
/**
* 备注
*/
private String remark;
}

View File

@ -0,0 +1,75 @@
package cn.iocoder.yudao.module.erp.dal.dataobject.finance;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOutDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.math.BigDecimal;
/**
* ERP 收款项 DO
*
* @author 芋道源码
*/
@TableName("erp_finance_receipt_item")
@KeySequence("erp_finance_receipt_item_seq") // 用于 OraclePostgreSQLKingbaseDB2H2 数据库的主键自增如果是 MySQL 等数据库可不写
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ErpFinanceReceiptItemDO extends BaseDO {
/**
* 入库项编号
*/
@TableId
private Long id;
/**
* 收款单编号
*
* 关联 {@link ErpFinanceReceiptDO#getId()}
*/
private Long receiptId;
/**
* 业务类型
*
* 枚举 {@link cn.iocoder.yudao.module.erp.enums.common.ErpBizTypeEnum} 的销售出库退货
*/
private Integer bizType;
/**
* 业务编号
*
* 例如说{@link ErpSaleOutDO#getId()}
*/
private Long bizId;
/**
* 业务单号
*
* 例如说{@link ErpSaleOutDO#getNo()}
*/
private String bizNo;
/**
* 应收金额单位
*/
private BigDecimal totalPrice;
/**
* 已收金额单位
*/
private BigDecimal receiptedPrice;
/**
* 本次收款单位
*/
private BigDecimal receiptPrice;
/**
* 备注
*/
private String remark;
}

View File

@ -83,7 +83,7 @@ public class ErpPurchaseInDO extends BaseDO {
/**
* 已支付金额单位
*
* 目的 TODO erp_finance_payment 结合记录已支付金额
* 目的 {@link cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentDO} 结合记录已支付金额
*/
private BigDecimal paymentPrice;

View File

@ -83,7 +83,7 @@ public class ErpPurchaseReturnDO extends BaseDO {
/**
* 已退款金额单位
*
* 目的 TODO erp_finance_payment 结合记录已退款金额
* 目的 {@link cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentDO} 结合记录已支付金额
*/
private BigDecimal refundPrice;

View File

@ -83,9 +83,15 @@ public class ErpSaleOutDO extends BaseDO {
/**
* 最终合计价格单位
*
* totalPrice = totalProductPrice + totalTaxPrice - discountPrice
* totalPrice = totalProductPrice + totalTaxPrice - discountPrice + otherPrice
*/
private BigDecimal totalPrice;
/**
* 已收款金额单位
*
* 目的 {@link cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinanceReceiptDO} 结合记录已收款金额
*/
private BigDecimal receiptPrice;
/**
* 合计产品价格单位
@ -110,18 +116,6 @@ public class ErpSaleOutDO extends BaseDO {
*/
private BigDecimal otherPrice;
// TODO 芋艿receiptPrice
/**
* 本次收款单位
*
* payPrice = totalPrice + otherPrice - debtPrice
*/
private BigDecimal payPrice;
/**
* 本次欠款单位
*/
private BigDecimal debtPrice;
/**
* 附件地址
*/

View File

@ -83,9 +83,15 @@ public class ErpSaleReturnDO extends BaseDO {
/**
* 最终合计价格单位
*
* totalPrice = totalProductPrice + totalTaxPrice - discountPrice
* totalPrice = totalProductPrice + totalTaxPrice - discountPrice + otherPrice
*/
private BigDecimal totalPrice;
/**
* 已退款金额单位
*
* 目的 {@link cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinanceReceiptDO} 结合记录已退款金额
*/
private BigDecimal refundPrice;
/**
* 合计产品价格单位
@ -110,17 +116,6 @@ public class ErpSaleReturnDO extends BaseDO {
*/
private BigDecimal otherPrice;
/**
* 本次收款单位
*
* refundPrice = totalPrice + otherPrice - debtPrice
*/
private BigDecimal refundPrice;
/**
* 本次欠款单位
*/
private BigDecimal debtPrice;
/**
* 附件地址
*/

View File

@ -0,0 +1,44 @@
package cn.iocoder.yudao.module.erp.dal.mysql.finance;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinanceReceiptItemDO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.ibatis.annotations.Mapper;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* ERP 收款单项 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface ErpFinanceReceiptItemMapper extends BaseMapperX<ErpFinanceReceiptItemDO> {
default List<ErpFinanceReceiptItemDO> selectListByReceiptId(Long receiptId) {
return selectList(ErpFinanceReceiptItemDO::getReceiptId, receiptId);
}
default List<ErpFinanceReceiptItemDO> selectListByReceiptIds(Collection<Long> receiptIds) {
return selectList(ErpFinanceReceiptItemDO::getReceiptId, receiptIds);
}
default BigDecimal selectReceiptPriceSumByBizIdAndBizType(Long bizId, Integer bizType) {
// SQL sum 查询
List<Map<String, Object>> result = selectMaps(new QueryWrapper<ErpFinanceReceiptItemDO>()
.select("SUM(receipt_price) AS receiptPriceSum")
.eq("biz_id", bizId)
.eq("biz_type", bizType));
// 获得数量
if (CollUtil.isEmpty(result)) {
return BigDecimal.ZERO;
}
return BigDecimal.valueOf(MapUtil.getDouble(result.get(0), "receiptPriceSum", 0D));
}
}

View File

@ -0,0 +1,48 @@
package cn.iocoder.yudao.module.erp.dal.mysql.finance;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.receipt.ErpFinanceReceiptPageReqVO;
import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinanceReceiptDO;
import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinanceReceiptItemDO;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.apache.ibatis.annotations.Mapper;
/**
* ERP 收款单 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface ErpFinanceReceiptMapper extends BaseMapperX<ErpFinanceReceiptDO> {
default PageResult<ErpFinanceReceiptDO> selectPage(ErpFinanceReceiptPageReqVO reqVO) {
MPJLambdaWrapperX<ErpFinanceReceiptDO> query = new MPJLambdaWrapperX<ErpFinanceReceiptDO>()
.likeIfPresent(ErpFinanceReceiptDO::getNo, reqVO.getNo())
.betweenIfPresent(ErpFinanceReceiptDO::getReceiptTime, reqVO.getReceiptTime())
.eqIfPresent(ErpFinanceReceiptDO::getCustomerId, reqVO.getCustomerId())
.eqIfPresent(ErpFinanceReceiptDO::getCreator, reqVO.getCreator())
.eqIfPresent(ErpFinanceReceiptDO::getFinanceUserId, reqVO.getFinanceUserId())
.eqIfPresent(ErpFinanceReceiptDO::getAccountId, reqVO.getAccountId())
.eqIfPresent(ErpFinanceReceiptDO::getStatus, reqVO.getStatus())
.likeIfPresent(ErpFinanceReceiptDO::getRemark, reqVO.getRemark())
.orderByDesc(ErpFinanceReceiptDO::getId);
if (reqVO.getBizNo() != null) {
query.leftJoin(ErpFinanceReceiptItemDO.class, ErpFinanceReceiptItemDO::getReceiptId, ErpFinanceReceiptDO::getId)
.eq(reqVO.getBizNo() != null, ErpFinanceReceiptItemDO::getBizNo, reqVO.getBizNo())
.groupBy(ErpFinanceReceiptDO::getId); // 避免 1 对多查询产生相同的 1
}
return selectJoinPage(reqVO, ErpFinanceReceiptDO.class, query);
}
default int updateByIdAndStatus(Long id, Integer status, ErpFinanceReceiptDO updateObj) {
return update(updateObj, new LambdaUpdateWrapper<ErpFinanceReceiptDO>()
.eq(ErpFinanceReceiptDO::getId, id).eq(ErpFinanceReceiptDO::getStatus, status));
}
default ErpFinanceReceiptDO selectByNo(String no) {
return selectOne(ErpFinanceReceiptDO::getNo, no);
}
}

View File

@ -7,11 +7,12 @@ import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.out.ErpSaleOutPageReqVO;
import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOutDO;
import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOutItemDO;
import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.apache.ibatis.annotations.Mapper;
import java.math.BigDecimal;
import java.util.List;
import java.util.Objects;
/**
* ERP 销售出库 Mapper
@ -32,10 +33,17 @@ public interface ErpSaleOutMapper extends BaseMapperX<ErpSaleOutDO> {
.eqIfPresent(ErpSaleOutDO::getAccountId, reqVO.getAccountId())
.likeIfPresent(ErpSaleOutDO::getOrderNo, reqVO.getOrderNo())
.orderByDesc(ErpSaleOutDO::getId);
if (Boolean.TRUE.equals(reqVO.getDebtStatus())) {
query.gt(ErpSaleOutDO::getDebtPrice, BigDecimal.ZERO);
} else if (Boolean.FALSE.equals(reqVO.getDebtStatus())) {
query.eq(ErpSaleOutDO::getDebtPrice, BigDecimal.ZERO);
// 收款状态为什么需要 t. 的原因是因为联表查询时需要指定表名不然会报字段不存在的错误
if (Objects.equals(reqVO.getReceiptStatus(), ErpSaleOutPageReqVO.RECEIPT_STATUS_NONE)) {
query.eq(ErpSaleOutDO::getReceiptPrice, 0);
} else if (Objects.equals(reqVO.getReceiptStatus(), ErpSaleOutPageReqVO.RECEIPT_STATUS_PART)) {
query.gt(ErpSaleOutDO::getReceiptPrice, 0).apply("t.receipt_price < t.total_price");
} else if (Objects.equals(reqVO.getReceiptStatus(), ErpSaleOutPageReqVO.RECEIPT_STATUS_ALL)) {
query.apply("t.receipt_price = t.total_price");
}
if (Boolean.TRUE.equals(reqVO.getReceiptEnable())) {
query.eq(ErpSaleOutDO::getStatus, ErpAuditStatus.APPROVE.getStatus())
.apply("t.receipt_price < t.total_price");
}
if (reqVO.getWarehouseId() != null || reqVO.getProductId() != null) {
query.leftJoin(ErpSaleOutItemDO.class, ErpSaleOutItemDO::getOutId, ErpSaleOutDO::getId)

View File

@ -5,12 +5,15 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.returns.ErpSaleReturnPageReqVO;
import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOutDO;
import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleReturnDO;
import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleReturnItemDO;
import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
import java.util.Objects;
/**
* ERP 销售退货 Mapper
@ -31,6 +34,18 @@ public interface ErpSaleReturnMapper extends BaseMapperX<ErpSaleReturnDO> {
.eqIfPresent(ErpSaleReturnDO::getAccountId, reqVO.getAccountId())
.likeIfPresent(ErpSaleReturnDO::getOrderNo, reqVO.getOrderNo())
.orderByDesc(ErpSaleReturnDO::getId);
// 退款状态为什么需要 t. 的原因是因为联表查询时需要指定表名不然会报字段不存在的错误
if (Objects.equals(reqVO.getRefundStatus(), ErpSaleReturnPageReqVO.REFUND_STATUS_NONE)) {
query.eq(ErpSaleReturnDO::getRefundPrice, 0);
} else if (Objects.equals(reqVO.getRefundStatus(), ErpSaleReturnPageReqVO.REFUND_STATUS_PART)) {
query.gt(ErpSaleReturnDO::getRefundPrice, 0).apply("t.refund_price < t.total_price");
} else if (Objects.equals(reqVO.getRefundStatus(), ErpSaleReturnPageReqVO.REFUND_STATUS_ALL)) {
query.apply("t.refund_price = t.total_price");
}
if (Boolean.TRUE.equals(reqVO.getRefundEnable())) {
query.eq(ErpSaleOutDO::getStatus, ErpAuditStatus.APPROVE.getStatus())
.apply("t.refund_price < t.total_price");
}
if (reqVO.getWarehouseId() != null || reqVO.getProductId() != null) {
query.leftJoin(ErpSaleReturnItemDO.class, ErpSaleReturnItemDO::getReturnId, ErpSaleReturnDO::getId)
.eq(reqVO.getWarehouseId() != null, ErpSaleReturnItemDO::getWarehouseId, reqVO.getWarehouseId())

View File

@ -68,6 +68,10 @@ public class ErpNoRedisDAO {
* 付款单 {@link cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentDO}
*/
public static final String FINANCE_PAYMENT_NO_PREFIX = "FKD";
/**
* 收款单 {@link cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinanceReceiptDO}
*/
public static final String FINANCE_RECEIPT_NO_PREFIX = "SKD";
@Resource
private StringRedisTemplate stringRedisTemplate;

View File

@ -96,6 +96,9 @@ public class ErpFinancePaymentServiceImpl implements ErpFinancePaymentService {
// 2.2 插入付款单项
paymentItems.forEach(o -> o.setPaymentId(payment.getId()));
financePaymentItemMapper.insertBatch(paymentItems);
// 3. 更新采购入库退货的付款金额情况
updatePurchasePrice(paymentItems);
return payment.getId();
}
@ -127,9 +130,6 @@ public class ErpFinancePaymentServiceImpl implements ErpFinancePaymentService {
financePaymentMapper.updateById(updateObj);
// 2.2 更新付款单项
updateFinancePaymentItemList(updateReqVO.getId(), paymentItems);
// 3. 更新采购入库退货的付款金额情况
updatePurchasePrice(paymentItems);
}
private void calculateTotalPrice(ErpFinancePaymentDO payment, List<ErpFinancePaymentItemDO> paymentItems) {

View File

@ -0,0 +1,84 @@
package cn.iocoder.yudao.module.erp.service.finance;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.receipt.ErpFinanceReceiptPageReqVO;
import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.receipt.ErpFinanceReceiptSaveReqVO;
import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinanceReceiptDO;
import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinanceReceiptItemDO;
import jakarta.validation.Valid;
import java.util.Collection;
import java.util.List;
/**
* ERP 收款单 Service 接口
*
* @author 芋道源码
*/
public interface ErpFinanceReceiptService {
/**
* 创建收款单
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createFinanceReceipt(@Valid ErpFinanceReceiptSaveReqVO createReqVO);
/**
* 更新收款单
*
* @param updateReqVO 更新信息
*/
void updateFinanceReceipt(@Valid ErpFinanceReceiptSaveReqVO updateReqVO);
/**
* 更新收款单的状态
*
* @param id 编号
* @param status 状态
*/
void updateFinanceReceiptStatus(Long id, Integer status);
/**
* 删除收款单
*
* @param ids 编号数组
*/
void deleteFinanceReceipt(List<Long> ids);
/**
* 获得收款单
*
* @param id 编号
* @return 收款单
*/
ErpFinanceReceiptDO getFinanceReceipt(Long id);
/**
* 获得收款单分页
*
* @param pageReqVO 分页查询
* @return 收款单分页
*/
PageResult<ErpFinanceReceiptDO> getFinanceReceiptPage(ErpFinanceReceiptPageReqVO pageReqVO);
// ==================== 收款单项 ====================
/**
* 获得收款单项列表
*
* @param receiptId 收款单编号
* @return 收款单项列表
*/
List<ErpFinanceReceiptItemDO> getFinanceReceiptItemListByReceiptId(Long receiptId);
/**
* 获得收款单项 List
*
* @param receiptIds 收款单编号数组
* @return 收款单项 List
*/
List<ErpFinanceReceiptItemDO> getFinanceReceiptItemListByReceiptIds(Collection<Long> receiptIds);
}

View File

@ -0,0 +1,273 @@
package cn.iocoder.yudao.module.erp.service.finance;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.receipt.ErpFinanceReceiptPageReqVO;
import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.receipt.ErpFinanceReceiptSaveReqVO;
import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinanceReceiptDO;
import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinanceReceiptItemDO;
import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOutDO;
import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleReturnDO;
import cn.iocoder.yudao.module.erp.dal.mysql.finance.ErpFinanceReceiptItemMapper;
import cn.iocoder.yudao.module.erp.dal.mysql.finance.ErpFinanceReceiptMapper;
import cn.iocoder.yudao.module.erp.dal.redis.no.ErpNoRedisDAO;
import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus;
import cn.iocoder.yudao.module.erp.enums.common.ErpBizTypeEnum;
import cn.iocoder.yudao.module.erp.service.sale.ErpCustomerService;
import cn.iocoder.yudao.module.erp.service.sale.ErpSaleOutService;
import cn.iocoder.yudao.module.erp.service.sale.ErpSaleReturnService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*;
// TODO 芋艿记录操作日志
/**
* ERP 收款单 Service 实现类
*
* @author 芋道源码
*/
@Service
@Validated
public class ErpFinanceReceiptServiceImpl implements ErpFinanceReceiptService {
@Resource
private ErpFinanceReceiptMapper financeReceiptMapper;
@Resource
private ErpFinanceReceiptItemMapper financeReceiptItemMapper;
@Resource
private ErpNoRedisDAO noRedisDAO;
@Resource
private ErpCustomerService customerService;
@Resource
private ErpAccountService accountService;
@Resource
private ErpSaleOutService saleOutService;
@Resource
private ErpSaleReturnService saleReturnService;
@Resource
private AdminUserApi adminUserApi;
@Override
@Transactional(rollbackFor = Exception.class)
public Long createFinanceReceipt(ErpFinanceReceiptSaveReqVO createReqVO) {
// 1.1 校验订单项的有效性
List<ErpFinanceReceiptItemDO> receiptItems = validateFinanceReceiptItems(
createReqVO.getCustomerId(), createReqVO.getItems());
// 1.2 校验客户
customerService.validateCustomer(createReqVO.getCustomerId());
// 1.3 校验结算账户
if (createReqVO.getAccountId() != null) {
accountService.validateAccount(createReqVO.getAccountId());
}
// 1.4 校验财务人员
if (createReqVO.getFinanceUserId() != null) {
adminUserApi.validateUser(createReqVO.getFinanceUserId());
}
// 1.5 生成收款单号并校验唯一性
String no = noRedisDAO.generate(ErpNoRedisDAO.FINANCE_RECEIPT_NO_PREFIX);
if (financeReceiptMapper.selectByNo(no) != null) {
throw exception(FINANCE_RECEIPT_NO_EXISTS);
}
// 2.1 插入收款单
ErpFinanceReceiptDO receipt = BeanUtils.toBean(createReqVO, ErpFinanceReceiptDO.class, in -> in
.setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus()));
calculateTotalPrice(receipt, receiptItems);
financeReceiptMapper.insert(receipt);
// 2.2 插入收款单项
receiptItems.forEach(o -> o.setReceiptId(receipt.getId()));
financeReceiptItemMapper.insertBatch(receiptItems);
// 3. 更新销售出库退货的收款金额情况
updateSalePrice(receiptItems);
return receipt.getId();
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateFinanceReceipt(ErpFinanceReceiptSaveReqVO updateReqVO) {
// 1.1 校验存在
ErpFinanceReceiptDO receipt = validateFinanceReceiptExists(updateReqVO.getId());
if (ErpAuditStatus.APPROVE.getStatus().equals(receipt.getStatus())) {
throw exception(FINANCE_RECEIPT_UPDATE_FAIL_APPROVE, receipt.getNo());
}
// 1.2 校验客户
customerService.validateCustomer(updateReqVO.getCustomerId());
// 1.3 校验结算账户
if (updateReqVO.getAccountId() != null) {
accountService.validateAccount(updateReqVO.getAccountId());
}
// 1.4 校验财务人员
if (updateReqVO.getFinanceUserId() != null) {
adminUserApi.validateUser(updateReqVO.getFinanceUserId());
}
// 1.5 校验收款单项的有效性
List<ErpFinanceReceiptItemDO> receiptItems = validateFinanceReceiptItems(
updateReqVO.getCustomerId(), updateReqVO.getItems());
// 2.1 更新收款单
ErpFinanceReceiptDO updateObj = BeanUtils.toBean(updateReqVO, ErpFinanceReceiptDO.class);
calculateTotalPrice(updateObj, receiptItems);
financeReceiptMapper.updateById(updateObj);
// 2.2 更新收款单项
updateFinanceReceiptItemList(updateReqVO.getId(), receiptItems);
}
private void calculateTotalPrice(ErpFinanceReceiptDO receipt, List<ErpFinanceReceiptItemDO> receiptItems) {
receipt.setTotalPrice(getSumValue(receiptItems, ErpFinanceReceiptItemDO::getReceiptPrice, BigDecimal::add, BigDecimal.ZERO));
receipt.setReceiptPrice(receipt.getTotalPrice().subtract(receipt.getDiscountPrice()));
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateFinanceReceiptStatus(Long id, Integer status) {
boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status);
// 1.1 校验存在
ErpFinanceReceiptDO receipt = validateFinanceReceiptExists(id);
// 1.2 校验状态
if (receipt.getStatus().equals(status)) {
throw exception(approve ? FINANCE_RECEIPT_APPROVE_FAIL : FINANCE_RECEIPT_PROCESS_FAIL);
}
// 2. 更新状态
int updateCount = financeReceiptMapper.updateByIdAndStatus(id, receipt.getStatus(),
new ErpFinanceReceiptDO().setStatus(status));
if (updateCount == 0) {
throw exception(approve ? FINANCE_RECEIPT_APPROVE_FAIL : FINANCE_RECEIPT_PROCESS_FAIL);
}
}
private List<ErpFinanceReceiptItemDO> validateFinanceReceiptItems(
Long customerId,
List<ErpFinanceReceiptSaveReqVO.Item> list) {
return convertList(list, o -> BeanUtils.toBean(o, ErpFinanceReceiptItemDO.class, item -> {
if (ObjectUtil.equal(item.getBizType(), ErpBizTypeEnum.SALE_OUT.getType())) {
ErpSaleOutDO saleOut = saleOutService.validateSaleOut(item.getBizId());
Assert.equals(saleOut.getCustomerId(), customerId, "客户必须相同");
item.setTotalPrice(saleOut.getTotalPrice()).setBizNo(saleOut.getNo());
} else if (ObjectUtil.equal(item.getBizType(), ErpBizTypeEnum.SALE_RETURN.getType())) {
ErpSaleReturnDO saleReturn = saleReturnService.validateSaleReturn(item.getBizId());
Assert.equals(saleReturn.getCustomerId(), customerId, "客户必须相同");
item.setTotalPrice(saleReturn.getTotalPrice().negate()).setBizNo(saleReturn.getNo());
} else {
throw new IllegalArgumentException("业务类型不正确:" + item.getBizType());
}
}));
}
private void updateFinanceReceiptItemList(Long id, List<ErpFinanceReceiptItemDO> newList) {
// 第一步对比新老数据获得添加修改删除的列表
List<ErpFinanceReceiptItemDO> oldList = financeReceiptItemMapper.selectListByReceiptId(id);
List<List<ErpFinanceReceiptItemDO>> diffList = diffList(oldList, newList, // id 不同就认为是不同的记录
(oldVal, newVal) -> oldVal.getId().equals(newVal.getId()));
// 第二步批量添加修改删除
if (CollUtil.isNotEmpty(diffList.get(0))) {
diffList.get(0).forEach(o -> o.setReceiptId(id));
financeReceiptItemMapper.insertBatch(diffList.get(0));
}
if (CollUtil.isNotEmpty(diffList.get(1))) {
financeReceiptItemMapper.updateBatch(diffList.get(1));
}
if (CollUtil.isNotEmpty(diffList.get(2))) {
financeReceiptItemMapper.deleteBatchIds(convertList(diffList.get(2), ErpFinanceReceiptItemDO::getId));
}
// 第三步更新销售出库退货的收款金额情况
updateSalePrice(CollectionUtils.newArrayList(diffList));
}
private void updateSalePrice(List<ErpFinanceReceiptItemDO> receiptItems) {
receiptItems.forEach(receiptItem -> {
BigDecimal totalReceiptPrice = financeReceiptItemMapper.selectReceiptPriceSumByBizIdAndBizType(
receiptItem.getBizId(), receiptItem.getBizType());
if (ErpBizTypeEnum.SALE_OUT.getType().equals(receiptItem.getBizType())) {
saleOutService.updateSaleInReceiptPrice(receiptItem.getBizId(), totalReceiptPrice);
} else if (ErpBizTypeEnum.SALE_RETURN.getType().equals(receiptItem.getBizType())) {
saleReturnService.updateSaleReturnRefundPrice(receiptItem.getBizId(), totalReceiptPrice.negate());
} else {
throw new IllegalArgumentException("业务类型不正确:" + receiptItem.getBizType());
}
});
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteFinanceReceipt(List<Long> ids) {
// 1. 校验不处于已审批
List<ErpFinanceReceiptDO> receipts = financeReceiptMapper.selectBatchIds(ids);
if (CollUtil.isEmpty(receipts)) {
return;
}
receipts.forEach(receipt -> {
if (ErpAuditStatus.APPROVE.getStatus().equals(receipt.getStatus())) {
throw exception(FINANCE_RECEIPT_DELETE_FAIL_APPROVE, receipt.getNo());
}
});
// 2. 遍历删除并记录操作日志
receipts.forEach(receipt -> {
// 2.1 删除收款单
financeReceiptMapper.deleteById(receipt.getId());
// 2.2 删除收款单项
List<ErpFinanceReceiptItemDO> receiptItems = financeReceiptItemMapper.selectListByReceiptId(receipt.getId());
financeReceiptItemMapper.deleteBatchIds(convertSet(receiptItems, ErpFinanceReceiptItemDO::getId));
// 2.3 更新销售出库退货的收款金额情况
updateSalePrice(receiptItems);
});
}
private ErpFinanceReceiptDO validateFinanceReceiptExists(Long id) {
ErpFinanceReceiptDO receipt = financeReceiptMapper.selectById(id);
if (receipt == null) {
throw exception(FINANCE_RECEIPT_NOT_EXISTS);
}
return receipt;
}
@Override
public ErpFinanceReceiptDO getFinanceReceipt(Long id) {
return financeReceiptMapper.selectById(id);
}
@Override
public PageResult<ErpFinanceReceiptDO> getFinanceReceiptPage(ErpFinanceReceiptPageReqVO pageReqVO) {
return financeReceiptMapper.selectPage(pageReqVO);
}
// ==================== 收款单项 ====================
@Override
public List<ErpFinanceReceiptItemDO> getFinanceReceiptItemListByReceiptId(Long receiptId) {
return financeReceiptItemMapper.selectListByReceiptId(receiptId);
}
@Override
public List<ErpFinanceReceiptItemDO> getFinanceReceiptItemListByReceiptIds(Collection<Long> receiptIds) {
if (CollUtil.isEmpty(receiptIds)) {
return Collections.emptyList();
}
return financeReceiptItemMapper.selectListByReceiptIds(receiptIds);
}
}

View File

@ -194,7 +194,7 @@ public class ErpPurchaseInServiceImpl implements ErpPurchaseInService {
return;
}
if (paymentPrice.compareTo(purchaseIn.getTotalPrice()) > 0) {
throw exception(PURCHASE_IN_FAIL_PAYMENT_PRICE_EXCEED, purchaseIn.getTotalPrice(), paymentPrice);
throw exception(PURCHASE_IN_FAIL_PAYMENT_PRICE_EXCEED, paymentPrice, purchaseIn.getTotalPrice());
}
purchaseInMapper.updateById(new ErpPurchaseInDO().setId(id).setPaymentPrice(paymentPrice));
}

View File

@ -190,7 +190,7 @@ public class ErpPurchaseReturnServiceImpl implements ErpPurchaseReturnService {
return;
}
if (refundPrice.compareTo(purchaseReturn.getTotalPrice()) > 0) {
throw exception(PURCHASE_RETURN_FAIL_REFUND_PRICE_EXCEED, purchaseReturn.getTotalPrice(), refundPrice);
throw exception(PURCHASE_RETURN_FAIL_REFUND_PRICE_EXCEED, refundPrice, purchaseReturn.getTotalPrice());
}
purchaseReturnMapper.updateById(new ErpPurchaseReturnDO().setId(id).setRefundPrice(refundPrice));
}

View File

@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOutDO;
import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOutItemDO;
import jakarta.validation.Valid;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.List;
@ -40,6 +41,14 @@ public interface ErpSaleOutService {
*/
void updateSaleOutStatus(Long id, Integer status);
/**
* 更新销售出库的收款金额
*
* @param id 编号
* @param receiptPrice 收款金额
*/
void updateSaleInReceiptPrice(Long id, BigDecimal receiptPrice);
/**
* 删除销售出库
*
@ -55,6 +64,14 @@ public interface ErpSaleOutService {
*/
ErpSaleOutDO getSaleOut(Long id);
/**
* 校验销售出库已经审核通过
*
* @param id 编号
* @return 销售出库
*/
ErpSaleOutDO validateSaleOut(Long id);
/**
* 获得销售出库分页
*
@ -81,4 +98,5 @@ public interface ErpSaleOutService {
*/
List<ErpSaleOutItemDO> getSaleOutItemListByOutIds(Collection<Long> outIds);
}

View File

@ -148,10 +148,7 @@ public class ErpSaleOutServiceImpl implements ErpSaleOutService {
saleOut.setDiscountPercent(BigDecimal.ZERO);
}
saleOut.setDiscountPrice(MoneyUtils.priceMultiplyPercent(saleOut.getTotalPrice(), saleOut.getDiscountPercent()));
saleOut.setTotalPrice(saleOut.getTotalPrice().subtract(saleOut.getDiscountPrice()));
// 计算应收金额
BigDecimal allPrice = saleOut.getTotalPrice().add(saleOut.getOtherPrice());
saleOut.setDebtPrice(allPrice.subtract(saleOut.getPayPrice()));
saleOut.setTotalPrice(saleOut.getTotalPrice().subtract(saleOut.getDiscountPrice().add(saleOut.getOtherPrice())));
}
private void updateSaleOrderOutCount(Long orderId) {
@ -174,6 +171,10 @@ public class ErpSaleOutServiceImpl implements ErpSaleOutService {
if (saleOut.getStatus().equals(status)) {
throw exception(approve ? SALE_OUT_APPROVE_FAIL : SALE_OUT_PROCESS_FAIL);
}
// 1.3 校验已退款
if (approve && saleOut.getReceiptPrice().compareTo(BigDecimal.ZERO) > 0) {
throw exception(SALE_OUT_PROCESS_FAIL_EXISTS_RECEIPT);
}
// 2. 更新状态
int updateCount = saleOutMapper.updateByIdAndStatus(id, saleOut.getStatus(),
@ -194,6 +195,18 @@ public class ErpSaleOutServiceImpl implements ErpSaleOutService {
});
}
@Override
public void updateSaleInReceiptPrice(Long id, BigDecimal receiptPrice) {
ErpSaleOutDO saleOut = saleOutMapper.selectById(id);
if (saleOut.getReceiptPrice().equals(receiptPrice)) {
return;
}
if (receiptPrice.compareTo(saleOut.getTotalPrice()) > 0) {
throw exception(SALE_OUT_FAIL_RECEIPT_PRICE_EXCEED, receiptPrice, saleOut.getTotalPrice());
}
saleOutMapper.updateById(new ErpSaleOutDO().setId(id).setReceiptPrice(receiptPrice));
}
private List<ErpSaleOutItemDO> validateSaleOutItems(List<ErpSaleOutSaveReqVO.Item> list) {
// 1. 校验产品存在
List<ErpProductDO> productList = productService.validProductList(
@ -271,6 +284,15 @@ public class ErpSaleOutServiceImpl implements ErpSaleOutService {
return saleOutMapper.selectById(id);
}
@Override
public ErpSaleOutDO validateSaleOut(Long id) {
ErpSaleOutDO saleOut = validateSaleOutExists(id);
if (ObjectUtil.notEqual(saleOut.getStatus(), ErpAuditStatus.APPROVE.getStatus())) {
throw exception(SALE_OUT_NOT_APPROVE);
}
return saleOut;
}
@Override
public PageResult<ErpSaleOutDO> getSaleOutPage(ErpSaleOutPageReqVO pageReqVO) {
return saleOutMapper.selectPage(pageReqVO);

View File

@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleReturnDO;
import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleReturnItemDO;
import jakarta.validation.Valid;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.List;
@ -40,6 +41,14 @@ public interface ErpSaleReturnService {
*/
void updateSaleReturnStatus(Long id, Integer status);
/**
* 更新销售退货的退款金额
*
* @param id 编号
* @param refundPrice 退款金额
*/
void updateSaleReturnRefundPrice(Long id, BigDecimal refundPrice);
/**
* 删除销售退货
*
@ -55,6 +64,14 @@ public interface ErpSaleReturnService {
*/
ErpSaleReturnDO getSaleReturn(Long id);
/**
* 校验销售退货已经审核通过
*
* @param id 编号
* @return 销售退货
*/
ErpSaleReturnDO validateSaleReturn(Long id);
/**
* 获得销售退货分页
*

View File

@ -148,10 +148,7 @@ public class ErpSaleReturnServiceImpl implements ErpSaleReturnService {
saleReturn.setDiscountPercent(BigDecimal.ZERO);
}
saleReturn.setDiscountPrice(MoneyUtils.priceMultiplyPercent(saleReturn.getTotalPrice(), saleReturn.getDiscountPercent()));
saleReturn.setTotalPrice(saleReturn.getTotalPrice().subtract(saleReturn.getDiscountPrice()));
// 计算应退金额
BigDecimal allPrice = saleReturn.getTotalPrice().add(saleReturn.getOtherPrice());
saleReturn.setDebtPrice(allPrice.subtract(saleReturn.getRefundPrice()));
saleReturn.setTotalPrice(saleReturn.getTotalPrice().subtract(saleReturn.getDiscountPrice().add(saleReturn.getOtherPrice())));
}
private void updateSaleOrderReturnCount(Long orderId) {
@ -174,6 +171,10 @@ public class ErpSaleReturnServiceImpl implements ErpSaleReturnService {
if (saleReturn.getStatus().equals(status)) {
throw exception(approve ? SALE_RETURN_APPROVE_FAIL : SALE_RETURN_PROCESS_FAIL);
}
// 1.3 校验已退款
if (approve && saleReturn.getRefundPrice().compareTo(BigDecimal.ZERO) > 0) {
throw exception(SALE_RETURN_PROCESS_FAIL_EXISTS_REFUND);
}
// 2. 更新状态
int updateCount = saleReturnMapper.updateByIdAndStatus(id, saleReturn.getStatus(),
@ -194,6 +195,18 @@ public class ErpSaleReturnServiceImpl implements ErpSaleReturnService {
});
}
@Override
public void updateSaleReturnRefundPrice(Long id, BigDecimal refundPrice) {
ErpSaleReturnDO saleReturn = saleReturnMapper.selectById(id);
if (saleReturn.getRefundPrice().equals(refundPrice)) {
return;
}
if (refundPrice.compareTo(saleReturn.getTotalPrice()) > 0) {
throw exception(SALE_RETURN_FAIL_REFUND_PRICE_EXCEED, refundPrice, saleReturn.getTotalPrice());
}
saleReturnMapper.updateById(new ErpSaleReturnDO().setId(id).setRefundPrice(refundPrice));
}
private List<ErpSaleReturnItemDO> validateSaleReturnItems(List<ErpSaleReturnSaveReqVO.Item> list) {
// 1. 校验产品存在
List<ErpProductDO> productList = productService.validProductList(
@ -271,6 +284,15 @@ public class ErpSaleReturnServiceImpl implements ErpSaleReturnService {
return saleReturnMapper.selectById(id);
}
@Override
public ErpSaleReturnDO validateSaleReturn(Long id) {
ErpSaleReturnDO saleReturn = validateSaleReturnExists(id);
if (ObjectUtil.notEqual(saleReturn.getStatus(), ErpAuditStatus.APPROVE.getStatus())) {
throw exception(SALE_RETURN_NOT_APPROVE);
}
return saleReturn;
}
@Override
public PageResult<ErpSaleReturnDO> getSaleReturnPage(ErpSaleReturnPageReqVO pageReqVO) {
return saleReturnMapper.selectPage(pageReqVO);