jason.lam hace 2 meses
padre
commit
e7de2fd6c6
Se han modificado 24 ficheros con 670 adiciones y 18 borrados
  1. +4
    -2
      src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickOrderLine.kt
  2. +2
    -0
      src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickOrderRepository.kt
  3. +154
    -10
      src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt
  4. +7
    -0
      src/main/java/com/ffii/fpsms/modules/pickOrder/web/PickOrderController.kt
  5. +9
    -0
      src/main/java/com/ffii/fpsms/modules/pickOrder/web/models/ConsoPickOrderRequest.kt
  6. +89
    -0
      src/main/java/com/ffii/fpsms/modules/pickOrder/web/models/ConsoPickOrderResponse.kt
  7. +7
    -1
      src/main/java/com/ffii/fpsms/modules/stock/entity/InventoryLotLine.kt
  8. +3
    -0
      src/main/java/com/ffii/fpsms/modules/stock/entity/InventoryLotLineRepository.kt
  9. +5
    -1
      src/main/java/com/ffii/fpsms/modules/stock/entity/StockInLine.kt
  10. +2
    -0
      src/main/java/com/ffii/fpsms/modules/stock/entity/SuggestPickLotRepository.kt
  11. +3
    -3
      src/main/java/com/ffii/fpsms/modules/stock/entity/SuggestedPickLot.kt
  12. +6
    -0
      src/main/java/com/ffii/fpsms/modules/stock/entity/enum/InventoryLotLineEnum.kt
  13. +17
    -0
      src/main/java/com/ffii/fpsms/modules/stock/entity/enum/InventoryLotLineEnumConverter.kt
  14. +36
    -0
      src/main/java/com/ffii/fpsms/modules/stock/entity/projection/InventoryLotLineInfo.kt
  15. +64
    -0
      src/main/java/com/ffii/fpsms/modules/stock/service/InventoryLotLineService.kt
  16. +4
    -1
      src/main/java/com/ffii/fpsms/modules/stock/service/StockInLineService.kt
  17. +142
    -0
      src/main/java/com/ffii/fpsms/modules/stock/service/SuggestedPickLotService.kt
  18. +23
    -0
      src/main/java/com/ffii/fpsms/modules/stock/web/InventoryLotLineController.kt
  19. +26
    -0
      src/main/java/com/ffii/fpsms/modules/stock/web/SuggestedPickLotController.kt
  20. +15
    -0
      src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveInventoryLotLineRequest.kt
  21. +12
    -0
      src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveSuggestedPickLotRequest.kt
  22. +17
    -0
      src/main/java/com/ffii/fpsms/modules/stock/web/model/SuggestedPickLotRequest.kt
  23. +16
    -0
      src/main/java/com/ffii/fpsms/modules/stock/web/model/SuggestedPickLotResponse.kt
  24. +7
    -0
      src/main/resources/db/changelog/changes/20250620_01_derek/01_add_lot_line_id_to_stock_in_line.sql

+ 4
- 2
src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickOrderLine.kt Ver fichero

@@ -5,6 +5,8 @@ import com.fasterxml.jackson.annotation.JsonManagedReference
import com.ffii.core.entity.BaseEntity
import com.ffii.fpsms.modules.master.entity.Items
import com.ffii.fpsms.modules.master.entity.UomConversion
import com.ffii.fpsms.modules.pickOrder.enums.PickOrderLineStatus
import com.ffii.fpsms.modules.pickOrder.enums.PickOrderLineStatusConverter
import com.ffii.fpsms.modules.stock.entity.StockOutLine
import com.ffii.fpsms.modules.stock.entity.SuggestedPickLot
import jakarta.persistence.*
@@ -36,10 +38,10 @@ open class PickOrderLine : BaseEntity<Long>() {
@JoinColumn(name = "uomId", nullable = false)
open var uom: UomConversion? = null

@Size(max = 30)
@Convert(converter = PickOrderLineStatusConverter::class)
@NotNull
@Column(name = "status", nullable = false, length = 30)
open var status: String? = null
open var status: PickOrderLineStatus? = null

@JsonManagedReference
@OneToMany(mappedBy = "pickOrderLine", cascade = [CascadeType.ALL], orphanRemoval = true)


+ 2
- 0
src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickOrderRepository.kt Ver fichero

@@ -53,4 +53,6 @@ interface PickOrderRepository : AbstractRepository<PickOrder, Long> {
fun findAllByIdInAndConsoCodeIsNullAndStatus(id: List<Serializable>, status: PickOrderStatus): List<PickOrder>

fun findPickOrderInfoByIdIn(id: List<Serializable>): List<PickOrderInfo>

fun findAllByConsoCode(consoCode: String): List<PickOrder>
}

+ 154
- 10
src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt Ver fichero

@@ -1,17 +1,21 @@
package com.ffii.fpsms.modules.pickOrder.service

import com.ffii.core.response.RecordsRes
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.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.web.models.ConsoPickOrderRequest
import com.ffii.fpsms.modules.pickOrder.web.models.SearchPickOrderRequest
import com.ffii.fpsms.modules.pickOrder.web.models.*
import com.ffii.fpsms.modules.stock.service.StockOutLineService
import com.ffii.fpsms.modules.stock.service.SuggestedPickLotService
import com.ffii.fpsms.modules.stock.web.model.SuggestedPickLotForPoRequest
import org.springframework.context.annotation.Lazy
import org.springframework.data.domain.PageRequest
import org.springframework.stereotype.Service
import java.io.Serializable
import java.math.BigDecimal
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
@@ -19,10 +23,12 @@ import java.time.format.DateTimeFormatter
@Service
open class PickOrderService(
val pickOrderRepository: PickOrderRepository,
val stockOutLineService: StockOutLineService,
val suggestedPickLotService: SuggestedPickLotService,
) {
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 {
val formatter = DateTimeFormatter.ofPattern(pattern)
val formatter = DateTimeFormatter.ofPattern(pattern!!)
return LocalDateTime.parse(dateTime, formatter)
} catch (e: Exception) {
return null
@@ -38,8 +44,8 @@ open class PickOrderService(

val response = pickOrderRepository.findPickOrderInfoByConditionsAndPageable(
code = request.code ?: "all",
targetDateFrom = LocalDateTimeParse(request.targetDateFrom),
targetDateTo = LocalDateTimeParse(request.targetDateTo),
targetDateFrom = localDateTimeParse(request.targetDateFrom),
targetDateTo = localDateTimeParse(request.targetDateTo),
type = request.type ?: "all",
status = request.status ?: "all",
itemName = request.itemName ?: "all",
@@ -76,26 +82,164 @@ open class PickOrderService(

open fun consoPickOrders(request: ConsoPickOrderRequest): List<PickOrderInfo> {
val newConsoCode = assignConsoCode()
val pickOrders = pickOrderRepository.findAllByIdInAndConsoCodeIsNullAndStatus(request.ids, PickOrderStatus.PENDING)
val pickOrders = pickOrderRepository.findAllByIdInAndStatus(request.ids, PickOrderStatus.PENDING)
pickOrders.forEach {
it.consoCode = newConsoCode
}

val updatedPickOrders = pickOrderRepository.saveAll(pickOrders)
val updatedPickOrderInfos = updatedPickOrders.map { po -> po.id as Serializable }.let { pickOrderRepository.findPickOrderInfoByIdIn(it) }
val updatedPickOrderInfos = updatedPickOrders.map { po -> po.id as Serializable }
.let { pickOrderRepository.findPickOrderInfoByIdIn(it) }

return updatedPickOrderInfos
}

open fun deconsoPickOrders(request: ConsoPickOrderRequest): List<PickOrderInfo> {
val pickOrders = pickOrderRepository.findAllByIdInAndStatus(request.ids, PickOrderStatus.PENDING)
val pickOrders = pickOrderRepository.findAllByIdInAndStatus(request.ids, PickOrderStatus.CONSOLIDATED)
pickOrders.forEach {
it.consoCode = null
}

val updatedPickOrders = pickOrderRepository.saveAll(pickOrders)
val updatedPickOrderInfos = updatedPickOrders.map { po -> po.id as Serializable }.let { pickOrderRepository.findPickOrderInfoByIdIn(it) }
val updatedPickOrderInfos = updatedPickOrders.map { po -> po.id as Serializable }
.let { pickOrderRepository.findPickOrderInfoByIdIn(it) }

return updatedPickOrderInfos
}

// TODO: Add actual pick lots
open fun consoPickOrderDetail(consoCode: String): ConsoPickOrderResponse {
val zero = BigDecimal.ZERO

// pick orders
val pos = pickOrderRepository.findAllByConsoCode(consoCode)

// Suggestions for Pick Order
val suggestions = suggestedPickLotService.suggestionForPickOrders(SuggestedPickLotForPoRequest(pickOrders = pos))
val suggestedList = suggestions.suggestedList

// Mapping: PickOrder -> PickOrderInConso
val finalPos = pos.map { po ->
val pols = po.pickOrderLines

// Suggestions for Pick Order Line
// val suggestions = suggestedPickLotService.suggestionForPickOrderLines(pols)

// Pick Order Lines
// Mapping: PickOrderLine -> PickOrderLineInConso
val finalPols = pols.map { pol ->
val item = pol.item
val uom = pol.uom

// Check If already have suggestion
var suggestion = pol.suggestedPickLots
if (suggestion.isEmpty()) {
suggestion = suggestedList.filter { it.pickOrderLine?.id == pol.id }.toMutableList()
}

// Mapping: SuggestedPickLot -> SuggestPickLotInConso
val finalSuggestion = suggestion.map {
val inventoryLotLine = it.suggestedLotLine
val remainingQty = (inventoryLotLine?.inQty ?: zero)
.minus(inventoryLotLine?.outQty ?: zero)
.minus(inventoryLotLine?.holdQty ?: zero)
val stockUom = inventoryLotLine?.stockUom?.uom

// Mapping: InventoryLotLine -> InventoryLotLineInConso
val finalInventoryLotLine = InventoryLotLineInConso(
id = inventoryLotLine?.id,
lotNo = inventoryLotLine?.inventoryLot?.lotNo,
inQty = inventoryLotLine?.inQty,
outQty = inventoryLotLine?.outQty,
holdQty = inventoryLotLine?.holdQty,
remainingQty = remainingQty,
stockUom = IdCodeDesc(stockUom?.id, stockUom?.code, stockUom?.udfudesc),
status = inventoryLotLine?.status?.value,
remarks = inventoryLotLine?.remarks
)

// Return
SuggestPickLotInConso(
id = it.id,
type = it.type?.value,
qty = it.qty,
inventoryLotLine = finalInventoryLotLine,
pickSuggested = it.pickSuggested
)
}

// Return
PickOrderLineInConso(
id = pol.id,
item = IdCodeName(item?.id, item?.code, item?.name),
qty = pol.qty,
uom = IdCodeDesc(uom?.id, uom?.code, uom?.udfudesc),
suggestPickLots = finalSuggestion,
status = pol.status?.value,
actualPickLots = mutableListOf()
)
}

val releasedBy = po.releasedBy
val assignTo = po.assignTo

// Return
PickOrderInConso(
id = po.id,
code = po.code,
targetDate = po.targetDate,
completeDate = po.completeDate,
releasedDate = po.releasedDate,
releasedBy = IdName(releasedBy?.id, releasedBy?.name),
assignTo = IdName(assignTo?.id, assignTo?.name),
pickOrderLines = finalPols,
type = po.type?.value,
status = po.status?.value,
)
}

// Items
val finalItems = finalPos
.flatMap { it.pickOrderLines }
.groupBy { it.item.id }
.map { (_itemId, _line) ->
val itemSuggestions = _line
.flatMap { it.suggestPickLots }
.groupBy { it.inventoryLotLine.id to it.pickSuggested }
.map{ (_key, _lot) ->
SuggestPickLotInConso(
id = _key.first,
type = _lot.first().type,
inventoryLotLine = _lot.first().inventoryLotLine,
qty = _lot.fold(zero) { sum, item -> sum + (item.qty ?: zero)},
pickSuggested = _key.second
)
}

ItemInConso(
id = _line.first().item.id,
code = _line.first().item.code,
name = _line.first().item.name,
qty = _line.fold(zero) { sum, item -> sum + (item.qty ?: zero)},
suggestPickLots = itemSuggestions,
actualPickLots = mutableListOf()
)
}

val response = ConsoPickOrderResponse(
consoCode = consoCode,
pickOrders = finalPos,
items = finalItems
)

return response
}

// open fun releaseConsoPickOrder(request: ReleaseConsoPickOrderRequest): MessageResponse {
// val pos = pickOrderRepository.findAllByConsoCode(request.consoCode)
// val suggestedPickLots = suggestedPickLotService.convertRequestsToEntities(request.suggestedPickLots)
//
// suggestedPickLotService.saveAll(suggestedPickLots)
// // TODO: Add Response
// }
}

+ 7
- 0
src/main/java/com/ffii/fpsms/modules/pickOrder/web/PickOrderController.kt Ver fichero

@@ -5,6 +5,7 @@ import com.ffii.fpsms.modules.pickOrder.entity.PickOrderRepository
import com.ffii.fpsms.modules.pickOrder.entity.projection.PickOrderInfo
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.SearchPickOrderRequest
import jakarta.validation.Valid
import org.springframework.data.domain.Page
@@ -12,6 +13,7 @@ import org.springframework.data.domain.PageRequest
import org.springframework.data.domain.Pageable
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.ModelAttribute
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
@@ -47,4 +49,9 @@ class PickOrderController(
fun deconsoPickOrders(@Valid @RequestBody request: ConsoPickOrderRequest): List<PickOrderInfo> {
return pickOrderService.deconsoPickOrders(request)
}

@GetMapping("/consoDetail/{consoCode}")
fun consoPickOrderDetail(@PathVariable consoCode: String): ConsoPickOrderResponse {
return pickOrderService.consoPickOrderDetail(consoCode);
}
}

+ 9
- 0
src/main/java/com/ffii/fpsms/modules/pickOrder/web/models/ConsoPickOrderRequest.kt Ver fichero

@@ -1,5 +1,14 @@
package com.ffii.fpsms.modules.pickOrder.web.models

import com.ffii.fpsms.modules.stock.web.model.SaveSuggestedPickLotRequest

// Consolidated / De-consolidated
data class ConsoPickOrderRequest (
val ids: List<Long>
)

// Release Consolidated Pick Order
data class ReleaseConsoPickOrderRequest (
val consoCode: String,
val suggestedPickLots: List<SaveSuggestedPickLotRequest>
)

+ 89
- 0
src/main/java/com/ffii/fpsms/modules/pickOrder/web/models/ConsoPickOrderResponse.kt Ver fichero

@@ -0,0 +1,89 @@
package com.ffii.fpsms.modules.pickOrder.web.models

import java.math.BigDecimal
import java.time.LocalDateTime

// Final Response
data class ConsoPickOrderResponse(
val consoCode: String,
val pickOrders: List<PickOrderInConso>,
val items: List<ItemInConso>
)

// Components
data class PickOrderInConso(
val id: Long?,
val code: String?,
val targetDate: LocalDateTime?,
val completeDate: LocalDateTime?,
val releasedDate: LocalDateTime?,
val releasedBy: IdName,
val assignTo: IdName,
val pickOrderLines: List<PickOrderLineInConso>,
val type: String?,
val status: String?,
)

data class PickOrderLineInConso(
val id: Long?,
val item: IdCodeName,
val qty: BigDecimal?,
val uom: IdCodeDesc,
val status: String?,
val suggestPickLots: List<SuggestPickLotInConso>,
val actualPickLots: List<ActualPickLotInConso>,
)

data class SuggestPickLotInConso(
val id: Long?,
val type: String?,
val inventoryLotLine: InventoryLotLineInConso,
val qty: BigDecimal?,
val pickSuggested: Boolean?,
)

data class ActualPickLotInConso(
val id: Long,
val type: String,
val inventoryLotLine: InventoryLotLineInConso,
val qty: BigDecimal,
)

data class InventoryLotLineInConso(
val id: Long?,
val lotNo: String?,
val inQty: BigDecimal?,
val outQty: BigDecimal?,
val holdQty: BigDecimal?,
val remainingQty: BigDecimal?,
val stockUom: IdCodeDesc,
val status: String?,
val remarks: String?,
)

data class ItemInConso(
val id: Long?,
val code: String?,
val name: String?,
val qty: BigDecimal?,
val suggestPickLots: List<SuggestPickLotInConso>,
val actualPickLots: List<ActualPickLotInConso>,
)

// Common
data class IdName(
val id: Long?,
val name: String?,
)

data class IdCodeName(
val id: Long?,
val code: String?,
val name: String?,
)

data class IdCodeDesc(
val id: Long?,
val code: String?,
val desc: String?,
)

+ 7
- 1
src/main/java/com/ffii/fpsms/modules/stock/entity/InventoryLotLine.kt Ver fichero

@@ -1,9 +1,13 @@
package com.ffii.fpsms.modules.stock.entity

import com.fasterxml.jackson.annotation.JsonBackReference
import com.ffii.core.entity.BaseEntity
import com.ffii.fpsms.modules.master.entity.ItemUom
import com.ffii.fpsms.modules.master.entity.Warehouse
import com.ffii.fpsms.modules.stock.entity.enum.InventoryLotLineStatus
import com.ffii.fpsms.modules.stock.entity.enum.InventoryLotLineStringConverter
import jakarta.persistence.Column
import jakarta.persistence.Convert
import jakarta.persistence.Entity
import jakarta.persistence.JoinColumn
import jakarta.persistence.ManyToOne
@@ -16,6 +20,7 @@ import java.math.BigDecimal
@Entity
@Table(name = "inventory_lot_line")
open class InventoryLotLine : BaseEntity<Long>() {
// @JsonBackReference
@NotNull
@ManyToOne
@JoinColumn(name = "inventoryLotId")
@@ -40,8 +45,9 @@ open class InventoryLotLine : BaseEntity<Long>() {
@Column(name = "holdQty")
open var holdQty: BigDecimal? = null

@Convert(converter = InventoryLotLineStringConverter::class)
@Column(name = "status")
open var status: String? = null
open var status: InventoryLotLineStatus? = null

@Column(name = "remarks")
open var remarks: String? = null


+ 3
- 0
src/main/java/com/ffii/fpsms/modules/stock/entity/InventoryLotLineRepository.kt Ver fichero

@@ -1,8 +1,11 @@
package com.ffii.fpsms.modules.stock.entity

import com.ffii.core.support.AbstractRepository
import com.ffii.fpsms.modules.stock.entity.projection.InventoryLotLineInfo
import org.springframework.stereotype.Repository
import java.io.Serializable

@Repository
interface InventoryLotLineRepository : AbstractRepository<InventoryLotLine, Long> {
fun findInventoryLotLineInfoByInventoryLotItemIdIn(ids: List<Serializable>): List<InventoryLotLineInfo>
}

+ 5
- 1
src/main/java/com/ffii/fpsms/modules/stock/entity/StockInLine.kt Ver fichero

@@ -69,10 +69,14 @@ open class StockInLine : BaseEntity<Long>() {
@JoinColumn(name = "userId")
open var user: User? = null

@OneToOne
@ManyToOne
@JoinColumn(name = "inventoryLotId")
open var inventoryLot: InventoryLot? = null

@OneToOne
@JoinColumn(name = "inventoryLotLineId")
open var inventoryLotLine: InventoryLotLine? = null

@Column(name = "lotNo")
open var lotNo: String? = null



+ 2
- 0
src/main/java/com/ffii/fpsms/modules/stock/entity/SuggestPickLotRepository.kt Ver fichero

@@ -1,8 +1,10 @@
package com.ffii.fpsms.modules.stock.entity

import com.ffii.core.support.AbstractRepository
import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLine
import org.springframework.stereotype.Repository

@Repository
interface SuggestPickLotRepository : AbstractRepository<SuggestedPickLot, Long> {
fun findAllByPickOrderLineIn(lines: List<PickOrderLine>): List<SuggestedPickLot>
}

+ 3
- 3
src/main/java/com/ffii/fpsms/modules/stock/entity/SuggestedPickLot.kt Ver fichero

@@ -19,14 +19,14 @@ open class SuggestedPickLot: BaseEntity<Long>() {
@Column(name = "type", nullable = false, length = 100)
open var type: SuggestedPickLotType? = null

@JsonBackReference
// @JsonBackReference
@ManyToOne
@JoinColumn(name = "stockOutLineId")
open var stockOutLine: StockOutLine? = null

@JsonBackReference
// @JsonBackReference
@ManyToOne
@JoinColumn(name = "suggestedLotLineId", nullable = false)
@JoinColumn(name = "suggestedLotLineId")
open var suggestedLotLine: InventoryLotLine? = null

@JsonBackReference


+ 6
- 0
src/main/java/com/ffii/fpsms/modules/stock/entity/enum/InventoryLotLineEnum.kt Ver fichero

@@ -0,0 +1,6 @@
package com.ffii.fpsms.modules.stock.entity.enum

enum class InventoryLotLineStatus(val value: String) {
AVAILABLE ("available"),
UNAVAILABLE ("unavailable")
}

+ 17
- 0
src/main/java/com/ffii/fpsms/modules/stock/entity/enum/InventoryLotLineEnumConverter.kt Ver fichero

@@ -0,0 +1,17 @@
package com.ffii.fpsms.modules.stock.entity.enum

import jakarta.persistence.AttributeConverter
import jakarta.persistence.Converter

@Converter(autoApply = true)
class InventoryLotLineStringConverter: AttributeConverter<InventoryLotLineStatus, String> {
override fun convertToDatabaseColumn(status: InventoryLotLineStatus?): String? {
return status?.value
}

override fun convertToEntityAttribute(value: String?): InventoryLotLineStatus? {
return value?.let { v ->
InventoryLotLineStatus.entries.find { it.value == v }
}
}
}

+ 36
- 0
src/main/java/com/ffii/fpsms/modules/stock/entity/projection/InventoryLotLineInfo.kt Ver fichero

@@ -0,0 +1,36 @@
package com.ffii.fpsms.modules.stock.entity.projection

import org.springframework.beans.factory.annotation.Value
import java.math.BigDecimal
import java.time.LocalDate

interface InventoryLotLineItemInfo {
val id: Long
val code: String
val name: String
}

interface InventoryLotLineWarehouseInfo {
val id: Long
val code: String
val name: String
val description: String
val capacity: BigDecimal
}

interface InventoryLotLineInfo {
val id: Long?
@get:Value("#{target.inventoryLot.item}")
val item: InventoryLotLineItemInfo?
val warehouse: InventoryLotLineWarehouseInfo?
var inQty: BigDecimal?
var outQty: BigDecimal?
var holdQty: BigDecimal?
@get:Value("#{target.status.value}")
val status: String?
val remarks: String?
@get:Value("#{target.stockUom.uom.udfudesc}")
val uom: String?
@get:Value("#{target.inventoryLot.expiryDate}")
val expiryDate: LocalDate
}

+ 64
- 0
src/main/java/com/ffii/fpsms/modules/stock/service/InventoryLotLineService.kt Ver fichero

@@ -0,0 +1,64 @@
package com.ffii.fpsms.modules.stock.service

import com.ffii.fpsms.modules.master.entity.ItemUomRespository
import com.ffii.fpsms.modules.master.entity.WarehouseRepository
import com.ffii.fpsms.modules.stock.entity.InventoryLotLine
import com.ffii.fpsms.modules.stock.entity.InventoryLotLineRepository
import com.ffii.fpsms.modules.stock.entity.InventoryLotRepository
import com.ffii.fpsms.modules.stock.entity.enum.InventoryLotLineStatus
import com.ffii.fpsms.modules.stock.entity.projection.InventoryLotLineInfo
import com.ffii.fpsms.modules.stock.web.model.SaveInventoryLotLineRequest
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Propagation
import org.springframework.transaction.annotation.Transactional
import java.math.BigDecimal
import java.util.Optional
import kotlin.jvm.optionals.getOrNull

@Service
open class InventoryLotLineService(
private val inventoryLotLineRepository: InventoryLotLineRepository,
private val inventoryLotRepository: InventoryLotRepository,
private val warehouseRepository: WarehouseRepository,
private val itemUomRespository: ItemUomRespository
) {
open fun findById(id: Long): Optional<InventoryLotLine> {
return inventoryLotLineRepository.findById(id)
}

open fun allInventoryLotLinesByItemIdIn(itemIds: List<Long>): List<InventoryLotLineInfo> {
return inventoryLotLineRepository.findInventoryLotLineInfoByInventoryLotItemIdIn(itemIds)
}

open fun saveInventoryLotLine(request: SaveInventoryLotLineRequest): InventoryLotLine {
val inventoryLotLine =
request.id?.let { inventoryLotLineRepository.findById(it).getOrNull() } ?: InventoryLotLine()
val inventoryLot = request.inventoryLotId?.let { inventoryLotRepository.findById(it).getOrNull() }
val warehouse = request.warehouseId?.let { warehouseRepository.findById(it).getOrNull() }
val stockUom = request.stockUomId?.let { itemUomRespository.findById(it).getOrNull() }
val remainingQty =
(request.inQty ?: BigDecimal(0)) - (request.outQty ?: BigDecimal(0)) - (request.holdQty ?: BigDecimal(0))
val status = request.status?.let { _status -> InventoryLotLineStatus.entries.find { it.value == _status } }
val qtyStatus = when (remainingQty > BigDecimal(0)) {
true -> InventoryLotLineStatus.AVAILABLE
else -> InventoryLotLineStatus.UNAVAILABLE
}

inventoryLotLine.apply {
this.inventoryLot = inventoryLot
this.warehouse = warehouse
inQty = request.inQty
outQty = request.outQty
holdQty = request.holdQty
this.stockUom = stockUom
this.status =
when (status == InventoryLotLineStatus.AVAILABLE && qtyStatus == InventoryLotLineStatus.AVAILABLE) {
true -> InventoryLotLineStatus.AVAILABLE
else -> InventoryLotLineStatus.UNAVAILABLE
}
remarks = request.remarks
}

return inventoryLotLineRepository.save(inventoryLotLine)
}
}

+ 4
- 1
src/main/java/com/ffii/fpsms/modules/stock/service/StockInLineService.kt Ver fichero

@@ -30,6 +30,7 @@ import com.ffii.fpsms.modules.master.entity.WarehouseRepository
import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrderRepository
import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderLineStatus
import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderStatus
import com.ffii.fpsms.modules.stock.entity.enum.InventoryLotLineStatus
import com.ffii.fpsms.modules.stock.entity.projection.StockInLineInfo
import java.io.FileNotFoundException
import java.time.format.DateTimeFormatter
@@ -143,7 +144,7 @@ open class StockInLineService(
this.inventoryLot = stockInLine.inventoryLot
this.warehouse = warehouse
this.inQty = convertedBaseQty
this.status = "available"
this.status = InventoryLotLineStatus.AVAILABLE
this.stockUom = stockItemUom
}
val savedInventoryLotLine = inventoryLotLineRepository.saveAndFlush(inventoryLotLine)
@@ -251,6 +252,7 @@ open class StockInLineService(
this.status = request.status
this.expiryDate = stockInLine.expiryDate ?: request.expiryDate
this.inventoryLot = stockInLine.inventoryLot ?: savedInventoryLot
this.inventoryLotLine = savedInventoryLotLine
this.lotNo = stockInLine.lotNo ?: savedInventoryLot?.lotNo
}
val savedStockInLine = saveAndFlush(stockInLine)
@@ -320,6 +322,7 @@ open class StockInLineService(
this.acceptedQty = request.acceptedQty
this.status = request.status
this.inventoryLot = savedInventoryLot ?: stockInLine.inventoryLot
this.inventoryLotLine = savedInventoryLotLine
this.lotNo = savedInventoryLot?.lotNo ?: stockInLine.lotNo
this.expiryDate = stockInLine.expiryDate ?: request.expiryDate
this.productLotNo = stockInLine.productLotNo ?: request.productLotNo


+ 142
- 0
src/main/java/com/ffii/fpsms/modules/stock/service/SuggestedPickLotService.kt Ver fichero

@@ -0,0 +1,142 @@
package com.ffii.fpsms.modules.stock.service

import com.ffii.fpsms.modules.pickOrder.entity.PickOrder
import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLine
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.enum.InventoryLotLineStatus
import com.ffii.fpsms.modules.stock.entity.projection.InventoryLotLineInfo
import com.ffii.fpsms.modules.stock.enums.SuggestedPickLotType
import com.ffii.fpsms.modules.stock.web.model.SaveSuggestedPickLotRequest
import com.ffii.fpsms.modules.stock.web.model.SuggestedPickLotForPoRequest
import com.ffii.fpsms.modules.stock.web.model.SuggestedPickLotForPolRequest
import com.ffii.fpsms.modules.stock.web.model.SuggestedPickLotResponse
import org.springframework.stereotype.Service
import java.math.BigDecimal
import kotlin.jvm.optionals.getOrDefault
import kotlin.jvm.optionals.getOrNull

@Service
open class SuggestedPickLotService(
val suggestedPickLotRepository: SuggestPickLotRepository,
val stockOutLIneRepository: StockOutLIneRepository,
val inventoryLotLineRepository: InventoryLotLineRepository,
val pickOrderLineRepository: PickOrderLineRepository,
val inventoryLotLineService: InventoryLotLineService,
) {
open fun suggestionForPickOrders(request: SuggestedPickLotForPoRequest): SuggestedPickLotResponse {
val pos = request.pickOrders
val suggestedList = mutableListOf<SuggestedPickLot>()
var holdQtyMap = request.holdQtyMap

pos.forEach {
val response = suggestionForPickOrderLines(SuggestedPickLotForPolRequest(
holdQtyMap = holdQtyMap,
pickOrderLines = it.pickOrderLines
))

holdQtyMap = response.holdQtyMap
suggestedList += response.suggestedList
}

return SuggestedPickLotResponse(holdQtyMap = holdQtyMap, suggestedList = suggestedList)
}

open fun suggestionForPickOrderLines(request: SuggestedPickLotForPolRequest): SuggestedPickLotResponse {
val pols = request.pickOrderLines
val itemIds = pols.mapNotNull { it.item?.id }
val zero = BigDecimal.ZERO

val suggestedList: MutableList<SuggestedPickLot> = mutableListOf()
val holdQtyMap: MutableMap<Long?, BigDecimal?> = request.holdQtyMap

// get current inventory lot line qty & grouped by item Id
val availableInventoryLotLines = inventoryLotLineService
.allInventoryLotLinesByItemIdIn(itemIds)
.filter { (it.inQty ?: zero).minus(it.outQty ?: zero).minus(it.holdQty ?: zero) > zero }
.filter { it.status == InventoryLotLineStatus.AVAILABLE.value }
.sortedBy { it.expiryDate }
.groupBy { it.item?.id }

// loop for suggest pick lot line
pols.forEach { line ->
val lotLines = availableInventoryLotLines[line.item?.id].orEmpty()
var remainingQty = line.qty ?: zero
val updatedLotLines = mutableListOf<InventoryLotLineInfo>()

lotLines.forEachIndexed { index, lotLine ->
if (remainingQty <= zero) return@forEachIndexed

val availableQty = (lotLine.inQty ?: zero)
.minus(lotLine.outQty ?: zero)
.minus((lotLine.holdQty ?: zero)
.plus(holdQtyMap[lotLine.id] ?: zero)
)

if (availableQty <= zero) {
updatedLotLines += lotLine
return@forEachIndexed
}
val inventoryLotLine = lotLine.id?.let { inventoryLotLineService.findById(it).getOrNull() }
val originalHoldQty = inventoryLotLine?.holdQty

// Update Qty
val assignQty = minOf(availableQty, remainingQty)
remainingQty = remainingQty.minus(assignQty)
holdQtyMap[lotLine.id] = (holdQtyMap[lotLine.id] ?: zero).plus(assignQty)
// lotLine.holdQty = lotLine.holdQty?.plus(assignQty)
suggestedList += SuggestedPickLot().apply {
type = SuggestedPickLotType.PICK_ORDER
suggestedLotLine = inventoryLotLine
pickOrderLine = line
qty = assignQty
}
}

// if still have remainingQty
if (remainingQty > zero) {
suggestedList += SuggestedPickLot().apply {
type = SuggestedPickLotType.PICK_ORDER
suggestedLotLine = null
pickOrderLine = line
qty = remainingQty
}
}
}
return SuggestedPickLotResponse(holdQtyMap = holdQtyMap, suggestedList = suggestedList)
}

open fun convertRequestToEntity(request: SaveSuggestedPickLotRequest): SuggestedPickLot{
val suggestedPickLot =
request.id?.let { id -> suggestedPickLotRepository.findById(id).getOrDefault(SuggestedPickLot()) }
?: SuggestedPickLot()
val type = request.type?.let { _type -> SuggestedPickLotType.entries.find { it.value == _type } }
val stockOutLine = request.stockOutLineId?.let { id -> stockOutLIneRepository.findById(id).getOrNull() }
val suggestedLotLine =
request.suggestedLotLineId?.let { id -> inventoryLotLineRepository.findById(id).getOrNull() }
val pickOrderLine = request.pickOrderLineId?.let { id -> pickOrderLineRepository.findById(id).getOrNull() }

suggestedPickLot.apply {
this.type = type
this.stockOutLine = stockOutLine
this.suggestedLotLine = suggestedLotLine
this.pickOrderLine = pickOrderLine
qty = request.qty
}

return suggestedPickLot
}

open fun saveSuggestedPickLot(request: SaveSuggestedPickLotRequest): SuggestedPickLot {
val suggestedPickLot = convertRequestToEntity(request)

return suggestedPickLotRepository.save(suggestedPickLot)
}

open fun saveAll(request: List<SuggestedPickLot>): List<SuggestedPickLot> {
return suggestedPickLotRepository.saveAll(request)
}
}

+ 23
- 0
src/main/java/com/ffii/fpsms/modules/stock/web/InventoryLotLineController.kt Ver fichero

@@ -0,0 +1,23 @@
package com.ffii.fpsms.modules.stock.web

import com.ffii.fpsms.modules.pickOrder.web.models.ConsoPickOrderRequest
import com.ffii.fpsms.modules.stock.entity.InventoryLotLineRepository
import jakarta.validation.Valid
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController

@RequestMapping("/inventoryLotLine")
@RestController
class InventoryLotLineController (
private val inventoryLotLineRepository: InventoryLotLineRepository

){
// @PostMapping("/test")
// fun test(@Valid @RequestBody request: ConsoPickOrderRequest) :Any {
// return inventoryLotLineRepository.findInventoryLotLineInfoByInventoryLotItemIdIn(request.ids)
// }
}

+ 26
- 0
src/main/java/com/ffii/fpsms/modules/stock/web/SuggestedPickLotController.kt Ver fichero

@@ -0,0 +1,26 @@
package com.ffii.fpsms.modules.stock.web

import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLineRepository
import com.ffii.fpsms.modules.pickOrder.entity.PickOrderRepository
import com.ffii.fpsms.modules.pickOrder.enums.PickOrderStatus
import com.ffii.fpsms.modules.purchaseOrder.service.PurchaseOrderLineService
import com.ffii.fpsms.modules.stock.service.SuggestedPickLotService
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RequestMapping("/suggestedPickLot")
@RestController
class SuggestedPickLotController(
val suggestedPickLotService: SuggestedPickLotService,
val pickOrderLineService: PurchaseOrderLineService,
val pickOrderRepository: PickOrderRepository
) {
// @GetMapping("/test/{conso}")
// fun test(@PathVariable conso: String): Any {
// val test1 = pickOrderRepository.findAllByConsoCode(conso).flatMap { it.pickOrderLines }
// return suggestedPickLotService.suggestionForPickOrderLines(test1)
// }
}

+ 15
- 0
src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveInventoryLotLineRequest.kt Ver fichero

@@ -0,0 +1,15 @@
package com.ffii.fpsms.modules.stock.web.model

import java.math.BigDecimal

data class SaveInventoryLotLineRequest(
val id: Long?,
val inventoryLotId: Long?,
val warehouseId: Long?,
val inQty: BigDecimal?,
val outQty: BigDecimal?,
val holdQty: BigDecimal?,
val stockUomId: Long?,
val status: String?,
val remarks: String?
)

+ 12
- 0
src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveSuggestedPickLotRequest.kt Ver fichero

@@ -0,0 +1,12 @@
package com.ffii.fpsms.modules.stock.web.model

import java.math.BigDecimal

data class SaveSuggestedPickLotRequest (
val id: Long?,
val type: String?,
val stockOutLineId: Long?,
val suggestedLotLineId: Long?,
val pickOrderLineId: Long?,
val qty: BigDecimal?
)

+ 17
- 0
src/main/java/com/ffii/fpsms/modules/stock/web/model/SuggestedPickLotRequest.kt Ver fichero

@@ -0,0 +1,17 @@
package com.ffii.fpsms.modules.stock.web.model

import com.ffii.fpsms.modules.pickOrder.entity.PickOrder
import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLine
import java.math.BigDecimal

data class SuggestedPickLotForPoRequest(
val holdQtyMap: MutableMap<Long?, BigDecimal?> = mutableMapOf(),
val pickOrders: List<PickOrder>
)

data class SuggestedPickLotForPolRequest(
val holdQtyMap: MutableMap<Long?, BigDecimal?> = mutableMapOf(),
val pickOrderLines: List<PickOrderLine>
)



+ 16
- 0
src/main/java/com/ffii/fpsms/modules/stock/web/model/SuggestedPickLotResponse.kt Ver fichero

@@ -0,0 +1,16 @@
package com.ffii.fpsms.modules.stock.web.model

import com.ffii.fpsms.modules.stock.entity.SuggestedPickLot
import java.math.BigDecimal

//data class SuggestInventoryLotLine(
// val type: String?,
// val suggestedLotLine: InventoryLotLine?,
// val pickOrderLine: PickOrderLine?,
// val qty: BigDecimal?
//)

data class SuggestedPickLotResponse(
val holdQtyMap: MutableMap<Long?, BigDecimal?>,
val suggestedList: List<SuggestedPickLot>
)

+ 7
- 0
src/main/resources/db/changelog/changes/20250620_01_derek/01_add_lot_line_id_to_stock_in_line.sql Ver fichero

@@ -0,0 +1,7 @@
-- liquibase formatted sql
-- changeset derek:add_lot_line_id_to_stock_in_line

ALTER TABLE `stock_in_line`
ADD COLUMN `inventoryLotLineId` INT(11) NULL AFTER `inventoryLotId`,
ADD CONSTRAINT FK_STOCK_IN_LINE_ON_LOT_LINE_ID FOREIGN KEY (inventoryLotLineId) REFERENCES inventory_lot_line (id)
;

Cargando…
Cancelar
Guardar