Added bulk material update endpoint and support for deprecation handling logic:

- **Controller**: Introduced `updateMaterial` method in `MaterialController` for batch material updates.
- **Service**: Implemented update logic in `MaterialService` to handle both updates and deprecations based on the new `MaterialUpdateDTO`.
- **Transformer**: Updated `MaterialUpdateDTOTransformer` to map deprecation status and remove unused fields.
- **Repository**: Added `updateByPartNumber` and `deleteByIds` methods in `MaterialRepository` for efficient updates and deprecations.
This commit is contained in:
Jan 2025-11-06 20:22:48 +01:00
parent 6ab0f4d630
commit ae10417c44
5 changed files with 67 additions and 51 deletions

View file

@ -1,10 +1,12 @@
package de.avatic.lcc.controller.configuration; package de.avatic.lcc.controller.configuration;
import de.avatic.lcc.dto.configuration.material.update.MaterialUpdateDTO;
import de.avatic.lcc.dto.configuration.material.view.MaterialDetailDTO; import de.avatic.lcc.dto.configuration.material.view.MaterialDetailDTO;
import de.avatic.lcc.dto.generic.MaterialDTO; import de.avatic.lcc.dto.generic.MaterialDTO;
import de.avatic.lcc.repositories.pagination.SearchQueryResult; import de.avatic.lcc.repositories.pagination.SearchQueryResult;
import de.avatic.lcc.service.access.MaterialService; import de.avatic.lcc.service.access.MaterialService;
import jakarta.validation.constraints.Min; import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.Size;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
@ -44,7 +46,7 @@ public class MaterialController {
@RequestParam(defaultValue = "1") @Min(1) int page, @RequestParam(defaultValue = "1") @Min(1) int page,
@RequestParam(required = false) Optional<String> filter) { @RequestParam(required = false) Optional<String> filter) {
SearchQueryResult<MaterialDTO> materials = materialService.listMaterial(filter, page, limit,Boolean.parseBoolean(excludeDeprecated)); SearchQueryResult<MaterialDTO> materials = materialService.listMaterial(filter, page, limit, Boolean.parseBoolean(excludeDeprecated));
return ResponseEntity.ok() return ResponseEntity.ok()
.header("X-Total-Count", String.valueOf(materials.getTotalElements())) .header("X-Total-Count", String.valueOf(materials.getTotalElements()))
@ -65,4 +67,11 @@ public class MaterialController {
public ResponseEntity<MaterialDetailDTO> getMaterialDetails(@PathVariable Integer id) { public ResponseEntity<MaterialDetailDTO> getMaterialDetails(@PathVariable Integer id) {
return ResponseEntity.ok(materialService.getMaterial(id)); return ResponseEntity.ok(materialService.getMaterial(id));
} }
@PutMapping({"/", ""})
@PreAuthorize("hasRole('MATERIAL')")
public ResponseEntity<Void> updateMaterial(List<@Size(max = 100, min = 1) MaterialUpdateDTO> materials) {
materialService.updateMaterial(materials);
return ResponseEntity.ok().build();
}
} }

View file

@ -5,38 +5,20 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.Objects; import java.util.Objects;
public class MaterialUpdateDTO { public class MaterialUpdateDTO {
private Integer id;
@JsonProperty("part_number") @JsonProperty("part_number")
private String partNumber; private String partNumber;
private String name; private String name;
@JsonProperty("hs_code")
private String hsCode; private String hsCode;
private boolean delete;
public MaterialUpdateDTO() { public MaterialUpdateDTO() {
} }
public MaterialUpdateDTO(Integer id, String partNumber, String name, String hsCode) {
this.id = id;
this.partNumber = partNumber;
this.name = name;
this.hsCode = hsCode;
}
public String getHsCode() {
return hsCode;
}
public void setHsCode(String hsCode) {
this.hsCode = hsCode;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getPartNumber() { public String getPartNumber() {
return partNumber; return partNumber;
} }
@ -53,28 +35,19 @@ public class MaterialUpdateDTO {
this.name = name; this.name = name;
} }
@Override public String getHsCode() {
public String toString() { return hsCode;
return "MaterialSummaryDTO{" +
"id='" + id + '\'' +
", partNumber='" + partNumber + '\'' +
", name='" + name + '\'' +
'}';
} }
@Override public void setHsCode(String hsCode) {
public boolean equals(Object o) { this.hsCode = hsCode;
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MaterialUpdateDTO that = (MaterialUpdateDTO) o;
return Objects.equals(id, that.id) &&
Objects.equals(partNumber, that.partNumber) &&
Objects.equals(name, that.name);
} }
@Override public boolean isDelete() {
public int hashCode() { return delete;
return Objects.hash(id, partNumber, name);
} }
public void setDelete(boolean delete) {
this.delete = delete;
}
} }

View file

@ -155,6 +155,12 @@ public class MaterialRepository {
return Optional.ofNullable(jdbcTemplate.update(updateQuery, material.getName(), material.getPartNumber(), material.getNormalizedPartNumber(), material.getHsCode(), material.getId()) == 0 ? null : material.getId()); return Optional.ofNullable(jdbcTemplate.update(updateQuery, material.getName(), material.getPartNumber(), material.getNormalizedPartNumber(), material.getHsCode(), material.getId()) == 0 ? null : material.getId());
} }
@Transactional
public void updateByPartNumber(Material material) {
String updateQuery = "UPDATE material SET name = ?, part_number = ?, normalized_part_number = ?, hs_code = ? WHERE normalized_part_number = ?";
jdbcTemplate.update(updateQuery, material.getName(), material.getPartNumber(), material.getNormalizedPartNumber(), material.getHsCode(), material.getNormalizedPartNumber());
}
/** /**
* Returns all IDs from the input list that don't exist in the material table * Returns all IDs from the input list that don't exist in the material table
* @param ids List of integers to check * @param ids List of integers to check
@ -198,6 +204,16 @@ public class MaterialRepository {
); );
} }
public void deleteByIds(List<Integer> ids) {
String placeholders = ids.stream()
.map(id -> "?")
.collect(Collectors.joining(","));
String sql = "UPDATE material SET is_deprecated = TRUE WHERE id IN ("+placeholders+")";
jdbcTemplate.update(sql, ids);
}
private static class MaterialMapper implements RowMapper<Material> { private static class MaterialMapper implements RowMapper<Material> {

View file

@ -1,5 +1,6 @@
package de.avatic.lcc.service.access; package de.avatic.lcc.service.access;
import de.avatic.lcc.dto.configuration.material.update.MaterialUpdateDTO;
import de.avatic.lcc.dto.configuration.material.view.MaterialDetailDTO; import de.avatic.lcc.dto.configuration.material.view.MaterialDetailDTO;
import de.avatic.lcc.dto.generic.MaterialDTO; import de.avatic.lcc.dto.generic.MaterialDTO;
import de.avatic.lcc.model.db.materials.Material; import de.avatic.lcc.model.db.materials.Material;
@ -8,10 +9,13 @@ import de.avatic.lcc.repositories.pagination.SearchQueryPagination;
import de.avatic.lcc.repositories.pagination.SearchQueryResult; import de.avatic.lcc.repositories.pagination.SearchQueryResult;
import de.avatic.lcc.service.transformer.generic.MaterialTransformer; import de.avatic.lcc.service.transformer.generic.MaterialTransformer;
import de.avatic.lcc.service.transformer.material.MaterialDetailTransformer; import de.avatic.lcc.service.transformer.material.MaterialDetailTransformer;
import de.avatic.lcc.service.transformer.material.MaterialUpdateDTOTransformer;
import de.avatic.lcc.util.exception.badrequest.MaterialNotFoundException; import de.avatic.lcc.util.exception.badrequest.MaterialNotFoundException;
import de.avatic.lcc.util.exception.badrequest.NotFoundException; import de.avatic.lcc.util.exception.badrequest.NotFoundException;
import jakarta.validation.constraints.Size;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional; import java.util.Optional;
/** /**
@ -25,6 +29,7 @@ public class MaterialService {
private final MaterialRepository materialRepository; private final MaterialRepository materialRepository;
private final MaterialTransformer materialTransformer; private final MaterialTransformer materialTransformer;
private final MaterialDetailTransformer materialDetailTransformer; private final MaterialDetailTransformer materialDetailTransformer;
private final MaterialUpdateDTOTransformer materialUpdateDTOTransformer;
/** /**
* Constructor for MaterialService. * Constructor for MaterialService.
@ -33,19 +38,20 @@ public class MaterialService {
* @param materialTransformer transformer to convert material entities to DTOs * @param materialTransformer transformer to convert material entities to DTOs
* @param materialDetailTransformer transformer to convert material entities to detail DTOs * @param materialDetailTransformer transformer to convert material entities to detail DTOs
*/ */
public MaterialService(MaterialRepository materialRepository, MaterialTransformer materialTransformer, MaterialDetailTransformer materialDetailTransformer) { public MaterialService(MaterialRepository materialRepository, MaterialTransformer materialTransformer, MaterialDetailTransformer materialDetailTransformer, MaterialUpdateDTOTransformer materialUpdateDTOTransformer) {
this.materialRepository = materialRepository; this.materialRepository = materialRepository;
this.materialTransformer = materialTransformer; this.materialTransformer = materialTransformer;
this.materialDetailTransformer = materialDetailTransformer; this.materialDetailTransformer = materialDetailTransformer;
this.materialUpdateDTOTransformer = materialUpdateDTOTransformer;
} }
/** /**
* Lists materials based on the provided criteria, including optional filtering, * Lists materials based on the provided criteria, including optional filtering,
* pagination, and exclusion of deprecated entries. * pagination, and exclusion of deprecated entries.
* *
* @param filter an optional filter to apply to the material list, such as a search term * @param filter an optional filter to apply to the material list, such as a search term
* @param page the page number for pagination * @param page the page number for pagination
* @param limit the maximum number of materials to include in the result * @param limit the maximum number of materials to include in the result
* @param excludeDeprecated a flag indicating whether to exclude deprecated materials * @param excludeDeprecated a flag indicating whether to exclude deprecated materials
* @return a {@code SearchQueryResult<MaterialDTO>} containing the paginated list of materials * @return a {@code SearchQueryResult<MaterialDTO>} containing the paginated list of materials
*/ */
@ -62,7 +68,20 @@ public class MaterialService {
* @throws MaterialNotFoundException if no material with the given ID is found * @throws MaterialNotFoundException if no material with the given ID is found
*/ */
public MaterialDetailDTO getMaterial(Integer id) { public MaterialDetailDTO getMaterial(Integer id) {
return materialDetailTransformer.toMaterialDetailDTO(materialRepository.getByIdIncludeDeprecated(id).orElseThrow(() -> new NotFoundException(NotFoundException.NotFoundType.MATERIAL,id.toString()))); return materialDetailTransformer.toMaterialDetailDTO(materialRepository.getByIdIncludeDeprecated(id).orElseThrow(() -> new NotFoundException(NotFoundException.NotFoundType.MATERIAL, id.toString())));
}
public void updateMaterial(List<@Size(max = 100, min = 1) MaterialUpdateDTO> dtos) {
var materials = dtos.stream().map(materialUpdateDTOTransformer::fromMaterialUpdateDTO).toList();
var toBeDeleted = materials.stream().filter(m -> m.getDeprecated() != null && m.getDeprecated()).map(Material::getId).toList();
var toBeUpdated = materials.stream().filter(m -> m.getDeprecated() != null && m.getDeprecated()).toList();
if (!toBeDeleted.isEmpty())
toBeDeleted.forEach(materialRepository::setDeprecatedById);
if (!toBeUpdated.isEmpty())
toBeUpdated.forEach(materialRepository::updateByPartNumber);
} }
/** /**

View file

@ -4,7 +4,7 @@ import de.avatic.lcc.dto.configuration.material.update.MaterialUpdateDTO;
import de.avatic.lcc.model.db.materials.Material; import de.avatic.lcc.model.db.materials.Material;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@Deprecated
@Service @Service
public class MaterialUpdateDTOTransformer { public class MaterialUpdateDTOTransformer {
@ -15,8 +15,7 @@ public class MaterialUpdateDTOTransformer {
entity.setNormalizedPartNumber(normalizePartNumber(dto.getPartNumber())); entity.setNormalizedPartNumber(normalizePartNumber(dto.getPartNumber()));
entity.setPartNumber(dto.getPartNumber()); entity.setPartNumber(dto.getPartNumber());
entity.setName(dto.getName()); entity.setName(dto.getName());
entity.setDeprecated(false); entity.setDeprecated(!dto.isDelete());
entity.setId(dto.getId());
entity.setHsCode(dto.getHsCode()); entity.setHsCode(dto.getHsCode());
return entity; return entity;