|
|
@@ -0,0 +1,301 @@ |
|
|
|
|
|
package com.ffii.fpsms.modules.master.service |
|
|
|
|
|
|
|
|
|
|
|
import com.ffii.core.exception.NotFoundException |
|
|
|
|
|
import com.ffii.fpsms.modules.master.entity.* |
|
|
|
|
|
import com.ffii.fpsms.modules.master.web.models.* |
|
|
|
|
|
import com.ffii.fpsms.modules.qc.entity.projection.QcItemInfo |
|
|
|
|
|
import jakarta.validation.Valid |
|
|
|
|
|
import org.springframework.stereotype.Service |
|
|
|
|
|
import org.springframework.web.bind.annotation.RequestBody |
|
|
|
|
|
|
|
|
|
|
|
@Service |
|
|
|
|
|
open class QcItemAllService( |
|
|
|
|
|
private val qcCategoryRepository: QcCategoryRepository, |
|
|
|
|
|
private val qcItemRepository: QcItemRepository, |
|
|
|
|
|
private val qcItemCategoryRepository: QcItemCategoryRepository, |
|
|
|
|
|
private val itemsQcCategoryMappingRepository: ItemsQcCategoryMappingRepository, |
|
|
|
|
|
private val itemsRepository: ItemsRepository |
|
|
|
|
|
) { |
|
|
|
|
|
|
|
|
|
|
|
// Get item count by qc category |
|
|
|
|
|
open fun getItemCountByQcCategory(qcCategoryId: Long): Long { |
|
|
|
|
|
return itemsQcCategoryMappingRepository.countByQcCategoryId(qcCategoryId) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Get qc item count by qc category |
|
|
|
|
|
open fun getQcItemCountByQcCategory(qcCategoryId: Long): Long { |
|
|
|
|
|
return qcItemCategoryRepository.findAllByQcCategoryId(qcCategoryId).size.toLong() |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Get all qc categories with item counts (batch operation for performance) |
|
|
|
|
|
open fun getAllQcCategoriesWithItemCounts(): List<QcCategoryWithItemCount> { |
|
|
|
|
|
val categories = qcCategoryRepository.findAllByDeletedIsFalse() |
|
|
|
|
|
val allMappings = itemsQcCategoryMappingRepository.findAll() |
|
|
|
|
|
|
|
|
|
|
|
// Group mappings by qcCategoryId and count |
|
|
|
|
|
val countMap = allMappings |
|
|
|
|
|
.filter { it.qcCategoryId != null } |
|
|
|
|
|
.groupBy { it.qcCategoryId!! } |
|
|
|
|
|
.mapValues { it.value.size.toLong() } |
|
|
|
|
|
|
|
|
|
|
|
return categories.map { category -> |
|
|
|
|
|
QcCategoryWithItemCount( |
|
|
|
|
|
id = category.id!!, |
|
|
|
|
|
code = category.code, |
|
|
|
|
|
name = category.name, |
|
|
|
|
|
description = category.description, |
|
|
|
|
|
itemCount = countMap[category.id] ?: 0L |
|
|
|
|
|
) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Get all qc categories with qc item counts (batch operation for performance) |
|
|
|
|
|
open fun getAllQcCategoriesWithQcItemCounts(): List<QcCategoryWithQcItemCount> { |
|
|
|
|
|
val categories = qcCategoryRepository.findAllByDeletedIsFalse() |
|
|
|
|
|
val allMappings = qcItemCategoryRepository.findAll() |
|
|
|
|
|
|
|
|
|
|
|
// Group mappings by qcCategoryId and count |
|
|
|
|
|
val countMap = allMappings |
|
|
|
|
|
.filter { it.qcCategory?.id != null } |
|
|
|
|
|
.groupBy { it.qcCategory!!.id!! } |
|
|
|
|
|
.mapValues { it.value.size.toLong() } |
|
|
|
|
|
|
|
|
|
|
|
return categories.map { category -> |
|
|
|
|
|
QcCategoryWithQcItemCount( |
|
|
|
|
|
id = category.id!!, |
|
|
|
|
|
code = category.code, |
|
|
|
|
|
name = category.name, |
|
|
|
|
|
description = category.description, |
|
|
|
|
|
qcItemCount = countMap[category.id] ?: 0L |
|
|
|
|
|
) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Check if can delete qc category |
|
|
|
|
|
open fun canDeleteQcCategory(id: Long): Boolean { |
|
|
|
|
|
val itemCount = getItemCountByQcCategory(id) |
|
|
|
|
|
val qcItemCount = getQcItemCountByQcCategory(id) |
|
|
|
|
|
return itemCount == 0L && qcItemCount == 0L |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Check if can delete qc item |
|
|
|
|
|
open fun canDeleteQcItem(id: Long): Boolean { |
|
|
|
|
|
// Check if this qc item is linked to any qc category |
|
|
|
|
|
val count = qcItemCategoryRepository.countByQcItemId(id) |
|
|
|
|
|
return count == 0L |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Get item and qc category mappings |
|
|
|
|
|
open fun getItemQcCategoryMappings(qcCategoryId: Long?, itemId: Long?): List<ItemQcCategoryMappingInfo> { |
|
|
|
|
|
val mappings = itemsQcCategoryMappingRepository.findMappings(qcCategoryId, itemId) |
|
|
|
|
|
return mappings.map { mapping -> |
|
|
|
|
|
val item = if (mapping.itemId != null) itemsRepository.findById(mapping.itemId!!).orElse(null) else null |
|
|
|
|
|
val qcCategory = if (mapping.qcCategoryId != null) qcCategoryRepository.findById(mapping.qcCategoryId!!).orElse(null) else null |
|
|
|
|
|
|
|
|
|
|
|
ItemQcCategoryMappingInfo( |
|
|
|
|
|
id = mapping.id!!, |
|
|
|
|
|
itemId = mapping.itemId ?: 0L, |
|
|
|
|
|
itemCode = item?.code, |
|
|
|
|
|
itemName = item?.name, |
|
|
|
|
|
qcCategoryId = mapping.qcCategoryId ?: 0L, |
|
|
|
|
|
qcCategoryCode = qcCategory?.code, |
|
|
|
|
|
qcCategoryName = qcCategory?.name, |
|
|
|
|
|
type = mapping.type |
|
|
|
|
|
) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Save item and qc category mapping |
|
|
|
|
|
open fun saveItemQcCategoryMapping(itemId: Long, qcCategoryId: Long, type: String): ItemQcCategoryMappingInfo { |
|
|
|
|
|
// Check if mapping already exists |
|
|
|
|
|
val existing = itemsQcCategoryMappingRepository.findByItemIdAndQcCategoryIdAndType(itemId, qcCategoryId, type) |
|
|
|
|
|
val mapping = existing ?: ItemsQcCategoryMapping() |
|
|
|
|
|
|
|
|
|
|
|
mapping.itemId = itemId |
|
|
|
|
|
mapping.qcCategoryId = qcCategoryId |
|
|
|
|
|
mapping.type = type |
|
|
|
|
|
|
|
|
|
|
|
val saved = itemsQcCategoryMappingRepository.save(mapping) |
|
|
|
|
|
|
|
|
|
|
|
val item = itemsRepository.findById(itemId).orElseThrow { NotFoundException() } |
|
|
|
|
|
val qcCategory = qcCategoryRepository.findById(qcCategoryId).orElseThrow { NotFoundException() } |
|
|
|
|
|
|
|
|
|
|
|
return ItemQcCategoryMappingInfo( |
|
|
|
|
|
id = saved.id!!, |
|
|
|
|
|
itemId = saved.itemId ?: 0L, |
|
|
|
|
|
itemCode = item.code, |
|
|
|
|
|
itemName = item.name, |
|
|
|
|
|
qcCategoryId = saved.qcCategoryId ?: 0L, |
|
|
|
|
|
qcCategoryCode = qcCategory.code, |
|
|
|
|
|
qcCategoryName = qcCategory.name, |
|
|
|
|
|
type = saved.type |
|
|
|
|
|
) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Delete item and qc category mapping |
|
|
|
|
|
open fun deleteItemQcCategoryMapping(mappingId: Long) { |
|
|
|
|
|
itemsQcCategoryMappingRepository.deleteById(mappingId) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Get qc category and qc item mappings |
|
|
|
|
|
open fun getQcCategoryQcItemMappings(qcCategoryId: Long): List<QcItemInfo> { |
|
|
|
|
|
return qcItemCategoryRepository.findAllByQcCategoryId(qcCategoryId) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Save qc category and qc item mapping |
|
|
|
|
|
open fun saveQcCategoryQcItemMapping( |
|
|
|
|
|
qcCategoryId: Long, |
|
|
|
|
|
qcItemId: Long, |
|
|
|
|
|
order: Int, |
|
|
|
|
|
description: String? |
|
|
|
|
|
): QcItemCategory { |
|
|
|
|
|
val qcCategory = qcCategoryRepository.findById(qcCategoryId).orElseThrow { NotFoundException() } |
|
|
|
|
|
val qcItem = qcItemRepository.findById(qcItemId).orElseThrow { NotFoundException() } |
|
|
|
|
|
|
|
|
|
|
|
// Check if mapping already exists |
|
|
|
|
|
val existing = qcCategory.qcItemCategory.firstOrNull { it.qcItem?.id == qcItemId } |
|
|
|
|
|
val mapping = existing ?: QcItemCategory() |
|
|
|
|
|
|
|
|
|
|
|
mapping.qcCategory = qcCategory |
|
|
|
|
|
mapping.qcItem = qcItem |
|
|
|
|
|
mapping.order = order |
|
|
|
|
|
// Description is optional - use qcItem's description if not provided |
|
|
|
|
|
mapping.description = description ?: qcItem.description |
|
|
|
|
|
|
|
|
|
|
|
if (existing == null) { |
|
|
|
|
|
qcCategory.qcItemCategory.add(mapping) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
val savedCategory = qcCategoryRepository.save(qcCategory) |
|
|
|
|
|
|
|
|
|
|
|
// Return the saved mapping from the saved category to avoid lazy loading issues |
|
|
|
|
|
return savedCategory.qcItemCategory.firstOrNull { it.qcItem?.id == qcItemId } ?: mapping |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Delete qc category and qc item mapping |
|
|
|
|
|
open fun deleteQcCategoryQcItemMapping(mappingId: Long) { |
|
|
|
|
|
val mapping = qcItemCategoryRepository.findById(mappingId).orElseThrow { NotFoundException() } |
|
|
|
|
|
val qcCategory = mapping.qcCategory |
|
|
|
|
|
if (qcCategory != null) { |
|
|
|
|
|
qcCategory.qcItemCategory.remove(mapping) |
|
|
|
|
|
qcCategoryRepository.save(qcCategory) |
|
|
|
|
|
} |
|
|
|
|
|
qcItemCategoryRepository.delete(mapping) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Save qc category with validation |
|
|
|
|
|
open fun saveQcCategoryWithValidation(@Valid @RequestBody request: SaveQcCategoryRequest): SaveQcCategoryResponse { |
|
|
|
|
|
val errors = mutableMapOf<String, String>() |
|
|
|
|
|
val id = request.id |
|
|
|
|
|
val qcCategory = if (id != null) qcCategoryRepository.findById(id).orElseThrow() else QcCategory() |
|
|
|
|
|
|
|
|
|
|
|
if (errors.isNotEmpty()) { |
|
|
|
|
|
return SaveQcCategoryResponse( |
|
|
|
|
|
id = request.id, |
|
|
|
|
|
code = request.code, |
|
|
|
|
|
name = request.name, |
|
|
|
|
|
description = request.description, |
|
|
|
|
|
errors = errors |
|
|
|
|
|
) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
qcCategory.apply { |
|
|
|
|
|
code = request.code |
|
|
|
|
|
name = request.name |
|
|
|
|
|
description = request.description |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
val savedQcCategory = qcCategoryRepository.save(qcCategory) |
|
|
|
|
|
|
|
|
|
|
|
return SaveQcCategoryResponse( |
|
|
|
|
|
id = savedQcCategory.id, |
|
|
|
|
|
code = savedQcCategory.code, |
|
|
|
|
|
name = savedQcCategory.name, |
|
|
|
|
|
description = savedQcCategory.description, |
|
|
|
|
|
errors = null |
|
|
|
|
|
) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Delete qc category with validation |
|
|
|
|
|
open fun deleteQcCategoryWithValidation(id: Long): DeleteResponse { |
|
|
|
|
|
if (!canDeleteQcCategory(id)) { |
|
|
|
|
|
val itemCount = getItemCountByQcCategory(id) |
|
|
|
|
|
val qcItemCount = getQcItemCountByQcCategory(id) |
|
|
|
|
|
return DeleteResponse( |
|
|
|
|
|
success = false, |
|
|
|
|
|
canDelete = false, |
|
|
|
|
|
message = "Cannot delete QcCategory. It has $itemCount item(s) and $qcItemCount qc item(s) linked to it." |
|
|
|
|
|
) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
val qcCategory = qcCategoryRepository.findById(id).orElseThrow { NotFoundException() } |
|
|
|
|
|
qcCategory.deleted = true |
|
|
|
|
|
qcCategoryRepository.save(qcCategory) |
|
|
|
|
|
|
|
|
|
|
|
return DeleteResponse( |
|
|
|
|
|
success = true, |
|
|
|
|
|
canDelete = true, |
|
|
|
|
|
message = "QcCategory deleted successfully" |
|
|
|
|
|
) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Save qc item with validation |
|
|
|
|
|
open fun saveQcItemWithValidation(@Valid @RequestBody request: SaveQcItemRequest): SaveQcItemResponse { |
|
|
|
|
|
val errors = mutableMapOf<String, String>() |
|
|
|
|
|
val id = request.id |
|
|
|
|
|
val qcItem = if (id != null) qcItemRepository.findById(id).orElseThrow() else QcItem() |
|
|
|
|
|
|
|
|
|
|
|
if (errors.isNotEmpty()) { |
|
|
|
|
|
return SaveQcItemResponse( |
|
|
|
|
|
id = request.id, |
|
|
|
|
|
code = request.code, |
|
|
|
|
|
name = request.name, |
|
|
|
|
|
description = request.description, |
|
|
|
|
|
errors = errors |
|
|
|
|
|
) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
qcItem.apply { |
|
|
|
|
|
code = request.code |
|
|
|
|
|
name = request.name |
|
|
|
|
|
description = request.description |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
val savedQcItem = qcItemRepository.save(qcItem) |
|
|
|
|
|
|
|
|
|
|
|
return SaveQcItemResponse( |
|
|
|
|
|
id = savedQcItem.id, |
|
|
|
|
|
code = savedQcItem.code, |
|
|
|
|
|
name = savedQcItem.name, |
|
|
|
|
|
description = savedQcItem.description, |
|
|
|
|
|
errors = null |
|
|
|
|
|
) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Delete qc item with validation |
|
|
|
|
|
open fun deleteQcItemWithValidation(id: Long): DeleteResponse { |
|
|
|
|
|
if (!canDeleteQcItem(id)) { |
|
|
|
|
|
return DeleteResponse( |
|
|
|
|
|
success = false, |
|
|
|
|
|
canDelete = false, |
|
|
|
|
|
message = "Cannot delete QcItem. It is linked to one or more QcCategories." |
|
|
|
|
|
) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
val qcItem = qcItemRepository.findById(id).orElseThrow { NotFoundException() } |
|
|
|
|
|
qcItem.deleted = true |
|
|
|
|
|
qcItemRepository.save(qcItem) |
|
|
|
|
|
|
|
|
|
|
|
return DeleteResponse( |
|
|
|
|
|
success = true, |
|
|
|
|
|
canDelete = true, |
|
|
|
|
|
message = "QcItem deleted successfully" |
|
|
|
|
|
) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Get item by code (for Tab 0 - validate item code input) |
|
|
|
|
|
open fun getItemByCode(code: String): Items? { |
|
|
|
|
|
return itemsRepository.findByCodeAndDeletedFalse(code) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|