| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.sales; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.ruoyi.RuoYiApplication; |
| | | import com.ruoyi.common.enums.ApproveTypeEnum; |
| | | import com.ruoyi.common.enums.StockOutQualifiedRecordTypeEnum; |
| | | import com.ruoyi.sales.mapper.SalesLedgerMapper; |
| | | import com.ruoyi.sales.mapper.SalesLedgerProductMapper; |
| | | import com.ruoyi.sales.mapper.ShippingInfoMapper; |
| | | import com.ruoyi.sales.pojo.SalesLedger; |
| | | import com.ruoyi.sales.pojo.SalesLedgerProduct; |
| | | import com.ruoyi.sales.pojo.ShippingInfo; |
| | | import org.apache.commons.collections4.CollectionUtils; |
| | | import org.junit.jupiter.api.Test; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.boot.test.context.SpringBootTest; |
| | | import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; |
| | | import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; |
| | | import org.springframework.test.annotation.Rollback; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import javax.sql.DataSource; |
| | | import java.math.BigDecimal; |
| | | import java.sql.Timestamp; |
| | | 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.Objects; |
| | | import java.util.stream.Collectors; |
| | | |
| | | import static org.junit.jupiter.api.Assertions.assertTrue; |
| | | |
| | | /** |
| | | * åè´§å°è´¦æ¹éåºåºä¸æ¥æå¯¹é½ï¼æµè¯/æ°æ®ä¿®å¤ç¨ï¼ã |
| | | * <p> |
| | | * è¡¨ç»æä»¥ {@code product-inventory-management-hbtmblc.sql} 为åï¼ä¾å¦ï¼ |
| | | * {@code shipping_info.shipping_date} 为 {@code datetime}ï¼ |
| | | * {@code approve_process.approve_time}/{@code approve_over_time} 为 {@code datetime}ï¼ |
| | | * {@code approve_process.start_date}/{@code end_date} 为 {@code date}ï¼ |
| | | * åè´§å®¡æ¹æ ·æ¬äºç±ä¸º {@code å货审æ¹:} + éå®ååå·ï¼å¦ {@code å货审æ¹:D20260413001}ï¼è§èæ¬çº¦ L1988ï¼ã |
| | | * {@code stock_out_record.record_type='13'}ã{@code record_id} å¯¹åº {@code shipping_info.id}ï¼è§èæ¬çº¦ L28306 èµ·ï¼ã |
| | | * <p>æ¬åº {@code product-inventory-management-hbtmblc.sql} ä¸ {@code sys_user} æ {@code dept_id} åæ®µï¼é¨é¨éè¿ |
| | | * {@code sys_user_dept} å
³è {@code sys_dept}ï¼è¡¥å
¨å®¡æ¹æ¶ææ¤ç»æè§£æç³è¯·äººé¨é¨ã |
| | | * <p><b>åºåºå°è´¦ä¸ºä½æä¸å°ååå·</b>ï¼å表 SQL ç¨ {@code sales_ledger.sales_contract_no} è¿æ»¤æ¶ï¼ä¾èµ |
| | | * {@code stock_out_record.sales_ledger_id}ã{@code sales_ledger_product_id} å
³èéå®å°è´¦ï¼è§ {@code StockOutRecordMapper.xml}ï¼ã |
| | | * è¥æªæå
¥è®°å½ææå
¥æ¶æªå¸¦è¿ä¸¤åï¼ååæ ¼åºåºåè¡¨ä¸ºç©ºãæ¬ç±»<b>ä¸ä¿®æ¹åºå表</b>ï¼ä»
å {@code stock_out_record} <b>INSERT</b> 䏿¡ãéå®-åè´§åºåºãè®°å½å¹¶åå
¥ä¸è¿°å¤é®ã |
| | | * <p> |
| | | * é»è¾æ¦è¦ï¼ |
| | | * <ul> |
| | | * <li>éå {@code shipping_info}ï¼è¥ {@code sales_ledger.delivery_date} 为空åè·³è¿ã</li> |
| | | * <li>è¥è¯¥è¡å·²åå¨ {@code record_type = 13} ä¸ {@code record_id = shipping_info.id} çåºåºè®°å½ï¼ä¸åæå
¥ï¼ä»
坹齿¥æã</li> |
| | | * <li>å¦å卿»¡è¶³äº§åå·²å
¥åºçæ¡ä»¶æ¶ï¼æåºåºæ°éè§£æè§ååå
¥ä¸æ¡åºåºè®°å½ï¼{@code record_type=13}ï¼{@code record_id=shipping_info.id}ï¼ï¼ |
| | | * å¹¶åå产å {@code shipped_quantity}ï¼<b>ä¸è°ç¨</b>åºåæ£åé»è¾ã</li> |
| | | * <li><b>æ¥æåºåï¼å¯ä¸ï¼</b>ï¼ä¸å¾åå
³èéå®è®¢å {@code sales_ledger.delivery_date}ï¼äº¤ä»æ¥æï¼ç<strong>æ¥åæ¥åä¸å¤©</strong>ï¼ |
| | | * æ¶å»ç»ä¸ä¸ºå½æ¥ 00:00:00ãä¾å¦äº¤ä»æ¥ 2026-04-10 â åè´§æ¥æãåºåºæ¥æãå
¥åºè®°å½æ¶é´ã审æ¹ç³è¯·/ç»ææ¥æçå对é½å° 2026-04-09 00:00:00ï¼åå {@code D260403007} åç±»åºæ¯ï¼ã</li> |
| | | * <li><b>对é½é¡ºåº</b>ï¼â å
æ¹åè´§å°è´¦ {@code shipping_info.shipping_date}ï¼å {@code update_time}ï¼ï¼â¡ åæ¹è¯¥äº§åè¡çå
¥åº/åºåºè®°å½æ¶é´ï¼ |
| | | * ⢠{@code shipment_approval}ï¼â£ {@code approve_process} ä¸ä¸è¯¥éå®ååç¸å
³ç<b>å货审æ¹</b>ï¼{@code approve_type=7}ï¼ä¸<b>éå®å
¥åºå®¡æ¹</b>ï¼{@code approve_type=9}ï¼å« Web å
¥åºä¸æ«ç åæ ¼/ä¸åæ ¼å
¥åºäºç±åç¼ï¼ãæå
¥åºåºè®°å½å¨â ä¹åã</li> |
| | | * <li><b>主æµç¨åãè¡¥å
¨ã</b>ï¼å¯¹ãå·²å
¥åºä¸å·²åè´§ãçéå®äº§åè¡ï¼è§ {@link #isProductStockedForSync} / {@link #isProductShippedForSync}ï¼ï¼è¥ç¼ºå° |
| | | * {@code approve_process} å货审æ¹ï¼äºç± {@code å货审æ¹:ååå·}ï¼æç¼ºå°ä»»ä¸å½¢å¼éå®å
¥åºå®¡æ¹ï¼åæå
¥<b>å·²éè¿</b>ï¼{@code approve_status=2}ï¼è®°å½å䏿¡åæç {@code approve_node}ï¼ |
| | | * è¥è¯¥äº§åè¡å°æ {@code shipping_info}ï¼åæå
¥ä¸æ¡ç¶æä¸ºãå·²åè´§ãçåè´§å°è´¦ï¼ä¸ä¸å¡ãä¸è¡äº§å䏿¡åè´§è®°å½ã对é½ï¼ã</li> |
| | | * </ul> |
| | | * <p><b>çé¢ä»æä¸å°åºåºç常è§åå </b>ï¼ |
| | | * <ul> |
| | | * <li>æªæå¼æäº¤å¼å
³ï¼è§ä¸æ¹ãå¦ä½çæ£ååºãï¼æ¶æ¬ç¨ä¾ä¸ä¼æ§è¡ï¼æ°æ®åºä¸ä¼æä»»ä½åæ´ã</li> |
| | | * <li>å äºåæ°ä½ Maven/IDE è¿ç䏿¯æµè§å¨åä¸å¥æ°æ®æºï¼çé¢ä»æ¾ç¤ºæ§æ°æ®ã</li> |
| | | * <li>产åè¡ {@code product_stock_status != 2}ï¼æªæ è®°å
¨é¨å
¥åºï¼æ¶åé»è¾ä¼ {@code continue}ï¼å·²ç¨ {@link #RELAX_PRODUCT_STOCK_STATUS_CHECK} æ¾å®½ã</li> |
| | | * <li>è§£æåºçåºåºæ°é为 0 æ¶åå
åªæ¹æ¥æä¸ INSERTââ已对ãåè´§ç¶æ=å·²åè´§ãå
åºä»æå
¥ä¸æ¡ã</li> |
| | | * <li>åæ ¼åºåºå表ä¼å¸¦ {@code type=0}ï¼æå
¥æ¶å·²å {@code type='0'}ã</li> |
| | | * </ul> |
| | | * <p><b>å¦ä½çæ£ååº</b>ï¼æ»¡è¶³å
¶ä¸å³å¯ï¼ï¼ |
| | | * <ul> |
| | | * <li>IDEï¼Run Configuration â VM options å¢å {@code -Druoyi.shippingBatch.commit=true}</li> |
| | | * <li>ç¯å¢åéï¼{@code RUOYI_SHIPPING_BATCH_COMMIT=true}ï¼Windows å¯å¨è¿è¡å {@code set RUOYI_SHIPPING_BATCH_COMMIT=true}ï¼</li> |
| | | * <li>Mavenï¼{@code mvn test -Dtest=ShippingLedgerOutboundAndDatesBatchTest "-DargLine=-Druoyi.shippingBatch.commit=true"}</li> |
| | | * </ul> |
| | | * 并确认 profile æå䏿µè§å¨ç¸åçä¸å¡åºã |
| | | */ |
| | | @SpringBootTest(classes = RuoYiApplication.class) |
| | | class ShippingLedgerOutboundAndDatesBatchTest { |
| | | |
| | | private static final Logger log = LoggerFactory.getLogger(ShippingLedgerOutboundAndDatesBatchTest.class); |
| | | |
| | | private static final int SALE_PRODUCT_TYPE = 1; |
| | | private static final String SHIP_DONE = "å·²åè´§"; |
| | | private static final int DELIVERY_APPROVE_TYPE = ApproveTypeEnum.DELIVERY.getCode(); |
| | | private static final int STOCK_IN_APPROVE_TYPE = ApproveTypeEnum.STOCK_IN.getCode(); |
| | | /** |
| | | * ä¸ {@link com.ruoyi.approve.pojo.ApproveProcess} 注éä¸è´ï¼2=å®¡æ ¸å®æï¼éè¿ï¼ã |
| | | */ |
| | | private static final int APPROVE_STATUS_COMPLETED = 2; |
| | | /** |
| | | * 审æ¹èç¹ï¼1=åæï¼è§ {@code approve_node} æ ·ä¾æ°æ®ï¼ã |
| | | */ |
| | | private static final int APPROVE_NODE_STATUS_AGREE = 1; |
| | | private static final long FALLBACK_SYS_USER_ID = 1L; |
| | | |
| | | /** |
| | | * ä»
å¤çè¿äº {@code shipping_info.id}ï¼ç©ºè¡¨ç¤ºå
¨é¨ã |
| | | */ |
| | | private static final List<Long> ONLY_SHIPPING_INFO_IDS = Collections.emptyList(); |
| | | |
| | | /** |
| | | * ä»
å¤çè¿äºéå®ååå·ï¼ä¸ {@code sales_ledger.sales_contract_no} ä¸è´ï¼ï¼ä¾å¦ {@code D260503010}ï¼ç©ºè¡¨ç¤ºå
¨é¨ã |
| | | */ |
| | | private static final List<String> ONLY_SALES_CONTRACT_NOS = Collections.emptyList(); |
| | | |
| | | /** |
| | | * å½å©ä½å¯åºåºé为 0 æ¶ï¼æ¯å¦ç¨è®¢åè¡ {@code quantity} ä½ä¸ºè¡¥å½åºåºæ°éã |
| | | */ |
| | | private static final boolean USE_LINE_QUANTITY_WHEN_REMAINING_ZERO = true; |
| | | |
| | | /** |
| | | * 为 false æ¶ä»
å¤ç {@code product_stock_status = 2}ï¼å·²å
¨é¨å
¥åºï¼ç产åè¡ï¼æ°æ®ä¿®å¤å»ºè®® trueï¼é¿å
被跳è¿å¯¼è´ä¸åºåºè®°å½ã |
| | | */ |
| | | private static final boolean RELAX_PRODUCT_STOCK_STATUS_CHECK = true; |
| | | |
| | | /** |
| | | * æ¯å¦å
è®¸æ¬æ¹ä»»å¡æäº¤æ°æ®åºï¼ç³»ç»å±æ§ {@code ruoyi.shippingBatch.commit=true} æç¯å¢åé {@code RUOYI_SHIPPING_BATCH_COMMIT=true}ï¼ã |
| | | */ |
| | | @SuppressWarnings("unused") |
| | | static boolean commitSwitchEnabled() { |
| | | String p = System.getProperty("ruoyi.shippingBatch.commit"); |
| | | if (p != null && "true".equalsIgnoreCase(p.trim())) { |
| | | return true; |
| | | } |
| | | String e = System.getenv("RUOYI_SHIPPING_BATCH_COMMIT"); |
| | | return e != null && "true".equalsIgnoreCase(e.trim()); |
| | | } |
| | | |
| | | @Autowired |
| | | private DataSource dataSource; |
| | | |
| | | @Autowired |
| | | private ShippingInfoMapper shippingInfoMapper; |
| | | |
| | | @Autowired |
| | | private SalesLedgerMapper salesLedgerMapper; |
| | | |
| | | @Autowired |
| | | private SalesLedgerProductMapper salesLedgerProductMapper; |
| | | |
| | | /** |
| | | * æªæ¾å¼å
许æäº¤æ¶æ¬ç¨ä¾ä¸æ§è¡ï¼é¿å
{@code mvn test} 误åä¸å¡åºï¼ãè§ç±»æ³¨é {@link #commitSwitchEnabled()}ã |
| | | */ |
| | | @Test |
| | | @Transactional |
| | | @Rollback(false) |
| | | void batchOutboundAndAlignDatesToDayBeforeDelivery() { |
| | | NamedParameterJdbcTemplate named = new NamedParameterJdbcTemplate(dataSource); |
| | | |
| | | LedgerSyncStats syncStats = syncMissingApprovalsAndShippingLedgers(named); |
| | | log.warn( |
| | | "è¡¥å
¨å®¡æ¹/åè´§å°è´¦: æ«æè®¢åæ°={}, æ°å¢å货审æ¹={}, æ°å¢å
¥åºå®¡æ¹={}, æ°å¢ shipping_info è¡æ°={}", |
| | | syncStats.scannedLedgers, syncStats.deliveryInserted, syncStats.stockInInserted, syncStats.shippingInserted); |
| | | |
| | | LambdaQueryWrapper<ShippingInfo> qw = new LambdaQueryWrapper<ShippingInfo>().orderByAsc(ShippingInfo::getId); |
| | | if (!ONLY_SHIPPING_INFO_IDS.isEmpty()) { |
| | | qw.in(ShippingInfo::getId, ONLY_SHIPPING_INFO_IDS); |
| | | } |
| | | List<ShippingInfo> shippingRows = shippingInfoMapper.selectList(qw); |
| | | |
| | | int outboundCount = 0; |
| | | int datePatchCount = 0; |
| | | |
| | | for (ShippingInfo shipRow : shippingRows) { |
| | | SalesLedger ledger = salesLedgerMapper.selectById(shipRow.getSalesLedgerId()); |
| | | if (ledger == null) { |
| | | continue; |
| | | } |
| | | if (!ONLY_SALES_CONTRACT_NOS.isEmpty()) { |
| | | String cno = ledger.getSalesContractNo(); |
| | | if (cno == null || !ONLY_SALES_CONTRACT_NOS.contains(cno)) { |
| | | continue; |
| | | } |
| | | } |
| | | if (ledger.getDeliveryDate() == null) { |
| | | continue; |
| | | } |
| | | |
| | | LocalDate dayBeforeDelivery = ledger.getDeliveryDate().minusDays(1); |
| | | LocalDateTime alignedStart = LocalDateTime.of(dayBeforeDelivery, LocalTime.MIDNIGHT); |
| | | Timestamp alignedTs = Timestamp.valueOf(alignedStart); |
| | | java.sql.Date alignedSqlDate = java.sql.Date.valueOf(dayBeforeDelivery); |
| | | |
| | | boolean hasShipOutRow = alreadyShipOutForShippingRow(named, shipRow.getId()); |
| | | // ä»
ä»¥ãæ¯å¦å·²ææ¬äººå°è´¦å¯¹åºçåè´§åºåºã为åï¼ä¸å delivery_status=5 è·³è¿ï¼å¦å伿¼æãå·²åè´§å´æ 13 è®°å½ãçèæ°æ® |
| | | if (hasShipOutRow) { |
| | | patchDatesForRow(named, ledger, shipRow, alignedTs, alignedSqlDate); |
| | | datePatchCount++; |
| | | continue; |
| | | } |
| | | |
| | | SalesLedgerProduct product = salesLedgerProductMapper.selectById(shipRow.getSalesLedgerProductId()); |
| | | if (product == null || !Objects.equals(product.getType(), SALE_PRODUCT_TYPE)) { |
| | | patchDatesForRow(named, ledger, shipRow, alignedTs, alignedSqlDate); |
| | | datePatchCount++; |
| | | continue; |
| | | } |
| | | if (!RELAX_PRODUCT_STOCK_STATUS_CHECK) { |
| | | if (product.getProductStockStatus() == null || product.getProductStockStatus() != 2) { |
| | | patchDatesForRow(named, ledger, shipRow, alignedTs, alignedSqlDate); |
| | | datePatchCount++; |
| | | continue; |
| | | } |
| | | } |
| | | if (product.getProductModelId() == null) { |
| | | patchDatesForRow(named, ledger, shipRow, alignedTs, alignedSqlDate); |
| | | datePatchCount++; |
| | | continue; |
| | | } |
| | | |
| | | product.fillRemainingQuantity(); |
| | | BigDecimal outboundQty = resolveOutboundQuantity(product); |
| | | if (outboundQty == null || outboundQty.compareTo(BigDecimal.ZERO) <= 0) { |
| | | if (SHIP_DONE.equals(shipRow.getStatus())) { |
| | | outboundQty = product.getQuantity() != null && product.getQuantity().compareTo(BigDecimal.ZERO) > 0 |
| | | ? product.getQuantity() |
| | | : BigDecimal.ONE; |
| | | } |
| | | } |
| | | if (outboundQty == null || outboundQty.compareTo(BigDecimal.ZERO) <= 0) { |
| | | patchDatesForRow(named, ledger, shipRow, alignedTs, alignedSqlDate); |
| | | datePatchCount++; |
| | | continue; |
| | | } |
| | | |
| | | // â å
ååè´§æ¥æï¼äº¤ä»æ¥åä¸å¤©ï¼ï¼â¡ ä»
INSERT åºåºå°è´¦è®°å½ï¼ä¸æ£åºåè¡¨ï¼ |
| | | patchShippingInfoDateFirst(named, shipRow.getId(), alignedTs); |
| | | insertSaleShipOutboundRecord(named, ledger, shipRow, product, outboundQty, alignedTs); |
| | | |
| | | BigDecimal oldShipped = product.getShippedQuantity() == null ? BigDecimal.ZERO : product.getShippedQuantity(); |
| | | product.setShippedQuantity(oldShipped.add(outboundQty)); |
| | | product.fillRemainingQuantity(); |
| | | salesLedgerProductMapper.updateById(product); |
| | | |
| | | patchShippingInfoStatusShipped(named, shipRow.getId(), alignedTs); |
| | | |
| | | patchStockRecordTimes(named, ledger.getId(), product.getId(), shipRow.getId(), alignedTs); |
| | | patchShipmentApprovalTimes(named, shipRow.getId(), alignedTs); |
| | | patchDeliveryApproveDates(named, ledger, alignedTs, alignedSqlDate); |
| | | patchStockInApproveDates(named, ledger, alignedTs, alignedSqlDate); |
| | | |
| | | refreshSalesLedgerAggregate(ledger.getId()); |
| | | outboundCount++; |
| | | datePatchCount++; |
| | | } |
| | | |
| | | assertTrue(outboundCount >= 0 && datePatchCount >= 0, |
| | | "åºåºå¤çè¡æ°: " + outboundCount + ", æ¥æå¯¹é½æ¶åè¡æ°(å«è·³è¿ä»
æ¹æ¥æ): " + datePatchCount); |
| | | log.warn( |
| | | "åè´§/åºåºæ¹éä»»å¡å·²æäº¤ãåºåº INSERT è¡æ°={}, æ¥æå¯¹é½è¡æ°={}, shipping_info æ»è¡æ°={}", |
| | | outboundCount, datePatchCount, shippingRows.size()); |
| | | } |
| | | |
| | | private static final class LedgerSyncStats { |
| | | private int scannedLedgers; |
| | | private int deliveryInserted; |
| | | private int stockInInserted; |
| | | private int shippingInserted; |
| | | } |
| | | |
| | | private static final class ApproveActor { |
| | | private final long userId; |
| | | private final String nickName; |
| | | private final long deptId; |
| | | private final String deptName; |
| | | |
| | | private ApproveActor(long userId, String nickName, long deptId, String deptName) { |
| | | this.userId = userId; |
| | | this.nickName = nickName != null ? nickName : ""; |
| | | this.deptId = deptId; |
| | | this.deptName = deptName != null && !deptName.isEmpty() ? deptName : "æ»å
¬å¸"; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * éåéå®å°è´¦ï¼å¯¹å·²å
¥åºä¸å·²åè´§ç产åè¡è¡¥å
¨å货审æ¹ãå
¥åºå®¡æ¹ï¼ä¸åå¨åæå
¥ä¸ºå·²éè¿ï¼ï¼å¹¶è¡¥å
¨ç¼ºå¤±ç {@code shipping_info}ã |
| | | */ |
| | | private LedgerSyncStats syncMissingApprovalsAndShippingLedgers(NamedParameterJdbcTemplate named) { |
| | | LedgerSyncStats stats = new LedgerSyncStats(); |
| | | LambdaQueryWrapper<SalesLedger> lqw = new LambdaQueryWrapper<SalesLedger>().orderByAsc(SalesLedger::getId); |
| | | if (!ONLY_SALES_CONTRACT_NOS.isEmpty()) { |
| | | lqw.in(SalesLedger::getSalesContractNo, ONLY_SALES_CONTRACT_NOS); |
| | | } |
| | | List<SalesLedger> ledgers = salesLedgerMapper.selectList(lqw); |
| | | for (SalesLedger ledger : ledgers) { |
| | | stats.scannedLedgers++; |
| | | if (ledger.getId() == null || ledger.getDeliveryDate() == null |
| | | || ledger.getSalesContractNo() == null || ledger.getSalesContractNo().isEmpty()) { |
| | | continue; |
| | | } |
| | | LocalDate dayBefore = ledger.getDeliveryDate().minusDays(1); |
| | | LocalDateTime alignedStart = LocalDateTime.of(dayBefore, LocalTime.MIDNIGHT); |
| | | Timestamp alignedTs = Timestamp.valueOf(alignedStart); |
| | | java.sql.Date alignedSqlDate = java.sql.Date.valueOf(dayBefore); |
| | | |
| | | List<SalesLedgerProduct> products = salesLedgerProductMapper.selectList( |
| | | new LambdaQueryWrapper<SalesLedgerProduct>() |
| | | .eq(SalesLedgerProduct::getSalesLedgerId, ledger.getId()) |
| | | .eq(SalesLedgerProduct::getType, SALE_PRODUCT_TYPE)); |
| | | if (CollectionUtils.isEmpty(products)) { |
| | | continue; |
| | | } |
| | | List<SalesLedgerProduct> stockedAndShipped = products.stream() |
| | | .filter(this::isProductStockedForSync) |
| | | .filter(this::isProductShippedForSync) |
| | | .collect(Collectors.toList()); |
| | | if (stockedAndShipped.isEmpty()) { |
| | | continue; |
| | | } |
| | | |
| | | ApproveActor actor = resolveApproveActor(ledger, named); |
| | | long tenant = ledger.getTenantId() != null ? ledger.getTenantId() : actor.deptId; |
| | | |
| | | if (!deliveryApprovalExists(named, ledger.getSalesContractNo())) { |
| | | insertCompletedApproveProcess(named, ledger, alignedTs, alignedSqlDate, actor, tenant, |
| | | DELIVERY_APPROVE_TYPE, "å货审æ¹:" + ledger.getSalesContractNo(), null); |
| | | stats.deliveryInserted++; |
| | | } |
| | | if (!stockInApprovalExists(named, ledger.getSalesContractNo())) { |
| | | List<Long> stockedLineIds = products.stream() |
| | | .filter(this::isProductStockedForSync) |
| | | .map(SalesLedgerProduct::getId) |
| | | .filter(Objects::nonNull) |
| | | .sorted() |
| | | .collect(Collectors.toList()); |
| | | if (!stockedLineIds.isEmpty()) { |
| | | String ids = stockedLineIds.stream().map(String::valueOf).collect(Collectors.joining(",")); |
| | | String remark = "salesStock:" + ledger.getId() + ":" + ids; |
| | | insertCompletedApproveProcess(named, ledger, alignedTs, alignedSqlDate, actor, tenant, |
| | | STOCK_IN_APPROVE_TYPE, "å
¥åºå®¡æ¹:" + ledger.getSalesContractNo(), remark); |
| | | stats.stockInInserted++; |
| | | } |
| | | } |
| | | for (SalesLedgerProduct line : stockedAndShipped) { |
| | | if (line.getId() == null) { |
| | | continue; |
| | | } |
| | | long shipCnt = shippingInfoMapper.selectCount( |
| | | new LambdaQueryWrapper<ShippingInfo>().eq(ShippingInfo::getSalesLedgerProductId, line.getId())); |
| | | if (shipCnt > 0) { |
| | | continue; |
| | | } |
| | | insertSyntheticShippingInfo(named, ledger, line, alignedTs, actor, tenant); |
| | | stats.shippingInserted++; |
| | | } |
| | | } |
| | | return stats; |
| | | } |
| | | |
| | | private boolean isProductStockedForSync(SalesLedgerProduct p) { |
| | | if (Objects.equals(p.getProductStockStatus(), 2)) { |
| | | return true; |
| | | } |
| | | BigDecimal sq = p.getStockedQuantity(); |
| | | return sq != null && sq.compareTo(BigDecimal.ZERO) > 0; |
| | | } |
| | | |
| | | private boolean isProductShippedForSync(SalesLedgerProduct p) { |
| | | BigDecimal sh = p.getShippedQuantity(); |
| | | if (sh != null && sh.compareTo(BigDecimal.ZERO) > 0) { |
| | | return true; |
| | | } |
| | | List<ShippingInfo> rows = shippingInfoMapper.selectList( |
| | | new LambdaQueryWrapper<ShippingInfo>().eq(ShippingInfo::getSalesLedgerProductId, p.getId())); |
| | | return rows.stream().anyMatch(r -> SHIP_DONE.equals(r.getStatus())); |
| | | } |
| | | |
| | | private boolean deliveryApprovalExists(NamedParameterJdbcTemplate named, String contractNo) { |
| | | MapSqlParameterSource p = new MapSqlParameterSource(); |
| | | p.addValue("cno", contractNo); |
| | | p.addValue("atype", DELIVERY_APPROVE_TYPE); |
| | | Integer cnt = named.queryForObject( |
| | | "SELECT COUNT(1) FROM approve_process WHERE approve_delete = 0 AND approve_type = :atype " |
| | | + "AND approve_reason = CONCAT('å货审æ¹:', :cno)", |
| | | p, Integer.class); |
| | | return cnt != null && cnt > 0; |
| | | } |
| | | |
| | | private boolean stockInApprovalExists(NamedParameterJdbcTemplate named, String contractNo) { |
| | | MapSqlParameterSource p = new MapSqlParameterSource(); |
| | | p.addValue("cno", contractNo); |
| | | p.addValue("atype", STOCK_IN_APPROVE_TYPE); |
| | | Integer cnt = named.queryForObject( |
| | | "SELECT COUNT(1) FROM approve_process WHERE approve_delete = 0 AND approve_type = :atype AND (" |
| | | + "approve_reason = CONCAT('å
¥åºå®¡æ¹:', :cno) " |
| | | + "OR approve_reason LIKE CONCAT('å
¥åºå®¡æ¹:', :cno, ':%') " |
| | | + "OR approve_reason = CONCAT('é宿«ç åæ ¼å
¥åºå®¡æ¹:', :cno) " |
| | | + "OR approve_reason = CONCAT('é宿«ç ä¸åæ ¼å
¥åºå®¡æ¹:', :cno))", |
| | | p, Integer.class); |
| | | return cnt != null && cnt > 0; |
| | | } |
| | | |
| | | private ApproveActor resolveApproveActor(SalesLedger ledger, NamedParameterJdbcTemplate named) { |
| | | String ep = ledger.getEntryPerson(); |
| | | if (ep != null && !ep.trim().isEmpty()) { |
| | | try { |
| | | return loadApproveActor(named, Long.parseLong(ep.trim())); |
| | | } catch (NumberFormatException ignored) { |
| | | // å½å
¥äººå¯è½æ¯æµç§°çéæ°åï¼éåé»è®¤è´¦å· |
| | | } |
| | | } |
| | | return loadApproveActor(named, FALLBACK_SYS_USER_ID); |
| | | } |
| | | |
| | | private ApproveActor loadApproveActor(NamedParameterJdbcTemplate named, long userId) { |
| | | MapSqlParameterSource p = new MapSqlParameterSource("id", userId); |
| | | List<ApproveActor> list = named.query( |
| | | "SELECT u.user_id AS userId, u.nick_name AS nickName, " |
| | | + "(SELECT sud.dept_id FROM sys_user_dept sud WHERE sud.user_id = u.user_id ORDER BY sud.id ASC LIMIT 1) AS deptId, " |
| | | + "(SELECT d.dept_name FROM sys_user_dept sud INNER JOIN sys_dept d ON d.dept_id = sud.dept_id " |
| | | + "WHERE sud.user_id = u.user_id AND (d.del_flag = '0' OR d.del_flag IS NULL) ORDER BY sud.id ASC LIMIT 1) AS deptName " |
| | | + "FROM sys_user u " |
| | | + "WHERE u.user_id = :id AND (u.del_flag = '0' OR u.del_flag IS NULL) LIMIT 1", |
| | | p, (rs, i) -> { |
| | | long uid = rs.getLong("userId"); |
| | | String nick = rs.getString("nickName"); |
| | | Long dId = rs.getObject("deptId") != null ? rs.getLong("deptId") : 100L; |
| | | String dName = rs.getString("deptName"); |
| | | return new ApproveActor(uid, nick, dId, dName != null ? dName : "æ»å
¬å¸"); |
| | | }); |
| | | if (list.isEmpty()) { |
| | | return new ApproveActor(FALLBACK_SYS_USER_ID, "管çåè´¦å·", 100L, "æ»å
¬å¸"); |
| | | } |
| | | return list.get(0); |
| | | } |
| | | |
| | | private String nextSyntheticApproveId(NamedParameterJdbcTemplate named, LocalDate logicalDay) { |
| | | String pfx = logicalDay.format(DateTimeFormatter.BASIC_ISO_DATE); |
| | | MapSqlParameterSource p = new MapSqlParameterSource("pfx", pfx); |
| | | Integer maxSuffix = named.queryForObject( |
| | | "SELECT IFNULL(MAX(CAST(RIGHT(approve_id, 3) AS UNSIGNED)), 0) FROM approve_process " |
| | | + "WHERE approve_delete = 0 AND CHAR_LENGTH(approve_id) = 11 AND approve_id LIKE CONCAT(:pfx, '%')", |
| | | p, Integer.class); |
| | | int n = (maxSuffix == null ? 0 : maxSuffix) + 1; |
| | | if (n > 999) { |
| | | n = 1; |
| | | } |
| | | return pfx + String.format("%03d", n); |
| | | } |
| | | |
| | | private void insertCompletedApproveProcess(NamedParameterJdbcTemplate named, SalesLedger ledger, |
| | | Timestamp alignedTs, java.sql.Date alignedSqlDate, |
| | | ApproveActor actor, long tenant, int approveType, |
| | | String reason, String remark) { |
| | | LocalDate logicalDay = alignedSqlDate.toLocalDate(); |
| | | String approveId = nextSyntheticApproveId(named, logicalDay); |
| | | String uidStr = String.valueOf(actor.userId); |
| | | MapSqlParameterSource p = new MapSqlParameterSource(); |
| | | p.addValue("approveId", approveId); |
| | | p.addValue("uid", actor.userId); |
| | | p.addValue("nick", actor.nickName); |
| | | p.addValue("deptId", actor.deptId); |
| | | p.addValue("deptName", actor.deptName); |
| | | p.addValue("uidStr", uidStr); |
| | | p.addValue("reason", reason); |
| | | p.addValue("remark", remark); |
| | | p.addValue("dt", alignedTs); |
| | | p.addValue("d", alignedSqlDate); |
| | | p.addValue("tenant", tenant); |
| | | p.addValue("atype", approveType); |
| | | p.addValue("st", APPROVE_STATUS_COMPLETED); |
| | | named.update( |
| | | "INSERT INTO approve_process (approve_id, approve_user, approve_user_name, approve_dept_id, approve_dept_name, " |
| | | + "approve_user_ids, approve_user_names, approve_user_current_id, approve_user_current_name, " |
| | | + "approve_reason, approve_time, approve_over_time, approve_status, approve_delete, tenant_id, approve_type, " |
| | | + "approve_remark, create_time, start_date, end_date) " |
| | | + "VALUES (:approveId, :uid, :nick, :deptId, :deptName, :uidStr, :nick, :uid, :nick, " |
| | | + ":reason, :dt, :dt, :st, 0, :tenant, :atype, :remark, :dt, :d, :d)", |
| | | p); |
| | | insertSingleCompletedApproveNode(named, approveId, alignedTs, actor); |
| | | } |
| | | |
| | | private void insertSingleCompletedApproveNode(NamedParameterJdbcTemplate named, String approveId, |
| | | Timestamp alignedTs, ApproveActor actor) { |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | MapSqlParameterSource p = new MapSqlParameterSource(); |
| | | p.addValue("apid", approveId); |
| | | p.addValue("uid", actor.userId); |
| | | p.addValue("nick", actor.nickName); |
| | | p.addValue("ts", alignedTs); |
| | | p.addValue("tenant", actor.deptId); |
| | | p.addValue("nst", APPROVE_NODE_STATUS_AGREE); |
| | | p.addValue("now", Timestamp.valueOf(now)); |
| | | named.update( |
| | | "INSERT INTO approve_node (approve_process_id, approve_node_order, approve_node_user_id, approve_node_user, " |
| | | + "approve_node_time, approve_node_status, tenant_id, delete_flag, create_time, update_time, create_user, update_user) " |
| | | + "VALUES (:apid, 1, :uid, :nick, :ts, :nst, :tenant, 0, :now, :now, :uid, :uid)", |
| | | p); |
| | | } |
| | | |
| | | private void insertSyntheticShippingInfo(NamedParameterJdbcTemplate named, SalesLedger ledger, |
| | | SalesLedgerProduct line, Timestamp alignedTs, |
| | | ApproveActor actor, long tenant) { |
| | | String shippingNo = "SH-SYNC-" + line.getId() + "-" + System.currentTimeMillis(); |
| | | MapSqlParameterSource p = new MapSqlParameterSource(); |
| | | p.addValue("slid", ledger.getId()); |
| | | p.addValue("slpid", line.getId()); |
| | | p.addValue("sd", alignedTs); |
| | | p.addValue("tenant", tenant); |
| | | p.addValue("uid", actor.userId); |
| | | p.addValue("sno", shippingNo); |
| | | named.update( |
| | | "INSERT INTO shipping_info (sales_ledger_id, sales_ledger_product_id, shipping_date, status, tenant_id, " |
| | | + "create_time, update_time, create_user, update_user, shipping_no, type) " |
| | | + "VALUES (:slid, :slpid, :sd, 'å·²åè´§', :tenant, :sd, :sd, :uid, :uid, :sno, '货车')", |
| | | p); |
| | | } |
| | | |
| | | /** |
| | | * è§£ææ¬è¡åºè¡¥å½çåºåºæ°éï¼ä¸ç¢°åºå表ï¼ãéå
对 {@code product} è°ç¨è¿ {@link SalesLedgerProduct#fillRemainingQuantity()}ã |
| | | */ |
| | | private BigDecimal resolveOutboundQuantity(SalesLedgerProduct product) { |
| | | BigDecimal rem = product.getRemainingShippedQuantity(); |
| | | if (rem != null && rem.compareTo(BigDecimal.ZERO) > 0) { |
| | | return rem; |
| | | } |
| | | BigDecimal stocked = product.getStockedQuantity() == null ? BigDecimal.ZERO : product.getStockedQuantity(); |
| | | BigDecimal shipped = product.getShippedQuantity() == null ? BigDecimal.ZERO : product.getShippedQuantity(); |
| | | BigDecimal gap = stocked.subtract(shipped); |
| | | if (gap.compareTo(BigDecimal.ZERO) > 0) { |
| | | return gap; |
| | | } |
| | | if (USE_LINE_QUANTITY_WHEN_REMAINING_ZERO && product.getQuantity() != null |
| | | && product.getQuantity().compareTo(BigDecimal.ZERO) > 0) { |
| | | return product.getQuantity(); |
| | | } |
| | | return BigDecimal.ZERO; |
| | | } |
| | | |
| | | /** |
| | | * æå
¥ãéå®-åè´§åºåºãè®°å½ï¼ä¸ååºåæ°éï¼ä»
ä¿è¯åºåºå°è´¦åè¡¨å¯æååå·æ¥å°ã |
| | | */ |
| | | private void insertSaleShipOutboundRecord(NamedParameterJdbcTemplate named, SalesLedger ledger, |
| | | ShippingInfo shipRow, SalesLedgerProduct product, BigDecimal outboundQty, |
| | | Timestamp alignedTs) { |
| | | String batchNo = "CK-DIRECT-" + shipRow.getId() + "-" + System.currentTimeMillis(); |
| | | int createUser = shipRow.getCreateUser() != null ? shipRow.getCreateUser() : 1; |
| | | MapSqlParameterSource p = new MapSqlParameterSource(); |
| | | p.addValue("batch", batchNo); |
| | | p.addValue("qty", outboundQty); |
| | | p.addValue("rid", shipRow.getId()); |
| | | p.addValue("rt", StockOutQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode()); |
| | | p.addValue("pmid", product.getProductModelId()); |
| | | p.addValue("ts", alignedTs); |
| | | p.addValue("cuser", createUser); |
| | | p.addValue("slid", ledger.getId()); |
| | | p.addValue("slpid", product.getId()); |
| | | named.update( |
| | | "INSERT INTO stock_out_record (outbound_batches, stock_out_num, record_id, record_type, product_model_id, " |
| | | + "create_time, update_time, create_user, update_user, type, sales_ledger_id, sales_ledger_product_id) " |
| | | + "VALUES (:batch, :qty, :rid, :rt, :pmid, :ts, :ts, :cuser, :cuser, '0', :slid, :slpid)", |
| | | p); |
| | | } |
| | | |
| | | private boolean alreadyShipOutForShippingRow(NamedParameterJdbcTemplate named, Long shippingInfoId) { |
| | | MapSqlParameterSource p = new MapSqlParameterSource(); |
| | | p.addValue("rid", shippingInfoId); |
| | | p.addValue("rt", StockOutQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode()); |
| | | Integer cnt = named.queryForObject( |
| | | "SELECT COUNT(1) FROM stock_out_record WHERE record_id = :rid AND record_type = :rt", |
| | | p, Integer.class); |
| | | return cnt != null && cnt > 0; |
| | | } |
| | | |
| | | /** |
| | | * â åè´§å°è´¦ â â¡ å
¥åºåºè®°å½ â ⢠shipment_approval â ⣠å货审æ¹ï¼åä¸ºäº¤ä»æ¥åä¸å¤© 00:00:00ï¼ã |
| | | */ |
| | | private void patchDatesForRow(NamedParameterJdbcTemplate named, SalesLedger ledger, ShippingInfo shipRow, |
| | | Timestamp alignedTs, java.sql.Date alignedSqlDate) { |
| | | patchShippingInfoDateFirst(named, shipRow.getId(), alignedTs); |
| | | if (ledger.getId() != null && shipRow.getSalesLedgerProductId() != null) { |
| | | patchStockRecordTimes(named, ledger.getId(), shipRow.getSalesLedgerProductId(), shipRow.getId(), alignedTs); |
| | | } |
| | | patchShipmentApprovalTimes(named, shipRow.getId(), alignedTs); |
| | | patchDeliveryApproveDates(named, ledger, alignedTs, alignedSqlDate); |
| | | patchStockInApproveDates(named, ledger, alignedTs, alignedSqlDate); |
| | | } |
| | | |
| | | /** |
| | | * â å
æ´æ°åè´§å°è´¦ {@code shipping_date}ï¼å {@code update_time}ï¼ã |
| | | */ |
| | | private void patchShippingInfoDateFirst(NamedParameterJdbcTemplate named, Long shippingInfoId, Timestamp alignedTs) { |
| | | MapSqlParameterSource ship = new MapSqlParameterSource(); |
| | | ship.addValue("sd", alignedTs); |
| | | ship.addValue("sid", shippingInfoId); |
| | | named.update( |
| | | "UPDATE shipping_info SET shipping_date = :sd, update_time = :sd WHERE id = :sid", |
| | | ship); |
| | | } |
| | | |
| | | /** |
| | | * åºåºå®æåå°åè´§å°è´¦ç¶æç½®ä¸ºå·²åè´§ï¼åè´§æ¥æå·²å¨æ£åºååå
¥ï¼ã |
| | | */ |
| | | private void patchShippingInfoStatusShipped(NamedParameterJdbcTemplate named, Long shippingInfoId, Timestamp alignedTs) { |
| | | MapSqlParameterSource p = new MapSqlParameterSource(); |
| | | p.addValue("st", SHIP_DONE); |
| | | p.addValue("ts", alignedTs); |
| | | p.addValue("sid", shippingInfoId); |
| | | named.update( |
| | | "UPDATE shipping_info SET status = :st, update_time = :ts WHERE id = :sid", |
| | | p); |
| | | } |
| | | |
| | | private void patchStockRecordTimes(NamedParameterJdbcTemplate named, Long salesLedgerId, Long salesLedgerProductId, |
| | | Long shippingInfoId, Timestamp alignedTs) { |
| | | MapSqlParameterSource p = new MapSqlParameterSource(); |
| | | p.addValue("ts", alignedTs); |
| | | p.addValue("sid", salesLedgerId); |
| | | p.addValue("spid", salesLedgerProductId); |
| | | named.update( |
| | | "UPDATE stock_in_record SET create_time = :ts, update_time = :ts " |
| | | + "WHERE sales_ledger_id = :sid AND sales_ledger_product_id = :spid", |
| | | p); |
| | | |
| | | MapSqlParameterSource p2 = new MapSqlParameterSource(); |
| | | p2.addValue("ts", alignedTs); |
| | | p2.addValue("sid", salesLedgerId); |
| | | p2.addValue("spid", salesLedgerProductId); |
| | | named.update( |
| | | "UPDATE stock_out_record SET create_time = :ts, update_time = :ts " |
| | | + "WHERE sales_ledger_id = :sid AND sales_ledger_product_id = :spid", |
| | | p2); |
| | | |
| | | MapSqlParameterSource p3 = new MapSqlParameterSource(); |
| | | p3.addValue("ts", alignedTs); |
| | | p3.addValue("rid", shippingInfoId); |
| | | p3.addValue("rt", StockOutQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode()); |
| | | named.update( |
| | | "UPDATE stock_out_record SET create_time = :ts, update_time = :ts " |
| | | + "WHERE record_id = :rid AND record_type = :rt", |
| | | p3); |
| | | } |
| | | |
| | | /** |
| | | * ä¸ hbtmblc èæ¬ä¸è´ï¼{@code approve_time}/{@code approve_over_time} 为 datetimeï¼{@code start_date}/{@code end_date} 为 dateã |
| | | */ |
| | | private void patchDeliveryApproveDates(NamedParameterJdbcTemplate named, SalesLedger ledger, |
| | | Timestamp alignedDateTime, java.sql.Date alignedSqlDate) { |
| | | if (ledger.getSalesContractNo() == null || ledger.getSalesContractNo().isEmpty()) { |
| | | return; |
| | | } |
| | | String reason = "å货审æ¹:" + ledger.getSalesContractNo(); |
| | | MapSqlParameterSource p = new MapSqlParameterSource(); |
| | | p.addValue("dt", alignedDateTime); |
| | | p.addValue("d", alignedSqlDate); |
| | | p.addValue("reason", reason); |
| | | p.addValue("atype", DELIVERY_APPROVE_TYPE); |
| | | named.update( |
| | | "UPDATE approve_process SET approve_time = :dt, approve_over_time = :dt, start_date = :d, end_date = :d " |
| | | + "WHERE approve_delete = 0 AND approve_type = :atype AND approve_reason = :reason", |
| | | p); |
| | | } |
| | | |
| | | /** |
| | | * éå®è®¢åç¸å
³å
¥åºå®¡æ¹ï¼{@link ApproveTypeEnum#STOCK_IN}ãäºç±åç¼ä¸ä¸å¡ä»£ç ä¸è´ï¼è§ {@code SalesLedgerServiceImpl#salesStock} çï¼ã |
| | | * 使ç¨ç²¾ç¡®äºç±æ {@code å
¥åºå®¡æ¹:ååå·:...} æ©å±æ ¼å¼ï¼é¿å
ååå·äºä¸ºåç¼æ¶ {@code LIKE '...%'} 误å¹é
ã |
| | | */ |
| | | private void patchStockInApproveDates(NamedParameterJdbcTemplate named, SalesLedger ledger, |
| | | Timestamp alignedDateTime, java.sql.Date alignedSqlDate) { |
| | | if (ledger.getSalesContractNo() == null || ledger.getSalesContractNo().isEmpty()) { |
| | | return; |
| | | } |
| | | String cno = ledger.getSalesContractNo(); |
| | | MapSqlParameterSource p = new MapSqlParameterSource(); |
| | | p.addValue("dt", alignedDateTime); |
| | | p.addValue("d", alignedSqlDate); |
| | | p.addValue("atype", STOCK_IN_APPROVE_TYPE); |
| | | p.addValue("cno", cno); |
| | | named.update( |
| | | "UPDATE approve_process SET approve_time = :dt, approve_over_time = :dt, start_date = :d, end_date = :d " |
| | | + "WHERE approve_delete = 0 AND approve_type = :atype AND (" |
| | | + "approve_reason = CONCAT('å
¥åºå®¡æ¹:', :cno) " |
| | | + "OR approve_reason LIKE CONCAT('å
¥åºå®¡æ¹:', :cno, ':%') " |
| | | + "OR approve_reason = CONCAT('é宿«ç åæ ¼å
¥åºå®¡æ¹:', :cno) " |
| | | + "OR approve_reason = CONCAT('é宿«ç ä¸åæ ¼å
¥åºå®¡æ¹:', :cno))", |
| | | p); |
| | | } |
| | | |
| | | /** |
| | | * èæ¬ä¸ç {@code shipment_approval} 表ï¼è¥ææ°æ®ï¼æåè´§ä¿¡æ¯ id 坹齿¶é´ã |
| | | */ |
| | | private void patchShipmentApprovalTimes(NamedParameterJdbcTemplate named, Long shippingInfoId, Timestamp alignedTs) { |
| | | MapSqlParameterSource p = new MapSqlParameterSource(); |
| | | p.addValue("ts", alignedTs); |
| | | p.addValue("sid", shippingInfoId); |
| | | named.update( |
| | | "UPDATE shipment_approval SET create_time = :ts, update_time = :ts WHERE shipping_info_id = :sid", |
| | | p); |
| | | } |
| | | |
| | | private void refreshSalesLedgerAggregate(Long salesLedgerId) { |
| | | SalesLedger salesLedger = salesLedgerMapper.selectById(salesLedgerId); |
| | | if (salesLedger == null) { |
| | | return; |
| | | } |
| | | List<ShippingInfo> unsent = shippingInfoMapper.selectList(new LambdaQueryWrapper<ShippingInfo>() |
| | | .eq(ShippingInfo::getSalesLedgerId, salesLedgerId) |
| | | .ne(ShippingInfo::getStatus, SHIP_DONE)); |
| | | if (CollectionUtils.isEmpty(unsent) && !Integer.valueOf(5).equals(salesLedger.getDeliveryStatus())) { |
| | | salesLedger.setDeliveryStatus(5); |
| | | salesLedgerMapper.updateById(salesLedger); |
| | | } |
| | | |
| | | List<SalesLedgerProduct> ledgerAllProducts = salesLedgerProductMapper.selectList( |
| | | new LambdaQueryWrapper<SalesLedgerProduct>().eq(SalesLedgerProduct::getSalesLedgerId, salesLedgerId)); |
| | | boolean anyInbound = ledgerAllProducts.stream().anyMatch(p -> { |
| | | BigDecimal sq = p.getStockedQuantity(); |
| | | return sq != null && sq.compareTo(BigDecimal.ZERO) > 0; |
| | | }); |
| | | boolean allLinesFull = ledgerAllProducts.stream().allMatch(p -> Objects.equals(p.getProductStockStatus(), 2)); |
| | | salesLedger.setStockStatus(allLinesFull ? 2 : (anyInbound ? 1 : 0)); |
| | | salesLedgerMapper.updateById(salesLedger); |
| | | } |
| | | } |