@@ -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 | 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 com.ffii.core.entity.BaseEntity | ||||
import jakarta.persistence.* | import jakarta.persistence.* | ||||
import jakarta.validation.constraints.NotNull | 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`) | |||||
); |