@@ -55,4 +55,41 @@ interface JobOrderRepository : AbstractRepository<JobOrder, Long> { | |||||
""" | """ | ||||
) | ) | ||||
fun findJobOrderDetailById(id: Long): JobOrderDetailWithJsonString?; | fun findJobOrderDetailById(id: Long): JobOrderDetailWithJsonString?; | ||||
@Query( | |||||
nativeQuery = true, | |||||
value = """ | |||||
select | |||||
jo.id, | |||||
jo.code, | |||||
b.name, | |||||
jo.reqQty, | |||||
uc2.udfudesc as uom, | |||||
json_arrayagg( | |||||
json_object( | |||||
'id', jobm.id, | |||||
'code', i.code, | |||||
'name', i.name, | |||||
'lotNo', il.lotNo, | |||||
'reqQty', jobm.reqQty, | |||||
'uom', uc.udfudesc, | |||||
'status', jobm.status | |||||
) | |||||
) as pickLines, | |||||
jo.status | |||||
from job_order jo | |||||
left join bom b on b.id = jo.bomId | |||||
left join item_uom iu on b.itemId = iu.itemId and iu.salesUnit = true | |||||
left join uom_conversion uc2 on uc2.id = iu.uomId | |||||
left join job_order_bom_material jobm on jo.id = jobm.jobOrderId | |||||
left join items i on i.id = jobm.itemId | |||||
left join uom_conversion uc on uc.id = jobm.uomId | |||||
left join stock_out_line sol on sol.id = jobm.stockOutLineId | |||||
left join inventory_lot_line ill on ill.id = sol.inventoryLotLineId | |||||
left join inventory_lot il on il.id = ill.inventoryLotId | |||||
where jo.code = :code | |||||
group by jo.id, uc2.udfudesc | |||||
limit 1 | |||||
""" | |||||
) | |||||
fun findJobOrderDetailByCode(code: String): JobOrderDetailWithJsonString?; | |||||
} | } |
@@ -67,6 +67,23 @@ open class JobOrderService( | |||||
status = sqlResult.status | status = sqlResult.status | ||||
) | ) | ||||
} | } | ||||
open fun jobOrderDetailByCode(code: String): JobOrderDetail { | |||||
val sqlResult = jobOrderRepository.findJobOrderDetailByCode(code) ?: throw NoSuchElementException("Job Order not found: $code"); | |||||
val type = object : TypeToken<List<JobOrderDetailPickLine>>() {}.type | |||||
val jsonResult = sqlResult.pickLines?.let { GsonUtils.stringToJson<List<JobOrderDetailPickLine>>(it, type) } | |||||
return JobOrderDetail( | |||||
id = sqlResult.id, | |||||
code = sqlResult.code, | |||||
name = sqlResult.name, | |||||
reqQty = sqlResult.reqQty, | |||||
uom = sqlResult.uom, | |||||
pickLines = jsonResult, | |||||
status = sqlResult.status | |||||
) | |||||
} | |||||
open fun assignJobNo(): String { | open fun assignJobNo(): String { | ||||
val suffixFormat = "%03d" | val suffixFormat = "%03d" | ||||
val pattern = "yyyyMMdd" | val pattern = "yyyyMMdd" | ||||
@@ -37,6 +37,11 @@ class JobOrderController( | |||||
return jobOrderService.jobOrderDetail(id); | return jobOrderService.jobOrderDetail(id); | ||||
} | } | ||||
@GetMapping("/detailByCode/{code}") | |||||
fun jobOrderDetailByCode(@PathVariable code: String): JobOrderDetail { | |||||
return jobOrderService.jobOrderDetailByCode(code); | |||||
} | |||||
@PostMapping("/release") | @PostMapping("/release") | ||||
fun releaseJobOrder(@Valid @RequestBody request: JobOrderReleaseRequest): MessageResponse { | fun releaseJobOrder(@Valid @RequestBody request: JobOrderReleaseRequest): MessageResponse { | ||||
return jobOrderService.releaseJobOrder(request) | return jobOrderService.releaseJobOrder(request) | ||||
@@ -37,10 +37,12 @@ open class ItemsService( | |||||
+ " i.id, " | + " i.id, " | ||||
+ " concat(i.code , ' - ' , i.name) as label, " | + " concat(i.code , ' - ' , i.name) as label, " | ||||
+ " uc.id as uomId, " | + " uc.id as uomId, " | ||||
+ " uc.code as uom " | |||||
+ " uc.code as uom, " | |||||
+ " COALESCE(inv.onHandQty, 0) - COALESCE(inv.onHoldQty, 0) - COALESCE(inv.unavailableQty, 0) as currentStockBalance " | |||||
+ " from items i " | + " from items i " | ||||
+ " left join item_uom iu on iu.itemId = i.id and iu.deleted = false and iu.salesUnit = true " | + " left join item_uom iu on iu.itemId = i.id and iu.deleted = false and iu.salesUnit = true " | ||||
+ " left join uom_conversion uc on uc.id = iu.uomId " | + " left join uom_conversion uc on uc.id = iu.uomId " | ||||
+ " left join inventory inv on inv.itemId = i.id " | |||||
+ " where i.deleted = false " | + " where i.deleted = false " | ||||
+ " and i.type = :type " | + " and i.type = :type " | ||||
) | ) | ||||
@@ -127,7 +127,85 @@ open class PickOrderService( | |||||
val total = response.totalElements | val total = response.totalElements | ||||
return RecordsRes<PickOrderInfo>(records, total.toInt()) | return RecordsRes<PickOrderInfo>(records, total.toInt()) | ||||
} | } | ||||
open fun getPickOrdersWithStockBalanceByPage(request: SearchPickOrderRequest): RecordsRes<GetPickOrderInfo> { | |||||
val pageable = PageRequest.of(request.pageNum ?: 0, request.pageSize ?: 10); | |||||
// First get the basic pick order info for pagination | |||||
val response = pickOrderRepository.findPickOrderInfoByConditionsAndPageable( | |||||
code = request.code ?: "all", | |||||
targetDateFrom = localDateTimeParse(request.targetDateFrom), | |||||
targetDateTo = localDateTimeParse(request.targetDateTo), | |||||
type = request.type ?: "all", | |||||
status = request.status ?: "all", | |||||
itemName = request.itemName ?: "all", | |||||
pageable = pageable | |||||
) | |||||
val records = response.content | |||||
val pickOrderIds = records.mapNotNull { it.id } | |||||
if (pickOrderIds.isEmpty()) { | |||||
return RecordsRes(emptyList(), 0) | |||||
} | |||||
// Get full pick orders with relationships | |||||
val fullPickOrders = pickOrderRepository.findAllByIdIn(pickOrderIds) | |||||
// Get all item IDs | |||||
val itemIds = fullPickOrders | |||||
.flatMap { it.pickOrderLines } | |||||
.mapNotNull { it.item?.id } | |||||
.distinct() | |||||
// Get inventory data | |||||
val today = LocalDate.now() | |||||
val zero = BigDecimal.ZERO | |||||
val inventories = if (itemIds.isNotEmpty()) { | |||||
inventoryLotLineService | |||||
.allInventoryLotLinesByItemIdIn(itemIds) | |||||
.filter { it.status == InventoryLotLineStatus.AVAILABLE.value } | |||||
.filter { (it.inQty ?: zero).minus(it.outQty ?: zero).minus(it.holdQty ?: zero) > zero } | |||||
.filter { it.expiryDate.isAfter(today) || it.expiryDate.isEqual(today) } | |||||
.groupBy { it.item?.id } | |||||
} else { | |||||
emptyMap() | |||||
} | |||||
// Build response (removed suggestions) | |||||
val pickOrderInfos = fullPickOrders.map { po -> | |||||
val pickOrderLineInfos = po.pickOrderLines.map { pol -> | |||||
val inventory = pol.item?.id?.let { inventories[it] } | |||||
GetPickOrderLineInfo( | |||||
id = pol.id, | |||||
itemId = pol.item?.id, | |||||
itemCode = pol.item?.code, | |||||
itemName = pol.item?.name, | |||||
availableQty = inventory?.sumOf { i -> (i.availableQty ?: zero) }, | |||||
requiredQty = pol.qty, | |||||
uomCode = pol.uom?.code, | |||||
uomDesc = pol.uom?.udfudesc, | |||||
suggestedList = emptyList() // Empty list since you don't need suggestions | |||||
) | |||||
} | |||||
GetPickOrderInfo( | |||||
id = po.id, | |||||
code = po.code, | |||||
targetDate = po.targetDate, | |||||
type = po.type?.value, | |||||
status = po.status?.value, | |||||
pickOrderLines = pickOrderLineInfos | |||||
) | |||||
} | |||||
val total = response.totalElements | |||||
return RecordsRes(pickOrderInfos, total.toInt()) | |||||
} | |||||
open fun getConsoPickOrderList(args: MutableMap<String, Any>): List<Map<String, Any>> { | open fun getConsoPickOrderList(args: MutableMap<String, Any>): List<Map<String, Any>> { | ||||
val sql = StringBuilder( | val sql = StringBuilder( | ||||
"select" | "select" | ||||
@@ -145,6 +223,9 @@ open class PickOrderService( | |||||
if (args.containsKey("status")) { | if (args.containsKey("status")) { | ||||
sql.append(" AND po.status = :status "); | sql.append(" AND po.status = :status "); | ||||
} | } | ||||
if (args.containsKey("assignTo")) { | |||||
sql.append(" AND po.assignTo = :assignTo "); | |||||
} | |||||
sql.append(" group by po.consoCode, po.releasedDate, po.status, po.assignTo ") | sql.append(" group by po.consoCode, po.releasedDate, po.status, po.assignTo ") | ||||
return jdbcDao.queryForList(sql.toString(), args); | return jdbcDao.queryForList(sql.toString(), args); | ||||
} | } | ||||
@@ -45,11 +45,17 @@ class PickOrderController( | |||||
fun allPickOrdersByPage(@ModelAttribute request: SearchPickOrderRequest): RecordsRes<PickOrderInfo> { | fun allPickOrdersByPage(@ModelAttribute request: SearchPickOrderRequest): RecordsRes<PickOrderInfo> { | ||||
return pickOrderService.allPickOrdersByPage(request); | return pickOrderService.allPickOrdersByPage(request); | ||||
} | } | ||||
@GetMapping("/getRecordByPageWithStock") | |||||
fun getPickOrdersWithStockBalanceByPage(@ModelAttribute request: SearchPickOrderRequest): RecordsRes<GetPickOrderInfo> { | |||||
return pickOrderService.getPickOrdersWithStockBalanceByPage(request); | |||||
} | |||||
@GetMapping("/getRecordByPage-conso") | @GetMapping("/getRecordByPage-conso") | ||||
fun allConsoPickOrdersByPage(request: HttpServletRequest): RecordsRes<Map<String, Any>> { | fun allConsoPickOrdersByPage(request: HttpServletRequest): RecordsRes<Map<String, Any>> { | ||||
val criteriaArgs = CriteriaArgsBuilder.withRequest(request) | val criteriaArgs = CriteriaArgsBuilder.withRequest(request) | ||||
.addStringLike("consoCode") | .addStringLike("consoCode") | ||||
.addString("status") | .addString("status") | ||||
.addString("assignTo") | |||||
.build() | .build() | ||||
val pageSize = request.getParameter("pageSize")?.toIntOrNull() ?: 10 // Default to 10 if not provided | val pageSize = request.getParameter("pageSize")?.toIntOrNull() ?: 10 // Default to 10 if not provided | ||||
val pageNum = request.getParameter("pageNum")?.toIntOrNull() ?: 1 // Default to 1 if not provided | val pageNum = request.getParameter("pageNum")?.toIntOrNull() ?: 1 // Default to 1 if not provided | ||||