Browse Source

[Prod Schedule] Update Detailed Prod Schedule

master
cyril.tsui 1 month ago
parent
commit
5654613d0c
12 changed files with 241 additions and 45 deletions
  1. +1
    -1
      src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JobOrderBomMaterial.kt
  2. +31
    -0
      src/main/java/com/ffii/fpsms/modules/master/entity/ProductionScheduleLineRepository.kt
  3. +16
    -10
      src/main/java/com/ffii/fpsms/modules/master/entity/ProductionScheduleRepository.kt
  4. +13
    -1
      src/main/java/com/ffii/fpsms/modules/master/entity/projections/ProdScheduleInfo.kt
  5. +115
    -18
      src/main/java/com/ffii/fpsms/modules/master/service/ProductionScheduleService.kt
  6. +12
    -4
      src/main/java/com/ffii/fpsms/modules/master/web/ProductionScheduleController.kt
  7. +3
    -0
      src/main/java/com/ffii/fpsms/modules/master/web/models/ReleaseProdScheduleLineRequest.kt
  8. +2
    -2
      src/main/java/com/ffii/fpsms/modules/purchaseOrder/web/PurchaseOrderController.kt
  9. +9
    -9
      src/main/java/com/ffii/fpsms/modules/stock/entity/Inventory.kt
  10. +9
    -0
      src/main/java/com/ffii/fpsms/modules/stock/service/InventoryService.kt
  11. +11
    -0
      src/main/resources/db/changelog/changes/20250714_01_cyril/01_update_job_order_bom_material.sql
  12. +19
    -0
      src/main/resources/db/changelog/changes/20250715_01_cyril/01_update_inventory_default.sql

+ 1
- 1
src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JobOrderBomMaterial.kt View File

@@ -28,7 +28,7 @@ open class JobOrderBomMaterial : BaseEntity<Long>() {
@Column(name = "reqQty", nullable = false, precision = 14, scale = 2) @Column(name = "reqQty", nullable = false, precision = 14, scale = 2)
open var reqQty: BigDecimal? = null open var reqQty: BigDecimal? = null


@NotNull
// @NotNull
@ManyToOne @ManyToOne
@JoinColumn(name = "uomId", nullable = false) @JoinColumn(name = "uomId", nullable = false)
open var uom: UomConversion? = null open var uom: UomConversion? = null


+ 31
- 0
src/main/java/com/ffii/fpsms/modules/master/entity/ProductionScheduleLineRepository.kt View File

@@ -1,10 +1,41 @@
package com.ffii.fpsms.modules.master.entity package com.ffii.fpsms.modules.master.entity


import com.ffii.core.support.AbstractRepository import com.ffii.core.support.AbstractRepository
import com.ffii.fpsms.modules.master.entity.projections.DetailedProdScheduleLineBomMaterial
import com.ffii.fpsms.modules.master.entity.projections.DetailedProdScheduleLineBomMaterialInterface
import org.springframework.data.jpa.repository.Query import org.springframework.data.jpa.repository.Query
import org.springframework.stereotype.Repository import org.springframework.stereotype.Repository
import java.time.LocalDateTime import java.time.LocalDateTime


@Repository @Repository
interface ProductionScheduleLineRepository : AbstractRepository<ProductionScheduleLine, Long> { interface ProductionScheduleLineRepository : AbstractRepository<ProductionScheduleLine, Long> {
@Query(nativeQuery = true,
value = """
with prod_prop as (
select
psl.id as pslId,
round(coalesce(psl.prodQty, 0) / coalesce(b.outputQty, 1), 2) as proportion
from production_schedule_line psl
left join bom b on b.itemId = psl.itemId
where psl.id = :id and b.id is not null
)
select
psl.id as pslId,
bm.id,
b.outputQty as bomOutputQty,
bmi.code,
bmi.name,
bmi.`type`,
coalesce(i.onHandQty, 0) - coalesce(i.onHoldQty, 0) - coalesce(i.unavailableQty, 0) as availableQty,
ceil(coalesce(bm.qty, 0) * pp.proportion) as demandQty
from production_schedule_line psl
left join prod_prop pp on pp.pslId = psl.id
left join bom b on b.itemId = psl.itemId
left join bom_material bm on bm.bomId = b.id
left join items bmi on bmi.id = bm.itemId
left join inventory i on i.itemId = bmi.id
where psl.id = :id and bmi.id is not null
group by psl.id, bm.id, i.id, pp.proportion
""")
fun getBomMaterials(id: Long): List<DetailedProdScheduleLineBomMaterialInterface>?
} }

+ 16
- 10
src/main/java/com/ffii/fpsms/modules/master/entity/ProductionScheduleRepository.kt View File

@@ -1,9 +1,7 @@
package com.ffii.fpsms.modules.master.entity package com.ffii.fpsms.modules.master.entity


import com.ffii.core.support.AbstractRepository import com.ffii.core.support.AbstractRepository
import com.ffii.fpsms.modules.master.entity.projections.DetailedProdScheduleWithLine
import com.ffii.fpsms.modules.master.entity.projections.DetailedProdScheduleWithLineWithJsonString
import com.ffii.fpsms.modules.master.entity.projections.ProdScheduleInfo
import com.ffii.fpsms.modules.master.entity.projections.*
import org.springframework.data.domain.Page import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable import org.springframework.data.domain.Pageable
import org.springframework.data.jpa.repository.Query import org.springframework.data.jpa.repository.Query
@@ -85,7 +83,7 @@ interface ProductionScheduleRepository : AbstractRepository<ProductionSchedule,
with prod_prop as ( with prod_prop as (
select select
psl.id as pslId, psl.id as pslId,
coalesce(psl.prodQty, 0) / coalesce(b.outputQty, 1) as proportion
round(coalesce(psl.prodQty, 0) / coalesce(b.outputQty, 1), 2) as proportion
from production_schedule ps from production_schedule ps
left join production_schedule_line psl on psl.prodScheduleId = ps.id left join production_schedule_line psl on psl.prodScheduleId = ps.id
left join bom b on b.itemId = psl.itemId left join bom b on b.itemId = psl.itemId
@@ -99,7 +97,8 @@ interface ProductionScheduleRepository : AbstractRepository<ProductionSchedule,
select select
psl.id as pslId, psl.id as pslId,
coalesce(e.name, 'N/A') as equipName, coalesce(e.name, 'N/A') as equipName,
ceil(sum((coalesce(bp.prepTimeInMinute, 0) + coalesce(bp.durationInMinute, 0) + coalesce(bp.postProdTimeInMinute, 0)) * pp.proportion)) as totalMinutes
-- ceil(sum((coalesce(bp.prepTimeInMinute, 0) + coalesce(bp.durationInMinute, 0) + coalesce(bp.postProdTimeInMinute, 0)) * pp.proportion)) as totalMinutes
sum(coalesce(bp.prepTimeInMinute, 0) + coalesce(bp.durationInMinute, 0) + coalesce(bp.postProdTimeInMinute, 0)) as totalMinutes
from production_schedule ps from production_schedule ps
left join production_schedule_line psl on psl.prodScheduleId = ps.id left join production_schedule_line psl on psl.prodScheduleId = ps.id
left join prod_prop pp on pp.pslId = psl.id left join prod_prop pp on pp.pslId = psl.id
@@ -114,16 +113,19 @@ interface ProductionScheduleRepository : AbstractRepository<ProductionSchedule,
prod_material as ( prod_material as (
select select
r2.pslId, r2.pslId,
r2.bomOutputQty,
json_arrayagg(json_object('id', r2.id, 'code', r2.code, 'name', r2.name, 'type', r2.`type`, 'availableQty', r2.availableQty, 'demandQty', r2.demandQty)) as bomMaterials json_arrayagg(json_object('id', r2.id, 'code', r2.code, 'name', r2.name, 'type', r2.`type`, 'availableQty', r2.availableQty, 'demandQty', r2.demandQty)) as bomMaterials
from ( from (
select select
psl.id as pslId, psl.id as pslId,
bm.id, bm.id,
b.outputQty as bomOutputQty,
bmi.code, bmi.code,
bmi.name, bmi.name,
bmi.`type`, bmi.`type`,
coalesce(i.onHandQty, 0) - coalesce(i.onHoldQty, 0) - coalesce(i.unavailableQty, 0) as availableQty, coalesce(i.onHandQty, 0) - coalesce(i.onHoldQty, 0) - coalesce(i.unavailableQty, 0) as availableQty,
ceil(coalesce(bm.qty, 0) * pp.proportion) as demandQty
-- ceil(coalesce(bm.qty, 0) * pp.proportion) as demandQty
coalesce(bm.qty, 0) * pp.proportion as demandQty
from production_schedule ps from production_schedule ps
left join production_schedule_line psl on psl.prodScheduleId = ps.id left join production_schedule_line psl on psl.prodScheduleId = ps.id
left join prod_prop pp on pp.pslId = psl.id left join prod_prop pp on pp.pslId = psl.id
@@ -134,7 +136,7 @@ interface ProductionScheduleRepository : AbstractRepository<ProductionSchedule,
where ps.id = :id and bmi.id is not null where ps.id = :id and bmi.id is not null
group by ps.id, psl.id, bm.id, i.id, pp.proportion group by ps.id, psl.id, bm.id, i.id, pp.proportion
) r2 ) r2
group by r2.pslId
group by r2.pslId, r2.bomOutputQty
) )
select select
prod.id, prod.id,
@@ -142,7 +144,7 @@ interface ProductionScheduleRepository : AbstractRepository<ProductionSchedule,
prod.totalFGType, prod.totalFGType,
prod.totalEstProdCount, prod.totalEstProdCount,
json_arrayagg( json_arrayagg(
json_object('id', prod.pslId, 'bomMaterials', prod.bomMaterials, 'jobNo', prod.jobNo, 'code', prod.code, 'name', prod.name, 'type', prod.type, 'demandQty', prod.demandQty, 'prodTimeInMinute', prod.prodTimeInMinute, 'priority', prod.priority)
json_object('id', prod.pslId, 'bomMaterials', prod.bomMaterials, 'jobNo', prod.jobNo, 'code', prod.code, 'name', prod.name, 'type', prod.type, 'demandQty', prod.demandQty, 'bomOutputQty', prod.bomOutputQty, 'prodTimeInMinute', prod.prodTimeInMinute, 'priority', prod.priority, 'approved', prod.approved, 'proportion', prod.proportion)
) as prodScheduleLines ) as prodScheduleLines
from ( from (
select select
@@ -150,24 +152,28 @@ interface ProductionScheduleRepository : AbstractRepository<ProductionSchedule,
ps.scheduleAt, ps.scheduleAt,
ps.totalFGType, ps.totalFGType,
ps.totalEstProdCount, ps.totalEstProdCount,
psl.approverId is not null as approved,
psl.id as pslId, psl.id as pslId,
pm.bomMaterials, pm.bomMaterials,
pm.bomOutputQty,
coalesce(jo.code, 'N/A') as jobNo, coalesce(jo.code, 'N/A') as jobNo,
psli.code, psli.code,
psli.name, psli.name,
psli.`type`, psli.`type`,
psl.prodQty as demandQty, psl.prodQty as demandQty,
pe.prodTimeInMinute, pe.prodTimeInMinute,
psl.itemPriority as priority
psl.itemPriority as priority,
pp.proportion
from production_schedule ps from production_schedule ps
left join production_schedule_line psl on psl.prodScheduleId = ps.id left join production_schedule_line psl on psl.prodScheduleId = ps.id
left join items psli on psli.id = psl.itemId left join items psli on psli.id = psl.itemId
left join job_order jo on jo.prodScheduleLineId = psl.id left join job_order jo on jo.prodScheduleLineId = psl.id
left join prod_prop pp on pp.pslId = psl.id
left join prod_equip pe on pe.pslId = psl.id left join prod_equip pe on pe.pslId = psl.id
left join prod_material pm on pm.pslId = psl.id left join prod_material pm on pm.pslId = psl.id
where psl.deleted is false and pe.prodTimeInMinute is not null and pm.bomMaterials is not null where psl.deleted is false and pe.prodTimeInMinute is not null and pm.bomMaterials is not null
and ps.id = :id and ps.id = :id
group by psl.id, jo.id
group by psl.id, jo.id, pp.proportion, pm.bomOutputQty
) prod group by prod.id limit 1 ) prod group by prod.id limit 1
""" """
) )


+ 13
- 1
src/main/java/com/ffii/fpsms/modules/master/entity/projections/ProdScheduleInfo.kt View File

@@ -40,8 +40,11 @@ data class DetailedProdScheduleLineInfo(
val name: String?, val name: String?,
val type: String?, val type: String?,
val demandQty: BigDecimal?, val demandQty: BigDecimal?,
val bomOutputQty: BigDecimal?,
val prodTimeInMinute: List<DetailedProdScheduleLineProdTime>?, val prodTimeInMinute: List<DetailedProdScheduleLineProdTime>?,
val priority: BigDecimal?
val priority: BigDecimal?,
val approved: Boolean?,
val proportion: BigDecimal?
) )


data class DetailedProdScheduleLineBomMaterial ( data class DetailedProdScheduleLineBomMaterial (
@@ -53,6 +56,15 @@ data class DetailedProdScheduleLineBomMaterial (
val demandQty: BigDecimal? val demandQty: BigDecimal?
) )


interface DetailedProdScheduleLineBomMaterialInterface {
val id: Long?
val code: String?
val name: String?
val type: String?
val availableQty: BigDecimal?
val demandQty: BigDecimal?
}

data class DetailedProdScheduleLineProdTime ( data class DetailedProdScheduleLineProdTime (
val equipName: String?, val equipName: String?,
val totalMinutes: BigDecimal? val totalMinutes: BigDecimal?


+ 115
- 18
src/main/java/com/ffii/fpsms/modules/master/service/ProductionScheduleService.kt View File

@@ -15,15 +15,23 @@ 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.stock.entity.Inventory
import com.ffii.fpsms.modules.stock.entity.InventoryRepository
import com.ffii.fpsms.modules.stock.service.InventoryService
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
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.stereotype.Service import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.lang.reflect.Type
import java.math.BigDecimal import java.math.BigDecimal
import java.math.RoundingMode
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
import java.util.*
import kotlin.NoSuchElementException
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
@@ -41,6 +49,9 @@ open class ProductionScheduleService(
private val jobOrderBomMaterialService: JobOrderBomMaterialService, private val jobOrderBomMaterialService: JobOrderBomMaterialService,
private val bomService: BomService, private val bomService: BomService,
private val jobOrderProcessService: JobOrderProcessService, private val jobOrderProcessService: JobOrderProcessService,
private val inventoryService: InventoryService,
private val inventoryRepository: InventoryRepository,
private val itemUomService: ItemUomService
) : AbstractBaseEntityService<ProductionSchedule, Long, ProductionScheduleRepository>( ) : AbstractBaseEntityService<ProductionSchedule, Long, ProductionScheduleRepository>(
jdbcDao, jdbcDao,
productionScheduleRepository productionScheduleRepository
@@ -90,7 +101,7 @@ open class ProductionScheduleService(
// BigDecimal.ONE // BigDecimal.ONE
// } // }


val proportion = BigDecimal(line.prodQty).divide(bm.bom?.outputQty ?: BigDecimal.ONE)
val proportion = BigDecimal(line.prodQty).divide(bm.bom?.outputQty ?: BigDecimal.ONE, 2, RoundingMode.HALF_UP)


val demandQty = bm.qty?.times(proportion) ?: zero val demandQty = bm.qty?.times(proportion) ?: zero


@@ -174,7 +185,7 @@ open class ProductionScheduleService(
// BigDecimal.ONE // BigDecimal.ONE
// } // }


val proportion = BigDecimal(line.prodQty).divide(bm.bom?.outputQty ?: BigDecimal.ONE)
val proportion = BigDecimal(line.prodQty).divide(bm.bom?.outputQty ?: BigDecimal.ONE, 2, RoundingMode.HALF_UP)


val demandQty = bm.qty?.times(proportion) ?: zero val demandQty = bm.qty?.times(proportion) ?: zero


@@ -254,10 +265,19 @@ open class ProductionScheduleService(
) )
} }


class BooleanTypeAdapter: JsonDeserializer<Boolean> {
override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): Boolean {
// println(json)
return json.asInt == 1
}
}

open fun detailedProdScheduleDetail(id: Long): DetailedProdScheduleWithLine { open fun detailedProdScheduleDetail(id: Long): DetailedProdScheduleWithLine {
val sqlResult = productionScheduleRepository.findDetailedProdScheduleWithLine(id) ?: throw NoSuchElementException() val sqlResult = productionScheduleRepository.findDetailedProdScheduleWithLine(id) ?: throw NoSuchElementException()


val gson = Gson()
val gson = GsonBuilder()
.registerTypeAdapter(Boolean::class.javaObjectType, BooleanTypeAdapter())
.create()
val type = object : TypeToken<List<DetailedProdScheduleLineInfo>?>() {}.type val type = object : TypeToken<List<DetailedProdScheduleLineInfo>?>() {}.type
val gsonResult: List<DetailedProdScheduleLineInfo>? = gson.fromJson(sqlResult.prodScheduleLines, type) val gsonResult: List<DetailedProdScheduleLineInfo>? = gson.fromJson(sqlResult.prodScheduleLines, type)


@@ -270,14 +290,41 @@ open class ProductionScheduleService(
) )
} }


@Transactional(rollbackFor = [java.lang.Exception::class])
open fun saveProdScheduleLine(request: ReleaseProdScheduleLineRequest): MessageResponse {
val prodScheduleLine = request.id.let { productionScheduleLineRepository.findById(it).getOrNull() } ?: throw NoSuchElementException()

// Update Prod Schedule Line Prod qty
prodScheduleLine.apply {
prodQty = request.demandQty.toDouble()
}
productionScheduleLineRepository.saveAndFlush(prodScheduleLine)

val bomMaterials = prodScheduleLine.id?.let { productionScheduleLineRepository.getBomMaterials(it) }

return MessageResponse(
id = request.id,
name = null,
code = null,
type = null,
message = "Success",
entity = mapOf("bomMaterials" to bomMaterials),
errorPosition = null
)
}

@Transactional(rollbackFor = [java.lang.Exception::class])
open fun releaseProdScheduleLine(request: ReleaseProdScheduleLineRequest): MessageResponse { open fun releaseProdScheduleLine(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 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)
val proportion = request.demandQty.divide(bom?.outputQty ?: BigDecimal.ONE, 2, RoundingMode.HALF_UP)


// Update Prod Schedule Line Prod qty // Update Prod Schedule Line Prod qty
prodScheduleLine.apply { prodQty = request.demandQty.toDouble() }
prodScheduleLine.apply {
prodQty = request.demandQty.toDouble()
approverId = approver?.id
}
productionScheduleLineRepository.save(prodScheduleLine) productionScheduleLineRepository.save(prodScheduleLine)


// Create Job Order // Create Job Order
@@ -291,16 +338,61 @@ open class ProductionScheduleService(
val jo = jobOrderService.createJobOrder(joRequest) val jo = jobOrderService.createJobOrder(joRequest)


// Create Job Order Bom Materials // Create Job Order Bom Materials
val jobmRequests = bom?.bomMaterials?.map { bm ->
CreateJobOrderBomMaterialRequest(
joId = jo.id,
itemId = bm.item?.id,
reqQty = bm.qty?.times(proportion) ?: BigDecimal.ZERO,
uomId = bm.uom?.id
)
}
if (jobmRequests != null) {
jobOrderBomMaterialService.createJobOrderBomMaterials(jobmRequests)
if (bom?.bomMaterials != null) {
// Job Order Bom Material
val jobmRequests = bom.bomMaterials.map { bm ->
val demandQty = bm.qty?.times(proportion) ?: BigDecimal.ZERO

val jobm = CreateJobOrderBomMaterialRequest(
joId = jo.id,
itemId = bm.item?.id,
reqQty = bm.qty?.times(proportion) ?: BigDecimal.ZERO,
uomId = bm.salesUnit?.id
)

jobm
}

if (jobmRequests != null) {
jobOrderBomMaterialService.createJobOrderBomMaterials(jobmRequests)
}

// Inventory
val inventories = bom.bomMaterials.map { bm ->
val demandQty = bm.qty?.times(proportion) ?: BigDecimal.ZERO

var inventory = bm.item?.id?.let { inventoryRepository.findByItemId(it).getOrNull() }
if (inventory != null) {
inventory.apply {
this.onHoldQty = (this.onHoldQty ?: BigDecimal.ZERO).plus(demandQty)
}
} else {
if (bm.item != null) {
val itemUom = bm.item?.id?.let { itemUomService.findSalesUnitByItemId(it) }
inventory = Inventory().apply {
item = bm.item
onHandQty = BigDecimal.ZERO
unavailableQty = BigDecimal.ZERO
this.onHoldQty = demandQty
uom = itemUom?.uom
status = "unavailable"
}
}
}

inventory
}.groupBy { it?.item } // Group by item
.mapNotNull { (item, invList) ->
if (invList.isNotEmpty()) {
invList[0]?.apply {
onHoldQty = invList.sumOf { it?.onHoldQty ?: BigDecimal.ZERO }
}
} else {
null
}
}

inventoryRepository.saveAllAndFlush(inventories)
} }


// Create Job Order Process // Create Job Order Process
@@ -316,12 +408,17 @@ open class ProductionScheduleService(
jobOrderProcessService.createJobOrderProcesses(jopRequests) jobOrderProcessService.createJobOrderProcesses(jopRequests)
} }


// Get Latest Data
// val bomMaterials = prodScheduleLine.id?.let { productionScheduleLineRepository.getBomMaterials(it) }
val latestDetail = prodScheduleLine.productionSchedule.id?.let { detailedProdScheduleDetail(it) }

return MessageResponse( return MessageResponse(
id = request.id, id = request.id,
name = null, name = null,
code = null,
code = jo.code,
type = null, type = null,
message = "Success", message = "Success",
entity = mapOf("prodScheduleLines" to latestDetail?.prodScheduleLines),
errorPosition = null errorPosition = null
) )
} }


+ 12
- 4
src/main/java/com/ffii/fpsms/modules/master/web/ProductionScheduleController.kt View File

@@ -13,6 +13,7 @@ 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 jakarta.servlet.http.HttpServletRequest import jakarta.servlet.http.HttpServletRequest
import jakarta.validation.Valid
import org.springframework.web.bind.annotation.* import org.springframework.web.bind.annotation.*
import java.time.Duration import java.time.Duration
import java.time.LocalDateTime import java.time.LocalDateTime
@@ -54,15 +55,22 @@ class ProductionScheduleController(
return productionScheduleService.detailedProdScheduleDetail(id) return productionScheduleService.detailedProdScheduleDetail(id)
} }


@PostMapping("/detail/detailed/save")
fun saveDetailedProdScheduleDetail(@Valid @RequestBody request: ReleaseProdScheduleLineRequest): MessageResponse {
return productionScheduleService.saveProdScheduleLine(request)
}

@PostMapping("/detail/detailed/releaseLine")
fun releaseProdScheduleLine(@Valid @RequestBody request: ReleaseProdScheduleLineRequest): MessageResponse {
return productionScheduleService.releaseProdScheduleLine(request)
}

@GetMapping("/getRecordByPage") @GetMapping("/getRecordByPage")
fun allProdSchedulesByPage(@ModelAttribute request: SearchProdScheduleRequest): RecordsRes<ProdScheduleInfo> { fun allProdSchedulesByPage(@ModelAttribute request: SearchProdScheduleRequest): RecordsRes<ProdScheduleInfo> {
return productionScheduleService.allProdSchedulesByPage(request); return productionScheduleService.allProdSchedulesByPage(request);
} }


@PostMapping("/releaseLine")
fun releaseProdScheduleLine(request: ReleaseProdScheduleLineRequest): MessageResponse {
return productionScheduleService.releaseProdScheduleLine(request)
}



@RequestMapping(value = ["/testDetailSchedule"], method = [RequestMethod.GET]) @RequestMapping(value = ["/testDetailSchedule"], method = [RequestMethod.GET])
fun generateDetailSchedule(request: HttpServletRequest?): Int { fun generateDetailSchedule(request: HttpServletRequest?): Int {


+ 3
- 0
src/main/java/com/ffii/fpsms/modules/master/web/models/ReleaseProdScheduleLineRequest.kt View File

@@ -1,8 +1,11 @@
package com.ffii.fpsms.modules.master.web.models package com.ffii.fpsms.modules.master.web.models


import jakarta.validation.constraints.NotNull
import java.math.BigDecimal import java.math.BigDecimal


data class ReleaseProdScheduleLineRequest( data class ReleaseProdScheduleLineRequest(
@field:NotNull(message = "Id cannot be null")
val id: Long, val id: Long,
@field:NotNull(message = "Demand Qty cannot be null")
val demandQty: BigDecimal, val demandQty: BigDecimal,
) )

+ 2
- 2
src/main/java/com/ffii/fpsms/modules/purchaseOrder/web/PurchaseOrderController.kt View File

@@ -29,8 +29,8 @@ class PurchaseOrderController(
// @RequestParam(required = false) pageNum: Int, // @RequestParam(required = false) pageNum: Int,
// @RequestParam(required = false) pageSize: Int // @RequestParam(required = false) pageSize: Int
): RecordsRes<PurchaseOrderDataClass> { ): RecordsRes<PurchaseOrderDataClass> {
println("request")
println(request)
// println("request")
// println(request)
val criteriaArgs = CriteriaArgsBuilder.withRequest(request) val criteriaArgs = CriteriaArgsBuilder.withRequest(request)
.addStringLike("code") .addStringLike("code")
.addString("status") .addString("status")


+ 9
- 9
src/main/java/com/ffii/fpsms/modules/stock/entity/Inventory.kt View File

@@ -11,19 +11,19 @@ import java.math.BigDecimal
@Entity @Entity
@Table(name = "inventory") @Table(name = "inventory")
open class Inventory: BaseEntity<Long>(){ open class Inventory: BaseEntity<Long>(){
@NotNull
// @NotNull
@Column(name = "onHandQty") @Column(name = "onHandQty")
open var onHandQty: BigDecimal? = null open var onHandQty: BigDecimal? = null


@NotNull
// @NotNull
@Column(name = "onHoldQty") @Column(name = "onHoldQty")
open var onHoldQty: BigDecimal? = null open var onHoldQty: BigDecimal? = null


@NotNull
// @NotNull
@Column(name = "unavailableQty") @Column(name = "unavailableQty")
open var unavailableQty: BigDecimal? = null open var unavailableQty: BigDecimal? = null


@NotNull
// @NotNull
@Column(name = "price") @Column(name = "price")
open var price: BigDecimal? = null open var price: BigDecimal? = null


@@ -35,23 +35,23 @@ open class Inventory: BaseEntity<Long>(){
@JoinColumn(name = "currencyId") @JoinColumn(name = "currencyId")
open var currency: Currency? = null open var currency: Currency? = null


@NotNull
// @NotNull
@Column(name = "cpu") // cost per unit @Column(name = "cpu") // cost per unit
open var cpu: BigDecimal? = null open var cpu: BigDecimal? = null


@NotNull
// @NotNull
@Column(name = "cpuUnit") @Column(name = "cpuUnit")
open var cpuUnit: String? = null open var cpuUnit: String? = null


@NotNull
// @NotNull
@Column(name = "cpm") // cost per unit @Column(name = "cpm") // cost per unit
open var cpm: BigDecimal? = null open var cpm: BigDecimal? = null


@NotNull
// @NotNull
@Column(name = "cpmUnit") @Column(name = "cpmUnit")
open var cpmUnit: String? = null open var cpmUnit: String? = null


@NotNull
// @NotNull
@ManyToOne @ManyToOne
@JoinColumn(name = "uomId") @JoinColumn(name = "uomId")
open var uom: UomConversion? = null open var uom: UomConversion? = null


+ 9
- 0
src/main/java/com/ffii/fpsms/modules/stock/service/InventoryService.kt View File

@@ -5,6 +5,9 @@ import com.ffii.core.support.JdbcDao
import com.ffii.fpsms.modules.common.CodeGenerator import com.ffii.fpsms.modules.common.CodeGenerator
import com.ffii.fpsms.modules.master.entity.Items import com.ffii.fpsms.modules.master.entity.Items
import com.ffii.fpsms.modules.master.entity.ItemsRepository import com.ffii.fpsms.modules.master.entity.ItemsRepository
import com.ffii.fpsms.modules.master.entity.UomConversionRepository
import com.ffii.fpsms.modules.master.service.ItemUomService
import com.ffii.fpsms.modules.master.service.UomConversionService
import com.ffii.fpsms.modules.master.web.models.MessageResponse import com.ffii.fpsms.modules.master.web.models.MessageResponse
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
@@ -12,14 +15,19 @@ import com.ffii.fpsms.modules.stock.entity.projection.InventoryInfo
import com.ffii.fpsms.modules.stock.web.model.SaveInventoryRequest import com.ffii.fpsms.modules.stock.web.model.SaveInventoryRequest
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import java.io.IOException import java.io.IOException
import java.math.BigDecimal
import java.time.LocalDate import java.time.LocalDate
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
import kotlin.jvm.optionals.getOrNull


@Service @Service
open class InventoryService( open class InventoryService(
private val jdbcDao: JdbcDao, private val jdbcDao: JdbcDao,
private val inventoryRepository: InventoryRepository, private val inventoryRepository: InventoryRepository,
private val itemsRepository: ItemsRepository, private val itemsRepository: ItemsRepository,
private val uomConversionService: UomConversionService,
private val uomConversionRepository: UomConversionRepository,
private val itemUomService: ItemUomService,
): AbstractBaseEntityService<Inventory, Long, InventoryRepository>(jdbcDao, inventoryRepository) { ): AbstractBaseEntityService<Inventory, Long, InventoryRepository>(jdbcDao, inventoryRepository) {
open fun allInventory(): List<Inventory> { open fun allInventory(): List<Inventory> {
// TODO: Replace by actual logic // TODO: Replace by actual logic
@@ -39,6 +47,7 @@ open class InventoryService(
return inventoryRepository.findInventoryInfoByItemIdInAndDeletedIsFalse(itemIds); return inventoryRepository.findInventoryInfoByItemIdInAndDeletedIsFalse(itemIds);
} }



// @Throws(IOException::class) // @Throws(IOException::class)
// open fun updateInventory(request: SaveInventoryRequest): MessageResponse { // open fun updateInventory(request: SaveInventoryRequest): MessageResponse {
// // out need id // // out need id


+ 11
- 0
src/main/resources/db/changelog/changes/20250714_01_cyril/01_update_job_order_bom_material.sql View File

@@ -0,0 +1,11 @@
-- liquibase formatted sql
-- changeset cyril:update_job_order_bom_material

ALTER TABLE `job_order_bom_material`
DROP FOREIGN KEY `FK_JOBM_TO_UOM_CONVERSION_ON_UOM_ID`;
ALTER TABLE `job_order_bom_material`
CHANGE COLUMN `uomId` `uomId` INT NULL ;
ALTER TABLE `job_order_bom_material`
ADD CONSTRAINT `FK_JOBM_TO_UOM_CONVERSION_ON_UOM_ID`
FOREIGN KEY (`uomId`)
REFERENCES `uom_conversion` (`id`);

+ 19
- 0
src/main/resources/db/changelog/changes/20250715_01_cyril/01_update_inventory_default.sql View File

@@ -0,0 +1,19 @@
-- liquibase formatted sql
-- changeset cyril:update_inventory_default

ALTER TABLE `inventory`
DROP FOREIGN KEY `FK_INVENTORY_TO_UOM_ON_UOM_ID`;
ALTER TABLE `inventory`
CHANGE COLUMN `onHandQty` `onHandQty` DECIMAL(14,2) NOT NULL DEFAULT '0.00' ,
CHANGE COLUMN `price` `price` INT NULL DEFAULT 0 ,
CHANGE COLUMN `cpu` `cpu` DECIMAL(14,2) NULL DEFAULT '0.00' ,
CHANGE COLUMN `cpuUnit` `cpuUnit` VARCHAR(255) NULL DEFAULT 'HKD' ,
CHANGE COLUMN `cpm` `cpm` DECIMAL(14,2) NULL DEFAULT '0.00' ,
CHANGE COLUMN `cpmUnit` `cpmUnit` VARCHAR(255) NULL DEFAULT 'HKD' ,
CHANGE COLUMN `uomId` `uomId` INT NULL ,
CHANGE COLUMN `status` `status` VARCHAR(255) NOT NULL DEFAULT 'available' ;
ALTER TABLE `inventory`
ADD CONSTRAINT `FK_INVENTORY_TO_UOM_ON_UOM_ID`
FOREIGN KEY (`uomId`)
REFERENCES `uom_conversion` (`id`);


Loading…
Cancel
Save