@@ -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 | ||||
@@ -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>? | |||||
} | } |
@@ -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 | ||||
""" | """ | ||||
) | ) | ||||
@@ -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? | ||||
@@ -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 | ||||
) | ) | ||||
} | } | ||||
@@ -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 { | ||||
@@ -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, | ||||
) | ) |
@@ -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") | ||||
@@ -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 | ||||
@@ -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 | ||||
@@ -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`); |
@@ -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`); | |||||