| @@ -10,12 +10,12 @@ import java.time.LocalDateTime | |||
| @Table(name = "item_uom") | |||
| open class ItemUom : BaseEntity<Long>() { | |||
| @NotNull | |||
| @ManyToOne(fetch = FetchType.LAZY, optional = false) | |||
| @ManyToOne | |||
| @JoinColumn(name = "uomId", nullable = false) | |||
| open var uom: UomConversion? = null | |||
| @NotNull | |||
| @ManyToOne(fetch = FetchType.LAZY, optional = false) | |||
| @ManyToOne | |||
| @JoinColumn(name = "itemId", nullable = false) | |||
| 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.m18.entity.M18DataLog | |||
| 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.PurchaseOrderLineStatusConverter | |||
| import jakarta.persistence.* | |||
| @@ -50,10 +51,9 @@ open class PurchaseOrderLine : BaseEntity<Long>(){ | |||
| @JoinColumn(name = "m18DataLogId", nullable = false) | |||
| 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 itemName: String?, | |||
| val qty: BigDecimal, | |||
| val uom: String? = null, | |||
| val price: BigDecimal, | |||
| val status: String, | |||
| @@ -53,7 +53,6 @@ open class PurchaseOrderLineService( | |||
| this.purchaseOrder = purchaseOrder | |||
| qty = request.qty | |||
| price = request.price | |||
| this.currency = currency | |||
| this.status = status | |||
| this.m18DataLog = m18DataLog ?: this.m18DataLog | |||
| } | |||
| @@ -56,6 +56,7 @@ open class PurchaseOrderService( | |||
| thisPol.itemNo!!, | |||
| thisPol.item!!.name, | |||
| thisPol.qty!!, | |||
| thisPol.uom!!.code, | |||
| thisPol.price!!, | |||
| thisPol.status!!.toString(), | |||
| inLine | |||
| @@ -1,7 +1,9 @@ | |||
| package com.ffii.fpsms.modules.stock.entity | |||
| 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.UomConversion | |||
| import com.ffii.fpsms.modules.master.entity.Warehouse | |||
| import jakarta.persistence.* | |||
| import jakarta.validation.constraints.NotNull | |||
| @@ -13,42 +15,42 @@ import java.time.LocalDate | |||
| @Table(name = "inventory") | |||
| open class Inventory: BaseEntity<Long>(){ | |||
| @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") | |||
| open var stockInLine: Long? = null // change this later | |||
| @ManyToOne | |||
| @JoinColumn(name = "currencyId") | |||
| open var currency: Currency? = null | |||
| @NotNull | |||
| @Column(name = "qty") | |||
| open var qty: Double? = null | |||
| @Column(name = "cpu") // cost per unit | |||
| open var cpu: BigDecimal? = null | |||
| @NotNull | |||
| @Column(name = "lotNo") | |||
| open var lotNo: String? = null | |||
| @Column(name = "cpuUnit") | |||
| open var cpuUnit: String? = null | |||
| @NotNull | |||
| @Column(name = "expiryDate") | |||
| open var expiryDate: LocalDate? = null | |||
| @Column(name = "cpm") // cost per unit | |||
| open var cpm: BigDecimal? = null | |||
| @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 | |||
| @Column(name = "status") | |||
| 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 | |||
| @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 | |||
| @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 java.math.BigDecimal | |||
| import java.time.Instant | |||
| import java.time.LocalDate | |||
| import java.time.LocalDateTime | |||
| @Entity | |||
| @@ -46,20 +47,27 @@ open class StockInLine : BaseEntity<Long>() { | |||
| @Column(name = "priceUnit", length = 5) | |||
| 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 | |||
| @Column(name = "status", nullable = false, length = 10) | |||
| // @Enumerated(EnumType.STRING) | |||
| @Column(name = "status") | |||
| open var status: String? = null | |||
| @ManyToOne | |||
| @JoinColumn(name = "userId") | |||
| 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 org.springframework.beans.factory.annotation.Value | |||
| import java.math.BigDecimal | |||
| import java.time.LocalDate | |||
| import java.time.LocalDateTime | |||
| interface StockInLineInfo { | |||
| @@ -21,7 +22,10 @@ interface StockInLineInfo { | |||
| val acceptedQty: BigDecimal | |||
| val price: BigDecimal? | |||
| val priceUnit: BigDecimal? | |||
| val productDate: LocalDateTime? | |||
| val shelfLifeDate: LocalDateTime? | |||
| val receiptDate: LocalDateTime? | |||
| val productionDate: LocalDateTime? | |||
| val expiryDate: LocalDate? | |||
| 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.stock.entity.Inventory | |||
| 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 org.springframework.stereotype.Service | |||
| import java.io.IOException | |||
| @@ -26,76 +25,67 @@ open class InventoryService( | |||
| val inventory = inventoryRepository.findAll() | |||
| 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.JdbcDao | |||
| import com.ffii.fpsms.modules.common.CodeGenerator | |||
| import com.ffii.fpsms.modules.master.entity.ItemsRepository | |||
| import com.ffii.fpsms.modules.master.web.models.MessageResponse | |||
| 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.SaveStockInRequest | |||
| 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.transaction.annotation.Transactional | |||
| import java.io.IOException | |||
| import java.math.BigDecimal | |||
| import java.time.LocalDate | |||
| import java.time.LocalDateTime | |||
| @Service | |||
| open class StockInLineService( | |||
| @@ -21,7 +26,9 @@ open class StockInLineService( | |||
| private val stockInService: StockInService, | |||
| private val stockInRepository: StockInRepository, | |||
| private val stockInLineRepository: StockInLineRepository, | |||
| private val inventoryLotRepository: InventoryLotRepository, | |||
| private val itemRepository: ItemsRepository, | |||
| private val inventoryLotService: InventoryLotService, | |||
| ): AbstractBaseEntityService<StockInLine, Long, StockInLineRepository>(jdbcDao, stockInLineRepository) { | |||
| @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) | |||
| @Transactional | |||
| open fun update(request: SaveStockInLineRequest): MessageResponse { | |||
| @@ -66,15 +92,29 @@ open class StockInLineService( | |||
| errorPosition = null, | |||
| ) | |||
| // 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) { | |||
| 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 { | |||
| // user = null | |||
| productionDate = request.productionDate?.atStartOfDay() // maybe need to change the request to LocalDateTime | |||
| productLotNo = request.productLotNo | |||
| receiptDate = request.receiptDate?.atStartOfDay() | |||
| status = request.status | |||
| expiryDate = request.expiryDate | |||
| lotNo = savedInventoryLot?.lotNo | |||
| } | |||
| val savedStockInLine = saveAndFlush(stockInLine) | |||
| val lineInfo = stockInLineRepository.findStockInLineInfoByIdAndDeletedFalse(savedStockInLine.id!!) | |||
| @@ -108,19 +148,40 @@ open class StockInLineService( | |||
| acceptedQty = stockInLine.acceptedQty!!.minus(request.acceptedQty) | |||
| price = stockInLine.price | |||
| priceUnit = stockInLine.priceUnit | |||
| productDate = stockInLine.productDate | |||
| shelfLifeDate = stockInLine.shelfLifeDate | |||
| productionDate = stockInLine.productionDate | |||
| expiryDate = stockInLine.expiryDate | |||
| status = stockInLine.status // this does update status | |||
| 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 { | |||
| receiptDate = request.receiptDate?.atStartOfDay() | |||
| productionDate = request.productionDate?.atStartOfDay() | |||
| acceptedQty = request.acceptedQty | |||
| status = request.status | |||
| lotNo = savedInventoryLot?.lotNo | |||
| expiryDate = request.expiryDate | |||
| productLotNo = request.productLotNo | |||
| } | |||
| val stockInLineEntries = listOf(stockInLine, newStockInLine) | |||
| val savedEntries = stockInLineRepository.saveAllAndFlush(stockInLineEntries) | |||
| val ids = savedEntries.map { it.id!! } | |||
| val lineInfos = stockInLineRepository.findStockInLineInfoByIdInAndDeletedFalse(ids) | |||
| val lineInfoList = stockInLineRepository.findStockInLineInfoByIdInAndDeletedFalse(ids) | |||
| return MessageResponse( | |||
| id = stockInLine.id, | |||
| code = null, | |||
| @@ -128,7 +189,7 @@ open class StockInLineService( | |||
| type = "Save success", | |||
| message = "created 2 stock in line", | |||
| 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.NotNull | |||
| import java.time.LocalDate | |||
| import java.time.LocalDateTime | |||
| data class SaveInventoryRequest( | |||
| 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, | |||
| @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"), | |||
| ThirdDetermine("determine3"), | |||
| RECEIVING("receiving"), | |||
| RECEIVED("received"), | |||
| COMPLETE("completed"); | |||
| } | |||
| data class SaveStockInRequest( | |||
| @@ -38,5 +39,10 @@ data class SaveStockInLineRequest( | |||
| val purchaseOrderLineId: Long, | |||
| val itemId: Long, | |||
| val acceptedQty: BigDecimal, | |||
| val acceptedWeight: BigDecimal?, | |||
| 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; | |||