@@ -10,12 +10,12 @@ import java.time.LocalDateTime | |||||
@Table(name = "item_uom") | @Table(name = "item_uom") | ||||
open class ItemUom : BaseEntity<Long>() { | open class ItemUom : BaseEntity<Long>() { | ||||
@NotNull | @NotNull | ||||
@ManyToOne(fetch = FetchType.LAZY, optional = false) | |||||
@ManyToOne | |||||
@JoinColumn(name = "uomId", nullable = false) | @JoinColumn(name = "uomId", nullable = false) | ||||
open var uom: UomConversion? = null | open var uom: UomConversion? = null | ||||
@NotNull | @NotNull | ||||
@ManyToOne(fetch = FetchType.LAZY, optional = false) | |||||
@ManyToOne | |||||
@JoinColumn(name = "itemId", nullable = false) | @JoinColumn(name = "itemId", nullable = false) | ||||
open var item: Items? = null | open var item: Items? = null | ||||
@@ -4,6 +4,7 @@ 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.m18.entity.M18DataLog | import com.ffii.fpsms.m18.entity.M18DataLog | ||||
import com.ffii.fpsms.modules.master.entity.Currency | import com.ffii.fpsms.modules.master.entity.Currency | ||||
import com.ffii.fpsms.modules.master.entity.UomConversion | |||||
import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderLineStatus | import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderLineStatus | ||||
import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderLineStatusConverter | import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderLineStatusConverter | ||||
import jakarta.persistence.* | import jakarta.persistence.* | ||||
@@ -50,10 +51,9 @@ open class PurchaseOrderLine : BaseEntity<Long>(){ | |||||
@JoinColumn(name = "m18DataLogId", nullable = false) | @JoinColumn(name = "m18DataLogId", nullable = false) | ||||
open var m18DataLog: M18DataLog? = null | open var m18DataLog: M18DataLog? = null | ||||
@Column(name = "uomId") | |||||
open var uomId: Long? = null | |||||
@ManyToOne | |||||
@JoinColumn(name = "uomId") | |||||
// @Column(name = "uomId") | |||||
open var uom: UomConversion? = null | |||||
@ManyToOne(fetch = FetchType.LAZY) | |||||
@JoinColumn(name = "currencyId") | |||||
open var currency: Currency? = null | |||||
} | } |
@@ -24,6 +24,7 @@ data class PoLineWithStockInLine ( | |||||
val itemNo: String, | val itemNo: String, | ||||
val itemName: String?, | val itemName: String?, | ||||
val qty: BigDecimal, | val qty: BigDecimal, | ||||
val uom: String? = null, | |||||
val price: BigDecimal, | val price: BigDecimal, | ||||
val status: String, | val status: String, | ||||
@@ -53,7 +53,6 @@ open class PurchaseOrderLineService( | |||||
this.purchaseOrder = purchaseOrder | this.purchaseOrder = purchaseOrder | ||||
qty = request.qty | qty = request.qty | ||||
price = request.price | price = request.price | ||||
this.currency = currency | |||||
this.status = status | this.status = status | ||||
this.m18DataLog = m18DataLog ?: this.m18DataLog | this.m18DataLog = m18DataLog ?: this.m18DataLog | ||||
} | } | ||||
@@ -56,6 +56,7 @@ open class PurchaseOrderService( | |||||
thisPol.itemNo!!, | thisPol.itemNo!!, | ||||
thisPol.item!!.name, | thisPol.item!!.name, | ||||
thisPol.qty!!, | thisPol.qty!!, | ||||
thisPol.uom!!.code, | |||||
thisPol.price!!, | thisPol.price!!, | ||||
thisPol.status!!.toString(), | thisPol.status!!.toString(), | ||||
inLine | inLine | ||||
@@ -1,7 +1,9 @@ | |||||
package com.ffii.fpsms.modules.stock.entity | package com.ffii.fpsms.modules.stock.entity | ||||
import com.ffii.core.entity.BaseEntity | import com.ffii.core.entity.BaseEntity | ||||
import com.ffii.fpsms.modules.master.entity.Currency | |||||
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.Warehouse | import com.ffii.fpsms.modules.master.entity.Warehouse | ||||
import jakarta.persistence.* | import jakarta.persistence.* | ||||
import jakarta.validation.constraints.NotNull | import jakarta.validation.constraints.NotNull | ||||
@@ -13,42 +15,42 @@ import java.time.LocalDate | |||||
@Table(name = "inventory") | @Table(name = "inventory") | ||||
open class Inventory: BaseEntity<Long>(){ | open class Inventory: BaseEntity<Long>(){ | ||||
@NotNull | @NotNull | ||||
@OneToOne | |||||
@JoinColumn(name = "itemId") | |||||
open var item: Items? = null | |||||
@Column(name = "qty") | |||||
open var qty: BigDecimal? = null | |||||
@NotNull | |||||
@Column(name = "price") | |||||
open var price: BigDecimal? = null | |||||
@Column(name = "stockInLineId") | @Column(name = "stockInLineId") | ||||
open var stockInLine: Long? = null // change this later | open var stockInLine: Long? = null // change this later | ||||
@ManyToOne | |||||
@JoinColumn(name = "currencyId") | |||||
open var currency: Currency? = null | |||||
@NotNull | @NotNull | ||||
@Column(name = "qty") | |||||
open var qty: Double? = null | |||||
@Column(name = "cpu") // cost per unit | |||||
open var cpu: BigDecimal? = null | |||||
@NotNull | @NotNull | ||||
@Column(name = "lotNo") | |||||
open var lotNo: String? = null | |||||
@Column(name = "cpuUnit") | |||||
open var cpuUnit: String? = null | |||||
@NotNull | @NotNull | ||||
@Column(name = "expiryDate") | |||||
open var expiryDate: LocalDate? = null | |||||
@Column(name = "cpm") // cost per unit | |||||
open var cpm: BigDecimal? = null | |||||
@NotNull | @NotNull | ||||
@Column(name = "uomId") | |||||
open var uomId: Long? = null | |||||
@Column(name = "cpmUnit") | |||||
open var cpmUnit: String? = null | |||||
@NotNull | |||||
@ManyToOne | |||||
@JoinColumn(name = "uomId") | |||||
open var uomId: UomConversion? = null | |||||
// @NotNull | // @NotNull | ||||
@Column(name = "status") | @Column(name = "status") | ||||
open var status: String? = null | open var status: String? = null | ||||
@NotNull | |||||
@ManyToOne(fetch = FetchType.LAZY, optional = false) | |||||
@JoinColumn(name = "warehouseId", nullable = false) | |||||
open var warehouse: Warehouse? = null | |||||
@Column(name = "price", precision = 14, scale = 2) | |||||
open var price: BigDecimal? = null | |||||
@Size(max = 5) | |||||
@Column(name = "priceUnit", length = 5) | |||||
open var priceUnit: String? = null | |||||
} | } |
@@ -0,0 +1,42 @@ | |||||
package com.ffii.fpsms.modules.stock.entity | |||||
import com.ffii.core.entity.BaseEntity | |||||
import com.ffii.fpsms.modules.master.entity.Items | |||||
import jakarta.persistence.* | |||||
import jakarta.validation.constraints.NotNull | |||||
import jakarta.validation.constraints.Size | |||||
import org.hibernate.annotations.JdbcTypeCode | |||||
import org.hibernate.type.SqlTypes | |||||
import java.time.LocalDate | |||||
import java.time.LocalDateTime | |||||
@Entity | |||||
@Table(name = "inventory_lot") | |||||
open class InventoryLot : BaseEntity<Long>() { | |||||
@NotNull | |||||
@OneToOne | |||||
@JoinColumn(name = "itemId", nullable = false) | |||||
open var item: Items? = null | |||||
@NotNull | |||||
@ManyToOne | |||||
@JoinColumn(name = "stockInLineId", nullable = false) | |||||
open var stockInLine: StockInLine? = null | |||||
@Column(name = "productionDate") | |||||
open var productionDate: LocalDateTime? = null | |||||
@Column(name = "stockInDate") | |||||
open var stockInDate: LocalDateTime? = null | |||||
@NotNull | |||||
@Column(name = "expiryDate") | |||||
open var expiryDate: LocalDate? = null | |||||
@Column(name = "lotNo") | |||||
open var lotNo: String? = null | |||||
// @JdbcTypeCode(SqlTypes.JSON) | |||||
// @Column(name = "qrCodeJson") | |||||
// open var qrCodeJson: MutableMap<String, Any>? = null | |||||
} |
@@ -0,0 +1,35 @@ | |||||
package com.ffii.fpsms.modules.stock.entity | |||||
import com.ffii.core.entity.BaseEntity | |||||
import jakarta.persistence.Column | |||||
import jakarta.persistence.Entity | |||||
import jakarta.persistence.Table | |||||
import jakarta.validation.constraints.NotNull | |||||
import org.hibernate.annotations.JdbcTypeCode | |||||
import org.hibernate.type.SqlTypes | |||||
import java.math.BigDecimal | |||||
@Entity | |||||
@Table(name = "inventory_lot_line") | |||||
open class InventoryLotLine : BaseEntity<Long>() { | |||||
@NotNull | |||||
@Column(name = "inQty") | |||||
open var inQty: BigDecimal? = null | |||||
@NotNull | |||||
@Column(name = "outQty") | |||||
open var outQty: BigDecimal? = null | |||||
@NotNull | |||||
@Column(name = "holdQty") | |||||
open var holdQty: BigDecimal? = null | |||||
@NotNull | |||||
@Column(name = "status") | |||||
open var status: String? = null | |||||
@JdbcTypeCode(SqlTypes.JSON) | |||||
@Column(name = "qrCode") | |||||
open var qrCode: MutableMap<String, Any?>? = null | |||||
} |
@@ -0,0 +1,8 @@ | |||||
package com.ffii.fpsms.modules.stock.entity | |||||
import com.ffii.core.support.AbstractRepository | |||||
import org.springframework.stereotype.Repository | |||||
@Repository | |||||
interface InventoryLotLineRepository : AbstractRepository<InventoryLotLine, Long> { | |||||
} |
@@ -1,25 +0,0 @@ | |||||
package com.ffii.fpsms.modules.stock.entity | |||||
import com.ffii.core.entity.BaseEntity | |||||
import jakarta.persistence.* | |||||
import jakarta.validation.constraints.NotNull | |||||
import jakarta.validation.constraints.Size | |||||
import org.hibernate.annotations.JdbcTypeCode | |||||
import org.hibernate.type.SqlTypes | |||||
@Entity | |||||
@Table(name = "inventory_lot_no") | |||||
open class InventoryLotNo : BaseEntity<Long>() { | |||||
@NotNull | |||||
@ManyToOne(fetch = FetchType.LAZY, optional = false) | |||||
@JoinColumn(name = "inventoryId", nullable = false) | |||||
open var inventory: Inventory? = null | |||||
@Size(max = 30) | |||||
@Column(name = "lotNo", length = 30) | |||||
open var lotNo: String? = null | |||||
@JdbcTypeCode(SqlTypes.JSON) | |||||
@Column(name = "qrCodeJson") | |||||
open var qrCodeJson: MutableMap<String, Any>? = null | |||||
} |
@@ -4,5 +4,5 @@ import com.ffii.core.support.AbstractRepository | |||||
import org.springframework.stereotype.Repository | import org.springframework.stereotype.Repository | ||||
@Repository | @Repository | ||||
interface InventoryLotNoRepository : AbstractRepository<InventoryLotNo, Long> { | |||||
interface InventoryLotRepository: AbstractRepository<InventoryLot, Long> { | |||||
} | } |
@@ -4,5 +4,5 @@ import com.ffii.core.support.AbstractRepository | |||||
import org.springframework.stereotype.Repository | import org.springframework.stereotype.Repository | ||||
@Repository | @Repository | ||||
interface InventoryRepository : AbstractRepository<Inventory, Long> { | |||||
interface InventoryRepository: AbstractRepository<Inventory, Long> { | |||||
} | } |
@@ -9,6 +9,7 @@ import jakarta.validation.constraints.NotNull | |||||
import jakarta.validation.constraints.Size | import jakarta.validation.constraints.Size | ||||
import java.math.BigDecimal | import java.math.BigDecimal | ||||
import java.time.Instant | import java.time.Instant | ||||
import java.time.LocalDate | |||||
import java.time.LocalDateTime | import java.time.LocalDateTime | ||||
@Entity | @Entity | ||||
@@ -46,20 +47,27 @@ open class StockInLine : BaseEntity<Long>() { | |||||
@Column(name = "priceUnit", length = 5) | @Column(name = "priceUnit", length = 5) | ||||
open var priceUnit: String? = null | open var priceUnit: String? = null | ||||
@Column(name = "productDate") | |||||
open var productDate: LocalDateTime? = null | |||||
@Column(name = "receiptDate") | |||||
open var receiptDate: LocalDateTime? = null | |||||
@Column(name = "shelfLifeDate") | |||||
open var shelfLifeDate: LocalDateTime? = null | |||||
@Column(name = "productionDate") | |||||
open var productionDate: LocalDateTime? = null | |||||
@Column(name = "expiryDate") | |||||
open var expiryDate: LocalDate? = null | |||||
// @Size(max = 10) | |||||
@NotNull | @NotNull | ||||
@Column(name = "status", nullable = false, length = 10) | |||||
// @Enumerated(EnumType.STRING) | |||||
@Column(name = "status") | |||||
open var status: String? = null | open var status: String? = null | ||||
@ManyToOne | @ManyToOne | ||||
@JoinColumn(name = "userId") | @JoinColumn(name = "userId") | ||||
open var user: User? = null | open var user: User? = null | ||||
@Column(name = "lotNo") | |||||
open var lotNo: String? = null | |||||
@Column(name = "productLotNo") | |||||
open var productLotNo: String? = null | |||||
} | } |
@@ -4,6 +4,7 @@ import com.ffii.fpsms.modules.master.entity.Items | |||||
import com.ffii.fpsms.modules.master.entity.ItemsRepository | import com.ffii.fpsms.modules.master.entity.ItemsRepository | ||||
import org.springframework.beans.factory.annotation.Value | import org.springframework.beans.factory.annotation.Value | ||||
import java.math.BigDecimal | import java.math.BigDecimal | ||||
import java.time.LocalDate | |||||
import java.time.LocalDateTime | import java.time.LocalDateTime | ||||
interface StockInLineInfo { | interface StockInLineInfo { | ||||
@@ -21,7 +22,10 @@ interface StockInLineInfo { | |||||
val acceptedQty: BigDecimal | val acceptedQty: BigDecimal | ||||
val price: BigDecimal? | val price: BigDecimal? | ||||
val priceUnit: BigDecimal? | val priceUnit: BigDecimal? | ||||
val productDate: LocalDateTime? | |||||
val shelfLifeDate: LocalDateTime? | |||||
val receiptDate: LocalDateTime? | |||||
val productionDate: LocalDateTime? | |||||
val expiryDate: LocalDate? | |||||
val status: String | val status: String | ||||
var lotNo: String? | |||||
var productLotNo: String? | |||||
} | } |
@@ -0,0 +1,46 @@ | |||||
package com.ffii.fpsms.modules.stock.service | |||||
import com.ffii.core.support.AbstractBaseEntityService | |||||
import com.ffii.core.support.JdbcDao | |||||
import com.ffii.fpsms.modules.master.entity.ItemsRepository | |||||
import com.ffii.fpsms.modules.master.web.models.MessageResponse | |||||
import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrder | |||||
import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrderRepository | |||||
import com.ffii.fpsms.modules.stock.entity.InventoryLot | |||||
import com.ffii.fpsms.modules.stock.entity.InventoryLotRepository | |||||
import com.ffii.fpsms.modules.stock.web.model.SaveInventoryRequest | |||||
import org.springframework.stereotype.Service | |||||
import org.springframework.transaction.annotation.Transactional | |||||
import java.io.IOException | |||||
@Service | |||||
open class InventoryLotService( | |||||
private val jdbcDao: JdbcDao, | |||||
private val itemsRepository: ItemsRepository, | |||||
private val inventoryLotRepository: InventoryLotRepository, | |||||
): AbstractBaseEntityService<InventoryLot, Long, InventoryLotRepository>(jdbcDao, inventoryLotRepository) { | |||||
@Throws(IOException::class) | |||||
@Transactional | |||||
open fun createOrUpdate(request: SaveInventoryRequest): MessageResponse { | |||||
val inventoryLot = if (request.id !== null) inventoryLotRepository.findById(request.id).orElseThrow() else InventoryLot() | |||||
val thisExpiryDate = if (request.id !== null) inventoryLot.expiryDate else request.expiryDate | |||||
val item = itemsRepository.findById(request.itemId).orElseThrow() | |||||
inventoryLot.apply { | |||||
this.item = item | |||||
stockInDate = request.stockInDate | |||||
expiryDate = thisExpiryDate | |||||
lotNo = request.lotNo | |||||
} | |||||
val savedInventoryLot = saveAndFlush(inventoryLot) | |||||
return MessageResponse( | |||||
id = savedInventoryLot.id, | |||||
code = savedInventoryLot.lotNo, | |||||
name = item.name, | |||||
type = item.type, | |||||
message = "Inventory Lot Save Success", | |||||
errorPosition = null, | |||||
entity = savedInventoryLot, | |||||
) | |||||
} | |||||
} |
@@ -8,7 +8,6 @@ import com.ffii.fpsms.modules.master.entity.ItemsRepository | |||||
import com.ffii.fpsms.modules.master.web.models.MessageResponse | import com.ffii.fpsms.modules.master.web.models.MessageResponse | ||||
import com.ffii.fpsms.modules.stock.entity.Inventory | import com.ffii.fpsms.modules.stock.entity.Inventory | ||||
import com.ffii.fpsms.modules.stock.entity.InventoryRepository | import com.ffii.fpsms.modules.stock.entity.InventoryRepository | ||||
import com.ffii.fpsms.modules.stock.service.InventoryService.SQL.INVENTORY_COUNT | |||||
import com.ffii.fpsms.modules.stock.web.model.SaveInventoryRequest | import com.ffii.fpsms.modules.stock.web.model.SaveInventoryRequest | ||||
import org.springframework.stereotype.Service | import org.springframework.stereotype.Service | ||||
import java.io.IOException | import java.io.IOException | ||||
@@ -26,76 +25,67 @@ open class InventoryService( | |||||
val inventory = inventoryRepository.findAll() | val inventory = inventoryRepository.findAll() | ||||
return inventory | return inventory | ||||
} | } | ||||
object SQL { | |||||
val INVENTORY_COUNT = StringBuilder("select" | |||||
+ " count(id) " | |||||
+ " from inventory i " | |||||
+ " where i.created >= :from " | |||||
+ " and i.created = :to " | |||||
+ " and i.itemId = :itemId" | |||||
) | |||||
} | |||||
@Throws(IOException::class) | |||||
open fun updateInventory(request: SaveInventoryRequest): MessageResponse { | |||||
// out need id | |||||
// in not necessary | |||||
var reqQty = request.qty | |||||
if (request.type === "out") reqQty *= -1 | |||||
if (request.id !== null) { // old record | |||||
val inventory = inventoryRepository.findById(request.id).orElseThrow() | |||||
val newStatus = request.status ?: inventory.status | |||||
val newExpiry = request.expiryDate ?: inventory.expiryDate | |||||
// uom should not be changing | |||||
// stock in line should not be changing | |||||
// item id should not be changing | |||||
inventory.apply { | |||||
qty = inventory.qty!! + reqQty | |||||
expiryDate = newExpiry | |||||
status = newStatus | |||||
} | |||||
val savedInventory = inventoryRepository.saveAndFlush(inventory) | |||||
return MessageResponse( | |||||
id = savedInventory.id, | |||||
code = savedInventory.lotNo, | |||||
name = savedInventory.item!!.name, | |||||
type = savedInventory.status, | |||||
message = "update success", | |||||
errorPosition = null | |||||
) | |||||
} else { // new record | |||||
val inventory = Inventory() | |||||
val item = itemsRepository.findById(request.itemId).orElseThrow() | |||||
val from = LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE) | |||||
val to = LocalDate.now().plusDays(1).format(DateTimeFormatter.ISO_LOCAL_DATE) | |||||
// val stockInLine = .... | |||||
val args = mapOf( | |||||
"from" to from, | |||||
"to" to to, | |||||
"itemId" to item.id | |||||
) | |||||
val prefix = "LOT" | |||||
val count = jdbcDao.queryForInt(INVENTORY_COUNT.toString(), args) | |||||
val newLotNo = CodeGenerator.generateCode(prefix, item.id!!, count) | |||||
val newExpiry = request.expiryDate | |||||
inventory.apply { | |||||
// this.stockInLine = stockInline | |||||
this.item = item | |||||
stockInLine = 0 | |||||
qty = reqQty | |||||
lotNo = newLotNo | |||||
expiryDate = newExpiry | |||||
uomId = 0 | |||||
// status default "pending" in db | |||||
} | |||||
val savedInventory = inventoryRepository.saveAndFlush(inventory) | |||||
return MessageResponse( | |||||
id = savedInventory.id, | |||||
code = savedInventory.lotNo, | |||||
name = savedInventory.item!!.name, | |||||
type = savedInventory.status, | |||||
message = "save success", | |||||
errorPosition = null | |||||
) | |||||
} | |||||
} | |||||
// @Throws(IOException::class) | |||||
// open fun updateInventory(request: SaveInventoryRequest): MessageResponse { | |||||
// // out need id | |||||
// // in not necessary | |||||
// var reqQty = request.qty | |||||
// if (request.type === "out") reqQty *= -1 | |||||
// if (request.id !== null) { // old record | |||||
// val inventory = inventoryRepository.findById(request.id).orElseThrow() | |||||
// val newStatus = request.status ?: inventory.status | |||||
// val newExpiry = request.expiryDate ?: inventory.expiryDate | |||||
// // uom should not be changing | |||||
// // stock in line should not be changing | |||||
// // item id should not be changing | |||||
// inventory.apply { | |||||
// qty = inventory.qty!! + reqQty | |||||
// expiryDate = newExpiry | |||||
// status = newStatus | |||||
// } | |||||
// val savedInventory = inventoryRepository.saveAndFlush(inventory) | |||||
// return MessageResponse( | |||||
// id = savedInventory.id, | |||||
// code = savedInventory.lotNo, | |||||
// name = "savedInventory.item!!.name", | |||||
// type = savedInventory.status, | |||||
// message = "update success", | |||||
// errorPosition = null | |||||
// ) | |||||
// } else { // new record | |||||
// val inventory = Inventory() | |||||
// val item = itemsRepository.findById(request.itemId).orElseThrow() | |||||
// val from = LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE) | |||||
// val to = LocalDate.now().plusDays(1).format(DateTimeFormatter.ISO_LOCAL_DATE) | |||||
//// val stockInLine = .... | |||||
// val args = mapOf( | |||||
// "from" to from, | |||||
// "to" to to, | |||||
// "itemId" to item.id | |||||
// ) | |||||
// val prefix = "LOT" | |||||
// val count = jdbcDao.queryForInt(INVENTORY_COUNT.toString(), args) | |||||
// val newLotNo = CodeGenerator.generateCode(prefix, item.id!!, count) | |||||
// val newExpiry = request.expiryDate | |||||
// inventory.apply { | |||||
//// this.stockInLine = stockInline | |||||
//// this.item = item | |||||
// stockInLine = 0 | |||||
// qty = reqQty | |||||
// lotNo = newLotNo | |||||
// expiryDate = newExpiry | |||||
// uomId = 0 | |||||
// // status default "pending" in db | |||||
// } | |||||
// val savedInventory = inventoryRepository.saveAndFlush(inventory) | |||||
// return MessageResponse( | |||||
// id = savedInventory.id, | |||||
// code = savedInventory.lotNo, | |||||
// name = "savedInventory.item!!.name", | |||||
// type = savedInventory.status, | |||||
// message = "save success", | |||||
// errorPosition = null | |||||
// ) | |||||
// } | |||||
// } | |||||
} | } |
@@ -2,6 +2,7 @@ package com.ffii.fpsms.modules.stock.service | |||||
import com.ffii.core.support.AbstractBaseEntityService | import com.ffii.core.support.AbstractBaseEntityService | ||||
import com.ffii.core.support.JdbcDao | import com.ffii.core.support.JdbcDao | ||||
import com.ffii.fpsms.modules.common.CodeGenerator | |||||
import com.ffii.fpsms.modules.master.entity.ItemsRepository | import com.ffii.fpsms.modules.master.entity.ItemsRepository | ||||
import com.ffii.fpsms.modules.master.web.models.MessageResponse | import com.ffii.fpsms.modules.master.web.models.MessageResponse | ||||
import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrderLineRepository | import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrderLineRepository | ||||
@@ -9,10 +10,14 @@ import com.ffii.fpsms.modules.stock.entity.* | |||||
import com.ffii.fpsms.modules.stock.web.model.SaveStockInLineRequest | import com.ffii.fpsms.modules.stock.web.model.SaveStockInLineRequest | ||||
import com.ffii.fpsms.modules.stock.web.model.SaveStockInRequest | import com.ffii.fpsms.modules.stock.web.model.SaveStockInRequest | ||||
import com.ffii.fpsms.modules.stock.web.model.StockInLineStatus | import com.ffii.fpsms.modules.stock.web.model.StockInLineStatus | ||||
import com.ffii.fpsms.modules.stock.sql.StockSql.SQL.INVENTORY_COUNT | |||||
import com.ffii.fpsms.modules.stock.web.model.SaveInventoryRequest | |||||
import org.springframework.stereotype.Service | import org.springframework.stereotype.Service | ||||
import org.springframework.transaction.annotation.Transactional | import org.springframework.transaction.annotation.Transactional | ||||
import java.io.IOException | import java.io.IOException | ||||
import java.math.BigDecimal | import java.math.BigDecimal | ||||
import java.time.LocalDate | |||||
import java.time.LocalDateTime | |||||
@Service | @Service | ||||
open class StockInLineService( | open class StockInLineService( | ||||
@@ -21,7 +26,9 @@ open class StockInLineService( | |||||
private val stockInService: StockInService, | private val stockInService: StockInService, | ||||
private val stockInRepository: StockInRepository, | private val stockInRepository: StockInRepository, | ||||
private val stockInLineRepository: StockInLineRepository, | private val stockInLineRepository: StockInLineRepository, | ||||
private val inventoryLotRepository: InventoryLotRepository, | |||||
private val itemRepository: ItemsRepository, | private val itemRepository: ItemsRepository, | ||||
private val inventoryLotService: InventoryLotService, | |||||
): AbstractBaseEntityService<StockInLine, Long, StockInLineRepository>(jdbcDao, stockInLineRepository) { | ): AbstractBaseEntityService<StockInLine, Long, StockInLineRepository>(jdbcDao, stockInLineRepository) { | ||||
@Throws(IOException::class) | @Throws(IOException::class) | ||||
@@ -53,6 +60,25 @@ open class StockInLineService( | |||||
) | ) | ||||
} | } | ||||
@Transactional | |||||
fun saveInventoryLotWhenStockIn(request: SaveStockInLineRequest, stockInLine: StockInLine): InventoryLot { | |||||
val inventoryLot = InventoryLot() | |||||
val args = mapOf( | |||||
"from" to LocalDate.now().atStartOfDay(), | |||||
"to" to LocalDateTime.now(), | |||||
"itemId" to stockInLine.item!!.id | |||||
) | |||||
val inventoryCount = jdbcDao.queryForInt(INVENTORY_COUNT.toString(), args) | |||||
val newLotNo = CodeGenerator.generateCode(prefix = "POLOT", itemId = stockInLine.item!!.id!!, count = inventoryCount) | |||||
inventoryLot.apply { | |||||
this.item = stockInLine.item | |||||
this.stockInLine = stockInLine | |||||
stockInDate = LocalDateTime.now() | |||||
expiryDate = request.expiryDate// frontend form input | |||||
lotNo = newLotNo | |||||
} | |||||
return inventoryLotRepository.saveAndFlush(inventoryLot) | |||||
} | |||||
@Throws(IOException::class) | @Throws(IOException::class) | ||||
@Transactional | @Transactional | ||||
open fun update(request: SaveStockInLineRequest): MessageResponse { | open fun update(request: SaveStockInLineRequest): MessageResponse { | ||||
@@ -66,15 +92,29 @@ open class StockInLineService( | |||||
errorPosition = null, | errorPosition = null, | ||||
) | ) | ||||
// return list of stock in line, update data grid with the list | // return list of stock in line, update data grid with the list | ||||
println(request.acceptedQty) | |||||
println(stockInLine.acceptedQty) | |||||
println(request.acceptedQty == stockInLine.acceptedQty) | |||||
println(request.acceptedQty.equals(stockInLine.acceptedQty)) | |||||
println(request.acceptedQty.compareTo(stockInLine.acceptedQty) == 0) | |||||
if (request.acceptedQty.compareTo(stockInLine.acceptedQty) == 0) { | if (request.acceptedQty.compareTo(stockInLine.acceptedQty) == 0) { | ||||
var savedInventoryLot: InventoryLot? = null | |||||
if (request.status == StockInLineStatus.RECEIVED.status) { | |||||
if (request.expiryDate == null) { | |||||
return MessageResponse( | |||||
id = null, | |||||
code = null, | |||||
name = null, | |||||
type = "Found Null", | |||||
message = "missing expiry", | |||||
errorPosition = "expiryDate", | |||||
) | |||||
} | |||||
savedInventoryLot = saveInventoryLotWhenStockIn(request = request, stockInLine = stockInLine) | |||||
} | |||||
stockInLine.apply { | stockInLine.apply { | ||||
// user = null | // user = null | ||||
productionDate = request.productionDate?.atStartOfDay() // maybe need to change the request to LocalDateTime | |||||
productLotNo = request.productLotNo | |||||
receiptDate = request.receiptDate?.atStartOfDay() | |||||
status = request.status | status = request.status | ||||
expiryDate = request.expiryDate | |||||
lotNo = savedInventoryLot?.lotNo | |||||
} | } | ||||
val savedStockInLine = saveAndFlush(stockInLine) | val savedStockInLine = saveAndFlush(stockInLine) | ||||
val lineInfo = stockInLineRepository.findStockInLineInfoByIdAndDeletedFalse(savedStockInLine.id!!) | val lineInfo = stockInLineRepository.findStockInLineInfoByIdAndDeletedFalse(savedStockInLine.id!!) | ||||
@@ -108,19 +148,40 @@ open class StockInLineService( | |||||
acceptedQty = stockInLine.acceptedQty!!.minus(request.acceptedQty) | acceptedQty = stockInLine.acceptedQty!!.minus(request.acceptedQty) | ||||
price = stockInLine.price | price = stockInLine.price | ||||
priceUnit = stockInLine.priceUnit | priceUnit = stockInLine.priceUnit | ||||
productDate = stockInLine.productDate | |||||
shelfLifeDate = stockInLine.shelfLifeDate | |||||
productionDate = stockInLine.productionDate | |||||
expiryDate = stockInLine.expiryDate | |||||
status = stockInLine.status // this does update status | status = stockInLine.status // this does update status | ||||
user = stockInLine.user | user = stockInLine.user | ||||
} | } | ||||
var savedInventoryLot: InventoryLot? = null | |||||
if (request.status == StockInLineStatus.RECEIVED.status) { | |||||
if (request.expiryDate == null) { | |||||
return MessageResponse( | |||||
id = null, | |||||
code = null, | |||||
name = null, | |||||
type = "Found Null", | |||||
message = "missing expiry", | |||||
errorPosition = "expiryDate", | |||||
) | |||||
} | |||||
savedInventoryLot = saveInventoryLotWhenStockIn(request = request, stockInLine = stockInLine) | |||||
} | |||||
stockInLine.apply { | stockInLine.apply { | ||||
receiptDate = request.receiptDate?.atStartOfDay() | |||||
productionDate = request.productionDate?.atStartOfDay() | |||||
acceptedQty = request.acceptedQty | acceptedQty = request.acceptedQty | ||||
status = request.status | status = request.status | ||||
lotNo = savedInventoryLot?.lotNo | |||||
expiryDate = request.expiryDate | |||||
productLotNo = request.productLotNo | |||||
} | } | ||||
val stockInLineEntries = listOf(stockInLine, newStockInLine) | val stockInLineEntries = listOf(stockInLine, newStockInLine) | ||||
val savedEntries = stockInLineRepository.saveAllAndFlush(stockInLineEntries) | val savedEntries = stockInLineRepository.saveAllAndFlush(stockInLineEntries) | ||||
val ids = savedEntries.map { it.id!! } | val ids = savedEntries.map { it.id!! } | ||||
val lineInfos = stockInLineRepository.findStockInLineInfoByIdInAndDeletedFalse(ids) | |||||
val lineInfoList = stockInLineRepository.findStockInLineInfoByIdInAndDeletedFalse(ids) | |||||
return MessageResponse( | return MessageResponse( | ||||
id = stockInLine.id, | id = stockInLine.id, | ||||
code = null, | code = null, | ||||
@@ -128,7 +189,7 @@ open class StockInLineService( | |||||
type = "Save success", | type = "Save success", | ||||
message = "created 2 stock in line", | message = "created 2 stock in line", | ||||
errorPosition = null, | errorPosition = null, | ||||
entity = lineInfos | |||||
entity = lineInfoList | |||||
) | ) | ||||
} | } | ||||
} | } | ||||
@@ -0,0 +1,13 @@ | |||||
package com.ffii.fpsms.modules.stock.sql | |||||
open class StockSql { | |||||
object SQL { | |||||
val INVENTORY_COUNT = StringBuilder("select" | |||||
+ " count(id) " | |||||
+ " from inventory_lot i " | |||||
+ " where i.stockInDate <= :from " | |||||
+ " and i.stockInDate < :to " | |||||
+ " and i.itemId = :itemId" | |||||
) | |||||
} | |||||
} |
@@ -3,19 +3,12 @@ package com.ffii.fpsms.modules.stock.web.model | |||||
import jakarta.validation.constraints.NotBlank | import jakarta.validation.constraints.NotBlank | ||||
import jakarta.validation.constraints.NotNull | import jakarta.validation.constraints.NotNull | ||||
import java.time.LocalDate | import java.time.LocalDate | ||||
import java.time.LocalDateTime | |||||
data class SaveInventoryRequest( | data class SaveInventoryRequest( | ||||
val id: Long?, | val id: Long?, | ||||
val stockInLineId: Long?, | |||||
val status: String?, | |||||
val uomId: Long?, | |||||
val expiryDate: LocalDate?, | |||||
@field:NotNull(message = "itemId cannot be null") | |||||
val itemId: Long, | val itemId: Long, | ||||
@field:NotNull(message = "qty cannot be null") | |||||
val qty: Double, | |||||
// FOR POSTING STOCK IN AND STOCK OUT | |||||
@field:NotBlank(message = "type cannot be empty") | |||||
val type: String, // E.G. "in", "out", "disable": for disable lot | |||||
val stockInDate: LocalDateTime?, | |||||
val expiryDate: LocalDate?, | |||||
val lotNo: String?, | |||||
) | ) |
@@ -17,6 +17,7 @@ enum class StockInLineStatus(val status: String) { | |||||
SecondDetermine("determine2"), | SecondDetermine("determine2"), | ||||
ThirdDetermine("determine3"), | ThirdDetermine("determine3"), | ||||
RECEIVING("receiving"), | RECEIVING("receiving"), | ||||
RECEIVED("received"), | |||||
COMPLETE("completed"); | COMPLETE("completed"); | ||||
} | } | ||||
data class SaveStockInRequest( | data class SaveStockInRequest( | ||||
@@ -38,5 +39,10 @@ data class SaveStockInLineRequest( | |||||
val purchaseOrderLineId: Long, | val purchaseOrderLineId: Long, | ||||
val itemId: Long, | val itemId: Long, | ||||
val acceptedQty: BigDecimal, | val acceptedQty: BigDecimal, | ||||
val acceptedWeight: BigDecimal?, | |||||
val status: String?, | val status: String?, | ||||
val expiryDate: LocalDate?, | |||||
val productLotNo: String?, | |||||
val receiptDate: LocalDate?, | |||||
val productionDate: LocalDate?, | |||||
) | ) |
@@ -0,0 +1,68 @@ | |||||
--liquibase formatted sql | |||||
--changeset derek:create inventory related | |||||
ALTER TABLE `stock_ledger` | |||||
DROP CONSTRAINT `fk_ledger_inventory_id`; | |||||
DROP TABLE `inventory_lot_no`; | |||||
DROP TABLE `inventory`; | |||||
CREATE TABLE `inventory` | |||||
( | |||||
`id` INT NOT NULL AUTO_INCREMENT, | |||||
`created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, | |||||
`createdBy` VARCHAR(30) NULL DEFAULT NULL, | |||||
`version` INT NOT NULL DEFAULT '0', | |||||
`modified` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, | |||||
`modifiedBy` VARCHAR(30) NULL DEFAULT NULL, | |||||
`deleted` TINYINT(1) NOT NULL DEFAULT '0', | |||||
`qty` DECIMAL(14, 2) NOT NULL, | |||||
`price` VARCHAR(30) NOT NULL, | |||||
`currencyId` INT(11) NOT NULL, | |||||
`cpu` DECIMAL(14, 2) NOT NULL, | |||||
`cpuUnit` VARCHAR(255) NOT NULL, | |||||
`cpm` DECIMAL(14, 2) NOT NULL, | |||||
`cpmUnit` VARCHAR(255) NOT NULL, | |||||
`uomId` INT(11) NOT NULL, | |||||
`status` VARCHAR(255) NOT NULL, | |||||
CONSTRAINT pk_inventory PRIMARY KEY (id), | |||||
CONSTRAINT FK_INVENTORY_TO_CURRENCY_ON_CURRENCY_ID FOREIGN KEY (`currencyId`) REFERENCES `currency` (`id`), | |||||
CONSTRAINT FK_INVENTORY_TO_UOM_ON_UOM_ID FOREIGN KEY (`uomId`) REFERENCES `uom_conversion` (`id`) | |||||
); | |||||
CREATE TABLE `inventory_lot` | |||||
( | |||||
`id` INT NOT NULL AUTO_INCREMENT, | |||||
`created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, | |||||
`createdBy` VARCHAR(30) NULL DEFAULT NULL, | |||||
`version` INT NOT NULL DEFAULT '0', | |||||
`modified` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, | |||||
`modifiedBy` VARCHAR(30) NULL DEFAULT NULL, | |||||
`deleted` TINYINT(1) NOT NULL DEFAULT '0', | |||||
`itemId` INT(11) NOT NULL, | |||||
`stockInLineId` INT(30) NOT NULL, | |||||
`productionDate` DATETIME NULL, | |||||
`stockInDate` DATETIME NULL, | |||||
`expiryDate` DATE NOT NULL, | |||||
`lotNo` VARCHAR(512) NOT NULL, | |||||
CONSTRAINT pk_inventory_lot PRIMARY KEY (id), | |||||
CONSTRAINT FK_INVENTORY_LOT_TO_ITEMS_ON_ITEM_ID FOREIGN KEY (`itemId`) REFERENCES `items` (`id`), | |||||
CONSTRAINT FK_INVENTORY_LOT_TO_STOCK_IN_LINE_ON_STOCKINLINE_ID FOREIGN KEY (`stockInLineId`) REFERENCES `stock_in_line` (`id`) | |||||
); | |||||
CREATE TABLE `inventory_lot_line` | |||||
( | |||||
`id` INT NOT NULL AUTO_INCREMENT, | |||||
`created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, | |||||
`createdBy` VARCHAR(30) NULL DEFAULT NULL, | |||||
`version` INT NOT NULL DEFAULT '0', | |||||
`modified` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, | |||||
`modifiedBy` VARCHAR(30) NULL DEFAULT NULL, | |||||
`deleted` TINYINT(1) NOT NULL DEFAULT '0', | |||||
`inQty` DECIMAL(14, 2) NOT NULL, | |||||
`outQty` DECIMAL(14, 2) NOT NULL, | |||||
`holdQty` DECIMAL(14, 2) NOT NULL, | |||||
`status` VARCHAR(255) NOT NULL DEFAULT 'AVAILABLE', | |||||
`remarks` VARCHAR(1024) NULL, | |||||
`qrCode` JSON NOT NULL, | |||||
CONSTRAINT pk_inventory_lot PRIMARY KEY (id) | |||||
); | |||||
ALTER TABLE `stock_ledger` | |||||
ADD CONSTRAINT `fk_ledger_inventory_id` FOREIGN KEY (inventoryId) REFERENCES `inventory` (`id`); |
@@ -0,0 +1,5 @@ | |||||
--liquibase formatted sql | |||||
--changeset derek:update inventory data type | |||||
ALTER TABLE `inventory` | |||||
MODIFY COLUMN `price` INT NOT NULL; |
@@ -0,0 +1,7 @@ | |||||
--liquibase formatted sql | |||||
--changeset derek:update inventory lot line data type | |||||
ALTER TABLE `inventory_lot_line` | |||||
MODIFY COLUMN `inQty` DECIMAL(14, 2) NOT NULL DEFAULT 0, | |||||
MODIFY COLUMN `outQty` DECIMAL(14, 2) NOT NULL DEFAULT 0, | |||||
MODIFY COLUMN `holdQty` DECIMAL(14, 2) NOT NULL DEFAULT 0; |
@@ -0,0 +1,5 @@ | |||||
--liquibase formatted sql | |||||
--changeset derek:add lotNo in stock in line | |||||
ALTER TABLE `stock_in_line` | |||||
ADD COLUMN `lotNo` VARCHAR(512) NULL AFTER `userId`; |
@@ -0,0 +1,5 @@ | |||||
--liquibase formatted sql | |||||
--changeset derek:add receipt date in stock in line | |||||
ALTER TABLE `stock_in_line` | |||||
ADD COLUMN `receiptDate` DATETIME NULL AFTER `priceUnit`; |
@@ -0,0 +1,5 @@ | |||||
--liquibase formatted sql | |||||
--changeset derek:add receipt date in stock in line | |||||
ALTER TABLE `stock_in_line` | |||||
CHANGE COLUMN `shelfLifeDate` `expiryDate` DATE NULL; |
@@ -0,0 +1,5 @@ | |||||
--liquibase formatted sql | |||||
--changeset derek:add product lotno in stock in line | |||||
ALTER TABLE `stock_in_line` | |||||
ADD COLUMN `productLotNo` VARCHAR(255) NULL; |
@@ -0,0 +1,5 @@ | |||||
--liquibase formatted sql | |||||
--changeset derek:RENAME PRODUCT DATE in stock in line | |||||
ALTER TABLE `stock_in_line` | |||||
CHANGE COLUMN `productDate` `productionDate` DATETIME NULL; |