This commit is contained in:
2026-08-20 18:22:20 +08:00
23 changed files with 401 additions and 48 deletions
@@ -7,9 +7,10 @@ import com.baiyue.lastleg.inboundplan.dto.ConvertInboundPlanRequest;
import com.baiyue.lastleg.inboundplan.dto.BatchFbaAppointmentRequest;
import com.baiyue.lastleg.inboundplan.dto.FbaAppointmentResponse;
import com.baiyue.lastleg.inboundplan.dto.InboundPlanResponse;
import com.baiyue.lastleg.inboundplan.dto.InboundPlanListResponse;
import com.baiyue.lastleg.inboundplan.dto.InboundPlanRequests;
import com.baiyue.lastleg.inboundplan.dto.InboundPlanSummary;
import com.baiyue.lastleg.inboundplan.dto.SupplementPalletRequest;
import com.baiyue.lastleg.inboundplan.dto.UpdateFbaAppointmentRequest;
import com.baiyue.lastleg.inboundplan.service.InboundFbaAppointmentService;
import com.baiyue.lastleg.inboundplan.service.InboundPlanService;
import com.baiyue.lastleg.security.OverseasPermissions;
@@ -25,24 +26,26 @@ import org.springframework.web.server.ResponseStatusException;
@RestController
@RequestMapping("/api/platform/v1/last-leg/inbound-plans")
@Tag(name = "Tail-leg inbound plans")
@Tag(name = "尾程入仓计划")
public class InboundPlanController {
@Resource private InboundPlanService service;
@Resource private InboundFbaAppointmentService appointmentService;
/** 按入仓计划明细分页查询可预约的入库订单行。 */
@GetMapping
@PreAuthorize("hasAuthority('" + OverseasPermissions.INBOUND_ORDER_LIST + "')")
@Operation(summary = "Page inbound plans without PDA, internal-event or outbound workflow fields", description = "Required authority: " + OverseasPermissions.INBOUND_ORDER_LIST)
public ApiResponse<PageResult<InboundPlanSummary>> page(
@Operation(summary = "分页查询入库订单", description = "Required authority: " + OverseasPermissions.INBOUND_ORDER_LIST)
public ApiResponse<PageResult<InboundPlanListResponse>> page(
@AuthenticationPrincipal AuthenticatedUser user,
@RequestParam(required = false) String customerId,
@RequestParam String customerId,
@Valid InboundPlanRequests.Query query) {
return ApiResponse.success(service.page(scope(user, customerId), query));
}
@PostMapping("/convert")
@PreAuthorize("hasAuthority('" + OverseasPermissions.INBOUND_ORDER_CREATE + "')")
@Operation(summary = "Convert one inbound order to one inbound plan", description = "Required authority: " + OverseasPermissions.INBOUND_ORDER_CREATE)
/** 将入仓单转换为入仓计划。 */
@Operation(summary = "入仓单转入仓计划", description = "Required authority: " + OverseasPermissions.INBOUND_ORDER_CREATE)
public ApiResponse<InboundPlanResponse> convert(@AuthenticationPrincipal AuthenticatedUser user,
@Valid @RequestBody ConvertInboundPlanRequest request) {
return ApiResponse.success(service.convert(scope(user, request.customerId()), request.orderId()));
@@ -50,7 +53,8 @@ public class InboundPlanController {
@GetMapping("/{planNo}")
@PreAuthorize("hasAuthority('" + OverseasPermissions.INBOUND_ORDER_LIST + "')")
@Operation(summary = "Get an inbound plan with lines, pallets and allocations", description = "Required authority: " + OverseasPermissions.INBOUND_ORDER_LIST)
/** 查询入仓计划及其明细、托盘和分配信息。 */
@Operation(summary = "查询入仓计划详情", description = "Required authority: " + OverseasPermissions.INBOUND_ORDER_LIST)
public ApiResponse<InboundPlanResponse> detail(@AuthenticationPrincipal AuthenticatedUser user,
@PathVariable String planNo,
@RequestParam(required = false) String customerId) {
@@ -59,7 +63,8 @@ public class InboundPlanController {
@PostMapping("/{planNo}/pallets/supplement")
@PreAuthorize("hasAuthority('" + OverseasPermissions.INBOUND_ORDER_EDIT + "')")
@Operation(summary = "Add one printable supplemental pallet for a plan destination warehouse", description = "Required authority: " + OverseasPermissions.INBOUND_ORDER_EDIT)
/** 为计划目标仓新增一个可打印的补充托盘。 */
@Operation(summary = "新增补充托盘", description = "Required authority: " + OverseasPermissions.INBOUND_ORDER_EDIT)
public ApiResponse<InboundPlanResponse.Pallet> supplementPallet(@AuthenticationPrincipal AuthenticatedUser user,
@PathVariable String planNo,
@Valid @RequestBody SupplementPalletRequest request) {
@@ -68,7 +73,8 @@ public class InboundPlanController {
@PostMapping("/fba-appointments/batch")
@PreAuthorize("hasAuthority('" + OverseasPermissions.INBOUND_ORDER_EDIT + "')")
@Operation(summary = "Create one FBA appointment for selected inbound lines", description = "Required authority: " + OverseasPermissions.INBOUND_ORDER_EDIT)
/** 为选中的入仓明细创建 FBA 预约。 */
@Operation(summary = "创建 FBA 预约", description = "Required authority: " + OverseasPermissions.INBOUND_ORDER_EDIT)
public ApiResponse<FbaAppointmentResponse> batchFbaAppointment(
@AuthenticationPrincipal AuthenticatedUser user,
@Valid @RequestBody BatchFbaAppointmentRequest request) {
@@ -79,7 +85,8 @@ public class InboundPlanController {
@GetMapping("/fba-appointments/{appointmentNo}")
@PreAuthorize("hasAuthority('" + OverseasPermissions.INBOUND_ORDER_LIST + "')")
@Operation(summary = "Get an FBA appointment and its selected inbound lines", description = "Required authority: " + OverseasPermissions.INBOUND_ORDER_LIST)
/** 查询 FBA 预约及其选中的入仓明细。 */
@Operation(summary = "查询 FBA 预约详情", description = "Required authority: " + OverseasPermissions.INBOUND_ORDER_LIST)
public ApiResponse<FbaAppointmentResponse> fbaAppointmentDetail(
@AuthenticationPrincipal AuthenticatedUser user,
@PathVariable String appointmentNo,
@@ -87,6 +94,17 @@ public class InboundPlanController {
return ApiResponse.success(appointmentService.detail(scope(user, customerId), appointmentNo));
}
@PutMapping("/fba-appointments/{appointmentNo}")
@PreAuthorize("hasAuthority('" + OverseasPermissions.INBOUND_ORDER_EDIT + "')")
@Operation(summary = "编辑 FBA 预约", description = "Required authority: " + OverseasPermissions.INBOUND_ORDER_EDIT)
public ApiResponse<FbaAppointmentResponse> updateFbaAppointment(
@AuthenticationPrincipal AuthenticatedUser user,
@PathVariable String appointmentNo,
@Valid @RequestBody UpdateFbaAppointmentRequest request) {
return ApiResponse.success(appointmentService.update(scope(user, request.customerId()),
appointmentNo, request));
}
private String scope(AuthenticatedUser user, String requestedCustomer) {
if (user == null || user.tenantId() == null) {
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
@@ -13,6 +13,7 @@ public record BatchFbaAppointmentRequest(
@NotEmpty @Size(max = 500) List<@NotNull Long> lineIds,
@NotBlank @Size(max = 64) String appointmentNo,
@NotNull LocalDateTime appointmentAt,
@NotNull LocalDateTime expectedLoadingAt,
@NotBlank @Size(max = 32) String appointmentStatus,
@Size(max = 1000) String appointmentRemark
) {
@@ -8,6 +8,7 @@ public record FbaAppointmentResponse(
String customerId,
String appointmentNo,
LocalDateTime appointmentAt,
LocalDateTime expectedLoadingAt,
String appointmentStatus,
String appointmentRemark,
String fbaWarehouseCode,
@@ -0,0 +1,30 @@
package com.baiyue.lastleg.inboundplan.dto;
import java.math.BigDecimal;
import java.time.LocalDateTime;
public record InboundPlanListResponse(
Long lineId,
Long planId,
String planNo,
String customerId,
Long orderId,
String inboundCode,
String orderType,
String warehouseId,
String warehouseCode,
String warehouseName,
String fbaWarehouseCode,
String customerName,
Integer packageCount,
BigDecimal volumeCbm,
Integer palletCount,
String status,
String appointmentNo,
String appointmentStatus,
LocalDateTime appointmentAt,
LocalDateTime expectedLoadingAt,
String dispatchStatus,
LocalDateTime createdAt
) {
}
@@ -2,6 +2,7 @@ package com.baiyue.lastleg.inboundplan.dto;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.Pattern;
import java.time.LocalDateTime;
@@ -17,8 +18,12 @@ public final class InboundPlanRequests {
String status,
String overseasWarehouseId,
String destinationWarehouseCode,
String warehouse,
String fbaWarehouseCode,
@Pattern(regexp = "UNAPPOINTED|PENDING|SUCCESS|FAILED|CANCELLED")
String appointmentStatus,
@Pattern(regexp = "UNSCHEDULED|SCHEDULED")
String dispatchStatus,
LocalDateTime createdFrom,
LocalDateTime createdTo
) {
@@ -0,0 +1,16 @@
package com.baiyue.lastleg.inboundplan.dto;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.time.LocalDateTime;
public record UpdateFbaAppointmentRequest(
String customerId,
@NotNull LocalDateTime appointmentAt,
@NotNull LocalDateTime expectedLoadingAt,
@NotBlank @Size(max = 32) String appointmentStatus,
@Size(max = 1000) String appointmentRemark
) {
}
@@ -15,6 +15,7 @@ public class InboundFbaAppointment {
private String customerId;
private String appointmentNo;
private LocalDateTime appointmentAt;
private LocalDateTime expectedLoadingAt;
private String appointmentStatus;
private String appointmentRemark;
private String fbaWarehouseCode;
@@ -0,0 +1,55 @@
package com.baiyue.lastleg.inboundplan.mapper;
import com.baiyue.lastleg.inboundplan.dto.InboundPlanListResponse;
import com.baiyue.lastleg.inboundplan.dto.InboundPlanRequests;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface InboundPlanQueryMapper {
String FROM_AND_FILTER = " FROM ll_inbound_plan_line l" +
" JOIN ll_inbound_plan p ON p.id=l.plan_id AND p.customer_id=#{customerId}" +
" JOIN ll_order o ON o.id=l.order_id AND o.customer_id=#{customerId}" +
" LEFT JOIN ll_inbound_fba_appointment_item ai ON ai.line_id=l.id" +
" AND ai.id=(SELECT ai2.id FROM ll_inbound_fba_appointment_item ai2" +
" JOIN ll_inbound_fba_appointment a2 ON a2.id=ai2.appointment_id" +
" WHERE ai2.line_id=l.id AND a2.customer_id=#{customerId}" +
" ORDER BY a2.created_at DESC,ai2.id DESC LIMIT 1)" +
" LEFT JOIN ll_inbound_fba_appointment a ON a.id=ai.appointment_id AND a.customer_id=#{customerId}" +
" WHERE 1=1" +
"<if test='q.planNo != null and q.planNo != \"\"'> AND p.plan_no LIKE CONCAT('%',#{q.planNo},'%')</if>" +
"<if test='q.inboundCode != null and q.inboundCode != \"\"'> AND l.inbound_code LIKE CONCAT('%',#{q.inboundCode},'%')</if>" +
"<if test='q.status != null and q.status != \"\"'> AND p.status=#{q.status}</if>" +
"<if test='q.overseasWarehouseId != null and q.overseasWarehouseId != \"\"'> AND l.destination_warehouse_id=#{q.overseasWarehouseId}</if>" +
"<if test='q.destinationWarehouseCode != null and q.destinationWarehouseCode != \"\"'> AND l.destination_warehouse_code=#{q.destinationWarehouseCode}</if>" +
"<if test='q.warehouse != null and q.warehouse != \"\"'> AND (l.destination_warehouse_id LIKE CONCAT('%',#{q.warehouse},'%') OR l.destination_warehouse_code LIKE CONCAT('%',#{q.warehouse},'%') OR l.destination_warehouse_name LIKE CONCAT('%',#{q.warehouse},'%'))</if>" +
"<if test='q.fbaWarehouseCode != null and q.fbaWarehouseCode != \"\"'> AND l.fba_warehouse_code LIKE CONCAT('%',#{q.fbaWarehouseCode},'%')</if>" +
"<choose><when test='q.appointmentStatus == \"UNAPPOINTED\"'> AND ai.id IS NULL</when>" +
"<when test='q.appointmentStatus != null and q.appointmentStatus != \"\"'> AND a.appointment_status=#{q.appointmentStatus}</when></choose>" +
"<choose><when test='q.dispatchStatus == \"SCHEDULED\"'> AND EXISTS (SELECT 1 FROM ll_dispatch_item di WHERE di.customer_id=#{customerId} AND di.order_id=l.order_id)</when>" +
"<when test='q.dispatchStatus == \"UNSCHEDULED\"'> AND NOT EXISTS (SELECT 1 FROM ll_dispatch_item di WHERE di.customer_id=#{customerId} AND di.order_id=l.order_id)</when></choose>" +
"<if test='q.createdFrom != null'> AND p.created_at &gt;=#{q.createdFrom}</if>" +
"<if test='q.createdTo != null'> AND p.created_at &lt;=#{q.createdTo}</if>";
@Select("<script>SELECT l.id lineId,p.id planId,p.plan_no planNo,p.customer_id customerId," +
"l.order_id orderId,l.inbound_code inboundCode,o.order_type orderType," +
"l.destination_warehouse_id warehouseId,l.destination_warehouse_code warehouseCode," +
"l.destination_warehouse_name warehouseName,l.fba_warehouse_code fbaWarehouseCode," +
"o.customer_name customerName,l.required_package_count packageCount,l.volume_cbm volumeCbm," +
"l.planned_pallet_count palletCount,p.status status,a.appointment_no appointmentNo," +
"a.appointment_status appointmentStatus,a.appointment_at appointmentAt," +
"a.expected_loading_at expectedLoadingAt," +
"CASE WHEN EXISTS (SELECT 1 FROM ll_dispatch_item di WHERE di.customer_id=#{customerId} AND di.order_id=l.order_id)" +
" THEN 'SCHEDULED' ELSE 'UNSCHEDULED' END dispatchStatus,p.created_at createdAt" +
FROM_AND_FILTER + " ORDER BY p.created_at DESC,l.id DESC LIMIT #{limit} OFFSET #{offset}</script>")
List<InboundPlanListResponse> rows(@Param("customerId") String customerId,
@Param("q") InboundPlanRequests.Query query,
@Param("offset") long offset,
@Param("limit") long limit);
@Select("<script>SELECT COUNT(*)" + FROM_AND_FILTER + "</script>")
long count(@Param("customerId") String customerId, @Param("q") InboundPlanRequests.Query query);
}
@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baiyue.lastleg.inboundplan.dto.BatchFbaAppointmentRequest;
import com.baiyue.lastleg.inboundplan.dto.FbaAppointmentResponse;
import com.baiyue.lastleg.inboundplan.dto.UpdateFbaAppointmentRequest;
import com.baiyue.lastleg.inboundplan.entity.InboundFbaAppointment;
import com.baiyue.lastleg.inboundplan.entity.InboundFbaAppointmentItem;
import com.baiyue.lastleg.inboundplan.mapper.InboundFbaAppointmentItemMapper;
@@ -44,7 +45,7 @@ public class InboundFbaAppointmentService {
InboundFbaAppointment existing = find(customerId, appointmentNo);
if (existing != null) {
return verifyReplay(existing, lineIds, request.appointmentAt(), status, remark);
return verifyReplay(existing, lineIds, request.appointmentAt(), request.expectedLoadingAt(), status, remark);
}
List<FbaAppointmentLineSnapshot> lines = queryMapper.lockLines(customerId, lineIds);
@@ -57,7 +58,7 @@ public class InboundFbaAppointmentService {
// The row locks serialize concurrent submissions selecting the same inbound lines.
existing = find(customerId, appointmentNo);
if (existing != null) {
return verifyReplay(existing, lineIds, request.appointmentAt(), status, remark);
return verifyReplay(existing, lineIds, request.appointmentAt(), request.expectedLoadingAt(), status, remark);
}
LocalDateTime now = LocalDateTime.now();
@@ -66,6 +67,7 @@ public class InboundFbaAppointmentService {
appointment.setCustomerId(customerId);
appointment.setAppointmentNo(appointmentNo);
appointment.setAppointmentAt(request.appointmentAt());
appointment.setExpectedLoadingAt(request.expectedLoadingAt());
appointment.setAppointmentStatus(status);
appointment.setAppointmentRemark(remark);
appointment.setFbaWarehouseCode(fbaWarehouseCode);
@@ -78,7 +80,7 @@ public class InboundFbaAppointmentService {
} catch (DuplicateKeyException exception) {
InboundFbaAppointment concurrent = find(customerId, appointmentNo);
if (concurrent != null) {
return verifyReplay(concurrent, lineIds, request.appointmentAt(), status, remark);
return verifyReplay(concurrent, lineIds, request.appointmentAt(), request.expectedLoadingAt(), status, remark);
}
throw exception;
}
@@ -108,12 +110,31 @@ public class InboundFbaAppointmentService {
return response(appointment, items(appointment.getId()));
}
@Transactional
public FbaAppointmentResponse update(String customerId, String appointmentNo,
UpdateFbaAppointmentRequest request) {
InboundFbaAppointment appointment = find(customerId,
requiredTrimmed(appointmentNo, "Appointment number is required"));
if (appointment == null) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "FBA appointment not found");
}
appointment.setAppointmentAt(request.appointmentAt());
appointment.setExpectedLoadingAt(request.expectedLoadingAt());
appointment.setAppointmentStatus(normalizeStatus(request.appointmentStatus()));
appointment.setAppointmentRemark(trimToNull(request.appointmentRemark()));
appointment.setUpdatedAt(LocalDateTime.now());
appointmentMapper.updateById(appointment);
return response(appointment, items(appointment.getId()));
}
private FbaAppointmentResponse verifyReplay(InboundFbaAppointment existing, List<Long> requestedLineIds,
LocalDateTime appointmentAt, String status, String remark) {
LocalDateTime appointmentAt, LocalDateTime expectedLoadingAt,
String status, String remark) {
List<InboundFbaAppointmentItem> existingItems = items(existing.getId());
Set<Long> existingLineIds = existingItems.stream()
.map(InboundFbaAppointmentItem::getLineId).collect(java.util.stream.Collectors.toSet());
if (!Objects.equals(existing.getAppointmentAt(), appointmentAt)
|| !Objects.equals(existing.getExpectedLoadingAt(), expectedLoadingAt)
|| !Objects.equals(existing.getAppointmentStatus(), status)
|| !Objects.equals(existing.getAppointmentRemark(), remark)
|| existingLineIds.size() != requestedLineIds.size()
@@ -149,7 +170,8 @@ public class InboundFbaAppointmentService {
private FbaAppointmentResponse response(InboundFbaAppointment appointment,
List<InboundFbaAppointmentItem> items) {
return new FbaAppointmentResponse(appointment.getId(), appointment.getCustomerId(),
appointment.getAppointmentNo(), appointment.getAppointmentAt(), appointment.getAppointmentStatus(),
appointment.getAppointmentNo(), appointment.getAppointmentAt(), appointment.getExpectedLoadingAt(),
appointment.getAppointmentStatus(),
appointment.getAppointmentRemark(), appointment.getFbaWarehouseCode(), appointment.getSelectedCount(),
appointment.getCreatedBy(), appointment.getCreatedAt(), appointment.getUpdatedAt(),
items.stream().map(item -> new FbaAppointmentResponse.Item(item.getId(), item.getPlanId(),
@@ -3,9 +3,9 @@ package com.baiyue.lastleg.inboundplan.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baiyue.common.core.api.PageResult;
import com.baiyue.lastleg.inboundplan.dto.InboundPlanListResponse;
import com.baiyue.lastleg.inboundplan.dto.InboundPlanRequests;
import com.baiyue.lastleg.inboundplan.dto.InboundPlanResponse;
import com.baiyue.lastleg.inboundplan.dto.InboundPlanSummary;
import com.baiyue.lastleg.inboundplan.dto.SupplementPalletRequest;
import com.baiyue.lastleg.inboundplan.entity.InboundPlan;
import com.baiyue.lastleg.inboundplan.entity.InboundPlanLine;
@@ -16,6 +16,7 @@ import com.baiyue.lastleg.inboundplan.mapper.InboundPlanMapper;
import com.baiyue.lastleg.inboundplan.mapper.InboundPlanOrderMapper;
import com.baiyue.lastleg.inboundplan.mapper.InboundPlanPalletAllocationMapper;
import com.baiyue.lastleg.inboundplan.mapper.InboundPlanPalletMapper;
import com.baiyue.lastleg.inboundplan.mapper.InboundPlanQueryMapper;
import com.baiyue.lastleg.inboundplan.model.SourceOrderSnapshot;
import jakarta.annotation.Resource;
import org.springframework.http.HttpStatus;
@@ -38,16 +39,18 @@ public class InboundPlanService {
@Resource private InboundPlanPalletMapper palletMapper;
@Resource private InboundPlanPalletAllocationMapper allocationMapper;
@Resource private InboundPlanOrderMapper orderMapper;
@Resource private InboundPlanQueryMapper queryMapper;
@Transactional(readOnly = true)
public PageResult<InboundPlanSummary> page(String customerId, InboundPlanRequests.Query query) {
public PageResult<InboundPlanListResponse> page(String customerId, InboundPlanRequests.Query query) {
if (query.createdFrom() != null && query.createdTo() != null
&& query.createdFrom().isAfter(query.createdTo())) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "createdFrom must not be after createdTo");
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
"createdFrom must not be after createdTo");
}
long total = planMapper.countRows(customerId, query);
List<InboundPlanSummary> records = total == 0 ? List.of()
: planMapper.pageRows(customerId, query, Math.multiplyExact(query.page() - 1, query.size()), query.size());
long total = queryMapper.count(customerId, query);
List<InboundPlanListResponse> records = total == 0 ? List.of() : queryMapper.rows(
customerId, query, (query.page() - 1) * query.size(), query.size());
long pages = total == 0 ? 0 : (total + query.size() - 1) / query.size();
return new PageResult<>(records, query.page(), query.size(), total, pages);
}
@@ -13,6 +13,7 @@ public record InventoryLedgerView(
String palletNo,
String batchNo,
String warehouseLocationCode,
String skuCode,
String businessType,
String businessOrderNo,
String outboundOrderNo,
@@ -21,6 +21,8 @@ public final class InventoryQueryRequests {
String businessOrderNo,
String skuCode,
String productName,
String batchNo,
String warehouseLocationCode,
String inventoryStatus
) {
public BalanceQuery {
@@ -44,6 +46,8 @@ public final class InventoryQueryRequests {
String outboundOrderNo,
String palletNo,
String batchNo,
String skuCode,
String warehouseLocationCode,
LocalDateTime occurredFrom,
LocalDateTime occurredTo
) {
@@ -21,6 +21,8 @@ public interface InventoryQueryMapper {
"<if test='q.businessOrderNo != null and q.businessOrderNo != \"\"'> AND EXISTS (SELECT 1 FROM ll_inventory_ledger l WHERE l.balance_id=ll_inventory_balance.id AND l.customer_id=#{customerId} AND l.business_order_no LIKE CONCAT('%',#{q.businessOrderNo},'%'))</if>" +
"<if test='q.skuCode != null and q.skuCode != \"\"'> AND sku_code LIKE CONCAT('%',#{q.skuCode},'%')</if>" +
"<if test='q.productName != null and q.productName != \"\"'> AND (product_name_zh LIKE CONCAT('%',#{q.productName},'%') OR product_name_en LIKE CONCAT('%',#{q.productName},'%'))</if>" +
"<if test='q.batchNo != null and q.batchNo != \"\"'> AND EXISTS (SELECT 1 FROM ll_inventory_position p WHERE p.balance_id=ll_inventory_balance.id AND p.customer_id=#{customerId} AND p.batch_no LIKE CONCAT('%',#{q.batchNo},'%'))</if>" +
"<if test='q.warehouseLocationCode != null and q.warehouseLocationCode != \"\"'> AND EXISTS (SELECT 1 FROM ll_inventory_position p WHERE p.balance_id=ll_inventory_balance.id AND p.customer_id=#{customerId} AND p.warehouse_location_code LIKE CONCAT('%',#{q.warehouseLocationCode},'%'))</if>" +
"<choose>" +
"<when test='q.inventoryStatus == \"AVAILABLE\"'> AND available_packages &gt; 0</when>" +
"<when test='q.inventoryStatus == \"HOLD\"'> AND hold_packages &gt; 0</when>" +
@@ -41,6 +43,8 @@ public interface InventoryQueryMapper {
"<if test='q.businessOrderNo != null and q.businessOrderNo != \"\"'> AND EXISTS (SELECT 1 FROM ll_inventory_ledger l WHERE l.balance_id=b.id AND l.customer_id=#{customerId} AND l.business_order_no LIKE CONCAT('%',#{q.businessOrderNo},'%'))</if>" +
"<if test='q.skuCode != null and q.skuCode != \"\"'> AND b.sku_code LIKE CONCAT('%',#{q.skuCode},'%')</if>" +
"<if test='q.productName != null and q.productName != \"\"'> AND (b.product_name_zh LIKE CONCAT('%',#{q.productName},'%') OR b.product_name_en LIKE CONCAT('%',#{q.productName},'%'))</if>" +
"<if test='q.batchNo != null and q.batchNo != \"\"'> AND EXISTS (SELECT 1 FROM ll_inventory_position p2 WHERE p2.balance_id=b.id AND p2.customer_id=#{customerId} AND p2.batch_no LIKE CONCAT('%',#{q.batchNo},'%'))</if>" +
"<if test='q.warehouseLocationCode != null and q.warehouseLocationCode != \"\"'> AND EXISTS (SELECT 1 FROM ll_inventory_position p2 WHERE p2.balance_id=b.id AND p2.customer_id=#{customerId} AND p2.warehouse_location_code LIKE CONCAT('%',#{q.warehouseLocationCode},'%'))</if>" +
"<choose>" +
"<when test='q.inventoryStatus == \"AVAILABLE\"'> AND b.available_packages &gt; 0</when>" +
"<when test='q.inventoryStatus == \"HOLD\"'> AND b.hold_packages &gt; 0</when>" +
@@ -83,6 +87,8 @@ public interface InventoryQueryMapper {
@Select("<script>SELECT * FROM ll_inventory_position WHERE customer_id=#{customerId} AND balance_id=#{balanceId}" +
"<if test='q.palletNo != null and q.palletNo != \"\"'> AND pallet_no LIKE CONCAT('%',#{q.palletNo},'%')</if>" +
"<if test='q.batchNo != null and q.batchNo != \"\"'> AND batch_no LIKE CONCAT('%',#{q.batchNo},'%')</if>" +
"<if test='q.skuCode != null and q.skuCode != \"\"'> AND sku_code LIKE CONCAT('%',#{q.skuCode},'%')</if>" +
"<if test='q.warehouseLocationCode != null and q.warehouseLocationCode != \"\"'> AND warehouse_location_code LIKE CONCAT('%',#{q.warehouseLocationCode},'%')</if>" +
"<choose>" +
"<when test='q.inventoryStatus == \"AVAILABLE\"'> AND available_packages &gt; 0</when>" +
"<when test='q.inventoryStatus == \"HOLD\"'> AND hold_packages &gt; 0</when>" +
@@ -142,7 +142,7 @@ public class InventoryQueryService {
return new InventoryLedgerView(value.getId(), value.getCustomerId(), value.getOverseasWarehouseId(),
value.getDestinationWarehouseCode(), value.getInboundOrderId(), value.getInboundOrderNo(), value.getInboundCode(),
value.getPalletNo(), value.getBatchNo(),
value.getWarehouseLocationCode(), value.getBusinessType(), value.getBusinessOrderNo(),
value.getWarehouseLocationCode(), value.getSkuCode(), value.getBusinessType(), value.getBusinessOrderNo(),
value.getOutboundOrderNo(), value.getStatusFrom(), value.getStatusTo(),
number(value.getBeforePackages()), number(value.getDeltaPackages()), number(value.getAfterPackages()),
value.getOperatorId(), value.getRemark(), value.getOccurredAt());