Просмотр исходного кода

code update for save detailed schedule

production_process
jason.lam 2 месяцев назад
Родитель
Сommit
1cf0bb56ef
2 измененных файлов: 198 добавлений и 2 удалений
  1. +187
    -2
      src/main/java/com/ffii/fpsms/modules/master/service/ProductionScheduleService.kt
  2. +11
    -0
      src/main/java/com/ffii/fpsms/modules/master/web/ProductionScheduleController.kt

+ 187
- 2
src/main/java/com/ffii/fpsms/modules/master/service/ProductionScheduleService.kt Просмотреть файл

@@ -6,6 +6,7 @@ import com.ffii.fpsms.modules.master.entity.*
import com.ffii.fpsms.modules.master.web.models.MessageResponse
import org.springframework.stereotype.Service
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.util.*
import kotlin.collections.component1
import kotlin.collections.component2
@@ -19,7 +20,190 @@ open class ProductionScheduleService(
private val productionScheduleLineRepository: ProductionScheduleLineRepository,
): AbstractBaseEntityService<ProductionSchedule, Long, ProductionScheduleRepository>(jdbcDao, productionScheduleRepository) {
// do mapping with projection
open val formatter: DateTimeFormatter? = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")

//====================細排相關 START====================//

open fun getDailyProductionCount(assignDate: Int, selectedDate: LocalDateTime): Int {

val args = mapOf(
"assignDate" to assignDate,
"selectedDate" to selectedDate.format(formatter)
)

val sql = StringBuilder(
" SELECT "
+ " SUM(psl.prodQty) AS dailyProdCount "
+ " FROM production_schedule ps "
+ " LEFT JOIN production_schedule_line psl ON psl.prodScheduleId = ps.id "
+ " WHERE ps.deleted = FALSE "
+ " AND psl.deleted = FALSE "
+ " AND DATE(ps.scheduleAt) LIKE DATE(:selectedDate) "
+ " AND psl.assignDate = :assignDate "
+ " GROUP BY ps.id "
);
return jdbcDao.queryForInt(sql.toString(), args);
}

open fun generateDetailScheduleByDay(assignDate: Int, selectedDate: LocalDateTime) {
val detailScheduleOutputList = ArrayList<ProductionScheduleRecord>()

//increasement available
var idleProductionCount = 22000.0 - getDailyProductionCount(assignDate, selectedDate);

println("idleProductionCount - " + idleProductionCount);

val args = mapOf(
"assignDate" to assignDate,
"selectedDate" to selectedDate.format(formatter)
)

val scheduledList: List<ProductionScheduleRecord> = getProductionScheduleRecord(args)

//用缺口決定生產順序
val productionPriorityMap: HashMap<ProductionScheduleRecord, Double> = HashMap();

//TODO: update to use SQL get shop order record for detail scheduling (real prodQty and openBal)
for (record in scheduledList){
println("Object - " + record.toString() );
val tempRecord= record;
tempRecord.prodQty = tempRecord.prodQty *2;
val difference = -(tempRecord.targetMinStock + tempRecord.prodQty - tempRecord.estCloseBal) /*TODO: this should be real close bal*/;
productionPriorityMap.put(tempRecord, difference)
}

//sort by difference
val sortedEntries = productionPriorityMap.entries.sortedBy { it.value }

var accProdCount = 0.0;
var fgCount = 0L;

for ((updatedScheduleRecord, totalDifference) in sortedEntries) {
//match id with rough schedule record to create new record
val targetRecord = scheduledList.find { it.itemId == updatedScheduleRecord.itemId }
val prodDifference = updatedScheduleRecord.prodQty - (targetRecord?.prodQty ?: 0.0)

if(idleProductionCount - prodDifference >= 0){
//have enough quoter for adjustment
idleProductionCount -= prodDifference;
detailScheduleOutputList.add(updatedScheduleRecord)
accProdCount += updatedScheduleRecord.prodQty
fgCount++
}
else{
println("[INFO] item " + updatedScheduleRecord.name + " have bee skipped");
}
}

saveDetailScheduleOutput(detailScheduleOutputList, accProdCount, fgCount)

//return detailScheduleOutput
}

open fun saveDetailScheduleOutput(sortedEntries: List<ProductionScheduleRecord>, accProdCount: Double, fgCount: Long) {
val tempObj = ProductionSchedule()
tempObj.id = -1;
tempObj.scheduleAt = LocalDateTime.now()
tempObj.totalFGType = fgCount;
tempObj.totalEstProdCount = accProdCount;
tempObj.id = saveProductionScheduleToDatabase(tempObj);
tempObj.type = "detailed"

for ( detailedScheduleRecord in sortedEntries) {
saveDetailedScheduleLineToDatabase(tempObj.id ?: -1, detailedScheduleRecord)
}
}


private fun saveDetailedScheduleLineToDatabase(parentId: Long, detailedScheduleObj: ProductionScheduleRecord) {
try {
var savedItem = ProductionScheduleLine()
savedItem.id = -1;
savedItem.prodScheduleId = parentId;
savedItem.itemId = detailedScheduleObj.itemId ?: -1;
savedItem.lastMonthAvgSales = detailedScheduleObj.lastMonthAvgSales ?: 0.0;
savedItem.refScheduleId = detailedScheduleObj.id;
savedItem.approverId = null;
savedItem.estCloseBal = detailedScheduleObj.estCloseBal;
savedItem.prodQty = detailedScheduleObj.prodQty
savedItem.type = "detailed"
savedItem.assignDate = detailedScheduleObj.assignDate;
savedItem.itemPriority = detailedScheduleObj.itemPriority //TODO: to be updated
productionScheduleLineRepository.saveAndFlush(savedItem)

} catch (e: Exception) {
throw RuntimeException("Error saving production schedule line: ${e.message}", e)
}
}
//====================細排相關 END====================//

open class ProductionScheduleRecord : ProductionScheduleLine() {
//SQL record in general with item name
open var name: String = "" //item name
open var targetMinStock: Double = 0.0;

override fun toString(): String {
return "ProductionScheduleRecord(name='$name', targetMinStock=$targetMinStock)"
}

}

open fun getProductionScheduleRecord(args: Map<String, Any>): List<ProductionScheduleRecord> {

val sql = StringBuilder(
" SELECT "
+ " psl.id, "
+ " psl.itemPriority, "
+ " psl.itemId, "
+ " i.name, "
+ " psl.lastMonthAvgSales, "
+ " psl.lastMonthAvgSales*2 AS targetMinStock, "
+ " psl.prodQty, "
+ " psl.estCloseBal, "
+ " psl.`type`, "
+ " psl.assignDate "
+ " FROM production_schedule ps "
+ " LEFT JOIN production_schedule_line psl ON psl.prodScheduleId = ps.id "
+ " LEFT JOIN items i ON psl.itemId = i.id "
+ " WHERE ps.deleted = FALSE "
+ " AND psl.deleted = FALSE "
);

if(args.containsKey("selectedDate")){
sql.append(" AND DATE(ps.scheduleAt) LIKE DATE(:selectedDate) ");
}
if(args.containsKey("name")){
sql.append(" AND i.name LIKE :name ");
}
if(args.containsKey("assignDate")){
sql.append(" AND psl.assignDate = :assignDate ");
}
if(args.containsKey("id")){
sql.append(" AND i.id = :id ");
}

sql.append(" ORDER BY psl.assignDate, psl.itemPriority ASC ");

val resultList = jdbcDao.queryForList(sql.toString(), args, );

// Convert the list of maps to a list of ProductionScheduleRecord
return resultList.map { row ->
ProductionScheduleRecord().apply {
this.id = row["id"].toString().toLong()
this.itemPriority = row["itemPriority"].toString().toLong()
this.itemId = row["itemId"].toString().toLong()
this.name = row["name"] as String
this.lastMonthAvgSales = row["lastMonthAvgSales"].toString().toDouble()
this.targetMinStock = row["targetMinStock"].toString().toDouble() // Ensure correct type
this.prodQty = row["prodQty"].toString().toDouble()
this.estCloseBal = row["estCloseBal"].toString().toDouble()
this.type = row["type"] as String
this.assignDate = row["assignDate"].toString().toLong()
}
}
}

//====================粗排相關 START====================//
open fun convertToFinishedGoodList(): ArrayList<FinishedGood> {
val roughScheduleList = itemService.getRoughScheduleList() // Retrieve the list from your existing method
val finishedGoodList = ArrayList<FinishedGood>()
@@ -193,7 +377,7 @@ open class ProductionScheduleService(
tempObj.scheduleAt = LocalDateTime.now()
tempObj.totalFGType = fgCount;
tempObj.totalEstProdCount = accProdCount;
tempObj.id = saveRoughScheduleToDatabase(tempObj);
tempObj.id = saveProductionScheduleToDatabase(tempObj);
tempObj.type = "rough"

for ((roughScheduleRecord, totalDifference) in sortedEntries) {
@@ -201,7 +385,7 @@ open class ProductionScheduleService(
}
}

private fun saveRoughScheduleToDatabase(newRecord: ProductionSchedule): Long?{
private fun saveProductionScheduleToDatabase(newRecord: ProductionSchedule): Long?{
try {
val savedItem = productionScheduleRepository.saveAndFlush(newRecord)
return savedItem.id
@@ -233,6 +417,7 @@ open class ProductionScheduleService(
}

}
//====================粗排相關 END====================//

open class FinishedGood {
//var fgDetail: FinishedGood? = null


+ 11
- 0
src/main/java/com/ffii/fpsms/modules/master/web/ProductionScheduleController.kt Просмотреть файл

@@ -7,6 +7,7 @@ import com.ffii.fpsms.modules.master.service.ProductionScheduleService.FinishedG
import com.ffii.fpsms.modules.master.service.ProductionScheduleService.RoughScheduleObj
import jakarta.servlet.http.HttpServletRequest
import org.springframework.web.bind.annotation.*
import java.time.LocalDateTime
import java.util.ArrayList
import java.util.HashMap
import kotlin.collections.component1
@@ -23,6 +24,16 @@ class ProductionScheduleController(
// return uomConversionService.allItems()
// }

@RequestMapping(value = ["/testDetailSchedule"], method = [RequestMethod.GET])
fun generateDetailSchedule(request: HttpServletRequest?): Int {
try {
productionScheduleService.generateDetailScheduleByDay(1, LocalDateTime.of(2025,5,28,0,0,0))
return 200
} catch (e: Exception) {
throw RuntimeException("Error generate schedule: ${e.message}", e)
}
}

@RequestMapping(value = ["/testRoughSchedule"], method = [RequestMethod.GET])
fun generateRoughSchedule(request: HttpServletRequest?): List<HashMap<RoughScheduleObj, Double>> {
try {


Загрузка…
Отмена
Сохранить