| @@ -46,6 +46,56 @@ interface InventoryRepository: AbstractRepository<Inventory, Long> { | |||||
| pageable: Pageable | pageable: Pageable | ||||
| ): Page<InventoryInfo> | ): 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 findInventoryInfoByItemIdInAndDeletedIsFalse(itemIds: List<Serializable>): List<InventoryInfo> | ||||
| fun findInventoryInfoByItemInAndDeletedIsFalse(items: List<Items>): List<InventoryInfo> | fun findInventoryInfoByItemInAndDeletedIsFalse(items: List<Items>): List<InventoryInfo> | ||||
| @@ -88,6 +88,43 @@ open class InventoryService( | |||||
| return RecordsRes<InventoryInfo>(records, total.toInt()); | 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>{ | open fun allInventoriesByItems(items: List<Items>): List<InventoryInfo>{ | ||||
| return inventoryRepository.findInventoryInfoByItemInAndDeletedIsFalse(items); | 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.entity.projection.InventoryInfo | ||||
| import com.ffii.fpsms.modules.stock.service.InventoryService | import com.ffii.fpsms.modules.stock.service.InventoryService | ||||
| import com.ffii.fpsms.modules.stock.web.model.SearchInventoryRequest | 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 org.springframework.web.bind.annotation.* | ||||
| import java.time.LocalDate | |||||
| @RestController | @RestController | ||||
| @RequestMapping("/inventory") | @RequestMapping("/inventory") | ||||
| @@ -26,14 +21,16 @@ class InventoryController( | |||||
| return inventoryService.allInventoriesByPage(request); | 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") | @PostMapping("/import-bom") | ||||
| fun importInventory() { | fun importInventory() { | ||||
| val reportResult = inventoryService.importInventoryExcel() | 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)) | |||||
| } | } | ||||
| } | |||||
| } | |||||