From 3fd61b2386021218909e0211b647cbd63f270f76 Mon Sep 17 00:00:00 2001
From: gongchunyi <deslre0381@gmail.com>
Date: 星期五, 27 三月 2026 15:53:48 +0800
Subject: [PATCH] feat: 销售发货单打印

---
 src/main/java/com/ruoyi/sales/controller/SalesLedgerController.java    |    8 +
 src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java |  126 +++++++++++++++++++++++++
 src/main/java/com/ruoyi/sales/service/ISalesLedgerService.java         |    6 +
 src/main/java/com/ruoyi/sales/dto/SalesInvoicesDto.java                |  102 ++++++++++++++++++++
 4 files changed, 241 insertions(+), 1 deletions(-)

diff --git a/src/main/java/com/ruoyi/sales/controller/SalesLedgerController.java b/src/main/java/com/ruoyi/sales/controller/SalesLedgerController.java
index c149b52..c961cff 100644
--- a/src/main/java/com/ruoyi/sales/controller/SalesLedgerController.java
+++ b/src/main/java/com/ruoyi/sales/controller/SalesLedgerController.java
@@ -380,4 +380,12 @@
         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);
+    }
+
 }
diff --git a/src/main/java/com/ruoyi/sales/dto/SalesInvoicesDto.java b/src/main/java/com/ruoyi/sales/dto/SalesInvoicesDto.java
new file mode 100644
index 0000000..5026f2d
--- /dev/null
+++ b/src/main/java/com/ruoyi/sales/dto/SalesInvoicesDto.java
@@ -0,0 +1,102 @@
+package com.ruoyi.sales.dto;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * <br>
+ * 鎵撳嵃閿�鍞彂璐у崟Dto
+ * </br>
+ *
+ * @author deslrey
+ * @version 1.0
+ * @since 2026/03/27 15:30
+ */
+@Data
+public class SalesInvoicesDto {
+
+    @ApiModelProperty("瀹㈡埛鍚嶇О")
+    private String customerName;
+
+    @ApiModelProperty("鑱旂郴浜�")
+    private String contactPerson;
+
+    @ApiModelProperty("鑱旂郴鐢佃瘽")
+    private String contactPhone;
+
+    @ApiModelProperty("閫佽揣鍦板潃")
+    private String companyAddress;
+
+    @ApiModelProperty("鍙戣揣鍗曞彿")
+    private String deliveryNo;
+
+    @ApiModelProperty("瀵规柟鍗曞彿")
+    private String externalOrderNo;
+
+    @ApiModelProperty("鎬诲悎璁℃暟閲�")
+    private BigDecimal totalQuantity;
+
+    @ApiModelProperty("鎬诲悎璁¢潰绉�")
+    private BigDecimal totalArea;
+
+    @ApiModelProperty("鍒跺崟鍛�")
+    private String orderMaker;
+
+    @ApiModelProperty("鍒跺崟鏃ユ湡")
+    @JsonFormat(pattern = "yyyy/MM/dd")
+    private LocalDateTime executionDate;
+
+    @ApiModelProperty("鍙戣揣璁㈠崟鍒嗙粍")
+    private List<InvoiceOrderGroupDto> groups;
+
+    /**
+     * 璁㈠崟鍒嗙粍 DTO
+     */
+    @Data
+    public static class InvoiceOrderGroupDto {
+        @ApiModelProperty("璁㈠崟缂栧彿")
+        private String salesContractNo;
+
+        @ApiModelProperty("浜у搧鍚嶇О")
+        private String productName;
+
+        @ApiModelProperty("鏄庣粏鍒楄〃")
+        private List<InvoiceItemDto> items;
+
+        @ApiModelProperty("灏忚鏁伴噺")
+        private BigDecimal groupTotalQuantity;
+
+        @ApiModelProperty("灏忚闈㈢Н")
+        private BigDecimal groupTotalArea;
+    }
+
+    /**
+     * 鍙戣揣鍗曟槑缁� DTO
+     */
+    @Data
+    public static class InvoiceItemDto {
+        @ApiModelProperty("妤煎眰缂栧彿")
+        private String floorCode;
+
+        @ApiModelProperty("瀹�*楂�")
+        private String widthHeight;
+
+        @ApiModelProperty("鏁伴噺")
+        private BigDecimal quantity;
+
+        @ApiModelProperty("闈㈢Н")
+        private BigDecimal area;
+
+        @ApiModelProperty("澶囨敞")
+        private String remark;
+
+        @ApiModelProperty("鍔犲伐瑕佹眰")
+        private String processRequirement;
+    }
+
+}
diff --git a/src/main/java/com/ruoyi/sales/service/ISalesLedgerService.java b/src/main/java/com/ruoyi/sales/service/ISalesLedgerService.java
index f96512a..23a675c 100644
--- a/src/main/java/com/ruoyi/sales/service/ISalesLedgerService.java
+++ b/src/main/java/com/ruoyi/sales/service/ISalesLedgerService.java
@@ -31,7 +31,8 @@
 
     int addOrUpdateSalesLedger(SalesLedgerDto salesLedgerDto);
 
-    List<SalesLedgerProduct> getSalesLedgerProductListByRelateId(@NotNull Long relateId,@NotNull SaleEnum type);
+    List<SalesLedgerProduct> getSalesLedgerProductListByRelateId(@NotNull Long relateId, @NotNull SaleEnum type);
+
     List<SalesLedgerProduct> getSalesLedgerProductListByIds(@Nullable List<Long> relateIds, @NotNull SaleEnum type);
 
     void handleSalesLedgerProducts(Long salesLedgerId, List<SalesLedgerProduct> products, SaleEnum type);
@@ -61,4 +62,7 @@
     SalesLedgerProcessRouteDto salesProcess(Long salesLedgerId);
 
     SalesOrdersDto salesOrders(Long salesLedgerId);
+
+    SalesInvoicesDto salesInvoices(List<Long> salesLedgerIds);
+
 }
diff --git a/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java b/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
index e5dd539..025fe60 100644
--- a/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
+++ b/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
@@ -1180,6 +1180,132 @@
         return dto;
     }
 
+    @Override
+    public SalesInvoicesDto salesInvoices(List<Long> salesLedgerIds) {
+        if (CollectionUtils.isEmpty(salesLedgerIds)) {
+            throw new ServiceException("閿�鍞彂璐у崟鎵撳嵃澶辫触,閿�鍞鍗曚笉鑳戒负绌�");
+        }
+
+        List<SalesLedger> ledgers = salesLedgerMapper.selectBatchIds(salesLedgerIds);
+        if (CollectionUtils.isEmpty(ledgers)) {
+            throw new ServiceException("閿�鍞彂璐у崟鎵撳嵃澶辫触,鏈壘鍒板搴斿彴璐﹁褰�");
+        }
+
+        Long customerId = ledgers.get(0).getCustomerId();
+        for (SalesLedger ledger : ledgers) {
+            if (!Objects.equals(customerId, ledger.getCustomerId())) {
+                throw new ServiceException("閿�鍞彂璐у崟鍚堝苟鎵撳嵃鍙兘鏄悓涓�涓鎴�");
+            }
+        }
+
+        SalesInvoicesDto dto = new SalesInvoicesDto();
+
+        Customer customer = customerMapper.selectById(customerId);
+        if (customer != null) {
+            dto.setCustomerName(customer.getCustomerName());
+            dto.setContactPerson(customer.getContactPerson());
+            dto.setContactPhone(customer.getContactPhone());
+
+            StringBuilder address = new StringBuilder();
+            if (customer.getRegionsId() != null) {
+                CustomerRegions regions = customerRegionsService.getById(customer.getRegionsId());
+                if (regions != null) {
+                    address.append(regions.getRegionsName());
+                }
+            }
+            if (StringUtils.isNotEmpty(customer.getCompanyAddress())) {
+                address.append(customer.getCompanyAddress());
+            }
+            dto.setCompanyAddress(address.toString());
+        }
+
+        //  鍙戣揣鍗曞彿 (XF + 鏃ユ湡 + 搴忓垪)
+        String dateStr = LocalDate.now().format(DateTimeFormatter.ofPattern("yyMMdd"));
+        String redisKey = "sales:delivery:seq:" + dateStr;
+        Long seq = redisTemplate.opsForValue().increment(redisKey);
+        if (seq != null && seq == 1) {
+            redisTemplate.expire(redisKey, 48, TimeUnit.HOURS);
+        }
+        dto.setDeliveryNo("XF" + dateStr + String.format("%03d", seq != null ? seq : 1));
+
+        // 瀵规柟鍗曞彿
+//        dto.setExternalOrderNo(ledgers.get(0).getCustomerContractNo());
+
+        //  鏌ヨ鎵�鏈変骇鍝�
+        List<SalesLedgerProduct> allProducts = salesLedgerProductMapper.selectList(
+                new LambdaQueryWrapper<SalesLedgerProduct>().in(SalesLedgerProduct::getSalesLedgerId, salesLedgerIds));
+
+        if (CollectionUtils.isNotEmpty(allProducts)) {
+            Map<Long, SalesLedger> ledgerMap = ledgers.stream()
+                    .collect(Collectors.toMap(SalesLedger::getId, Function.identity()));
+
+            Map<Long, List<SalesLedgerProduct>> groupedData = new LinkedHashMap<>();
+            for (SalesLedgerProduct p : allProducts) {
+                groupedData.computeIfAbsent(p.getSalesLedgerId(), k -> new ArrayList<>()).add(p);
+            }
+
+            List<SalesInvoicesDto.InvoiceOrderGroupDto> groups = new ArrayList<>();
+            BigDecimal totalQty = BigDecimal.ZERO;
+            BigDecimal totalArea = BigDecimal.ZERO;
+
+            for (Map.Entry<Long, List<SalesLedgerProduct>> ledgerEntry : groupedData.entrySet()) {
+                SalesLedger ledger = ledgerMap.get(ledgerEntry.getKey());
+                String orderNo = ledger != null ? ledger.getSalesContractNo() : "";
+                List<SalesLedgerProduct> products = ledgerEntry.getValue();
+
+                SalesInvoicesDto.InvoiceOrderGroupDto group = new SalesInvoicesDto.InvoiceOrderGroupDto();
+                group.setSalesContractNo(orderNo);
+                if (CollectionUtils.isNotEmpty(products)) {
+                    group.setProductName(products.get(0).getProductCategory());
+                }
+
+                List<SalesInvoicesDto.InvoiceItemDto> itemDtos = new ArrayList<>();
+                BigDecimal groupQty = BigDecimal.ZERO;
+                BigDecimal groupArea = BigDecimal.ZERO;
+
+                for (SalesLedgerProduct p : products) {
+                    SalesInvoicesDto.InvoiceItemDto item = new SalesInvoicesDto.InvoiceItemDto();
+                    item.setFloorCode(p.getFloorCode());
+                    item.setWidthHeight((p.getWidth() != null ? p.getWidth().stripTrailingZeros().toPlainString() : "0") +
+                            " * " + (p.getHeight() != null ? p.getHeight().stripTrailingZeros().toPlainString() : "0"));
+                    item.setQuantity(p.getQuantity());
+
+                    // 闈㈢Н
+                    BigDecimal area = p.getSettleTotalArea() != null ? p.getSettleTotalArea() : p.getActualTotalArea();
+                    if (area == null && p.getWidth() != null && p.getHeight() != null && p.getQuantity() != null) {
+                        area = p.getWidth().multiply(p.getHeight()).multiply(p.getQuantity()).divide(new BigDecimal(1000000), 2, RoundingMode.HALF_UP);
+                    }
+                    item.setArea(area);
+                    item.setRemark(p.getRemark());
+                    item.setProcessRequirement(p.getProcessRequirement());
+
+                    itemDtos.add(item);
+                    groupQty = groupQty.add(p.getQuantity() != null ? p.getQuantity() : BigDecimal.ZERO);
+                    groupArea = groupArea.add(area != null ? area : BigDecimal.ZERO);
+                }
+
+                group.setItems(itemDtos);
+                group.setGroupTotalQuantity(groupQty);
+                group.setGroupTotalArea(groupArea.setScale(2, RoundingMode.HALF_UP));
+                groups.add(group);
+
+                totalQty = totalQty.add(groupQty);
+                totalArea = totalArea.add(groupArea);
+            }
+            dto.setGroups(groups);
+            dto.setTotalQuantity(totalQty);
+            dto.setTotalArea(totalArea.setScale(2, RoundingMode.HALF_UP));
+        }
+
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+        if (loginUser != null && loginUser.getUser() != null) {
+            dto.setOrderMaker(loginUser.getUser().getNickName());
+        }
+        dto.setExecutionDate(LocalDateTime.now());
+
+        return dto;
+    }
+
     private int findFirstMissingSequence(List<Integer> sequences) {
         if (sequences.isEmpty()) {
             return 1;

--
Gitblit v1.9.3