| @@ -8,10 +8,13 @@ import com.ffii.fpsms.modules.pickOrder.entity.projection.PickOrderInfo | |||
| import com.ffii.fpsms.modules.pickOrder.enums.PickOrderStatus | |||
| import com.ffii.fpsms.modules.pickOrder.web.models.* | |||
| import com.ffii.fpsms.modules.stock.entity.InventoryLotLineRepository | |||
| import com.ffii.fpsms.modules.stock.entity.StockOut | |||
| import com.ffii.fpsms.modules.stock.entity.StockOutRepository | |||
| import com.ffii.fpsms.modules.stock.entity.projection.CurrentInventoryItemInfo | |||
| import com.ffii.fpsms.modules.stock.service.InventoryService | |||
| import com.ffii.fpsms.modules.stock.service.StockOutLineService | |||
| import com.ffii.fpsms.modules.stock.service.SuggestedPickLotService | |||
| import com.ffii.fpsms.modules.stock.web.model.StockOutStatus | |||
| import com.ffii.fpsms.modules.stock.web.model.SuggestedPickLotForPoRequest | |||
| import com.ffii.fpsms.modules.user.entity.UserRepository | |||
| import com.ffii.fpsms.modules.user.service.UserService | |||
| @@ -36,6 +39,7 @@ open class PickOrderService( | |||
| val userService: UserService, | |||
| private val inventoryLotLineRepository: InventoryLotLineRepository, | |||
| val inventoryService: InventoryService, | |||
| private val stockOutRepository: StockOutRepository | |||
| ) { | |||
| open fun localDateTimeParse(dateTime: String?, pattern: String? = "YYYY-MM-DD hh:mm:ss"): LocalDateTime? { | |||
| try { | |||
| @@ -149,8 +153,8 @@ open class PickOrderService( | |||
| + " i.name as itemName, " | |||
| + " pol.qty as qty, " | |||
| + " max(uc.code) as uom, " | |||
| + " group_concat(w.name) as warehouse, " | |||
| + " group_concat(il.lotNo) as suggestedLotNo " | |||
| + " group_concat(JSON_ARRAY(w.name)) as warehouse, " | |||
| + " group_concat(JSON_ARRAY(il.lotNo)) as suggestedLotNo " | |||
| + " from pick_order po " | |||
| + " left join pick_order_line pol on pol.poId = po.id " | |||
| + " left join items i on i.id = pol.itemId " | |||
| @@ -390,7 +394,14 @@ open class PickOrderService( | |||
| val suggestions = | |||
| suggestedPickLotService.suggestionForPickOrders(SuggestedPickLotForPoRequest(pickOrders = pos)) | |||
| val currUser = SecurityUtils.getUser().orElseThrow() | |||
| val stockOut = StockOut().apply { | |||
| this.type = "job" | |||
| this.consoPickOrderCode = request.consoCode | |||
| this.status = StockOutStatus.PENDING.status | |||
| this.handler = currUser.id | |||
| } | |||
| stockOutRepository.save(stockOut) | |||
| suggestedPickLotService.saveAll(suggestions.suggestedList) | |||
| pickOrderRepository.saveAll(pos) | |||
| return ResponseEntity("success", HttpStatus.OK) | |||
| @@ -17,11 +17,8 @@ open class StockOut: BaseEntity<Long>(){ | |||
| @Column(name = "deliveryOrderCode") | |||
| open var deliveryOrderCode: String? = null | |||
| @Column(name = "pickOrderCode") | |||
| open var pickOrderCode: String? = null | |||
| @Column(name = "consoCode") | |||
| open var consoCode: String? = null | |||
| @Column(name = "consoPickOrderCode") | |||
| open var consoPickOrderCode: String? = null | |||
| @Column(name = "completeDate") | |||
| open var completeDate: LocalDateTime? = null | |||
| @@ -1,6 +1,7 @@ | |||
| package com.ffii.fpsms.modules.stock.entity | |||
| import com.ffii.core.support.AbstractRepository | |||
| import com.ffii.fpsms.modules.stock.entity.projection.StockOutLineInfo | |||
| import com.ffii.fpsms.modules.stock.web.model.StockOutStatus | |||
| import org.springframework.stereotype.Repository | |||
| @@ -8,4 +9,5 @@ import org.springframework.stereotype.Repository | |||
| interface StockOutLIneRepository: AbstractRepository<StockOutLine, Long> { | |||
| fun findAllByStockOutIdAndDeletedFalse(stockOutId: Long): List<StockOutLine> | |||
| // fun findAllByStockOutIdAndDeletedFalse(stockOutId: Long, status: StockOutStatus): List<StockOutLine> | |||
| fun findAllByPickOrderLineIdAndDeletedFalse(pickOrderLineId: Long): List<StockOutLineInfo> | |||
| } | |||
| @@ -25,10 +25,9 @@ open class StockOutLine: BaseEntity<Long>() { | |||
| @JoinColumn(name = "stockOutId") | |||
| open var stockOut: StockOut? = null | |||
| @NotNull | |||
| @OneToOne | |||
| @JoinColumn(name = "inventoryId") | |||
| open var inventory: Inventory? = null | |||
| @ManyToOne | |||
| @JoinColumn(name = "inventoryLotLineId") | |||
| open var inventoryLotLine: InventoryLotLine? = null | |||
| @NotNull | |||
| @Column(name = "status") | |||
| @@ -2,7 +2,9 @@ package com.ffii.fpsms.modules.stock.entity | |||
| import com.ffii.core.support.AbstractRepository | |||
| import org.springframework.stereotype.Repository | |||
| import java.util.Optional | |||
| @Repository | |||
| interface StockOutRepository: AbstractRepository<StockOut, Long> { | |||
| fun findByConsoPickOrderCode(consoPickOrderCode: String) : Optional<StockOut> | |||
| } | |||
| @@ -0,0 +1,26 @@ | |||
| package com.ffii.fpsms.modules.stock.entity.projection | |||
| import org.springframework.beans.factory.annotation.Value | |||
| import java.math.BigDecimal | |||
| import java.time.LocalDateTime | |||
| interface StockOutLineInfo { | |||
| val id: Long | |||
| @get:Value("#{target.item?.id}") | |||
| val itemId: Long | |||
| @get:Value("#{target.item?.name}") | |||
| val itemName: String? | |||
| val itemNo: String | |||
| val qty: BigDecimal | |||
| @get:Value("#{target.stockOut?.id}") | |||
| val stockOutId: Long | |||
| @get:Value("#{target.pickOrderLine?.id}") | |||
| val pickOrderLineId: Long | |||
| @get:Value("#{target.inventoryLotLine?.id}") | |||
| val inventoryLotLineId: Long? | |||
| val status: String | |||
| val pickTime: LocalDateTime? | |||
| } | |||
| @@ -4,7 +4,9 @@ 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.pickOrder.entity.PickOrderLineRepository | |||
| import com.ffii.fpsms.modules.stock.entity.* | |||
| import com.ffii.fpsms.modules.stock.entity.projection.StockOutLineInfo | |||
| import com.ffii.fpsms.modules.stock.web.model.SaveStockOutLineRequest | |||
| import com.ffii.fpsms.modules.stock.web.model.SaveStockOutRequest | |||
| import com.ffii.fpsms.modules.stock.web.model.StockOutLineStatus | |||
| @@ -18,102 +20,76 @@ import java.time.LocalDateTime | |||
| @Service | |||
| open class StockOutLineService( | |||
| private val jdbcDao: JdbcDao, | |||
| private val pickOrderLineRepository: PickOrderLineRepository, | |||
| private val stockOutRepository: StockOutRepository, | |||
| private val stockOutLIneRepository: StockOutLIneRepository, | |||
| private val stockOutLineRepository: StockOutLIneRepository, | |||
| private val itemRepository: ItemsRepository, | |||
| private val inventoryRepository: InventoryRepository, | |||
| ): AbstractBaseEntityService<StockOutLine, Long, StockOutLIneRepository>(jdbcDao, stockOutLIneRepository) { | |||
| private val inventoryLotLineRepository: InventoryLotLineRepository | |||
| ): AbstractBaseEntityService<StockOutLine, Long, StockOutLIneRepository>(jdbcDao, stockOutLineRepository) { | |||
| @Throws(IOException::class) | |||
| @Transactional | |||
| open fun findAllByStockOutId(stockOutId: Long): List<StockOutLine> { | |||
| return stockOutLIneRepository.findAllByStockOutIdAndDeletedFalse(stockOutId) | |||
| return stockOutLineRepository.findAllByStockOutIdAndDeletedFalse(stockOutId) | |||
| } | |||
| @Throws(IOException::class) | |||
| @Transactional | |||
| open fun deleteById(id: Long): String { | |||
| val deleteStockOutline = stockOutLIneRepository.findById(id).orElseThrow().apply { | |||
| val deleteStockOutline = stockOutLineRepository.findById(id).orElseThrow().apply { | |||
| deleted = true | |||
| } | |||
| val updateItem = stockOutLIneRepository.save(deleteStockOutline) | |||
| val updateItem = stockOutLineRepository.save(deleteStockOutline) | |||
| val response = "mark deleted item: ${updateItem.id}" | |||
| return response | |||
| } | |||
| @Throws(IOException::class) | |||
| @Transactional | |||
| open fun deleteByStockOutId(id: Long): String { | |||
| val deleteStockOutline = stockOutLIneRepository.findAllByStockOutIdAndDeletedFalse(id).map {sol -> | |||
| val deleteStockOutline = stockOutLineRepository.findAllByStockOutIdAndDeletedFalse(id).map {sol -> | |||
| sol.apply { | |||
| deleted = true | |||
| } | |||
| } | |||
| val updateItemIds = stockOutLIneRepository.saveAllAndFlush(deleteStockOutline).map { it.id } | |||
| val updateItemIds = stockOutLineRepository.saveAllAndFlush(deleteStockOutline).map { it.id } | |||
| val response = "mark deleted items: $updateItemIds" | |||
| return response | |||
| } | |||
| @Throws(IOException::class) | |||
| @Transactional | |||
| /// only create?????? | |||
| open fun getAllStockOutLineByPickOrderLineId(pickOrderLineId: Long): List<StockOutLineInfo> { | |||
| return stockOutLineRepository.findAllByPickOrderLineIdAndDeletedFalse(pickOrderLineId) | |||
| } | |||
| @Transactional | |||
| open fun create(request: SaveStockOutLineRequest): MessageResponse { | |||
| val stockOutLine = StockOutLine() | |||
| val stockOut = stockOutRepository.findByConsoPickOrderCode(request.consoCode).orElseThrow() | |||
| val pickOrderLine = pickOrderLineRepository.findById(request.pickOrderLineId).orElseThrow() | |||
| val item = itemRepository.findById(request.itemId).orElseThrow() | |||
| stockOutLine.apply { | |||
| this.item = item | |||
| qty = request.qty | |||
| status = StockOutLineStatus.PENDING.status //create the base record = the original total sum of one item | |||
| } | |||
| val savedStockOutLine = stockOutLIneRepository.saveAndFlush(stockOutLine) | |||
| val inventoryLotLine = inventoryLotLineRepository.findById(request.inventoryLotLineId).orElseThrow() | |||
| val stockOutLine = StockOutLine() | |||
| .apply { | |||
| this.item = item | |||
| this.qty = request.qty | |||
| this.stockOut = stockOut | |||
| this.inventoryLotLine | |||
| this.pickOrderLine = pickOrderLine | |||
| this.status = StockOutLineStatus.PENDING.status | |||
| } | |||
| val savedStockOutLine = saveAndFlush(stockOutLine) | |||
| return MessageResponse( | |||
| id = savedStockOutLine.id, | |||
| code = item.code, | |||
| name = item.name, | |||
| name = savedStockOutLine.inventoryLotLine!!.inventoryLot!!.lotNo, | |||
| code = savedStockOutLine.stockOut!!.consoPickOrderCode, | |||
| type = savedStockOutLine.status, | |||
| message = "save success", | |||
| errorPosition = null | |||
| message = "success", | |||
| errorPosition = null, | |||
| entity = savedStockOutLine, | |||
| ) | |||
| } | |||
| // stock out -> stock out line record : {qty = 100} | |||
| // each pick create new stock out line {...sol, status = picked} | |||
| // when confirm all picked and complete in web | |||
| // update: | |||
| // 1.stock out status | |||
| // remove: | |||
| // 1.stock out line : {qty = 100} | |||
| @Throws(IOException::class) | |||
| @Transactional | |||
| open fun pick(request: SaveStockOutLineRequest): MessageResponse { | |||
| val item = itemRepository.findById(request.itemId).orElseThrow() | |||
| if (request.inventoryId === null) { | |||
| return MessageResponse( | |||
| id = request.id, | |||
| code = null, | |||
| name = item.name, | |||
| type = null, | |||
| message = "inventory is null or not exist", | |||
| errorPosition = "inventoryId" | |||
| ) | |||
| } | |||
| val stockOutLine = StockOutLine() | |||
| val _stockOut = stockOutRepository.findById(request.stockOutId).orElseThrow() | |||
| val _inventory = inventoryRepository.findById(request.inventoryId).orElseThrow() | |||
| val picker = request.pickerId | |||
| stockOutLine.apply { | |||
| this.item = item | |||
| qty = request.qty | |||
| stockOut = _stockOut | |||
| inventory = _inventory | |||
| status = "picked" | |||
| pickTime = LocalDateTime.now() | |||
| pickerId = picker | |||
| } | |||
| val savedOutLine = stockOutLIneRepository.saveAndFlush(stockOutLine) | |||
| return MessageResponse( | |||
| id = savedOutLine.id, | |||
| code = null, | |||
| name = null, | |||
| type = savedOutLine.status, | |||
| message = "item picked", | |||
| errorPosition = null | |||
| ) | |||
| open fun update(request: SaveStockOutLineRequest) { | |||
| val stockOutLine = stockOutLineRepository.findById(request.id!!).orElseThrow() | |||
| } | |||
| } | |||
| @@ -25,35 +25,6 @@ open class StockOutService( | |||
| ): AbstractBaseEntityService<StockOut, Long, StockOutRepository>(jdbcDao, stockOutRepository) { | |||
| @Throws(IOException::class) | |||
| @Transactional | |||
| // update record data | |||
| open fun saveStockOut(request: SaveStockOutRequest): MessageResponse { | |||
| val stockOut = if (request.id !== null) stockOutRepository.findById(request.id).orElseThrow() else StockOut() | |||
| stockOut.apply { | |||
| type = request.type | |||
| deliveryOrderCode = request.deliveryOrderCode | |||
| pickOrderCode = request.pickOrderCode | |||
| consoCode = request.consoCode | |||
| completeDate = request.completeDate | |||
| handler = request.handler | |||
| targetOutletId = request.targetOutletId | |||
| remarks = request.remarks | |||
| } | |||
| val savedStockOut = stockOutRepository.saveAndFlush(stockOut) | |||
| // only create stock out line, no update | |||
| for (outLine in request.stockOutLine) { | |||
| stockOutLineService.create(outLine) | |||
| } | |||
| return MessageResponse( | |||
| id = savedStockOut.id, | |||
| code = savedStockOut.pickOrderCode ?: savedStockOut.deliveryOrderCode , | |||
| name = savedStockOut.consoCode, | |||
| type = savedStockOut.type, | |||
| message = "save success", | |||
| errorPosition = null | |||
| ) | |||
| } | |||
| @Throws(IOException::class) | |||
| @Transactional | |||
| open fun complete(request: SaveStockOutRequest): MessageResponse { | |||
| if (request.id === null) { | |||
| return MessageResponse( | |||
| @@ -80,8 +51,8 @@ open class StockOutService( | |||
| if (!isBalanced) { | |||
| return MessageResponse( | |||
| id = request.id, | |||
| code = stockOut.consoCode, | |||
| name = stockOut.pickOrderCode ?: stockOut.deliveryOrderCode, | |||
| code = "stockOut.consoCode", | |||
| name = "stockOut.pickOrderCode ?: stockOut.deliveryOrderCode", | |||
| type = stockOut.type, | |||
| message = "there are items not picked", | |||
| errorPosition = null | |||
| @@ -101,7 +72,7 @@ open class StockOutService( | |||
| val ledgers = pickLines.map {stockOutLine -> | |||
| StockLedger().apply { | |||
| this.stockOutLine = stockOutLine | |||
| this.inventory = stockOutLine.inventory | |||
| // this.inventory = stockOutLine.inventory | |||
| outQty = stockOutLine.qty | |||
| } | |||
| } | |||
| @@ -115,8 +86,8 @@ open class StockOutService( | |||
| val savedStockOut = stockOutRepository.saveAndFlush(stockOut) | |||
| return MessageResponse( | |||
| id = savedStockOut.id, | |||
| code = savedStockOut.consoCode, | |||
| name = savedStockOut.pickOrderCode ?: savedStockOut.deliveryOrderCode, | |||
| code = "savedStockOut.consoCode", | |||
| name = "savedStockOut.pickOrderCode ?: savedStockOut.deliveryOrderCode", | |||
| type = savedStockOut.type, | |||
| message = "stock out completed", | |||
| errorPosition = null | |||
| @@ -37,6 +37,9 @@ class InventoryLotLineController ( | |||
| val zero = BigDecimal.ZERO | |||
| return LotLineInfo( | |||
| inventoryLotLineId = inventoryLotLine.id!!, | |||
| itemId = inventoryLotLine.inventoryLot!!.item!!.id!!, | |||
| itemNo = inventoryLotLine.inventoryLot!!.item!!.code!!, | |||
| itemName = inventoryLotLine.inventoryLot!!.item!!.name!!, | |||
| lotNo = stockInLine.lotNo!!, | |||
| remainingQty = (inventoryLotLine.inQty ?: zero) | |||
| .minus(inventoryLotLine.outQty ?: zero) | |||
| @@ -0,0 +1,25 @@ | |||
| package com.ffii.fpsms.modules.stock.web | |||
| import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrderLine | |||
| import com.ffii.fpsms.modules.stock.entity.StockOutLine | |||
| import com.ffii.fpsms.modules.stock.entity.projection.StockOutLineInfo | |||
| import com.ffii.fpsms.modules.stock.service.StockInLineService | |||
| import com.ffii.fpsms.modules.stock.service.StockOutLineService | |||
| import jakarta.validation.Valid | |||
| import org.springframework.web.bind.annotation.GetMapping | |||
| import org.springframework.web.bind.annotation.PathVariable | |||
| import org.springframework.web.bind.annotation.PostMapping | |||
| import org.springframework.web.bind.annotation.RequestMapping | |||
| import org.springframework.web.bind.annotation.RestController | |||
| @RestController | |||
| @RequestMapping("/stockOutLine") | |||
| class StockOutLineController( | |||
| private val stockOutLineService: StockOutLineService | |||
| ) { | |||
| @GetMapping("/getByPickOrderLineId/{pickOrderLineId}") | |||
| fun getByPurchaseOrderLineId(@Valid @PathVariable pickOrderLineId: Long): List<StockOutLineInfo> { | |||
| return stockOutLineService.getAllStockOutLineByPickOrderLineId(pickOrderLineId) | |||
| } | |||
| } | |||
| @@ -4,6 +4,9 @@ import java.math.BigDecimal | |||
| data class LotLineInfo( | |||
| val inventoryLotLineId: Long, | |||
| val itemId: Long, | |||
| val itemNo: String, | |||
| val itemName: String, | |||
| val lotNo: String, | |||
| val remainingQty: BigDecimal, | |||
| val uom: String | |||
| @@ -30,10 +30,11 @@ data class SaveStockOutRequest( | |||
| data class SaveStockOutLineRequest( | |||
| val id: Long?, | |||
| val consoCode: String, | |||
| val itemId: Long, | |||
| val qty: Double, | |||
| val stockOutId: Long, | |||
| val inventoryId: Long?, | |||
| val pickOrderLineId: Long, | |||
| val inventoryLotLineId: Long, | |||
| val status: StockOutLineStatus?, | |||
| val pickTime: LocalDateTime?, | |||
| val pickerId: Long? | |||
| @@ -77,6 +77,15 @@ public class UserService extends AbstractBaseEntityService<User, Long, UserRepos | |||
| }); | |||
| } | |||
| public User getUserById(Long id) { | |||
| User user = userRepository.findById(id).orElseThrow(); | |||
| Set<GrantedAuthority> auths = new LinkedHashSet<GrantedAuthority>(); | |||
| auths.add(new SimpleGrantedAuthority("ROLE_USER")); | |||
| jdbcDao.queryForList(USER_AUTH_SQL + UNION_SQL + GROUP_AUTH_SQL, Map.of("userId", user.getId())) | |||
| .forEach(item -> auths.add(new SimpleGrantedAuthority((String) item.get("authority")))); | |||
| user.setAuthorities(auths); | |||
| return user; | |||
| } | |||
| public Optional<User> findByUsername(String username) { | |||
| return userRepository.findByUsernameAndDeletedFalse(username); | |||
| } | |||
| @@ -98,7 +98,11 @@ public class UserController{ | |||
| logger.info(test); | |||
| return test; | |||
| } | |||
| @GetMapping("/user-info/{id}") | |||
| // @PreAuthorize("hasAuthority('VIEW_USER')") | |||
| public ResponseEntity<?> getUserById(@PathVariable Long id) throws NotFoundException { | |||
| return ResponseEntity.ok(userService.getUserById(id)); | |||
| } | |||
| // @Operation(summary = "delete user", responses = { @ApiResponse(responseCode = "204"), | |||
| // @ApiResponse(responseCode = "404", content = @Content) }) | |||
| @DeleteMapping("/{id}") | |||
| @@ -123,6 +127,7 @@ public class UserController{ | |||
| // @PreAuthorize("hasAuthority('MAINTAIN_USER')") | |||
| public ResponseEntity<?> createPublicUserRecord(@RequestBody NewPublicUserReq req) throws UnsupportedEncodingException { | |||
| logger.info("Create user request:"); | |||
| req.setPassword("mms1234"); // default for now | |||
| return ResponseEntity.ok(new IdRes(userService.newPublicUserRecord(req).getId())) ; | |||
| } | |||
| @@ -197,6 +202,7 @@ public class UserController{ | |||
| return ResponseEntity.ok(password); | |||
| } | |||
| // @Operation(summary = "get password rules") | |||
| @GetMapping("/password-rule") | |||
| public PasswordRule passwordRlue() { | |||
| @@ -0,0 +1,7 @@ | |||
| -- liquibase formatted sql | |||
| -- changeset derek:update_stockoutline_lot_line_id | |||
| ALTER TABLE `stock_out_line` | |||
| CHANGE COLUMN `inventoryId` `inventoryLotLineId` INT(11) NULL, | |||
| ADD CONSTRAINT FK_STOCK_OUT_LINE_ON_LOT_LINE_ID FOREIGN KEY (inventoryLotLineId) REFERENCES inventory_lot_line (id) | |||
| ; | |||
| @@ -0,0 +1,7 @@ | |||
| -- liquibase formatted sql | |||
| -- changeset derek:remove_consoCode_change_pickOrderCode | |||
| ALTER TABLE `stock_out` | |||
| DROP COLUMN `consoCode`, | |||
| CHANGE COLUMN `pickOrderCode` `consoPickOrderCode` VARCHAR(255) NULL | |||
| ; | |||