| @@ -25,6 +25,9 @@ import org.springframework.stereotype.Service | |||
| import java.math.BigDecimal | |||
| import kotlin.jvm.optionals.getOrNull | |||
| 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 | |||
| open class WarehouseService( | |||
| private val jdbcDao: JdbcDao, | |||
| @@ -58,7 +61,18 @@ open class WarehouseService( | |||
| open fun saveWarehouse(request: SaveWarehouseRequest): 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 { | |||
| // 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) { | |||
| @@ -100,6 +114,7 @@ open class WarehouseService( | |||
| if (request.area != null) area = request.area | |||
| if (request.slot != null) slot = request.slot | |||
| if (request.stockTakeSection != null) stockTakeSection = request.stockTakeSection | |||
| if (request.stockTakeSectionDescription != null) stockTakeSectionDescription = request.stockTakeSectionDescription | |||
| }; | |||
| return warehouseRepository.save(warehouse); | |||
| @@ -390,4 +405,29 @@ open class WarehouseService( | |||
| // calculate total sort value: floor * 10000 + letter * 100 + 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.JasperPrint | |||
| import java.io.OutputStream | |||
| import com.ffii.fpsms.modules.master.web.models.StockTakeSectionInfo | |||
| import com.ffii.fpsms.modules.master.web.models.UpdateSectionDescriptionRequest | |||
| @RestController | |||
| @RequestMapping("/warehouse") | |||
| class WarehouseController( | |||
| @@ -97,4 +98,24 @@ class WarehouseController( | |||
| out.write(JasperExportManager.exportReportToPdf(jasperPrint)) | |||
| 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 order: String? = null, | |||
| val stockTakeSection: String? = null, | |||
| val stockTakeSectionDescription: String? = null, | |||
| ) | |||
| data class NewWarehouseRequest( | |||
| @@ -37,3 +38,13 @@ data class NewWarehouseRequest( | |||
| val slot: 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 productProcessIds = productProcesses.map { it.id } | |||
| @@ -1397,12 +1397,14 @@ open class ProductProcessService( | |||
| val timeNeedToComplete = bomProcessRepository.findByBomId(productProcesses.bom?.id ?: 0L) | |||
| .filter { !it.deleted } | |||
| .sumOf { it.durationInMinute ?: 0 } | |||
| val bomIsDrink = productProcesses.bom?.isDrink | |||
| AllJoborderProductProcessInfoResponse( | |||
| id = productProcesses.id ?: 0L, | |||
| productProcessCode = productProcesses.productProcessCode, | |||
| status = productProcesses.status.value, | |||
| startTime = productProcesses.startTime, | |||
| endTime = productProcesses.endTime, | |||
| isDrink = bomIsDrink, | |||
| matchStatus = if (joPickOrdersList.isNotEmpty() && | |||
| joPickOrdersList.all { it.matchStatus == JoPickOrderStatus.completed } | |||
| ) { | |||
| @@ -1445,6 +1447,9 @@ open class ProductProcessService( | |||
| ) | |||
| }.filter { response -> | |||
| // 过滤掉已完成上架的 job order | |||
| if (isDrink != null && response.isDrink != isDrink) { | |||
| return@filter false | |||
| } | |||
| val jobOrder = jobOrderRepository.findById(response.jobOrderId ?: 0L).orElse(null) | |||
| val stockInLineStatus = jobOrder?.stockInLines?.firstOrNull()?.status | |||
| stockInLineStatus != "completed" | |||
| @@ -193,8 +193,10 @@ class ProductProcessController( | |||
| } | |||
| @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}") | |||
| fun startProductProcessLine(@PathVariable lineId: Long): MessageResponse { | |||
| @@ -180,6 +180,7 @@ data class AllJoborderProductProcessInfoResponse( | |||
| val FinishedProductProcessLineCount: Int, | |||
| val stockInLineId: Long?, | |||
| val TimeNeedToComplete: Int?, | |||
| val isDrink: Boolean?, | |||
| val lines: List<ProductProcessInfoResponse> | |||
| ) | |||
| data class ProductProcessInfoResponse( | |||
| @@ -25,6 +25,7 @@ data class AllPickedStockTakeListReponse( | |||
| val ReStockTakeTrueFalse: Boolean, | |||
| @JsonFormat(pattern = "yyyy-MM-dd") | |||
| val planStartDate: LocalDate?, | |||
| val stockTakeSectionDescription: String?, | |||
| ) | |||
| data class InventoryLotDetailResponse( | |||
| val id: Long, | |||
| @@ -99,6 +100,12 @@ data class BatchSaveApproverStockTakeRecordRequest( | |||
| val variancePercentTolerance: BigDecimal? = null, | |||
| ) | |||
| data class BatchSaveApproverStockTakeAllRequest( | |||
| val stockTakeId: Long, | |||
| val approverId: Long, | |||
| val variancePercentTolerance: BigDecimal? = null, | |||
| ) | |||
| data class BatchSaveApproverStockTakeRecordResponse( | |||
| val successCount: Int, | |||
| val errorCount: Int, | |||