@@ -31,6 +31,8 @@ dependencies { | |||||
implementation 'org.springframework.security:spring-security-ldap' | implementation 'org.springframework.security:spring-security-ldap' | ||||
implementation 'org.liquibase:liquibase-core' | implementation 'org.liquibase:liquibase-core' | ||||
implementation 'com.google.code.gson:gson:2.8.5' | implementation 'com.google.code.gson:gson:2.8.5' | ||||
implementation("org.apache.pdfbox:pdfbox:2.0.29") | |||||
implementation("org.apache.pdfbox:fontbox:2.0.34") | |||||
// // https://mvnrepository.com/artifact/org.springdoc/springdoc-openapi-starter-webmvc-ui | // // https://mvnrepository.com/artifact/org.springdoc/springdoc-openapi-starter-webmvc-ui | ||||
// implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.8") | // implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.8") | ||||
@@ -0,0 +1,109 @@ | |||||
package com.ffii.core.utils | |||||
import org.apache.pdfbox.pdmodel.PDDocument | |||||
import org.apache.pdfbox.rendering.PDFRenderer | |||||
import org.apache.pdfbox.rendering.ImageType | |||||
import java.awt.image.BufferedImage | |||||
import java.io.File | |||||
import java.io.OutputStream | |||||
import java.net.Socket | |||||
import java.util.* | |||||
/** | |||||
* Utility class for generating and sending print jobs to a Zebra printer using ZPL. | |||||
* This class requires the 'org.apache.pdfbox:pdfbox' dependency to be included in your project. | |||||
*/ | |||||
open class ZebraPrinterUtil { | |||||
companion object { | |||||
/** | |||||
* Converts the first page of a PDF document into a ZPL command and sends it to the specified printer. | |||||
* | |||||
* @param pdfFile The PDF file to be printed. | |||||
* @param printerIp The IP address of the Zebra printer. | |||||
* @param printerPort The port of the Zebra printer, typically 9100. | |||||
* @throws Exception if there is an error during file processing or printing. | |||||
*/ | |||||
@JvmStatic | |||||
fun printPdfToZebra(pdfFile: File, printerIp: String, printerPort: Int) { | |||||
// Check if the file exists and is readable | |||||
if (!pdfFile.exists() || !pdfFile.canRead()) { | |||||
throw IllegalArgumentException("Error: File not found or not readable at path: ${pdfFile.absolutePath}") | |||||
} | |||||
try { | |||||
// 1. Load the PDF document | |||||
PDDocument.load(pdfFile).use { document -> | |||||
val renderer = PDFRenderer(document) | |||||
// 2. Render the first page of the PDF as a monochrome image | |||||
// The '300 / 72f' scales the image to 300 DPI. | |||||
val image = renderer.renderImage(0, 300 / 72f, ImageType.BINARY) | |||||
// 3. Convert the image to a ZPL format string | |||||
val zplCommand = convertImageToZpl(image) | |||||
// 4. Send the ZPL command to the printer via a network socket | |||||
val printData = zplCommand.toByteArray() | |||||
Socket(printerIp, printerPort).use { socket -> | |||||
val os: OutputStream = socket.getOutputStream() | |||||
os.write(printData) | |||||
os.flush() | |||||
} | |||||
} | |||||
} catch (e: Exception) { | |||||
// Re-throw the exception with a more descriptive message | |||||
throw Exception("Error processing print job for PDF: ${e.message}", e) | |||||
} | |||||
} | |||||
/** | |||||
* Converts a BufferedImage (monochrome) to a ZPL string using the ^GFA command. | |||||
* This function handles the conversion of pixel data to a compressed hex string. | |||||
* | |||||
* @param image The BufferedImage to convert. | |||||
* @return A ZPL-formatted string ready to be sent to the printer. | |||||
*/ | |||||
private fun convertImageToZpl(image: BufferedImage): String { | |||||
val zpl = StringBuilder() | |||||
zpl.append("^XA\n") | |||||
val width = image.width | |||||
val height = image.height | |||||
// ZPL format for a graphical image is ^GFA | |||||
val bytesPerRow = (width + 7) / 8 | |||||
val totalBytes = bytesPerRow * height | |||||
zpl.append("^FO0,0^GFA,").append(totalBytes).append(",").append(totalBytes).append(",").append(bytesPerRow).append(",") | |||||
// Extract pixel data and convert to ZPL hex string | |||||
for (y in 0 until height) { | |||||
val rowBits = BitSet(width) | |||||
for (x in 0 until width) { | |||||
// Check for a black pixel (0xFF000000 in ARGB) | |||||
if (image.getRGB(x, y) == 0xFF000000.toInt()) { | |||||
rowBits.set(x) | |||||
} | |||||
} | |||||
for (i in 0 until bytesPerRow) { | |||||
var byteValue = 0 | |||||
for (bit in 0 until 8) { | |||||
if (rowBits.get(i * 8 + bit)) { | |||||
byteValue = byteValue or (1 shl (7 - bit)) | |||||
} | |||||
} | |||||
zpl.append(String.format("%02X", byteValue)) | |||||
} | |||||
} | |||||
zpl.append("^FS\n") | |||||
zpl.append("^XZ\n") | |||||
return zpl.toString() | |||||
} | |||||
} | |||||
} |
@@ -28,10 +28,14 @@ open class M18SchedulerService( | |||||
) { | ) { | ||||
var logger: Logger = LoggerFactory.getLogger(JwtTokenUtil::class.java) | var logger: Logger = LoggerFactory.getLogger(JwtTokenUtil::class.java) | ||||
val dataStringFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd") | val dataStringFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd") | ||||
val defaultCronExpression = "0 0 2 * * *"; | |||||
@Volatile | @Volatile | ||||
var scheduledM18Po: ScheduledFuture<*>? = null | var scheduledM18Po: ScheduledFuture<*>? = null | ||||
@Volatile | |||||
var scheduledM18Master: ScheduledFuture<*>? = null | |||||
fun isValidCronExpression(cronExpression: String): Boolean { | fun isValidCronExpression(cronExpression: String): Boolean { | ||||
return try { | return try { | ||||
CronTrigger(cronExpression) | CronTrigger(cronExpression) | ||||
@@ -43,35 +47,39 @@ open class M18SchedulerService( | |||||
@PostConstruct | @PostConstruct | ||||
fun init() { | fun init() { | ||||
scheduleM18PoTask() | scheduleM18PoTask() | ||||
scheduleM18MasterData() | |||||
} | } | ||||
fun scheduleM18PoTask() { | |||||
val defaultCronExpression = "0 0 2 * * *"; | |||||
scheduledM18Po?.cancel(false) | |||||
fun commonSchedule(scheduled: ScheduledFuture<*>?, settingName: String, scheduleFunc: () -> Unit) { | |||||
scheduled?.cancel(false) | |||||
var cron = settingsService.findByName(SettingNames.SCHEDULE_M18_PO).getOrNull()?.value ?: defaultCronExpression; | |||||
var cron = settingsService.findByName(settingName).getOrNull()?.value ?: defaultCronExpression; | |||||
if (!isValidCronExpression(cron)) { | if (!isValidCronExpression(cron)) { | ||||
cron = defaultCronExpression | cron = defaultCronExpression | ||||
} | } | ||||
scheduledM18Po = taskScheduler.schedule( | scheduledM18Po = taskScheduler.schedule( | ||||
{ | { | ||||
// testTask(); | |||||
getM18Pos() | |||||
scheduleFunc() | |||||
}, | }, | ||||
CronTrigger(cron) | CronTrigger(cron) | ||||
) | ) | ||||
println("Cron: $cron") | |||||
} | } | ||||
fun testTask() { | |||||
println("Test: ${LocalDateTime.now()}") | |||||
// Scheduler | |||||
fun scheduleM18PoTask() { | |||||
commonSchedule(scheduledM18Po, SettingNames.SCHEDULE_M18_PO, ::getM18Pos) | |||||
} | } | ||||
fun scheduleM18MasterData() { | |||||
commonSchedule(scheduledM18Master, SettingNames.SCHEDULE_M18_MASTER, ::getM18MasterData) | |||||
} | |||||
// Tasks | |||||
// @Async | // @Async | ||||
// @Scheduled(cron = "0 0 2 * * *") // (SS/MM/HH/DD/MM/YY) | |||||
// @Scheduled(cron = "0 0 2 * * *") // (SS/MM/HH/DD/MM/YY) | // @Scheduled(cron = "0 0 2 * * *") // (SS/MM/HH/DD/MM/YY) | ||||
open fun getM18Pos() { | open fun getM18Pos() { | ||||
logger.info("Daily Scheduler - PO") | |||||
val currentTime = LocalDateTime.now() | val currentTime = LocalDateTime.now() | ||||
val today = currentTime.toLocalDate().atStartOfDay() | val today = currentTime.toLocalDate().atStartOfDay() | ||||
val yesterday = today.minusDays(1L) | val yesterday = today.minusDays(1L) | ||||
@@ -84,4 +92,22 @@ open class M18SchedulerService( | |||||
logger.info("today: ${today.format(dataStringFormat)}") | logger.info("today: ${today.format(dataStringFormat)}") | ||||
logger.info("yesterday: ${yesterday.format(dataStringFormat)}") | logger.info("yesterday: ${yesterday.format(dataStringFormat)}") | ||||
} | } | ||||
open fun getM18MasterData() { | |||||
logger.info("Daily Scheduler - Master Data") | |||||
val currentTime = LocalDateTime.now() | |||||
val today = currentTime.toLocalDate().atStartOfDay() | |||||
val yesterday = today.minusDays(1L) | |||||
val request = M18CommonRequest( | |||||
modifiedDateTo = today.format(dataStringFormat), | |||||
modifiedDateFrom = yesterday.format(dataStringFormat) | |||||
) | |||||
m18MasterDataService.saveUnits(request) | |||||
m18MasterDataService.saveProducts(request) | |||||
m18MasterDataService.saveVendors(request) | |||||
m18MasterDataService.saveBusinessUnits(request) | |||||
m18MasterDataService.saveCurrencies(request) | |||||
logger.info("today: ${today.format(dataStringFormat)}") | |||||
logger.info("yesterday: ${yesterday.format(dataStringFormat)}") | |||||
} | |||||
} | } |
@@ -5,6 +5,7 @@ import com.ffii.fpsms.m18.M18Config | |||||
import com.ffii.fpsms.m18.service.* | import com.ffii.fpsms.m18.service.* | ||||
import com.ffii.fpsms.m18.web.models.M18CommonRequest | import com.ffii.fpsms.m18.web.models.M18CommonRequest | ||||
import com.ffii.fpsms.modules.common.SettingNames | import com.ffii.fpsms.modules.common.SettingNames | ||||
import com.ffii.fpsms.modules.common.scheduler.SchedulerService | |||||
import com.ffii.fpsms.modules.master.entity.ItemUom | import com.ffii.fpsms.modules.master.entity.ItemUom | ||||
import com.ffii.fpsms.modules.master.entity.Items | import com.ffii.fpsms.modules.master.entity.Items | ||||
import com.ffii.fpsms.modules.master.entity.ShopRepository | import com.ffii.fpsms.modules.master.entity.ShopRepository | ||||
@@ -31,7 +32,7 @@ class M18TestController ( | |||||
private val itemUomService: ItemUomService, | private val itemUomService: ItemUomService, | ||||
private val m18PurchaseQuotationService: M18PurchaseQuotationService, | private val m18PurchaseQuotationService: M18PurchaseQuotationService, | ||||
private val m18DeliveryOrderService: M18DeliveryOrderService, | private val m18DeliveryOrderService: M18DeliveryOrderService, | ||||
val m18SchedulerService: M18SchedulerService, | |||||
val schedulerService: SchedulerService, | |||||
private val settingsService: SettingsService, | private val settingsService: SettingsService, | ||||
) { | ) { | ||||
var logger: Logger = LoggerFactory.getLogger(JwtTokenUtil::class.java) | var logger: Logger = LoggerFactory.getLogger(JwtTokenUtil::class.java) | ||||
@@ -60,13 +61,13 @@ class M18TestController ( | |||||
@GetMapping("/test4") | @GetMapping("/test4") | ||||
fun test4(): Any { | fun test4(): Any { | ||||
return m18SchedulerService.getM18Pos(); | |||||
return schedulerService.getM18Pos(); | |||||
} | } | ||||
// --------------------------------------------- Scheduler --------------------------------------------- /// | // --------------------------------------------- Scheduler --------------------------------------------- /// | ||||
@GetMapping("/schedule/po") // | @GetMapping("/schedule/po") // | ||||
fun schedulePo(@RequestParam @Valid newCron: String) { | fun schedulePo(@RequestParam @Valid newCron: String) { | ||||
settingsService.update(SettingNames.SCHEDULE_M18_PO, newCron); | settingsService.update(SettingNames.SCHEDULE_M18_PO, newCron); | ||||
m18SchedulerService.scheduleM18PoTask() | |||||
schedulerService.scheduleM18PoTask() | |||||
} | } | ||||
// --------------------------------------------- Master Data --------------------------------------------- /// | // --------------------------------------------- Master Data --------------------------------------------- /// | ||||
@@ -25,6 +25,11 @@ public abstract class SettingNames { | |||||
*/ | */ | ||||
public static final String SCHEDULE_M18_PO = "SCHEDULE.m18.po"; | public static final String SCHEDULE_M18_PO = "SCHEDULE.m18.po"; | ||||
public static final String SCHEDULE_M18_MASTER = "SCHEDULE.m18.master"; | |||||
public static final String SCHEDULE_PROD_ROUGH = "SCHEDULE.prod.rough"; | |||||
public static final String SCHEDULE_PROD_DETAILED = "SCHEDULE.prod.detailed"; | |||||
/* | /* | ||||
* Mail settings | * Mail settings | ||||
*/ | */ | ||||
@@ -0,0 +1,186 @@ | |||||
package com.ffii.fpsms.modules.common.scheduler | |||||
import com.ffii.core.utils.JwtTokenUtil | |||||
import com.ffii.fpsms.m18.service.M18DeliveryOrderService | |||||
import com.ffii.fpsms.m18.service.M18MasterDataService | |||||
import com.ffii.fpsms.m18.service.M18PurchaseOrderService | |||||
import com.ffii.fpsms.m18.web.models.M18CommonRequest | |||||
import com.ffii.fpsms.modules.common.SettingNames | |||||
import com.ffii.fpsms.modules.master.service.ProductionScheduleService | |||||
import com.ffii.fpsms.modules.settings.service.SettingsService | |||||
import jakarta.annotation.PostConstruct | |||||
import org.slf4j.Logger | |||||
import org.slf4j.LoggerFactory | |||||
import org.springframework.scheduling.TaskScheduler | |||||
import org.springframework.scheduling.support.CronTrigger | |||||
import org.springframework.stereotype.Service | |||||
import java.time.LocalDate | |||||
import java.time.LocalDateTime | |||||
import java.time.format.DateTimeFormatter | |||||
import java.util.HashMap | |||||
import java.util.concurrent.ScheduledFuture | |||||
import kotlin.jvm.optionals.getOrNull | |||||
@Service | |||||
open class SchedulerService( | |||||
val settingsService: SettingsService, | |||||
val taskScheduler: TaskScheduler, | |||||
val productionScheduleService: ProductionScheduleService, | |||||
val m18PurchaseOrderService: M18PurchaseOrderService, | |||||
val m18DeliveryOrderService: M18DeliveryOrderService, | |||||
val m18MasterDataService: M18MasterDataService, | |||||
) { | |||||
var logger: Logger = LoggerFactory.getLogger(JwtTokenUtil::class.java) | |||||
val dataStringFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd") | |||||
val defaultCronExpression = "0 0 2 * * *"; | |||||
@Volatile | |||||
var scheduledM18Po: ScheduledFuture<*>? = null | |||||
@Volatile | |||||
var scheduledM18Master: ScheduledFuture<*>? = null | |||||
@Volatile | |||||
var scheduledRoughProd: ScheduledFuture<*>? = null | |||||
@Volatile | |||||
var scheduledDetailedProd: ScheduledFuture<*>? = null | |||||
// Common Function | |||||
fun isValidCronExpression(cronExpression: String): Boolean { | |||||
return try { | |||||
CronTrigger(cronExpression) | |||||
true | |||||
} catch (e: IllegalArgumentException) { | |||||
false | |||||
} | |||||
} | |||||
fun commonSchedule(scheduled: ScheduledFuture<*>?, settingName: String, scheduleFunc: () -> Unit) { | |||||
scheduled?.cancel(false) | |||||
var cron = settingsService.findByName(settingName).getOrNull()?.value ?: defaultCronExpression; | |||||
if (!isValidCronExpression(cron)) { | |||||
cron = defaultCronExpression | |||||
} | |||||
scheduledM18Po = taskScheduler.schedule( | |||||
{ | |||||
scheduleFunc() | |||||
}, | |||||
CronTrigger(cron) | |||||
) | |||||
} | |||||
// Init Scheduler | |||||
@PostConstruct | |||||
fun init() { | |||||
scheduleM18PoTask(); | |||||
scheduleM18MasterData(); | |||||
scheduleRoughProd(); | |||||
scheduleDetailedProd(); | |||||
} | |||||
// Scheduler | |||||
// --------------------------- FP-MTMS --------------------------- // | |||||
fun scheduleRoughProd() { | |||||
commonSchedule(scheduledRoughProd, SettingNames.SCHEDULE_PROD_ROUGH, ::getRoughProdSchedule) | |||||
} | |||||
fun scheduleDetailedProd() { | |||||
commonSchedule(scheduledDetailedProd, SettingNames.SCHEDULE_PROD_DETAILED, ::getDetailedProdSchedule) | |||||
} | |||||
// --------------------------- M18 --------------------------- // | |||||
fun scheduleM18PoTask() { | |||||
commonSchedule(scheduledM18Po, SettingNames.SCHEDULE_M18_PO, ::getM18Pos) | |||||
} | |||||
fun scheduleM18MasterData() { | |||||
commonSchedule(scheduledM18Master, SettingNames.SCHEDULE_M18_MASTER, ::getM18MasterData) | |||||
} | |||||
// Function for schedule | |||||
// --------------------------- FP-MTMS --------------------------- // | |||||
open fun getRoughProdSchedule() { | |||||
try { | |||||
logger.info("Daily Scheduler - Rough Prod") | |||||
val demoFGList = productionScheduleService.convertToFinishedGoodList(); | |||||
val result: HashMap<ProductionScheduleService.RoughScheduleObj, Double> = productionScheduleService.generateRoughScheduleByWeek(demoFGList) | |||||
val sortedEntries = result.entries.sortedBy { it.value } | |||||
var accProdCount = 0.0; | |||||
var fgCount = 0L; | |||||
for ((roughScheduleRecord, totalDifference) in sortedEntries) { | |||||
accProdCount += roughScheduleRecord.totalProductionCount; | |||||
fgCount ++; | |||||
println("[totalDifference:" + totalDifference + "] - " + roughScheduleRecord.toString()) | |||||
} | |||||
// Convert to List<HashMap<RoughScheduleObj, Double>> | |||||
productionScheduleService.saveRoughScheduleOutput(sortedEntries, accProdCount, fgCount); | |||||
} catch (e: Exception) { | |||||
throw RuntimeException("Error generate schedule: ${e.message}", e) | |||||
} | |||||
} | |||||
open fun getDetailedProdSchedule() { | |||||
try { | |||||
logger.info("Daily Scheduler - Detailed Prod") | |||||
// For test | |||||
val today = LocalDateTime.now() | |||||
// T, T+1, T+2 | |||||
for (day in longArrayOf(0L, 1L, 2L)) { | |||||
val produceAt = today.plusDays(day) | |||||
val latestRoughScheduleAt = productionScheduleService.getScheduledAtByDate(produceAt.toLocalDate()); | |||||
// assume schedule period is monday to sunday | |||||
val assignDate = produceAt.dayOfWeek.value | |||||
//TODO: update this to receive selectedDate and assignDate from schedule | |||||
println("produceAt: $produceAt | today: $today | latestRoughScheduleAt: $latestRoughScheduleAt | assignDate: $assignDate ") | |||||
productionScheduleService.generateDetailedScheduleByDay(assignDate, latestRoughScheduleAt.atStartOfDay(), produceAt) | |||||
} | |||||
} catch (e: Exception) { | |||||
throw RuntimeException("Error generate schedule: ${e.message}", e) | |||||
} | |||||
} | |||||
// --------------------------- M18 --------------------------- // | |||||
// @Async | |||||
// @Scheduled(cron = "0 0 2 * * *") // (SS/MM/HH/DD/MM/YY) | |||||
open fun getM18Pos() { | |||||
logger.info("Daily Scheduler - PO") | |||||
val currentTime = LocalDateTime.now() | |||||
val today = currentTime.toLocalDate().atStartOfDay() | |||||
val yesterday = today.minusDays(1L) | |||||
val request = M18CommonRequest( | |||||
modifiedDateTo = today.format(dataStringFormat), | |||||
modifiedDateFrom = yesterday.format(dataStringFormat) | |||||
) | |||||
m18PurchaseOrderService.savePurchaseOrders(request); | |||||
m18DeliveryOrderService.saveDeliveryOrders(request); | |||||
logger.info("today: ${today.format(dataStringFormat)}") | |||||
logger.info("yesterday: ${yesterday.format(dataStringFormat)}") | |||||
} | |||||
open fun getM18MasterData() { | |||||
logger.info("Daily Scheduler - Master Data") | |||||
val currentTime = LocalDateTime.now() | |||||
val today = currentTime.toLocalDate().atStartOfDay() | |||||
val yesterday = today.minusDays(1L) | |||||
val request = M18CommonRequest( | |||||
modifiedDateTo = today.format(dataStringFormat), | |||||
modifiedDateFrom = yesterday.format(dataStringFormat) | |||||
) | |||||
m18MasterDataService.saveUnits(request) | |||||
m18MasterDataService.saveProducts(request) | |||||
// m18MasterDataService.saveBoms(request) | |||||
m18MasterDataService.saveVendors(request) | |||||
m18MasterDataService.saveBusinessUnits(request) | |||||
m18MasterDataService.saveCurrencies(request) | |||||
logger.info("today: ${today.format(dataStringFormat)}") | |||||
logger.info("yesterday: ${yesterday.format(dataStringFormat)}") | |||||
} | |||||
} |
@@ -39,6 +39,7 @@ import org.springframework.data.domain.Pageable | |||||
import org.springframework.stereotype.Service | import org.springframework.stereotype.Service | ||||
import org.springframework.transaction.annotation.Transactional | import org.springframework.transaction.annotation.Transactional | ||||
import java.io.IOException | import java.io.IOException | ||||
import java.math.BigDecimal | |||||
import java.util.HashMap | import java.util.HashMap | ||||
import java.util.Objects | import java.util.Objects | ||||
import kotlin.jvm.optionals.getOrDefault | import kotlin.jvm.optionals.getOrDefault | ||||
@@ -52,6 +53,21 @@ open class JobOrderProcessService( | |||||
private val jobOrderRepository: JobOrderRepository, | private val jobOrderRepository: JobOrderRepository, | ||||
) : AbstractBaseEntityService<JobOrderProcess, Long, JobOrderProcessRepository>(jdbcDao, jobOrderProcessRepository) { | ) : AbstractBaseEntityService<JobOrderProcess, Long, JobOrderProcessRepository>(jdbcDao, jobOrderProcessRepository) { | ||||
open fun createJobOrderProcessRequests(joId: Long): List<CreateJobOrderProcessRequest> { | |||||
val zero = BigDecimal.ZERO | |||||
val jo = jobOrderRepository.findById(joId).getOrNull() ?: throw NoSuchElementException() | |||||
val jopRequests = jo.bom?.bomProcesses?.map { bp -> | |||||
CreateJobOrderProcessRequest( | |||||
joId = jo.id, | |||||
processId = bp.process?.id, | |||||
seqNo = bp.seqNo, | |||||
) | |||||
} ?: listOf(); | |||||
return jopRequests | |||||
} | |||||
open fun createJobOrderProcesses(request: List<CreateJobOrderProcessRequest>): MessageResponse{ | open fun createJobOrderProcesses(request: List<CreateJobOrderProcessRequest>): MessageResponse{ | ||||
val joProcesses = request.map { req -> | val joProcesses = request.map { req -> | ||||
val jo = req.joId?.let { jobOrderRepository.findById(it).getOrNull() } | val jo = req.joId?.let { jobOrderRepository.findById(it).getOrNull() } | ||||
@@ -110,6 +126,10 @@ open class JobOrderProcessService( | |||||
} | } | ||||
} | } | ||||
fun createJobOrderProcessesByJoId(joId: Long): MessageResponse { | |||||
return createJobOrderProcesses(createJobOrderProcessRequests(joId)); | |||||
} | |||||
// open fun isCorrectMachineUsed(request: MachineRequest): MessageResponse{ | // open fun isCorrectMachineUsed(request: MachineRequest): MessageResponse{ | ||||
// // Further development, this with check equipment for the job process | // // Further development, this with check equipment for the job process | ||||
// | // | ||||
@@ -4,6 +4,7 @@ import com.ffii.core.response.RecordsRes | |||||
import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderDetail | import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderDetail | ||||
import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderInfo | import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderInfo | ||||
import com.ffii.fpsms.modules.jobOrder.service.JobOrderBomMaterialService | import com.ffii.fpsms.modules.jobOrder.service.JobOrderBomMaterialService | ||||
import com.ffii.fpsms.modules.jobOrder.service.JobOrderProcessService | |||||
import com.ffii.fpsms.modules.jobOrder.service.JobOrderService | import com.ffii.fpsms.modules.jobOrder.service.JobOrderService | ||||
import com.ffii.fpsms.modules.jobOrder.web.model.CreateJobOrderRequest | import com.ffii.fpsms.modules.jobOrder.web.model.CreateJobOrderRequest | ||||
import com.ffii.fpsms.modules.jobOrder.web.model.JobOrderReleaseRequest | import com.ffii.fpsms.modules.jobOrder.web.model.JobOrderReleaseRequest | ||||
@@ -23,6 +24,7 @@ import org.springframework.web.bind.annotation.RestController | |||||
class JobOrderController( | class JobOrderController( | ||||
private val jobOrderService: JobOrderService, | private val jobOrderService: JobOrderService, | ||||
private val jobOrderBomMaterialService: JobOrderBomMaterialService, | private val jobOrderBomMaterialService: JobOrderBomMaterialService, | ||||
private val jobOrderProcessService: JobOrderProcessService | |||||
) { | ) { | ||||
@GetMapping("/getRecordByPage") | @GetMapping("/getRecordByPage") | ||||
@@ -47,6 +49,7 @@ class JobOrderController( | |||||
throw NoSuchElementException() | throw NoSuchElementException() | ||||
} | } | ||||
jobOrderBomMaterialService.createJobOrderBomMaterialsByJoId(jo.id) | jobOrderBomMaterialService.createJobOrderBomMaterialsByJoId(jo.id) | ||||
jobOrderProcessService.createJobOrderProcessesByJoId(jo.id) | |||||
return jo | return jo | ||||
} | } |
@@ -11,6 +11,9 @@ open class ProductionSchedule : BaseEntity<Long>() { | |||||
@Column(name = "scheduleAt") | @Column(name = "scheduleAt") | ||||
open var scheduleAt: LocalDateTime = LocalDateTime.now() | open var scheduleAt: LocalDateTime = LocalDateTime.now() | ||||
@Column(name = "produceAt") | |||||
open var produceAt: LocalDateTime? = null; | |||||
@Column(name = "totalEstProdCount") | @Column(name = "totalEstProdCount") | ||||
open var totalEstProdCount: Double = 0.0 | open var totalEstProdCount: Double = 0.0 | ||||
@@ -18,6 +18,9 @@ interface ProductionScheduleRepository : AbstractRepository<ProductionSchedule, | |||||
) | ) | ||||
fun getLatestRoughScheduleAt(type: String?): LocalDateTime | fun getLatestRoughScheduleAt(type: String?): LocalDateTime | ||||
fun findByTypeAndProduceAtAndDeletedIsFalse(type: String, produceAt: LocalDateTime): ProductionSchedule? | |||||
// Mainly for rough prod schedule page | |||||
@Query( | @Query( | ||||
nativeQuery = true, | nativeQuery = true, | ||||
value = | value = | ||||
@@ -27,7 +30,6 @@ interface ProductionScheduleRepository : AbstractRepository<ProductionSchedule, | |||||
ps.id, | ps.id, | ||||
ps.deleted, | ps.deleted, | ||||
ps.scheduleAt, | ps.scheduleAt, | ||||
-- ps.produceAt, | |||||
date_add(ps.scheduleAt, interval 7 + 1 - if(weekday(ps.scheduleAt) = 6, 0, weekday(ps.scheduleAt) + 1) day) as schedulePeriod, | date_add(ps.scheduleAt, interval 7 + 1 - if(weekday(ps.scheduleAt) = 6, 0, weekday(ps.scheduleAt) + 1) day) as schedulePeriod, | ||||
date_add(ps.scheduleAt, interval 7 + 1 + 6 - if(weekday(ps.scheduleAt) = 6, 0, weekday(ps.scheduleAt) + 1) day) as schedulePeriodTo, | date_add(ps.scheduleAt, interval 7 + 1 + 6 - if(weekday(ps.scheduleAt) = 6, 0, weekday(ps.scheduleAt) + 1) day) as schedulePeriodTo, | ||||
ps.totalEstProdCount, | ps.totalEstProdCount, | ||||
@@ -38,7 +40,6 @@ interface ProductionScheduleRepository : AbstractRepository<ProductionSchedule, | |||||
select * from prod | select * from prod | ||||
where deleted = false | where deleted = false | ||||
and (:scheduleAt = '' or datediff(scheduleAt, coalesce(:scheduleAt, scheduleAt)) = 0) | and (:scheduleAt = '' or datediff(scheduleAt, coalesce(:scheduleAt, scheduleAt)) = 0) | ||||
-- and (:produceAt = '' or datediff(produceAt, coalesce(:produceAt, produceAt)) = 0) | |||||
and (:schedulePeriod = '' or datediff(schedulePeriod, coalesce(:schedulePeriod, schedulePeriod)) = 0) | and (:schedulePeriod = '' or datediff(schedulePeriod, coalesce(:schedulePeriod, schedulePeriod)) = 0) | ||||
and (:schedulePeriodTo = '' or datediff(schedulePeriodTo, coalesce(:schedulePeriodTo, schedulePeriodTo)) = 0) | and (:schedulePeriodTo = '' or datediff(schedulePeriodTo, coalesce(:schedulePeriodTo, schedulePeriodTo)) = 0) | ||||
and (:totalEstProdCount is null or :totalEstProdCount = '' or totalEstProdCount = :totalEstProdCount) | and (:totalEstProdCount is null or :totalEstProdCount = '' or totalEstProdCount = :totalEstProdCount) | ||||
@@ -52,7 +53,6 @@ interface ProductionScheduleRepository : AbstractRepository<ProductionSchedule, | |||||
ps.id, | ps.id, | ||||
ps.deleted, | ps.deleted, | ||||
ps.scheduleAt, | ps.scheduleAt, | ||||
-- ps.produceAt, | |||||
date_add(ps.scheduleAt, interval 7 + 1 - if(weekday(ps.scheduleAt) = 6, 0, weekday(ps.scheduleAt) + 1) day) as schedulePeriod, | date_add(ps.scheduleAt, interval 7 + 1 - if(weekday(ps.scheduleAt) = 6, 0, weekday(ps.scheduleAt) + 1) day) as schedulePeriod, | ||||
date_add(ps.scheduleAt, interval 7 + 1 + 6 - if(weekday(ps.scheduleAt) = 6, 0, weekday(ps.scheduleAt) + 1) day) as schedulePeriodTo, | date_add(ps.scheduleAt, interval 7 + 1 + 6 - if(weekday(ps.scheduleAt) = 6, 0, weekday(ps.scheduleAt) + 1) day) as schedulePeriodTo, | ||||
ps.totalEstProdCount, | ps.totalEstProdCount, | ||||
@@ -63,7 +63,6 @@ interface ProductionScheduleRepository : AbstractRepository<ProductionSchedule, | |||||
select count(*) from prod | select count(*) from prod | ||||
where deleted = false | where deleted = false | ||||
and (:scheduleAt = '' or datediff(scheduleAt, coalesce(:scheduleAt, scheduleAt)) = 0) | and (:scheduleAt = '' or datediff(scheduleAt, coalesce(:scheduleAt, scheduleAt)) = 0) | ||||
-- and (:produceAt = '' or datediff(produceAt, coalesce(:produceAt, produceAt)) = 0) | |||||
and (:schedulePeriod = '' or datediff(schedulePeriod, coalesce(:schedulePeriod, schedulePeriod)) = 0) | and (:schedulePeriod = '' or datediff(schedulePeriod, coalesce(:schedulePeriod, schedulePeriod)) = 0) | ||||
and (:schedulePeriodTo = '' or datediff(schedulePeriodTo, coalesce(:schedulePeriodTo, schedulePeriodTo)) = 0) | and (:schedulePeriodTo = '' or datediff(schedulePeriodTo, coalesce(:schedulePeriodTo, schedulePeriodTo)) = 0) | ||||
and (:totalEstProdCount is null or :totalEstProdCount = '' or totalEstProdCount = :totalEstProdCount) | and (:totalEstProdCount is null or :totalEstProdCount = '' or totalEstProdCount = :totalEstProdCount) | ||||
@@ -73,7 +72,6 @@ interface ProductionScheduleRepository : AbstractRepository<ProductionSchedule, | |||||
) | ) | ||||
fun findProdScheduleInfoByPage( | fun findProdScheduleInfoByPage( | ||||
scheduleAt: String?, | scheduleAt: String?, | ||||
// produceAt: String?, | |||||
schedulePeriod: String?, | schedulePeriod: String?, | ||||
schedulePeriodTo: String?, | schedulePeriodTo: String?, | ||||
totalEstProdCount: Double?, | totalEstProdCount: Double?, | ||||
@@ -81,6 +79,38 @@ interface ProductionScheduleRepository : AbstractRepository<ProductionSchedule, | |||||
pageable: Pageable | pageable: Pageable | ||||
): Page<ProdScheduleInfo> | ): Page<ProdScheduleInfo> | ||||
// Mainly for detailed prod schedule page | |||||
@Query( | |||||
nativeQuery = true, | |||||
value = | |||||
""" | |||||
select | |||||
ps.id, | |||||
ps.deleted, | |||||
ps.scheduleAt, | |||||
ps.produceAt, | |||||
ps.totalEstProdCount, | |||||
ps.totalFGType, | |||||
ps.`type` | |||||
from production_schedule ps | |||||
where deleted = false | |||||
and produceAt is not null | |||||
and scheduleAt in (select max(ps2.scheduleAt) from production_schedule ps2 group by ps2.produceAt) | |||||
-- and (:scheduleAt = '' or datediff(scheduleAt, coalesce(:scheduleAt, scheduleAt)) = 0) | |||||
and (:produceAt = '' or datediff(produceAt, coalesce(:produceAt, produceAt)) = 0) | |||||
and (:totalEstProdCount is null or :totalEstProdCount = '' or totalEstProdCount = :totalEstProdCount) | |||||
and (coalesce(:types) is null or type in :types) | |||||
order by id desc; | |||||
""" | |||||
) | |||||
fun findProdScheduleInfoByProduceAtByPage( | |||||
// scheduleAt: String?, | |||||
produceAt: String?, | |||||
totalEstProdCount: Double?, | |||||
types: List<String>?, | |||||
pageable: Pageable | |||||
): Page<ProdScheduleInfo> | |||||
@Query(nativeQuery = true, | @Query(nativeQuery = true, | ||||
value = | value = | ||||
""" | """ | ||||
@@ -49,7 +49,7 @@ open class ItemsService( | |||||
open fun getRoughScheduleList(): List<Map<String, Any>> { | open fun getRoughScheduleList(): List<Map<String, Any>> { | ||||
val now = LocalDateTime.now() | val now = LocalDateTime.now() | ||||
val lastMonthStart = now.minusMonths(1).withDayOfMonth(1) // Start of last month | |||||
val lastMonthStart = now.minusMonths(1).withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0) // Start of last month | |||||
val lastMonthEnd = now.minusDays(now.dayOfMonth.toLong()).withHour(23).withMinute(59).withSecond(59) // End of last month | val lastMonthEnd = now.minusDays(now.dayOfMonth.toLong()).withHour(23).withMinute(59).withSecond(59) // End of last month | ||||
val curMonthStart = now.withDayOfMonth(1) // Start of last month | val curMonthStart = now.withDayOfMonth(1) // Start of last month | ||||
@@ -82,6 +82,7 @@ open class ItemsService( | |||||
+ " WHERE do.deleted = false " | + " WHERE do.deleted = false " | ||||
+ " AND do.estimatedArrivalDate >= :lastMonthStart " | + " AND do.estimatedArrivalDate >= :lastMonthStart " | ||||
+ " AND do.estimatedArrivalDate <= :lastMonthEnd " | + " AND do.estimatedArrivalDate <= :lastMonthEnd " | ||||
+ " AND dol.itemId is not null " | |||||
+ " GROUP BY dol.itemId, dol.itemNo, i.name " | + " GROUP BY dol.itemId, dol.itemNo, i.name " | ||||
); | ); | ||||
return jdbcDao.queryForList(sql.toString(), args); | return jdbcDao.queryForList(sql.toString(), args); | ||||
@@ -16,6 +16,7 @@ import com.ffii.fpsms.modules.master.entity.projections.* | |||||
import com.ffii.fpsms.modules.master.web.models.MessageResponse | import com.ffii.fpsms.modules.master.web.models.MessageResponse | ||||
import com.ffii.fpsms.modules.master.web.models.ReleaseProdScheduleLineRequest | import com.ffii.fpsms.modules.master.web.models.ReleaseProdScheduleLineRequest | ||||
import com.ffii.fpsms.modules.master.web.models.SearchProdScheduleRequest | import com.ffii.fpsms.modules.master.web.models.SearchProdScheduleRequest | ||||
import com.ffii.fpsms.modules.settings.service.SettingsService | |||||
import com.ffii.fpsms.modules.stock.entity.Inventory | import com.ffii.fpsms.modules.stock.entity.Inventory | ||||
import com.ffii.fpsms.modules.stock.entity.InventoryRepository | import com.ffii.fpsms.modules.stock.entity.InventoryRepository | ||||
import com.ffii.fpsms.modules.stock.service.InventoryService | import com.ffii.fpsms.modules.stock.service.InventoryService | ||||
@@ -26,6 +27,7 @@ import com.google.gson.JsonDeserializer | |||||
import com.google.gson.JsonElement | import com.google.gson.JsonElement | ||||
import com.google.gson.reflect.TypeToken | import com.google.gson.reflect.TypeToken | ||||
import org.springframework.data.domain.PageRequest | import org.springframework.data.domain.PageRequest | ||||
import org.springframework.scheduling.support.CronTrigger | |||||
import org.springframework.stereotype.Service | import org.springframework.stereotype.Service | ||||
import org.springframework.transaction.annotation.Transactional | import org.springframework.transaction.annotation.Transactional | ||||
import java.lang.reflect.Type | import java.lang.reflect.Type | ||||
@@ -35,6 +37,7 @@ import java.time.DayOfWeek | |||||
import java.time.LocalDate | import java.time.LocalDate | ||||
import java.time.LocalDateTime | import java.time.LocalDateTime | ||||
import java.time.format.DateTimeFormatter | import java.time.format.DateTimeFormatter | ||||
import java.util.concurrent.ScheduledFuture | |||||
import kotlin.collections.component1 | import kotlin.collections.component1 | ||||
import kotlin.collections.component2 | import kotlin.collections.component2 | ||||
import kotlin.jvm.optionals.getOrNull | import kotlin.jvm.optionals.getOrNull | ||||
@@ -54,13 +57,15 @@ open class ProductionScheduleService( | |||||
private val jobOrderProcessService: JobOrderProcessService, | private val jobOrderProcessService: JobOrderProcessService, | ||||
private val inventoryService: InventoryService, | private val inventoryService: InventoryService, | ||||
private val inventoryRepository: InventoryRepository, | private val inventoryRepository: InventoryRepository, | ||||
private val itemUomService: ItemUomService | |||||
private val itemUomService: ItemUomService, | |||||
private val settingsService: SettingsService, | |||||
) : AbstractBaseEntityService<ProductionSchedule, Long, ProductionScheduleRepository>( | ) : AbstractBaseEntityService<ProductionSchedule, Long, ProductionScheduleRepository>( | ||||
jdbcDao, | jdbcDao, | ||||
productionScheduleRepository | productionScheduleRepository | ||||
) { | ) { | ||||
// do mapping with projection | // do mapping with projection | ||||
open val formatter: DateTimeFormatter? = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") | open val formatter: DateTimeFormatter? = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") | ||||
val defaultCronExpression = "0 0 2 * * *"; | |||||
open fun getLatestScheduleAt(type: String?): LocalDateTime { | open fun getLatestScheduleAt(type: String?): LocalDateTime { | ||||
return productionScheduleRepository.getLatestRoughScheduleAt(type) | return productionScheduleRepository.getLatestRoughScheduleAt(type) | ||||
@@ -69,7 +74,7 @@ open class ProductionScheduleService( | |||||
open fun getScheduledAtByDate(inputDate: LocalDate): LocalDate { | open fun getScheduledAtByDate(inputDate: LocalDate): LocalDate { | ||||
val daysToSubtract = when (inputDate.dayOfWeek) { | val daysToSubtract = when (inputDate.dayOfWeek) { | ||||
DayOfWeek.WEDNESDAY -> 7 | DayOfWeek.WEDNESDAY -> 7 | ||||
else -> (inputDate.dayOfWeek.value + 7 - DayOfWeek.WEDNESDAY.value) % 7 | |||||
else -> inputDate.dayOfWeek.value + 7 - DayOfWeek.WEDNESDAY.value | |||||
} | } | ||||
val lastWednesday = inputDate.minusDays(daysToSubtract.toLong()) | val lastWednesday = inputDate.minusDays(daysToSubtract.toLong()) | ||||
@@ -82,7 +87,6 @@ open class ProductionScheduleService( | |||||
val response = productionScheduleRepository.findProdScheduleInfoByPage( | val response = productionScheduleRepository.findProdScheduleInfoByPage( | ||||
scheduleAt = request.scheduleAt, | scheduleAt = request.scheduleAt, | ||||
// produceAt = request.produceAt, | |||||
schedulePeriod = request.schedulePeriod, | schedulePeriod = request.schedulePeriod, | ||||
schedulePeriodTo = request.schedulePeriodTo, | schedulePeriodTo = request.schedulePeriodTo, | ||||
totalEstProdCount = request.totalEstProdCount, | totalEstProdCount = request.totalEstProdCount, | ||||
@@ -95,6 +99,29 @@ open class ProductionScheduleService( | |||||
return RecordsRes<ProdScheduleInfo>(records, total.toInt()); | return RecordsRes<ProdScheduleInfo>(records, total.toInt()); | ||||
} | } | ||||
open fun allRoughProdSchedulesByPage(request: SearchProdScheduleRequest): RecordsRes<ProdScheduleInfo> { | |||||
// request.apply { | |||||
// types = listOf("rough") | |||||
// } | |||||
return allProdSchedulesByPage(request); | |||||
} | |||||
open fun allDetailedProdSchedulesByPage(request: SearchProdScheduleRequest): RecordsRes<ProdScheduleInfo> { | |||||
val pageable = PageRequest.of(request.pageNum ?: 0, request.pageSize ?: 10); | |||||
val response = productionScheduleRepository.findProdScheduleInfoByProduceAtByPage( | |||||
produceAt = request.produceAt, | |||||
totalEstProdCount = request.totalEstProdCount, | |||||
// types = listOf("detailed", "manual"), | |||||
types = request.types, | |||||
pageable = pageable | |||||
) | |||||
val records = response.content | |||||
val total = response.totalElements | |||||
return RecordsRes<ProdScheduleInfo>(records, total.toInt()); | |||||
} | |||||
open fun roughProdScheduleDetail(id: Long): RoughProdScheduleWithLine { | open fun roughProdScheduleDetail(id: Long): RoughProdScheduleWithLine { | ||||
val zero = BigDecimal.ZERO | val zero = BigDecimal.ZERO | ||||
val prodSchedule = productionScheduleRepository.findById(id).getOrNull() ?: throw NoSuchElementException() | val prodSchedule = productionScheduleRepository.findById(id).getOrNull() ?: throw NoSuchElementException() | ||||
@@ -303,10 +330,18 @@ open class ProductionScheduleService( | |||||
@Transactional(rollbackFor = [java.lang.Exception::class]) | @Transactional(rollbackFor = [java.lang.Exception::class]) | ||||
open fun saveProdScheduleLine(request: ReleaseProdScheduleLineRequest): MessageResponse { | open fun saveProdScheduleLine(request: ReleaseProdScheduleLineRequest): MessageResponse { | ||||
val prodScheduleLine = request.id.let { productionScheduleLineRepository.findById(it).getOrNull() } ?: throw NoSuchElementException() | val prodScheduleLine = request.id.let { productionScheduleLineRepository.findById(it).getOrNull() } ?: throw NoSuchElementException() | ||||
val prodSchedule = prodScheduleLine.productionSchedule | |||||
// Update Prod Schedule Type | |||||
prodSchedule.apply { | |||||
type = "manual" | |||||
} | |||||
productionScheduleRepository.saveAndFlush(prodSchedule) | |||||
// Update Prod Schedule Line Prod qty | // Update Prod Schedule Line Prod qty | ||||
prodScheduleLine.apply { | prodScheduleLine.apply { | ||||
prodQty = request.demandQty.toDouble() | prodQty = request.demandQty.toDouble() | ||||
type = "manual" | |||||
} | } | ||||
productionScheduleLineRepository.saveAndFlush(prodScheduleLine) | productionScheduleLineRepository.saveAndFlush(prodScheduleLine) | ||||
@@ -329,10 +364,21 @@ open class ProductionScheduleService( | |||||
val bom = prodScheduleLine.item.id?.let { bomService.findByItemId(it) } | val bom = prodScheduleLine.item.id?.let { bomService.findByItemId(it) } | ||||
val approver = SecurityUtils.getUser().getOrNull() | val approver = SecurityUtils.getUser().getOrNull() | ||||
val proportion = request.demandQty.divide(bom?.outputQty ?: BigDecimal.ONE, 5, RoundingMode.HALF_UP) | val proportion = request.demandQty.divide(bom?.outputQty ?: BigDecimal.ONE, 5, RoundingMode.HALF_UP) | ||||
val isSameQty = request.demandQty.equals(prodScheduleLine.prodQty) | |||||
// Update Prod Schedule Type | |||||
if (!isSameQty) { | |||||
val prodSchedule = prodScheduleLine.productionSchedule | |||||
prodSchedule.apply { | |||||
type = "manual" | |||||
} | |||||
productionScheduleRepository.save(prodSchedule) | |||||
} | |||||
// Update Prod Schedule Line Prod qty | // Update Prod Schedule Line Prod qty | ||||
prodScheduleLine.apply { | prodScheduleLine.apply { | ||||
prodQty = request.demandQty.toDouble() | prodQty = request.demandQty.toDouble() | ||||
this.type = if (isSameQty) this.type else "manual" | |||||
approverId = approver?.id | approverId = approver?.id | ||||
} | } | ||||
productionScheduleLineRepository.save(prodScheduleLine) | productionScheduleLineRepository.save(prodScheduleLine) | ||||
@@ -459,9 +505,15 @@ open class ProductionScheduleService( | |||||
return jdbcDao.queryForInt(sql.toString(), args); | return jdbcDao.queryForInt(sql.toString(), args); | ||||
} | } | ||||
open fun generateDetailedScheduleByDay(assignDate: Int, selectedDate: LocalDateTime) { | |||||
open fun generateDetailedScheduleByDay(assignDate: Int, selectedDate: LocalDateTime, produceAt: LocalDateTime) { | |||||
val detailedScheduleOutputList = ArrayList<ProductionScheduleRecord>() | val detailedScheduleOutputList = ArrayList<ProductionScheduleRecord>() | ||||
// check the produce date have manual changes. | |||||
val manualChange = productionScheduleRepository.findByTypeAndProduceAtAndDeletedIsFalse("detailed", produceAt) | |||||
if (manualChange != null) { | |||||
return; | |||||
} | |||||
//increasement available | //increasement available | ||||
var idleProductionCount = 22000.0 - getDailyProductionCount(assignDate, selectedDate); | var idleProductionCount = 22000.0 - getDailyProductionCount(assignDate, selectedDate); | ||||
@@ -518,17 +570,19 @@ open class ProductionScheduleService( | |||||
tempPriority++ | tempPriority++ | ||||
} | } | ||||
saveDetailedScheduleOutput(sortedOutputList, accProdCount, fgCount) | |||||
saveDetailedScheduleOutput(sortedOutputList, accProdCount, fgCount, produceAt) | |||||
} | } | ||||
open fun saveDetailedScheduleOutput( | open fun saveDetailedScheduleOutput( | ||||
sortedEntries: List<ProductionScheduleRecord>, | sortedEntries: List<ProductionScheduleRecord>, | ||||
accProdCount: Double, | accProdCount: Double, | ||||
fgCount: Long | |||||
fgCount: Long, | |||||
produceAt: LocalDateTime, | |||||
) { | ) { | ||||
val tempObj = ProductionSchedule() | val tempObj = ProductionSchedule() | ||||
tempObj.id = -1; | tempObj.id = -1; | ||||
tempObj.scheduleAt = LocalDateTime.now() | tempObj.scheduleAt = LocalDateTime.now() | ||||
tempObj.produceAt = produceAt; | |||||
tempObj.totalFGType = fgCount; | tempObj.totalFGType = fgCount; | ||||
tempObj.totalEstProdCount = accProdCount; | tempObj.totalEstProdCount = accProdCount; | ||||
tempObj.type = "detailed" | tempObj.type = "detailed" | ||||
@@ -539,7 +593,6 @@ open class ProductionScheduleService( | |||||
} | } | ||||
} | } | ||||
private fun saveDetailedScheduleLineToDatabase(parentId: Long, detailedScheduleObj: ProductionScheduleRecord) { | private fun saveDetailedScheduleLineToDatabase(parentId: Long, detailedScheduleObj: ProductionScheduleRecord) { | ||||
try { | try { | ||||
val prodSchedule = productionScheduleRepository.findById(parentId).get() | val prodSchedule = productionScheduleRepository.findById(parentId).get() | ||||
@@ -627,7 +680,6 @@ open class ProductionScheduleService( | |||||
sql.append(" ORDER BY psl.assignDate, psl.itemPriority ASC "); | sql.append(" ORDER BY psl.assignDate, psl.itemPriority ASC "); | ||||
print(sql) | print(sql) | ||||
print(args.toString()) | |||||
val resultList = jdbcDao.queryForList(sql.toString(), args); | val resultList = jdbcDao.queryForList(sql.toString(), args); | ||||
print(resultList) | print(resultList) | ||||
@@ -2,6 +2,7 @@ package com.ffii.fpsms.modules.master.web | |||||
import com.ffii.core.response.RecordsRes | import com.ffii.core.response.RecordsRes | ||||
import com.ffii.core.utils.CriteriaArgsBuilder | import com.ffii.core.utils.CriteriaArgsBuilder | ||||
import com.ffii.fpsms.modules.common.scheduler.SchedulerService | |||||
import com.ffii.fpsms.modules.master.entity.ProductionScheduleRepository | import com.ffii.fpsms.modules.master.entity.ProductionScheduleRepository | ||||
import com.ffii.fpsms.modules.master.entity.projections.DetailedProdScheduleWithLine | import com.ffii.fpsms.modules.master.entity.projections.DetailedProdScheduleWithLine | ||||
import com.ffii.fpsms.modules.master.entity.projections.ProdScheduleInfo | import com.ffii.fpsms.modules.master.entity.projections.ProdScheduleInfo | ||||
@@ -30,6 +31,7 @@ import kotlin.math.abs | |||||
class ProductionScheduleController( | class ProductionScheduleController( | ||||
private val productionScheduleService: ProductionScheduleService, | private val productionScheduleService: ProductionScheduleService, | ||||
private val productionScheduleRepository: ProductionScheduleRepository, | private val productionScheduleRepository: ProductionScheduleRepository, | ||||
private val schedulerService: SchedulerService, | |||||
) { | ) { | ||||
// @GetMapping | // @GetMapping | ||||
// fun allItems(): List<Items> { | // fun allItems(): List<Items> { | ||||
@@ -48,6 +50,16 @@ class ProductionScheduleController( | |||||
return productionScheduleService.getLatestScheduleAt("rough") | return productionScheduleService.getLatestScheduleAt("rough") | ||||
} | } | ||||
@GetMapping("/test1") | |||||
fun test1(): Any { | |||||
return schedulerService.getRoughProdSchedule(); | |||||
} | |||||
@GetMapping("/test2") | |||||
fun test2(): Any { | |||||
return schedulerService.getDetailedProdSchedule(); | |||||
} | |||||
@GetMapping("/detail/rough/{id}") | @GetMapping("/detail/rough/{id}") | ||||
fun getScheduleDetail(@PathVariable id: Long): RoughProdScheduleWithLine { | fun getScheduleDetail(@PathVariable id: Long): RoughProdScheduleWithLine { | ||||
return productionScheduleService.roughProdScheduleDetail(id) | return productionScheduleService.roughProdScheduleDetail(id) | ||||
@@ -73,7 +85,15 @@ class ProductionScheduleController( | |||||
return productionScheduleService.allProdSchedulesByPage(request); | return productionScheduleService.allProdSchedulesByPage(request); | ||||
} | } | ||||
@GetMapping("/getRecordByPage/rough") | |||||
fun allRoughProdSchedulesByPage(@ModelAttribute request: SearchProdScheduleRequest): RecordsRes<ProdScheduleInfo> { | |||||
return productionScheduleService.allRoughProdSchedulesByPage(request); | |||||
} | |||||
@GetMapping("/getRecordByPage/detailed") | |||||
fun allDetailedProdSchedulesByPage(@ModelAttribute request: SearchProdScheduleRequest): RecordsRes<ProdScheduleInfo> { | |||||
return productionScheduleService.allDetailedProdSchedulesByPage(request); | |||||
} | |||||
@RequestMapping(value = ["/testDetailedSchedule"], method = [RequestMethod.GET]) | @RequestMapping(value = ["/testDetailedSchedule"], method = [RequestMethod.GET]) | ||||
fun generateDetailSchedule(request: HttpServletRequest?): Int { | fun generateDetailSchedule(request: HttpServletRequest?): Int { | ||||
@@ -98,7 +118,7 @@ class ProductionScheduleController( | |||||
//TODO: update this to receive selectedDate and assignDate from schedule | //TODO: update this to receive selectedDate and assignDate from schedule | ||||
// productionScheduleService.generateDetailedScheduleByDay(1, LocalDateTime.of(2025,6,25,0,0,0)) | // productionScheduleService.generateDetailedScheduleByDay(1, LocalDateTime.of(2025,6,25,0,0,0)) | ||||
println("genDate: $genDate | today: $today | latestRoughScheduleAt: $latestRoughScheduleAt | assignDate: $assignDate ") | println("genDate: $genDate | today: $today | latestRoughScheduleAt: $latestRoughScheduleAt | assignDate: $assignDate ") | ||||
productionScheduleService.generateDetailedScheduleByDay(assignDate, latestRoughScheduleAt ?: LocalDateTime.now()) | |||||
productionScheduleService.generateDetailedScheduleByDay(assignDate, latestRoughScheduleAt ?: LocalDateTime.now(), genDate ?: today) | |||||
return 200 | return 200 | ||||
} catch (e: Exception) { | } catch (e: Exception) { | ||||
throw RuntimeException("Error generate schedule: ${e.message}", e) | throw RuntimeException("Error generate schedule: ${e.message}", e) | ||||
@@ -4,11 +4,12 @@ import java.time.LocalDate | |||||
data class SearchProdScheduleRequest ( | data class SearchProdScheduleRequest ( | ||||
val id: Long?, | val id: Long?, | ||||
val scheduleAt: String?, | |||||
val schedulePeriod: String?, | |||||
val schedulePeriodTo: String?, | |||||
val scheduleAt: String? = "", | |||||
val produceAt: String? = "", | |||||
val schedulePeriod: String? = "", | |||||
val schedulePeriodTo: String? = "", | |||||
val totalEstProdCount: Double?, | val totalEstProdCount: Double?, | ||||
val types: List<String>?, | |||||
var types: List<String>?, | |||||
val pageSize: Int?, | val pageSize: Int?, | ||||
val pageNum: Int?, | val pageNum: Int?, | ||||
) | ) |
@@ -23,6 +23,7 @@ data class PurchaseOrderDataClass( | |||||
val orderDate: LocalDateTime?, | val orderDate: LocalDateTime?, | ||||
val estimatedArrivalDate: LocalDateTime?, | val estimatedArrivalDate: LocalDateTime?, | ||||
val completeDate: LocalDateTime?, | val completeDate: LocalDateTime?, | ||||
val itemDetail: String, | |||||
val status: String, | val status: String, | ||||
val supplier: String?, | val supplier: String?, | ||||
var escalated: Boolean? | var escalated: Boolean? |
@@ -65,22 +65,52 @@ open class PurchaseOrderService( | |||||
// } | // } | ||||
open fun getPoList(args: MutableMap<String, Any>): List<PurchaseOrderDataClass> { | open fun getPoList(args: MutableMap<String, Any>): List<PurchaseOrderDataClass> { | ||||
val sql = StringBuilder( | val sql = StringBuilder( | ||||
" select " | |||||
+ " po.*, " | |||||
+ " s.name as supplier, " | |||||
+ " CASE " | |||||
+ " WHEN sil.purchaseOrderId IS NOT NULL THEN 1 " | |||||
+ " ELSE 0 " | |||||
+ " END AS escalated " | |||||
+ " from purchase_order po " | |||||
+ " left join shop s on s.id = po.supplierId " | |||||
+ " left join ( " | |||||
+ " select " | |||||
+ " sil.purchaseOrderId " | |||||
+ " from stock_in_line sil " | |||||
+ " where sil.status like 'determine%' " | |||||
+ " ) sil on sil.purchaseOrderId = po.id " | |||||
+ " where po.deleted = false " | |||||
"select * from ( " + | |||||
"select " + | |||||
" po.*," + | |||||
" group_concat(" + | |||||
" coalesce(i.code, \"N/A\"), " + | |||||
" '-', " + | |||||
" coalesce(i.name, \"N/A\")," + | |||||
" ' ('," + | |||||
" coalesce(uc.udfudesc, \"N/A\")," + | |||||
" ')'," + | |||||
" ': ', " + | |||||
" coalesce(pol.qty, 0), " + | |||||
" ' (', " + | |||||
" coalesce(sil2.sumAcceptedQty, 0)," + | |||||
" ')' " + | |||||
" SEPARATOR ','" + | |||||
" ) as itemDetail," + | |||||
" s.name as supplier, " + | |||||
" CASE " + | |||||
" WHEN sil.purchaseOrderId IS NOT NULL THEN 1 " + | |||||
" ELSE 0 " + | |||||
" END AS escalated " + | |||||
" from purchase_order po" + | |||||
" left join purchase_order_line pol on pol.purchaseOrderId = po.id" + | |||||
" left join items i on i.id = pol.itemId" + | |||||
" left join shop s on s.id = po.supplierId " + | |||||
" left join ( " + | |||||
" select " + | |||||
" sil.purchaseOrderId " + | |||||
" from stock_in_line sil " + | |||||
" where sil.status like 'determine%'" + | |||||
" and sil.deleted = false" + | |||||
" ) sil on sil.purchaseOrderId = po.id" + | |||||
" left join ( " + | |||||
" select " + | |||||
" sil.purchaseOrderLineId, " + | |||||
" sum(coalesce(sil.acceptedQty, 0)) as sumAcceptedQty " + | |||||
" from stock_in_line sil " + | |||||
" where sil.deleted = false " + | |||||
" and sil.status = 'completed' " + | |||||
" group by sil.purchaseOrderLineId " + | |||||
" ) sil2 on sil2.purchaseOrderLineId = pol.id" + | |||||
" left join item_uom iu on iu.itemId = pol.itemId and iu.purchaseUnit = true" + | |||||
" left join uom_conversion uc on uc.id = iu.uomId" + | |||||
" where po.deleted = false " + | |||||
" and pol.deleted = false " | |||||
) | ) | ||||
if (args.containsKey("code")){ | if (args.containsKey("code")){ | ||||
sql.append(" AND po.code like :code "); | sql.append(" AND po.code like :code "); | ||||
@@ -95,7 +125,24 @@ open class PurchaseOrderService( | |||||
sql.append(" and sil.purchaseOrderId IS NULL "); | sql.append(" and sil.purchaseOrderId IS NULL "); | ||||
} | } | ||||
} | } | ||||
if (args.containsKey("orderDate")){ | |||||
sql.append(" AND po.orderDate >= :orderDate "); | |||||
} | |||||
if (args.containsKey("orderDateTo")){ | |||||
sql.append(" AND po.orderDate <= :orderDateTo "); | |||||
} | |||||
if (args.containsKey("estimatedArrivalDate")){ | |||||
sql.append(" AND po.estimatedArrivalDate >= :estimatedArrivalDate "); | |||||
} | |||||
if (args.containsKey("estimatedArrivalDateTo")){ | |||||
sql.append(" AND po.estimatedArrivalDate <= :estimatedArrivalDateTo "); | |||||
} | |||||
sql.append(" group by po.id"); | |||||
sql.append(" order by po.orderDate desc") | sql.append(" order by po.orderDate desc") | ||||
sql.append(" ) r") | |||||
if (args.containsKey("itemDetail")){ | |||||
sql.append(" AND r.itemDetail like :itemDetail "); | |||||
} | |||||
val list = jdbcDao.queryForList(sql.toString(), args); | val list = jdbcDao.queryForList(sql.toString(), args); | ||||
println(list) | println(list) | ||||
@@ -105,6 +152,7 @@ open class PurchaseOrderService( | |||||
code = it["code"] as String, | code = it["code"] as String, | ||||
orderDate = it["orderDate"] as LocalDateTime?, | orderDate = it["orderDate"] as LocalDateTime?, | ||||
estimatedArrivalDate = it["estimatedArrivalDate"] as LocalDateTime?, | estimatedArrivalDate = it["estimatedArrivalDate"] as LocalDateTime?, | ||||
itemDetail = it["itemDetail"] as String, | |||||
completeDate = it["completeDate"] as LocalDateTime?, | completeDate = it["completeDate"] as LocalDateTime?, | ||||
status = it["status"] as String, | status = it["status"] as String, | ||||
supplier = it["supplier"] as String?, | supplier = it["supplier"] as String?, | ||||
@@ -4,6 +4,7 @@ import com.ffii.core.response.RecordsRes | |||||
import com.ffii.core.support.JdbcDao | import com.ffii.core.support.JdbcDao | ||||
import com.ffii.core.utils.CriteriaArgsBuilder | import com.ffii.core.utils.CriteriaArgsBuilder | ||||
import com.ffii.core.utils.PagingUtils | import com.ffii.core.utils.PagingUtils | ||||
import com.ffii.core.utils.ZebraPrinterUtil | |||||
import com.ffii.fpsms.modules.master.entity.Items | import com.ffii.fpsms.modules.master.entity.Items | ||||
import com.ffii.fpsms.modules.master.service.ItemsService | import com.ffii.fpsms.modules.master.service.ItemsService | ||||
import com.ffii.fpsms.modules.master.web.models.MessageResponse | import com.ffii.fpsms.modules.master.web.models.MessageResponse | ||||
@@ -12,16 +13,22 @@ import com.ffii.fpsms.modules.purchaseOrder.entity.projections.PurchaseOrderData | |||||
import com.ffii.fpsms.modules.purchaseOrder.entity.projections.PurchaseOrderInfo | import com.ffii.fpsms.modules.purchaseOrder.entity.projections.PurchaseOrderInfo | ||||
import com.ffii.fpsms.modules.purchaseOrder.service.PurchaseOrderService | import com.ffii.fpsms.modules.purchaseOrder.service.PurchaseOrderService | ||||
import com.ffii.fpsms.modules.purchaseOrder.web.model.PagingRequest | import com.ffii.fpsms.modules.purchaseOrder.web.model.PagingRequest | ||||
import com.ffii.fpsms.modules.stock.service.StockInLineService | |||||
import com.ffii.fpsms.modules.stock.web.model.ExportQrCodeRequest | |||||
import com.ffii.fpsms.modules.stock.web.model.SaveStockInLineRequest | import com.ffii.fpsms.modules.stock.web.model.SaveStockInLineRequest | ||||
import jakarta.servlet.http.HttpServletRequest | import jakarta.servlet.http.HttpServletRequest | ||||
import net.sf.jasperreports.engine.JasperExportManager | |||||
import net.sf.jasperreports.engine.JasperPrint | |||||
import org.springframework.data.domain.Page | import org.springframework.data.domain.Page | ||||
import org.springframework.data.domain.PageRequest | import org.springframework.data.domain.PageRequest | ||||
import org.springframework.web.bind.annotation.* | import org.springframework.web.bind.annotation.* | ||||
import java.io.File | |||||
@RestController | @RestController | ||||
@RequestMapping("/po") | @RequestMapping("/po") | ||||
class PurchaseOrderController( | class PurchaseOrderController( | ||||
private val purchaseOrderService: PurchaseOrderService | |||||
private val purchaseOrderService: PurchaseOrderService, | |||||
private val stockInLineService: StockInLineService | |||||
) { | ) { | ||||
@GetMapping("/list") | @GetMapping("/list") | ||||
fun getPoList( | fun getPoList( | ||||
@@ -35,6 +42,10 @@ class PurchaseOrderController( | |||||
.addStringLike("code") | .addStringLike("code") | ||||
.addString("status") | .addString("status") | ||||
.addBoolean("escalated") | .addBoolean("escalated") | ||||
.addDate("orderDate") | |||||
.addDate("orderDateTo") | |||||
.addDate("estimatedArrivalDate") | |||||
.addDate("estimatedArrivalDateTo") | |||||
.build() | .build() | ||||
println(criteriaArgs) | println(criteriaArgs) | ||||
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 | ||||
@@ -42,9 +53,11 @@ class PurchaseOrderController( | |||||
val fullList = purchaseOrderService.getPoList(criteriaArgs) | val fullList = purchaseOrderService.getPoList(criteriaArgs) | ||||
val paginatedList = PagingUtils.getPaginatedList(fullList,pageSize, pageNum) | val paginatedList = PagingUtils.getPaginatedList(fullList,pageSize, pageNum) | ||||
return RecordsRes(paginatedList, fullList.size) | return RecordsRes(paginatedList, fullList.size) | ||||
} | } | ||||
@GetMapping("/testing") | @GetMapping("/testing") | ||||
fun testing(request: HttpServletRequest) { | fun testing(request: HttpServletRequest) { | ||||
val criteriaArgs = CriteriaArgsBuilder.withRequest(request) | val criteriaArgs = CriteriaArgsBuilder.withRequest(request) | ||||
@@ -58,6 +71,34 @@ class PurchaseOrderController( | |||||
@GetMapping("/detail/{id}") // purchaseOrderId | @GetMapping("/detail/{id}") // purchaseOrderId | ||||
fun getDetailedPo(@PathVariable id: Long): Map<String, Any> { | fun getDetailedPo(@PathVariable id: Long): Map<String, Any> { | ||||
/* | |||||
// tested the sample printing function by a pdf file | |||||
// just get a pdf file for demo , it can be any files? didn't try pdf with pages or multi pdf files | |||||
val request = ExportQrCodeRequest( | |||||
stockInLineIds = listOf(2L) | |||||
) | |||||
val pdf = stockInLineService.exportStockInLineQrcode(request) | |||||
val jasperPrint = pdf["report"] as JasperPrint | |||||
// 1. Create a temporary file to save the PDF. | |||||
val tempPdfFile = File.createTempFile("print_job_", ".pdf") | |||||
try { | |||||
// 2. Export the JasperPrint to the temporary PDF file. | |||||
JasperExportManager.exportReportToPdfFile(jasperPrint, tempPdfFile.absolutePath) | |||||
// 3. Call the utility function with the temporary file. | |||||
ZebraPrinterUtil.printPdfToZebra(tempPdfFile, "192.168.245.16", 9100) | |||||
} finally { | |||||
// 4. Ensure the temporary file is deleted after the print job is sent. | |||||
tempPdfFile.delete() | |||||
} | |||||
*/ | |||||
return purchaseOrderService.getDetailedPo(id) | return purchaseOrderService.getDetailedPo(id) | ||||
} | } | ||||
@@ -268,7 +268,7 @@ open class InventoryService( | |||||
val oneYearExpiry = 1L | val oneYearExpiry = 1L | ||||
val stockInLineEntries = polList.map { pol -> | val stockInLineEntries = polList.map { pol -> | ||||
val newLotNo = CodeGenerator.generateCode( | val newLotNo = CodeGenerator.generateCode( | ||||
prefix = "POLOT", | |||||
prefix = "MPO", | |||||
itemId = pol.item!!.id!!, | itemId = pol.item!!.id!!, | ||||
count = pol.id!!.toInt() | count = pol.id!!.toInt() | ||||
) | ) | ||||
@@ -1,5 +1,6 @@ | |||||
package com.ffii.fpsms.modules.stock.service | package com.ffii.fpsms.modules.stock.service | ||||
import com.ffii.core.exception.BadRequestException | |||||
import com.ffii.core.support.AbstractBaseEntityService | import com.ffii.core.support.AbstractBaseEntityService | ||||
import com.ffii.core.support.JdbcDao | import com.ffii.core.support.JdbcDao | ||||
import com.ffii.core.utils.QrCodeUtil | import com.ffii.core.utils.QrCodeUtil | ||||
@@ -74,7 +75,7 @@ open class StockInLineService( | |||||
purchaseOrderLine.apply { | purchaseOrderLine.apply { | ||||
status = PurchaseOrderLineStatus.RECEIVING | status = PurchaseOrderLineStatus.RECEIVING | ||||
} | } | ||||
polRepository.saveAndFlush(purchaseOrderLine) | |||||
val pol = polRepository.saveAndFlush(purchaseOrderLine) | |||||
if (stockIn == null) { | if (stockIn == null) { | ||||
stockIn = stockInService.create(SaveStockInRequest(purchaseOrderId = request.purchaseOrderId)).entity as StockIn | stockIn = stockInService.create(SaveStockInRequest(purchaseOrderId = request.purchaseOrderId)).entity as StockIn | ||||
// update po status to receiving | // update po status to receiving | ||||
@@ -84,6 +85,10 @@ open class StockInLineService( | |||||
} | } | ||||
purchaseOrderRepository.save(po) | purchaseOrderRepository.save(po) | ||||
} | } | ||||
val allStockInLine = stockInLineRepository.findAllStockInLineInfoByStockInIdAndDeletedFalse(stockIn.id!!) | |||||
if (pol.qty!! < request.acceptedQty) { | |||||
throw BadRequestException() | |||||
} | |||||
stockInLine.apply { | stockInLine.apply { | ||||
this.item = item | this.item = item | ||||
itemNo = item.code | itemNo = item.code | ||||
@@ -116,7 +121,7 @@ open class StockInLineService( | |||||
"itemId" to stockInLine.item!!.id | "itemId" to stockInLine.item!!.id | ||||
) | ) | ||||
val inventoryCount = jdbcDao.queryForInt(INVENTORY_COUNT.toString(), args) | val inventoryCount = jdbcDao.queryForInt(INVENTORY_COUNT.toString(), args) | ||||
val newLotNo = CodeGenerator.generateCode(prefix = "POLOT", itemId = stockInLine.item!!.id!!, count = inventoryCount) | |||||
val newLotNo = CodeGenerator.generateCode(prefix = "MPO", itemId = stockInLine.item!!.id!!, count = inventoryCount) | |||||
inventoryLot.apply { | inventoryLot.apply { | ||||
this.item = stockInLine.item | this.item = stockInLine.item | ||||
this.stockInLine = stockInLine | this.stockInLine = stockInLine | ||||
@@ -247,11 +252,15 @@ open class StockInLineService( | |||||
message = "stock in line id is null", | message = "stock in line id is null", | ||||
errorPosition = null, | errorPosition = null, | ||||
) | ) | ||||
// val allStockInLine = stockInLineRepository.findAllStockInLineInfoByStockInIdAndDeletedFalse(stockInLine!!.stockIn!!.id!!) | |||||
if (stockInLine.expiryDate != null && request.expiryDate == null) { | if (stockInLine.expiryDate != null && request.expiryDate == null) { | ||||
request.apply { | request.apply { | ||||
expiryDate = stockInLine.expiryDate | expiryDate = stockInLine.expiryDate | ||||
} | } | ||||
} | } | ||||
// TODO: check all status to prevent reverting progress due to multiple users access to the same po? | |||||
// return list of stock in line, update data grid with the list | // return list of stock in line, update data grid with the list | ||||
if (request.acceptedQty.compareTo(stockInLine.acceptedQty) == 0) { | if (request.acceptedQty.compareTo(stockInLine.acceptedQty) == 0) { | ||||
var savedInventoryLot: InventoryLot? = null | var savedInventoryLot: InventoryLot? = null | ||||
@@ -0,0 +1,4 @@ | |||||
-- liquibase formatted sql | |||||
-- changeset cyril:insert_schedule_setting | |||||
INSERT INTO `settings` (`name`, `value`, `category`, `type`) VALUES ('SCHEDULE.m18.master', '0 0 1 * * *', 'SCHEDULE', 'string'); |
@@ -0,0 +1,5 @@ | |||||
-- liquibase formatted sql | |||||
-- changeset cyril:update_production_schedule | |||||
ALTER TABLE `production_schedule` | |||||
ADD COLUMN `produceAt` DATETIME NULL AFTER `scheduleAt`; |
@@ -0,0 +1,7 @@ | |||||
-- liquibase formatted sql | |||||
-- changeset cyril:insert_schedule_setting | |||||
INSERT INTO `settings` (`name`, `value`, `category`, `type`) | |||||
VALUES | |||||
('SCHEDULE.prod.rough', '0 0 4 * * Wed', 'SCHEDULE', 'string'), | |||||
('SCHEDULE.prod.detailed', '0 0 3 * * *', 'SCHEDULE', 'string'); |