浏览代码

Add suggested pick lot & conso pick order detail

master
cyril.tsui 2 个月前
父节点
当前提交
0064be31f2
共有 20 个文件被更改,包括 542 次插入45 次删除
  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. +140
    -7
      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. +37
    -31
      src/main/java/com/ffii/fpsms/modules/pickOrder/web/models/ConsoPickOrderResponse.kt
  6. +7
    -1
      src/main/java/com/ffii/fpsms/modules/stock/entity/InventoryLotLine.kt
  7. +3
    -0
      src/main/java/com/ffii/fpsms/modules/stock/entity/InventoryLotLineRepository.kt
  8. +2
    -0
      src/main/java/com/ffii/fpsms/modules/stock/entity/SuggestPickLotRepository.kt
  9. +3
    -3
      src/main/java/com/ffii/fpsms/modules/stock/entity/SuggestedPickLot.kt
  10. +6
    -0
      src/main/java/com/ffii/fpsms/modules/stock/entity/enum/InventoryLotLineEnum.kt
  11. +17
    -0
      src/main/java/com/ffii/fpsms/modules/stock/entity/enum/InventoryLotLineEnumConverter.kt
  12. +36
    -0
      src/main/java/com/ffii/fpsms/modules/stock/entity/projection/InventoryLotLineInfo.kt
  13. +64
    -0
      src/main/java/com/ffii/fpsms/modules/stock/service/InventoryLotLineService.kt
  14. +2
    -1
      src/main/java/com/ffii/fpsms/modules/stock/service/StockInLineService.kt
  15. +124
    -0
      src/main/java/com/ffii/fpsms/modules/stock/service/SuggestedPickLotService.kt
  16. +23
    -0
      src/main/java/com/ffii/fpsms/modules/stock/web/InventoryLotLineController.kt
  17. +25
    -0
      src/main/java/com/ffii/fpsms/modules/stock/web/SuggestedPickLotController.kt
  18. +15
    -0
      src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveInventoryLotLineRequest.kt
  19. +12
    -0
      src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveSuggestedPickLotRequest.kt
  20. +13
    -0
      src/main/java/com/ffii/fpsms/modules/stock/web/model/SuggestInventoryLotLineResponse.kt

+ 4
- 2
src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickOrderLine.kt 查看文件

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


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


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


+ 2
- 0
src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickOrderRepository.kt 查看文件

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


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

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

+ 140
- 7
src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt 查看文件

@@ -6,12 +6,15 @@ 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.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.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.SuggestInventoryLotLineResponse
import org.springframework.context.annotation.Lazy 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
import java.math.BigDecimal
import java.time.LocalDate import java.time.LocalDate
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
@@ -19,10 +22,12 @@ import java.time.format.DateTimeFormatter
@Service @Service
open class PickOrderService( open class PickOrderService(
val pickOrderRepository: PickOrderRepository, 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 { try {
val formatter = DateTimeFormatter.ofPattern(pattern)
val formatter = DateTimeFormatter.ofPattern(pattern!!)
return LocalDateTime.parse(dateTime, formatter) return LocalDateTime.parse(dateTime, formatter)
} catch (e: Exception) { } catch (e: Exception) {
return null return null
@@ -76,26 +81,154 @@ open class PickOrderService(


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


val updatedPickOrders = pickOrderRepository.saveAll(pickOrders) 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 return updatedPickOrderInfos
} }


open fun deconsoPickOrders(request: ConsoPickOrderRequest): List<PickOrderInfo> { 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 { pickOrders.forEach {
it.consoCode = null it.consoCode = null
} }


val updatedPickOrders = pickOrderRepository.saveAll(pickOrders) 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 return updatedPickOrderInfos
} }

open fun consoPickOrderDetail(consoCode: String): ConsoPickOrderResponse {
// Conso code
// Pick orders with items
// Items

val zero = BigDecimal.ZERO

// pick orders
// Mapping: PickOrder -> PickOrderInConso
val pos = pickOrderRepository.findAllByConsoCode(consoCode)
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 = suggestions.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,
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 = _line
.flatMap { it.suggestPickLots },
actualPickLots = mutableListOf()
)
}

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

return response
}
} }

+ 7
- 0
src/main/java/com/ffii/fpsms/modules/pickOrder/web/PickOrderController.kt 查看文件

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

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

+ 37
- 31
src/main/java/com/ffii/fpsms/modules/pickOrder/web/models/ConsoPickOrderResponse.kt 查看文件

@@ -12,33 +12,33 @@ data class ConsoPickOrderResponse(


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


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


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


@@ -50,33 +50,39 @@ data class ActualPickLotInConso(
) )


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


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


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

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


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

+ 7
- 1
src/main/java/com/ffii/fpsms/modules/stock/entity/InventoryLotLine.kt 查看文件

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


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


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


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


+ 3
- 0
src/main/java/com/ffii/fpsms/modules/stock/entity/InventoryLotLineRepository.kt 查看文件

@@ -1,8 +1,11 @@
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.InventoryLotLineInfo
import org.springframework.stereotype.Repository import org.springframework.stereotype.Repository
import java.io.Serializable


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

+ 2
- 0
src/main/java/com/ffii/fpsms/modules/stock/entity/SuggestPickLotRepository.kt 查看文件

@@ -1,8 +1,10 @@
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.pickOrder.entity.PickOrderLine
import org.springframework.stereotype.Repository import org.springframework.stereotype.Repository


@Repository @Repository
interface SuggestPickLotRepository : AbstractRepository<SuggestedPickLot, Long> { 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 查看文件

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


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


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


@JsonBackReference @JsonBackReference


+ 6
- 0
src/main/java/com/ffii/fpsms/modules/stock/entity/enum/InventoryLotLineEnum.kt 查看文件

@@ -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 查看文件

@@ -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 查看文件

@@ -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 查看文件

@@ -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)
}
}

+ 2
- 1
src/main/java/com/ffii/fpsms/modules/stock/service/StockInLineService.kt 查看文件

@@ -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.entity.PurchaseOrderRepository
import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderLineStatus import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderLineStatus
import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderStatus 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 com.ffii.fpsms.modules.stock.entity.projection.StockInLineInfo
import java.io.FileNotFoundException import java.io.FileNotFoundException
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
@@ -143,7 +144,7 @@ open class StockInLineService(
this.inventoryLot = stockInLine.inventoryLot this.inventoryLot = stockInLine.inventoryLot
this.warehouse = warehouse this.warehouse = warehouse
this.inQty = convertedBaseQty this.inQty = convertedBaseQty
this.status = "available"
this.status = InventoryLotLineStatus.AVAILABLE
this.stockUom = stockItemUom this.stockUom = stockItemUom
} }
val savedInventoryLotLine = inventoryLotLineRepository.saveAndFlush(inventoryLotLine) val savedInventoryLotLine = inventoryLotLineRepository.saveAndFlush(inventoryLotLine)


+ 124
- 0
src/main/java/com/ffii/fpsms/modules/stock/service/SuggestedPickLotService.kt 查看文件

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

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.SuggestInventoryLotLineResponse
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 suggestionForPickOrderLines(pickOrderlines: List<PickOrderLine>): List<SuggestedPickLot> {
val itemIds = pickOrderlines.mapNotNull { it.item?.id }
val zero = BigDecimal.ZERO

val suggestionList: MutableList<SuggestedPickLot> = mutableListOf()

// 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
pickOrderlines.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)

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

// Update Qty
val assignQty = minOf(availableQty, remainingQty)
remainingQty = remainingQty.minus(assignQty)
lotLine.holdQty = lotLine.holdQty?.plus(assignQty)
// println("2:${inventoryLotLine?.id}: ${inventoryLotLine?.holdQty}")
// println("3:${inventoryLotLine?.id}: ${originalHoldQty}")
suggestionList += SuggestedPickLot().apply {
type = SuggestedPickLotType.PICK_ORDER
suggestedLotLine = inventoryLotLine?.apply {
holdQty = originalHoldQty
}
pickOrderLine = line
qty = assignQty
}
// suggestionList += SuggestInventoryLotLineResponse(
// type = SuggestedPickLotType.PICK_ORDER.value,
// suggestedLotLine = inventoryLotLine,
// pickOrderLine = line,
// qty = assignQty
// )
}

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

open fun saveSuggestedPickLot(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 suggestedPickLotRepository.save(suggestedPickLot)
}
}

+ 23
- 0
src/main/java/com/ffii/fpsms/modules/stock/web/InventoryLotLineController.kt 查看文件

@@ -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)
}
}

+ 25
- 0
src/main/java/com/ffii/fpsms/modules/stock/web/SuggestedPickLotController.kt 查看文件

@@ -0,0 +1,25 @@
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.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 查看文件

@@ -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 查看文件

@@ -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?
)

+ 13
- 0
src/main/java/com/ffii/fpsms/modules/stock/web/model/SuggestInventoryLotLineResponse.kt 查看文件

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

import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLine
import com.ffii.fpsms.modules.stock.entity.InventoryLotLine
import com.ffii.fpsms.modules.stock.enums.SuggestedPickLotType
import java.math.BigDecimal

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

正在加载...
取消
保存