| @@ -1,14 +1,10 @@ | |||||
| package com.ffii.fpsms.modules.master.entity | package com.ffii.fpsms.modules.master.entity | ||||
| import com.fasterxml.jackson.annotation.JsonBackReference | |||||
| import com.fasterxml.jackson.annotation.JsonManagedReference | import com.fasterxml.jackson.annotation.JsonManagedReference | ||||
| import com.ffii.core.entity.BaseEntity | import com.ffii.core.entity.BaseEntity | ||||
| import jakarta.persistence.CascadeType | |||||
| import jakarta.persistence.Column | |||||
| import jakarta.persistence.Entity | |||||
| import jakarta.persistence.JoinColumn | |||||
| import jakarta.persistence.JoinTable | |||||
| import jakarta.persistence.OneToMany | |||||
| import jakarta.persistence.Table | |||||
| import com.ffii.core.entity.IdEntity | |||||
| import jakarta.persistence.* | |||||
| import jakarta.validation.constraints.NotNull | import jakarta.validation.constraints.NotNull | ||||
| @Entity | @Entity | ||||
| @@ -22,6 +18,10 @@ open class QcCategory : BaseEntity<Long>() { | |||||
| @Column(name = "name") | @Column(name = "name") | ||||
| open var name: String? = null | open var name: String? = null | ||||
| @NotNull | |||||
| @Column(name = "description") | |||||
| open var description: String? = null | |||||
| // @OneToMany(cascade = [CascadeType.ALL]) | // @OneToMany(cascade = [CascadeType.ALL]) | ||||
| // @JoinTable( | // @JoinTable( | ||||
| // name = "qc_item_category", | // name = "qc_item_category", | ||||
| @@ -33,4 +33,24 @@ open class QcCategory : BaseEntity<Long>() { | |||||
| @JsonManagedReference | @JsonManagedReference | ||||
| @OneToMany(mappedBy = "qcCategory", cascade = [CascadeType.ALL], orphanRemoval = true) | @OneToMany(mappedBy = "qcCategory", cascade = [CascadeType.ALL], orphanRemoval = true) | ||||
| open var qcItemCategory: MutableList<QcItemCategory> = mutableListOf() | open var qcItemCategory: MutableList<QcItemCategory> = mutableListOf() | ||||
| } | |||||
| @Entity | |||||
| @Table(name = "items_qc_category_mapping") | |||||
| open class ItemsQcCategoryMapping : IdEntity<Long>() { | |||||
| @NotNull | |||||
| @Column(name = "itemId") | |||||
| open var itemId: Long? = null | |||||
| @NotNull | |||||
| @Column(name = "qcCategoryId") | |||||
| open var qcCategoryId: Long? = null | |||||
| @NotNull | |||||
| @Column(name = "type") | |||||
| open var type: String? = null | |||||
| // | |||||
| // @ManyToOne(fetch = FetchType.LAZY) | |||||
| // @JoinColumn(name = "qcCategoryId") | |||||
| // val qcCategory: QcCategory? = null | |||||
| } | } | ||||
| @@ -2,6 +2,8 @@ package com.ffii.fpsms.modules.master.entity | |||||
| import com.ffii.core.support.AbstractRepository | import com.ffii.core.support.AbstractRepository | ||||
| import com.ffii.fpsms.modules.master.entity.projections.QcCategoryCombo | import com.ffii.fpsms.modules.master.entity.projections.QcCategoryCombo | ||||
| import com.ffii.fpsms.modules.qc.entity.projection.QcCategoryInfo | |||||
| import org.springframework.data.jpa.repository.Query | |||||
| import org.springframework.stereotype.Repository | import org.springframework.stereotype.Repository | ||||
| @Repository | @Repository | ||||
| @@ -9,4 +11,24 @@ interface QcCategoryRepository : AbstractRepository<QcCategory, Long> { | |||||
| fun findAllByDeletedIsFalse(): List<QcCategory>; | fun findAllByDeletedIsFalse(): List<QcCategory>; | ||||
| fun findQcCategoryComboByDeletedIsFalse(): List<QcCategoryCombo>; | fun findQcCategoryComboByDeletedIsFalse(): List<QcCategoryCombo>; | ||||
| // @Query( | |||||
| // value = """ | |||||
| // SELECT qcc.* FROM qc_category qcc | |||||
| // LEFT JOIN items_qc_category_mapping map ON map.qcCategoryId = qcc.id | |||||
| // WHERE map.type = :type AND map.itemId = :itemId AND qcc.deleted = false | |||||
| // ORDER BY qcc.id | |||||
| // LIMIT 1 | |||||
| // """, | |||||
| // nativeQuery = true | |||||
| // ) | |||||
| @Query( | |||||
| """ | |||||
| SELECT qcc FROM QcCategory qcc | |||||
| JOIN ItemsQcCategoryMapping map ON qcc.id = map.qcCategoryId | |||||
| WHERE map.itemId = :itemId AND map.type = :type AND qcc.deleted = false | |||||
| """ | |||||
| ) | |||||
| fun findQcCategoryInfoByItemIdAndType(itemId: Long, type: String): QcCategoryInfo?; | |||||
| // fun findByItemIdAndType(itemId: Long, type: String): QcCategory?; | |||||
| } | } | ||||
| @@ -22,7 +22,12 @@ open class QcItemCategory : IdEntity<Long>() { | |||||
| @JoinColumn(name = "qcItemId") | @JoinColumn(name = "qcItemId") | ||||
| open var qcItem: QcItem? = null | open var qcItem: QcItem? = null | ||||
| @NotNull | |||||
| @Column(name = "order") | |||||
| open var order: Int = 0 | |||||
| @Size(max = 1000) | @Size(max = 1000) | ||||
| @Column(name = "description", length = 1000) | @Column(name = "description", length = 1000) | ||||
| open var description: String? = null | open var description: String? = null | ||||
| } | } | ||||
| @@ -1,8 +1,11 @@ | |||||
| package com.ffii.fpsms.modules.master.entity | package com.ffii.fpsms.modules.master.entity | ||||
| import com.ffii.core.support.AbstractRepository | import com.ffii.core.support.AbstractRepository | ||||
| import com.ffii.fpsms.modules.qc.entity.projection.QcCategoryInfo | |||||
| import com.ffii.fpsms.modules.qc.entity.projection.QcItemInfo | |||||
| import org.springframework.stereotype.Repository | import org.springframework.stereotype.Repository | ||||
| @Repository | @Repository | ||||
| interface QcItemCategoryRepository : AbstractRepository<QcItemCategory, Long> { | interface QcItemCategoryRepository : AbstractRepository<QcItemCategory, Long> { | ||||
| fun findAllByQcCategoryId(qcCategoryId : Long): List<QcItemInfo> | |||||
| } | } | ||||
| @@ -373,4 +373,16 @@ open class ItemsService( | |||||
| return jdbcDao.queryForList(sql.toString(), args); | return jdbcDao.queryForList(sql.toString(), args); | ||||
| } | } | ||||
| open fun getItemsIdWithSameCategoryForQc(args: Map<String, Any>): List<Int> { | |||||
| val sql = StringBuilder("SELECT" | |||||
| + " i.id " | |||||
| + " FROM items i " | |||||
| + " INNER JOIN items_qc_category_mapping iqcm ON iqcm.itemId = i.id AND iqcm.type = :qcType " | |||||
| + " WHERE i.deleted = false " | |||||
| + " AND LEFT(i.code, 2) = (SELECT LEFT(code, 2) FROM items WHERE id = :itemId)" | |||||
| + " AND i.id != :itemId " | |||||
| ) | |||||
| return jdbcDao.queryForInts(sql.toString(), args); | |||||
| } | |||||
| } | } | ||||
| @@ -4,11 +4,13 @@ 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.projections.QcCategoryCombo | import com.ffii.fpsms.modules.master.entity.projections.QcCategoryCombo | ||||
| import com.ffii.fpsms.modules.qc.entity.projection.QcCategoryInfo | |||||
| import org.springframework.stereotype.Service | import org.springframework.stereotype.Service | ||||
| @Service | @Service | ||||
| open class QcCategoryService( | open class QcCategoryService( | ||||
| private val qcCategoryRepository: QcCategoryRepository | |||||
| private val qcCategoryRepository: QcCategoryRepository, | |||||
| private val itemsService: ItemsService | |||||
| ) { | ) { | ||||
| open fun allQcCategories(): List<QcCategory> { | open fun allQcCategories(): List<QcCategory> { | ||||
| return qcCategoryRepository.findAllByDeletedIsFalse() | return qcCategoryRepository.findAllByDeletedIsFalse() | ||||
| @@ -17,4 +19,17 @@ open class QcCategoryService( | |||||
| open fun getQcCategoryCombo(): List<QcCategoryCombo> { | open fun getQcCategoryCombo(): List<QcCategoryCombo> { | ||||
| return qcCategoryRepository.findQcCategoryComboByDeletedIsFalse(); | return qcCategoryRepository.findQcCategoryComboByDeletedIsFalse(); | ||||
| } | } | ||||
| open fun getQcCategoryInfoByMapping(itemId : Long, type: String): QcCategoryInfo? { | |||||
| var result = qcCategoryRepository.findQcCategoryInfoByItemIdAndType(itemId, type) | |||||
| if (result == null) { // Temporarily fix for missing QC template (Auto find template from similar items) | |||||
| val args = mapOf( | |||||
| "itemId" to itemId, | |||||
| "qcType" to type | |||||
| ) | |||||
| val similarItemIds = itemsService.getItemsIdWithSameCategoryForQc(args); | |||||
| result = qcCategoryRepository.findQcCategoryInfoByItemIdAndType(similarItemIds[0].toLong(), type) | |||||
| } | |||||
| return result; | |||||
| } | |||||
| } | } | ||||
| @@ -1,11 +1,14 @@ | |||||
| package com.ffii.fpsms.modules.master.service | package com.ffii.fpsms.modules.master.service | ||||
| import com.ffii.core.support.AbstractBaseEntityService | |||||
| import com.ffii.core.support.JdbcDao | |||||
| import com.ffii.core.exception.NotFoundException | |||||
| 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.QcItemRepository | import com.ffii.fpsms.modules.master.entity.QcItemRepository | ||||
| import com.ffii.fpsms.modules.master.web.models.SaveQcItemRequest | import com.ffii.fpsms.modules.master.web.models.SaveQcItemRequest | ||||
| import com.ffii.fpsms.modules.master.web.models.SaveQcItemResponse | import com.ffii.fpsms.modules.master.web.models.SaveQcItemResponse | ||||
| 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 | ||||
| @@ -13,6 +16,8 @@ import org.springframework.web.bind.annotation.RequestBody | |||||
| @Service | @Service | ||||
| open class QcItemService( | open class QcItemService( | ||||
| private val qcItemRepository: QcItemRepository, | private val qcItemRepository: QcItemRepository, | ||||
| private val qcCategoryRepository: QcCategoryRepository, | |||||
| private val qcItemCategoryRepository: QcItemCategoryRepository, | |||||
| ) { | ) { | ||||
| open fun allQcItems(): List<QcItem> { | open fun allQcItems(): List<QcItem> { | ||||
| return qcItemRepository.findAllByDeletedIsFalse() | return qcItemRepository.findAllByDeletedIsFalse() | ||||
| @@ -26,6 +31,11 @@ open class QcItemService( | |||||
| return qcItemRepository.findByIdAndDeletedIsFalse(id) | return qcItemRepository.findByIdAndDeletedIsFalse(id) | ||||
| } | } | ||||
| open fun findQcItemsByTemplate(itemId: Long, type: String): List<QcItemInfo>? { | |||||
| val qcCategoryInfo = qcCategoryRepository.findQcCategoryInfoByItemIdAndType(itemId, type) ?: throw NotFoundException() | |||||
| return qcItemCategoryRepository.findAllByQcCategoryId(qcCategoryInfo.id) | |||||
| } | |||||
| open fun markDeleted(id: Long): List<QcItem> { | open fun markDeleted(id: Long): List<QcItem> { | ||||
| val qcItem = qcItemRepository.findById(id).orElseThrow().apply { | val qcItem = qcItemRepository.findById(id).orElseThrow().apply { | ||||
| deleted = true | deleted = true | ||||
| @@ -1,11 +1,11 @@ | |||||
| package com.ffii.fpsms.modules.master.web | package com.ffii.fpsms.modules.master.web | ||||
| import com.ffii.core.exception.NotFoundException | |||||
| import com.ffii.fpsms.modules.master.entity.QcCategory | import com.ffii.fpsms.modules.master.entity.QcCategory | ||||
| import com.ffii.fpsms.modules.master.entity.projections.QcCategoryCombo | import com.ffii.fpsms.modules.master.entity.projections.QcCategoryCombo | ||||
| import com.ffii.fpsms.modules.master.service.QcCategoryService | import com.ffii.fpsms.modules.master.service.QcCategoryService | ||||
| import org.springframework.web.bind.annotation.GetMapping | |||||
| import org.springframework.web.bind.annotation.RequestMapping | |||||
| import org.springframework.web.bind.annotation.RestController | |||||
| import com.ffii.fpsms.modules.qc.entity.projection.QcCategoryInfo | |||||
| import org.springframework.web.bind.annotation.* | |||||
| @RestController | @RestController | ||||
| @RequestMapping("/qcCategories") | @RequestMapping("/qcCategories") | ||||
| @@ -21,4 +21,9 @@ class QcCategoryController( | |||||
| fun getQcCategoryCombo(): List<QcCategoryCombo> { | fun getQcCategoryCombo(): List<QcCategoryCombo> { | ||||
| return qcCategoryService.getQcCategoryCombo(); | return qcCategoryService.getQcCategoryCombo(); | ||||
| } | } | ||||
| @GetMapping("/items") | |||||
| fun getQcCategoryByTemplate(@RequestParam itemId: Long, @RequestParam(defaultValue = "IQC") type: String): QcCategoryInfo { | |||||
| return qcCategoryService.getQcCategoryInfoByMapping(itemId, type) ?: throw NotFoundException() | |||||
| } | |||||
| } | } | ||||
| @@ -7,7 +7,10 @@ import com.ffii.fpsms.modules.master.entity.QcItemRepository | |||||
| import com.ffii.fpsms.modules.master.service.QcItemService | import com.ffii.fpsms.modules.master.service.QcItemService | ||||
| import com.ffii.fpsms.modules.master.web.models.SaveQcItemRequest | import com.ffii.fpsms.modules.master.web.models.SaveQcItemRequest | ||||
| import com.ffii.fpsms.modules.master.web.models.SaveQcItemResponse | import com.ffii.fpsms.modules.master.web.models.SaveQcItemResponse | ||||
| 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 jakarta.validation.constraints.NotBlank | |||||
| import org.slf4j.Logger | import org.slf4j.Logger | ||||
| import org.slf4j.LoggerFactory | import org.slf4j.LoggerFactory | ||||
| import org.springframework.web.bind.annotation.* | import org.springframework.web.bind.annotation.* | ||||
| @@ -37,8 +40,20 @@ class QcItemController( | |||||
| return qcItemService.findQcItemById(id) ?: throw NotFoundException() | return qcItemService.findQcItemById(id) ?: throw NotFoundException() | ||||
| } | } | ||||
| @GetMapping("/template") | |||||
| fun qcItemsByTemplate(@RequestParam itemId: Long, @RequestParam(defaultValue = "IQC") type: String): List<QcItemInfo> { | |||||
| return qcItemService.findQcItemsByTemplate(itemId, type) ?: throw NotFoundException() | |||||
| } | |||||
| @PostMapping("/save") | @PostMapping("/save") | ||||
| fun saveQcItem(@Valid @RequestBody request: SaveQcItemRequest): SaveQcItemResponse { | fun saveQcItem(@Valid @RequestBody request: SaveQcItemRequest): SaveQcItemResponse { | ||||
| return qcItemService.saveQcItem(request) | return qcItemService.saveQcItem(request) | ||||
| } | } | ||||
| } | |||||
| } | |||||
| data class GetQcItemRequest( | |||||
| @field:NotBlank(message = "Item Id cannot be empty") | |||||
| val itemId: Long, | |||||
| @field:NotBlank(message = "Type cannot be empty") | |||||
| val type: String, | |||||
| ) | |||||
| @@ -0,0 +1,26 @@ | |||||
| package com.ffii.fpsms.modules.qc.entity.projection | |||||
| import com.ffii.fpsms.modules.master.entity.QcItemCategory | |||||
| import org.springframework.beans.factory.annotation.Value | |||||
| interface QcCategoryInfo { | |||||
| val id: Long | |||||
| val code: String | |||||
| val name: String? | |||||
| val description: String? | |||||
| @get:Value("#{target.qcItemCategory}") | |||||
| var qcItems: List<QcItemInfo> | |||||
| } | |||||
| interface QcItemInfo { | |||||
| val id: Long | |||||
| val order: Int | |||||
| @get:Value("#{target.qcItem.id}") | |||||
| val qcItemId: Long | |||||
| @get:Value("#{target.qcItem.code}") | |||||
| val code: String | |||||
| @get:Value("#{target.qcItem.name}") | |||||
| val name: String? | |||||
| @get:Value("#{target.qcItem.description}") | |||||
| val description: String? | |||||
| } | |||||
| @@ -0,0 +1,9 @@ | |||||
| -- liquibase formatted sql | |||||
| -- changeset kelvinS:create items qc category mapping table | |||||
| CREATE TABLE `items_qc_category_mapping` ( | |||||
| `id` INT NOT NULL, | |||||
| `itemId` INT NOT NULL, | |||||
| `qcCategoryId` INT NOT NULL, | |||||
| `type` VARCHAR(45) NOT NULL, | |||||
| PRIMARY KEY (`id`)); | |||||
| @@ -0,0 +1,5 @@ | |||||
| -- liquibase formatted sql | |||||
| -- changeset kelvinS:alter qc item category table | |||||
| ALTER TABLE `qc_item_category` | |||||
| ADD COLUMN `order` INT NOT NULL AFTER `qcCategoryId`; | |||||
| @@ -0,0 +1,5 @@ | |||||
| -- liquibase formatted sql | |||||
| -- changeset kelvinS:alter qc category table | |||||
| ALTER TABLE `qc_category` | |||||
| ADD COLUMN `description` VARCHAR(255) NULL AFTER `name`; | |||||
| @@ -0,0 +1,5 @@ | |||||
| -- liquibase formatted sql | |||||
| -- changeset kelvinS:alter item qc category mapping table | |||||
| ALTER TABLE `items_qc_category_mapping` | |||||
| CHANGE COLUMN `id` `id` INT NOT NULL AUTO_INCREMENT ; | |||||