Produktsøk-modal (product-search-modal)¶
Delt modal for å finne og legge til produktlinjer på en masterordre eller et
tilbud. Tre faner: Produkter (debounced, race-sikkert søk mot
products/search med tastatur-/klikkvalg og struktur-utvidelsespanel for
strukturhoder), Pakker (aktive malpakker med per-rad
inkluder/antall/frekvens-valg, satt inn transaksjonelt via
lines/from-template) og Produktpakker (S3 — aktive produktpakker,
skrivebeskyttet forhåndsvisning av alle linjene + én multiplikator, satt inn
transaksjonelt via lines/from-package; distinkt fra Pakker — se
Produktpakker).
Modalen brukes fra masterordre-linjegridet og fra tilbuds-linjegridet — samme
komponent, ulik vert.
Feature-tabell¶
| Komponent | Selector | Hovedfunksjon | Import |
|---|---|---|---|
| ProductSearchModalComponent | app-product-search-modal |
Produktsøk + struktur-utvidelse + malpakke-innsetting, åpnet via NgbModal.open(...) |
Deklarert i SubscriptionModule |
Kildefiler¶
| Del | Sti |
|---|---|
| Komponent | ui/ePortal.ui/src/app/components/subscription/product-search-modal/product-search-modal.component.ts |
| Mal | ui/ePortal.ui/src/app/components/subscription/product-search-modal/product-search-modal.component.html |
| Tester | ui/ePortal.ui/src/app/components/subscription/product-search-modal/product-search-modal.component.spec.ts |
| Deklarasjon | ui/ePortal.ui/src/app/components/subscription/subscription.module.ts |
Bruk¶
const ref = this.modalService.open(ProductSearchModalComponent, {
size: 'lg', centered: true, backdrop: 'static'
});
ref.componentInstance.customerNo = this.activeOrder.customerNo ?? null;
ref.componentInstance.masterOrderId = this.activeOrder.masterOrderId; // utelates fra et tilbud
ref.result.then(
(result: ProductSearchModalResult) => this.applyProductSelection(result),
() => { /* avvist — ingenting satt inn */ }
);
Inputs¶
| Input | Type | Standard | Beskrivelse |
|---|---|---|---|
customerNo |
number \| null |
null |
Kunde for pris-visning i struktur-panelet (best-effort). null løser kun standardpriser. |
masterOrderId |
number \| null |
null |
Skrivemål for Pakker- og Produktpakker-fanenes innsetting (lines/from-template / lines/from-package) når modalen er åpnet fra en masterordre. Styrer også om fanene vises — se under. |
offerId |
number \| null |
null |
Samme rolle som masterOrderId, men for et tilbud (MO-096-EXT / S3) — verten setter kun ÉN av de to. |
initialTab |
'products' \| 'packages' \| 'productPackages' |
'products' |
Startfane. Forslagsbanneret åpner direkte i 'packages'. |
preselectedTemplates |
TemplateProduct[] \| null |
null |
Åpner direkte i Pakker-forhåndsvisningen for disse malene (forslagsflyt). |
Komponenten lukkes via activeModal.close(result) (ikke en @Output()).
«Pakker»- og «Produktpakker»-fanene krever ett skrivemål¶
Getterene showPackagesTab og showProductPackagesTab returnerer begge
masterOrderId != null || offerId != null, og <li *ngIf="..."> skjuler
fanen når verten ikke har satt noen av de to. Grunn: begge fanenes innsetting
er transaksjonell og server-autoritativ (POST {masterOrderId eller
offerId}/lines/from-template for Pakker, .../lines/from-package for
Produktpakker) — uten et skrivemål ville flyten feilet stille. Produkt-/
strukturvalg (fanen Produkter) fungerer uavhengig av skrivemål.
Resultat-kontrakt¶
ref.result løses med ProductSearchModalResult:
kind |
Innhold | Vertens ansvar |
|---|---|---|
single |
product |
Sett inn én linje via vertens create-vei. |
structure |
product, headQuantity, selectedMembers |
Masterordre: transaksjonell struktur-innsetting. Tilbud: sett inn hodeproduktet som én linje (ingen struktur-endepunkt). |
template-inserted |
lines, productLookupFailed, alreadyApplied |
Modalen har allerede skrevet via lines/from-template mot masterordren — verten legger kun til radene (MasterOrderLine[]). Oppstår aldri når verten satte offerId i stedet for masterOrderId. |
offer-template-inserted |
lines, productLookupFailed, alreadyApplied |
Tilbuds-tvillingen av template-inserted — modalen har skrevet via lines/from-template mot tilbudet (OfferLine[]). Oppstår aldri når verten satte masterOrderId. |
product-package-inserted |
lines, productLookupFailed, alreadyApplied |
S3: modalen har allerede skrevet via lines/from-package mot masterordren — verten legger kun til radene (MasterOrderLine[]). Distinkt kind fra template-inserted (produktpakke ≠ malpakke). |
offer-product-package-inserted |
lines, productLookupFailed, alreadyApplied |
S3: tilbuds-tvillingen — modalen har skrevet via lines/from-package mot tilbudet (OfferLine[]). |
i18n¶
Fane- og søketekster ligger i BÅDE no.json og en.json under Subscription
(bl.a. pickerTabProducts, pickerTabPackages, pickerTabProductPackages,
productSearchTitle, productSearchPlaceholder, packageInsertLines,
productPackageInsertLines, productPackageSearchPlaceholder,
productPackageAlreadyApplied).
Kjente fallgruver¶
| Fallgruve | Riktig mønster |
|---|---|
Sette BÅDE masterOrderId OG offerId samtidig |
Sett kun ÉN — verten som åpner modalen vet hvilket objekt som er skrivemålet; begge satt gir udefinert routing i innsettings-logikken. |
Anta at structure-resultatet setter inn medlemmene på et tilbud |
Tilbud har ingen transaksjonell struktur-innsetting; kun hodeproduktet blir én linje. |
| Bytte fane midt i en Pakker-/Produktpakker-innsetting | setTab(...) blokkerer bytte mens inserting ELLER productPackageInserting er sann. |
Behandle product-package-inserted som en template-inserted-variant |
De er distinkte kind-verdier med distinkte i18n-nøkler (productPackageAlreadyApplied vs. packageAlreadyApplied) — en vert som ignorerer den ene mister meldinger for den andre. |