Showing
11 changed files
with
161 additions
and
3 deletions
| @@ -258,4 +258,16 @@ public class ReceiptLedgerInfoController extends DefaultBaseController { | @@ -258,4 +258,16 @@ public class ReceiptLedgerInfoController extends DefaultBaseController { | ||
| 258 | } | 258 | } |
| 259 | return InvokeResultBuilder.success(); | 259 | return InvokeResultBuilder.success(); |
| 260 | } | 260 | } |
| 261 | + | ||
| 262 | + | ||
| 263 | + /** | ||
| 264 | + * 冻结欠款客户订货单、草稿要车单数据 | ||
| 265 | + */ | ||
| 266 | + @ApiOperation("自动生成台账明细数据") | ||
| 267 | + @GetMapping("/autoFreeze") | ||
| 268 | + @Scheduled(cron = "0 50 2 * * ?") | ||
| 269 | + public InvokeResult<Void> autoFreeze() { | ||
| 270 | + receiptLedgerInfoService.autoFreeze(null); | ||
| 271 | + return InvokeResultBuilder.success(); | ||
| 272 | + } | ||
| 261 | } | 273 | } |
| @@ -23,6 +23,7 @@ import com.lframework.xingyun.sc.service.customer.CustomerCreditHistoryService; | @@ -23,6 +23,7 @@ import com.lframework.xingyun.sc.service.customer.CustomerCreditHistoryService; | ||
| 23 | import com.lframework.xingyun.sc.service.customer.CustomerCreditService; | 23 | import com.lframework.xingyun.sc.service.customer.CustomerCreditService; |
| 24 | import com.lframework.xingyun.sc.service.customer.CustomerDevelopPlanService; | 24 | import com.lframework.xingyun.sc.service.customer.CustomerDevelopPlanService; |
| 25 | import com.lframework.xingyun.sc.service.ledger.FundCoordinationService; | 25 | import com.lframework.xingyun.sc.service.ledger.FundCoordinationService; |
| 26 | +import com.lframework.xingyun.sc.service.ledger.ReceiptLedgerInfoService; | ||
| 26 | import com.lframework.xingyun.sc.service.order.*; | 27 | import com.lframework.xingyun.sc.service.order.*; |
| 27 | import com.lframework.xingyun.sc.service.purchase.ReplenishmentOrderService; | 28 | import com.lframework.xingyun.sc.service.purchase.ReplenishmentOrderService; |
| 28 | import com.lframework.xingyun.sc.service.shipments.ShipmentsOrderInfoService; | 29 | import com.lframework.xingyun.sc.service.shipments.ShipmentsOrderInfoService; |
| @@ -117,6 +118,8 @@ public class BusinessDataHandlerServiceImpl implements BusinessDataHandlerServic | @@ -117,6 +118,8 @@ public class BusinessDataHandlerServiceImpl implements BusinessDataHandlerServic | ||
| 117 | private FundCoordinationService fundCoordinationService; | 118 | private FundCoordinationService fundCoordinationService; |
| 118 | @Resource | 119 | @Resource |
| 119 | private ShipmentsOrderInfoService shipmentsOrderInfoService; | 120 | private ShipmentsOrderInfoService shipmentsOrderInfoService; |
| 121 | + @Resource | ||
| 122 | + private ReceiptLedgerInfoService receiptLedgerInfoService; | ||
| 120 | 123 | ||
| 121 | 124 | ||
| 122 | 125 | ||
| @@ -144,6 +147,7 @@ public class BusinessDataHandlerServiceImpl implements BusinessDataHandlerServic | @@ -144,6 +147,7 @@ public class BusinessDataHandlerServiceImpl implements BusinessDataHandlerServic | ||
| 144 | if (StringUtils.isBlank(businessType)) { | 147 | if (StringUtils.isBlank(businessType)) { |
| 145 | return; | 148 | return; |
| 146 | } | 149 | } |
| 150 | + String customerId = (String) variable.get("orderingUnit"); | ||
| 147 | // 获取审核结果 | 151 | // 获取审核结果 |
| 148 | String flowStatus = instance.getFlowStatus(); | 152 | String flowStatus = instance.getFlowStatus(); |
| 149 | // 更新业务数据 | 153 | // 更新业务数据 |
| @@ -161,7 +165,7 @@ public class BusinessDataHandlerServiceImpl implements BusinessDataHandlerServic | @@ -161,7 +165,7 @@ public class BusinessDataHandlerServiceImpl implements BusinessDataHandlerServic | ||
| 161 | handleStandardContractAuditData(flowStatus, businessId); | 165 | handleStandardContractAuditData(flowStatus, businessId); |
| 162 | break; | 166 | break; |
| 163 | case "PURCHASE_ORDER": | 167 | case "PURCHASE_ORDER": |
| 164 | - handlePurchaseOrderData(flowStatus, businessId); | 168 | + handlePurchaseOrderData(flowStatus, businessId, customerId); |
| 165 | break; | 169 | break; |
| 166 | case "ORDER_CANCELLATION": | 170 | case "ORDER_CANCELLATION": |
| 167 | handlePurchaseOrderRevokeData(flowStatus, businessId, variable); | 171 | handlePurchaseOrderRevokeData(flowStatus, businessId, variable); |
| @@ -266,7 +270,7 @@ public class BusinessDataHandlerServiceImpl implements BusinessDataHandlerServic | @@ -266,7 +270,7 @@ public class BusinessDataHandlerServiceImpl implements BusinessDataHandlerServic | ||
| 266 | * | 270 | * |
| 267 | * @param businessId 业务ID | 271 | * @param businessId 业务ID |
| 268 | */ | 272 | */ |
| 269 | - private void handlePurchaseOrderData(String flowStatus, String businessId) { | 273 | + private void handlePurchaseOrderData(String flowStatus, String businessId, String customerId) { |
| 270 | if (FlowInstanceStatus.APPROVE_PASS.getCode().equals(flowStatus) | 274 | if (FlowInstanceStatus.APPROVE_PASS.getCode().equals(flowStatus) |
| 271 | || FlowInstanceStatus.FINISH.getCode().equals(flowStatus)) { | 275 | || FlowInstanceStatus.FINISH.getCode().equals(flowStatus)) { |
| 272 | purchaseOrderInfoService.updateStatus(businessId, "PASS"); | 276 | purchaseOrderInfoService.updateStatus(businessId, "PASS"); |
| @@ -275,6 +279,7 @@ public class BusinessDataHandlerServiceImpl implements BusinessDataHandlerServic | @@ -275,6 +279,7 @@ public class BusinessDataHandlerServiceImpl implements BusinessDataHandlerServic | ||
| 275 | || FlowInstanceStatus.TERMINATION.getCode().equals(flowStatus)) { | 279 | || FlowInstanceStatus.TERMINATION.getCode().equals(flowStatus)) { |
| 276 | purchaseOrderInfoService.updateStatus(businessId, "REFUSE"); | 280 | purchaseOrderInfoService.updateStatus(businessId, "REFUSE"); |
| 277 | } | 281 | } |
| 282 | + receiptLedgerInfoService.autoFreeze(customerId); | ||
| 278 | } | 283 | } |
| 279 | 284 | ||
| 280 | /** | 285 | /** |
| @@ -32,10 +32,12 @@ import com.lframework.xingyun.sc.service.order.PurchaseOrderInfoService; | @@ -32,10 +32,12 @@ import com.lframework.xingyun.sc.service.order.PurchaseOrderInfoService; | ||
| 32 | import com.lframework.xingyun.sc.service.order.PurchaseOrderLineService; | 32 | import com.lframework.xingyun.sc.service.order.PurchaseOrderLineService; |
| 33 | import com.lframework.xingyun.sc.service.shipments.ShipmentsOrderInfoService; | 33 | import com.lframework.xingyun.sc.service.shipments.ShipmentsOrderInfoService; |
| 34 | import com.lframework.xingyun.sc.service.shipments.ShipmentsPlanDetailService; | 34 | import com.lframework.xingyun.sc.service.shipments.ShipmentsPlanDetailService; |
| 35 | +import com.lframework.xingyun.sc.service.shipments.car.DraftRequestCarTicketService; | ||
| 35 | import com.lframework.xingyun.sc.utils.CommonUtil; | 36 | import com.lframework.xingyun.sc.utils.CommonUtil; |
| 36 | import com.lframework.xingyun.sc.vo.ledger.receipt.*; | 37 | import com.lframework.xingyun.sc.vo.ledger.receipt.*; |
| 37 | import lombok.extern.slf4j.Slf4j; | 38 | import lombok.extern.slf4j.Slf4j; |
| 38 | import org.apache.commons.collections4.CollectionUtils; | 39 | import org.apache.commons.collections4.CollectionUtils; |
| 40 | +import org.apache.commons.lang3.BooleanUtils; | ||
| 39 | import org.apache.commons.lang3.StringUtils; | 41 | import org.apache.commons.lang3.StringUtils; |
| 40 | import org.springframework.transaction.annotation.Transactional; | 42 | import org.springframework.transaction.annotation.Transactional; |
| 41 | import com.lframework.xingyun.sc.mappers.ReceiptLedgerInfoMapper; | 43 | import com.lframework.xingyun.sc.mappers.ReceiptLedgerInfoMapper; |
| @@ -77,6 +79,8 @@ public class ReceiptLedgerInfoServiceImpl extends BaseMpServiceImpl<ReceiptLedge | @@ -77,6 +79,8 @@ public class ReceiptLedgerInfoServiceImpl extends BaseMpServiceImpl<ReceiptLedge | ||
| 77 | private ShipmentsPlanDetailService shipmentsPlanDetailService; | 79 | private ShipmentsPlanDetailService shipmentsPlanDetailService; |
| 78 | @Resource | 80 | @Resource |
| 79 | private ContractDistributorStandardService contractDistributorStandardService; | 81 | private ContractDistributorStandardService contractDistributorStandardService; |
| 82 | + @Resource | ||
| 83 | + private DraftRequestCarTicketService draftRequestCarTicketService; | ||
| 80 | 84 | ||
| 81 | 85 | ||
| 82 | // 定义状态优先级顺序(从高到低) | 86 | // 定义状态优先级顺序(从高到低) |
| @@ -783,6 +787,71 @@ public class ReceiptLedgerInfoServiceImpl extends BaseMpServiceImpl<ReceiptLedge | @@ -783,6 +787,71 @@ public class ReceiptLedgerInfoServiceImpl extends BaseMpServiceImpl<ReceiptLedge | ||
| 783 | baseMapper.batchAdd(ledgerInfoList); | 787 | baseMapper.batchAdd(ledgerInfoList); |
| 784 | } | 788 | } |
| 785 | 789 | ||
| 790 | + @Override | ||
| 791 | + public Map<String, Boolean> checkIsDebt(String customerId) { | ||
| 792 | + LocalDateTime endTime = LocalDateTime.now(); | ||
| 793 | + LocalDateTime startTime = LocalDate.now().atStartOfDay(); | ||
| 794 | + | ||
| 795 | + LambdaQueryWrapper<ReceiptLedgerInfo> queryWrapper = Wrappers.lambdaQuery(ReceiptLedgerInfo.class); | ||
| 796 | + queryWrapper.eq(ReceiptLedgerInfo::getDelFlag, Boolean.FALSE) | ||
| 797 | + .ge(ReceiptLedgerInfo::getCreateTime, startTime) | ||
| 798 | + .le(ReceiptLedgerInfo::getCreateTime, endTime); | ||
| 799 | + if (StringUtils.isNotBlank(customerId)) { | ||
| 800 | + queryWrapper.eq(ReceiptLedgerInfo::getCustomerId, customerId); | ||
| 801 | + } | ||
| 802 | + List<ReceiptLedgerInfo> ledgerInfoList = getBaseMapper().selectList(queryWrapper); | ||
| 803 | + if (CollectionUtils.isEmpty(ledgerInfoList)) { | ||
| 804 | + return Collections.emptyMap(); | ||
| 805 | + } | ||
| 806 | + Map<String, Boolean> result = new HashMap<>(); | ||
| 807 | + for (ReceiptLedgerInfo ledgerInfo : ledgerInfoList) { | ||
| 808 | + BigDecimal endAccountReceivable = ledgerInfo.getEndAccountReceivable(); | ||
| 809 | + if (endAccountReceivable.compareTo(BigDecimal.ZERO) > 0) { | ||
| 810 | + result.put(ledgerInfo.getCustomerId(), Boolean.TRUE); | ||
| 811 | + } else { | ||
| 812 | + result.put(ledgerInfo.getCustomerId(), Boolean.FALSE); | ||
| 813 | + } | ||
| 814 | + } | ||
| 815 | + return result; | ||
| 816 | + } | ||
| 817 | + | ||
| 818 | + @Override | ||
| 819 | + @Transactional(rollbackFor = Exception.class) | ||
| 820 | + public void autoFreeze(String customerId) { | ||
| 821 | + Map<String, Boolean> debtCustomerMap = checkIsDebt(customerId); | ||
| 822 | + Set<String> customerIds = debtCustomerMap.keySet(); | ||
| 823 | + if (CollectionUtils.isEmpty(customerIds)) { | ||
| 824 | + return; | ||
| 825 | + } | ||
| 826 | + List<PurchaseOrderInfo> orderInfoList = purchaseOrderInfoService.listByCustomerIds(new ArrayList<>(customerIds)); | ||
| 827 | + if (CollectionUtils.isEmpty(orderInfoList)) { | ||
| 828 | + return; | ||
| 829 | + } | ||
| 830 | + // 按客户分组订货单 | ||
| 831 | + Map<String, List<String>> orderInfoMap = new HashMap<>(); | ||
| 832 | + for (PurchaseOrderInfo orderInfo : orderInfoList) { | ||
| 833 | + String orderingUnit = orderInfo.getOrderingUnit(); | ||
| 834 | + List<String> list = orderInfoMap.computeIfAbsent(orderingUnit, k -> new ArrayList<>()); | ||
| 835 | + if ("PRODUCTION".equals(orderInfo.getType())) { | ||
| 836 | + list.add(orderInfo.getId()); | ||
| 837 | + } | ||
| 838 | + } | ||
| 839 | + List<String> needFreezeOrderIds = new ArrayList<>(); | ||
| 840 | + for (String id : customerIds) { | ||
| 841 | + Boolean debt = debtCustomerMap.get(id); | ||
| 842 | + List<String> orderIds = orderInfoMap.get(id); | ||
| 843 | + if (BooleanUtils.isTrue(debt) && CollectionUtils.isNotEmpty(orderIds)) { | ||
| 844 | + needFreezeOrderIds.addAll(orderIds); | ||
| 845 | + } | ||
| 846 | + } | ||
| 847 | + // 冻结订货单 | ||
| 848 | + purchaseOrderInfoService.freeze(needFreezeOrderIds); | ||
| 849 | + // 冻结草稿要车单 | ||
| 850 | + draftRequestCarTicketService.freezeOrDeblocking(Boolean.TRUE, needFreezeOrderIds); | ||
| 851 | + // 删除已有发货计划(过滤已生成发货单的) | ||
| 852 | + shipmentsPlanDetailService.delByOrderIds(needFreezeOrderIds); | ||
| 853 | + } | ||
| 854 | + | ||
| 786 | 855 | ||
| 787 | /** | 856 | /** |
| 788 | * 从多个状态中获取最高优先级的状态 | 857 | * 从多个状态中获取最高优先级的状态 |
| @@ -629,6 +629,18 @@ public class PurchaseOrderInfoServiceImpl extends BaseMpServiceImpl<PurchaseOrde | @@ -629,6 +629,18 @@ public class PurchaseOrderInfoServiceImpl extends BaseMpServiceImpl<PurchaseOrde | ||
| 629 | } | 629 | } |
| 630 | 630 | ||
| 631 | @Override | 631 | @Override |
| 632 | + public void freeze(List<String> ids) { | ||
| 633 | + if (CollectionUtils.isEmpty(ids)) { | ||
| 634 | + return; | ||
| 635 | + } | ||
| 636 | + LambdaUpdateWrapper<PurchaseOrderInfo> updateWrapper = Wrappers.lambdaUpdate(PurchaseOrderInfo.class); | ||
| 637 | + updateWrapper.set(PurchaseOrderInfo::isFreeze, Boolean.TRUE) | ||
| 638 | + .in(PurchaseOrderInfo::getId, ids); | ||
| 639 | + getBaseMapper().update(updateWrapper); | ||
| 640 | + } | ||
| 641 | + | ||
| 642 | + | ||
| 643 | + @Override | ||
| 632 | public void cleanCacheByKey(Serializable key) { | 644 | public void cleanCacheByKey(Serializable key) { |
| 633 | 645 | ||
| 634 | } | 646 | } |
| @@ -511,6 +511,22 @@ public class ShipmentsPlanDetailServiceImpl extends BaseMpServiceImpl<ShipmentsP | @@ -511,6 +511,22 @@ public class ShipmentsPlanDetailServiceImpl extends BaseMpServiceImpl<ShipmentsP | ||
| 511 | getBaseMapper().deleteBatchIds(ids); | 511 | getBaseMapper().deleteBatchIds(ids); |
| 512 | } | 512 | } |
| 513 | 513 | ||
| 514 | + @Override | ||
| 515 | + public void delByOrderIds(List<String> orderIds) { | ||
| 516 | + if (CollectionUtils.isEmpty(orderIds)) { | ||
| 517 | + return; | ||
| 518 | + } | ||
| 519 | + LambdaUpdateWrapper<ShipmentsPlanDetail> updateWrapper = Wrappers.lambdaUpdate(ShipmentsPlanDetail.class); | ||
| 520 | + updateWrapper.eq(ShipmentsPlanDetail::getPreShipments, Boolean.FALSE) | ||
| 521 | + .in(ShipmentsPlanDetail::getOrderId, orderIds) | ||
| 522 | + .and(wq -> wq | ||
| 523 | + .isNull(ShipmentsPlanDetail::getShipmentOrderId) | ||
| 524 | + .or() | ||
| 525 | + .eq(ShipmentsPlanDetail::getShipmentOrderId, "") | ||
| 526 | + ); | ||
| 527 | + getBaseMapper().delete(updateWrapper); | ||
| 528 | + } | ||
| 529 | + | ||
| 514 | 530 | ||
| 515 | /** | 531 | /** |
| 516 | * 封装发货计划明细数据 | 532 | * 封装发货计划明细数据 |
| @@ -276,6 +276,14 @@ public class DraftRequestCarTicketServiceImpl extends BaseMpServiceImpl<DraftReq | @@ -276,6 +276,14 @@ public class DraftRequestCarTicketServiceImpl extends BaseMpServiceImpl<DraftReq | ||
| 276 | } | 276 | } |
| 277 | 277 | ||
| 278 | @Override | 278 | @Override |
| 279 | + public void freezeOrDeblocking(Boolean flag, List<String> orderIds) { | ||
| 280 | + LambdaUpdateWrapper<DraftRequestCarTicket> updateWrapper = Wrappers.lambdaUpdate(DraftRequestCarTicket.class); | ||
| 281 | + updateWrapper.set(DraftRequestCarTicket::isFreeze, flag) | ||
| 282 | + .in(DraftRequestCarTicket::getPurchaseOrderId, orderIds); | ||
| 283 | + getBaseMapper().update(updateWrapper); | ||
| 284 | + } | ||
| 285 | + | ||
| 286 | + @Override | ||
| 279 | public void cleanCacheByKey(Serializable key) { | 287 | public void cleanCacheByKey(Serializable key) { |
| 280 | 288 | ||
| 281 | } | 289 | } |
| @@ -9,6 +9,7 @@ import com.lframework.starter.web.core.components.resp.PageResult; | @@ -9,6 +9,7 @@ import com.lframework.starter.web.core.components.resp.PageResult; | ||
| 9 | 9 | ||
| 10 | import java.time.LocalDate; | 10 | import java.time.LocalDate; |
| 11 | import java.util.List; | 11 | import java.util.List; |
| 12 | +import java.util.Map; | ||
| 12 | 13 | ||
| 13 | /** | 14 | /** |
| 14 | * 应收款台账明细 Service | 15 | * 应收款台账明细 Service |
| @@ -130,4 +131,16 @@ public interface ReceiptLedgerInfoService extends BaseMpService<ReceiptLedgerInf | @@ -130,4 +131,16 @@ public interface ReceiptLedgerInfoService extends BaseMpService<ReceiptLedgerInf | ||
| 130 | * @param vo | 131 | * @param vo |
| 131 | */ | 132 | */ |
| 132 | void depositReconciliation(UpdateReceiptLedgerInfoVo vo); | 133 | void depositReconciliation(UpdateReceiptLedgerInfoVo vo); |
| 134 | + | ||
| 135 | + /** | ||
| 136 | + * 检查客户是否欠款 | ||
| 137 | + * | ||
| 138 | + * @param customerId 客户ID | ||
| 139 | + */ | ||
| 140 | + Map<String, Boolean> checkIsDebt(String customerId); | ||
| 141 | + | ||
| 142 | + /** | ||
| 143 | + * 冻结欠款客户,管控发货 | ||
| 144 | + */ | ||
| 145 | + void autoFreeze(String customerId); | ||
| 133 | } | 146 | } |
| @@ -146,4 +146,12 @@ public interface PurchaseOrderInfoService extends BaseMpService<PurchaseOrderInf | @@ -146,4 +146,12 @@ public interface PurchaseOrderInfoService extends BaseMpService<PurchaseOrderInf | ||
| 146 | * @param ids | 146 | * @param ids |
| 147 | */ | 147 | */ |
| 148 | void batchUnblock(List<String> ids); | 148 | void batchUnblock(List<String> ids); |
| 149 | + | ||
| 150 | + /** | ||
| 151 | + * 冻结 | ||
| 152 | + * 支持批量操作 | ||
| 153 | + * | ||
| 154 | + * @param ids 主键ID集合 | ||
| 155 | + */ | ||
| 156 | + void freeze(List<String> ids); | ||
| 149 | } | 157 | } |
xingyun-sc/src/main/java/com/lframework/xingyun/sc/service/shipments/ShipmentsPlanDetailService.java
| @@ -146,4 +146,12 @@ public interface ShipmentsPlanDetailService extends BaseMpService<ShipmentsPlanD | @@ -146,4 +146,12 @@ public interface ShipmentsPlanDetailService extends BaseMpService<ShipmentsPlanD | ||
| 146 | * | 146 | * |
| 147 | */ | 147 | */ |
| 148 | void checkOutdated(); | 148 | void checkOutdated(); |
| 149 | + | ||
| 150 | + /** | ||
| 151 | + * 根据订货单ID批量删除 | ||
| 152 | + * 过滤已生成发货单的数据 | ||
| 153 | + * | ||
| 154 | + * @param orderIds 订货单ID集合 | ||
| 155 | + */ | ||
| 156 | + void delByOrderIds(List<String> orderIds); | ||
| 149 | } | 157 | } |
| @@ -77,4 +77,11 @@ public interface DraftRequestCarTicketService extends BaseMpService<DraftRequest | @@ -77,4 +77,11 @@ public interface DraftRequestCarTicketService extends BaseMpService<DraftRequest | ||
| 77 | * @return Map<String, DraftRequestCarTicket> | 77 | * @return Map<String, DraftRequestCarTicket> |
| 78 | */ | 78 | */ |
| 79 | List<DraftRequestCarTicket> listByOrderId(List<String> orderIds); | 79 | List<DraftRequestCarTicket> listByOrderId(List<String> orderIds); |
| 80 | + | ||
| 81 | + /** | ||
| 82 | + * 冻结或解封 | ||
| 83 | + * | ||
| 84 | + * @param flag 标识符 | ||
| 85 | + */ | ||
| 86 | + void freezeOrDeblocking(Boolean flag, List<String> orderIds); | ||
| 80 | } | 87 | } |
| @@ -190,7 +190,7 @@ | @@ -190,7 +190,7 @@ | ||
| 190 | from tbl_purchase_order_line ol | 190 | from tbl_purchase_order_line ol |
| 191 | inner join purchase_order_info o on ol.purchase_order_id = o.id | 191 | inner join purchase_order_info o on ol.purchase_order_id = o.id |
| 192 | left join base_data_customer c on o.ordering_unit = c.id | 192 | left join base_data_customer c on o.ordering_unit = c.id |
| 193 | - where ol.del_flag = false | 193 | + where ol.del_flag = false and o.freeze = false |
| 194 | and o.type = 'PRODUCTION' and o.examine_status = 'PASS' and o.status not in ('SHIPPED', 'DELIVERED', 'CANCEL') | 194 | and o.type = 'PRODUCTION' and o.examine_status = 'PASS' and o.status not in ('SHIPPED', 'DELIVERED', 'CANCEL') |
| 195 | and (o.revoke_status != 'UNDOING' or o.revoke_status is null) | 195 | and (o.revoke_status != 'UNDOING' or o.revoke_status is null) |
| 196 | and (o.spec_change_status != 'IN_PROGRESS' or o.spec_change_status is null) | 196 | and (o.spec_change_status != 'IN_PROGRESS' or o.spec_change_status is null) |