| @@ -46,6 +46,56 @@ interface InventoryRepository: AbstractRepository<Inventory, Long> { | |||
| pageable: Pageable | |||
| ): Page<InventoryInfo> | |||
| /** | |||
| * Inventory search page only: no baseUnit/uomId match required; | |||
| * one row per item = latest inventory id (no larger id for same item). | |||
| */ | |||
| @Query( | |||
| """ | |||
| SELECT i FROM Inventory i | |||
| WHERE (:code IS NULL OR :code = '' OR LOWER(i.item.code) LIKE LOWER(CONCAT('%', :code, '%'))) | |||
| AND (:name IS NULL OR :name = '' OR LOWER(i.item.name) LIKE LOWER(CONCAT('%', :name, '%'))) | |||
| AND (:type IS NULL OR :type = '' OR i.item.type = :type) | |||
| AND i.deleted = false | |||
| AND NOT EXISTS ( | |||
| SELECT 1 FROM Inventory i2 | |||
| WHERE i2.item.id = i.item.id | |||
| AND i2.deleted = false | |||
| AND i2.id > i.id | |||
| ) | |||
| """ | |||
| ) | |||
| fun findLatestInventoryInfoByItemCodeContainsAndItemNameContainsAndItemTypeAndDeletedIsFalse( | |||
| code: String, | |||
| name: String, | |||
| type: String, | |||
| pageable: Pageable, | |||
| ): Page<InventoryInfo> | |||
| @Query( | |||
| """ | |||
| SELECT i FROM Inventory i | |||
| WHERE (:code IS NULL OR :code = '' OR LOWER(i.item.code) LIKE LOWER(CONCAT('%', :code, '%'))) | |||
| AND (:name IS NULL OR :name = '' OR LOWER(i.item.name) LIKE LOWER(CONCAT('%', :name, '%'))) | |||
| AND (:type IS NULL OR :type = '' OR i.item.type = :type) | |||
| AND i.item.id IN :itemIds | |||
| AND i.deleted = false | |||
| AND NOT EXISTS ( | |||
| SELECT 1 FROM Inventory i2 | |||
| WHERE i2.item.id = i.item.id | |||
| AND i2.deleted = false | |||
| AND i2.id > i.id | |||
| ) | |||
| """ | |||
| ) | |||
| fun findLatestInventoryInfoByItemCodeContainsAndItemNameContainsAndItemTypeAndItemIdInAndDeletedIsFalse( | |||
| code: String, | |||
| name: String, | |||
| type: String, | |||
| itemIds: List<Long>, | |||
| pageable: Pageable, | |||
| ): Page<InventoryInfo> | |||
| fun findInventoryInfoByItemIdInAndDeletedIsFalse(itemIds: List<Serializable>): List<InventoryInfo> | |||
| fun findInventoryInfoByItemInAndDeletedIsFalse(items: List<Items>): List<InventoryInfo> | |||
| @@ -88,6 +88,43 @@ open class InventoryService( | |||
| return RecordsRes<InventoryInfo>(records, total.toInt()); | |||
| } | |||
| /** | |||
| * Inventory search page only: no baseUnit/uomId filter; | |||
| * one inventory row per item (MAX id). Shared getRecordByPage is unchanged. | |||
| */ | |||
| open fun searchInventoriesByLatestInventory( | |||
| request: SearchInventoryRequest, | |||
| ): 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 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()) | |||
| } | |||
| open fun allInventoriesByItems(items: List<Items>): List<InventoryInfo>{ | |||
| return inventoryRepository.findInventoryInfoByItemInAndDeletedIsFalse(items); | |||
| } | |||
| @@ -4,12 +4,7 @@ import com.ffii.core.response.RecordsRes | |||
| import com.ffii.fpsms.modules.stock.entity.projection.InventoryInfo | |||
| import com.ffii.fpsms.modules.stock.service.InventoryService | |||
| import com.ffii.fpsms.modules.stock.web.model.SearchInventoryRequest | |||
| import org.springframework.core.io.ByteArrayResource | |||
| import org.springframework.core.io.Resource | |||
| import org.springframework.http.HttpHeaders | |||
| import org.springframework.http.ResponseEntity | |||
| import org.springframework.web.bind.annotation.* | |||
| import java.time.LocalDate | |||
| @RestController | |||
| @RequestMapping("/inventory") | |||
| @@ -26,14 +21,16 @@ class InventoryController( | |||
| return inventoryService.allInventoriesByPage(request); | |||
| } | |||
| /** Inventory search page only: latest inventory row per item, no baseUnit/uomId filter. */ | |||
| @GetMapping("/searchLatest/getRecordByPage") | |||
| fun searchInventoriesByLatestInventory( | |||
| @ModelAttribute request: SearchInventoryRequest, | |||
| ): RecordsRes<InventoryInfo> { | |||
| return inventoryService.searchInventoriesByLatestInventory(request) | |||
| } | |||
| @PostMapping("/import-bom") | |||
| fun importInventory() { | |||
| val reportResult = inventoryService.importInventoryExcel() | |||
| // val filename = "bom_excel_issue_log_${LocalDate.now()}.xlsx" | |||
| // return ResponseEntity.ok() | |||
| // .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"$filename\"") | |||
| // .header(HttpHeaders.CONTENT_TYPE, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") | |||
| // .body(ByteArrayResource(reportResult)) | |||
| } | |||
| } | |||
| } | |||