mirror of
https://github.com/s-frick/effigenix.git
synced 2026-03-28 10:19:35 +01:00
feat(masterdata): Infra-Layer für Supplier Aggregate
Liquibase-Migration (006), JPA-Entities (SupplierEntity, QualityCertificateEmbeddable), Mapper, Spring-Data-Repo, Domain-Repo-Adapter, Request-DTOs, SupplierController (9 Endpoints), ErrorMapper und UseCaseConfiguration erweitert.
This commit is contained in:
parent
8b2fd38192
commit
6ec07e7b34
16 changed files with 869 additions and 0 deletions
|
|
@ -3,6 +3,7 @@ package de.effigenix.infrastructure.config;
|
||||||
import de.effigenix.application.masterdata.*;
|
import de.effigenix.application.masterdata.*;
|
||||||
import de.effigenix.domain.masterdata.ArticleRepository;
|
import de.effigenix.domain.masterdata.ArticleRepository;
|
||||||
import de.effigenix.domain.masterdata.ProductCategoryRepository;
|
import de.effigenix.domain.masterdata.ProductCategoryRepository;
|
||||||
|
import de.effigenix.domain.masterdata.SupplierRepository;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
|
@ -90,4 +91,51 @@ public class MasterDataUseCaseConfiguration {
|
||||||
) {
|
) {
|
||||||
return new DeleteProductCategory(categoryRepository, articleRepository);
|
return new DeleteProductCategory(categoryRepository, articleRepository);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== Supplier Use Cases ====================
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public CreateSupplier createSupplier(SupplierRepository supplierRepository) {
|
||||||
|
return new CreateSupplier(supplierRepository);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public UpdateSupplier updateSupplier(SupplierRepository supplierRepository) {
|
||||||
|
return new UpdateSupplier(supplierRepository);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public GetSupplier getSupplier(SupplierRepository supplierRepository) {
|
||||||
|
return new GetSupplier(supplierRepository);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ListSuppliers listSuppliers(SupplierRepository supplierRepository) {
|
||||||
|
return new ListSuppliers(supplierRepository);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ActivateSupplier activateSupplier(SupplierRepository supplierRepository) {
|
||||||
|
return new ActivateSupplier(supplierRepository);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public DeactivateSupplier deactivateSupplier(SupplierRepository supplierRepository) {
|
||||||
|
return new DeactivateSupplier(supplierRepository);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public RateSupplier rateSupplier(SupplierRepository supplierRepository) {
|
||||||
|
return new RateSupplier(supplierRepository);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public AddCertificate addCertificate(SupplierRepository supplierRepository) {
|
||||||
|
return new AddCertificate(supplierRepository);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public RemoveCertificate removeCertificate(SupplierRepository supplierRepository) {
|
||||||
|
return new RemoveCertificate(supplierRepository);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
package de.effigenix.infrastructure.masterdata.persistence.entity;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Embeddable;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
@Embeddable
|
||||||
|
public class QualityCertificateEmbeddable {
|
||||||
|
|
||||||
|
@Column(name = "certificate_type", nullable = false, length = 100)
|
||||||
|
private String certificateType;
|
||||||
|
|
||||||
|
@Column(name = "issuer", length = 200)
|
||||||
|
private String issuer;
|
||||||
|
|
||||||
|
@Column(name = "valid_from")
|
||||||
|
private LocalDate validFrom;
|
||||||
|
|
||||||
|
@Column(name = "valid_until")
|
||||||
|
private LocalDate validUntil;
|
||||||
|
|
||||||
|
protected QualityCertificateEmbeddable() {}
|
||||||
|
|
||||||
|
public QualityCertificateEmbeddable(String certificateType, String issuer, LocalDate validFrom, LocalDate validUntil) {
|
||||||
|
this.certificateType = certificateType;
|
||||||
|
this.issuer = issuer;
|
||||||
|
this.validFrom = validFrom;
|
||||||
|
this.validUntil = validUntil;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCertificateType() { return certificateType; }
|
||||||
|
public String getIssuer() { return issuer; }
|
||||||
|
public LocalDate getValidFrom() { return validFrom; }
|
||||||
|
public LocalDate getValidUntil() { return validUntil; }
|
||||||
|
|
||||||
|
public void setCertificateType(String certificateType) { this.certificateType = certificateType; }
|
||||||
|
public void setIssuer(String issuer) { this.issuer = issuer; }
|
||||||
|
public void setValidFrom(LocalDate validFrom) { this.validFrom = validFrom; }
|
||||||
|
public void setValidUntil(LocalDate validUntil) { this.validUntil = validUntil; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,113 @@
|
||||||
|
package de.effigenix.infrastructure.masterdata.persistence.entity;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "suppliers")
|
||||||
|
public class SupplierEntity {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@Column(name = "id", nullable = false, length = 36)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@Column(name = "name", nullable = false, unique = true, length = 200)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Column(name = "phone", length = 50)
|
||||||
|
private String phone;
|
||||||
|
|
||||||
|
@Column(name = "email", length = 255)
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
@Column(name = "contact_person", length = 200)
|
||||||
|
private String contactPerson;
|
||||||
|
|
||||||
|
@Column(name = "street", length = 200)
|
||||||
|
private String street;
|
||||||
|
|
||||||
|
@Column(name = "house_number", length = 20)
|
||||||
|
private String houseNumber;
|
||||||
|
|
||||||
|
@Column(name = "postal_code", length = 20)
|
||||||
|
private String postalCode;
|
||||||
|
|
||||||
|
@Column(name = "city", length = 100)
|
||||||
|
private String city;
|
||||||
|
|
||||||
|
@Column(name = "country", length = 2)
|
||||||
|
private String country;
|
||||||
|
|
||||||
|
@Column(name = "payment_due_days")
|
||||||
|
private Integer paymentDueDays;
|
||||||
|
|
||||||
|
@Column(name = "payment_description", length = 500)
|
||||||
|
private String paymentDescription;
|
||||||
|
|
||||||
|
@Column(name = "quality_score")
|
||||||
|
private Integer qualityScore;
|
||||||
|
|
||||||
|
@Column(name = "delivery_score")
|
||||||
|
private Integer deliveryScore;
|
||||||
|
|
||||||
|
@Column(name = "price_score")
|
||||||
|
private Integer priceScore;
|
||||||
|
|
||||||
|
@Column(name = "status", nullable = false, length = 20)
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
@Column(name = "created_at", nullable = false, updatable = false)
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
|
||||||
|
@Column(name = "updated_at", nullable = false)
|
||||||
|
private LocalDateTime updatedAt;
|
||||||
|
|
||||||
|
@ElementCollection(fetch = FetchType.EAGER)
|
||||||
|
@CollectionTable(name = "quality_certificates", joinColumns = @JoinColumn(name = "supplier_id"))
|
||||||
|
private List<QualityCertificateEmbeddable> certificates = new ArrayList<>();
|
||||||
|
|
||||||
|
public SupplierEntity() {}
|
||||||
|
|
||||||
|
public String getId() { return id; }
|
||||||
|
public String getName() { return name; }
|
||||||
|
public String getPhone() { return phone; }
|
||||||
|
public String getEmail() { return email; }
|
||||||
|
public String getContactPerson() { return contactPerson; }
|
||||||
|
public String getStreet() { return street; }
|
||||||
|
public String getHouseNumber() { return houseNumber; }
|
||||||
|
public String getPostalCode() { return postalCode; }
|
||||||
|
public String getCity() { return city; }
|
||||||
|
public String getCountry() { return country; }
|
||||||
|
public Integer getPaymentDueDays() { return paymentDueDays; }
|
||||||
|
public String getPaymentDescription() { return paymentDescription; }
|
||||||
|
public Integer getQualityScore() { return qualityScore; }
|
||||||
|
public Integer getDeliveryScore() { return deliveryScore; }
|
||||||
|
public Integer getPriceScore() { return priceScore; }
|
||||||
|
public String getStatus() { return status; }
|
||||||
|
public LocalDateTime getCreatedAt() { return createdAt; }
|
||||||
|
public LocalDateTime getUpdatedAt() { return updatedAt; }
|
||||||
|
public List<QualityCertificateEmbeddable> getCertificates() { return certificates; }
|
||||||
|
|
||||||
|
public void setId(String id) { this.id = id; }
|
||||||
|
public void setName(String name) { this.name = name; }
|
||||||
|
public void setPhone(String phone) { this.phone = phone; }
|
||||||
|
public void setEmail(String email) { this.email = email; }
|
||||||
|
public void setContactPerson(String contactPerson) { this.contactPerson = contactPerson; }
|
||||||
|
public void setStreet(String street) { this.street = street; }
|
||||||
|
public void setHouseNumber(String houseNumber) { this.houseNumber = houseNumber; }
|
||||||
|
public void setPostalCode(String postalCode) { this.postalCode = postalCode; }
|
||||||
|
public void setCity(String city) { this.city = city; }
|
||||||
|
public void setCountry(String country) { this.country = country; }
|
||||||
|
public void setPaymentDueDays(Integer paymentDueDays) { this.paymentDueDays = paymentDueDays; }
|
||||||
|
public void setPaymentDescription(String paymentDescription) { this.paymentDescription = paymentDescription; }
|
||||||
|
public void setQualityScore(Integer qualityScore) { this.qualityScore = qualityScore; }
|
||||||
|
public void setDeliveryScore(Integer deliveryScore) { this.deliveryScore = deliveryScore; }
|
||||||
|
public void setPriceScore(Integer priceScore) { this.priceScore = priceScore; }
|
||||||
|
public void setStatus(String status) { this.status = status; }
|
||||||
|
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
|
||||||
|
public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
|
||||||
|
public void setCertificates(List<QualityCertificateEmbeddable> certificates) { this.certificates = certificates; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,107 @@
|
||||||
|
package de.effigenix.infrastructure.masterdata.persistence.mapper;
|
||||||
|
|
||||||
|
import de.effigenix.domain.masterdata.*;
|
||||||
|
import de.effigenix.infrastructure.masterdata.persistence.entity.QualityCertificateEmbeddable;
|
||||||
|
import de.effigenix.infrastructure.masterdata.persistence.entity.SupplierEntity;
|
||||||
|
import de.effigenix.shared.common.Address;
|
||||||
|
import de.effigenix.shared.common.ContactInfo;
|
||||||
|
import de.effigenix.shared.common.PaymentTerms;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class SupplierMapper {
|
||||||
|
|
||||||
|
public SupplierEntity toEntity(Supplier supplier) {
|
||||||
|
var entity = new SupplierEntity();
|
||||||
|
entity.setId(supplier.id().value());
|
||||||
|
entity.setName(supplier.name().value());
|
||||||
|
|
||||||
|
var contact = supplier.contactInfo();
|
||||||
|
if (contact != null) {
|
||||||
|
entity.setPhone(contact.phone());
|
||||||
|
entity.setEmail(contact.email());
|
||||||
|
entity.setContactPerson(contact.contactPerson());
|
||||||
|
}
|
||||||
|
|
||||||
|
var address = supplier.address();
|
||||||
|
if (address != null) {
|
||||||
|
entity.setStreet(address.street());
|
||||||
|
entity.setHouseNumber(address.houseNumber());
|
||||||
|
entity.setPostalCode(address.postalCode());
|
||||||
|
entity.setCity(address.city());
|
||||||
|
entity.setCountry(address.country());
|
||||||
|
}
|
||||||
|
|
||||||
|
var terms = supplier.paymentTerms();
|
||||||
|
if (terms != null) {
|
||||||
|
entity.setPaymentDueDays(terms.paymentDueDays());
|
||||||
|
entity.setPaymentDescription(terms.description());
|
||||||
|
}
|
||||||
|
|
||||||
|
var rating = supplier.rating();
|
||||||
|
if (rating != null) {
|
||||||
|
entity.setQualityScore(rating.qualityScore());
|
||||||
|
entity.setDeliveryScore(rating.deliveryScore());
|
||||||
|
entity.setPriceScore(rating.priceScore());
|
||||||
|
}
|
||||||
|
|
||||||
|
entity.setStatus(supplier.status().name());
|
||||||
|
entity.setCreatedAt(supplier.createdAt());
|
||||||
|
entity.setUpdatedAt(supplier.updatedAt());
|
||||||
|
|
||||||
|
List<QualityCertificateEmbeddable> certs = supplier.certificates().stream()
|
||||||
|
.map(c -> new QualityCertificateEmbeddable(
|
||||||
|
c.certificateType(), c.issuer(), c.validFrom(), c.validUntil()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
entity.setCertificates(certs);
|
||||||
|
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Supplier toDomain(SupplierEntity entity) {
|
||||||
|
Address address = null;
|
||||||
|
if (entity.getStreet() != null) {
|
||||||
|
address = new Address(
|
||||||
|
entity.getStreet(), entity.getHouseNumber(),
|
||||||
|
entity.getPostalCode(), entity.getCity(), entity.getCountry()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ContactInfo contactInfo = new ContactInfo(
|
||||||
|
entity.getPhone(), entity.getEmail(), entity.getContactPerson()
|
||||||
|
);
|
||||||
|
|
||||||
|
PaymentTerms paymentTerms = null;
|
||||||
|
if (entity.getPaymentDueDays() != null) {
|
||||||
|
paymentTerms = new PaymentTerms(entity.getPaymentDueDays(), entity.getPaymentDescription());
|
||||||
|
}
|
||||||
|
|
||||||
|
SupplierRating rating = null;
|
||||||
|
if (entity.getQualityScore() != null) {
|
||||||
|
rating = new SupplierRating(
|
||||||
|
entity.getQualityScore(), entity.getDeliveryScore(), entity.getPriceScore()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<QualityCertificate> certificates = entity.getCertificates().stream()
|
||||||
|
.map(c -> new QualityCertificate(
|
||||||
|
c.getCertificateType(), c.getIssuer(), c.getValidFrom(), c.getValidUntil()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
return Supplier.reconstitute(
|
||||||
|
SupplierId.of(entity.getId()),
|
||||||
|
new SupplierName(entity.getName()),
|
||||||
|
address,
|
||||||
|
contactInfo,
|
||||||
|
paymentTerms,
|
||||||
|
certificates,
|
||||||
|
rating,
|
||||||
|
SupplierStatus.valueOf(entity.getStatus()),
|
||||||
|
entity.getCreatedAt(),
|
||||||
|
entity.getUpdatedAt()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
package de.effigenix.infrastructure.masterdata.persistence.repository;
|
||||||
|
|
||||||
|
import de.effigenix.domain.masterdata.*;
|
||||||
|
import de.effigenix.infrastructure.masterdata.persistence.mapper.SupplierMapper;
|
||||||
|
import de.effigenix.shared.common.RepositoryError;
|
||||||
|
import de.effigenix.shared.common.Result;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public class JpaSupplierRepository implements SupplierRepository {
|
||||||
|
|
||||||
|
private final SupplierJpaRepository jpaRepository;
|
||||||
|
private final SupplierMapper mapper;
|
||||||
|
|
||||||
|
public JpaSupplierRepository(SupplierJpaRepository jpaRepository, SupplierMapper mapper) {
|
||||||
|
this.jpaRepository = jpaRepository;
|
||||||
|
this.mapper = mapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result<RepositoryError, Optional<Supplier>> findById(SupplierId id) {
|
||||||
|
try {
|
||||||
|
Optional<Supplier> result = jpaRepository.findById(id.value())
|
||||||
|
.map(mapper::toDomain);
|
||||||
|
return Result.success(result);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return Result.failure(new RepositoryError.DatabaseError(e.getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result<RepositoryError, List<Supplier>> findAll() {
|
||||||
|
try {
|
||||||
|
List<Supplier> result = jpaRepository.findAll().stream()
|
||||||
|
.map(mapper::toDomain)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
return Result.success(result);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return Result.failure(new RepositoryError.DatabaseError(e.getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result<RepositoryError, List<Supplier>> findByStatus(SupplierStatus status) {
|
||||||
|
try {
|
||||||
|
List<Supplier> result = jpaRepository.findByStatus(status.name()).stream()
|
||||||
|
.map(mapper::toDomain)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
return Result.success(result);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return Result.failure(new RepositoryError.DatabaseError(e.getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public Result<RepositoryError, Void> save(Supplier supplier) {
|
||||||
|
try {
|
||||||
|
jpaRepository.save(mapper.toEntity(supplier));
|
||||||
|
return Result.success(null);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return Result.failure(new RepositoryError.DatabaseError(e.getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public Result<RepositoryError, Void> delete(Supplier supplier) {
|
||||||
|
try {
|
||||||
|
jpaRepository.deleteById(supplier.id().value());
|
||||||
|
return Result.success(null);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return Result.failure(new RepositoryError.DatabaseError(e.getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result<RepositoryError, Boolean> existsByName(SupplierName name) {
|
||||||
|
try {
|
||||||
|
return Result.success(jpaRepository.existsByName(name.value()));
|
||||||
|
} catch (Exception e) {
|
||||||
|
return Result.failure(new RepositoryError.DatabaseError(e.getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
package de.effigenix.infrastructure.masterdata.persistence.repository;
|
||||||
|
|
||||||
|
import de.effigenix.infrastructure.masterdata.persistence.entity.SupplierEntity;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface SupplierJpaRepository extends JpaRepository<SupplierEntity, String> {
|
||||||
|
|
||||||
|
List<SupplierEntity> findByStatus(String status);
|
||||||
|
|
||||||
|
boolean existsByName(String name);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,283 @@
|
||||||
|
package de.effigenix.infrastructure.masterdata.web.controller;
|
||||||
|
|
||||||
|
import de.effigenix.application.masterdata.*;
|
||||||
|
import de.effigenix.application.masterdata.command.*;
|
||||||
|
import de.effigenix.domain.masterdata.Supplier;
|
||||||
|
import de.effigenix.domain.masterdata.SupplierError;
|
||||||
|
import de.effigenix.domain.masterdata.SupplierId;
|
||||||
|
import de.effigenix.domain.masterdata.SupplierStatus;
|
||||||
|
import de.effigenix.infrastructure.masterdata.web.dto.*;
|
||||||
|
import de.effigenix.shared.common.Result;
|
||||||
|
import de.effigenix.shared.security.ActorId;
|
||||||
|
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/suppliers")
|
||||||
|
@SecurityRequirement(name = "Bearer Authentication")
|
||||||
|
@Tag(name = "Suppliers", description = "Supplier management endpoints")
|
||||||
|
public class SupplierController {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(SupplierController.class);
|
||||||
|
|
||||||
|
private final CreateSupplier createSupplier;
|
||||||
|
private final UpdateSupplier updateSupplier;
|
||||||
|
private final GetSupplier getSupplier;
|
||||||
|
private final ListSuppliers listSuppliers;
|
||||||
|
private final ActivateSupplier activateSupplier;
|
||||||
|
private final DeactivateSupplier deactivateSupplier;
|
||||||
|
private final RateSupplier rateSupplier;
|
||||||
|
private final AddCertificate addCertificate;
|
||||||
|
private final RemoveCertificate removeCertificate;
|
||||||
|
|
||||||
|
public SupplierController(
|
||||||
|
CreateSupplier createSupplier,
|
||||||
|
UpdateSupplier updateSupplier,
|
||||||
|
GetSupplier getSupplier,
|
||||||
|
ListSuppliers listSuppliers,
|
||||||
|
ActivateSupplier activateSupplier,
|
||||||
|
DeactivateSupplier deactivateSupplier,
|
||||||
|
RateSupplier rateSupplier,
|
||||||
|
AddCertificate addCertificate,
|
||||||
|
RemoveCertificate removeCertificate
|
||||||
|
) {
|
||||||
|
this.createSupplier = createSupplier;
|
||||||
|
this.updateSupplier = updateSupplier;
|
||||||
|
this.getSupplier = getSupplier;
|
||||||
|
this.listSuppliers = listSuppliers;
|
||||||
|
this.activateSupplier = activateSupplier;
|
||||||
|
this.deactivateSupplier = deactivateSupplier;
|
||||||
|
this.rateSupplier = rateSupplier;
|
||||||
|
this.addCertificate = addCertificate;
|
||||||
|
this.removeCertificate = removeCertificate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
||||||
|
public ResponseEntity<Supplier> createSupplier(
|
||||||
|
@Valid @RequestBody CreateSupplierRequest request,
|
||||||
|
Authentication authentication
|
||||||
|
) {
|
||||||
|
var actorId = extractActorId(authentication);
|
||||||
|
logger.info("Creating supplier: {} by actor: {}", request.name(), actorId.value());
|
||||||
|
|
||||||
|
var cmd = new CreateSupplierCommand(
|
||||||
|
request.name(), request.phone(), request.email(), request.contactPerson(),
|
||||||
|
request.street(), request.houseNumber(), request.postalCode(),
|
||||||
|
request.city(), request.country(),
|
||||||
|
request.paymentDueDays(), request.paymentDescription()
|
||||||
|
);
|
||||||
|
var result = createSupplier.execute(cmd, actorId);
|
||||||
|
|
||||||
|
if (result.isFailure()) {
|
||||||
|
throw new SupplierDomainErrorException(result.unsafeGetError());
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("Supplier created: {}", request.name());
|
||||||
|
return ResponseEntity.status(HttpStatus.CREATED).body(result.unsafeGetValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public ResponseEntity<List<Supplier>> listSuppliers(
|
||||||
|
@RequestParam(value = "status", required = false) SupplierStatus status,
|
||||||
|
Authentication authentication
|
||||||
|
) {
|
||||||
|
var actorId = extractActorId(authentication);
|
||||||
|
logger.info("Listing suppliers by actor: {}", actorId.value());
|
||||||
|
|
||||||
|
Result<SupplierError, List<Supplier>> result;
|
||||||
|
if (status != null) {
|
||||||
|
result = listSuppliers.executeByStatus(status);
|
||||||
|
} else {
|
||||||
|
result = listSuppliers.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.isFailure()) {
|
||||||
|
throw new SupplierDomainErrorException(result.unsafeGetError());
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResponseEntity.ok(result.unsafeGetValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public ResponseEntity<Supplier> getSupplier(
|
||||||
|
@PathVariable("id") String supplierId,
|
||||||
|
Authentication authentication
|
||||||
|
) {
|
||||||
|
var actorId = extractActorId(authentication);
|
||||||
|
logger.info("Getting supplier: {} by actor: {}", supplierId, actorId.value());
|
||||||
|
|
||||||
|
var result = getSupplier.execute(SupplierId.of(supplierId));
|
||||||
|
|
||||||
|
if (result.isFailure()) {
|
||||||
|
throw new SupplierDomainErrorException(result.unsafeGetError());
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResponseEntity.ok(result.unsafeGetValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
||||||
|
public ResponseEntity<Supplier> updateSupplier(
|
||||||
|
@PathVariable("id") String supplierId,
|
||||||
|
@Valid @RequestBody UpdateSupplierRequest request,
|
||||||
|
Authentication authentication
|
||||||
|
) {
|
||||||
|
var actorId = extractActorId(authentication);
|
||||||
|
logger.info("Updating supplier: {} by actor: {}", supplierId, actorId.value());
|
||||||
|
|
||||||
|
var cmd = new UpdateSupplierCommand(
|
||||||
|
supplierId,
|
||||||
|
request.name(), request.phone(), request.email(), request.contactPerson(),
|
||||||
|
request.street(), request.houseNumber(), request.postalCode(),
|
||||||
|
request.city(), request.country(),
|
||||||
|
request.paymentDueDays(), request.paymentDescription()
|
||||||
|
);
|
||||||
|
var result = updateSupplier.execute(cmd, actorId);
|
||||||
|
|
||||||
|
if (result.isFailure()) {
|
||||||
|
throw new SupplierDomainErrorException(result.unsafeGetError());
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("Supplier updated: {}", supplierId);
|
||||||
|
return ResponseEntity.ok(result.unsafeGetValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{id}/activate")
|
||||||
|
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
||||||
|
public ResponseEntity<Supplier> activate(
|
||||||
|
@PathVariable("id") String supplierId,
|
||||||
|
Authentication authentication
|
||||||
|
) {
|
||||||
|
var actorId = extractActorId(authentication);
|
||||||
|
logger.info("Activating supplier: {} by actor: {}", supplierId, actorId.value());
|
||||||
|
|
||||||
|
var result = activateSupplier.execute(SupplierId.of(supplierId), actorId);
|
||||||
|
|
||||||
|
if (result.isFailure()) {
|
||||||
|
throw new SupplierDomainErrorException(result.unsafeGetError());
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("Supplier activated: {}", supplierId);
|
||||||
|
return ResponseEntity.ok(result.unsafeGetValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{id}/deactivate")
|
||||||
|
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
||||||
|
public ResponseEntity<Supplier> deactivate(
|
||||||
|
@PathVariable("id") String supplierId,
|
||||||
|
Authentication authentication
|
||||||
|
) {
|
||||||
|
var actorId = extractActorId(authentication);
|
||||||
|
logger.info("Deactivating supplier: {} by actor: {}", supplierId, actorId.value());
|
||||||
|
|
||||||
|
var result = deactivateSupplier.execute(SupplierId.of(supplierId), actorId);
|
||||||
|
|
||||||
|
if (result.isFailure()) {
|
||||||
|
throw new SupplierDomainErrorException(result.unsafeGetError());
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("Supplier deactivated: {}", supplierId);
|
||||||
|
return ResponseEntity.ok(result.unsafeGetValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{id}/rating")
|
||||||
|
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
||||||
|
public ResponseEntity<Supplier> rateSupplier(
|
||||||
|
@PathVariable("id") String supplierId,
|
||||||
|
@Valid @RequestBody RateSupplierRequest request,
|
||||||
|
Authentication authentication
|
||||||
|
) {
|
||||||
|
var actorId = extractActorId(authentication);
|
||||||
|
logger.info("Rating supplier: {} by actor: {}", supplierId, actorId.value());
|
||||||
|
|
||||||
|
var cmd = new RateSupplierCommand(
|
||||||
|
supplierId, request.qualityScore(), request.deliveryScore(), request.priceScore()
|
||||||
|
);
|
||||||
|
var result = rateSupplier.execute(cmd, actorId);
|
||||||
|
|
||||||
|
if (result.isFailure()) {
|
||||||
|
throw new SupplierDomainErrorException(result.unsafeGetError());
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("Supplier rated: {}", supplierId);
|
||||||
|
return ResponseEntity.ok(result.unsafeGetValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{id}/certificates")
|
||||||
|
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
||||||
|
public ResponseEntity<Supplier> addCertificate(
|
||||||
|
@PathVariable("id") String supplierId,
|
||||||
|
@Valid @RequestBody AddCertificateRequest request,
|
||||||
|
Authentication authentication
|
||||||
|
) {
|
||||||
|
var actorId = extractActorId(authentication);
|
||||||
|
logger.info("Adding certificate to supplier: {} by actor: {}", supplierId, actorId.value());
|
||||||
|
|
||||||
|
var cmd = new AddCertificateCommand(
|
||||||
|
supplierId, request.certificateType(), request.issuer(),
|
||||||
|
request.validFrom(), request.validUntil()
|
||||||
|
);
|
||||||
|
var result = addCertificate.execute(cmd, actorId);
|
||||||
|
|
||||||
|
if (result.isFailure()) {
|
||||||
|
throw new SupplierDomainErrorException(result.unsafeGetError());
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("Certificate added to supplier: {}", supplierId);
|
||||||
|
return ResponseEntity.status(HttpStatus.CREATED).body(result.unsafeGetValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}/certificates")
|
||||||
|
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
||||||
|
public ResponseEntity<Void> removeCertificate(
|
||||||
|
@PathVariable("id") String supplierId,
|
||||||
|
@Valid @RequestBody RemoveCertificateRequest request,
|
||||||
|
Authentication authentication
|
||||||
|
) {
|
||||||
|
var actorId = extractActorId(authentication);
|
||||||
|
logger.info("Removing certificate from supplier: {} by actor: {}", supplierId, actorId.value());
|
||||||
|
|
||||||
|
var cmd = new RemoveCertificateCommand(
|
||||||
|
supplierId, request.certificateType(), request.issuer(), request.validFrom()
|
||||||
|
);
|
||||||
|
var result = removeCertificate.execute(cmd, actorId);
|
||||||
|
|
||||||
|
if (result.isFailure()) {
|
||||||
|
throw new SupplierDomainErrorException(result.unsafeGetError());
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("Certificate removed from supplier: {}", supplierId);
|
||||||
|
return ResponseEntity.noContent().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ActorId extractActorId(Authentication authentication) {
|
||||||
|
if (authentication == null || authentication.getName() == null) {
|
||||||
|
throw new IllegalStateException("No authentication found in SecurityContext");
|
||||||
|
}
|
||||||
|
return ActorId.of(authentication.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SupplierDomainErrorException extends RuntimeException {
|
||||||
|
private final SupplierError error;
|
||||||
|
|
||||||
|
public SupplierDomainErrorException(SupplierError error) {
|
||||||
|
super(error.message());
|
||||||
|
this.error = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SupplierError getError() {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
package de.effigenix.infrastructure.masterdata.web.dto;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
public record AddCertificateRequest(
|
||||||
|
@NotBlank String certificateType,
|
||||||
|
String issuer,
|
||||||
|
LocalDate validFrom,
|
||||||
|
LocalDate validUntil
|
||||||
|
) {}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package de.effigenix.infrastructure.masterdata.web.dto;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
|
||||||
|
public record CreateSupplierRequest(
|
||||||
|
@NotBlank String name,
|
||||||
|
@NotBlank String phone,
|
||||||
|
String email,
|
||||||
|
String contactPerson,
|
||||||
|
String street,
|
||||||
|
String houseNumber,
|
||||||
|
String postalCode,
|
||||||
|
String city,
|
||||||
|
String country,
|
||||||
|
Integer paymentDueDays,
|
||||||
|
String paymentDescription
|
||||||
|
) {}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
package de.effigenix.infrastructure.masterdata.web.dto;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.Max;
|
||||||
|
import jakarta.validation.constraints.Min;
|
||||||
|
|
||||||
|
public record RateSupplierRequest(
|
||||||
|
@Min(1) @Max(5) int qualityScore,
|
||||||
|
@Min(1) @Max(5) int deliveryScore,
|
||||||
|
@Min(1) @Max(5) int priceScore
|
||||||
|
) {}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
package de.effigenix.infrastructure.masterdata.web.dto;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
public record RemoveCertificateRequest(
|
||||||
|
@NotBlank String certificateType,
|
||||||
|
String issuer,
|
||||||
|
LocalDate validFrom
|
||||||
|
) {}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
package de.effigenix.infrastructure.masterdata.web.dto;
|
||||||
|
|
||||||
|
public record UpdateSupplierRequest(
|
||||||
|
String name,
|
||||||
|
String phone,
|
||||||
|
String email,
|
||||||
|
String contactPerson,
|
||||||
|
String street,
|
||||||
|
String houseNumber,
|
||||||
|
String postalCode,
|
||||||
|
String city,
|
||||||
|
String country,
|
||||||
|
Integer paymentDueDays,
|
||||||
|
String paymentDescription
|
||||||
|
) {}
|
||||||
|
|
@ -2,6 +2,7 @@ package de.effigenix.infrastructure.masterdata.web.exception;
|
||||||
|
|
||||||
import de.effigenix.domain.masterdata.ArticleError;
|
import de.effigenix.domain.masterdata.ArticleError;
|
||||||
import de.effigenix.domain.masterdata.ProductCategoryError;
|
import de.effigenix.domain.masterdata.ProductCategoryError;
|
||||||
|
import de.effigenix.domain.masterdata.SupplierError;
|
||||||
|
|
||||||
public final class MasterDataErrorHttpStatusMapper {
|
public final class MasterDataErrorHttpStatusMapper {
|
||||||
|
|
||||||
|
|
@ -22,6 +23,18 @@ public final class MasterDataErrorHttpStatusMapper {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int toHttpStatus(SupplierError error) {
|
||||||
|
return switch (error) {
|
||||||
|
case SupplierError.SupplierNotFound e -> 404;
|
||||||
|
case SupplierError.CertificateNotFound e -> 404;
|
||||||
|
case SupplierError.SupplierNameAlreadyExists e -> 409;
|
||||||
|
case SupplierError.InvalidRating e -> 400;
|
||||||
|
case SupplierError.ValidationFailure e -> 400;
|
||||||
|
case SupplierError.Unauthorized e -> 403;
|
||||||
|
case SupplierError.RepositoryFailure e -> 500;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public static int toHttpStatus(ProductCategoryError error) {
|
public static int toHttpStatus(ProductCategoryError error) {
|
||||||
return switch (error) {
|
return switch (error) {
|
||||||
case ProductCategoryError.CategoryNotFound e -> 404;
|
case ProductCategoryError.CategoryNotFound e -> 404;
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,11 @@ package de.effigenix.infrastructure.usermanagement.web.exception;
|
||||||
|
|
||||||
import de.effigenix.domain.masterdata.ArticleError;
|
import de.effigenix.domain.masterdata.ArticleError;
|
||||||
import de.effigenix.domain.masterdata.ProductCategoryError;
|
import de.effigenix.domain.masterdata.ProductCategoryError;
|
||||||
|
import de.effigenix.domain.masterdata.SupplierError;
|
||||||
import de.effigenix.domain.usermanagement.UserError;
|
import de.effigenix.domain.usermanagement.UserError;
|
||||||
import de.effigenix.infrastructure.masterdata.web.controller.ArticleController;
|
import de.effigenix.infrastructure.masterdata.web.controller.ArticleController;
|
||||||
import de.effigenix.infrastructure.masterdata.web.controller.ProductCategoryController;
|
import de.effigenix.infrastructure.masterdata.web.controller.ProductCategoryController;
|
||||||
|
import de.effigenix.infrastructure.masterdata.web.controller.SupplierController;
|
||||||
import de.effigenix.infrastructure.masterdata.web.exception.MasterDataErrorHttpStatusMapper;
|
import de.effigenix.infrastructure.masterdata.web.exception.MasterDataErrorHttpStatusMapper;
|
||||||
import de.effigenix.infrastructure.usermanagement.web.controller.AuthController;
|
import de.effigenix.infrastructure.usermanagement.web.controller.AuthController;
|
||||||
import de.effigenix.infrastructure.usermanagement.web.controller.UserController;
|
import de.effigenix.infrastructure.usermanagement.web.controller.UserController;
|
||||||
|
|
@ -135,6 +137,25 @@ public class GlobalExceptionHandler {
|
||||||
return ResponseEntity.status(status).body(errorResponse);
|
return ResponseEntity.status(status).body(errorResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(SupplierController.SupplierDomainErrorException.class)
|
||||||
|
public ResponseEntity<ErrorResponse> handleSupplierDomainError(
|
||||||
|
SupplierController.SupplierDomainErrorException ex,
|
||||||
|
HttpServletRequest request
|
||||||
|
) {
|
||||||
|
SupplierError error = ex.getError();
|
||||||
|
int status = MasterDataErrorHttpStatusMapper.toHttpStatus(error);
|
||||||
|
logger.warn("Supplier domain error: {} - {}", error.code(), error.message());
|
||||||
|
|
||||||
|
ErrorResponse errorResponse = ErrorResponse.from(
|
||||||
|
error.code(),
|
||||||
|
error.message(),
|
||||||
|
status,
|
||||||
|
request.getRequestURI()
|
||||||
|
);
|
||||||
|
|
||||||
|
return ResponseEntity.status(status).body(errorResponse);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles validation errors from @Valid annotations.
|
* Handles validation errors from @Valid annotations.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<databaseChangeLog
|
||||||
|
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
|
||||||
|
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
|
||||||
|
|
||||||
|
<changeSet id="006-create-suppliers-table" author="effigenix">
|
||||||
|
<createTable tableName="suppliers">
|
||||||
|
<column name="id" type="VARCHAR(36)">
|
||||||
|
<constraints primaryKey="true" nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="name" type="VARCHAR(200)">
|
||||||
|
<constraints nullable="false" unique="true"/>
|
||||||
|
</column>
|
||||||
|
<column name="phone" type="VARCHAR(50)"/>
|
||||||
|
<column name="email" type="VARCHAR(255)"/>
|
||||||
|
<column name="contact_person" type="VARCHAR(200)"/>
|
||||||
|
<column name="street" type="VARCHAR(200)"/>
|
||||||
|
<column name="house_number" type="VARCHAR(20)"/>
|
||||||
|
<column name="postal_code" type="VARCHAR(20)"/>
|
||||||
|
<column name="city" type="VARCHAR(100)"/>
|
||||||
|
<column name="country" type="VARCHAR(2)"/>
|
||||||
|
<column name="payment_due_days" type="INT"/>
|
||||||
|
<column name="payment_description" type="VARCHAR(500)"/>
|
||||||
|
<column name="quality_score" type="INT"/>
|
||||||
|
<column name="delivery_score" type="INT"/>
|
||||||
|
<column name="price_score" type="INT"/>
|
||||||
|
<column name="status" type="VARCHAR(20)" defaultValue="ACTIVE">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="created_at" type="TIMESTAMP" defaultValueComputed="CURRENT_TIMESTAMP">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="updated_at" type="TIMESTAMP" defaultValueComputed="CURRENT_TIMESTAMP">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
</createTable>
|
||||||
|
<sql>
|
||||||
|
ALTER TABLE suppliers ADD CONSTRAINT chk_supplier_status CHECK (status IN ('ACTIVE', 'INACTIVE'));
|
||||||
|
</sql>
|
||||||
|
<createIndex tableName="suppliers" indexName="idx_suppliers_name">
|
||||||
|
<column name="name"/>
|
||||||
|
</createIndex>
|
||||||
|
<createIndex tableName="suppliers" indexName="idx_suppliers_status">
|
||||||
|
<column name="status"/>
|
||||||
|
</createIndex>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
<changeSet id="006-create-quality-certificates-table" author="effigenix">
|
||||||
|
<createTable tableName="quality_certificates">
|
||||||
|
<column name="supplier_id" type="VARCHAR(36)">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="certificate_type" type="VARCHAR(100)">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="issuer" type="VARCHAR(200)"/>
|
||||||
|
<column name="valid_from" type="DATE"/>
|
||||||
|
<column name="valid_until" type="DATE"/>
|
||||||
|
</createTable>
|
||||||
|
<addPrimaryKey tableName="quality_certificates"
|
||||||
|
columnNames="supplier_id, certificate_type, valid_from"
|
||||||
|
constraintName="pk_quality_certificates"/>
|
||||||
|
<addForeignKeyConstraint baseTableName="quality_certificates" baseColumnNames="supplier_id"
|
||||||
|
referencedTableName="suppliers" referencedColumnNames="id"
|
||||||
|
constraintName="fk_quality_certificates_supplier" onDelete="CASCADE"/>
|
||||||
|
<createIndex tableName="quality_certificates" indexName="idx_quality_certificates_supplier_id">
|
||||||
|
<column name="supplier_id"/>
|
||||||
|
</createIndex>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
</databaseChangeLog>
|
||||||
|
|
@ -10,5 +10,6 @@
|
||||||
<include file="db/changelog/changes/003-create-audit-logs-table.xml"/>
|
<include file="db/changelog/changes/003-create-audit-logs-table.xml"/>
|
||||||
<include file="db/changelog/changes/004-seed-admin-user.xml"/>
|
<include file="db/changelog/changes/004-seed-admin-user.xml"/>
|
||||||
<include file="db/changelog/changes/005-create-masterdata-schema.xml"/>
|
<include file="db/changelog/changes/005-create-masterdata-schema.xml"/>
|
||||||
|
<include file="db/changelog/changes/006-create-supplier-schema.xml"/>
|
||||||
|
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue