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