2 天以前 684e4306a08feeae188070f264148e6765da8dcf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
package com.ruoyi.procurementrecord.service.impl;
 
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.procurementrecord.bean.dto.ProcurementRecordOutAdd;
import com.ruoyi.procurementrecord.bean.dto.ProcurementRecordOutPageDto;
import com.ruoyi.procurementrecord.bean.dto.ProcurementUpdateDto;
import com.ruoyi.procurementrecord.bean.vo.BatchNoOptionVo;
import com.ruoyi.procurementrecord.mapper.ProcurementRecordOutMapper;
import com.ruoyi.procurementrecord.pojo.ProcurementRecordOut;
import com.ruoyi.procurementrecord.service.ProcurementRecordOutService;
import com.ruoyi.project.system.domain.SysUser;
import com.ruoyi.project.system.mapper.SysUserMapper;
import com.ruoyi.sales.dto.ShippingInfoDto;
import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
import com.ruoyi.sales.pojo.SalesLedgerProduct;
import com.ruoyi.sales.pojo.ShippingProductDetail;
import com.ruoyi.sales.service.ShippingInfoService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
 
import jakarta.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
 
/**
 * @author :yys
 * @date : 2025/7/8 13:29
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class ProcurementRecordOutServiceImpl extends ServiceImpl<ProcurementRecordOutMapper, ProcurementRecordOut> implements ProcurementRecordOutService {
 
    /**
     * 出库类型:销售出库。油品销售出库即发货
     */
    private static final Integer TYPE_SALE_OUT = 2;
 
    /**
     * 销售产品明细类型:1销售(sales_ledger_product.type,与出库 type 语义不同)
     */
    private static final Integer LEDGER_PRODUCT_TYPE_SALE = 1;
 
    public final ProcurementRecordOutMapper procurementRecordOutMapper;
 
    public final SysUserMapper sysUserMapper;
 
    public final ShippingInfoService shippingInfoService;
 
    public final SalesLedgerProductMapper salesLedgerProductMapper;
 
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int stockout(ProcurementRecordOutAdd procurementRecordOutAdd) {
        SysUser sysUser = sysUserMapper.selectUserById(Long.valueOf(procurementRecordOutAdd.getUserId().toString()));
        if(sysUser == null){
            throw new RuntimeException("出库人不存在");
        }
        // 查询时间范围为当天数量
        LocalDate now = LocalDate.now();
        DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyyMMdd");
        LambdaQueryWrapper<ProcurementRecordOut> procurementRecordOutLambdaQueryWrapper = new LambdaQueryWrapper<>();
        procurementRecordOutLambdaQueryWrapper.ge(ProcurementRecordOut::getCreateTime, now)  // 大于等于当天
                .lt(ProcurementRecordOut::getCreateTime, now.plusDays(1)); // 小于明天
        Long aLong1 = procurementRecordOutMapper.selectCount(procurementRecordOutLambdaQueryWrapper);
        // 2. 定义日期格式(必须与字符串格式完全匹配)
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        LocalDateTime localDateTime = null;
        // 3. 转换:String → LocalDate → LocalDateTime(补充0点时间)
        if(StringUtils.isNotEmpty(procurementRecordOutAdd.getTime())){
            LocalDate localDate = LocalDate.parse(procurementRecordOutAdd.getTime(), formatter);
            // 获取当前时分秒
            LocalTime localTime = LocalTime.now();
            localDateTime = localDate.atTime(localTime);}
        // 查询采购出库数量
        LambdaQueryWrapper<ProcurementRecordOut> procurementRecordLambdaQueryWrapper = new LambdaQueryWrapper<>();
        procurementRecordLambdaQueryWrapper.eq(ProcurementRecordOut::getProcurementRecordStorageId, procurementRecordOutAdd.getId());
        Long aLong = procurementRecordOutMapper.selectCount(procurementRecordLambdaQueryWrapper);
        ProcurementRecordOut.ProcurementRecordOutBuilder procurementRecordOut = ProcurementRecordOut.builder()
                .procurementRecordStorageId(procurementRecordOutAdd.getId())
                .code("SC" + dateFormat.format(now) + String.format("%03d", aLong1 + 1))
                .salesLedgerProductId(procurementRecordOutAdd.getSalesLedgerProductId())
                .inboundBatches(aLong.equals(0L) ? "第1批次" : "第"+ (aLong + 1) + "批次")
                .inboundNum(new BigDecimal(procurementRecordOutAdd.getQuantity()))
                .type(procurementRecordOutAdd.getType())
                .createTime(localDateTime == null ? LocalDateTime.now() : localDateTime)
                .createUser(Long.valueOf(procurementRecordOutAdd.getUserId()))
                .createBy(sysUser.getNickName())
                .updateUser(Long.valueOf(procurementRecordOutAdd.getUserId()))
                .updateTime(LocalDateTime.now())
                .productModelId(procurementRecordOutAdd.getProductModelId())
                .batchNo(procurementRecordOutAdd.getBatchNo());
        ProcurementRecordOut record = procurementRecordOut.build();
        this.save(record);
 
        // 油品销售出库即发货:自动生成一条已发货的发货台账并回写关联
        if (TYPE_SALE_OUT.equals(record.getType())) {
            record.setShippingInfoId(createShippingForOutbound(record));
            this.updateById(record);
        }
        return 0;
    }
 
    /**
     * 按出库记录生成发货台账:批次必填,库存由发货侧扣减
     */
    private Long createShippingForOutbound(ProcurementRecordOut record) {
        if (StringUtils.isBlank(record.getBatchNo())) {
            throw new RuntimeException("请选择出库批次");
        }
        SalesLedgerProduct salesLedgerProduct = salesLedgerProductMapper.selectById(record.getSalesLedgerProductId());
        if (salesLedgerProduct == null || !LEDGER_PRODUCT_TYPE_SALE.equals(salesLedgerProduct.getType())) {
            throw new RuntimeException("销售出库未关联到有效的销售产品明细");
        }
        ShippingProductDetail detail = new ShippingProductDetail();
        detail.setProductModelId(record.getProductModelId());
        detail.setQuantity(record.getInboundNum());
        detail.setBatchNo(record.getBatchNo());
 
        ShippingInfoDto shippingInfoDto = new ShippingInfoDto();
        shippingInfoDto.setSalesLedgerId(salesLedgerProduct.getSalesLedgerId());
        shippingInfoDto.setSalesLedgerProductId(record.getSalesLedgerProductId());
        shippingInfoDto.setBatchNoDetailList(Collections.singletonList(detail));
        return shippingInfoService.addShippedFromOutbound(shippingInfoDto);
    }
 
    @Override
    public IPage<ProcurementRecordOutPageDto> listPage(Page page, ProcurementRecordOutPageDto procurementDto) {
        return procurementRecordOutMapper.listPage(page, procurementDto);
    }
 
    public List<ProcurementRecordOut> getProcurementRecordOutByIds(List<Integer> id) {
        return procurementRecordOutMapper.selectBatchIds(id);
    }
 
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deletePro(ProcurementUpdateDto procurementDto) {
        List<ProcurementRecordOut> procurementRecordOutByIds = getProcurementRecordOutByIds(procurementDto.getIds());
        if(CollectionUtils.isEmpty(procurementRecordOutByIds)){
            throw new RuntimeException("未查询到选中的人");
        }
        // 销售出库已生成发货单,删除出库单前先冲销发货单并归还已扣库存
        for (ProcurementRecordOut record : procurementRecordOutByIds) {
            if (record.getShippingInfoId() != null) {
                shippingInfoService.cancelByOutbound(record.getShippingInfoId());
            }
        }
        procurementRecordOutMapper.deleteBatchIds(procurementRecordOutByIds.stream().map(ProcurementRecordOut::getId).collect(Collectors.toList()));
        return 0;
    }
 
    @Override
    public void export(HttpServletResponse response) {
        List<ProcurementRecordOutPageDto> list = procurementRecordOutMapper.list();
        ExcelUtil<ProcurementRecordOutPageDto> util = new ExcelUtil<>(ProcurementRecordOutPageDto.class);
        util.exportExcel(response, list, "出库台账");
    }
 
    @Override
    public IPage<ProcurementRecordOutPageDto> listPageByProduct(Page page, ProcurementRecordOutPageDto procurementDto) {
        return procurementRecordOutMapper.listPageByProduct(page, procurementDto);
    }
 
    @Override
    public IPage<ProcurementRecordOutPageDto> listPageByCustom(Page page, ProcurementRecordOutPageDto procurementDto) {
        return procurementRecordOutMapper.listPageByCustom(page, procurementDto);
    }
 
    @Override
    public IPage<ProcurementRecordOutPageDto> listPageBySemiProduct(Page page, ProcurementRecordOutPageDto procurementDto) {
        return procurementRecordOutMapper.listPageBySemiProduct(page, procurementDto);
    }
 
    @Override
    public List<BatchNoOptionVo> listBatchNoByProductModelId(Long productModelId) {
        if (productModelId == null) {
            return Collections.emptyList();
        }
        return procurementRecordOutMapper.listBatchNoByProductModelId(productModelId);
    }
}