mirror of
https://github.com/s-frick/effigenix.git
synced 2026-03-28 11:59:35 +01:00
fix(masterdata): MASTERDATA-Permissions und JSON-Serialisierung der REST-Responses
MASTERDATA_READ/WRITE fehlten im Permission-Enum und in den Rollen-Seed-Daten, dadurch bekam der Admin bei allen Stammdaten-Schreiboperationen Access Denied. Die Masterdata-Controller gaben Domain-Objekte direkt als JSON zurück, die von Jackson nicht serialisiert werden konnten (method-style Accessors statt JavaBean- Getter). Response-DTOs als Records eingeführt, die Domain-Objekte in flache JSON-Strukturen mappen. Frontend-Mapping-Layer entfernt, da Backend-Responses jetzt 1:1 die erwarteten Feldnamen liefern.
This commit is contained in:
parent
3cccab1f4d
commit
fbed3f899f
26 changed files with 481 additions and 364 deletions
|
|
@ -79,6 +79,10 @@ public enum Permission {
|
||||||
LABEL_WRITE,
|
LABEL_WRITE,
|
||||||
LABEL_PRINT,
|
LABEL_PRINT,
|
||||||
|
|
||||||
|
// ==================== Master Data BC ====================
|
||||||
|
MASTERDATA_READ,
|
||||||
|
MASTERDATA_WRITE,
|
||||||
|
|
||||||
// ==================== Filiales BC ====================
|
// ==================== Filiales BC ====================
|
||||||
BRANCH_READ,
|
BRANCH_READ,
|
||||||
BRANCH_WRITE,
|
BRANCH_WRITE,
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,6 @@ import de.effigenix.domain.masterdata.ArticleError;
|
||||||
import de.effigenix.domain.masterdata.ArticleId;
|
import de.effigenix.domain.masterdata.ArticleId;
|
||||||
import de.effigenix.domain.masterdata.ArticleStatus;
|
import de.effigenix.domain.masterdata.ArticleStatus;
|
||||||
import de.effigenix.domain.masterdata.ProductCategoryId;
|
import de.effigenix.domain.masterdata.ProductCategoryId;
|
||||||
import de.effigenix.domain.masterdata.SalesUnitId;
|
|
||||||
import de.effigenix.domain.masterdata.SupplierId;
|
|
||||||
import de.effigenix.infrastructure.masterdata.web.dto.*;
|
import de.effigenix.infrastructure.masterdata.web.dto.*;
|
||||||
import de.effigenix.shared.common.Result;
|
import de.effigenix.shared.common.Result;
|
||||||
import de.effigenix.shared.security.ActorId;
|
import de.effigenix.shared.security.ActorId;
|
||||||
|
|
@ -73,7 +71,7 @@ public class ArticleController {
|
||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
||||||
public ResponseEntity<Article> createArticle(
|
public ResponseEntity<ArticleResponse> createArticle(
|
||||||
@Valid @RequestBody CreateArticleRequest request,
|
@Valid @RequestBody CreateArticleRequest request,
|
||||||
Authentication authentication
|
Authentication authentication
|
||||||
) {
|
) {
|
||||||
|
|
@ -91,11 +89,11 @@ public class ArticleController {
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Article created: {}", request.articleNumber());
|
logger.info("Article created: {}", request.articleNumber());
|
||||||
return ResponseEntity.status(HttpStatus.CREATED).body(result.unsafeGetValue());
|
return ResponseEntity.status(HttpStatus.CREATED).body(ArticleResponse.from(result.unsafeGetValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public ResponseEntity<List<Article>> listArticles(
|
public ResponseEntity<List<ArticleResponse>> listArticles(
|
||||||
@RequestParam(value = "categoryId", required = false) String categoryId,
|
@RequestParam(value = "categoryId", required = false) String categoryId,
|
||||||
@RequestParam(value = "status", required = false) ArticleStatus status,
|
@RequestParam(value = "status", required = false) ArticleStatus status,
|
||||||
Authentication authentication
|
Authentication authentication
|
||||||
|
|
@ -116,11 +114,12 @@ public class ArticleController {
|
||||||
throw new ArticleDomainErrorException(result.unsafeGetError());
|
throw new ArticleDomainErrorException(result.unsafeGetError());
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResponseEntity.ok(result.unsafeGetValue());
|
var response = result.unsafeGetValue().stream().map(ArticleResponse::from).toList();
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/{id}")
|
@GetMapping("/{id}")
|
||||||
public ResponseEntity<Article> getArticle(
|
public ResponseEntity<ArticleResponse> getArticle(
|
||||||
@PathVariable("id") String articleId,
|
@PathVariable("id") String articleId,
|
||||||
Authentication authentication
|
Authentication authentication
|
||||||
) {
|
) {
|
||||||
|
|
@ -133,12 +132,12 @@ public class ArticleController {
|
||||||
throw new ArticleDomainErrorException(result.unsafeGetError());
|
throw new ArticleDomainErrorException(result.unsafeGetError());
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResponseEntity.ok(result.unsafeGetValue());
|
return ResponseEntity.ok(ArticleResponse.from(result.unsafeGetValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/{id}")
|
@PutMapping("/{id}")
|
||||||
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
||||||
public ResponseEntity<Article> updateArticle(
|
public ResponseEntity<ArticleResponse> updateArticle(
|
||||||
@PathVariable("id") String articleId,
|
@PathVariable("id") String articleId,
|
||||||
@Valid @RequestBody UpdateArticleRequest request,
|
@Valid @RequestBody UpdateArticleRequest request,
|
||||||
Authentication authentication
|
Authentication authentication
|
||||||
|
|
@ -154,12 +153,12 @@ public class ArticleController {
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Article updated: {}", articleId);
|
logger.info("Article updated: {}", articleId);
|
||||||
return ResponseEntity.ok(result.unsafeGetValue());
|
return ResponseEntity.ok(ArticleResponse.from(result.unsafeGetValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{id}/activate")
|
@PostMapping("/{id}/activate")
|
||||||
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
||||||
public ResponseEntity<Article> activate(
|
public ResponseEntity<ArticleResponse> activate(
|
||||||
@PathVariable("id") String articleId,
|
@PathVariable("id") String articleId,
|
||||||
Authentication authentication
|
Authentication authentication
|
||||||
) {
|
) {
|
||||||
|
|
@ -173,12 +172,12 @@ public class ArticleController {
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Article activated: {}", articleId);
|
logger.info("Article activated: {}", articleId);
|
||||||
return ResponseEntity.ok(result.unsafeGetValue());
|
return ResponseEntity.ok(ArticleResponse.from(result.unsafeGetValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{id}/deactivate")
|
@PostMapping("/{id}/deactivate")
|
||||||
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
||||||
public ResponseEntity<Article> deactivate(
|
public ResponseEntity<ArticleResponse> deactivate(
|
||||||
@PathVariable("id") String articleId,
|
@PathVariable("id") String articleId,
|
||||||
Authentication authentication
|
Authentication authentication
|
||||||
) {
|
) {
|
||||||
|
|
@ -192,12 +191,12 @@ public class ArticleController {
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Article deactivated: {}", articleId);
|
logger.info("Article deactivated: {}", articleId);
|
||||||
return ResponseEntity.ok(result.unsafeGetValue());
|
return ResponseEntity.ok(ArticleResponse.from(result.unsafeGetValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{id}/sales-units")
|
@PostMapping("/{id}/sales-units")
|
||||||
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
||||||
public ResponseEntity<Article> addSalesUnit(
|
public ResponseEntity<ArticleResponse> addSalesUnit(
|
||||||
@PathVariable("id") String articleId,
|
@PathVariable("id") String articleId,
|
||||||
@Valid @RequestBody AddSalesUnitRequest request,
|
@Valid @RequestBody AddSalesUnitRequest request,
|
||||||
Authentication authentication
|
Authentication authentication
|
||||||
|
|
@ -213,7 +212,7 @@ public class ArticleController {
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Sales unit added to article: {}", articleId);
|
logger.info("Sales unit added to article: {}", articleId);
|
||||||
return ResponseEntity.status(HttpStatus.CREATED).body(result.unsafeGetValue());
|
return ResponseEntity.status(HttpStatus.CREATED).body(ArticleResponse.from(result.unsafeGetValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/{id}/sales-units/{suId}")
|
@DeleteMapping("/{id}/sales-units/{suId}")
|
||||||
|
|
@ -239,7 +238,7 @@ public class ArticleController {
|
||||||
|
|
||||||
@PutMapping("/{id}/sales-units/{suId}/price")
|
@PutMapping("/{id}/sales-units/{suId}/price")
|
||||||
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
||||||
public ResponseEntity<Article> updateSalesUnitPrice(
|
public ResponseEntity<ArticleResponse> updateSalesUnitPrice(
|
||||||
@PathVariable("id") String articleId,
|
@PathVariable("id") String articleId,
|
||||||
@PathVariable("suId") String salesUnitId,
|
@PathVariable("suId") String salesUnitId,
|
||||||
@Valid @RequestBody UpdateSalesUnitPriceRequest request,
|
@Valid @RequestBody UpdateSalesUnitPriceRequest request,
|
||||||
|
|
@ -256,12 +255,12 @@ public class ArticleController {
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Sales unit price updated in article: {}", articleId);
|
logger.info("Sales unit price updated in article: {}", articleId);
|
||||||
return ResponseEntity.ok(result.unsafeGetValue());
|
return ResponseEntity.ok(ArticleResponse.from(result.unsafeGetValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{id}/suppliers")
|
@PostMapping("/{id}/suppliers")
|
||||||
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
||||||
public ResponseEntity<Article> assignSupplier(
|
public ResponseEntity<ArticleResponse> assignSupplier(
|
||||||
@PathVariable("id") String articleId,
|
@PathVariable("id") String articleId,
|
||||||
@Valid @RequestBody AssignSupplierRequest request,
|
@Valid @RequestBody AssignSupplierRequest request,
|
||||||
Authentication authentication
|
Authentication authentication
|
||||||
|
|
@ -277,7 +276,7 @@ public class ArticleController {
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Supplier assigned to article: {}", articleId);
|
logger.info("Supplier assigned to article: {}", articleId);
|
||||||
return ResponseEntity.ok(result.unsafeGetValue());
|
return ResponseEntity.ok(ArticleResponse.from(result.unsafeGetValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/{id}/suppliers/{supplierId}")
|
@DeleteMapping("/{id}/suppliers/{supplierId}")
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ public class CustomerController {
|
||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
||||||
public ResponseEntity<Customer> createCustomer(
|
public ResponseEntity<CustomerResponse> createCustomer(
|
||||||
@Valid @RequestBody CreateCustomerRequest request,
|
@Valid @RequestBody CreateCustomerRequest request,
|
||||||
Authentication authentication
|
Authentication authentication
|
||||||
) {
|
) {
|
||||||
|
|
@ -93,11 +93,11 @@ public class CustomerController {
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Customer created: {}", request.name());
|
logger.info("Customer created: {}", request.name());
|
||||||
return ResponseEntity.status(HttpStatus.CREATED).body(result.unsafeGetValue());
|
return ResponseEntity.status(HttpStatus.CREATED).body(CustomerResponse.from(result.unsafeGetValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public ResponseEntity<List<Customer>> listCustomers(
|
public ResponseEntity<List<CustomerResponse>> listCustomers(
|
||||||
@RequestParam(value = "type", required = false) CustomerType type,
|
@RequestParam(value = "type", required = false) CustomerType type,
|
||||||
@RequestParam(value = "status", required = false) CustomerStatus status,
|
@RequestParam(value = "status", required = false) CustomerStatus status,
|
||||||
Authentication authentication
|
Authentication authentication
|
||||||
|
|
@ -118,11 +118,12 @@ public class CustomerController {
|
||||||
throw new CustomerDomainErrorException(result.unsafeGetError());
|
throw new CustomerDomainErrorException(result.unsafeGetError());
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResponseEntity.ok(result.unsafeGetValue());
|
var response = result.unsafeGetValue().stream().map(CustomerResponse::from).toList();
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/{id}")
|
@GetMapping("/{id}")
|
||||||
public ResponseEntity<Customer> getCustomer(
|
public ResponseEntity<CustomerResponse> getCustomer(
|
||||||
@PathVariable("id") String customerId,
|
@PathVariable("id") String customerId,
|
||||||
Authentication authentication
|
Authentication authentication
|
||||||
) {
|
) {
|
||||||
|
|
@ -135,12 +136,12 @@ public class CustomerController {
|
||||||
throw new CustomerDomainErrorException(result.unsafeGetError());
|
throw new CustomerDomainErrorException(result.unsafeGetError());
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResponseEntity.ok(result.unsafeGetValue());
|
return ResponseEntity.ok(CustomerResponse.from(result.unsafeGetValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/{id}")
|
@PutMapping("/{id}")
|
||||||
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
||||||
public ResponseEntity<Customer> updateCustomer(
|
public ResponseEntity<CustomerResponse> updateCustomer(
|
||||||
@PathVariable("id") String customerId,
|
@PathVariable("id") String customerId,
|
||||||
@Valid @RequestBody UpdateCustomerRequest request,
|
@Valid @RequestBody UpdateCustomerRequest request,
|
||||||
Authentication authentication
|
Authentication authentication
|
||||||
|
|
@ -163,12 +164,12 @@ public class CustomerController {
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Customer updated: {}", customerId);
|
logger.info("Customer updated: {}", customerId);
|
||||||
return ResponseEntity.ok(result.unsafeGetValue());
|
return ResponseEntity.ok(CustomerResponse.from(result.unsafeGetValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{id}/activate")
|
@PostMapping("/{id}/activate")
|
||||||
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
||||||
public ResponseEntity<Customer> activate(
|
public ResponseEntity<CustomerResponse> activate(
|
||||||
@PathVariable("id") String customerId,
|
@PathVariable("id") String customerId,
|
||||||
Authentication authentication
|
Authentication authentication
|
||||||
) {
|
) {
|
||||||
|
|
@ -182,12 +183,12 @@ public class CustomerController {
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Customer activated: {}", customerId);
|
logger.info("Customer activated: {}", customerId);
|
||||||
return ResponseEntity.ok(result.unsafeGetValue());
|
return ResponseEntity.ok(CustomerResponse.from(result.unsafeGetValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{id}/deactivate")
|
@PostMapping("/{id}/deactivate")
|
||||||
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
||||||
public ResponseEntity<Customer> deactivate(
|
public ResponseEntity<CustomerResponse> deactivate(
|
||||||
@PathVariable("id") String customerId,
|
@PathVariable("id") String customerId,
|
||||||
Authentication authentication
|
Authentication authentication
|
||||||
) {
|
) {
|
||||||
|
|
@ -201,12 +202,12 @@ public class CustomerController {
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Customer deactivated: {}", customerId);
|
logger.info("Customer deactivated: {}", customerId);
|
||||||
return ResponseEntity.ok(result.unsafeGetValue());
|
return ResponseEntity.ok(CustomerResponse.from(result.unsafeGetValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{id}/delivery-addresses")
|
@PostMapping("/{id}/delivery-addresses")
|
||||||
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
||||||
public ResponseEntity<Customer> addDeliveryAddress(
|
public ResponseEntity<CustomerResponse> addDeliveryAddress(
|
||||||
@PathVariable("id") String customerId,
|
@PathVariable("id") String customerId,
|
||||||
@Valid @RequestBody AddDeliveryAddressRequest request,
|
@Valid @RequestBody AddDeliveryAddressRequest request,
|
||||||
Authentication authentication
|
Authentication authentication
|
||||||
|
|
@ -227,7 +228,7 @@ public class CustomerController {
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Delivery address added to customer: {}", customerId);
|
logger.info("Delivery address added to customer: {}", customerId);
|
||||||
return ResponseEntity.status(HttpStatus.CREATED).body(result.unsafeGetValue());
|
return ResponseEntity.status(HttpStatus.CREATED).body(CustomerResponse.from(result.unsafeGetValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/{id}/delivery-addresses/{label}")
|
@DeleteMapping("/{id}/delivery-addresses/{label}")
|
||||||
|
|
@ -253,7 +254,7 @@ public class CustomerController {
|
||||||
|
|
||||||
@PutMapping("/{id}/frame-contract")
|
@PutMapping("/{id}/frame-contract")
|
||||||
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
||||||
public ResponseEntity<Customer> setFrameContract(
|
public ResponseEntity<CustomerResponse> setFrameContract(
|
||||||
@PathVariable("id") String customerId,
|
@PathVariable("id") String customerId,
|
||||||
@Valid @RequestBody SetFrameContractRequest request,
|
@Valid @RequestBody SetFrameContractRequest request,
|
||||||
Authentication authentication
|
Authentication authentication
|
||||||
|
|
@ -277,7 +278,7 @@ public class CustomerController {
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Frame contract set for customer: {}", customerId);
|
logger.info("Frame contract set for customer: {}", customerId);
|
||||||
return ResponseEntity.ok(result.unsafeGetValue());
|
return ResponseEntity.ok(CustomerResponse.from(result.unsafeGetValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/{id}/frame-contract")
|
@DeleteMapping("/{id}/frame-contract")
|
||||||
|
|
@ -301,7 +302,7 @@ public class CustomerController {
|
||||||
|
|
||||||
@PutMapping("/{id}/preferences")
|
@PutMapping("/{id}/preferences")
|
||||||
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
||||||
public ResponseEntity<Customer> setPreferences(
|
public ResponseEntity<CustomerResponse> setPreferences(
|
||||||
@PathVariable("id") String customerId,
|
@PathVariable("id") String customerId,
|
||||||
@Valid @RequestBody SetPreferencesRequest request,
|
@Valid @RequestBody SetPreferencesRequest request,
|
||||||
Authentication authentication
|
Authentication authentication
|
||||||
|
|
@ -317,7 +318,7 @@ public class CustomerController {
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Preferences set for customer: {}", customerId);
|
logger.info("Preferences set for customer: {}", customerId);
|
||||||
return ResponseEntity.ok(result.unsafeGetValue());
|
return ResponseEntity.ok(CustomerResponse.from(result.unsafeGetValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private ActorId extractActorId(Authentication authentication) {
|
private ActorId extractActorId(Authentication authentication) {
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,10 @@ import de.effigenix.application.masterdata.ListProductCategories;
|
||||||
import de.effigenix.application.masterdata.UpdateProductCategory;
|
import de.effigenix.application.masterdata.UpdateProductCategory;
|
||||||
import de.effigenix.application.masterdata.command.CreateProductCategoryCommand;
|
import de.effigenix.application.masterdata.command.CreateProductCategoryCommand;
|
||||||
import de.effigenix.application.masterdata.command.UpdateProductCategoryCommand;
|
import de.effigenix.application.masterdata.command.UpdateProductCategoryCommand;
|
||||||
import de.effigenix.domain.masterdata.ProductCategory;
|
|
||||||
import de.effigenix.domain.masterdata.ProductCategoryId;
|
import de.effigenix.domain.masterdata.ProductCategoryId;
|
||||||
import de.effigenix.infrastructure.masterdata.web.dto.CreateProductCategoryRequest;
|
import de.effigenix.infrastructure.masterdata.web.dto.CreateProductCategoryRequest;
|
||||||
|
import de.effigenix.infrastructure.masterdata.web.dto.ProductCategoryResponse;
|
||||||
import de.effigenix.infrastructure.masterdata.web.dto.UpdateProductCategoryRequest;
|
import de.effigenix.infrastructure.masterdata.web.dto.UpdateProductCategoryRequest;
|
||||||
import de.effigenix.shared.common.Result;
|
|
||||||
import de.effigenix.shared.security.ActorId;
|
import de.effigenix.shared.security.ActorId;
|
||||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
@ -52,7 +51,7 @@ public class ProductCategoryController {
|
||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
||||||
public ResponseEntity<ProductCategory> createCategory(
|
public ResponseEntity<ProductCategoryResponse> createCategory(
|
||||||
@Valid @RequestBody CreateProductCategoryRequest request,
|
@Valid @RequestBody CreateProductCategoryRequest request,
|
||||||
Authentication authentication
|
Authentication authentication
|
||||||
) {
|
) {
|
||||||
|
|
@ -67,11 +66,11 @@ public class ProductCategoryController {
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Product category created: {}", request.name());
|
logger.info("Product category created: {}", request.name());
|
||||||
return ResponseEntity.status(HttpStatus.CREATED).body(result.unsafeGetValue());
|
return ResponseEntity.status(HttpStatus.CREATED).body(ProductCategoryResponse.from(result.unsafeGetValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public ResponseEntity<List<ProductCategory>> listCategories(Authentication authentication) {
|
public ResponseEntity<List<ProductCategoryResponse>> listCategories(Authentication authentication) {
|
||||||
var actorId = extractActorId(authentication);
|
var actorId = extractActorId(authentication);
|
||||||
logger.info("Listing product categories by actor: {}", actorId.value());
|
logger.info("Listing product categories by actor: {}", actorId.value());
|
||||||
|
|
||||||
|
|
@ -81,12 +80,13 @@ public class ProductCategoryController {
|
||||||
throw new ProductCategoryDomainErrorException(result.unsafeGetError());
|
throw new ProductCategoryDomainErrorException(result.unsafeGetError());
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResponseEntity.ok(result.unsafeGetValue());
|
var response = result.unsafeGetValue().stream().map(ProductCategoryResponse::from).toList();
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/{id}")
|
@PutMapping("/{id}")
|
||||||
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
||||||
public ResponseEntity<ProductCategory> updateCategory(
|
public ResponseEntity<ProductCategoryResponse> updateCategory(
|
||||||
@PathVariable("id") String categoryId,
|
@PathVariable("id") String categoryId,
|
||||||
@Valid @RequestBody UpdateProductCategoryRequest request,
|
@Valid @RequestBody UpdateProductCategoryRequest request,
|
||||||
Authentication authentication
|
Authentication authentication
|
||||||
|
|
@ -102,7 +102,7 @@ public class ProductCategoryController {
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Product category updated: {}", categoryId);
|
logger.info("Product category updated: {}", categoryId);
|
||||||
return ResponseEntity.ok(result.unsafeGetValue());
|
return ResponseEntity.ok(ProductCategoryResponse.from(result.unsafeGetValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/{id}")
|
@DeleteMapping("/{id}")
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ public class SupplierController {
|
||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
||||||
public ResponseEntity<Supplier> createSupplier(
|
public ResponseEntity<SupplierResponse> createSupplier(
|
||||||
@Valid @RequestBody CreateSupplierRequest request,
|
@Valid @RequestBody CreateSupplierRequest request,
|
||||||
Authentication authentication
|
Authentication authentication
|
||||||
) {
|
) {
|
||||||
|
|
@ -84,11 +84,11 @@ public class SupplierController {
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Supplier created: {}", request.name());
|
logger.info("Supplier created: {}", request.name());
|
||||||
return ResponseEntity.status(HttpStatus.CREATED).body(result.unsafeGetValue());
|
return ResponseEntity.status(HttpStatus.CREATED).body(SupplierResponse.from(result.unsafeGetValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public ResponseEntity<List<Supplier>> listSuppliers(
|
public ResponseEntity<List<SupplierResponse>> listSuppliers(
|
||||||
@RequestParam(value = "status", required = false) SupplierStatus status,
|
@RequestParam(value = "status", required = false) SupplierStatus status,
|
||||||
Authentication authentication
|
Authentication authentication
|
||||||
) {
|
) {
|
||||||
|
|
@ -106,11 +106,12 @@ public class SupplierController {
|
||||||
throw new SupplierDomainErrorException(result.unsafeGetError());
|
throw new SupplierDomainErrorException(result.unsafeGetError());
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResponseEntity.ok(result.unsafeGetValue());
|
var response = result.unsafeGetValue().stream().map(SupplierResponse::from).toList();
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/{id}")
|
@GetMapping("/{id}")
|
||||||
public ResponseEntity<Supplier> getSupplier(
|
public ResponseEntity<SupplierResponse> getSupplier(
|
||||||
@PathVariable("id") String supplierId,
|
@PathVariable("id") String supplierId,
|
||||||
Authentication authentication
|
Authentication authentication
|
||||||
) {
|
) {
|
||||||
|
|
@ -123,12 +124,12 @@ public class SupplierController {
|
||||||
throw new SupplierDomainErrorException(result.unsafeGetError());
|
throw new SupplierDomainErrorException(result.unsafeGetError());
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResponseEntity.ok(result.unsafeGetValue());
|
return ResponseEntity.ok(SupplierResponse.from(result.unsafeGetValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/{id}")
|
@PutMapping("/{id}")
|
||||||
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
||||||
public ResponseEntity<Supplier> updateSupplier(
|
public ResponseEntity<SupplierResponse> updateSupplier(
|
||||||
@PathVariable("id") String supplierId,
|
@PathVariable("id") String supplierId,
|
||||||
@Valid @RequestBody UpdateSupplierRequest request,
|
@Valid @RequestBody UpdateSupplierRequest request,
|
||||||
Authentication authentication
|
Authentication authentication
|
||||||
|
|
@ -150,12 +151,12 @@ public class SupplierController {
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Supplier updated: {}", supplierId);
|
logger.info("Supplier updated: {}", supplierId);
|
||||||
return ResponseEntity.ok(result.unsafeGetValue());
|
return ResponseEntity.ok(SupplierResponse.from(result.unsafeGetValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{id}/activate")
|
@PostMapping("/{id}/activate")
|
||||||
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
||||||
public ResponseEntity<Supplier> activate(
|
public ResponseEntity<SupplierResponse> activate(
|
||||||
@PathVariable("id") String supplierId,
|
@PathVariable("id") String supplierId,
|
||||||
Authentication authentication
|
Authentication authentication
|
||||||
) {
|
) {
|
||||||
|
|
@ -169,12 +170,12 @@ public class SupplierController {
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Supplier activated: {}", supplierId);
|
logger.info("Supplier activated: {}", supplierId);
|
||||||
return ResponseEntity.ok(result.unsafeGetValue());
|
return ResponseEntity.ok(SupplierResponse.from(result.unsafeGetValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{id}/deactivate")
|
@PostMapping("/{id}/deactivate")
|
||||||
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
||||||
public ResponseEntity<Supplier> deactivate(
|
public ResponseEntity<SupplierResponse> deactivate(
|
||||||
@PathVariable("id") String supplierId,
|
@PathVariable("id") String supplierId,
|
||||||
Authentication authentication
|
Authentication authentication
|
||||||
) {
|
) {
|
||||||
|
|
@ -188,12 +189,12 @@ public class SupplierController {
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Supplier deactivated: {}", supplierId);
|
logger.info("Supplier deactivated: {}", supplierId);
|
||||||
return ResponseEntity.ok(result.unsafeGetValue());
|
return ResponseEntity.ok(SupplierResponse.from(result.unsafeGetValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{id}/rating")
|
@PostMapping("/{id}/rating")
|
||||||
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
||||||
public ResponseEntity<Supplier> rateSupplier(
|
public ResponseEntity<SupplierResponse> rateSupplier(
|
||||||
@PathVariable("id") String supplierId,
|
@PathVariable("id") String supplierId,
|
||||||
@Valid @RequestBody RateSupplierRequest request,
|
@Valid @RequestBody RateSupplierRequest request,
|
||||||
Authentication authentication
|
Authentication authentication
|
||||||
|
|
@ -211,12 +212,12 @@ public class SupplierController {
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Supplier rated: {}", supplierId);
|
logger.info("Supplier rated: {}", supplierId);
|
||||||
return ResponseEntity.ok(result.unsafeGetValue());
|
return ResponseEntity.ok(SupplierResponse.from(result.unsafeGetValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{id}/certificates")
|
@PostMapping("/{id}/certificates")
|
||||||
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
@PreAuthorize("hasAuthority('MASTERDATA_WRITE')")
|
||||||
public ResponseEntity<Supplier> addCertificate(
|
public ResponseEntity<SupplierResponse> addCertificate(
|
||||||
@PathVariable("id") String supplierId,
|
@PathVariable("id") String supplierId,
|
||||||
@Valid @RequestBody AddCertificateRequest request,
|
@Valid @RequestBody AddCertificateRequest request,
|
||||||
Authentication authentication
|
Authentication authentication
|
||||||
|
|
@ -235,7 +236,7 @@ public class SupplierController {
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Certificate added to supplier: {}", supplierId);
|
logger.info("Certificate added to supplier: {}", supplierId);
|
||||||
return ResponseEntity.status(HttpStatus.CREATED).body(result.unsafeGetValue());
|
return ResponseEntity.status(HttpStatus.CREATED).body(SupplierResponse.from(result.unsafeGetValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/{id}/certificates")
|
@DeleteMapping("/{id}/certificates")
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package de.effigenix.infrastructure.masterdata.web.dto;
|
||||||
|
|
||||||
|
import de.effigenix.shared.common.Address;
|
||||||
|
|
||||||
|
public record AddressResponse(
|
||||||
|
String street,
|
||||||
|
String houseNumber,
|
||||||
|
String postalCode,
|
||||||
|
String city,
|
||||||
|
String country
|
||||||
|
) {
|
||||||
|
public static AddressResponse from(Address address) {
|
||||||
|
if (address == null) return null;
|
||||||
|
return new AddressResponse(
|
||||||
|
address.street(),
|
||||||
|
address.houseNumber(),
|
||||||
|
address.postalCode(),
|
||||||
|
address.city(),
|
||||||
|
address.country()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
package de.effigenix.infrastructure.masterdata.web.dto;
|
||||||
|
|
||||||
|
import de.effigenix.domain.masterdata.Article;
|
||||||
|
import de.effigenix.domain.masterdata.SupplierId;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public record ArticleResponse(
|
||||||
|
String id,
|
||||||
|
String name,
|
||||||
|
String articleNumber,
|
||||||
|
String categoryId,
|
||||||
|
List<SalesUnitResponse> salesUnits,
|
||||||
|
String status,
|
||||||
|
List<String> supplierIds,
|
||||||
|
LocalDateTime createdAt,
|
||||||
|
LocalDateTime updatedAt
|
||||||
|
) {
|
||||||
|
public static ArticleResponse from(Article article) {
|
||||||
|
return new ArticleResponse(
|
||||||
|
article.id().value(),
|
||||||
|
article.name().value(),
|
||||||
|
article.articleNumber().value(),
|
||||||
|
article.categoryId().value(),
|
||||||
|
article.salesUnits().stream().map(SalesUnitResponse::from).toList(),
|
||||||
|
article.status().name(),
|
||||||
|
article.supplierReferences().stream().map(SupplierId::value).toList(),
|
||||||
|
article.createdAt(),
|
||||||
|
article.updatedAt()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
package de.effigenix.infrastructure.masterdata.web.dto;
|
||||||
|
|
||||||
|
import de.effigenix.shared.common.ContactInfo;
|
||||||
|
|
||||||
|
public record ContactInfoResponse(
|
||||||
|
String phone,
|
||||||
|
String email,
|
||||||
|
String contactPerson
|
||||||
|
) {
|
||||||
|
public static ContactInfoResponse from(ContactInfo ci) {
|
||||||
|
if (ci == null) return null;
|
||||||
|
return new ContactInfoResponse(ci.phone(), ci.email(), ci.contactPerson());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
package de.effigenix.infrastructure.masterdata.web.dto;
|
||||||
|
|
||||||
|
import de.effigenix.domain.masterdata.ContractLineItem;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
public record ContractLineItemResponse(
|
||||||
|
String articleId,
|
||||||
|
BigDecimal agreedPrice,
|
||||||
|
BigDecimal agreedQuantity,
|
||||||
|
String unit
|
||||||
|
) {
|
||||||
|
public static ContractLineItemResponse from(ContractLineItem item) {
|
||||||
|
return new ContractLineItemResponse(
|
||||||
|
item.articleId().value(),
|
||||||
|
item.agreedPrice().amount(),
|
||||||
|
item.agreedQuantity(),
|
||||||
|
item.unit().name()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
package de.effigenix.infrastructure.masterdata.web.dto;
|
||||||
|
|
||||||
|
import de.effigenix.domain.masterdata.Customer;
|
||||||
|
import de.effigenix.domain.masterdata.CustomerPreference;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public record CustomerResponse(
|
||||||
|
String id,
|
||||||
|
String name,
|
||||||
|
String type,
|
||||||
|
AddressResponse billingAddress,
|
||||||
|
ContactInfoResponse contactInfo,
|
||||||
|
PaymentTermsResponse paymentTerms,
|
||||||
|
List<DeliveryAddressResponse> deliveryAddresses,
|
||||||
|
FrameContractResponse frameContract,
|
||||||
|
List<String> preferences,
|
||||||
|
String status,
|
||||||
|
LocalDateTime createdAt,
|
||||||
|
LocalDateTime updatedAt
|
||||||
|
) {
|
||||||
|
public static CustomerResponse from(Customer customer) {
|
||||||
|
return new CustomerResponse(
|
||||||
|
customer.id().value(),
|
||||||
|
customer.name().value(),
|
||||||
|
customer.type().name(),
|
||||||
|
AddressResponse.from(customer.billingAddress()),
|
||||||
|
ContactInfoResponse.from(customer.contactInfo()),
|
||||||
|
PaymentTermsResponse.from(customer.paymentTerms()),
|
||||||
|
customer.deliveryAddresses().stream().map(DeliveryAddressResponse::from).toList(),
|
||||||
|
FrameContractResponse.from(customer.frameContract()),
|
||||||
|
customer.preferences().stream().map(CustomerPreference::name).toList(),
|
||||||
|
customer.status().name(),
|
||||||
|
customer.createdAt(),
|
||||||
|
customer.updatedAt()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
package de.effigenix.infrastructure.masterdata.web.dto;
|
||||||
|
|
||||||
|
import de.effigenix.domain.masterdata.DeliveryAddress;
|
||||||
|
|
||||||
|
public record DeliveryAddressResponse(
|
||||||
|
String label,
|
||||||
|
AddressResponse address,
|
||||||
|
String contactPerson,
|
||||||
|
String deliveryNotes
|
||||||
|
) {
|
||||||
|
public static DeliveryAddressResponse from(DeliveryAddress da) {
|
||||||
|
return new DeliveryAddressResponse(
|
||||||
|
da.label(),
|
||||||
|
AddressResponse.from(da.address()),
|
||||||
|
da.contactPerson(),
|
||||||
|
da.deliveryNotes()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
package de.effigenix.infrastructure.masterdata.web.dto;
|
||||||
|
|
||||||
|
import de.effigenix.domain.masterdata.FrameContract;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public record FrameContractResponse(
|
||||||
|
String id,
|
||||||
|
LocalDate validFrom,
|
||||||
|
LocalDate validUntil,
|
||||||
|
String deliveryRhythm,
|
||||||
|
List<ContractLineItemResponse> lineItems
|
||||||
|
) {
|
||||||
|
public static FrameContractResponse from(FrameContract fc) {
|
||||||
|
if (fc == null) return null;
|
||||||
|
return new FrameContractResponse(
|
||||||
|
fc.id().value(),
|
||||||
|
fc.validFrom(),
|
||||||
|
fc.validUntil(),
|
||||||
|
fc.deliveryRhythm().name(),
|
||||||
|
fc.lineItems().stream().map(ContractLineItemResponse::from).toList()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
package de.effigenix.infrastructure.masterdata.web.dto;
|
||||||
|
|
||||||
|
import de.effigenix.shared.common.PaymentTerms;
|
||||||
|
|
||||||
|
public record PaymentTermsResponse(
|
||||||
|
int paymentDueDays,
|
||||||
|
String paymentDescription
|
||||||
|
) {
|
||||||
|
public static PaymentTermsResponse from(PaymentTerms pt) {
|
||||||
|
if (pt == null) return null;
|
||||||
|
return new PaymentTermsResponse(pt.paymentDueDays(), pt.description());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package de.effigenix.infrastructure.masterdata.web.dto;
|
||||||
|
|
||||||
|
import de.effigenix.domain.masterdata.ProductCategory;
|
||||||
|
|
||||||
|
public record ProductCategoryResponse(
|
||||||
|
String id,
|
||||||
|
String name,
|
||||||
|
String description
|
||||||
|
) {
|
||||||
|
public static ProductCategoryResponse from(ProductCategory category) {
|
||||||
|
return new ProductCategoryResponse(
|
||||||
|
category.id().value(),
|
||||||
|
category.name().value(),
|
||||||
|
category.description()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
package de.effigenix.infrastructure.masterdata.web.dto;
|
||||||
|
|
||||||
|
import de.effigenix.domain.masterdata.QualityCertificate;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
public record QualityCertificateResponse(
|
||||||
|
String certificateType,
|
||||||
|
String issuer,
|
||||||
|
LocalDate validFrom,
|
||||||
|
LocalDate validUntil
|
||||||
|
) {
|
||||||
|
public static QualityCertificateResponse from(QualityCertificate cert) {
|
||||||
|
return new QualityCertificateResponse(
|
||||||
|
cert.certificateType(),
|
||||||
|
cert.issuer(),
|
||||||
|
cert.validFrom(),
|
||||||
|
cert.validUntil()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
package de.effigenix.infrastructure.masterdata.web.dto;
|
||||||
|
|
||||||
|
import de.effigenix.domain.masterdata.SalesUnit;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
public record SalesUnitResponse(
|
||||||
|
String id,
|
||||||
|
String unit,
|
||||||
|
String priceModel,
|
||||||
|
BigDecimal price
|
||||||
|
) {
|
||||||
|
public static SalesUnitResponse from(SalesUnit su) {
|
||||||
|
return new SalesUnitResponse(
|
||||||
|
su.id().value(),
|
||||||
|
su.unit().name(),
|
||||||
|
su.priceModel().name(),
|
||||||
|
su.price().amount()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package de.effigenix.infrastructure.masterdata.web.dto;
|
||||||
|
|
||||||
|
import de.effigenix.domain.masterdata.SupplierRating;
|
||||||
|
|
||||||
|
public record SupplierRatingResponse(
|
||||||
|
int qualityScore,
|
||||||
|
int deliveryScore,
|
||||||
|
int priceScore
|
||||||
|
) {
|
||||||
|
public static SupplierRatingResponse from(SupplierRating rating) {
|
||||||
|
if (rating == null) return null;
|
||||||
|
return new SupplierRatingResponse(
|
||||||
|
rating.qualityScore(),
|
||||||
|
rating.deliveryScore(),
|
||||||
|
rating.priceScore()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
package de.effigenix.infrastructure.masterdata.web.dto;
|
||||||
|
|
||||||
|
import de.effigenix.domain.masterdata.Supplier;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public record SupplierResponse(
|
||||||
|
String id,
|
||||||
|
String name,
|
||||||
|
AddressResponse address,
|
||||||
|
ContactInfoResponse contactInfo,
|
||||||
|
PaymentTermsResponse paymentTerms,
|
||||||
|
List<QualityCertificateResponse> certificates,
|
||||||
|
SupplierRatingResponse rating,
|
||||||
|
String status,
|
||||||
|
LocalDateTime createdAt,
|
||||||
|
LocalDateTime updatedAt
|
||||||
|
) {
|
||||||
|
public static SupplierResponse from(Supplier supplier) {
|
||||||
|
return new SupplierResponse(
|
||||||
|
supplier.id().value(),
|
||||||
|
supplier.name().value(),
|
||||||
|
AddressResponse.from(supplier.address()),
|
||||||
|
ContactInfoResponse.from(supplier.contactInfo()),
|
||||||
|
PaymentTermsResponse.from(supplier.paymentTerms()),
|
||||||
|
supplier.certificates().stream().map(QualityCertificateResponse::from).toList(),
|
||||||
|
SupplierRatingResponse.from(supplier.rating()),
|
||||||
|
supplier.status().name(),
|
||||||
|
supplier.createdAt(),
|
||||||
|
supplier.updatedAt()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
-- Add MASTERDATA_READ and MASTERDATA_WRITE permissions to relevant roles.
|
||||||
|
-- These permissions are required by the Masterdata BC controllers
|
||||||
|
-- (ArticleController, ProductCategoryController, SupplierController, CustomerController).
|
||||||
|
|
||||||
|
-- ADMIN gets both READ and WRITE
|
||||||
|
INSERT INTO role_permissions (role_id, permission) VALUES
|
||||||
|
('c0a80121-0000-0000-0000-000000000001', 'MASTERDATA_READ'),
|
||||||
|
('c0a80121-0000-0000-0000-000000000001', 'MASTERDATA_WRITE');
|
||||||
|
|
||||||
|
-- PROCUREMENT_MANAGER gets both (manages suppliers, articles)
|
||||||
|
INSERT INTO role_permissions (role_id, permission) VALUES
|
||||||
|
('c0a80121-0000-0000-0000-000000000006', 'MASTERDATA_READ'),
|
||||||
|
('c0a80121-0000-0000-0000-000000000006', 'MASTERDATA_WRITE');
|
||||||
|
|
||||||
|
-- SALES_MANAGER gets both (manages customers)
|
||||||
|
INSERT INTO role_permissions (role_id, permission) VALUES
|
||||||
|
('c0a80121-0000-0000-0000-000000000008', 'MASTERDATA_READ'),
|
||||||
|
('c0a80121-0000-0000-0000-000000000008', 'MASTERDATA_WRITE');
|
||||||
|
|
||||||
|
-- PRODUCTION_MANAGER gets READ (needs to view articles, categories)
|
||||||
|
INSERT INTO role_permissions (role_id, permission) VALUES
|
||||||
|
('c0a80121-0000-0000-0000-000000000002', 'MASTERDATA_READ');
|
||||||
|
|
||||||
|
-- WAREHOUSE_WORKER gets READ (needs to view articles)
|
||||||
|
INSERT INTO role_permissions (role_id, permission) VALUES
|
||||||
|
('c0a80121-0000-0000-0000-000000000007', 'MASTERDATA_READ');
|
||||||
|
|
||||||
|
-- SALES_STAFF gets READ (needs to view articles, customers)
|
||||||
|
INSERT INTO role_permissions (role_id, permission) VALUES
|
||||||
|
('c0a80121-0000-0000-0000-000000000009', 'MASTERDATA_READ');
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?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="008-add-masterdata-permissions" author="effigenix">
|
||||||
|
<sqlFile path="db/changelog/changes/008-add-masterdata-permissions.sql"/>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
</databaseChangeLog>
|
||||||
|
|
@ -12,5 +12,6 @@
|
||||||
<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"/>
|
<include file="db/changelog/changes/006-create-supplier-schema.xml"/>
|
||||||
<include file="db/changelog/changes/007-create-customer-schema.xml"/>
|
<include file="db/changelog/changes/007-create-customer-schema.xml"/>
|
||||||
|
<include file="db/changelog/changes/008-add-masterdata-permissions.xml"/>
|
||||||
|
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -6,11 +6,6 @@
|
||||||
* PUT /api/articles/{id}/sales-units/{suId}/price,
|
* PUT /api/articles/{id}/sales-units/{suId}/price,
|
||||||
* POST /api/articles/{id}/suppliers, DELETE /api/articles/{id}/suppliers/{supplierId}
|
* POST /api/articles/{id}/suppliers, DELETE /api/articles/{id}/suppliers/{supplierId}
|
||||||
*
|
*
|
||||||
* NOTE: Backend returns domain objects with nested VOs:
|
|
||||||
* { "id": {"value": "uuid"}, "name": {"value": "..."}, ...,
|
|
||||||
* "supplierReferences": [{"value": "uuid"}],
|
|
||||||
* "salesUnits": [{"id": {"value":"uuid"}, "unit":"KG", "priceModel":"WEIGHT_BASED",
|
|
||||||
* "price": {"amount": 2.49, "currency": "EUR"}}] }
|
|
||||||
* DELETE endpoints for sales-units and suppliers return 204 No Content → re-fetch.
|
* DELETE endpoints for sales-units and suppliers return 204 No Content → re-fetch.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
@ -75,94 +70,50 @@ export interface UpdateSalesUnitPriceRequest {
|
||||||
price: number;
|
price: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Backend response shapes (domain objects with nested VOs) ─────────────────
|
|
||||||
|
|
||||||
interface BackendSalesUnit {
|
|
||||||
id: { value: string };
|
|
||||||
unit: Unit;
|
|
||||||
priceModel: PriceModel;
|
|
||||||
price: { amount: number; currency: string };
|
|
||||||
}
|
|
||||||
|
|
||||||
interface BackendArticle {
|
|
||||||
id: { value: string };
|
|
||||||
name: { value: string };
|
|
||||||
articleNumber: { value: string };
|
|
||||||
categoryId: { value: string };
|
|
||||||
salesUnits: BackendSalesUnit[];
|
|
||||||
status: ArticleStatus;
|
|
||||||
supplierReferences: Array<{ value: string }>;
|
|
||||||
createdAt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapSalesUnit(bsu: BackendSalesUnit): SalesUnitDTO {
|
|
||||||
return {
|
|
||||||
id: bsu.id.value,
|
|
||||||
unit: bsu.unit,
|
|
||||||
priceModel: bsu.priceModel,
|
|
||||||
price: bsu.price.amount,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapArticle(ba: BackendArticle): ArticleDTO {
|
|
||||||
return {
|
|
||||||
id: ba.id.value,
|
|
||||||
name: ba.name.value,
|
|
||||||
articleNumber: ba.articleNumber.value,
|
|
||||||
categoryId: ba.categoryId.value,
|
|
||||||
salesUnits: ba.salesUnits.map(mapSalesUnit),
|
|
||||||
status: ba.status,
|
|
||||||
supplierIds: ba.supplierReferences.map((sr) => sr.value),
|
|
||||||
createdAt: ba.createdAt,
|
|
||||||
updatedAt: ba.updatedAt,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Resource factory ─────────────────────────────────────────────────────────
|
// ── Resource factory ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
export function createArticlesResource(client: AxiosInstance) {
|
export function createArticlesResource(client: AxiosInstance) {
|
||||||
return {
|
return {
|
||||||
async list(): Promise<ArticleDTO[]> {
|
async list(): Promise<ArticleDTO[]> {
|
||||||
const res = await client.get<BackendArticle[]>('/api/articles');
|
const res = await client.get<ArticleDTO[]>('/api/articles');
|
||||||
return res.data.map(mapArticle);
|
return res.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
async getById(id: string): Promise<ArticleDTO> {
|
async getById(id: string): Promise<ArticleDTO> {
|
||||||
const res = await client.get<BackendArticle>(`/api/articles/${id}`);
|
const res = await client.get<ArticleDTO>(`/api/articles/${id}`);
|
||||||
return mapArticle(res.data);
|
return res.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
async create(request: CreateArticleRequest): Promise<ArticleDTO> {
|
async create(request: CreateArticleRequest): Promise<ArticleDTO> {
|
||||||
const res = await client.post<BackendArticle>('/api/articles', request);
|
const res = await client.post<ArticleDTO>('/api/articles', request);
|
||||||
return mapArticle(res.data);
|
return res.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
async update(id: string, request: UpdateArticleRequest): Promise<ArticleDTO> {
|
async update(id: string, request: UpdateArticleRequest): Promise<ArticleDTO> {
|
||||||
const res = await client.put<BackendArticle>(`/api/articles/${id}`, request);
|
const res = await client.put<ArticleDTO>(`/api/articles/${id}`, request);
|
||||||
return mapArticle(res.data);
|
return res.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
async activate(id: string): Promise<ArticleDTO> {
|
async activate(id: string): Promise<ArticleDTO> {
|
||||||
const res = await client.post<BackendArticle>(`/api/articles/${id}/activate`);
|
const res = await client.post<ArticleDTO>(`/api/articles/${id}/activate`);
|
||||||
return mapArticle(res.data);
|
return res.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
async deactivate(id: string): Promise<ArticleDTO> {
|
async deactivate(id: string): Promise<ArticleDTO> {
|
||||||
const res = await client.post<BackendArticle>(`/api/articles/${id}/deactivate`);
|
const res = await client.post<ArticleDTO>(`/api/articles/${id}/deactivate`);
|
||||||
return mapArticle(res.data);
|
return res.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
async addSalesUnit(id: string, request: AddSalesUnitRequest): Promise<ArticleDTO> {
|
async addSalesUnit(id: string, request: AddSalesUnitRequest): Promise<ArticleDTO> {
|
||||||
const res = await client.post<BackendArticle>(`/api/articles/${id}/sales-units`, request);
|
const res = await client.post<ArticleDTO>(`/api/articles/${id}/sales-units`, request);
|
||||||
return mapArticle(res.data);
|
return res.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
// Returns 204 No Content → re-fetch article
|
// Returns 204 No Content → re-fetch article
|
||||||
async removeSalesUnit(articleId: string, salesUnitId: string): Promise<ArticleDTO> {
|
async removeSalesUnit(articleId: string, salesUnitId: string): Promise<ArticleDTO> {
|
||||||
await client.delete(`/api/articles/${articleId}/sales-units/${salesUnitId}`);
|
await client.delete(`/api/articles/${articleId}/sales-units/${salesUnitId}`);
|
||||||
const res = await client.get<BackendArticle>(`/api/articles/${articleId}`);
|
const res = await client.get<ArticleDTO>(`/api/articles/${articleId}`);
|
||||||
return mapArticle(res.data);
|
return res.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
async updateSalesUnitPrice(
|
async updateSalesUnitPrice(
|
||||||
|
|
@ -170,25 +121,25 @@ export function createArticlesResource(client: AxiosInstance) {
|
||||||
salesUnitId: string,
|
salesUnitId: string,
|
||||||
request: UpdateSalesUnitPriceRequest,
|
request: UpdateSalesUnitPriceRequest,
|
||||||
): Promise<ArticleDTO> {
|
): Promise<ArticleDTO> {
|
||||||
const res = await client.put<BackendArticle>(
|
const res = await client.put<ArticleDTO>(
|
||||||
`/api/articles/${articleId}/sales-units/${salesUnitId}/price`,
|
`/api/articles/${articleId}/sales-units/${salesUnitId}/price`,
|
||||||
request,
|
request,
|
||||||
);
|
);
|
||||||
return mapArticle(res.data);
|
return res.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
async assignSupplier(articleId: string, supplierId: string): Promise<ArticleDTO> {
|
async assignSupplier(articleId: string, supplierId: string): Promise<ArticleDTO> {
|
||||||
const res = await client.post<BackendArticle>(`/api/articles/${articleId}/suppliers`, {
|
const res = await client.post<ArticleDTO>(`/api/articles/${articleId}/suppliers`, {
|
||||||
supplierId,
|
supplierId,
|
||||||
});
|
});
|
||||||
return mapArticle(res.data);
|
return res.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
// Returns 204 No Content → re-fetch article
|
// Returns 204 No Content → re-fetch article
|
||||||
async removeSupplier(articleId: string, supplierId: string): Promise<ArticleDTO> {
|
async removeSupplier(articleId: string, supplierId: string): Promise<ArticleDTO> {
|
||||||
await client.delete(`/api/articles/${articleId}/suppliers/${supplierId}`);
|
await client.delete(`/api/articles/${articleId}/suppliers/${supplierId}`);
|
||||||
const res = await client.get<BackendArticle>(`/api/articles/${articleId}`);
|
const res = await client.get<ArticleDTO>(`/api/articles/${articleId}`);
|
||||||
return mapArticle(res.data);
|
return res.data;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,8 @@
|
||||||
* Categories resource – Real HTTP implementation.
|
* Categories resource – Real HTTP implementation.
|
||||||
* Endpoints: GET/POST /api/categories, PUT/DELETE /api/categories/{id}
|
* Endpoints: GET/POST /api/categories, PUT/DELETE /api/categories/{id}
|
||||||
*
|
*
|
||||||
* NOTE: The backend returns domain objects serialized with Jackson field-visibility.
|
* Backend returns ProductCategoryResponse records:
|
||||||
* VOs like ProductCategoryId and CategoryName serialize as nested records:
|
* { "id": "uuid", "name": "string", "description": "string|null" }
|
||||||
* { "id": {"value": "uuid"}, "name": {"value": "string"}, "description": "string|null" }
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { AxiosInstance } from 'axios';
|
import type { AxiosInstance } from 'axios';
|
||||||
|
|
@ -25,47 +24,30 @@ export interface UpdateCategoryRequest {
|
||||||
description?: string | null;
|
description?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Backend response shapes (domain objects with nested VOs) ─────────────────
|
|
||||||
|
|
||||||
interface BackendProductCategory {
|
|
||||||
id: { value: string };
|
|
||||||
name: { value: string };
|
|
||||||
description: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapCategory(bc: BackendProductCategory): ProductCategoryDTO {
|
|
||||||
return {
|
|
||||||
id: bc.id.value,
|
|
||||||
name: bc.name.value,
|
|
||||||
description: bc.description,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Resource factory ─────────────────────────────────────────────────────────
|
// ── Resource factory ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
export function createCategoriesResource(client: AxiosInstance) {
|
export function createCategoriesResource(client: AxiosInstance) {
|
||||||
return {
|
return {
|
||||||
async list(): Promise<ProductCategoryDTO[]> {
|
async list(): Promise<ProductCategoryDTO[]> {
|
||||||
const res = await client.get<BackendProductCategory[]>('/api/categories');
|
const res = await client.get<ProductCategoryDTO[]>('/api/categories');
|
||||||
return res.data.map(mapCategory);
|
return res.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
// No GET /api/categories/{id} endpoint – implemented as list + filter
|
|
||||||
async getById(id: string): Promise<ProductCategoryDTO> {
|
async getById(id: string): Promise<ProductCategoryDTO> {
|
||||||
const res = await client.get<BackendProductCategory[]>('/api/categories');
|
const res = await client.get<ProductCategoryDTO[]>('/api/categories');
|
||||||
const cat = res.data.find((c) => c.id.value === id);
|
const cat = res.data.find((c) => c.id === id);
|
||||||
if (!cat) throw new Error(`Kategorie nicht gefunden: ${id}`);
|
if (!cat) throw new Error(`Kategorie nicht gefunden: ${id}`);
|
||||||
return mapCategory(cat);
|
return cat;
|
||||||
},
|
},
|
||||||
|
|
||||||
async create(request: CreateCategoryRequest): Promise<ProductCategoryDTO> {
|
async create(request: CreateCategoryRequest): Promise<ProductCategoryDTO> {
|
||||||
const res = await client.post<BackendProductCategory>('/api/categories', request);
|
const res = await client.post<ProductCategoryDTO>('/api/categories', request);
|
||||||
return mapCategory(res.data);
|
return res.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
async update(id: string, request: UpdateCategoryRequest): Promise<ProductCategoryDTO> {
|
async update(id: string, request: UpdateCategoryRequest): Promise<ProductCategoryDTO> {
|
||||||
const res = await client.put<BackendProductCategory>(`/api/categories/${id}`, request);
|
const res = await client.put<ProductCategoryDTO>(`/api/categories/${id}`, request);
|
||||||
return mapCategory(res.data);
|
return res.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
async delete(id: string): Promise<void> {
|
async delete(id: string): Promise<void> {
|
||||||
|
|
|
||||||
|
|
@ -8,14 +8,6 @@
|
||||||
* DELETE /api/customers/{id}/frame-contract,
|
* DELETE /api/customers/{id}/frame-contract,
|
||||||
* PUT /api/customers/{id}/preferences
|
* PUT /api/customers/{id}/preferences
|
||||||
*
|
*
|
||||||
* NOTE: Backend returns domain objects with nested VOs:
|
|
||||||
* { "id": {"value":"uuid"}, "name": {"value":"string"},
|
|
||||||
* "billingAddress": {street, houseNumber, postalCode, city, country},
|
|
||||||
* "contactInfo": {phone, email, contactPerson},
|
|
||||||
* "paymentTerms": {paymentDueDays, description},
|
|
||||||
* "deliveryAddresses": [{label, address: {...}, contactPerson, deliveryNotes}],
|
|
||||||
* "frameContract": {"id": {"value":"uuid"}, validFrom, validUntil, deliveryRhythm, lineItems},
|
|
||||||
* "preferences": ["BIO", ...], "status": "ACTIVE", ... }
|
|
||||||
* DELETE delivery-addresses/{label} and DELETE frame-contract return 204 → re-fetch.
|
* DELETE delivery-addresses/{label} and DELETE frame-contract return 204 → re-fetch.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
@ -143,153 +135,75 @@ export interface SetFrameContractRequest {
|
||||||
lineItems: SetFrameContractLineItem[];
|
lineItems: SetFrameContractLineItem[];
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Backend response shapes (domain objects with nested VOs) ─────────────────
|
|
||||||
|
|
||||||
interface BackendPaymentTerms {
|
|
||||||
paymentDueDays: number;
|
|
||||||
description: string | null; // Note: backend field is "description", not "paymentDescription"
|
|
||||||
}
|
|
||||||
|
|
||||||
interface BackendContractLineItem {
|
|
||||||
articleId: { value: string };
|
|
||||||
agreedPrice: { amount: number; currency: string };
|
|
||||||
agreedQuantity: number | null;
|
|
||||||
unit: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface BackendFrameContract {
|
|
||||||
id: { value: string };
|
|
||||||
validFrom: string | null;
|
|
||||||
validUntil: string | null;
|
|
||||||
deliveryRhythm: DeliveryRhythm;
|
|
||||||
lineItems: BackendContractLineItem[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface BackendCustomer {
|
|
||||||
id: { value: string };
|
|
||||||
name: { value: string };
|
|
||||||
type: CustomerType;
|
|
||||||
status: CustomerStatus;
|
|
||||||
billingAddress: AddressDTO;
|
|
||||||
contactInfo: ContactInfoDTO;
|
|
||||||
paymentTerms: BackendPaymentTerms | null;
|
|
||||||
deliveryAddresses: DeliveryAddressDTO[]; // DeliveryAddress is a record → matches DTO shape
|
|
||||||
frameContract: BackendFrameContract | null;
|
|
||||||
preferences: CustomerPreference[];
|
|
||||||
createdAt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapLineItem(bli: BackendContractLineItem): ContractLineItemDTO {
|
|
||||||
return {
|
|
||||||
articleId: bli.articleId.value,
|
|
||||||
agreedPrice: bli.agreedPrice.amount,
|
|
||||||
agreedQuantity: bli.agreedQuantity,
|
|
||||||
unit: bli.unit,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapFrameContract(bfc: BackendFrameContract): FrameContractDTO {
|
|
||||||
return {
|
|
||||||
id: bfc.id.value,
|
|
||||||
validFrom: bfc.validFrom,
|
|
||||||
validUntil: bfc.validUntil,
|
|
||||||
deliveryRhythm: bfc.deliveryRhythm,
|
|
||||||
lineItems: bfc.lineItems.map(mapLineItem),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapCustomer(bc: BackendCustomer): CustomerDTO {
|
|
||||||
return {
|
|
||||||
id: bc.id.value,
|
|
||||||
name: bc.name.value,
|
|
||||||
type: bc.type,
|
|
||||||
status: bc.status,
|
|
||||||
billingAddress: bc.billingAddress,
|
|
||||||
contactInfo: bc.contactInfo,
|
|
||||||
paymentTerms: bc.paymentTerms
|
|
||||||
? {
|
|
||||||
paymentDueDays: bc.paymentTerms.paymentDueDays,
|
|
||||||
paymentDescription: bc.paymentTerms.description,
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
deliveryAddresses: bc.deliveryAddresses,
|
|
||||||
frameContract: bc.frameContract ? mapFrameContract(bc.frameContract) : null,
|
|
||||||
preferences: bc.preferences,
|
|
||||||
createdAt: bc.createdAt,
|
|
||||||
updatedAt: bc.updatedAt,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Resource factory ─────────────────────────────────────────────────────────
|
// ── Resource factory ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
export function createCustomersResource(client: AxiosInstance) {
|
export function createCustomersResource(client: AxiosInstance) {
|
||||||
return {
|
return {
|
||||||
async list(): Promise<CustomerDTO[]> {
|
async list(): Promise<CustomerDTO[]> {
|
||||||
const res = await client.get<BackendCustomer[]>('/api/customers');
|
const res = await client.get<CustomerDTO[]>('/api/customers');
|
||||||
return res.data.map(mapCustomer);
|
return res.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
async getById(id: string): Promise<CustomerDTO> {
|
async getById(id: string): Promise<CustomerDTO> {
|
||||||
const res = await client.get<BackendCustomer>(`/api/customers/${id}`);
|
const res = await client.get<CustomerDTO>(`/api/customers/${id}`);
|
||||||
return mapCustomer(res.data);
|
return res.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
async create(request: CreateCustomerRequest): Promise<CustomerDTO> {
|
async create(request: CreateCustomerRequest): Promise<CustomerDTO> {
|
||||||
const res = await client.post<BackendCustomer>('/api/customers', request);
|
const res = await client.post<CustomerDTO>('/api/customers', request);
|
||||||
return mapCustomer(res.data);
|
return res.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
async update(id: string, request: UpdateCustomerRequest): Promise<CustomerDTO> {
|
async update(id: string, request: UpdateCustomerRequest): Promise<CustomerDTO> {
|
||||||
const res = await client.put<BackendCustomer>(`/api/customers/${id}`, request);
|
const res = await client.put<CustomerDTO>(`/api/customers/${id}`, request);
|
||||||
return mapCustomer(res.data);
|
return res.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
async activate(id: string): Promise<CustomerDTO> {
|
async activate(id: string): Promise<CustomerDTO> {
|
||||||
const res = await client.post<BackendCustomer>(`/api/customers/${id}/activate`);
|
const res = await client.post<CustomerDTO>(`/api/customers/${id}/activate`);
|
||||||
return mapCustomer(res.data);
|
return res.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
async deactivate(id: string): Promise<CustomerDTO> {
|
async deactivate(id: string): Promise<CustomerDTO> {
|
||||||
const res = await client.post<BackendCustomer>(`/api/customers/${id}/deactivate`);
|
const res = await client.post<CustomerDTO>(`/api/customers/${id}/deactivate`);
|
||||||
return mapCustomer(res.data);
|
return res.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
async addDeliveryAddress(id: string, request: AddDeliveryAddressRequest): Promise<CustomerDTO> {
|
async addDeliveryAddress(id: string, request: AddDeliveryAddressRequest): Promise<CustomerDTO> {
|
||||||
const res = await client.post<BackendCustomer>(
|
const res = await client.post<CustomerDTO>(
|
||||||
`/api/customers/${id}/delivery-addresses`,
|
`/api/customers/${id}/delivery-addresses`,
|
||||||
request,
|
request,
|
||||||
);
|
);
|
||||||
return mapCustomer(res.data);
|
return res.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
// Returns 204 No Content → re-fetch customer
|
// Returns 204 No Content → re-fetch customer
|
||||||
async removeDeliveryAddress(id: string, label: string): Promise<CustomerDTO> {
|
async removeDeliveryAddress(id: string, label: string): Promise<CustomerDTO> {
|
||||||
await client.delete(`/api/customers/${id}/delivery-addresses/${encodeURIComponent(label)}`);
|
await client.delete(`/api/customers/${id}/delivery-addresses/${encodeURIComponent(label)}`);
|
||||||
const res = await client.get<BackendCustomer>(`/api/customers/${id}`);
|
const res = await client.get<CustomerDTO>(`/api/customers/${id}`);
|
||||||
return mapCustomer(res.data);
|
return res.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
async setFrameContract(id: string, request: SetFrameContractRequest): Promise<CustomerDTO> {
|
async setFrameContract(id: string, request: SetFrameContractRequest): Promise<CustomerDTO> {
|
||||||
const res = await client.put<BackendCustomer>(
|
const res = await client.put<CustomerDTO>(
|
||||||
`/api/customers/${id}/frame-contract`,
|
`/api/customers/${id}/frame-contract`,
|
||||||
request,
|
request,
|
||||||
);
|
);
|
||||||
return mapCustomer(res.data);
|
return res.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
// Returns 204 No Content → re-fetch customer
|
// Returns 204 No Content → re-fetch customer
|
||||||
async removeFrameContract(id: string): Promise<CustomerDTO> {
|
async removeFrameContract(id: string): Promise<CustomerDTO> {
|
||||||
await client.delete(`/api/customers/${id}/frame-contract`);
|
await client.delete(`/api/customers/${id}/frame-contract`);
|
||||||
const res = await client.get<BackendCustomer>(`/api/customers/${id}`);
|
const res = await client.get<CustomerDTO>(`/api/customers/${id}`);
|
||||||
return mapCustomer(res.data);
|
return res.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
async setPreferences(id: string, preferences: CustomerPreference[]): Promise<CustomerDTO> {
|
async setPreferences(id: string, preferences: CustomerPreference[]): Promise<CustomerDTO> {
|
||||||
const res = await client.put<BackendCustomer>(`/api/customers/${id}/preferences`, {
|
const res = await client.put<CustomerDTO>(`/api/customers/${id}/preferences`, {
|
||||||
preferences,
|
preferences,
|
||||||
});
|
});
|
||||||
return mapCustomer(res.data);
|
return res.data;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,6 @@
|
||||||
* POST /api/suppliers/{id}/certificates,
|
* POST /api/suppliers/{id}/certificates,
|
||||||
* DELETE /api/suppliers/{id}/certificates (with body)
|
* DELETE /api/suppliers/{id}/certificates (with body)
|
||||||
*
|
*
|
||||||
* NOTE: Backend returns domain objects with nested VOs:
|
|
||||||
* { "id": {"value":"uuid"}, "name": {"value":"string"},
|
|
||||||
* "address": {"street":"...","houseNumber":"...","postalCode":"...","city":"...","country":"DE"},
|
|
||||||
* "contactInfo": {"phone":"...","email":"...","contactPerson":"..."},
|
|
||||||
* "paymentTerms": {"paymentDueDays":30,"description":"..."},
|
|
||||||
* "certificates": [{"certificateType":"...","issuer":"...","validFrom":"2024-01-01","validUntil":"2026-12-31"}],
|
|
||||||
* "rating": {"qualityScore":4,"deliveryScore":4,"priceScore":5},
|
|
||||||
* "status": "ACTIVE", "createdAt":"...", "updatedAt":"..." }
|
|
||||||
* DELETE /api/suppliers/{id}/certificates returns 204 No Content → re-fetch.
|
* DELETE /api/suppliers/{id}/certificates returns 204 No Content → re-fetch.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
@ -113,122 +105,55 @@ export interface RemoveCertificateRequest {
|
||||||
validFrom: string;
|
validFrom: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Backend response shapes (domain objects with nested VOs) ─────────────────
|
|
||||||
|
|
||||||
interface BackendAddress {
|
|
||||||
street: string;
|
|
||||||
houseNumber: string | null;
|
|
||||||
postalCode: string;
|
|
||||||
city: string;
|
|
||||||
country: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface BackendContactInfo {
|
|
||||||
phone: string;
|
|
||||||
email: string | null;
|
|
||||||
contactPerson: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface BackendPaymentTerms {
|
|
||||||
paymentDueDays: number;
|
|
||||||
description: string | null; // Note: backend field is "description", not "paymentDescription"
|
|
||||||
}
|
|
||||||
|
|
||||||
interface BackendQualityCertificate {
|
|
||||||
certificateType: string;
|
|
||||||
issuer: string;
|
|
||||||
validFrom: string; // LocalDate → "2024-01-01"
|
|
||||||
validUntil: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface BackendSupplierRating {
|
|
||||||
qualityScore: number;
|
|
||||||
deliveryScore: number;
|
|
||||||
priceScore: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface BackendSupplier {
|
|
||||||
id: { value: string };
|
|
||||||
name: { value: string };
|
|
||||||
address: BackendAddress | null;
|
|
||||||
contactInfo: BackendContactInfo;
|
|
||||||
paymentTerms: BackendPaymentTerms | null;
|
|
||||||
certificates: BackendQualityCertificate[];
|
|
||||||
rating: BackendSupplierRating | null;
|
|
||||||
status: SupplierStatus;
|
|
||||||
createdAt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapSupplier(bs: BackendSupplier): SupplierDTO {
|
|
||||||
return {
|
|
||||||
id: bs.id.value,
|
|
||||||
name: bs.name.value,
|
|
||||||
status: bs.status,
|
|
||||||
address: bs.address,
|
|
||||||
contactInfo: bs.contactInfo,
|
|
||||||
paymentTerms: bs.paymentTerms
|
|
||||||
? {
|
|
||||||
paymentDueDays: bs.paymentTerms.paymentDueDays,
|
|
||||||
paymentDescription: bs.paymentTerms.description,
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
certificates: bs.certificates,
|
|
||||||
rating: bs.rating,
|
|
||||||
createdAt: bs.createdAt,
|
|
||||||
updatedAt: bs.updatedAt,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Resource factory ─────────────────────────────────────────────────────────
|
// ── Resource factory ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
export function createSuppliersResource(client: AxiosInstance) {
|
export function createSuppliersResource(client: AxiosInstance) {
|
||||||
return {
|
return {
|
||||||
async list(): Promise<SupplierDTO[]> {
|
async list(): Promise<SupplierDTO[]> {
|
||||||
const res = await client.get<BackendSupplier[]>('/api/suppliers');
|
const res = await client.get<SupplierDTO[]>('/api/suppliers');
|
||||||
return res.data.map(mapSupplier);
|
return res.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
async getById(id: string): Promise<SupplierDTO> {
|
async getById(id: string): Promise<SupplierDTO> {
|
||||||
const res = await client.get<BackendSupplier>(`/api/suppliers/${id}`);
|
const res = await client.get<SupplierDTO>(`/api/suppliers/${id}`);
|
||||||
return mapSupplier(res.data);
|
return res.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
async create(request: CreateSupplierRequest): Promise<SupplierDTO> {
|
async create(request: CreateSupplierRequest): Promise<SupplierDTO> {
|
||||||
const res = await client.post<BackendSupplier>('/api/suppliers', request);
|
const res = await client.post<SupplierDTO>('/api/suppliers', request);
|
||||||
return mapSupplier(res.data);
|
return res.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
async update(id: string, request: UpdateSupplierRequest): Promise<SupplierDTO> {
|
async update(id: string, request: UpdateSupplierRequest): Promise<SupplierDTO> {
|
||||||
const res = await client.put<BackendSupplier>(`/api/suppliers/${id}`, request);
|
const res = await client.put<SupplierDTO>(`/api/suppliers/${id}`, request);
|
||||||
return mapSupplier(res.data);
|
return res.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
async activate(id: string): Promise<SupplierDTO> {
|
async activate(id: string): Promise<SupplierDTO> {
|
||||||
const res = await client.post<BackendSupplier>(`/api/suppliers/${id}/activate`);
|
const res = await client.post<SupplierDTO>(`/api/suppliers/${id}/activate`);
|
||||||
return mapSupplier(res.data);
|
return res.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
async deactivate(id: string): Promise<SupplierDTO> {
|
async deactivate(id: string): Promise<SupplierDTO> {
|
||||||
const res = await client.post<BackendSupplier>(`/api/suppliers/${id}/deactivate`);
|
const res = await client.post<SupplierDTO>(`/api/suppliers/${id}/deactivate`);
|
||||||
return mapSupplier(res.data);
|
return res.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
async rate(id: string, request: RateSupplierRequest): Promise<SupplierDTO> {
|
async rate(id: string, request: RateSupplierRequest): Promise<SupplierDTO> {
|
||||||
const res = await client.post<BackendSupplier>(`/api/suppliers/${id}/rating`, request);
|
const res = await client.post<SupplierDTO>(`/api/suppliers/${id}/rating`, request);
|
||||||
return mapSupplier(res.data);
|
return res.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
async addCertificate(id: string, request: AddCertificateRequest): Promise<SupplierDTO> {
|
async addCertificate(id: string, request: AddCertificateRequest): Promise<SupplierDTO> {
|
||||||
const res = await client.post<BackendSupplier>(`/api/suppliers/${id}/certificates`, request);
|
const res = await client.post<SupplierDTO>(`/api/suppliers/${id}/certificates`, request);
|
||||||
return mapSupplier(res.data);
|
return res.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
// Returns 204 No Content → re-fetch supplier
|
// Returns 204 No Content → re-fetch supplier
|
||||||
async removeCertificate(id: string, request: RemoveCertificateRequest): Promise<SupplierDTO> {
|
async removeCertificate(id: string, request: RemoveCertificateRequest): Promise<SupplierDTO> {
|
||||||
await client.delete(`/api/suppliers/${id}/certificates`, { data: request });
|
await client.delete(`/api/suppliers/${id}/certificates`, { data: request });
|
||||||
const res = await client.get<BackendSupplier>(`/api/suppliers/${id}`);
|
const res = await client.get<SupplierDTO>(`/api/suppliers/${id}`);
|
||||||
return mapSupplier(res.data);
|
return res.data;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue