liding
4 天以前 c16a5f590db461391a4441e520f3264526c11905
src/main/java/com/ruoyi/sales/controller/SalesLedgerController.java
@@ -1,9 +1,8 @@
package com.ruoyi.sales.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.basic.pojo.Customer;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
@@ -11,12 +10,10 @@
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.framework.web.page.TableDataInfo;
import com.ruoyi.sales.dto.InvoiceLedgerDto;
import com.ruoyi.sales.dto.SalesLedgerDto;
import com.ruoyi.sales.dto.*;
import com.ruoyi.sales.mapper.InvoiceLedgerMapper;
import com.ruoyi.sales.mapper.ReceiptPaymentMapper;
import com.ruoyi.sales.pojo.ReceiptPayment;
import com.ruoyi.sales.pojo.SalesLedger;
import com.ruoyi.sales.pojo.SalesLedgerProcessRoute;
import com.ruoyi.sales.service.ICommonFileService;
import com.ruoyi.sales.service.ISalesLedgerService;
import io.swagger.annotations.Api;
@@ -26,9 +23,11 @@
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -59,9 +58,6 @@
    @Autowired
    private InvoiceLedgerMapper invoiceLedgerMapper;
    @Autowired
    private ReceiptPaymentMapper receiptPaymentMapper;
    /**
     * 导入销售台账
     */
@@ -71,7 +67,8 @@
    public AjaxResult importData(@RequestParam("file")
                                 @ApiParam(value = "Excel文件", required = true)
                                 MultipartFile file) {
        return salesLedgerService.importData(file);
        salesLedgerService.importData(file);
        return AjaxResult.success();
    }
    @ApiOperation("导出销售台账模板")
@@ -116,16 +113,16 @@
     * 查询销售台账列表
     */
    @GetMapping("/list")
    public TableDataInfo list(Page page, SalesLedgerDto salesLedgerDto) {
    public TableDataInfo list(Page<?> page, SalesLedgerDto salesLedgerDto) {
        startPage();
        List<SalesLedger> list = salesLedgerService.selectSalesLedgerList(salesLedgerDto);
        // 计算已开票金额/未开票金额(已填写发票金额为准)
        if(CollectionUtils.isEmpty(list)){
        if (CollectionUtils.isEmpty(list)) {
            return getDataTable(list);
        }
        List<Long> salesLedgerIds = list.stream().map(SalesLedger::getId).collect(Collectors.toList());
        List<InvoiceLedgerDto> invoiceLedgerDtoList = invoiceLedgerMapper.invoicedTotal(salesLedgerIds);
        if(CollectionUtils.isEmpty(invoiceLedgerDtoList)){
        if (CollectionUtils.isEmpty(invoiceLedgerDtoList)) {
            return getDataTable(list);
        }
        for (SalesLedger salesLedger : list) {
@@ -154,10 +151,10 @@
    @Log(title = "销售台账", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(HttpServletResponse response, SalesLedgerDto salesLedgerDto) {
        Page page = new Page(-1,-1);
        Page<?> page = new Page<>(-1, -1);
        IPage<SalesLedger> salesLedgerIPage = listPage(page, salesLedgerDto);
        ExcelUtil<SalesLedger> util = new ExcelUtil<SalesLedger>(SalesLedger.class);
        if(salesLedgerIPage == null){
        if (salesLedgerIPage == null) {
            util.exportExcel(response, new ArrayList<>(), "销售台账数据");
            return;
        }
@@ -166,12 +163,36 @@
    }
    /**
     * 导出销售台账列表(包含产品明细,两个sheet页)
     */
    @Log(title = "销售台账", businessType = BusinessType.EXPORT)
    @PostMapping("/exportWithProducts")
    @ApiOperation("导出销售台账及产品明细(两个sheet页)")
    public void exportWithProducts(HttpServletResponse response, SalesLedgerDto salesLedgerDto) {
        salesLedgerService.exportWithProducts(response, salesLedgerDto);
    }
    /**
     * 导出售后台账工艺路线
     */
    @Log(title = "销售台账工艺路线", businessType = BusinessType.EXPORT)
    @PostMapping("/exportProcessRoute")
    @ApiOperation("导出售后台账工艺路线")
    public void exportProcessRoute(HttpServletResponse response,
                                   HttpServletRequest request,
                                   @RequestParam(value = "completedTimeStart", required = false) String completedTimeStart,
                                   @RequestParam(value = "completedTimeEnd", required = false) String completedTimeEnd) {
        List<Long> salesLedgerIds = parseSalesLedgerIds(request);
        salesLedgerService.exportProcessRoute(response, salesLedgerIds, completedTimeStart, completedTimeEnd);
    }
    /**
     * 导出开票登记列表
     */
    @Log(title = "导出开票登记列表", businessType = BusinessType.EXPORT)
    @PostMapping("/exportOne")
    public void exportOne(HttpServletResponse response, SalesLedgerDto salesLedgerDto) {
        Page page = new Page();
        Page<?> page = new Page<>();
        page.setCurrent(-1);
        page.setSize(-1);
        IPage<SalesLedger> salesLedgerIPage = listPage(page, salesLedgerDto);
@@ -186,6 +207,67 @@
    @PostMapping("/addOrUpdateSalesLedger")
    public AjaxResult add(@RequestBody SalesLedgerDto salesLedgerDto) {
        return toAjax(salesLedgerService.addOrUpdateSalesLedger(salesLedgerDto));
    }
    /**
     * 销售订单绑定工艺路线
     */
    @PostMapping("/saleProcessBind")
    @ApiOperation("销售订单绑定工艺路线")
    public AjaxResult saleProcessBind(@RequestBody SalesLedgerProcessRouteDto salesLedgerProcessRouteDto) {
        salesLedgerService.saleProcessBind(salesLedgerProcessRouteDto);
        return AjaxResult.success();
    }
    /**
     * 反审核操作
     */
    @Log(title = "销售台账反审核", businessType = BusinessType.UPDATE)
    @PostMapping("/counterReview")
    @ApiOperation("反审核操作:作废或重新生成")
    public AjaxResult counterReview(@RequestBody CounterReviewDto dto) {
        List<Long> newLedgerIds = salesLedgerService.counterReview(dto);
        AjaxResult result = AjaxResult.success("反审核成功");
        if (newLedgerIds != null && !newLedgerIds.isEmpty()) {
            result.put("newLedgerIds", newLedgerIds);
        }
        return result;
    }
    /**
     * 标记订单完成
     */
    @Log(title = "销售台账标记完成", businessType = BusinessType.UPDATE)
    @PostMapping("/markOrderCompleted")
    @ApiOperation("标记订单完成(不可撤销)")
    public AjaxResult markOrderCompleted(@RequestBody Map<String, Object> params) {
        @SuppressWarnings("unchecked")
        List<Long> ids = ((List<Object>) params.get("ids")).stream()
            .map(obj -> Long.valueOf(obj.toString()))
            .collect(Collectors.toList());
        if (ids == null || ids.isEmpty()) {
            return AjaxResult.error("请选择要标记完成的订单");
        }
        salesLedgerService.markOrderCompleted(ids);
        return AjaxResult.success("标记完成成功");
    }
    /**
     * 递增打印次数
     */
    @PostMapping("/incrementPrintCount")
    @ApiOperation("递增打印次数")
    public AjaxResult incrementPrintCount(@RequestBody Map<String, Object> params) {
        Long id = params.get("id") != null ? Long.valueOf(params.get("id").toString()) : null;
        String printType = (String) params.get("printType");
        if (id == null) {
            return AjaxResult.error("销售台账ID不能为空");
        }
        if (printType == null || (!"label".equals(printType) && !"document".equals(printType))) {
            return AjaxResult.error("打印类型必须为 label 或 document");
        }
        salesLedgerService.incrementPrintCount(id, printType);
        return AjaxResult.success();
    }
    /**
@@ -249,7 +331,7 @@
     * 近半年开票,回款金额
     */
    @GetMapping("/getAmountHalfYear")
    public AjaxResult getAmountHalfYear(@RequestParam(value = "type",defaultValue = "1") Integer type) {
    public AjaxResult getAmountHalfYear(@RequestParam(value = "type", defaultValue = "1") Integer type) {
        return AjaxResult.success(salesLedgerService.getAmountHalfYear(type));
    }
@@ -257,91 +339,8 @@
     * 查询销售台账列表
     */
    @GetMapping("/listPage")
    public IPage<SalesLedger> listPage(Page page, SalesLedgerDto salesLedgerDto) {
        IPage<SalesLedger> iPage = salesLedgerService.selectSalesLedgerListPage(page, salesLedgerDto);
        //  查询结果为空,直接返回
        if (CollectionUtils.isEmpty(iPage.getRecords())) {
            return iPage;
        }
        //  获取当前页所有台账记录的 ID 集合
        List<Long> salesLedgerIds = iPage.getRecords().stream().map(SalesLedger::getId).collect(Collectors.toList());
        //  查询发票信息的已开票金额
        List<InvoiceLedgerDto> invoiceLedgerDtoList = invoiceLedgerMapper.invoicedTotal(salesLedgerIds);
        if (CollectionUtils.isEmpty(invoiceLedgerDtoList)) {
            invoiceLedgerDtoList = Collections.emptyList();
        }
        //  转换发票数据, key 为台账ID, value 为该台账的总开票金额
        Map<Long, BigDecimal> invoiceTotals = invoiceLedgerDtoList.stream()
                .filter(dto -> dto.getSalesLedgerId() != null && dto.getInvoiceTotal() != null)
                .collect(Collectors.toMap(
                        dto -> dto.getSalesLedgerId().longValue(),
                        InvoiceLedgerDto::getInvoiceTotal,
                        BigDecimal::add // 存在重复ID执行累加
                ));
        //  查询回款/付款记录
        List<ReceiptPayment> receiptPayments = Collections.emptyList();
        if (!CollectionUtils.isEmpty(salesLedgerIds)) {
            receiptPayments = receiptPaymentMapper.selectList(new LambdaQueryWrapper<ReceiptPayment>()
                    .in(ReceiptPayment::getSalesLedgerId, salesLedgerIds));
        }
        //  转换回款数据, key 为台账ID, value 为该台账的总回款金额
        Map<Long, BigDecimal> receiptTotals = new HashMap<>();
        if (!CollectionUtils.isEmpty(receiptPayments)) {
            for (ReceiptPayment receiptPayment : receiptPayments) {
                if (receiptPayment.getSalesLedgerId() != null && receiptPayment.getReceiptPaymentAmount() != null) {
                    //  如果 key 存在则相加,不存在则放入
                    receiptTotals.merge(receiptPayment.getSalesLedgerId(), receiptPayment.getReceiptPaymentAmount(), BigDecimal::add);
                }
            }
        }
        for (SalesLedger salesLedger : iPage.getRecords()) {
            Long ledgerId = salesLedger.getId();
            // 合同总金额
            BigDecimal contractAmount = salesLedger.getContractAmount() == null ? BigDecimal.ZERO : salesLedger.getContractAmount();
            // 开票总额和回款总额
            BigDecimal invoiceTotal = invoiceTotals.getOrDefault(ledgerId, BigDecimal.ZERO);
            BigDecimal receiptPaymentAmountTotal = receiptTotals.getOrDefault(ledgerId, BigDecimal.ZERO);
            //  未开票金额 = 合同金额 - 已开票金额
            BigDecimal noInvoiceAmountTotal = contractAmount.subtract(invoiceTotal);
            if (noInvoiceAmountTotal.compareTo(BigDecimal.ZERO) < 0) {
                noInvoiceAmountTotal = BigDecimal.ZERO;
            }
            //  待回款金额 = 已开票金额 - 已回款金额
            BigDecimal noReceiptPaymentAmountTotal = invoiceTotal.subtract(receiptPaymentAmountTotal);
            if (noReceiptPaymentAmountTotal.compareTo(BigDecimal.ZERO) < 0) {
                noReceiptPaymentAmountTotal = BigDecimal.ZERO;
            }
            salesLedger.setNoInvoiceAmountTotal(noInvoiceAmountTotal);
            salesLedger.setInvoiceTotal(invoiceTotal);
            salesLedger.setReceiptPaymentAmountTotal(receiptPaymentAmountTotal);
            salesLedger.setNoReceiptAmount(noReceiptPaymentAmountTotal);
            //  如果已经有过开票或回款操作,则不允许编辑
            boolean hasInvoiceOperation = invoiceTotal.compareTo(BigDecimal.ZERO) > 0;
            boolean hasReceiptOperation = receiptPaymentAmountTotal.compareTo(BigDecimal.ZERO) > 0;
            salesLedger.setIsEdit(!(hasInvoiceOperation || hasReceiptOperation));
        }
        if (ObjectUtils.isNotEmpty(salesLedgerDto.getStatus())) {
            if (salesLedgerDto.getStatus()) {
                // 清除所有“未开票金额”为 0 的记录
                iPage.getRecords().removeIf(salesLedger ->
                        Objects.equals(salesLedger.getNoInvoiceAmountTotal(), new BigDecimal("0.00")));
                iPage.setTotal(iPage.getRecords().size());
            }
        }
        return iPage;
    public IPage<SalesLedger> listPage(Page<?> page, SalesLedgerDto salesLedgerDto) {
        return salesLedgerService.selectSalesLedgerListPage(page, salesLedgerDto);
    }
    @ApiOperation("查询销售台账消耗物料信息")
@@ -349,4 +348,166 @@
    public R getSalesLedgerWithProductsLoss(Long salesLedgerId) {
        return R.ok(salesLedgerService.getSalesLedgerWithProductsLoss(salesLedgerId));
    }
    @ApiOperation("获取销售订单绑定的工艺路线")
    @GetMapping("/salesProcess/{salesLedgerId}")
    public AjaxResult salesProcess(@PathVariable Long salesLedgerId) {
        SalesLedgerProcessRouteDto dto = salesLedgerService.salesProcess(salesLedgerId);
        return AjaxResult.success(dto);
    }
    @GetMapping("/processCard/{salesLedgerId}")
    @ApiOperation("打印生产流程卡")
    public AjaxResult processCard(@PathVariable Long salesLedgerId) {
        SalesProcessCardDto dto = salesLedgerService.processCard(salesLedgerId);
        return AjaxResult.success(dto);
    }
    @GetMapping("/salesOrders/{salesLedgerId}")
    @ApiOperation("打印销售订单")
    public AjaxResult salesOrders(@PathVariable Long salesLedgerId) {
        SalesOrdersDto salesOrdersDto = salesLedgerService.salesOrders(salesLedgerId);
        return AjaxResult.success(salesOrdersDto);
    }
    @PostMapping("/salesInvoices")
    @ApiOperation("打印销售发货单")
    public AjaxResult salesInvoices(@RequestBody List<Long> salesLedgerIds) {
        SalesInvoicesDto dto = salesLedgerService.salesInvoices(salesLedgerIds);
        return AjaxResult.success(dto);
    }
    @GetMapping("/salesLabel/{salesLedgerId}")
    @ApiOperation("打印订单标签")
    public AjaxResult salesLabel(@PathVariable Long salesLedgerId) {
        List<SalesLabelDto> list = salesLedgerService.salesLabel(salesLedgerId);
        return AjaxResult.success(list);
    }
    @PostMapping("/salesStock")
    @ApiOperation("销售台账产品入库")
    public AjaxResult salesStock(@RequestBody SalesProductStockDto dto) {
        salesLedgerService.salesStock(dto);
        return AjaxResult.success();
    }
    @GetMapping("/shippedCustomers")
    @ApiOperation("已发货客户名单")
    public AjaxResult shippedCustomers() {
        List<Customer> list = salesLedgerService.shippedCustomers();
        return AjaxResult.success(list);
    }
    @PostMapping("/scanInbound")
    @ApiOperation("销售订单扫码-合格入库")
    public AjaxResult scanInbound(@RequestBody SalesScanInboundDto dto) {
        salesLedgerService.scanInbound(dto);
        return AjaxResult.success();
    }
    @PostMapping("/scanInboundUnqualified")
    @ApiOperation("销售订单扫码-不合格入库")
    public AjaxResult scanInboundUnqualified(@RequestBody SalesScanInboundDto dto) {
        salesLedgerService.scanInboundUnqualified(dto);
        return AjaxResult.success();
    }
    @PostMapping("/scanOutbound")
    @ApiOperation("销售订单扫码-合格出库")
    public AjaxResult scanOutbound(@RequestBody SalesScanInboundDto dto) {
        salesLedgerService.scanOutbound(dto);
        return AjaxResult.success();
    }
    @PostMapping("/scanShipApply")
    @ApiOperation("销售订单扫码-发起发货审批(填写车牌/快递、审批人、附件;通过后自动扣库存并标记已发货)")
    public AjaxResult scanShipApply(@RequestBody SalesScanShipDto dto) {
        salesLedgerService.scanShipApply(dto);
        return AjaxResult.success("发货审批已发起");
    }
    @PostMapping("/scanOutboundUnqualified")
    @ApiOperation("销售订单扫码-不合格出库")
    public AjaxResult scanOutboundUnqualified(@RequestBody SalesScanInboundDto dto) {
        salesLedgerService.scanOutboundUnqualified(dto);
        return AjaxResult.success();
    }
    @PostMapping("/salesHistory/shippingImport")
    @ApiOperation("销售发货历史数据导入-已发货")
    public AjaxResult shippingImport(MultipartFile file) {
        salesLedgerService.shippingImport(file);
        return AjaxResult.success();
    }
    @PostMapping("/salesHistory/notShippingImport")
    @ApiOperation("销售发货历史数据导入-未发货")
    public AjaxResult notShippingImport(MultipartFile file) {
        salesLedgerService.notShippingImport(file);
        return AjaxResult.success();
    }
    @PostMapping("/salesHistory/shippingImportTemplate")
    @ApiOperation("销售发货历史数据导入-已发货导入模板下载")
    public void shippingImportTemplate(HttpServletResponse response) {
        ExcelUtil<SalesShippingImportDto> excelUtil = new ExcelUtil<>(SalesShippingImportDto.class);
        excelUtil.importTemplateExcel(response, "已出库导入模板下载");
    }
    @PostMapping("/salesHistory/notShippingImportTemplate")
    @ApiOperation("销售发货历史数据导入-未发货导入模板下载")
    public void notShippingImportTemplate(HttpServletResponse response) {
        ExcelUtil<SalesNotShippingImportDto> excelUtil = new ExcelUtil<>(SalesNotShippingImportDto.class);
        excelUtil.importTemplateExcel(response, "未出库导入模板下载");
    }
    private List<Long> parseSalesLedgerIds(HttpServletRequest request) {
        if (request == null) {
            return Collections.emptyList();
        }
        List<Long> ids = new ArrayList<>();
        Map<String, String[]> parameterMap = request.getParameterMap();
        if (parameterMap == null || parameterMap.isEmpty()) {
            return ids;
        }
        String directValue = request.getParameter("salesLedgerIds");
        if (StringUtils.hasText(directValue)) {
            for (String value : directValue.split(",")) {
                addParsedLong(ids, value);
            }
        }
        for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
            String key = entry.getKey();
            if (!StringUtils.hasText(key)) {
                continue;
            }
            if (!"salesLedgerIds".equals(key) && !key.startsWith("salesLedgerIds[")) {
                continue;
            }
            String[] values = entry.getValue();
            if (values == null) {
                continue;
            }
            for (String value : values) {
                addParsedLong(ids, value);
            }
        }
        return ids.stream().filter(Objects::nonNull).distinct().collect(Collectors.toList());
    }
    private void addParsedLong(List<Long> target, String value) {
        if (target == null || !StringUtils.hasText(value)) {
            return;
        }
        try {
            target.add(Long.valueOf(value.trim()));
        } catch (Exception ignored) {
            // ignore invalid id values
        }
    }
}