1
0
Fork 0
mirror of https://github.com/s-frick/effigenix.git synced 2026-03-28 10:09:35 +01:00
effigenix/frontend/packages/api-client/src/resources/suppliers.ts
Sebastian Frick d27dbaa843 feat(cli): Stammdaten-TUI mit Master Data API-Anbindung
- Neue Screens: Kategorien, Lieferanten, Artikel, Kunden (jeweils
  Liste, Detail, Anlegen + Detailaktionen wie Bewertung, Zertifikate,
  Verkaufseinheiten, Lieferadressen, Präferenzen)
- API-Client: Resources für alle 4 Stammdaten-Aggregate implementiert
  (categories, suppliers, articles, customers) mit Mapping von
  verschachtelten Domain-VOs auf flache DTOs
- Lieferant, Artikel, Kategorie: echte HTTP-Calls gegen Backend
  (/api/suppliers, /api/articles, /api/categories, /api/customers)
- 204-No-Content-Endpoints (removeSalesUnit, removeSupplier,
  removeCertificate, removeDeliveryAddress, removeFrameContract)
  lösen Re-Fetch des Aggregats aus
- MasterdataMenu, Navigation-Erweiterung, App.tsx-Routing
2026-02-18 13:35:20 +01:00

236 lines
6.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Suppliers resource Real HTTP implementation.
* Endpoints: GET/POST /api/suppliers, GET/PUT /api/suppliers/{id},
* POST /api/suppliers/{id}/activate|deactivate,
* POST /api/suppliers/{id}/rating,
* POST /api/suppliers/{id}/certificates,
* 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.
*/
import type { AxiosInstance } from 'axios';
export interface AddressDTO {
street: string;
houseNumber: string | null;
postalCode: string;
city: string;
country: string;
}
export interface ContactInfoDTO {
phone: string;
email: string | null;
contactPerson: string | null;
}
export interface PaymentTermsDTO {
paymentDueDays: number;
paymentDescription: string | null;
}
export interface QualityCertificateDTO {
certificateType: string;
issuer: string;
validFrom: string;
validUntil: string;
}
export interface SupplierRatingDTO {
qualityScore: number;
deliveryScore: number;
priceScore: number;
}
export type SupplierStatus = 'ACTIVE' | 'INACTIVE';
export interface SupplierDTO {
id: string;
name: string;
status: SupplierStatus;
address: AddressDTO | null;
contactInfo: ContactInfoDTO;
paymentTerms: PaymentTermsDTO | null;
certificates: QualityCertificateDTO[];
rating: SupplierRatingDTO | null;
createdAt: string;
updatedAt: string;
}
export interface CreateSupplierRequest {
name: string;
phone: string;
email?: string;
contactPerson?: string;
street?: string;
houseNumber?: string;
postalCode?: string;
city?: string;
country?: string;
paymentDueDays?: number;
paymentDescription?: string;
}
export interface UpdateSupplierRequest {
name?: string;
phone?: string;
email?: string | null;
contactPerson?: string | null;
street?: string | null;
houseNumber?: string | null;
postalCode?: string | null;
city?: string | null;
country?: string | null;
paymentDueDays?: number | null;
paymentDescription?: string | null;
}
export interface RateSupplierRequest {
qualityScore: number;
deliveryScore: number;
priceScore: number;
}
export interface AddCertificateRequest {
certificateType: string;
issuer: string;
validFrom: string;
validUntil: string;
}
export interface RemoveCertificateRequest {
certificateType: string;
issuer: 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 ─────────────────────────────────────────────────────────
export function createSuppliersResource(client: AxiosInstance) {
return {
async list(): Promise<SupplierDTO[]> {
const res = await client.get<BackendSupplier[]>('/api/suppliers');
return res.data.map(mapSupplier);
},
async getById(id: string): Promise<SupplierDTO> {
const res = await client.get<BackendSupplier>(`/api/suppliers/${id}`);
return mapSupplier(res.data);
},
async create(request: CreateSupplierRequest): Promise<SupplierDTO> {
const res = await client.post<BackendSupplier>('/api/suppliers', request);
return mapSupplier(res.data);
},
async update(id: string, request: UpdateSupplierRequest): Promise<SupplierDTO> {
const res = await client.put<BackendSupplier>(`/api/suppliers/${id}`, request);
return mapSupplier(res.data);
},
async activate(id: string): Promise<SupplierDTO> {
const res = await client.post<BackendSupplier>(`/api/suppliers/${id}/activate`);
return mapSupplier(res.data);
},
async deactivate(id: string): Promise<SupplierDTO> {
const res = await client.post<BackendSupplier>(`/api/suppliers/${id}/deactivate`);
return mapSupplier(res.data);
},
async rate(id: string, request: RateSupplierRequest): Promise<SupplierDTO> {
const res = await client.post<BackendSupplier>(`/api/suppliers/${id}/rating`, request);
return mapSupplier(res.data);
},
async addCertificate(id: string, request: AddCertificateRequest): Promise<SupplierDTO> {
const res = await client.post<BackendSupplier>(`/api/suppliers/${id}/certificates`, request);
return mapSupplier(res.data);
},
// Returns 204 No Content → re-fetch supplier
async removeCertificate(id: string, request: RemoveCertificateRequest): Promise<SupplierDTO> {
await client.delete(`/api/suppliers/${id}/certificates`, { data: request });
const res = await client.get<BackendSupplier>(`/api/suppliers/${id}`);
return mapSupplier(res.data);
},
};
}
export type SuppliersResource = ReturnType<typeof createSuppliersResource>;