@@ -7,4 +7,5 @@ data class MessageResponse( | |||
val type: String?, | |||
val message: String?, | |||
val errorPosition: String?, // e.g. duplicated code | |||
val entity: Any? = null | |||
) |
@@ -4,6 +4,7 @@ import com.ffii.core.support.AbstractRepository | |||
import com.ffii.fpsms.modules.purchaseOrder.entity.projections.PurchaseOrderInfo | |||
import org.springframework.stereotype.Repository | |||
import java.io.Serializable | |||
import java.util.Optional | |||
@Repository | |||
interface PurchaseOrderRepository : AbstractRepository<PurchaseOrder, Long> { | |||
@@ -11,4 +12,6 @@ interface PurchaseOrderRepository : AbstractRepository<PurchaseOrder, Long> { | |||
fun findPurchaseOrderInfoByDeletedIsFalse(): List<PurchaseOrderInfo> | |||
fun findPurchaseOrderInfoByIdAndDeletedIsFalse(id: Long): PurchaseOrderInfo | |||
fun findByIdAndDeletedFalse(id: Long): Optional<PurchaseOrder> | |||
} |
@@ -1,6 +1,7 @@ | |||
package com.ffii.fpsms.modules.purchaseOrder.entity.projections | |||
import com.ffii.fpsms.modules.stock.entity.StockInLine | |||
import com.ffii.fpsms.modules.stock.entity.projection.StockInLineInfo | |||
import org.springframework.beans.factory.annotation.Value | |||
import java.math.BigDecimal | |||
@@ -19,11 +20,12 @@ interface PurchaseOrderLineInfo { | |||
data class PoLineWithStockInLine ( | |||
val id: Long, | |||
val purchaseOrderId: Long, | |||
val itemId: Long, | |||
val itemNo: String, | |||
val itemName: String?, | |||
val qty: BigDecimal, | |||
val price: BigDecimal, | |||
val status: String, | |||
val stockInLine: List<StockInLine>? | |||
val stockInLine: List<StockInLineInfo>? | |||
) |
@@ -26,6 +26,7 @@ import kotlin.jvm.optionals.getOrNull | |||
open class PurchaseOrderService( | |||
private val jdbcDao: JdbcDao, | |||
private val purchaseOrderRepository: PurchaseOrderRepository, | |||
private val purchaseOrderLineRepository: PurchaseOrderLineRepository, | |||
private val polRepository: PurchaseOrderLineRepository, | |||
private val shopRepository: ShopRepository, | |||
private val m18DataLogRepository: M18DataLogRepository, | |||
@@ -39,18 +40,19 @@ open class PurchaseOrderService( | |||
return purchaseOrderRepository.findAll() | |||
} | |||
open fun getDetailedPo(id: Long): Map<String, Any> { | |||
// REMINDER: po code have duplication | |||
val po = purchaseOrderRepository.findPurchaseOrderInfoByIdAndDeletedIsFalse(id) | |||
val pol = polRepository.findAllByPurchaseOrderIdAndDeletedIsFalse(id) | |||
// val pol = polRepository.findAllPurchaseOrderLineInfoByPurchaseOrderIdAndDeletedIsFalse(id) | |||
val stockIn = stockInRepository.findByPurchaseOrderIdAndDeletedFalse(id) | |||
val stockInLine = if (stockIn == null) listOf() else { | |||
stockInLineRepository.findAllByStockInIdAndDeletedFalse(stockIn.id!!) | |||
stockInLineRepository.findAllStockInLineInfoByStockInIdAndDeletedFalse(stockIn.id!!) | |||
} | |||
val mappedPoLine = pol.map { thisPol -> | |||
val inLine = stockInLine.filter { it.itemNo.equals(thisPol.itemNo)} | |||
val inLine = stockInLine.filter { it.purchaseOrderLineId == thisPol.id } | |||
PoLineWithStockInLine( | |||
thisPol.id!!, | |||
thisPol.purchaseOrder!!.id!!, | |||
thisPol.item!!.id!!, | |||
thisPol.itemNo!!, | |||
thisPol.item!!.name, | |||
thisPol.qty!!, | |||
@@ -1,6 +1,7 @@ | |||
package com.ffii.fpsms.modules.stock.entity | |||
import com.ffii.core.entity.BaseEntity | |||
import com.ffii.fpsms.m18.entity.M18DataLog | |||
import com.ffii.fpsms.modules.master.entity.Shop | |||
import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrder | |||
import com.ffii.fpsms.modules.stock.entity.enum.StockInStatus | |||
@@ -27,6 +28,10 @@ open class StockIn : BaseEntity<Long>() { | |||
@JoinColumn(name = "shopId") | |||
open var shop: Shop? = null | |||
@OneToOne | |||
@JoinColumn(name = "m18DataLogId") | |||
open var m18DataLog: M18DataLog? = null | |||
@OneToOne | |||
@JoinColumn(name = "purchaseOrderId") | |||
open var purchaseOrder: PurchaseOrder? = null | |||
@@ -44,9 +49,8 @@ open class StockIn : BaseEntity<Long>() { | |||
@Column(name = "completeDate") | |||
open var completeDate: LocalDateTime? = null | |||
@Size(max = 10) | |||
@NotNull | |||
@Column(name = "status", nullable = false, length = 10) | |||
@Enumerated(EnumType.STRING) | |||
open var status: StockInStatus? = null | |||
// @Enumerated(EnumType.STRING) | |||
open var status: String? = null | |||
} |
@@ -1,21 +1,21 @@ | |||
package com.ffii.fpsms.modules.stock.entity | |||
import com.ffii.core.entity.BaseEntity | |||
import com.ffii.fpsms.m18.entity.M18DataLog | |||
import com.ffii.fpsms.modules.master.entity.Items | |||
import com.ffii.fpsms.modules.stock.entity.enum.StockInLineStatus | |||
import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrderLine | |||
import com.ffii.fpsms.modules.user.entity.User | |||
import jakarta.persistence.* | |||
import jakarta.validation.constraints.NotNull | |||
import jakarta.validation.constraints.Size | |||
import java.math.BigDecimal | |||
import java.time.Instant | |||
import java.time.LocalDateTime | |||
@Entity | |||
@Table(name = "stock_in_line") | |||
open class StockInLine : BaseEntity<Long>() { | |||
@NotNull | |||
@ManyToOne(fetch = FetchType.LAZY, optional = false) | |||
@ManyToOne | |||
@JoinColumn(name = "itemId", nullable = false) | |||
open var item: Items? = null | |||
@@ -24,8 +24,12 @@ open class StockInLine : BaseEntity<Long>() { | |||
@Column(name = "itemNo", nullable = false, length = 20) | |||
open var itemNo: String? = null | |||
@ManyToOne | |||
@JoinColumn(name = "purchaseOrderLineId") | |||
open var purchaseOrderLine: PurchaseOrderLine? = null | |||
@NotNull | |||
@ManyToOne(fetch = FetchType.LAZY, optional = false) | |||
@ManyToOne | |||
@JoinColumn(name = "stockInId", nullable = false) | |||
open var stockIn: StockIn? = null | |||
@@ -43,23 +47,19 @@ open class StockInLine : BaseEntity<Long>() { | |||
open var priceUnit: String? = null | |||
@Column(name = "productDate") | |||
open var productDate: Instant? = null | |||
open var productDate: LocalDateTime? = null | |||
@Column(name = "shelfLifeDate") | |||
open var shelfLifeDate: Instant? = null | |||
open var shelfLifeDate: LocalDateTime? = null | |||
@Size(max = 10) | |||
// @Size(max = 10) | |||
@NotNull | |||
@Column(name = "status", nullable = false, length = 10) | |||
@Enumerated(EnumType.STRING) | |||
open var status: StockInLineStatus? = null | |||
// @Enumerated(EnumType.STRING) | |||
open var status: String? = null | |||
@ManyToOne(fetch = FetchType.LAZY) | |||
@ManyToOne | |||
@JoinColumn(name = "userId") | |||
open var user: User? = null | |||
@NotNull | |||
@ManyToOne(fetch = FetchType.LAZY, optional = false) | |||
@JoinColumn(name = "m18DataLogId", nullable = false) | |||
open var m18DataLog: M18DataLog? = null | |||
} |
@@ -1,9 +1,13 @@ | |||
package com.ffii.fpsms.modules.stock.entity | |||
import com.ffii.core.support.AbstractRepository | |||
import com.ffii.fpsms.modules.stock.entity.projection.StockInLineInfo | |||
import org.springframework.stereotype.Repository | |||
@Repository | |||
interface StockInLineRepository : AbstractRepository<StockInLine, Long> { | |||
fun findAllByStockInIdAndDeletedFalse(stockInId: Long): List<StockInLine> | |||
fun findAllStockInLineInfoByStockInIdAndDeletedFalse(stockInId: Long): List<StockInLineInfo> | |||
fun findStockInLineInfoByIdAndDeletedFalse(id: Long): StockInLineInfo | |||
fun findStockInLineInfoByIdInAndDeletedFalse(id: List<Long>): List<StockInLineInfo> | |||
} |
@@ -0,0 +1,27 @@ | |||
package com.ffii.fpsms.modules.stock.entity.projection | |||
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.LocalDateTime | |||
interface StockInLineInfo { | |||
val id: Long | |||
@get:Value("#{target.item?.id}") | |||
val itemId: Long | |||
@get:Value("#{target.item?.name}") | |||
val itemName: String? | |||
val itemNo: String | |||
@get:Value("#{target.stockIn?.id}") | |||
val stockInId: Long | |||
@get:Value("#{target.purchaseOrderLine?.id}") | |||
val purchaseOrderLineId: Long? | |||
val demandQty: BigDecimal? | |||
val acceptedQty: BigDecimal | |||
val price: BigDecimal? | |||
val priceUnit: BigDecimal? | |||
val productDate: LocalDateTime? | |||
val shelfLifeDate: LocalDateTime? | |||
val status: String | |||
} |
@@ -0,0 +1,136 @@ | |||
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.PurchaseOrderLineRepository | |||
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 org.springframework.stereotype.Service | |||
import org.springframework.transaction.annotation.Transactional | |||
import java.io.IOException | |||
import java.math.BigDecimal | |||
@Service | |||
open class StockInLineService( | |||
private val jdbcDao: JdbcDao, | |||
private val polRepository: PurchaseOrderLineRepository, | |||
private val stockInService: StockInService, | |||
private val stockInRepository: StockInRepository, | |||
private val stockInLineRepository: StockInLineRepository, | |||
private val itemRepository: ItemsRepository, | |||
): AbstractBaseEntityService<StockInLine, Long, StockInLineRepository>(jdbcDao, stockInLineRepository) { | |||
@Throws(IOException::class) | |||
@Transactional | |||
open fun create(request: SaveStockInLineRequest): MessageResponse { | |||
val stockInLine = StockInLine() | |||
val item = itemRepository.findById(request.itemId).orElseThrow() | |||
val purchaseOrderLine = polRepository.findById(request.purchaseOrderLineId).orElseThrow() | |||
val stockIn = stockInRepository.findByPurchaseOrderIdAndDeletedFalse(request.purchaseOrderId) | |||
?: stockInService.create(SaveStockInRequest(purchaseOrderId = request.purchaseOrderId)).entity as StockIn | |||
stockInLine.apply { | |||
this.item = item | |||
itemNo = item.code | |||
this.purchaseOrderLine = purchaseOrderLine | |||
this.stockIn = stockIn | |||
acceptedQty = request.acceptedQty | |||
status = StockInLineStatus.PENDING.status | |||
} | |||
val savedInLine = saveAndFlush(stockInLine) | |||
val lineInfo = stockInLineRepository.findStockInLineInfoByIdAndDeletedFalse(savedInLine.id!!) | |||
return MessageResponse( | |||
id = savedInLine.id, | |||
code = savedInLine.itemNo, | |||
name = savedInLine.item!!.name, | |||
type = "stock in line created: status = pending", | |||
message = "save success", | |||
errorPosition = null, | |||
entity = lineInfo | |||
) | |||
} | |||
@Throws(IOException::class) | |||
@Transactional | |||
open fun update(request: SaveStockInLineRequest): MessageResponse { | |||
val stockInLine = if (request.id != null) stockInLineRepository.findById(request.id).orElseThrow() | |||
else return MessageResponse( | |||
id = null, | |||
code = null, | |||
name = null, | |||
type = "Found Null", | |||
message = "stock in line id is null", | |||
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) { | |||
stockInLine.apply { | |||
// user = null | |||
status = request.status | |||
} | |||
val savedStockInLine = saveAndFlush(stockInLine) | |||
val lineInfo = stockInLineRepository.findStockInLineInfoByIdAndDeletedFalse(savedStockInLine.id!!) | |||
return MessageResponse( | |||
id = savedStockInLine.id, | |||
code = null, | |||
name = null, | |||
type = "update Success", | |||
message = "stock in line update status success", | |||
errorPosition = null, | |||
entity = listOf(lineInfo) | |||
) | |||
} else { | |||
if (request.acceptedQty <= BigDecimal(0) ) { | |||
return MessageResponse( | |||
id = null, | |||
code = null, | |||
name = null, | |||
type = "acceptedQty == 0", | |||
message = "acceptedQty cannot be 0", | |||
errorPosition = "request.acceptedQty", | |||
) | |||
} | |||
val newStockInLine = StockInLine() | |||
newStockInLine.apply { | |||
this.item = stockInLine.item | |||
itemNo = stockInLine.itemNo | |||
this.purchaseOrderLine = stockInLine.purchaseOrderLine | |||
this.stockIn = stockInLine.stockIn | |||
demandQty = stockInLine.demandQty | |||
acceptedQty = stockInLine.acceptedQty!!.minus(request.acceptedQty) | |||
price = stockInLine.price | |||
priceUnit = stockInLine.priceUnit | |||
productDate = stockInLine.productDate | |||
shelfLifeDate = stockInLine.shelfLifeDate | |||
status = stockInLine.status // this does update status | |||
user = stockInLine.user | |||
} | |||
stockInLine.apply { | |||
acceptedQty = request.acceptedQty | |||
status = request.status | |||
} | |||
val stockInLineEntries = listOf(stockInLine, newStockInLine) | |||
val savedEntries = stockInLineRepository.saveAllAndFlush(stockInLineEntries) | |||
val ids = savedEntries.map { it.id!! } | |||
val lineInfos = stockInLineRepository.findStockInLineInfoByIdInAndDeletedFalse(ids) | |||
return MessageResponse( | |||
id = stockInLine.id, | |||
code = null, | |||
name = null, | |||
type = "Save success", | |||
message = "created 2 stock in line", | |||
errorPosition = null, | |||
entity = lineInfos | |||
) | |||
} | |||
} | |||
} |
@@ -0,0 +1,59 @@ | |||
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.web.models.MessageResponse | |||
import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrderRepository | |||
import com.ffii.fpsms.modules.stock.entity.StockIn | |||
import com.ffii.fpsms.modules.stock.entity.StockInLine | |||
import com.ffii.fpsms.modules.stock.entity.StockInLineRepository | |||
import com.ffii.fpsms.modules.stock.entity.StockInRepository | |||
import com.ffii.fpsms.modules.stock.web.model.SaveStockInRequest | |||
import com.ffii.fpsms.modules.stock.web.model.StockInStatus | |||
import org.springframework.stereotype.Service | |||
import org.springframework.transaction.annotation.Transactional | |||
import java.io.IOException | |||
@Service | |||
open class StockInService( | |||
private val jdbcDao: JdbcDao, | |||
private val stockInRepository: StockInRepository, | |||
private val purchaseOrderRepository: PurchaseOrderRepository, | |||
): AbstractBaseEntityService<StockIn, Long, StockInRepository>(jdbcDao, stockInRepository) { | |||
@Throws(IOException::class) | |||
@Transactional | |||
open fun create(request: SaveStockInRequest): MessageResponse { | |||
val stockIn = StockIn() | |||
val purchaseOrder = if (request.purchaseOrderId != null) | |||
purchaseOrderRepository.findByIdAndDeletedFalse(request.purchaseOrderId).orElseThrow() | |||
else return MessageResponse( | |||
id = null, | |||
code = null, | |||
name = null, | |||
type = "Found Null", | |||
message = "request.purchaseOrderId is null", | |||
errorPosition = "Stock In" | |||
) | |||
stockIn.apply { | |||
code = purchaseOrder.code | |||
supplier = purchaseOrder.supplier | |||
this.purchaseOrder = purchaseOrder | |||
// shop = purchaseOrder.shop | |||
orderDate = purchaseOrder.orderDate | |||
estimatedCompleteDate = purchaseOrder.estimatedArrivalDate?.toLocalDate() | |||
completeDate = purchaseOrder.completeDate | |||
status = StockInStatus.PENDING.status | |||
} | |||
val savedStockIn = saveAndFlush(stockIn) | |||
return MessageResponse( | |||
id = savedStockIn.id, | |||
code = savedStockIn.code, | |||
name = savedStockIn.code, | |||
type = "stock in created: status = ${savedStockIn.status}", | |||
message = "save success", | |||
errorPosition = null, | |||
entity = savedStockIn | |||
) | |||
} | |||
} |
@@ -0,0 +1,28 @@ | |||
package com.ffii.fpsms.modules.stock.web | |||
import com.ffii.fpsms.modules.master.web.models.MessageResponse | |||
import com.ffii.fpsms.modules.master.web.models.NewItemRequest | |||
import com.ffii.fpsms.modules.stock.service.StockInLineService | |||
import com.ffii.fpsms.modules.stock.web.model.SaveStockInLineRequest | |||
import jakarta.validation.Valid | |||
import org.springframework.web.bind.annotation.GetMapping | |||
import org.springframework.web.bind.annotation.PostMapping | |||
import org.springframework.web.bind.annotation.RequestBody | |||
import org.springframework.web.bind.annotation.RequestMapping | |||
import org.springframework.web.bind.annotation.RestController | |||
@RestController | |||
@RequestMapping("/stockInLine") | |||
class StockInLineController( | |||
private val stockInLineService: StockInLineService | |||
) { | |||
@PostMapping("/create") | |||
fun create(@Valid @RequestBody newItem: SaveStockInLineRequest): MessageResponse { | |||
return stockInLineService.create(newItem) | |||
} | |||
@PostMapping("/update") | |||
fun update(@Valid @RequestBody newItem: SaveStockInLineRequest): MessageResponse { | |||
return stockInLineService.update(newItem) | |||
} | |||
} |
@@ -0,0 +1,42 @@ | |||
package com.ffii.fpsms.modules.stock.web.model | |||
import java.math.BigDecimal | |||
import java.time.LocalDate | |||
import java.time.LocalDateTime | |||
enum class StockInStatus(val status: String) { | |||
PENDING("pending"), | |||
COMPLETE("completed"), | |||
// CANCELLED("cancelled") | |||
} | |||
class GameScore(val grade: String) | |||
enum class StockInLineStatus(val status: String) { | |||
PENDING("pending"), | |||
QC("qc"), | |||
FirstDetermine("determine1"), | |||
SecondDetermine("determine2"), | |||
ThirdDetermine("determine3"), | |||
RECEIVING("receiving"), | |||
COMPLETE("completed"); | |||
} | |||
data class SaveStockInRequest( | |||
val purchaseOrderId: Long? = null, | |||
val code: String? = null, | |||
val supplierId: Long? = null, | |||
val shopId: Long? = null, | |||
val orderDate: Long? = null, | |||
val estimatedCompleteDate: LocalDateTime? = null, | |||
val completeDate: LocalDateTime? = null, | |||
val status: LocalDateTime? = null, | |||
val stockOutId: Long? = null, | |||
// val m18 | |||
) | |||
data class SaveStockInLineRequest( | |||
val id: Long?, | |||
val purchaseOrderId: Long, | |||
val purchaseOrderLineId: Long, | |||
val itemId: Long, | |||
val acceptedQty: BigDecimal, | |||
val status: String?, | |||
) |
@@ -0,0 +1,7 @@ | |||
--liquibase formatted sql | |||
--changeset derek:udpate stock in line, add polId | |||
ALTER TABLE `stock_in_line` | |||
ADD COLUMN `purchaseOrderLineId` INT NOT NULL after `deleted`, | |||
ADD CONSTRAINT `FK_STOCK_IN_LINE_PURCHASE_ORDER_LINE_ON_PURCHASEORDERLINEID` | |||
FOREIGN KEY (`purchaseOrderLineId`) REFERENCES `purchase_order_line` (`id`); |
@@ -0,0 +1,6 @@ | |||
--liquibase formatted sql | |||
--changeset derek:udpate stock in line, drop datalogId | |||
ALTER TABLE `stock_in_line` | |||
DROP CONSTRAINT `FK_STOCK_IN_LINE_ON_M18DATALOGID`, | |||
DROP COLUMN `m18DataLogId`; |
@@ -0,0 +1,5 @@ | |||
--liquibase formatted sql | |||
--changeset derek:update stock in, drop datalogId fk | |||
ALTER TABLE `stock_in` | |||
DROP CONSTRAINT `FK_STOCK_IN_ON_M18DATALOGID`; |
@@ -0,0 +1,6 @@ | |||
--liquibase formatted sql | |||
--changeset derek:update stock in, restore datalogId fk | |||
ALTER TABLE `stock_in` | |||
ADD CONSTRAINT FK_STOCK_IN_ON_M18DATALOGID FOREIGN KEY (m18DataLogId) REFERENCES m18_data_log (id), | |||
MODIFY COLUMN `m18DataLogId` INT NULL; |