Parcourir la source

庫存調整 Remarks 寫入 DB 並回填

production
CANCERYS\kw093 il y a 1 semaine
Parent
révision
4d839530a5
8 fichiers modifiés avec 69 ajouts et 13 suppressions
  1. +4
    -2
      src/main/java/com/ffii/fpsms/config/security/SecurityConfig.java
  2. +1
    -3
      src/main/java/com/ffii/fpsms/modules/report/web/StockLotOnhandReportController.kt
  3. +3
    -0
      src/main/java/com/ffii/fpsms/modules/stock/entity/StockAdjustmentRecord.kt
  4. +14
    -1
      src/main/java/com/ffii/fpsms/modules/stock/entity/StockAdjustmentRecordRepository.kt
  5. +26
    -6
      src/main/java/com/ffii/fpsms/modules/stock/service/StockAdjustmentService.kt
  6. +8
    -0
      src/main/java/com/ffii/fpsms/modules/stock/web/StockAdjustmentController.kt
  7. +7
    -1
      src/main/java/com/ffii/fpsms/modules/stock/web/model/StockAdjustmentRequest.kt
  8. +6
    -0
      src/main/resources/db/changelog/changes/20260908_stock_adjustment_record_remarks/01_add_remarks.sql

+ 4
- 2
src/main/java/com/ffii/fpsms/config/security/SecurityConfig.java Voir le fichier

@@ -77,8 +77,8 @@ public class SecurityConfig {
/** /**
* FP-MTMS Version Checklist | Functions Ref. No. 67 | v1.0.0 | 2026-08-13 * FP-MTMS Version Checklist | Functions Ref. No. 67 | v1.0.0 | 2026-08-13
* FP-MTMS Version Checklist | Functions Ref. No. 51 | v1.0.1 | 2026-08-06 * FP-MTMS Version Checklist | Functions Ref. No. 51 | v1.0.1 | 2026-08-06
* FP-MTMS Version Checklist | Functions Ref. No. 3 | v1.0.6 | 2026-09-07
* (stockAdjustment/submit → INVENTORY_ADJUST only)
* FP-MTMS Version Checklist | Functions Ref. No. 3 | v1.0.7 | 2026-09-08
* (stockAdjustment/submit and GET /latestRemarks → INVENTORY_ADJUST)
*/ */
@Bean @Bean
@Order(1) @Order(1)
@@ -119,6 +119,8 @@ public class SecurityConfig {
.hasAnyAuthority("TESTING", "ADMIN") .hasAnyAuthority("TESTING", "ADMIN")
.requestMatchers(HttpMethod.POST, "/stockAdjustment/submit") .requestMatchers(HttpMethod.POST, "/stockAdjustment/submit")
.hasAuthority("INVENTORY_ADJUST") .hasAuthority("INVENTORY_ADJUST")
.requestMatchers(HttpMethod.GET, "/stockAdjustment/latestRemarks")
.hasAuthority("INVENTORY_ADJUST")
.requestMatchers(HttpMethod.GET, "/inventoryLotLine/trace") .requestMatchers(HttpMethod.GET, "/inventoryLotLine/trace")
.hasAuthority("ITEM_TRACING") .hasAuthority("ITEM_TRACING")
.requestMatchers(HttpMethod.GET, "/inventoryLotLine/trace/location/**") .requestMatchers(HttpMethod.GET, "/inventoryLotLine/trace/location/**")


+ 1
- 3
src/main/java/com/ffii/fpsms/modules/report/web/StockLotOnhandReportController.kt Voir le fichier

@@ -216,7 +216,6 @@ class StockLotOnhandReportController(
"樓層", "倉庫", "區域", "儲位", "樓層", "倉庫", "區域", "儲位",
"最後異動日", "最後異動(入庫/出庫)", "最後異動日", "最後異動(入庫/出庫)",
"批號結餘", "單位均價", "庫存總價值", "批號結餘", "單位均價", "庫存總價值",
"備註",
) )
val totalColumns = headers.size val totalColumns = headers.size
var rowIndex = 0 var rowIndex = 0
@@ -301,7 +300,6 @@ class StockLotOnhandReportController(
setNumberCell(r, 12, m["lotQtyRaw"], styles.number) setNumberCell(r, 12, m["lotQtyRaw"], styles.number)
setNumberCell(r, 13, m["avgUnitPriceRaw"], styles.number) setNumberCell(r, 13, m["avgUnitPriceRaw"], styles.number)
setNumberCell(r, 14, m["stockValueRaw"], styles.number) setNumberCell(r, 14, m["stockValueRaw"], styles.number)
setTextCell(r, 15, m["remarks"], styles.text)
} }
} }


@@ -310,7 +308,7 @@ class StockLotOnhandReportController(
sheet.setAutoFilter(CellRangeAddress(headerRowIndex, lastRowIndex, 0, totalColumns - 1)) sheet.setAutoFilter(CellRangeAddress(headerRowIndex, lastRowIndex, 0, totalColumns - 1))
} }
sheet.createFreezePane(0, headerRowIndex + 1) sheet.createFreezePane(0, headerRowIndex + 1)
intArrayOf(14, 28, 14, 8, 20, 12, 10, 12, 10, 10, 12, 20, 12, 12, 14, 28)
intArrayOf(14, 28, 14, 8, 20, 12, 10, 12, 10, 10, 12, 20, 12, 12, 14)
.forEachIndexed { i, w -> sheet.setColumnWidth(i, w * 256) } .forEachIndexed { i, w -> sheet.setColumnWidth(i, w * 256) }


val out = ByteArrayOutputStream() val out = ByteArrayOutputStream()


+ 3
- 0
src/main/java/com/ffii/fpsms/modules/stock/entity/StockAdjustmentRecord.kt Voir le fichier

@@ -31,6 +31,9 @@ open class StockAdjustmentRecord : BaseEntity<Long>() {
@Column(name = "outQty", precision = 14, scale = 2) @Column(name = "outQty", precision = 14, scale = 2)
open var outQty: BigDecimal? = null open var outQty: BigDecimal? = null


@Column(name = "remarks", length = 500)
open var remarks: String? = null

@JsonBackReference @JsonBackReference
@ManyToOne @ManyToOne
@JoinColumn(name = "stockInLineId") @JoinColumn(name = "stockInLineId")


+ 14
- 1
src/main/java/com/ffii/fpsms/modules/stock/entity/StockAdjustmentRecordRepository.kt Voir le fichier

@@ -1,7 +1,20 @@
package com.ffii.fpsms.modules.stock.entity package com.ffii.fpsms.modules.stock.entity


import com.ffii.core.support.AbstractRepository import com.ffii.core.support.AbstractRepository
import org.springframework.data.jpa.repository.Query
import org.springframework.data.repository.query.Param
import org.springframework.stereotype.Repository import org.springframework.stereotype.Repository


@Repository @Repository
interface StockAdjustmentRecordRepository : AbstractRepository<StockAdjustmentRecord, Long>
interface StockAdjustmentRecordRepository : AbstractRepository<StockAdjustmentRecord, Long> {
@Query(
"""
SELECT r FROM StockAdjustmentRecord r
WHERE r.item.id = :itemId
AND r.deleted = false
AND r.remarks IS NOT NULL
ORDER BY r.id DESC
"""
)
fun findRemarkRecordsByItemId(@Param("itemId") itemId: Long): List<StockAdjustmentRecord>
}

+ 26
- 6
src/main/java/com/ffii/fpsms/modules/stock/service/StockAdjustmentService.kt Voir le fichier

@@ -4,6 +4,7 @@ import com.ffii.fpsms.modules.master.web.models.MessageResponse
import com.ffii.fpsms.modules.stock.entity.InventoryLotLine import com.ffii.fpsms.modules.stock.entity.InventoryLotLine
import com.ffii.fpsms.modules.stock.entity.InventoryLotLineRepository import com.ffii.fpsms.modules.stock.entity.InventoryLotLineRepository
import com.ffii.fpsms.modules.stock.web.model.StockAdjustmentRequest import com.ffii.fpsms.modules.stock.web.model.StockAdjustmentRequest
import com.ffii.fpsms.modules.stock.web.model.StockAdjustmentRemarksResponse
import com.ffii.fpsms.modules.stock.web.model.StockInRequest import com.ffii.fpsms.modules.stock.web.model.StockInRequest
import com.ffii.fpsms.modules.stock.web.model.StockOutRequest import com.ffii.fpsms.modules.stock.web.model.StockOutRequest
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
@@ -24,6 +25,7 @@ open class StockAdjustmentService(
private val stockAdjustmentRecordRepository: StockAdjustmentRecordRepository private val stockAdjustmentRecordRepository: StockAdjustmentRecordRepository
) { ) {


/** FP-MTMS Version Checklist | Functions Ref. No. 3 | v1.0.7 | 2026-09-08 */
@Transactional @Transactional
open fun submit(request: StockAdjustmentRequest): MessageResponse { open fun submit(request: StockAdjustmentRequest): MessageResponse {
val originalById = request.originalLines.filter { it.id > 0 }.associateBy { it.id } val originalById = request.originalLines.filter { it.id > 0 }.associateBy { it.id }
@@ -36,7 +38,7 @@ open class StockAdjustmentService(
inventoryLotLineId = line.id, inventoryLotLineId = line.id,
type = "ADJ" type = "ADJ"
) )
saveAdjustmentRecordForStockOut(stockOutLine)
saveAdjustmentRecordForStockOut(stockOutLine, line.remarks)
} }
for (current in request.currentLines) { for (current in request.currentLines) {
// Branch 2: New entry — createStockIn (OPEN or ADJ) // Branch 2: New entry — createStockIn (OPEN or ADJ)
@@ -56,7 +58,7 @@ open class StockAdjustmentService(
warehouseId = current.warehouseId warehouseId = current.warehouseId
) )
) )
saveAdjustmentRecordForStockIn(stockInLine)
saveAdjustmentRecordForStockIn(stockInLine, current.remarks)
continue continue
} }


@@ -74,7 +76,7 @@ open class StockAdjustmentService(
stockInRequest, stockInRequest,
inventoryLotLine inventoryLotLine
) )
saveAdjustmentRecordForStockIn(stockInLine)
saveAdjustmentRecordForStockIn(stockInLine, current.remarks)
} else { } else {
// Branch 3 (qty down): adjustment outbound only (not pick createStockOut) // Branch 3 (qty down): adjustment outbound only (not pick createStockOut)
val stockOutLine = stockOutLineService.createStockOutForAdjustment( val stockOutLine = stockOutLineService.createStockOutForAdjustment(
@@ -84,7 +86,7 @@ open class StockAdjustmentService(
type = "ADJ" type = "ADJ"
) )
) )
saveAdjustmentRecordForStockOut(stockOutLine)
saveAdjustmentRecordForStockOut(stockOutLine, current.remarks)
} }
} }


@@ -98,6 +100,19 @@ open class StockAdjustmentService(
) )
} }


/** FP-MTMS Version Checklist | Functions Ref. No. 3 | v1.0.7 | 2026-09-08 */
open fun findLatestRemarksByItemId(itemId: Long): List<StockAdjustmentRemarksResponse> {
val seenLotNos = HashSet<String>()
val result = mutableListOf<StockAdjustmentRemarksResponse>()
for (row in stockAdjustmentRecordRepository.findRemarkRecordsByItemId(itemId)) {
val remarks = normalizeRemarks(row.remarks) ?: continue
val lotNo = row.lotNo?.trim().orEmpty()
if (lotNo.isEmpty() || !seenLotNos.add(lotNo)) continue
result.add(StockAdjustmentRemarksResponse(lotNo = lotNo, remarks = remarks))
}
return result
}

private fun buildStockInRequestFromExistingLotLine( private fun buildStockInRequestFromExistingLotLine(
inventoryLotLine: InventoryLotLine, inventoryLotLine: InventoryLotLine,
acceptedQty: BigDecimal acceptedQty: BigDecimal
@@ -131,7 +146,7 @@ open class StockAdjustmentService(
warehouseId = warehouseId warehouseId = warehouseId
) )
} }
private fun saveAdjustmentRecordForStockIn(stockInLine: StockInLine) {
private fun saveAdjustmentRecordForStockIn(stockInLine: StockInLine, remarks: String?) {
val item = stockInLine.item ?: return val item = stockInLine.item ?: return
val record = StockAdjustmentRecord().apply { val record = StockAdjustmentRecord().apply {
this.item = item this.item = item
@@ -140,13 +155,14 @@ open class StockAdjustmentService(
this.lotNo = stockInLine.lotNo this.lotNo = stockInLine.lotNo
this.inQty = stockInLine.acceptedQty this.inQty = stockInLine.acceptedQty
this.outQty = null this.outQty = null
this.remarks = normalizeRemarks(remarks)
this.stockInLine = stockInLine this.stockInLine = stockInLine
this.stockOutLine = null this.stockOutLine = null
} }
stockAdjustmentRecordRepository.save(record) stockAdjustmentRecordRepository.save(record)
} }


private fun saveAdjustmentRecordForStockOut(stockOutLine: StockOutLine) {
private fun saveAdjustmentRecordForStockOut(stockOutLine: StockOutLine, remarks: String?) {
val item = stockOutLine.item ?: return val item = stockOutLine.item ?: return
val lotNo = stockOutLine.inventoryLotLine?.inventoryLot?.lotNo val lotNo = stockOutLine.inventoryLotLine?.inventoryLot?.lotNo
val record = StockAdjustmentRecord().apply { val record = StockAdjustmentRecord().apply {
@@ -156,9 +172,13 @@ open class StockAdjustmentService(
this.lotNo = lotNo this.lotNo = lotNo
this.inQty = null this.inQty = null
this.outQty = BigDecimal.valueOf(stockOutLine.qty ?: 0.0) this.outQty = BigDecimal.valueOf(stockOutLine.qty ?: 0.0)
this.remarks = normalizeRemarks(remarks)
this.stockInLine = null this.stockInLine = null
this.stockOutLine = stockOutLine this.stockOutLine = stockOutLine
} }
stockAdjustmentRecordRepository.save(record) stockAdjustmentRecordRepository.save(record)
} }

private fun normalizeRemarks(remarks: String?): String? =
remarks?.trim()?.takeIf { it.isNotEmpty() }
} }

+ 8
- 0
src/main/java/com/ffii/fpsms/modules/stock/web/StockAdjustmentController.kt Voir le fichier

@@ -3,6 +3,7 @@ package com.ffii.fpsms.modules.stock.web
import com.ffii.fpsms.modules.master.web.models.MessageResponse import com.ffii.fpsms.modules.master.web.models.MessageResponse
import com.ffii.fpsms.modules.stock.service.StockAdjustmentService import com.ffii.fpsms.modules.stock.service.StockAdjustmentService
import com.ffii.fpsms.modules.stock.web.model.StockAdjustmentRequest import com.ffii.fpsms.modules.stock.web.model.StockAdjustmentRequest
import com.ffii.fpsms.modules.stock.web.model.StockAdjustmentRemarksResponse
import jakarta.validation.Valid import jakarta.validation.Valid
import org.springframework.web.bind.annotation.* import org.springframework.web.bind.annotation.*


@@ -11,8 +12,15 @@ import org.springframework.web.bind.annotation.*
class StockAdjustmentController( class StockAdjustmentController(
private val stockAdjustmentService: StockAdjustmentService private val stockAdjustmentService: StockAdjustmentService
) { ) {
/** FP-MTMS Version Checklist | Functions Ref. No. 3 | v1.0.7 | 2026-09-08 */
@PostMapping("/submit") @PostMapping("/submit")
fun submit(@Valid @RequestBody request: StockAdjustmentRequest): MessageResponse { fun submit(@Valid @RequestBody request: StockAdjustmentRequest): MessageResponse {
return stockAdjustmentService.submit(request) return stockAdjustmentService.submit(request)
} }

/** FP-MTMS Version Checklist | Functions Ref. No. 3 | v1.0.7 | 2026-09-08 */
@GetMapping("/latestRemarks")
fun latestRemarks(@RequestParam itemId: Long): List<StockAdjustmentRemarksResponse> {
return stockAdjustmentService.findLatestRemarksByItemId(itemId)
}
} }

+ 7
- 1
src/main/java/com/ffii/fpsms/modules/stock/web/model/StockAdjustmentRequest.kt Voir le fichier

@@ -14,11 +14,17 @@ data class StockAdjustmentLineRequest(
val itemNo: String, val itemNo: String,
val expiryDate: String, val expiryDate: String,
val warehouseId: Long, val warehouseId: Long,
val uom: String? = null
val uom: String? = null,
val remarks: String? = null
) )


data class StockAdjustmentRequest( data class StockAdjustmentRequest(
val itemId: Long, val itemId: Long,
val originalLines: List<StockAdjustmentLineRequest>, val originalLines: List<StockAdjustmentLineRequest>,
val currentLines: List<StockAdjustmentLineRequest> val currentLines: List<StockAdjustmentLineRequest>
)

data class StockAdjustmentRemarksResponse(
val lotNo: String?,
val remarks: String
) )

+ 6
- 0
src/main/resources/db/changelog/changes/20260908_stock_adjustment_record_remarks/01_add_remarks.sql Voir le fichier

@@ -0,0 +1,6 @@
--liquibase formatted sql
--changeset fpsms:stock_adjustment_record_remarks
--comment: Store stock adjustment reason/remarks

ALTER TABLE `stock_adjustment_record`
ADD COLUMN `remarks` VARCHAR(500) NULL AFTER `outQty`;

Chargement…
Annuler
Enregistrer