@@ -0,0 +1,16 @@ | |||
package com.ffii.fpsms.modules.common | |||
import java.time.LocalDate | |||
import java.time.format.DateTimeFormatter | |||
object CodeGenerator { | |||
private var dateFormat = DateTimeFormatter.ofPattern("yyMMdd") | |||
fun generateCode(prefix: String, itemId: Long, count: Int): String { | |||
// prefix = "ITEM" || "LOT" | |||
// count = number of record from db | |||
val todayStr = LocalDate.now().format(dateFormat) | |||
val itemStr = String.format("%04d", itemId) | |||
val countStr = String.format("%04d", count) | |||
return "$prefix-$todayStr$itemStr$countStr" | |||
} | |||
} |
@@ -1,7 +1,5 @@ | |||
package com.ffii.fpsms.modules.master.entity | |||
import com.fasterxml.jackson.annotation.JsonInclude | |||
import com.fasterxml.jackson.annotation.JsonManagedReference | |||
import com.ffii.core.entity.BaseEntity | |||
import jakarta.persistence.* | |||
import jakarta.validation.constraints.NotNull | |||
@@ -0,0 +1,39 @@ | |||
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 java.time.LocalDate | |||
@Entity | |||
@Table(name = "inventory") | |||
open class Inventory: BaseEntity<Long>(){ | |||
@NotNull | |||
@OneToOne | |||
@JoinColumn(name = "itemId") | |||
open var item: Items? = null | |||
@Column(name = "stockInLineId") | |||
open var stockInLine: Long? = null // change this later | |||
@NotNull | |||
@Column(name = "qty") | |||
open var qty: Double? = null | |||
@NotNull | |||
@Column(name = "lotNo") | |||
open var lotNo: String? = null | |||
@NotNull | |||
@Column(name = "expiryDate") | |||
open var expiryDate: LocalDate? = null | |||
@NotNull | |||
@Column(name = "uomId") | |||
open var uomId: Long? = null | |||
// @NotNull | |||
@Column(name = "status") | |||
open var status: String? = 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 InventoryRepository : AbstractRepository<Inventory, Long> { | |||
} |
@@ -0,0 +1,99 @@ | |||
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.Items | |||
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.web.model.SaveInventoryRequest | |||
import org.springframework.stereotype.Service | |||
import java.io.IOException | |||
import java.time.LocalDate | |||
import java.time.format.DateTimeFormatter | |||
@Service | |||
open class InventoryService( | |||
private val jdbcDao: JdbcDao, | |||
private val inventoryRepository: InventoryRepository, | |||
private val itemsRepository: ItemsRepository, | |||
): AbstractBaseEntityService<Inventory, Long, InventoryRepository>(jdbcDao, inventoryRepository) { | |||
open fun allInventory(): List<Inventory> { | |||
// TODO: Replace by actual logic | |||
val inventory = inventoryRepository.findAll() | |||
return inventory | |||
} | |||
@Throws(IOException::class) | |||
open fun updateInventory(request: SaveInventoryRequest): MessageResponse { | |||
// out need id | |||
// in not necessary | |||
val reqQty = if (request.type === "out") { | |||
request.qty * -1 | |||
} else { | |||
request.qty | |||
} | |||
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 be changing | |||
// stock in line should be changing | |||
// item id should 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, | |||
) | |||
val sql = StringBuilder("select" | |||
+ " count(id) " | |||
+ " from inventory i " | |||
+ " where i.created >= :from " | |||
+ " and i.created = :to " | |||
) | |||
val prefix = "LOT" | |||
val count = jdbcDao.queryForInt(sql.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 | |||
) | |||
} | |||
} | |||
} |
@@ -0,0 +1,21 @@ | |||
package com.ffii.fpsms.modules.stock.web.model | |||
import jakarta.validation.constraints.NotBlank | |||
import jakarta.validation.constraints.NotNull | |||
import java.time.LocalDate | |||
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 | |||
) |
@@ -0,0 +1,75 @@ | |||
--liquibase formatted sql | |||
--changeset derek:create_stock_and_out | |||
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', | |||
`lotNo` VARCHAR(255) NOT NULL, | |||
`itemId` INT NOT NULL, | |||
`stockInLineId` INT NOT NULL, | |||
`qty` DECIMAL(14,2) NOT NULL DEFAULT 0, | |||
`expiryDate` DATE NOT NULL, | |||
`uomId` INT(11) NOT NULL DEFAULT 0, | |||
`status` VARCHAR(50) NOT NULL DEFAULT 'stored', | |||
PRIMARY KEY (`id`), | |||
CONSTRAINT fk_inventory_item_id FOREIGN KEY (`itemId`) REFERENCES `items` (`id`) | |||
); | |||
CREATE TABLE `stock_out` ( | |||
`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', | |||
`type` VARCHAR(50) NOT NULL, | |||
`deliveryOrderCode` VARCHAR(255) NULL, | |||
`pickOrderCode` VARCHAR(255) NULL, | |||
`consoCode` VARCHAR(255) NULL, | |||
`completeDate` DATETIME NULL, | |||
`status` VARCHAR(50) NOT NULL DEFAULT 'pending', | |||
`handlerId` INT(11) NOT NULL, | |||
`targetOutletId` INT(11) NULL, | |||
`remarks` VARCHAR(255) NULL, | |||
PRIMARY KEY (`id`) | |||
); | |||
CREATE TABLE `stock_out_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', | |||
`itemId` INT(11) NOT NULL, | |||
`qty` DECIMAL(14,2) NOT NULL, | |||
`inventoryId` INT(11) NULL, | |||
`status` VARCHAR(30) NOT NULL DEFAULT 'pending', | |||
`pickTime` DATETIME NULL, | |||
`pickerId` INT(11) NULL, | |||
PRIMARY KEY (`id`), | |||
CONSTRAINT fk_stock_out_line_items_id FOREIGN KEY (`itemId`) REFERENCES `items` (`id`) | |||
); | |||
CREATE TABLE `stock_ledger` ( | |||
`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', | |||
`stockInLineId` INT(11) NULL, | |||
`stockOutLineId` INT(11) NULL, | |||
`inventoryId` INT(11) NOT NULL, | |||
`inQty` DECIMAL(14,2) NULL, | |||
`outQty` DECIMAL(14,2) NULL, | |||
`balance` DECIMAL(14,2) NULL, | |||
PRIMARY KEY (`id`), | |||
CONSTRAINT fk_ledger_stock_out_line_id FOREIGN KEY (`stockOutLineId`) REFERENCES `stock_out_line` (`id`), | |||
CONSTRAINT fk_ledger_inventory_id FOREIGN KEY (`inventoryId`) REFERENCES `inventory` (`id`) | |||
); |
@@ -0,0 +1,14 @@ | |||
--liquibase formatted sql | |||
--changeset derek:m18_raw_data | |||
CREATE TABLE `m18_raw_data` ( | |||
`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', | |||
`data` VARCHAR(1024) NOT NULL, | |||
PRIMARY KEY (`id`) | |||
); |