| @@ -25,6 +25,9 @@ import org.springframework.stereotype.Service | |||||
| import java.math.BigDecimal | import java.math.BigDecimal | ||||
| import kotlin.jvm.optionals.getOrNull | import kotlin.jvm.optionals.getOrNull | ||||
| import com.ffii.fpsms.modules.master.web.models.ExcelWarehouseData | import com.ffii.fpsms.modules.master.web.models.ExcelWarehouseData | ||||
| import com.ffii.core.exception.BadRequestException | |||||
| import com.ffii.fpsms.modules.master.web.models.StockTakeSectionInfo | |||||
| import com.ffii.fpsms.modules.master.web.models.UpdateSectionDescriptionRequest | |||||
| @Service | @Service | ||||
| open class WarehouseService( | open class WarehouseService( | ||||
| private val jdbcDao: JdbcDao, | private val jdbcDao: JdbcDao, | ||||
| @@ -58,7 +61,18 @@ open class WarehouseService( | |||||
| open fun saveWarehouse(request: SaveWarehouseRequest): Warehouse { | open fun saveWarehouse(request: SaveWarehouseRequest): Warehouse { | ||||
| val warehouse = request.id?.let { warehouseRepository.findById(it).getOrNull() } ?: Warehouse(); | val warehouse = request.id?.let { warehouseRepository.findById(it).getOrNull() } ?: Warehouse(); | ||||
| val section = request.stockTakeSection?.takeIf { it.isNotBlank() } | |||||
| if (section != null) { | |||||
| val existingList = warehouseRepository.findAllByStockTakeSectionAndDeletedIsFalse(section) | |||||
| .filter { it.id != request.id } // exclude current record when edit | |||||
| if (existingList.isNotEmpty()) { | |||||
| val existingDesc = existingList.mapNotNull { it.stockTakeSectionDescription }.firstOrNull()?.trim() ?: "" | |||||
| val inputDesc = request.stockTakeSectionDescription?.trim() ?: "" | |||||
| if (existingDesc != inputDesc) { | |||||
| throw BadRequestException("stockTakeSectionDescription not same") | |||||
| } | |||||
| } | |||||
| } | |||||
| warehouse.apply { | warehouse.apply { | ||||
| // Generate code, name, description if not provided but store_id, warehouse, area, slot are provided | // Generate code, name, description if not provided but store_id, warehouse, area, slot are provided | ||||
| if (request.code == null && request.store_id != null && request.warehouse != null && request.area != null && request.slot != null) { | if (request.code == null && request.store_id != null && request.warehouse != null && request.area != null && request.slot != null) { | ||||
| @@ -100,6 +114,7 @@ open class WarehouseService( | |||||
| if (request.area != null) area = request.area | if (request.area != null) area = request.area | ||||
| if (request.slot != null) slot = request.slot | if (request.slot != null) slot = request.slot | ||||
| if (request.stockTakeSection != null) stockTakeSection = request.stockTakeSection | if (request.stockTakeSection != null) stockTakeSection = request.stockTakeSection | ||||
| if (request.stockTakeSectionDescription != null) stockTakeSectionDescription = request.stockTakeSectionDescription | |||||
| }; | }; | ||||
| return warehouseRepository.save(warehouse); | return warehouseRepository.save(warehouse); | ||||
| @@ -390,4 +405,29 @@ open class WarehouseService( | |||||
| // calculate total sort value: floor * 10000 + letter * 100 + slot | // calculate total sort value: floor * 10000 + letter * 100 + slot | ||||
| return floorOrder * 10000L + letterOrder * 100L + slot | return floorOrder * 10000L + letterOrder * 100L + slot | ||||
| } | } | ||||
| open fun getStockTakeSections(): List<StockTakeSectionInfo> { | |||||
| val all = warehouseRepository.findAllByDeletedIsFalse() | |||||
| .filter { it.stockTakeSection != null && it.stockTakeSection!!.isNotBlank() } | |||||
| val grouped = all.groupBy { it.stockTakeSection!! } | |||||
| return grouped.map { (section, list) -> | |||||
| val desc = list.mapNotNull { it.stockTakeSectionDescription }.firstOrNull()?.trim() | |||||
| StockTakeSectionInfo(section, desc, list.size.toLong()) | |||||
| }.sortedBy { it.stockTakeSection } | |||||
| } | |||||
| open fun updateSectionDescription(section: String, newDescription: String?) { | |||||
| val list = warehouseRepository.findAllByStockTakeSectionAndDeletedIsFalse(section) | |||||
| list.forEach { w -> | |||||
| w.stockTakeSectionDescription = newDescription?.takeIf { it.isNotBlank() } | |||||
| warehouseRepository.save(w) | |||||
| } | |||||
| } | |||||
| open fun clearWarehouseSection(warehouseId: Long) { | |||||
| val w = warehouseRepository.findById(warehouseId).orElseThrow() | |||||
| w.stockTakeSection = null | |||||
| w.stockTakeSectionDescription = null | |||||
| warehouseRepository.save(w) | |||||
| } | |||||
| } | } | ||||
| @@ -25,7 +25,8 @@ import org.springframework.web.multipart.MultipartHttpServletRequest | |||||
| import net.sf.jasperreports.engine.JasperExportManager | import net.sf.jasperreports.engine.JasperExportManager | ||||
| import net.sf.jasperreports.engine.JasperPrint | import net.sf.jasperreports.engine.JasperPrint | ||||
| import java.io.OutputStream | import java.io.OutputStream | ||||
| import com.ffii.fpsms.modules.master.web.models.StockTakeSectionInfo | |||||
| import com.ffii.fpsms.modules.master.web.models.UpdateSectionDescriptionRequest | |||||
| @RestController | @RestController | ||||
| @RequestMapping("/warehouse") | @RequestMapping("/warehouse") | ||||
| class WarehouseController( | class WarehouseController( | ||||
| @@ -97,4 +98,24 @@ class WarehouseController( | |||||
| out.write(JasperExportManager.exportReportToPdf(jasperPrint)) | out.write(JasperExportManager.exportReportToPdf(jasperPrint)) | ||||
| out.flush() | out.flush() | ||||
| } | } | ||||
| @GetMapping("/stockTakeSections") | |||||
| fun getStockTakeSections(): List<StockTakeSectionInfo> { | |||||
| return warehouseService.getStockTakeSections() | |||||
| } | |||||
| // PATCH /warehouse/section/{section}/description (body: { "stockTakeSectionDescription": "demo1" }) | |||||
| @org.springframework.web.bind.annotation.PatchMapping("/section/{section}/description") | |||||
| fun updateSectionDescription( | |||||
| @PathVariable section: String, | |||||
| @RequestBody body: UpdateSectionDescriptionRequest | |||||
| ): Unit { | |||||
| warehouseService.updateSectionDescription(section, body.stockTakeSectionDescription) | |||||
| } | |||||
| // POST /warehouse/{id}/clearSection | |||||
| @PostMapping("/{id}/clearSection") | |||||
| fun clearWarehouseSection(@PathVariable id: Long): Warehouse { | |||||
| warehouseService.clearWarehouseSection(id) | |||||
| return warehouseService.findById(id)!! | |||||
| } | |||||
| } | } | ||||
| @@ -14,6 +14,7 @@ data class SaveWarehouseRequest( | |||||
| val slot: String? = null, | val slot: String? = null, | ||||
| val order: String? = null, | val order: String? = null, | ||||
| val stockTakeSection: String? = null, | val stockTakeSection: String? = null, | ||||
| val stockTakeSectionDescription: String? = null, | |||||
| ) | ) | ||||
| data class NewWarehouseRequest( | data class NewWarehouseRequest( | ||||
| @@ -37,3 +38,13 @@ data class NewWarehouseRequest( | |||||
| val slot: String, | val slot: String, | ||||
| val rowIndex: String | val rowIndex: String | ||||
| ) | ) | ||||
| data class StockTakeSectionInfo( | |||||
| val stockTakeSection: String, | |||||
| val stockTakeSectionDescription: String?, | |||||
| val warehouseCount: Long | |||||
| ) | |||||
| data class UpdateSectionDescriptionRequest( | |||||
| val stockTakeSectionDescription: String? | |||||
| ) | |||||
| @@ -1376,7 +1376,7 @@ open class ProductProcessService( | |||||
| ) | ) | ||||
| } | } | ||||
| open fun getAllJoborderProductProcessInfo(): List<AllJoborderProductProcessInfoResponse> { | |||||
| open fun getAllJoborderProductProcessInfo(isDrink: Boolean?): List<AllJoborderProductProcessInfoResponse> { | |||||
| val productProcesses = productProcessRepository.findAllByDeletedIsFalse() | val productProcesses = productProcessRepository.findAllByDeletedIsFalse() | ||||
| val productProcessIds = productProcesses.map { it.id } | val productProcessIds = productProcesses.map { it.id } | ||||
| @@ -1397,12 +1397,14 @@ open class ProductProcessService( | |||||
| val timeNeedToComplete = bomProcessRepository.findByBomId(productProcesses.bom?.id ?: 0L) | val timeNeedToComplete = bomProcessRepository.findByBomId(productProcesses.bom?.id ?: 0L) | ||||
| .filter { !it.deleted } | .filter { !it.deleted } | ||||
| .sumOf { it.durationInMinute ?: 0 } | .sumOf { it.durationInMinute ?: 0 } | ||||
| val bomIsDrink = productProcesses.bom?.isDrink | |||||
| AllJoborderProductProcessInfoResponse( | AllJoborderProductProcessInfoResponse( | ||||
| id = productProcesses.id ?: 0L, | id = productProcesses.id ?: 0L, | ||||
| productProcessCode = productProcesses.productProcessCode, | productProcessCode = productProcesses.productProcessCode, | ||||
| status = productProcesses.status.value, | status = productProcesses.status.value, | ||||
| startTime = productProcesses.startTime, | startTime = productProcesses.startTime, | ||||
| endTime = productProcesses.endTime, | endTime = productProcesses.endTime, | ||||
| isDrink = bomIsDrink, | |||||
| matchStatus = if (joPickOrdersList.isNotEmpty() && | matchStatus = if (joPickOrdersList.isNotEmpty() && | ||||
| joPickOrdersList.all { it.matchStatus == JoPickOrderStatus.completed } | joPickOrdersList.all { it.matchStatus == JoPickOrderStatus.completed } | ||||
| ) { | ) { | ||||
| @@ -1445,6 +1447,9 @@ open class ProductProcessService( | |||||
| ) | ) | ||||
| }.filter { response -> | }.filter { response -> | ||||
| // 过滤掉已完成上架的 job order | // 过滤掉已完成上架的 job order | ||||
| if (isDrink != null && response.isDrink != isDrink) { | |||||
| return@filter false | |||||
| } | |||||
| val jobOrder = jobOrderRepository.findById(response.jobOrderId ?: 0L).orElse(null) | val jobOrder = jobOrderRepository.findById(response.jobOrderId ?: 0L).orElse(null) | ||||
| val stockInLineStatus = jobOrder?.stockInLines?.firstOrNull()?.status | val stockInLineStatus = jobOrder?.stockInLines?.firstOrNull()?.status | ||||
| stockInLineStatus != "completed" | stockInLineStatus != "completed" | ||||
| @@ -193,8 +193,10 @@ class ProductProcessController( | |||||
| } | } | ||||
| @GetMapping("/Demo/Process/all") | @GetMapping("/Demo/Process/all") | ||||
| fun demoprocessall(): List<AllJoborderProductProcessInfoResponse> { | |||||
| return productProcessService.getAllJoborderProductProcessInfo() | |||||
| fun demoprocessall( | |||||
| @RequestParam(required = false) isDrink: Boolean? | |||||
| ): List<AllJoborderProductProcessInfoResponse> { | |||||
| return productProcessService.getAllJoborderProductProcessInfo(isDrink) | |||||
| } | } | ||||
| @PostMapping("/Demo/ProcessLine/start/{lineId}") | @PostMapping("/Demo/ProcessLine/start/{lineId}") | ||||
| fun startProductProcessLine(@PathVariable lineId: Long): MessageResponse { | fun startProductProcessLine(@PathVariable lineId: Long): MessageResponse { | ||||
| @@ -180,6 +180,7 @@ data class AllJoborderProductProcessInfoResponse( | |||||
| val FinishedProductProcessLineCount: Int, | val FinishedProductProcessLineCount: Int, | ||||
| val stockInLineId: Long?, | val stockInLineId: Long?, | ||||
| val TimeNeedToComplete: Int?, | val TimeNeedToComplete: Int?, | ||||
| val isDrink: Boolean?, | |||||
| val lines: List<ProductProcessInfoResponse> | val lines: List<ProductProcessInfoResponse> | ||||
| ) | ) | ||||
| data class ProductProcessInfoResponse( | data class ProductProcessInfoResponse( | ||||
| @@ -25,6 +25,7 @@ data class AllPickedStockTakeListReponse( | |||||
| val ReStockTakeTrueFalse: Boolean, | val ReStockTakeTrueFalse: Boolean, | ||||
| @JsonFormat(pattern = "yyyy-MM-dd") | @JsonFormat(pattern = "yyyy-MM-dd") | ||||
| val planStartDate: LocalDate?, | val planStartDate: LocalDate?, | ||||
| val stockTakeSectionDescription: String?, | |||||
| ) | ) | ||||
| data class InventoryLotDetailResponse( | data class InventoryLotDetailResponse( | ||||
| val id: Long, | val id: Long, | ||||
| @@ -99,6 +100,12 @@ data class BatchSaveApproverStockTakeRecordRequest( | |||||
| val variancePercentTolerance: BigDecimal? = null, | val variancePercentTolerance: BigDecimal? = null, | ||||
| ) | ) | ||||
| data class BatchSaveApproverStockTakeAllRequest( | |||||
| val stockTakeId: Long, | |||||
| val approverId: Long, | |||||
| val variancePercentTolerance: BigDecimal? = null, | |||||
| ) | |||||
| data class BatchSaveApproverStockTakeRecordResponse( | data class BatchSaveApproverStockTakeRecordResponse( | ||||
| val successCount: Int, | val successCount: Int, | ||||
| val errorCount: Int, | val errorCount: Int, | ||||