diff --git a/src/main/java/com/ffii/fpsms/m18/service/M18DeliveryOrderService.kt b/src/main/java/com/ffii/fpsms/m18/service/M18DeliveryOrderService.kt index 4532091..dc95196 100644 --- a/src/main/java/com/ffii/fpsms/m18/service/M18DeliveryOrderService.kt +++ b/src/main/java/com/ffii/fpsms/m18/service/M18DeliveryOrderService.kt @@ -63,7 +63,7 @@ open class M18DeliveryOrderService( val dateTo = request.modifiedDateTo?.let { LocalDateTime.parse(it, formatter).toLocalDate().toString() } - val lastModifyDateConds = + val lastDateConds = //"lastModifyDate=largerOrEqual=${request.modifiedDateFrom ?: lastModifyDateStart}=and=lastModifyDate=lessOrEqual=${request.modifiedDateTo ?: lastModifyDateEnd}" "dDate=largerOrEqual=${dateFrom ?: lastModifyDateStart}=and=dDate=lessOrEqual=${dateTo ?: lastModifyDateEnd}" @@ -74,7 +74,7 @@ open class M18DeliveryOrderService( "venId=equal=", "=or=" ) - val shopPoConds = "(${shopPoBuyers})=and=(${shopPoSupplier})=and=(${lastModifyDateConds})" + val shopPoConds = "(${shopPoBuyers})=and=(${shopPoSupplier})=and=(${lastDateConds})" println("shopPoConds: ${shopPoConds}") val shopPoParams = M18PurchaseOrderListRequest( params = null, diff --git a/src/main/java/com/ffii/fpsms/m18/service/M18PurchaseOrderService.kt b/src/main/java/com/ffii/fpsms/m18/service/M18PurchaseOrderService.kt index ef5e6c9..5a9367e 100644 --- a/src/main/java/com/ffii/fpsms/m18/service/M18PurchaseOrderService.kt +++ b/src/main/java/com/ffii/fpsms/m18/service/M18PurchaseOrderService.kt @@ -24,6 +24,7 @@ import org.slf4j.LoggerFactory import org.springframework.stereotype.Service import java.time.LocalDateTime import kotlin.reflect.full.memberProperties +import java.time.format.DateTimeFormatter @Service open class M18PurchaseOrderService( @@ -57,8 +58,16 @@ open class M18PurchaseOrderService( // Include material po, oem po open fun getPurchaseOrdersWithType(request: M18CommonRequest): M18PurchaseOrderListResponseWithType? { val purchaseOrders = M18PurchaseOrderListResponseWithType(mutableListOf()) - val lastModifyDateConds = - "lastModifyDate=largerOrEqual=${request.modifiedDateFrom ?: lastModifyDateStart}=and=lastModifyDate=lessOrEqual=${request.modifiedDateTo ?: lastModifyDateEnd}" + val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") + val dateFrom = request.modifiedDateFrom?.let { + LocalDateTime.parse(it, formatter).toLocalDate().toString() + } + val dateTo = request.modifiedDateTo?.let { + LocalDateTime.parse(it, formatter).toLocalDate().toString() + } + val lastDateConds = + //"lastModifyDate=largerOrEqual=${request.modifiedDateFrom ?: lastModifyDateStart}=and=lastModifyDate=lessOrEqual=${request.modifiedDateTo ?: lastModifyDateEnd}" + "dDate=largerOrEqual=${dateFrom ?: lastModifyDateStart}=and=dDate=lessOrEqual=${dateTo ?: lastModifyDateEnd}" // Material PO val materialPoBuyers = commonUtils.listToString(listOf(m18Config.BEID_PP, m18Config.BEID_PF), "beId=equal=", "=or=") @@ -67,7 +76,7 @@ open class M18PurchaseOrderService( "venId=unequal=", "=or=" ) - val materialPoConds = "(${materialPoBuyers})=and=(${materialPoSupplierNot})=and=(${lastModifyDateConds})" + val materialPoConds = "(${materialPoBuyers})=and=(${materialPoSupplierNot})=and=(${lastDateConds})" println("materialPoConds: ${materialPoConds}") val materialPoParams = M18PurchaseOrderListRequest( params = null, diff --git a/src/main/java/com/ffii/fpsms/modules/common/scheduler/service/SchedulerService.kt b/src/main/java/com/ffii/fpsms/modules/common/scheduler/service/SchedulerService.kt index 596d20d..16c8837 100644 --- a/src/main/java/com/ffii/fpsms/modules/common/scheduler/service/SchedulerService.kt +++ b/src/main/java/com/ffii/fpsms/modules/common/scheduler/service/SchedulerService.kt @@ -153,13 +153,24 @@ open class SchedulerService( logger.info("Daily Scheduler - PO") val currentTime = LocalDateTime.now() val today = currentTime.toLocalDate().atStartOfDay() - val yesterday = today.minusDays(1L) + /* val yesterday = today.minusDays(1L) val request = M18CommonRequest( modifiedDateTo = today.format(dataStringFormat), modifiedDateFrom = yesterday.format(dataStringFormat) + )*/ + val tmr = today.plusDays(1L) + var request = M18CommonRequest( + modifiedDateTo = tmr.format(dataStringFormat), + modifiedDateFrom = tmr.format(dataStringFormat) ) m18PurchaseOrderService.savePurchaseOrders(request); - m18DeliveryOrderService.saveDeliveryOrders(request); + + //dDate from tmr to tmr + var requestDO = M18CommonRequest( + modifiedDateTo = tmr.format(dataStringFormat), + modifiedDateFrom = tmr.format(dataStringFormat) + ) + m18DeliveryOrderService.saveDeliveryOrders(requestDO); // logger.info("today: ${today.format(dataStringFormat)}") // logger.info("yesterday: ${yesterday.format(dataStringFormat)}") } diff --git a/src/main/java/com/ffii/fpsms/modules/master/entity/ProductionScheduleRepository.kt b/src/main/java/com/ffii/fpsms/modules/master/entity/ProductionScheduleRepository.kt index 47f54cb..6dba219 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/entity/ProductionScheduleRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/entity/ProductionScheduleRepository.kt @@ -52,7 +52,7 @@ interface ProductionScheduleRepository : AbstractRepository>, lineMats: List>): ByteArray { + val workbook = XSSFWorkbook() + + // 1. Group Production Lines by Date + val groupedData = lines.groupBy { + val produceAt = it["produceAt"] + when (produceAt) { + is LocalDateTime -> produceAt.toLocalDate().toString() + is java.sql.Timestamp -> produceAt.toLocalDateTime().toLocalDate().toString() + else -> produceAt?.toString()?.substring(0, 10) ?: "Unknown_Date" + } + } + + // 2. Define Header Style + val headerStyle = workbook.createCellStyle().apply { + fillForegroundColor = IndexedColors.GREY_25_PERCENT.index + fillPattern = FillPatternType.SOLID_FOREGROUND + val font = workbook.createFont() + font.bold = true + setFont(font) + } + + // 3. Create Production Worksheets + groupedData.forEach { (dateKey, dailyLines) -> + val sheetName = dateKey.replace("[/\\\\?*:\\[\\]]".toRegex(), "-") + val sheet = workbook.createSheet(sheetName) + + val headers = listOf("Item Name", "Avg Qty Last Month", "Stock Qty", "Days Left", "Output Qty", "Batch Need", "Priority") + val headerRow = sheet.createRow(0) + headers.forEachIndexed { i, title -> + val cell = headerRow.createCell(i) + cell.setCellValue(title) + cell.setCellStyle(headerStyle) + } + + dailyLines.forEachIndexed { index, line -> + val row = sheet.createRow(index + 1) + row.createCell(0).setCellValue(line["itemName"]?.toString() ?: "") + row.createCell(1).setCellValue(asDouble(line["avgQtyLastMonth"])) + row.createCell(2).setCellValue(asDouble(line["stockQty"])) + row.createCell(3).setCellValue(asDouble(line["daysLeft"])) + row.createCell(4).setCellValue(asDouble(line["outputdQty"])) // Note: Matching your snippet's "outputdQty" key + row.createCell(5).setCellValue(asDouble(line["batchNeed"])) + row.createCell(6).setCellValue(asDouble(line["itemPriority"])) + } + + for (i in headers.indices) { sheet.autoSizeColumn(i) } + } + + // 4. Create Material Summary Worksheet + val matSheet = workbook.createSheet("Material Summary") + val matHeaders = listOf( + "Mat Code", "Mat Name", "Required Qty", "Total Qty Need", + "UoM", "Purchased Qty", "On Hand Qty", "Unavailable Qty", + "Related Item Code", "Related Item Name" + ) + + val matHeaderRow = matSheet.createRow(0) + matHeaders.forEachIndexed { i, title -> + matHeaderRow.createCell(i).apply { + setCellValue(title) + setCellStyle(headerStyle) + } + } + + lineMats.forEachIndexed { index, rowData -> + val row = matSheet.createRow(index + 1) + + val totalNeed = asDouble(rowData["totalMatQtyNeed"]) + val purchased = asDouble(rowData["purchasedQty"]) + val onHand = asDouble(rowData["onHandQty"]) + + // Calculation: Required Qty = totalMatQtyNeed - purchasedQty - onHandQty (minimum 0) + val requiredQty = (totalNeed - purchased - onHand).coerceAtLeast(0.0) + + row.createCell(0).setCellValue(rowData["matCode"]?.toString() ?: "") + row.createCell(1).setCellValue(rowData["matName"]?.toString() ?: "") + row.createCell(2).setCellValue(requiredQty) + row.createCell(3).setCellValue(totalNeed) + row.createCell(4).setCellValue(rowData["uomName"]?.toString() ?: "") + row.createCell(5).setCellValue(purchased) + row.createCell(6).setCellValue(onHand) + row.createCell(7).setCellValue(asDouble(rowData["unavailableQty"])) + row.createCell(8).setCellValue(rowData["itemCode"]?.toString() ?: "") + row.createCell(9).setCellValue(rowData["itemName"]?.toString() ?: "") + } + + for (i in matHeaders.indices) { matSheet.autoSizeColumn(i) } + + // 5. Finalize and Return + val out = ByteArrayOutputStream() + workbook.use { it.write(out) } + return out.toByteArray() + } + + private fun asDouble(value: Any?): Double { + return when (value) { + is Number -> value.toDouble() + is String -> value.toDoubleOrNull() ?: 0.0 + else -> 0.0 + } + } + + //====================細排相關 START====================// + open fun searchExportProdSchedule(fromDate: LocalDate): List> { + + val args = mapOf( + "fromDate" to fromDate, + ) + + val sql = """ + select + it.code as itemCode, + it.name as itemName, + ps.produceAt, + psl.* + + from production_schedule_line psl + left join production_schedule ps on psl.prodScheduleId = ps.id + left join items it on psl.itemId = it.id + + where ps.produceAt >= :fromDate + + and ps.id = (select id from production_schedule where produceAt = ps.produceAt order by id desc limit 1) + + order by ps.produceAt asc, psl.itemPriority desc, itemCode asc + """; + + + return jdbcDao.queryForList(sql, args); + + } + + + open fun searchExportProdScheduleMaterial(fromDate: LocalDate): List> { + + val args = mapOf( + "fromDate" to fromDate, + ) + + val sql = """ + select + group_concat(distinct it.code) as itemCode, + group_concat(distinct it.name) as itemName, + itm.code as matCode, + itm.name as matName, + sum(bm.qty * psl.batchNeed) as totalMatQtyNeed, + iv.onHandQty, + iv.unavailableQty, + (select sum(qty) from purchase_order_line + left join purchase_order on purchase_order_line.purchaseOrderId = purchase_order.id + where purchase_order_line.itemId = itm.id and date(purchase_order.estimatedArrivalDate) >= date(now()) and purchase_order.completeDate is null) as purchasedQty, + bm.uomName, + min(ps.produceAt) as toProdcueFrom, + max(ps.produceAt) as toProdcueAt, + psl.* + + from production_schedule_line psl + left join production_schedule ps on psl.prodScheduleId = ps.id + left join items it on psl.itemId = it.id + left join bom on it.id = bom.itemId + left join bom_material bm on bom.id = bm.bomId + left join items itm on bm.itemId = itm.id + left join inventory iv on itm.id = iv.itemId + + where ps.produceAt >= :fromDate + and ps.id = (select id from production_schedule where produceAt = ps.produceAt order by id desc limit 1) + -- and it.code = 'PP2236' + -- order by ps.produceAt asc, psl.itemPriority desc, itemCode asc + group by matCode + """; + + + return jdbcDao.queryForList(sql, args); + + } + } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/master/web/ProductionScheduleController.kt b/src/main/java/com/ffii/fpsms/modules/master/web/ProductionScheduleController.kt index 42ac896..57b8d4e 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/web/ProductionScheduleController.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/web/ProductionScheduleController.kt @@ -23,7 +23,10 @@ import java.time.format.DateTimeFormatter import java.util.HashMap import kotlin.collections.component1 import kotlin.collections.component2 - +import org.springframework.http.ResponseEntity +import org.springframework.http.HttpHeaders +import org.springframework.http.MediaType +import org.springframework.web.bind.annotation.GetMapping @RestController @RequestMapping("/productionSchedule") @@ -228,4 +231,20 @@ class ProductionScheduleController( throw RuntimeException("Error generate schedule: ${e.message}", e) } } + + @PostMapping( + value = ["/export-prod-schedule"], + produces = ["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"] + ) + + fun exportProdSchedule(): ResponseEntity { + val data = productionScheduleService.searchExportProdSchedule(LocalDate.now()) + val dataMat = productionScheduleService.searchExportProdScheduleMaterial(LocalDate.now()) + val excelContent = productionScheduleService.exportProdScheduleToExcel(data, dataMat) + + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=production_schedule.xlsx") + .contentType(MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")) + .body(excelContent) + } } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/productProcess/service/ProductProcessService.kt b/src/main/java/com/ffii/fpsms/modules/productProcess/service/ProductProcessService.kt index f4f3102..38a2805 100644 --- a/src/main/java/com/ffii/fpsms/modules/productProcess/service/ProductProcessService.kt +++ b/src/main/java/com/ffii/fpsms/modules/productProcess/service/ProductProcessService.kt @@ -684,7 +684,7 @@ open class ProductProcessService( } } - open fun createProductProcessByJobOrderId(jobOrderId: Long): MessageResponse { + open fun createProductProcessByJobOrderId(jobOrderId: Long, priority: Int? = 50): MessageResponse { val jobOrder = jobOrderRepository.findById(jobOrderId).orElse(null) val bom = bomRepository.findById(jobOrder?.bom?.id ?: 0L).orElse(null) // val bom = jobOrder.bom.let { bomRepository.findById(it).orElse(null) } @@ -701,7 +701,7 @@ open class ProductProcessService( this.status = ProductProcessStatus.PENDING this.date = jobOrder?.planEnd?.toLocalDate() this.bom = bom - this.productionPriority = 50 + this.productionPriority = priority ?: 50 } productProcessRepository.save(productProcess)