| @@ -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`) | |||
| ); | |||