| @@ -38,24 +38,25 @@ dependencies { | |||
| implementation group: 'jakarta.validation', name: 'jakarta.validation-api', version: '3.0.2' | |||
| implementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: '2.15.2' | |||
| implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.15.2' | |||
| implementation group: 'com.fasterxml.jackson.module', name: 'jackson-module-kotlin', version: '2.15.2' | |||
| implementation group: 'io.jsonwebtoken', name: 'jjwt-api', version: '0.11.5' | |||
| implementation group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.11.5' | |||
| implementation group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.5' | |||
| implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" | |||
| implementation "org.jetbrains.kotlin:kotlin-reflect" | |||
| compileOnly group: 'jakarta.servlet', name: 'jakarta.servlet-api', version: '6.0.0' | |||
| runtimeOnly 'com.mysql:mysql-connector-j' | |||
| runtimeOnly 'com.unboundid:unboundid-ldapsdk:6.0.9' | |||
| testImplementation 'org.springframework.boot:spring-boot-starter-test' | |||
| testImplementation 'org.springframework.security:spring-security-test' | |||
| implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" | |||
| implementation "org.jetbrains.kotlin:kotlin-reflect" | |||
| } | |||
| configurations { | |||
| all { | |||
| configureEach { | |||
| exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging' | |||
| } | |||
| } | |||
| @@ -0,0 +1,10 @@ | |||
| package com.ffii.fpsms.enums | |||
| enum class Status(val value: Int) { | |||
| PENDING(1), | |||
| COMPLETE(2) | |||
| } | |||
| enum class RelationColumnType(val col: String) { | |||
| MATERIAL("materialId"), | |||
| PRODUCT("productId") | |||
| } | |||
| @@ -43,7 +43,7 @@ open class Material : BaseEntity<Long>() { | |||
| private val type: Set<ProductMaterialType> = HashSet() | |||
| @Column(name = "shelfLife") | |||
| open var shelfLife: Number? = null | |||
| open var shelfLife: Double? = null | |||
| @Column(name = "countryOfOrigin") | |||
| open var countryOfOrigin: String? = null | |||
| @@ -2,9 +2,11 @@ package com.ffii.fpsms.modules.master.entity | |||
| import com.ffii.core.support.AbstractRepository | |||
| import org.springframework.stereotype.Repository | |||
| import java.util.Optional | |||
| @Repository | |||
| interface MaterialRepository : AbstractRepository<Material, Long> { | |||
| // fun findAllByDeletedFalse(): List<Material>; | |||
| fun findAllByDeletedFalse(): List<Material>; | |||
| fun findByIdAndDeletedFalse(id: Long): Material; | |||
| fun findByCodeAndDeletedIsFalse(code: String): Material?; | |||
| } | |||
| @@ -43,7 +43,7 @@ open class Product : BaseEntity<Long>() { | |||
| private val type: Set<ProductMaterialType> = HashSet() | |||
| @Column(name = "shelfLife") | |||
| open var shelfLife: Number? = null | |||
| open var shelfLife: Double? = null | |||
| @Column(name = "countryOfOrigin") | |||
| open var countryOfOrigin: String? = null | |||
| @@ -1,27 +1,25 @@ | |||
| package com.ffii.fpsms.modules.master.entity | |||
| import com.fasterxml.jackson.annotation.JsonBackReference | |||
| import com.ffii.core.entity.IdEntity | |||
| import jakarta.persistence.* | |||
| import jakarta.validation.constraints.NotNull | |||
| @Entity | |||
| @Table(name = "product_material_type") | |||
| open class ProductMaterialType { | |||
| open class ProductMaterialType: IdEntity<Long>() { | |||
| @ManyToOne | |||
| @JoinColumn(name = "productId") | |||
| @JsonBackReference | |||
| @NotNull | |||
| open var product: Product? = null | |||
| @ManyToOne | |||
| @JoinColumn(name = "materialId") | |||
| @JsonBackReference | |||
| @NotNull | |||
| open var material: Material? = null | |||
| @Id | |||
| @NotNull | |||
| @Column(name = "name") | |||
| open var name: String? = null | |||
| @Column(name = "type") | |||
| open var type: String? = null | |||
| } | |||
| @@ -0,0 +1,14 @@ | |||
| package com.ffii.fpsms.modules.master.entity | |||
| import com.ffii.core.support.AbstractRepository | |||
| import org.springframework.stereotype.Repository | |||
| import org.springframework.transaction.annotation.Transactional | |||
| import java.util.Optional | |||
| @Repository | |||
| interface ProductMaterialTypeRepository : AbstractRepository<ProductMaterialType, Long> { | |||
| @Transactional | |||
| fun deleteAllByMaterialId(id: Long) | |||
| @Transactional | |||
| fun deleteAllByProductId(id: Long) | |||
| } | |||
| @@ -1,25 +1,23 @@ | |||
| package com.ffii.fpsms.modules.master.entity | |||
| import com.fasterxml.jackson.annotation.JsonBackReference | |||
| import com.ffii.core.entity.IdEntity | |||
| import jakarta.persistence.* | |||
| import jakarta.validation.constraints.NotNull | |||
| @Entity | |||
| @Table(name = "product_material_uom") | |||
| open class ProductMaterialUom { | |||
| open class ProductMaterialUom: IdEntity<Long>() { | |||
| @ManyToOne | |||
| @JoinColumn(name = "productId") | |||
| @JsonBackReference | |||
| @NotNull | |||
| open var product: Product? = null | |||
| @ManyToOne | |||
| @JoinColumn(name = "materialId") | |||
| @JsonBackReference | |||
| @NotNull | |||
| open var material: Material? = null | |||
| @Id | |||
| @NotNull | |||
| @Column(name = "uom") | |||
| open var uom: String? = null | |||
| @@ -0,0 +1,17 @@ | |||
| package com.ffii.fpsms.modules.master.entity | |||
| import com.ffii.core.support.AbstractRepository | |||
| import org.springframework.data.jpa.repository.Modifying | |||
| import org.springframework.data.jpa.repository.Query | |||
| import org.springframework.stereotype.Repository | |||
| import org.springframework.transaction.annotation.Transactional | |||
| @Repository | |||
| interface ProductMaterialUomRepository : AbstractRepository<ProductMaterialUom, Long> { | |||
| @Transactional | |||
| fun deleteAllByMaterialId(id: Long) | |||
| @Transactional | |||
| fun deleteAllByProductId(id: Long) | |||
| } | |||
| @@ -1,25 +1,23 @@ | |||
| package com.ffii.fpsms.modules.master.entity | |||
| import com.fasterxml.jackson.annotation.JsonBackReference | |||
| import com.ffii.core.entity.IdEntity | |||
| import jakarta.persistence.* | |||
| import jakarta.validation.constraints.NotNull | |||
| @Entity | |||
| @Table(name = "product_material_weightUnit") | |||
| open class ProductMaterialWeightUnit { | |||
| open class ProductMaterialWeightUnit: IdEntity<Long>() { | |||
| @ManyToOne | |||
| @JoinColumn(name = "productId") | |||
| @JsonBackReference | |||
| @NotNull | |||
| open var product: Product? = null | |||
| @ManyToOne | |||
| @JoinColumn(name = "materialId") | |||
| @JsonBackReference | |||
| @NotNull | |||
| open var material: Material? = null | |||
| @Id | |||
| @NotNull | |||
| @Column(name = "weightUnit") | |||
| open var weightUnit: String? = null | |||
| @@ -0,0 +1,12 @@ | |||
| package com.ffii.fpsms.modules.master.entity | |||
| import com.ffii.core.support.AbstractRepository | |||
| import org.springframework.stereotype.Repository | |||
| import org.springframework.transaction.annotation.Transactional | |||
| @Repository | |||
| interface ProductMaterialWeightUnitRepository : AbstractRepository<ProductMaterialWeightUnit, Long> { | |||
| @Transactional | |||
| fun deleteAllByMaterialId(id: Long) | |||
| @Transactional | |||
| fun deleteAllByProductId(id: Long) | |||
| } | |||
| @@ -5,4 +5,5 @@ import com.ffii.core.support.AbstractRepository | |||
| interface ProductRepository: AbstractRepository<Product, Long> { | |||
| fun findAllByDeletedFalse(): List<Product>; | |||
| fun findByIdAndDeletedFalse(id: Long): Product; | |||
| } | |||
| @@ -2,19 +2,75 @@ package com.ffii.fpsms.modules.master.service | |||
| import com.ffii.core.support.AbstractBaseEntityService | |||
| import com.ffii.core.support.JdbcDao | |||
| import com.ffii.fpsms.enums.RelationColumnType | |||
| import com.ffii.fpsms.modules.master.entity.Material | |||
| import com.ffii.fpsms.modules.master.entity.MaterialRepository | |||
| import com.ffii.fpsms.modules.master.web.models.NewMaterialRequest | |||
| import com.ffii.fpsms.modules.master.web.models.NewMaterialResponse | |||
| import org.springframework.stereotype.Service | |||
| import org.springframework.transaction.annotation.Transactional | |||
| import kotlin.jvm.optionals.getOrNull | |||
| @Service | |||
| open class MaterialService( | |||
| private val jdbcDao: JdbcDao, | |||
| private val materialRepository: MaterialRepository, | |||
| private val uomService: ProductMaterialUomService, | |||
| private val typeService: ProductMaterialTypeService, | |||
| private val weightUnitService: ProductMaterialWeightUnitService, | |||
| ): AbstractBaseEntityService<Material, Long, MaterialRepository>(jdbcDao, materialRepository) { | |||
| // do mapping with projection | |||
| open fun getMaterials(): List<Material> { | |||
| open fun allMaterials(): List<Material> { | |||
| // TODO: Replace by actual logic | |||
| val materials = materialRepository.findAll() | |||
| return materials | |||
| } | |||
| open fun getMaterial(id: Long): Material? { | |||
| return materialRepository.findById(id).getOrNull() | |||
| } | |||
| @Transactional | |||
| open fun saveMaterial(request: NewMaterialRequest): NewMaterialResponse { | |||
| val duplicateMaterial = materialRepository.findByCodeAndDeletedIsFalse(request.code) | |||
| val material = | |||
| if (request.id != null && request.id > 0) materialRepository.findByIdAndDeletedFalse(request.id) | |||
| else Material() | |||
| if (duplicateMaterial != null && duplicateMaterial.id != request.id) { | |||
| return NewMaterialResponse( | |||
| id = request.id, | |||
| code = request.code, | |||
| name = request.name, | |||
| message = "The material code has already existed", | |||
| errorPosition = "code" | |||
| ) | |||
| } | |||
| material.apply { | |||
| code = request.code | |||
| name = request.name | |||
| isConsumables = request.isConsumables | |||
| description = request.description | |||
| remarks = request.remarks | |||
| shelfLife = request.shelfLife | |||
| countryOfOrigin = request.countryOfOrigin | |||
| minHumid = request.minHumid | |||
| maxHumid = request.maxHumid | |||
| minTemp = request.minTemp | |||
| maxTemp = request.maxTemp | |||
| sampleRate = request.sampleRate | |||
| passingRate = request.passingRate | |||
| netWeight = request.netWeight | |||
| } | |||
| val savedMaterial = materialRepository.saveAndFlush(material) | |||
| val columnName = RelationColumnType.MATERIAL | |||
| if (!request.uom.isNullOrEmpty()) uomService.saveUom(savedMaterial.id!!, columnName, request.uom) | |||
| if (!request.weightUnit.isNullOrEmpty()) weightUnitService.saveWeightUnit(savedMaterial.id!!, columnName, request.weightUnit) | |||
| if (!request.type.isNullOrEmpty()) typeService.saveType(savedMaterial.id!!, columnName, request.type) | |||
| return NewMaterialResponse( | |||
| id = savedMaterial.id, | |||
| name = savedMaterial.name, | |||
| code = savedMaterial.code, | |||
| message = "Material Save Success", | |||
| errorPosition = null, | |||
| ) | |||
| } | |||
| } | |||
| @@ -0,0 +1,48 @@ | |||
| package com.ffii.fpsms.modules.master.service | |||
| import com.ffii.core.support.JdbcDao | |||
| import com.ffii.fpsms.enums.RelationColumnType | |||
| import com.ffii.fpsms.modules.master.entity.* | |||
| import com.ffii.fpsms.modules.master.web.models.MessageResponse | |||
| import com.ffii.fpsms.modules.master.web.models.NewTypeRequest | |||
| import com.ffii.fpsms.modules.master.web.models.NewWeightUnitRequest | |||
| import org.springframework.stereotype.Service | |||
| import org.springframework.transaction.annotation.Transactional | |||
| @Service | |||
| open class ProductMaterialTypeService( | |||
| private val jdbcDao: JdbcDao, | |||
| private val materialRepository: MaterialRepository, | |||
| private val productRepository: ProductRepository, | |||
| private val productMaterialTypeRepository: ProductMaterialTypeRepository | |||
| ) { | |||
| @Transactional | |||
| open fun saveType(id: Long, columnType: RelationColumnType, typeList: List<NewTypeRequest>): MessageResponse { | |||
| var material: Material? = null | |||
| var product: Product? = null | |||
| when (columnType) { | |||
| RelationColumnType.MATERIAL -> { | |||
| productMaterialTypeRepository.deleteAllByMaterialId(id) | |||
| material = materialRepository.findByIdAndDeletedFalse(id) | |||
| } | |||
| RelationColumnType.PRODUCT -> { | |||
| productMaterialTypeRepository.deleteAllByProductId(id) | |||
| product = productRepository.findByIdAndDeletedFalse(id) | |||
| } | |||
| } | |||
| val entries = mutableListOf<ProductMaterialType>() | |||
| for (entry in typeList) { | |||
| entries += ProductMaterialType().apply { | |||
| this.material = material | |||
| this.product = product | |||
| type = entry.type | |||
| } | |||
| } | |||
| productMaterialTypeRepository.saveAll(entries) | |||
| return MessageResponse( | |||
| id = id, | |||
| message = "Weight unit save success", | |||
| errorPosition = null | |||
| ) | |||
| } | |||
| } | |||
| @@ -0,0 +1,48 @@ | |||
| package com.ffii.fpsms.modules.master.service | |||
| import com.ffii.core.support.AbstractIdEntityService | |||
| import com.ffii.core.support.JdbcDao | |||
| import com.ffii.fpsms.enums.RelationColumnType | |||
| import com.ffii.fpsms.modules.master.entity.* | |||
| import com.ffii.fpsms.modules.master.web.models.MessageResponse | |||
| import com.ffii.fpsms.modules.master.web.models.NewUomRequest | |||
| import org.springframework.stereotype.Service | |||
| import org.springframework.transaction.annotation.Transactional | |||
| @Service | |||
| open class ProductMaterialUomService( | |||
| private val jdbcDao: JdbcDao, | |||
| private val uomRepository: ProductMaterialUomRepository, | |||
| private val materialRepository: MaterialRepository, | |||
| private val productRepository: ProductRepository, | |||
| ): AbstractIdEntityService<ProductMaterialUom, Long, ProductMaterialUomRepository>(jdbcDao, uomRepository) { | |||
| @Transactional | |||
| open fun saveUom(id: Long, columnType: RelationColumnType, uomList: List<NewUomRequest>): MessageResponse { | |||
| var material: Material? = null | |||
| var product: Product? = null | |||
| when (columnType) { | |||
| RelationColumnType.MATERIAL -> { | |||
| uomRepository.deleteAllByMaterialId(id) | |||
| material = materialRepository.findByIdAndDeletedFalse(id) | |||
| } | |||
| RelationColumnType.PRODUCT -> { | |||
| uomRepository.deleteAllByProductId(id) | |||
| product = productRepository.findByIdAndDeletedFalse(id) | |||
| } | |||
| } | |||
| val entries = mutableListOf<ProductMaterialUom>() | |||
| for (entry in uomList) { | |||
| entries += ProductMaterialUom().apply { | |||
| this.material = material | |||
| this.product = product | |||
| uom = entry.uom | |||
| } | |||
| } | |||
| uomRepository.saveAll(entries) | |||
| return MessageResponse( | |||
| id = id, | |||
| message = "Uom save success", | |||
| errorPosition = null | |||
| ) | |||
| } | |||
| } | |||
| @@ -0,0 +1,48 @@ | |||
| package com.ffii.fpsms.modules.master.service | |||
| import com.ffii.core.support.JdbcDao | |||
| import com.ffii.fpsms.enums.RelationColumnType | |||
| import com.ffii.fpsms.modules.master.entity.* | |||
| import com.ffii.fpsms.modules.master.web.models.MessageResponse | |||
| import com.ffii.fpsms.modules.master.web.models.NewWeightUnitRequest | |||
| import org.springframework.stereotype.Service | |||
| import org.springframework.transaction.annotation.Transactional | |||
| @Service | |||
| open class ProductMaterialWeightUnitService( | |||
| private val jdbcDao: JdbcDao, | |||
| private val weightUnitRepository: ProductMaterialWeightUnitRepository, | |||
| private val materialRepository: MaterialRepository, | |||
| private val productRepository: ProductRepository, | |||
| ) { | |||
| @Transactional | |||
| open fun saveWeightUnit(id: Long, columnType: RelationColumnType, weightUnits: List<NewWeightUnitRequest>): MessageResponse { | |||
| var material: Material? = null | |||
| var product: Product? = null | |||
| when (columnType) { | |||
| RelationColumnType.MATERIAL -> { | |||
| weightUnitRepository.deleteAllByMaterialId(id) | |||
| material = materialRepository.findByIdAndDeletedFalse(id) | |||
| } | |||
| RelationColumnType.PRODUCT -> { | |||
| weightUnitRepository.deleteAllByProductId(id) | |||
| product = productRepository.findByIdAndDeletedFalse(id) | |||
| } | |||
| } | |||
| val entries = mutableListOf<ProductMaterialWeightUnit>() | |||
| for (entry in weightUnits) { | |||
| entries += ProductMaterialWeightUnit().apply { | |||
| this.material = material | |||
| this.product = product | |||
| weightUnit = entry.weightUnit | |||
| conversion = entry.conversion | |||
| } | |||
| } | |||
| weightUnitRepository.saveAll(entries) | |||
| return MessageResponse( | |||
| id = id, | |||
| message = "Weight unit save success", | |||
| errorPosition = null | |||
| ) | |||
| } | |||
| } | |||
| @@ -1,18 +1,28 @@ | |||
| package com.ffii.fpsms.modules.master.web | |||
| import com.ffii.core.exception.NotFoundException | |||
| import com.ffii.fpsms.modules.master.entity.Material | |||
| import com.ffii.fpsms.modules.master.service.MaterialService | |||
| 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.master.web.models.NewMaterialRequest | |||
| import com.ffii.fpsms.modules.master.web.models.NewMaterialResponse | |||
| import jakarta.validation.Valid | |||
| import org.springframework.web.bind.annotation.* | |||
| @RestController | |||
| @RequestMapping("/materials") | |||
| @RequestMapping("/material") | |||
| class MaterialController( | |||
| private val materialService: MaterialService | |||
| ) { | |||
| @GetMapping | |||
| fun allMaterial(): List<Material> { | |||
| return materialService.getMaterials() | |||
| return materialService.allMaterials() | |||
| } | |||
| @GetMapping("/details/{id}") | |||
| fun getMaterial(@PathVariable id: Long): Material { | |||
| return materialService.getMaterial(id) ?: throw NotFoundException() | |||
| } | |||
| @PostMapping("/new") | |||
| fun saveMaterial(@Valid @RequestBody newMaterial: NewMaterialRequest): NewMaterialResponse { | |||
| return materialService.saveMaterial(newMaterial) | |||
| } | |||
| } | |||
| @@ -0,0 +1,9 @@ | |||
| package com.ffii.fpsms.modules.master.web.models | |||
| data class NewMaterialResponse( | |||
| val id: Long?, | |||
| val name: String?, | |||
| val code: String?, | |||
| val message: String?, // e.g. duplicated code | |||
| val errorPosition: String?, //error field | |||
| ) | |||
| @@ -0,0 +1,7 @@ | |||
| package com.ffii.fpsms.modules.master.web.models | |||
| data class MessageResponse( | |||
| val id: Long?, | |||
| val message: String?, | |||
| val errorPosition: String?, // e.g. duplicated code | |||
| ) | |||
| @@ -0,0 +1,40 @@ | |||
| package com.ffii.fpsms.modules.master.web.models | |||
| import jakarta.validation.constraints.NotBlank | |||
| import jakarta.validation.constraints.NotNull | |||
| data class NewMaterialRequest( | |||
| @field:NotBlank(message = "material code cannot be empty") | |||
| val code: String, | |||
| @field:NotBlank(message = "material name cannot be empty") | |||
| val name: String, | |||
| @field:NotNull(message = "isConsumables cannot be null") | |||
| val isConsumables: Boolean, | |||
| val id: Long?, | |||
| val description: String?, | |||
| val remarks: String?, | |||
| val shelfLife: Double?, | |||
| val countryOfOrigin: String?, | |||
| val minHumid: Double?, | |||
| val maxHumid: Double?, | |||
| val minTemp: Double?, | |||
| val maxTemp: Double?, | |||
| val sampleRate: Double?, | |||
| val passingRate: Double?, | |||
| val netWeight: Double?, | |||
| val type: List<NewTypeRequest>?, | |||
| val uom: List<NewUomRequest>?, | |||
| val weightUnit: List<NewWeightUnitRequest>?, | |||
| ) | |||
| data class NewUomRequest( | |||
| val uom: String | |||
| ) | |||
| data class NewTypeRequest( | |||
| val type: String, | |||
| ) | |||
| data class NewWeightUnitRequest( | |||
| val conversion: Double?, | |||
| val weightUnit: String, | |||
| ) | |||
| @@ -0,0 +1,30 @@ | |||
| package com.ffii.fpsms.modules.master.web.models | |||
| import jakarta.validation.constraints.NotBlank | |||
| import jakarta.validation.constraints.NotNull | |||
| data class NewProductRequest( | |||
| @field:NotBlank(message = "product name cannot be empty") | |||
| val code: String, | |||
| @field:NotBlank(message = "product name cannot be empty") | |||
| val name: String, | |||
| @field:NotNull(message = "isConsumables cannot be empty") | |||
| val isConsumables: Boolean, | |||
| val id: Long?, | |||
| val description: String?, | |||
| val remarks: String?, | |||
| val shelfLife: Number?, | |||
| val countryOfOrigin: String?, | |||
| val minHumid: Double?, | |||
| val maxHumid: Double?, | |||
| val minTemp: Double?, | |||
| val maxTemp: Double?, | |||
| val sampleRate: Double?, | |||
| val passingRate: Double?, | |||
| val netWeight: Double?, | |||
| val type: List<String>?, | |||
| val uom: List<String>?, | |||
| val weightUnit: List<String>?, | |||
| ) | |||
| @@ -0,0 +1,9 @@ | |||
| package com.ffii.fpsms.modules.master.web.models | |||
| data class NewProductResponse( | |||
| val id: Long?, | |||
| val name: String?, | |||
| val code: String?, | |||
| val message: String?, | |||
| val errorPosition: String?, // e.g. duplicated code | |||
| ) | |||
| @@ -0,0 +1,10 @@ | |||
| -- liquibase formatted sql | |||
| -- changeset derek:update_master_relation_tables | |||
| ALTER TABLE `product_material_type` | |||
| ADD COLUMN `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; | |||
| ALTER TABLE `product_material_uom` | |||
| ADD COLUMN `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; | |||
| ALTER TABLE `product_material_weightUnit` | |||
| ADD COLUMN `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; | |||
| @@ -0,0 +1,6 @@ | |||
| -- liquibase formatted sql | |||
| -- changeset derek:update ProdMat type col name | |||
| ALTER TABLE `product_material_type` | |||
| CHANGE COLUMN `name` `type` VARCHAR(30) NOT NULL; | |||
| @@ -0,0 +1,9 @@ | |||
| -- liquibase formatted sql | |||
| -- changeset derek:update ProdMat shelfLife col name | |||
| ALTER TABLE `product` | |||
| CHANGE COLUMN `shelfLife` `shelfLife` DECIMAL(14,1) NOT NULL; | |||
| ALTER TABLE `material` | |||
| CHANGE COLUMN `shelfLife` `shelfLife` DECIMAL(14,1) NOT NULL; | |||