| @@ -2,18 +2,16 @@ package com.ffii.fpsms.modules.pickOrder.service | |||||
| import com.ffii.core.response.RecordsRes | import com.ffii.core.response.RecordsRes | ||||
| import com.ffii.fpsms.modules.common.SecurityUtils | import com.ffii.fpsms.modules.common.SecurityUtils | ||||
| import com.ffii.fpsms.modules.master.web.models.MessageResponse | |||||
| import com.ffii.fpsms.modules.pickOrder.entity.PickOrder | |||||
| import com.ffii.fpsms.modules.pickOrder.entity.PickOrderRepository | import com.ffii.fpsms.modules.pickOrder.entity.PickOrderRepository | ||||
| import com.ffii.fpsms.modules.pickOrder.entity.projection.PickOrderInfo | import com.ffii.fpsms.modules.pickOrder.entity.projection.PickOrderInfo | ||||
| import com.ffii.fpsms.modules.pickOrder.entity.projection.PickOrderLineInfo | |||||
| import com.ffii.fpsms.modules.pickOrder.enums.PickOrderStatus | import com.ffii.fpsms.modules.pickOrder.enums.PickOrderStatus | ||||
| import com.ffii.fpsms.modules.pickOrder.web.models.* | import com.ffii.fpsms.modules.pickOrder.web.models.* | ||||
| import com.ffii.fpsms.modules.stock.entity.InventoryLotLineRepository | |||||
| import com.ffii.fpsms.modules.stock.entity.projection.CurrentInventoryItemInfo | |||||
| import com.ffii.fpsms.modules.stock.service.StockOutLineService | import com.ffii.fpsms.modules.stock.service.StockOutLineService | ||||
| import com.ffii.fpsms.modules.stock.service.SuggestedPickLotService | import com.ffii.fpsms.modules.stock.service.SuggestedPickLotService | ||||
| import com.ffii.fpsms.modules.stock.web.model.SuggestedPickLotForPoRequest | import com.ffii.fpsms.modules.stock.web.model.SuggestedPickLotForPoRequest | ||||
| import com.ffii.fpsms.modules.user.service.UserService | import com.ffii.fpsms.modules.user.service.UserService | ||||
| import org.springframework.context.annotation.Lazy | |||||
| import org.springframework.data.domain.PageRequest | import org.springframework.data.domain.PageRequest | ||||
| import org.springframework.stereotype.Service | import org.springframework.stereotype.Service | ||||
| import java.io.Serializable | import java.io.Serializable | ||||
| @@ -28,7 +26,7 @@ open class PickOrderService( | |||||
| val pickOrderRepository: PickOrderRepository, | val pickOrderRepository: PickOrderRepository, | ||||
| val stockOutLineService: StockOutLineService, | val stockOutLineService: StockOutLineService, | ||||
| val suggestedPickLotService: SuggestedPickLotService, | val suggestedPickLotService: SuggestedPickLotService, | ||||
| val userService: UserService, | |||||
| val userService: UserService, private val inventoryLotLineRepository: InventoryLotLineRepository, | |||||
| ) { | ) { | ||||
| open fun localDateTimeParse(dateTime: String?, pattern: String? = "YYYY-MM-DD hh:mm:ss"): LocalDateTime? { | open fun localDateTimeParse(dateTime: String?, pattern: String? = "YYYY-MM-DD hh:mm:ss"): LocalDateTime? { | ||||
| try { | try { | ||||
| @@ -69,7 +67,6 @@ open class PickOrderService( | |||||
| val prefix = "PICK" | val prefix = "PICK" | ||||
| val midfix = LocalDate.now().format(formatter) | val midfix = LocalDate.now().format(formatter) | ||||
| println(midfix) | |||||
| val suffix = String.format(suffixFormat, 1) | val suffix = String.format(suffixFormat, 1) | ||||
| val latestConsoCode = pickOrderRepository.findLatestConsoCodeByPrefix("${prefix}-${midfix}") | val latestConsoCode = pickOrderRepository.findLatestConsoCodeByPrefix("${prefix}-${midfix}") | ||||
| @@ -240,16 +237,43 @@ open class PickOrderService( | |||||
| return response | return response | ||||
| } | } | ||||
| open fun releaseConsoPickOrderInfo() { | |||||
| open fun releaseConsoPickOrderInfo(request: ReleaseConsoPickOrderRequest): ReleasePickOrderInfoResponse { | |||||
| val zero = BigDecimal.ZERO | |||||
| val pos = pickOrderRepository.findAllByConsoCode(request.consoCode) | |||||
| val requiredItems = pos | |||||
| .flatMap { it.pickOrderLines } | |||||
| .groupBy { it.item?.id } | |||||
| .map { (key, value) -> key to object : CurrentInventoryItemInfo { | |||||
| override val id: Long? = value[0].item?.id | |||||
| override val code: String? = value[0].item?.code | |||||
| override val name: String? = value[0].item?.name | |||||
| override var availableQty: BigDecimal? = zero | |||||
| override val requiredQty: BigDecimal = value.sumOf { it.qty ?: zero } | |||||
| }} // itemId - requiredQty | |||||
| val itemIds = requiredItems.mapNotNull { it.first } | |||||
| val inventories = inventoryLotLineRepository.findCurrentInventoryByItems(itemIds) | |||||
| val currentInventoryInfos = requiredItems.map { item -> | |||||
| val inventory = inventories.find { it.id == item.first } | |||||
| item.second.let { | |||||
| it.availableQty = inventory?.availableQty | |||||
| // return | |||||
| it | |||||
| } | |||||
| } | |||||
| return ReleasePickOrderInfoResponse( | |||||
| consoCode = request.consoCode, | |||||
| items = currentInventoryInfos, | |||||
| ) | |||||
| } | } | ||||
| open fun releaseConsoPickOrderAction(request: ReleaseConsoPickOrderRequest) { | open fun releaseConsoPickOrderAction(request: ReleaseConsoPickOrderRequest) { | ||||
| val releasedBy = SecurityUtils.getUser().getOrNull() | val releasedBy = SecurityUtils.getUser().getOrNull() | ||||
| val assignTo = request.assignTo?.let { userService.find(it) }?.getOrNull() | val assignTo = request.assignTo?.let { userService.find(it) }?.getOrNull() | ||||
| println("123") | |||||
| val pos = pickOrderRepository.findAllByConsoCode(request.consoCode) | val pos = pickOrderRepository.findAllByConsoCode(request.consoCode) | ||||
| println("456") | |||||
| pos.forEach { | pos.forEach { | ||||
| it.apply { | it.apply { | ||||
| this.releasedBy = releasedBy | this.releasedBy = releasedBy | ||||
| @@ -257,10 +281,6 @@ open class PickOrderService( | |||||
| status = PickOrderStatus.RELEASED | status = PickOrderStatus.RELEASED | ||||
| } | } | ||||
| } | } | ||||
| println(pos[0].releasedBy?.id) | |||||
| pickOrderRepository.saveAll(pos) | pickOrderRepository.saveAll(pos) | ||||
| // val suggestedPickLots = suggestedPickLotService.convertRequestsToEntities(request.suggestedPickLots) | |||||
| // | |||||
| // suggestedPickLotService.saveAll(suggestedPickLots) | |||||
| } | } | ||||
| } | } | ||||
| @@ -4,10 +4,7 @@ import com.ffii.core.response.RecordsRes | |||||
| import com.ffii.fpsms.modules.pickOrder.entity.PickOrderRepository | import com.ffii.fpsms.modules.pickOrder.entity.PickOrderRepository | ||||
| import com.ffii.fpsms.modules.pickOrder.entity.projection.PickOrderInfo | import com.ffii.fpsms.modules.pickOrder.entity.projection.PickOrderInfo | ||||
| import com.ffii.fpsms.modules.pickOrder.service.PickOrderService | import com.ffii.fpsms.modules.pickOrder.service.PickOrderService | ||||
| import com.ffii.fpsms.modules.pickOrder.web.models.ConsoPickOrderRequest | |||||
| import com.ffii.fpsms.modules.pickOrder.web.models.ConsoPickOrderResponse | |||||
| import com.ffii.fpsms.modules.pickOrder.web.models.ReleaseConsoPickOrderRequest | |||||
| import com.ffii.fpsms.modules.pickOrder.web.models.SearchPickOrderRequest | |||||
| import com.ffii.fpsms.modules.pickOrder.web.models.* | |||||
| import jakarta.validation.Valid | import jakarta.validation.Valid | ||||
| import org.springframework.data.domain.Page | import org.springframework.data.domain.Page | ||||
| import org.springframework.data.domain.PageRequest | import org.springframework.data.domain.PageRequest | ||||
| @@ -56,8 +53,13 @@ class PickOrderController( | |||||
| return pickOrderService.consoPickOrderDetail(consoCode); | return pickOrderService.consoPickOrderDetail(consoCode); | ||||
| } | } | ||||
| @GetMapping("/releaseConso") | |||||
| fun releaseConsoPickOrderInfo(@Valid @RequestBody request: ReleaseConsoPickOrderRequest): ReleasePickOrderInfoResponse { | |||||
| return pickOrderService.releaseConsoPickOrderInfo(request); | |||||
| } | |||||
| @PostMapping("/releaseConso") | @PostMapping("/releaseConso") | ||||
| fun releaseConsoPickOrders(@Valid @RequestBody request: ReleaseConsoPickOrderRequest) { | |||||
| fun releaseConsoPickOrderAction(@Valid @RequestBody request: ReleaseConsoPickOrderRequest) { | |||||
| pickOrderService.releaseConsoPickOrderAction(request) | pickOrderService.releaseConsoPickOrderAction(request) | ||||
| } | } | ||||
| } | } | ||||
| @@ -1,20 +1,13 @@ | |||||
| package com.ffii.fpsms.modules.pickOrder.web.models | package com.ffii.fpsms.modules.pickOrder.web.models | ||||
| import com.ffii.fpsms.modules.stock.entity.projection.CurrentInventoryItemInfo | |||||
| import java.math.BigDecimal | import java.math.BigDecimal | ||||
| import java.time.LocalDateTime | import java.time.LocalDateTime | ||||
| // Final Response - Release Conso Pick Order Page | // Final Response - Release Conso Pick Order Page | ||||
| data class ReleasePickOrderInfoResponse( | data class ReleasePickOrderInfoResponse( | ||||
| val consoCode: String, | val consoCode: String, | ||||
| val inventory: List<ReleasePickOrderInfoInventory> | |||||
| ) | |||||
| data class ReleasePickOrderInfoInventory( | |||||
| val id: Long?, | |||||
| val itemCode: String?, | |||||
| val itemName: String?, | |||||
| val availableQty: BigDecimal?, | |||||
| val requiredQty: BigDecimal?, | |||||
| val items: List<CurrentInventoryItemInfo> | |||||
| ) | ) | ||||
| // Final Response - Conso Pick Order Detail | // Final Response - Conso Pick Order Detail | ||||
| @@ -1,11 +1,27 @@ | |||||
| package com.ffii.fpsms.modules.stock.entity | package com.ffii.fpsms.modules.stock.entity | ||||
| import com.ffii.core.support.AbstractRepository | import com.ffii.core.support.AbstractRepository | ||||
| import com.ffii.fpsms.modules.stock.entity.projection.CurrentInventoryItemInfo | |||||
| import com.ffii.fpsms.modules.stock.entity.projection.InventoryLotLineInfo | import com.ffii.fpsms.modules.stock.entity.projection.InventoryLotLineInfo | ||||
| import org.springframework.data.jpa.repository.Query | |||||
| import org.springframework.stereotype.Repository | import org.springframework.stereotype.Repository | ||||
| import java.io.Serializable | import java.io.Serializable | ||||
| @Repository | @Repository | ||||
| interface InventoryLotLineRepository : AbstractRepository<InventoryLotLine, Long> { | interface InventoryLotLineRepository : AbstractRepository<InventoryLotLine, Long> { | ||||
| fun findInventoryLotLineInfoByInventoryLotItemIdIn(ids: List<Serializable>): List<InventoryLotLineInfo> | fun findInventoryLotLineInfoByInventoryLotItemIdIn(ids: List<Serializable>): List<InventoryLotLineInfo> | ||||
| @Query(""" | |||||
| select | |||||
| i.id as id, | |||||
| i.code as itemCode, | |||||
| i.name as itemName, | |||||
| sum(coalesce(ill.inQty, 0) - coalesce(ill.outQty, 0) - coalesce(ill.holdQty, 0)) as availableQty | |||||
| from InventoryLotLine ill | |||||
| left join InventoryLot il on ill.inventoryLot = il | |||||
| left join Items i on il.item = i | |||||
| where i.id in :items | |||||
| group by i.id | |||||
| """) | |||||
| fun findCurrentInventoryByItems(items: List<Serializable>): List<CurrentInventoryItemInfo> | |||||
| } | } | ||||
| @@ -20,17 +20,29 @@ interface InventoryLotLineWarehouseInfo { | |||||
| interface InventoryLotLineInfo { | interface InventoryLotLineInfo { | ||||
| val id: Long? | val id: Long? | ||||
| @get:Value("#{target.inventoryLot.item}") | @get:Value("#{target.inventoryLot.item}") | ||||
| val item: InventoryLotLineItemInfo? | val item: InventoryLotLineItemInfo? | ||||
| val warehouse: InventoryLotLineWarehouseInfo? | val warehouse: InventoryLotLineWarehouseInfo? | ||||
| var inQty: BigDecimal? | var inQty: BigDecimal? | ||||
| var outQty: BigDecimal? | var outQty: BigDecimal? | ||||
| var holdQty: BigDecimal? | var holdQty: BigDecimal? | ||||
| @get:Value("#{target.status.value}") | @get:Value("#{target.status.value}") | ||||
| val status: String? | val status: String? | ||||
| val remarks: String? | val remarks: String? | ||||
| @get:Value("#{target.stockUom.uom.udfudesc}") | @get:Value("#{target.stockUom.uom.udfudesc}") | ||||
| val uom: String? | val uom: String? | ||||
| @get:Value("#{target.inventoryLot.expiryDate}") | @get:Value("#{target.inventoryLot.expiryDate}") | ||||
| val expiryDate: LocalDate | val expiryDate: LocalDate | ||||
| } | |||||
| interface CurrentInventoryItemInfo { | |||||
| val id: Long? // item id | |||||
| val code: String? | |||||
| val name: String? | |||||
| val availableQty: BigDecimal? | |||||
| val requiredQty: BigDecimal? | |||||
| } | } | ||||
| @@ -3,10 +3,7 @@ package com.ffii.fpsms.modules.stock.service | |||||
| import com.ffii.fpsms.modules.pickOrder.entity.PickOrder | import com.ffii.fpsms.modules.pickOrder.entity.PickOrder | ||||
| import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLine | import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLine | ||||
| import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLineRepository | import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLineRepository | ||||
| import com.ffii.fpsms.modules.stock.entity.InventoryLotLineRepository | |||||
| import com.ffii.fpsms.modules.stock.entity.StockOutLIneRepository | |||||
| import com.ffii.fpsms.modules.stock.entity.SuggestPickLotRepository | |||||
| import com.ffii.fpsms.modules.stock.entity.SuggestedPickLot | |||||
| import com.ffii.fpsms.modules.stock.entity.* | |||||
| import com.ffii.fpsms.modules.stock.entity.enum.InventoryLotLineStatus | import com.ffii.fpsms.modules.stock.entity.enum.InventoryLotLineStatus | ||||
| import com.ffii.fpsms.modules.stock.entity.projection.InventoryLotLineInfo | import com.ffii.fpsms.modules.stock.entity.projection.InventoryLotLineInfo | ||||
| import com.ffii.fpsms.modules.stock.enums.SuggestedPickLotType | import com.ffii.fpsms.modules.stock.enums.SuggestedPickLotType | ||||
| @@ -27,6 +24,22 @@ open class SuggestedPickLotService( | |||||
| val pickOrderLineRepository: PickOrderLineRepository, | val pickOrderLineRepository: PickOrderLineRepository, | ||||
| val inventoryLotLineService: InventoryLotLineService, | val inventoryLotLineService: InventoryLotLineService, | ||||
| ) { | ) { | ||||
| // Calculation Available Qty / Remaining Qty | |||||
| open fun calculateRemainingQtyForInfo(inventoryLotLine: InventoryLotLineInfo?): BigDecimal { | |||||
| val zero = BigDecimal.ZERO | |||||
| return (inventoryLotLine?.inQty ?: zero) | |||||
| .minus(inventoryLotLine?.outQty ?: zero) | |||||
| .minus((inventoryLotLine?.holdQty ?: zero)) | |||||
| } | |||||
| open fun calculateRemainingQty(inventoryLotLine: InventoryLotLine?): BigDecimal { | |||||
| val zero = BigDecimal.ZERO | |||||
| return (inventoryLotLine?.inQty ?: zero) | |||||
| .minus(inventoryLotLine?.outQty ?: zero) | |||||
| .minus((inventoryLotLine?.holdQty ?: zero)) | |||||
| } | |||||
| // Suggestion | |||||
| open fun suggestionForPickOrders(request: SuggestedPickLotForPoRequest): SuggestedPickLotResponse { | open fun suggestionForPickOrders(request: SuggestedPickLotForPoRequest): SuggestedPickLotResponse { | ||||
| val pos = request.pickOrders | val pos = request.pickOrders | ||||
| val suggestedList = mutableListOf<SuggestedPickLot>() | val suggestedList = mutableListOf<SuggestedPickLot>() | ||||
| @@ -70,11 +83,9 @@ open class SuggestedPickLotService( | |||||
| lotLines.forEachIndexed { index, lotLine -> | lotLines.forEachIndexed { index, lotLine -> | ||||
| if (remainingQty <= zero) return@forEachIndexed | if (remainingQty <= zero) return@forEachIndexed | ||||
| val availableQty = (lotLine.inQty ?: zero) | |||||
| .minus(lotLine.outQty ?: zero) | |||||
| .minus((lotLine.holdQty ?: zero) | |||||
| .plus(holdQtyMap[lotLine.id] ?: zero) | |||||
| ) | |||||
| val availableQty = calculateRemainingQtyForInfo(lotLine) | |||||
| .minus(holdQtyMap[lotLine.id] ?: zero) | |||||
| if (availableQty <= zero) { | if (availableQty <= zero) { | ||||
| updatedLotLines += lotLine | updatedLotLines += lotLine | ||||
| @@ -109,6 +120,25 @@ open class SuggestedPickLotService( | |||||
| return SuggestedPickLotResponse(holdQtyMap = holdQtyMap, suggestedList = suggestedList) | return SuggestedPickLotResponse(holdQtyMap = holdQtyMap, suggestedList = suggestedList) | ||||
| } | } | ||||
| // Convertion | |||||
| open fun convertRequestsToEntities(request: List<SaveSuggestedPickLotRequest>): List<SuggestedPickLot> { | |||||
| // val zero = BigDecimal.ZERO | |||||
| // val entities = mutableListOf<SuggestedPickLot>() | |||||
| // val holdQtyCount = mutableMapOf<Long?, BigDecimal?>() | |||||
| // request.forEach { | |||||
| // val entity = convertRequestToEntity(it) | |||||
| // val suggestedLotLine = entity.suggestedLotLine | |||||
| // val remainQty = calculateRemainingQty(suggestedLotLine) | |||||
| // holdQtyCount[suggestedLotLine?.id] = (holdQtyCount[suggestedLotLine?.id] ?: zero).plus(suggestedLotLine?.holdQty ?: zero) | |||||
| // if (remainQty.minus(holdQtyCount[suggestedLotLine?.id] ?: zero) < zero) { | |||||
| // throw RuntimeException("The suggested pick qty is over. Please re-suggest the pick qty."); | |||||
| // } | |||||
| // entities += entity | |||||
| // } | |||||
| // return entities | |||||
| return request.map { convertRequestToEntity(it) } | |||||
| } | |||||
| open fun convertRequestToEntity(request: SaveSuggestedPickLotRequest): SuggestedPickLot{ | open fun convertRequestToEntity(request: SaveSuggestedPickLotRequest): SuggestedPickLot{ | ||||
| val suggestedPickLot = | val suggestedPickLot = | ||||
| request.id?.let { id -> suggestedPickLotRepository.findById(id).getOrDefault(SuggestedPickLot()) } | request.id?.let { id -> suggestedPickLotRepository.findById(id).getOrDefault(SuggestedPickLot()) } | ||||
| @@ -130,6 +160,7 @@ open class SuggestedPickLotService( | |||||
| return suggestedPickLot | return suggestedPickLot | ||||
| } | } | ||||
| // Save | |||||
| open fun saveSuggestedPickLot(request: SaveSuggestedPickLotRequest): SuggestedPickLot { | open fun saveSuggestedPickLot(request: SaveSuggestedPickLotRequest): SuggestedPickLot { | ||||
| val suggestedPickLot = convertRequestToEntity(request) | val suggestedPickLot = convertRequestToEntity(request) | ||||