| @@ -173,7 +173,8 @@ open class M18MasterDataService( | |||||
| LocationCode = null, | LocationCode = null, | ||||
| isEgg = null, | isEgg = null, | ||||
| isFee = null, | isFee = null, | ||||
| isBag = null | |||||
| isBag = null, | |||||
| qcType = null | |||||
| ) | ) | ||||
| val savedItem = itemsService.saveItem(saveItemRequest) | val savedItem = itemsService.saveItem(saveItemRequest) | ||||
| @@ -276,7 +277,8 @@ open class M18MasterDataService( | |||||
| LocationCode = null, | LocationCode = null, | ||||
| isEgg = null, | isEgg = null, | ||||
| isFee = null, | isFee = null, | ||||
| isBag = null | |||||
| isBag = null, | |||||
| qcType = null | |||||
| ) | ) | ||||
| val savedItem = itemsService.saveItem(saveItemRequest) | val savedItem = itemsService.saveItem(saveItemRequest) | ||||
| @@ -240,4 +240,32 @@ open fun getBagConsumptions(bagLotLineId: Long): List<BagConsumptionResponse> { | |||||
| ) | ) | ||||
| } | } | ||||
| } | } | ||||
| open fun softDeleteBagByItemId(itemId: Long): MessageResponse { | |||||
| val bag = bagRepository.findByItemIdAndDeletedIsFalse(itemId) | |||||
| if (bag == null) { | |||||
| return MessageResponse( | |||||
| id = null, | |||||
| code = null, | |||||
| name = null, | |||||
| type = null, | |||||
| message = "No bag found for itemId: $itemId", | |||||
| errorPosition = null, | |||||
| entity = null, | |||||
| ) | |||||
| } | |||||
| bag.deleted = true | |||||
| bagRepository.save(bag) | |||||
| return MessageResponse( | |||||
| id = bag.id, | |||||
| code = null, | |||||
| name = null, | |||||
| type = null, | |||||
| message = "Bag soft-deleted successfully", | |||||
| errorPosition = null, | |||||
| entity = null, | |||||
| ) | |||||
| } | |||||
| } | } | ||||
| @@ -62,4 +62,8 @@ class BagController( | |||||
| @GetMapping("/lot-lines/{bagLotLineId}/consumptions") | @GetMapping("/lot-lines/{bagLotLineId}/consumptions") | ||||
| fun getBagConsumptions(@PathVariable bagLotLineId: Long): List<BagConsumptionResponse> = | fun getBagConsumptions(@PathVariable bagLotLineId: Long): List<BagConsumptionResponse> = | ||||
| bagService.getBagConsumptions(bagLotLineId) | bagService.getBagConsumptions(bagLotLineId) | ||||
| @PutMapping("/by-item/{itemId}/soft-delete") | |||||
| fun softDeleteBagByItemId(@PathVariable itemId: Long): MessageResponse = | |||||
| bagService.softDeleteBagByItemId(itemId) | |||||
| } | } | ||||
| @@ -754,21 +754,19 @@ open class DoPickOrderService( | |||||
| * Get truck schedule dashboard data aggregated by store, truck lane, and departure time. | * Get truck schedule dashboard data aggregated by store, truck lane, and departure time. | ||||
| * Groups DoPickOrder and DoPickOrderRecord data to provide summary statistics. | * Groups DoPickOrder and DoPickOrderRecord data to provide summary statistics. | ||||
| */ | */ | ||||
| open fun getTruckScheduleDashboard(): List<TruckScheduleDashboardResponse> { | |||||
| val today = LocalDate.now() | |||||
| // Fetch all active DoPickOrders for today | |||||
| open fun getTruckScheduleDashboard(targetDate: LocalDate): List<TruckScheduleDashboardResponse> { | |||||
| // Fetch all active DoPickOrders for the target date | |||||
| val doPickOrders = doPickOrderRepository.findByStoreIdAndRequiredDeliveryDateAndTicketStatusIn( | val doPickOrders = doPickOrderRepository.findByStoreIdAndRequiredDeliveryDateAndTicketStatusIn( | ||||
| "2/F", today, listOf(DoPickOrderStatus.pending, DoPickOrderStatus.released, DoPickOrderStatus.completed) | |||||
| "2/F", targetDate, listOf(DoPickOrderStatus.pending, DoPickOrderStatus.released, DoPickOrderStatus.completed) | |||||
| ) + doPickOrderRepository.findByStoreIdAndRequiredDeliveryDateAndTicketStatusIn( | ) + doPickOrderRepository.findByStoreIdAndRequiredDeliveryDateAndTicketStatusIn( | ||||
| "4/F", today, listOf(DoPickOrderStatus.pending, DoPickOrderStatus.released, DoPickOrderStatus.completed) | |||||
| "4/F", targetDate, listOf(DoPickOrderStatus.pending, DoPickOrderStatus.released, DoPickOrderStatus.completed) | |||||
| ) | ) | ||||
| // Fetch all DoPickOrderRecords for today (completed records) | |||||
| // Fetch all DoPickOrderRecords for the target date (completed records) | |||||
| val doPickOrderRecords = doPickOrderRecordRepository.findByStoreIdAndRequiredDeliveryDateAndTicketStatusIn( | val doPickOrderRecords = doPickOrderRecordRepository.findByStoreIdAndRequiredDeliveryDateAndTicketStatusIn( | ||||
| "2/F", today, listOf(DoPickOrderStatus.completed) | |||||
| "2/F", targetDate, listOf(DoPickOrderStatus.completed) | |||||
| ) + doPickOrderRecordRepository.findByStoreIdAndRequiredDeliveryDateAndTicketStatusIn( | ) + doPickOrderRecordRepository.findByStoreIdAndRequiredDeliveryDateAndTicketStatusIn( | ||||
| "4/F", today, listOf(DoPickOrderStatus.completed) | |||||
| "4/F", targetDate, listOf(DoPickOrderStatus.completed) | |||||
| ) | ) | ||||
| // Combine both types into a unified data structure for aggregation | // Combine both types into a unified data structure for aggregation | ||||
| @@ -105,8 +105,10 @@ class DoPickOrderController( | |||||
| } | } | ||||
| @GetMapping("/truck-schedule-dashboard") | @GetMapping("/truck-schedule-dashboard") | ||||
| fun getTruckScheduleDashboard(): List<TruckScheduleDashboardResponse> { | |||||
| return doPickOrderService.getTruckScheduleDashboard() | |||||
| fun getTruckScheduleDashboard( | |||||
| @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) date: LocalDate? | |||||
| ): List<TruckScheduleDashboardResponse> { | |||||
| return doPickOrderService.getTruckScheduleDashboard(date ?: LocalDate.now()) | |||||
| } | } | ||||
| } | } | ||||
| @@ -22,6 +22,10 @@ interface ItemsQcCategoryMappingRepository : AbstractRepository<ItemsQcCategoryM | |||||
| """ | """ | ||||
| ) | ) | ||||
| fun findMappings(qcCategoryId: Long?, itemId: Long?): List<ItemsQcCategoryMapping> | fun findMappings(qcCategoryId: Long?, itemId: Long?): List<ItemsQcCategoryMapping> | ||||
| fun findByItemIdAndType(itemId: Long, type: String): ItemsQcCategoryMapping? | |||||
| fun findByItemId(itemId: Long): List<ItemsQcCategoryMapping> | |||||
| fun findFirstByItemId(itemId: Long): ItemsQcCategoryMapping? | |||||
| } | } | ||||
| @@ -66,6 +66,7 @@ open class ItemsService( | |||||
| private val inventoryLotLineRepository: InventoryLotLineRepository, | private val inventoryLotLineRepository: InventoryLotLineRepository, | ||||
| private val inventoryLotRepository: InventoryLotRepository, | private val inventoryLotRepository: InventoryLotRepository, | ||||
| private val bagRepository: BagRepository, | private val bagRepository: BagRepository, | ||||
| private val itemsQcCategoryMappingRepository: ItemsQcCategoryMappingRepository, | |||||
| ): AbstractBaseEntityService<Items, Long, ItemsRepository>(jdbcDao, itemsRepository) { | ): AbstractBaseEntityService<Items, Long, ItemsRepository>(jdbcDao, itemsRepository) { | ||||
| private val excelImportPath: String = System.getProperty("user.home") + "/Downloads/StockTakeImport/" | private val excelImportPath: String = System.getProperty("user.home") + "/Downloads/StockTakeImport/" | ||||
| @@ -555,6 +556,20 @@ open class ItemsService( | |||||
| } | } | ||||
| } | } | ||||
| // Save QC category mapping if qcType and qcCategoryId are provided | |||||
| if (request.qcType != null && request.qcCategoryId != null && savedItem.id != null) { | |||||
| // Find existing mapping by itemId only (update existing record for this item) | |||||
| val existingMapping = itemsQcCategoryMappingRepository.findFirstByItemId(savedItem.id!!) | |||||
| val mapping = existingMapping ?: ItemsQcCategoryMapping() | |||||
| mapping.apply { | |||||
| itemId = savedItem.id | |||||
| qcCategoryId = request.qcCategoryId | |||||
| type = request.qcType | |||||
| } | |||||
| itemsQcCategoryMappingRepository.save(mapping) | |||||
| logger.info("Saved QC category mapping for item: ${savedItem.code} (ID: ${savedItem.id}), type: ${request.qcType}") | |||||
| } | |||||
| return MessageResponse( | return MessageResponse( | ||||
| id = savedItem.id, | id = savedItem.id, | ||||
| name = savedItem.name, | name = savedItem.name, | ||||
| @@ -4,10 +4,12 @@ import com.ffii.core.support.AbstractBaseEntityService | |||||
| import com.ffii.fpsms.modules.master.entity.QcCategory | import com.ffii.fpsms.modules.master.entity.QcCategory | ||||
| import com.ffii.fpsms.modules.master.entity.QcCategoryRepository | import com.ffii.fpsms.modules.master.entity.QcCategoryRepository | ||||
| import com.ffii.fpsms.modules.master.entity.QcItem | import com.ffii.fpsms.modules.master.entity.QcItem | ||||
| import com.ffii.fpsms.modules.master.entity.QcItemCategoryRepository | |||||
| import com.ffii.fpsms.modules.master.entity.projections.QcCategoryCombo | import com.ffii.fpsms.modules.master.entity.projections.QcCategoryCombo | ||||
| import com.ffii.fpsms.modules.master.web.models.SaveQcCategoryRequest | import com.ffii.fpsms.modules.master.web.models.SaveQcCategoryRequest | ||||
| import com.ffii.fpsms.modules.master.web.models.SaveQcCategoryResponse | import com.ffii.fpsms.modules.master.web.models.SaveQcCategoryResponse | ||||
| import com.ffii.fpsms.modules.qc.entity.projection.QcCategoryInfo | import com.ffii.fpsms.modules.qc.entity.projection.QcCategoryInfo | ||||
| import com.ffii.fpsms.modules.qc.entity.projection.QcItemInfo | |||||
| import jakarta.validation.Valid | import jakarta.validation.Valid | ||||
| import org.springframework.stereotype.Service | import org.springframework.stereotype.Service | ||||
| import org.springframework.web.bind.annotation.RequestBody | import org.springframework.web.bind.annotation.RequestBody | ||||
| @@ -15,6 +17,7 @@ import org.springframework.web.bind.annotation.RequestBody | |||||
| @Service | @Service | ||||
| open class QcCategoryService( | open class QcCategoryService( | ||||
| private val qcCategoryRepository: QcCategoryRepository, | private val qcCategoryRepository: QcCategoryRepository, | ||||
| private val qcItemCategoryRepository: QcItemCategoryRepository, | |||||
| private val itemsService: ItemsService | private val itemsService: ItemsService | ||||
| ) { | ) { | ||||
| open fun allQcCategories(): List<QcCategory> { | open fun allQcCategories(): List<QcCategory> { | ||||
| @@ -29,6 +32,10 @@ open class QcCategoryService( | |||||
| return qcCategoryRepository.findQcCategoryComboByDeletedIsFalse(); | return qcCategoryRepository.findQcCategoryComboByDeletedIsFalse(); | ||||
| } | } | ||||
| open fun getQcItemsByCategoryId(categoryId: Long): List<QcItemInfo> { | |||||
| return qcItemCategoryRepository.findAllByQcCategoryId(categoryId) | |||||
| } | |||||
| open fun getQcCategoryInfoByMapping(itemId : Long, type: String): QcCategoryInfo? { | open fun getQcCategoryInfoByMapping(itemId : Long, type: String): QcCategoryInfo? { | ||||
| var result = qcCategoryRepository.findQcCategoryInfoByItemIdAndType(itemId, type) | var result = qcCategoryRepository.findQcCategoryInfoByItemIdAndType(itemId, type) | ||||
| if (result == null) { // Temporarily fix for missing QC template (Auto find template from similar items) | if (result == null) { // Temporarily fix for missing QC template (Auto find template from similar items) | ||||
| @@ -8,6 +8,7 @@ import com.ffii.fpsms.modules.master.service.QcCategoryService | |||||
| import com.ffii.fpsms.modules.master.web.models.SaveQcCategoryRequest | import com.ffii.fpsms.modules.master.web.models.SaveQcCategoryRequest | ||||
| import com.ffii.fpsms.modules.master.web.models.SaveQcCategoryResponse | import com.ffii.fpsms.modules.master.web.models.SaveQcCategoryResponse | ||||
| import com.ffii.fpsms.modules.qc.entity.projection.QcCategoryInfo | import com.ffii.fpsms.modules.qc.entity.projection.QcCategoryInfo | ||||
| import com.ffii.fpsms.modules.qc.entity.projection.QcItemInfo | |||||
| import jakarta.validation.Valid | import jakarta.validation.Valid | ||||
| import org.springframework.web.bind.annotation.* | import org.springframework.web.bind.annotation.* | ||||
| @@ -41,6 +42,11 @@ class QcCategoryController( | |||||
| return qcCategoryService.getQcCategoryCombo(); | return qcCategoryService.getQcCategoryCombo(); | ||||
| } | } | ||||
| @GetMapping("/{id}/items") | |||||
| fun getQcItemsByCategoryId(@PathVariable id: Long): List<QcItemInfo> { | |||||
| return qcCategoryService.getQcItemsByCategoryId(id) | |||||
| } | |||||
| @GetMapping("/items") | @GetMapping("/items") | ||||
| fun getQcCategoryByTemplate(@RequestParam itemId: Long, @RequestParam(defaultValue = "IQC") type: String): QcCategoryInfo { | fun getQcCategoryByTemplate(@RequestParam itemId: Long, @RequestParam(defaultValue = "IQC") type: String): QcCategoryInfo { | ||||
| return qcCategoryService.getQcCategoryInfoByMapping(itemId, type) ?: throw NotFoundException() | return qcCategoryService.getQcCategoryInfoByMapping(itemId, type) ?: throw NotFoundException() | ||||
| @@ -53,6 +53,7 @@ data class NewItemRequest( | |||||
| val isEgg: Boolean?, | val isEgg: Boolean?, | ||||
| val isFee: Boolean?, | val isFee: Boolean?, | ||||
| val isBag: Boolean?, | val isBag: Boolean?, | ||||
| val qcType: String?, | |||||
| // val type: List<NewTypeRequest>?, | // val type: List<NewTypeRequest>?, | ||||
| // val uom: List<NewUomRequest>?, | // val uom: List<NewUomRequest>?, | ||||
| // val weightUnit: List<NewWeightUnitRequest>?, | // val weightUnit: List<NewWeightUnitRequest>?, | ||||
| @@ -21,6 +21,6 @@ interface QcItemInfo { | |||||
| val code: String | val code: String | ||||
| @get:Value("#{target.qcItem.name}") | @get:Value("#{target.qcItem.name}") | ||||
| val name: String? | val name: String? | ||||
| @get:Value("#{target.qcItem.description}") | |||||
| // Use description from qc_item_category table (category-specific description) | |||||
| val description: String? | val description: String? | ||||
| } | } | ||||
| @@ -43,6 +43,36 @@ open class ReportService( | |||||
| return jdbcDao.queryForList(sql, args) | return jdbcDao.queryForList(sql, args) | ||||
| } | } | ||||
| fun searchReport3( itemType: String?): List<Map<String, Any>> { | |||||
| val args = mutableMapOf<String, Any>( | |||||
| ) | |||||
| val itemTypeSql = if (!itemType.isNullOrBlank()) { | |||||
| args["itemType"] = "%$itemType%" | |||||
| "AND it.type LIKE :itemType" | |||||
| } else "" | |||||
| val sql = """ | |||||
| SELECT | |||||
| -- fake value for demo | |||||
| 8.8 * iv.onHandQty as stockValue, | |||||
| iv.onHandQty, | |||||
| it.code as itemCode, | |||||
| it.name as itemName, | |||||
| uc.code as uom, | |||||
| it.type, iv.status | |||||
| FROM inventory iv | |||||
| LEFT JOIN items it ON iv.itemId = it.id | |||||
| LEFT JOIN uom_conversion uc ON iv.uomId = uc.id | |||||
| WHERE iv.created >= :fromDate AND iv.created < :toDate | |||||
| $itemTypeSql | |||||
| """.trimIndent() | |||||
| return jdbcDao.queryForList(sql, args) | |||||
| } | |||||
| /** | /** | ||||
| * Compiles and fills a Jasper Report, returning the PDF as a ByteArray. | * Compiles and fills a Jasper Report, returning the PDF as a ByteArray. | ||||
| */ | */ | ||||
| @@ -47,23 +47,23 @@ class ReportController( | |||||
| @GetMapping("/print-report3") | @GetMapping("/print-report3") | ||||
| fun generateReport3( | fun generateReport3( | ||||
| @RequestParam fromDate: String, | |||||
| @RequestParam toDate: String, | |||||
| //@RequestParam fromDate: String, | |||||
| // @RequestParam toDate: String, | |||||
| //this is an exampe of optional param | //this is an exampe of optional param | ||||
| @RequestParam(required = false) itemType: String? | @RequestParam(required = false) itemType: String? | ||||
| ): ResponseEntity<ByteArray> { | ): ResponseEntity<ByteArray> { | ||||
| val parameters = mutableMapOf<String, Any>() | val parameters = mutableMapOf<String, Any>() | ||||
| parameters["fromDate"] = fromDate | |||||
| parameters["toDate"] = toDate | |||||
| //parameters["lastInDateStart"] = fromDate | |||||
| //parameters["lastInDateEnd"] = toDate | |||||
| // you may put more params to show | // you may put more params to show | ||||
| parameters["reportDate"] = "param1Value" | |||||
| parameters["param2"] = "param2Value" | |||||
| parameters["lastOutDateStart"] = "param1Value" | |||||
| parameters["lastOutDateEnd"] = "param2Value" | |||||
| // Query the DB to get a list of data | // Query the DB to get a list of data | ||||
| val dbData = reportService.searchReport1(fromDate, toDate, itemType) | |||||
| val dbData = reportService.searchReport3(itemType) | |||||
| val pdfBytes = reportService.createPdfResponse( | val pdfBytes = reportService.createPdfResponse( | ||||
| "/jasper/FGDeliveryReport.jrxml", | |||||
| "/jasper/StockBalanceReport.jrxml", | |||||
| parameters, | parameters, | ||||
| dbData | dbData | ||||
| ) | ) | ||||