@@ -55,4 +55,41 @@ interface JobOrderRepository : AbstractRepository<JobOrder, Long> { | |||
""" | |||
) | |||
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 | |||
) | |||
} | |||
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 { | |||
val suffixFormat = "%03d" | |||
val pattern = "yyyyMMdd" | |||
@@ -37,6 +37,11 @@ class JobOrderController( | |||
return jobOrderService.jobOrderDetail(id); | |||
} | |||
@GetMapping("/detailByCode/{code}") | |||
fun jobOrderDetailByCode(@PathVariable code: String): JobOrderDetail { | |||
return jobOrderService.jobOrderDetailByCode(code); | |||
} | |||
@PostMapping("/release") | |||
fun releaseJobOrder(@Valid @RequestBody request: JobOrderReleaseRequest): MessageResponse { | |||
return jobOrderService.releaseJobOrder(request) | |||
@@ -37,10 +37,12 @@ open class ItemsService( | |||
+ " i.id, " | |||
+ " concat(i.code , ' - ' , i.name) as label, " | |||
+ " 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 " | |||
+ " 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 inventory inv on inv.itemId = i.id " | |||
+ " where i.deleted = false " | |||
+ " and i.type = :type " | |||
) | |||
@@ -127,7 +127,85 @@ open class PickOrderService( | |||
val total = response.totalElements | |||
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>> { | |||
val sql = StringBuilder( | |||
"select" | |||
@@ -145,6 +223,9 @@ open class PickOrderService( | |||
if (args.containsKey("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 ") | |||
return jdbcDao.queryForList(sql.toString(), args); | |||
} | |||
@@ -45,11 +45,17 @@ class PickOrderController( | |||
fun allPickOrdersByPage(@ModelAttribute request: SearchPickOrderRequest): RecordsRes<PickOrderInfo> { | |||
return pickOrderService.allPickOrdersByPage(request); | |||
} | |||
@GetMapping("/getRecordByPageWithStock") | |||
fun getPickOrdersWithStockBalanceByPage(@ModelAttribute request: SearchPickOrderRequest): RecordsRes<GetPickOrderInfo> { | |||
return pickOrderService.getPickOrdersWithStockBalanceByPage(request); | |||
} | |||
@GetMapping("/getRecordByPage-conso") | |||
fun allConsoPickOrdersByPage(request: HttpServletRequest): RecordsRes<Map<String, Any>> { | |||
val criteriaArgs = CriteriaArgsBuilder.withRequest(request) | |||
.addStringLike("consoCode") | |||
.addString("status") | |||
.addString("assignTo") | |||
.build() | |||
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 | |||