@@ -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) | ||||
@@ -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> | |||||
} | } |
@@ -1,17 +1,21 @@ | |||||
package com.ffii.fpsms.modules.pickOrder.service | package com.ffii.fpsms.modules.pickOrder.service | ||||
import com.ffii.core.response.RecordsRes | 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.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.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.SuggestedPickLotForPoRequest | |||||
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 +23,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 | ||||
@@ -38,8 +44,8 @@ open class PickOrderService( | |||||
val response = pickOrderRepository.findPickOrderInfoByConditionsAndPageable( | val response = pickOrderRepository.findPickOrderInfoByConditionsAndPageable( | ||||
code = request.code ?: "all", | code = request.code ?: "all", | ||||
targetDateFrom = LocalDateTimeParse(request.targetDateFrom), | |||||
targetDateTo = LocalDateTimeParse(request.targetDateTo), | |||||
targetDateFrom = localDateTimeParse(request.targetDateFrom), | |||||
targetDateTo = localDateTimeParse(request.targetDateTo), | |||||
type = request.type ?: "all", | type = request.type ?: "all", | ||||
status = request.status ?: "all", | status = request.status ?: "all", | ||||
itemName = request.itemName ?: "all", | itemName = request.itemName ?: "all", | ||||
@@ -76,26 +82,164 @@ 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 | ||||
} | } | ||||
// 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 | |||||
// } | |||||
} | } |
@@ -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); | |||||
} | |||||
} | } |
@@ -1,5 +1,14 @@ | |||||
package com.ffii.fpsms.modules.pickOrder.web.models | package com.ffii.fpsms.modules.pickOrder.web.models | ||||
import com.ffii.fpsms.modules.stock.web.model.SaveSuggestedPickLotRequest | |||||
// Consolidated / De-consolidated | |||||
data class ConsoPickOrderRequest ( | data class ConsoPickOrderRequest ( | ||||
val ids: List<Long> | val ids: List<Long> | ||||
) | |||||
// Release Consolidated Pick Order | |||||
data class ReleaseConsoPickOrderRequest ( | |||||
val consoCode: String, | |||||
val suggestedPickLots: List<SaveSuggestedPickLotRequest> | |||||
) | ) |
@@ -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?, | |||||
) |
@@ -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 | ||||
@@ -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> | |||||
} | } |
@@ -69,10 +69,14 @@ open class StockInLine : BaseEntity<Long>() { | |||||
@JoinColumn(name = "userId") | @JoinColumn(name = "userId") | ||||
open var user: User? = null | open var user: User? = null | ||||
@OneToOne | |||||
@ManyToOne | |||||
@JoinColumn(name = "inventoryLotId") | @JoinColumn(name = "inventoryLotId") | ||||
open var inventoryLot: InventoryLot? = null | open var inventoryLot: InventoryLot? = null | ||||
@OneToOne | |||||
@JoinColumn(name = "inventoryLotLineId") | |||||
open var inventoryLotLine: InventoryLotLine? = null | |||||
@Column(name = "lotNo") | @Column(name = "lotNo") | ||||
open var lotNo: String? = null | open var lotNo: String? = null | ||||
@@ -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> | |||||
} | } |
@@ -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 | ||||
@@ -0,0 +1,6 @@ | |||||
package com.ffii.fpsms.modules.stock.entity.enum | |||||
enum class InventoryLotLineStatus(val value: String) { | |||||
AVAILABLE ("available"), | |||||
UNAVAILABLE ("unavailable") | |||||
} |
@@ -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 } | |||||
} | |||||
} | |||||
} |
@@ -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 | |||||
} |
@@ -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) | |||||
} | |||||
} |
@@ -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) | ||||
@@ -251,6 +252,7 @@ open class StockInLineService( | |||||
this.status = request.status | this.status = request.status | ||||
this.expiryDate = stockInLine.expiryDate ?: request.expiryDate | this.expiryDate = stockInLine.expiryDate ?: request.expiryDate | ||||
this.inventoryLot = stockInLine.inventoryLot ?: savedInventoryLot | this.inventoryLot = stockInLine.inventoryLot ?: savedInventoryLot | ||||
this.inventoryLotLine = savedInventoryLotLine | |||||
this.lotNo = stockInLine.lotNo ?: savedInventoryLot?.lotNo | this.lotNo = stockInLine.lotNo ?: savedInventoryLot?.lotNo | ||||
} | } | ||||
val savedStockInLine = saveAndFlush(stockInLine) | val savedStockInLine = saveAndFlush(stockInLine) | ||||
@@ -320,6 +322,7 @@ open class StockInLineService( | |||||
this.acceptedQty = request.acceptedQty | this.acceptedQty = request.acceptedQty | ||||
this.status = request.status | this.status = request.status | ||||
this.inventoryLot = savedInventoryLot ?: stockInLine.inventoryLot | this.inventoryLot = savedInventoryLot ?: stockInLine.inventoryLot | ||||
this.inventoryLotLine = savedInventoryLotLine | |||||
this.lotNo = savedInventoryLot?.lotNo ?: stockInLine.lotNo | this.lotNo = savedInventoryLot?.lotNo ?: stockInLine.lotNo | ||||
this.expiryDate = stockInLine.expiryDate ?: request.expiryDate | this.expiryDate = stockInLine.expiryDate ?: request.expiryDate | ||||
this.productLotNo = stockInLine.productLotNo ?: request.productLotNo | this.productLotNo = stockInLine.productLotNo ?: request.productLotNo | ||||
@@ -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) | |||||
} | |||||
} |
@@ -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) | |||||
// } | |||||
} |
@@ -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) | |||||
// } | |||||
} |
@@ -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? | |||||
) |
@@ -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? | |||||
) |
@@ -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> | |||||
) | |||||
@@ -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> | |||||
) |
@@ -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) | |||||
; |