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