Browse Source

Revert inventory location filters and item+UoM grouped search.

Restore searchLatest to one latest inventory row per item. Report multi-value item codes unchanged.

Co-authored-by: Cursor <[email protected]>
production
CANCERYS\kw093 13 hours ago
parent
commit
942ec0d8e2
8 changed files with 30 additions and 249 deletions
  1. +0
    -17
      src/main/java/com/ffii/fpsms/modules/master/entity/WarehouseRepository.kt
  2. +2
    -111
      src/main/java/com/ffii/fpsms/modules/stock/entity/InventoryLotLineRepository.kt
  3. +0
    -30
      src/main/java/com/ffii/fpsms/modules/stock/entity/projection/InventoryStockUomSearchRow.kt
  4. +1
    -36
      src/main/java/com/ffii/fpsms/modules/stock/service/InventoryLotLineService.kt
  5. +24
    -38
      src/main/java/com/ffii/fpsms/modules/stock/service/InventoryService.kt
  6. +3
    -4
      src/main/java/com/ffii/fpsms/modules/stock/web/InventoryController.kt
  7. +0
    -7
      src/main/java/com/ffii/fpsms/modules/stock/web/model/SearchInventoryLotLineInfoRequest.kt
  8. +0
    -6
      src/main/java/com/ffii/fpsms/modules/stock/web/model/SearchInventoryRequest.kt

+ 0
- 17
src/main/java/com/ffii/fpsms/modules/master/entity/WarehouseRepository.kt View File

@@ -5,7 +5,6 @@ import com.ffii.fpsms.modules.master.entity.projections.WarehouseCombo
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.jpa.repository.Query
import org.springframework.data.repository.query.Param
import org.springframework.stereotype.Repository
import java.io.Serializable

@@ -23,22 +22,6 @@ interface WarehouseRepository : AbstractRepository<Warehouse, Long> {
fun findAllByIdIn(ids: List<Long>): List<Warehouse>;
fun findAllByCodeAndDeletedIsFalse(code: String): List<Warehouse>

/** FP-MTMS Version Checklist | Functions Ref. No. 19 | v1.0.1 | 2026-09-10 */
@Query(
"""
SELECT w FROM Warehouse w
WHERE w.deleted = false
AND (:storeId IS NULL OR :storeId = '' OR w.store_id = :storeId)
AND (:warehouse IS NULL OR :warehouse = '' OR w.warehouse = :warehouse)
AND (:area IS NULL OR :area = '' OR w.area = :area)
"""
)
fun findByLocationFilters(
@Param("storeId") storeId: String?,
@Param("warehouse") warehouse: String?,
@Param("area") area: String?,
): List<Warehouse>

@Query(
"""
SELECT COUNT(w) FROM Warehouse w


+ 2
- 111
src/main/java/com/ffii/fpsms/modules/stock/entity/InventoryLotLineRepository.kt View File

@@ -3,7 +3,6 @@ package com.ffii.fpsms.modules.stock.entity
import com.ffii.core.support.AbstractRepository
import com.ffii.fpsms.modules.stock.entity.projection.CurrentInventoryItemInfo
import com.ffii.fpsms.modules.stock.entity.projection.InventoryLotLineInfo
import com.ffii.fpsms.modules.stock.entity.projection.InventoryStockUomSearchRow
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.jpa.repository.Modifying
@@ -57,116 +56,8 @@ WHERE ill.id = :id

fun findInventoryLotLineInfoByInventoryLotItemIdIn(ids: List<Serializable>): List<InventoryLotLineInfo>

@Query(
"""
SELECT ill FROM InventoryLotLine ill
WHERE (:id IS NULL OR ill.inventoryLot.item.id = :id)
AND (:uomId IS NULL OR ill.stockUom.uom.id = :uomId)
ORDER BY ill.id DESC
"""
)
/** FP-MTMS Version Checklist | Functions Ref. No. 80 | v1.0.0 | 2026-09-10 */
fun findInventoryLotLineInfoByItemId(
@Param("id") id: Long?,
@Param("uomId") uomId: Long?,
pageable: Pageable,
): Page<InventoryLotLineInfo>

@Query(
"""
SELECT DISTINCT ill.inventoryLot.item.id
FROM InventoryLotLine ill
WHERE ill.deleted = false
AND ill.status = :status
AND ill.warehouse.id IN :warehouseIds
"""
)
fun findDistinctItemIdsByWarehouseIdsAndStatus(
@Param("warehouseIds") warehouseIds: List<Long>,
@Param("status") status: InventoryLotLineStatus,
): List<Long>

@Query(
"""
SELECT ill FROM InventoryLotLine ill
WHERE (:id IS NULL OR ill.inventoryLot.item.id = :id)
AND ill.warehouse.id IN :warehouseIds
AND (:uomId IS NULL OR ill.stockUom.uom.id = :uomId)
ORDER BY ill.id DESC
"""
)
/** FP-MTMS Version Checklist | Functions Ref. No. 80 | v1.0.0 | 2026-09-10 */
fun findInventoryLotLineInfoByItemIdAndWarehouseIdIn(
@Param("id") id: Long?,
@Param("warehouseIds") warehouseIds: List<Long>,
@Param("uomId") uomId: Long?,
pageable: Pageable,
): Page<InventoryLotLineInfo>

@Query(
value = """
SELECT
MAX(CAST(ill.id AS long)) AS id,
CAST(item.id AS long) AS itemId,
item.code AS itemCode,
item.name AS itemName,
item.type AS itemType,
SUM(COALESCE(ill.inQty, 0)) AS onHandQty,
SUM(COALESCE(ill.holdQty, 0)) AS onHoldQty,
SUM(COALESCE(ill.inQty, 0) - COALESCE(ill.outQty, 0) - COALESCE(ill.holdQty, 0)) AS availableQty,
CAST(uom.id AS long) AS uomId,
uom.code AS uomCode,
uom.udfudesc AS uomUdfudesc,
uom.udfShortDesc AS uomShortDesc,
SUM(COALESCE(ill.inQty, 0) - COALESCE(ill.outQty, 0) - COALESCE(ill.holdQty, 0)) AS qtyPerSmallestUnit,
MAX(item.latestMarketUnitPrice) AS latestMarketUnitPrice,
MAX(item.latestMupUpdatedDate) AS latestMupUpdatedDate
FROM InventoryLotLine ill
JOIN ill.inventoryLot il
JOIN il.item item
JOIN ill.stockUom su
JOIN su.uom uom
WHERE ill.deleted = false
AND il.deleted = false
AND item.deleted = false
AND ill.status = :status
AND (:code IS NULL OR :code = '' OR LOWER(item.code) LIKE LOWER(CONCAT('%', :code, '%')))
AND (:name IS NULL OR :name = '' OR LOWER(item.name) LIKE LOWER(CONCAT('%', :name, '%')))
AND (:type IS NULL OR :type = '' OR item.type = :type)
AND (:hasWarehouseFilter = false OR ill.warehouse.id IN :warehouseIds)
AND (:lotNo IS NULL OR :lotNo = '' OR il.lotNo = :lotNo)
GROUP BY item.id, item.code, item.name, item.type, uom.id, uom.code, uom.udfudesc, uom.udfShortDesc
ORDER BY item.code ASC, uom.udfudesc ASC
""",
countQuery = """
SELECT COUNT(DISTINCT CONCAT(CAST(item.id AS string), ':', CAST(uom.id AS string)))
FROM InventoryLotLine ill
JOIN ill.inventoryLot il
JOIN il.item item
JOIN ill.stockUom su
JOIN su.uom uom
WHERE ill.deleted = false
AND il.deleted = false
AND item.deleted = false
AND ill.status = :status
AND (:code IS NULL OR :code = '' OR LOWER(item.code) LIKE LOWER(CONCAT('%', :code, '%')))
AND (:name IS NULL OR :name = '' OR LOWER(item.name) LIKE LOWER(CONCAT('%', :name, '%')))
AND (:type IS NULL OR :type = '' OR item.type = :type)
AND (:hasWarehouseFilter = false OR ill.warehouse.id IN :warehouseIds)
AND (:lotNo IS NULL OR :lotNo = '' OR il.lotNo = :lotNo)
""",
)
/** FP-MTMS Version Checklist | Functions Ref. No. 19 | v1.0.1 | 2026-09-10 */
fun searchInventoryGroupedByStockUom(
@Param("code") code: String,
@Param("name") name: String,
@Param("type") type: String,
@Param("status") status: InventoryLotLineStatus,
@Param("hasWarehouseFilter") hasWarehouseFilter: Boolean,
@Param("warehouseIds") warehouseIds: List<Long>,
@Param("lotNo") lotNo: String?,
pageable: Pageable,
): Page<InventoryStockUomSearchRow>
@Query("select ill from InventoryLotLine ill where :id is null or ill.inventoryLot.item.id = :id order by ill.id desc")
fun findInventoryLotLineInfoByItemId(id: Long?, pageable: Pageable): Page<InventoryLotLineInfo>

@Query("""
SELECT ill FROM InventoryLotLine ill


+ 0
- 30
src/main/java/com/ffii/fpsms/modules/stock/entity/projection/InventoryStockUomSearchRow.kt View File

@@ -1,30 +0,0 @@
package com.ffii.fpsms.modules.stock.entity.projection

import java.math.BigDecimal
import java.time.LocalDateTime

/**
* Inventory search page: one row per item + lot-line stock UoM.
*/
interface InventoryStockUomSearchRow {
val id: Long?
val itemId: Long?
val itemCode: String?
val itemName: String?
val itemType: String?
val onHandQty: BigDecimal?
val onHoldQty: BigDecimal?
val unavailableQty: BigDecimal?
val availableQty: BigDecimal?
val uomId: Long?
val uomCode: String?
val uomUdfudesc: String?
val uomShortDesc: String?
val qtyPerSmallestUnit: BigDecimal?
val baseUom: String?
val price: BigDecimal?
val currencyName: String?
val status: String?
val latestMarketUnitPrice: Double?
val latestMupUpdatedDate: LocalDateTime?
}

+ 1
- 36
src/main/java/com/ffii/fpsms/modules/stock/service/InventoryLotLineService.kt View File

@@ -76,15 +76,9 @@ open class InventoryLotLineService(
return inventoryLotLineRepository.findInventoryLotLineInfoByInventoryLotItemIdIn(itemIds)
}

/** FP-MTMS Version Checklist | Functions Ref. No. 80 | v1.0.0 | 2026-09-10 */
open fun allInventoryLotLinesByPage(request: SearchInventoryLotLineInfoRequest): RecordsRes<InventoryLotLineInfo> {
val pageable = PageRequest.of(request.pageNum ?: 0, request.pageSize ?: 10);

val warehouseIds = resolveWarehouseIds(request.storeId, request.warehouse, request.area)
if (warehouseIds != null && warehouseIds.isEmpty()) {
return RecordsRes(emptyList(), 0)
}

val response = if (request.stockIssueBadItem == true) {
inventoryLotLineRepository.findStockIssueBadItemLotLinesByItemId(
request.itemId,
@@ -92,15 +86,8 @@ open class InventoryLotLineService(
listOf(InventoryLotLineStatus.AVAILABLE, InventoryLotLineStatus.UNAVAILABLE),
pageable,
)
} else if (warehouseIds != null) {
inventoryLotLineRepository.findInventoryLotLineInfoByItemIdAndWarehouseIdIn(
request.itemId,
warehouseIds,
request.uomId,
pageable,
)
} else {
inventoryLotLineRepository.findInventoryLotLineInfoByItemId(request.itemId, request.uomId, pageable)
inventoryLotLineRepository.findInventoryLotLineInfoByItemId(request.itemId, pageable)
}

val records = response.content
@@ -108,28 +95,6 @@ open class InventoryLotLineService(
return RecordsRes<InventoryLotLineInfo>(records, total.toInt());
}

/**
* FP-MTMS Version Checklist | Functions Ref. No. 80 | v1.0.0 | 2026-09-10
* Returns null when no location filter is set.
*/
private fun resolveWarehouseIds(
storeId: String?,
warehouse: String?,
area: String?,
): List<Long>? {
val trimmedStoreId = storeId?.trim()?.takeIf { it.isNotEmpty() }
val trimmedWarehouse = warehouse?.trim()?.takeIf { it.isNotEmpty() }
val trimmedArea = area?.trim()?.takeIf { it.isNotEmpty() }
if (trimmedStoreId == null && trimmedWarehouse == null && trimmedArea == null) {
return null
}
return warehouseRepository.findByLocationFilters(
trimmedStoreId,
trimmedWarehouse,
trimmedArea,
).mapNotNull { it.id }
}

open fun searchStockIssueBadItemLotLines(
request: SearchStockIssueBadItemLotLineRequest,
): RecordsRes<InventoryLotLineInfo> {


+ 24
- 38
src/main/java/com/ffii/fpsms/modules/stock/service/InventoryService.kt View File

@@ -17,7 +17,6 @@ import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderType
import com.ffii.fpsms.modules.stock.entity.*
import com.ffii.fpsms.modules.stock.entity.enum.InventoryLotLineStatus
import com.ffii.fpsms.modules.stock.entity.projection.InventoryInfo
import com.ffii.fpsms.modules.stock.entity.projection.InventoryStockUomSearchRow
import com.ffii.fpsms.modules.stock.web.model.SearchInventoryRequest
import org.apache.poi.ss.usermodel.Sheet
import org.apache.poi.ss.usermodel.Workbook
@@ -90,56 +89,43 @@ open class InventoryService(
}

/**
* FP-MTMS Version Checklist | Functions Ref. No. 19 | v1.0.1 | 2026-09-10
* Inventory search page: one row per item + lot-line stock UoM.
* FP-MTMS Version Checklist | Functions Ref. No. 19 | v1.0.0 | 2026-07-17
* Inventory search page only: no baseUnit/uomId filter;
* one inventory row per item (latest id). Shared getRecordByPage is unchanged.
*/
open fun searchInventoriesByLatestInventory(
request: SearchInventoryRequest,
): RecordsRes<InventoryStockUomSearchRow> {
): RecordsRes<InventoryInfo> {
val pageable = PageRequest.of(request.pageNum ?: 0, request.pageSize ?: 10)
val code = request.code?.trim().orEmpty()
val name = request.name?.trim().orEmpty()
val type = request.type?.trim().orEmpty()
val lotNo = request.lotNo?.trim()?.takeIf { it.isNotEmpty() }
val warehouseIds = resolveWarehouseIds(request.storeId, request.warehouse, request.area)
if (warehouseIds != null && warehouseIds.isEmpty()) {
return RecordsRes(emptyList(), 0)
}

val hasWarehouseFilter = warehouseIds != null
val response = inventoryLotLineRepository.searchInventoryGroupedByStockUom(
code = code,
name = name,
type = type,
status = InventoryLotLineStatus.AVAILABLE,
hasWarehouseFilter = hasWarehouseFilter,
warehouseIds = warehouseIds ?: listOf(-1L),
lotNo = lotNo,
pageable = pageable,
)
val response = if (lotNo != null) {
val itemIds = inventoryLotLineRepository.findDistinctItemIdsByLotNo(lotNo)
if (itemIds.isEmpty()) {
return RecordsRes(emptyList(), 0)
}
inventoryRepository.findLatestInventoryInfoByItemCodeContainsAndItemNameContainsAndItemTypeAndItemIdInAndDeletedIsFalse(
code = code,
name = name,
type = type,
itemIds = itemIds,
pageable = pageable,
)
} else {
inventoryRepository.findLatestInventoryInfoByItemCodeContainsAndItemNameContainsAndItemTypeAndDeletedIsFalse(
code = code,
name = name,
type = type,
pageable = pageable,
)
}

return RecordsRes(response.content, response.totalElements.toInt())
}

/** FP-MTMS Version Checklist | Functions Ref. No. 19 | v1.0.1 | 2026-09-10 */
private fun resolveWarehouseIds(
storeId: String?,
warehouse: String?,
area: String?,
): List<Long>? {
val trimmedStoreId = storeId?.trim()?.takeIf { it.isNotEmpty() }
val trimmedWarehouse = warehouse?.trim()?.takeIf { it.isNotEmpty() }
val trimmedArea = area?.trim()?.takeIf { it.isNotEmpty() }
if (trimmedStoreId == null && trimmedWarehouse == null && trimmedArea == null) {
return null
}
return warehouseRepository.findByLocationFilters(
trimmedStoreId,
trimmedWarehouse,
trimmedArea,
).mapNotNull { it.id }
}

open fun allInventoriesByItems(items: List<Items>): List<InventoryInfo>{
return inventoryRepository.findInventoryInfoByItemInAndDeletedIsFalse(items);
}


+ 3
- 4
src/main/java/com/ffii/fpsms/modules/stock/web/InventoryController.kt View File

@@ -2,7 +2,6 @@ package com.ffii.fpsms.modules.stock.web

import com.ffii.core.response.RecordsRes
import com.ffii.fpsms.modules.stock.entity.projection.InventoryInfo
import com.ffii.fpsms.modules.stock.entity.projection.InventoryStockUomSearchRow
import com.ffii.fpsms.modules.stock.service.InventoryService
import com.ffii.fpsms.modules.stock.web.model.SearchInventoryRequest
import org.springframework.web.bind.annotation.*
@@ -23,13 +22,13 @@ class InventoryController(
}

/**
* FP-MTMS Version Checklist | Functions Ref. No. 19 | v1.0.1 | 2026-09-10
* Inventory search page: one row per item + lot-line stock UoM, with optional location filters.
* FP-MTMS Version Checklist | Functions Ref. No. 19 | v1.0.0 | 2026-07-17
* Inventory search page only: latest inventory row per item, no baseUnit/uomId filter.
*/
@GetMapping("/searchLatest/getRecordByPage")
fun searchInventoriesByLatestInventory(
@ModelAttribute request: SearchInventoryRequest,
): RecordsRes<InventoryStockUomSearchRow> {
): RecordsRes<InventoryInfo> {
return inventoryService.searchInventoriesByLatestInventory(request)
}



+ 0
- 7
src/main/java/com/ffii/fpsms/modules/stock/web/model/SearchInventoryLotLineInfoRequest.kt View File

@@ -2,15 +2,8 @@ package com.ffii.fpsms.modules.stock.web.model

data class SearchInventoryLotLineInfoRequest(
val itemId: Long? = null,
val uomId: Long? = null,
val pageSize: Int?,
val pageNum: Int?,
/** When true: non-expired lots with inQty > outQty; includes available and unavailable status. */
val stockIssueBadItem: Boolean? = false,
/** Floor / store, e.g. 2F. Used by inventory location search. */
val storeId: String? = null,
/** Warehouse zone, e.g. W201. */
val warehouse: String? = null,
/** Area, e.g. #R. */
val area: String? = null,
)

+ 0
- 6
src/main/java/com/ffii/fpsms/modules/stock/web/model/SearchInventoryRequest.kt View File

@@ -5,12 +5,6 @@ data class SearchInventoryRequest(
val name: String,
val type: String,
val lotNo: String? = null,
/** Floor / store, e.g. 2F. Used by inventory location search. */
val storeId: String? = null,
/** Warehouse zone, e.g. W201. */
val warehouse: String? = null,
/** Area, e.g. #R. */
val area: String? = null,
val pageNum: Int?,
val pageSize: Int?
)

Loading…
Cancel
Save