| @@ -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 | ||||