Sklady a rezervace
Sklady
Skladová zásoba je množství produktu, které je k dispozici k prodeji. Skladové zásoby jsou důležité pro správné fungování e-shopu, protože zajišťují, že zákazník může nakoupit pouze produkty, které jsou skladem.
Skladovost dále ovlivňuje viditelnost produktů v seznamech (toto je ale ovlivněno i dalšími faktory, jako je např. dostupnost daného produktu).
E-shop podporuje evidenci skladových zásob na více skladech. Ve výchozím stavu jsou pro zákazníka dostupné všechny sklady.
Stavy skladů a publikace
Sklady mohou nabývat tří stavů (CatalogEntityStatus):
| Stav | Viditelnost na FE | Viditelnost v administraci | Publikuje se do EvitaDB |
|---|---|---|---|
ACTIVE | ano | ano | ano (data + visibility) |
PRIVATE | ne | ano | ano (data v REFERENCE_STOCKS); ne ve REFERENCE_STOCK_VISIBILITIES |
TRASHED | ne | ne | ne |
PRIVATE sklady prochází publikační logikou v ProductToEvitaConverter.addProductQuantityOnStock stejně jako ACTIVE — žádné PRIVATE-specifické větve. Pravidla pro addOrUpdateStock (vázáno na shouldPublishStockTag) a addOrUpdateStockVisibility (vázáno na getVisibilityForProductAndStock) se aplikují identicky pro oba stavy. Rozdíl mezi PRIVATE a ACTIVE z pohledu FE zajišťují framework filtry při čtení (viz níže), nikoliv divergentní publikační logika.
Viditelnost referencí na PRIVATE sklady
Framework aplikuje dvě úrovně auto-filtrů, díky kterým FE PRIVATE sklady neuvidí — i když jsou data v evita indexu persistovaná:
AbstractEntityQuery.status = [ACTIVE](default) — filtr na top-level dotazy. FE dotazy přesPublishedProductStockProviderImpl.getAvailableStocksproto PRIVATE Stock entity nevrátí.DefaultReferenceConstraintGenerator— pro každý reference type, jehož cílová entita má atributstatus, automaticky přidáváentityHaving(attributeEquals(STATUS, ACTIVE))do dotazu. Reference (REFERENCE_STOCKSiREFERENCE_STOCK_VISIBILITIES) na PRIVATE sklady jsou tak vyfiltrovány na úrovni reference content, i když jsou v indexu persistované.
Důsledky:
- Self-heal funguje — flip
PRIVATE → ACTIVEna skladu okamžitě „odhalí" už dříve persistované reference na produktech, bez nutnosti republikovat produkty. FlipACTIVE → PRIVATEnaopak zneviditelní reference, opět bez republikace. - Custom admin pohled — pokud projekt potřebuje admin UI, které ukáže reference i pro PRIVATE sklady, musí buď přepsat
DefaultReferenceConstraintGenerator(přesReferenceConstraintGeneratorSPI), nebo dotazovat evita session přímo s vlastnímreferenceContent(name, filterBy(...)).
Změna statusu skladu (ACTIVE ↔ PRIVATE) nevyžaduje republikaci produktů — FE-facing pohled je řízen oběma výše uvedenými dynamickými filtry, které se vyhodnocují při každém dotazu.
Omezení skladu na země
Sklad lze v administraci omezit na země regionů katalogu (pole „Platný pro země“ v sekci „Omezení“, Stock#getCountryRestrictionCode(),
publikováno jako atribut countryRestrictions entity Stock). Typické použití je jeden katalog s regiony více zemí, kde se
zboží ze skladu jedné země nemá nabízet ani rezervovat na webu jiné země. Omezení je jednosměrné a nastavuje se u každého
skladu zvlášť.
- Rozhoduje země webu (regionu katalogu), nikoliv země zákazníka ani doručovací adresy.
- Prázdné omezení = sklad platí pro všechny země (výchozí chování).
- Porovnání kódů zemí nerozlišuje velikost písmen. Neznámá země = bez omezení. Na FE
(
PublishedProductStockProviderImpl) je země neznámá, pokud chybí kontext katalogu, region nemá zemi, nebo kontext patří jinému katalogu, než pro který se sklady načítají. Kontext vytvořený jen s katalogem (bez regionu – typicky joby a ukládání v administraci) ale používá výchozí region katalogu, takže se filtruje podle jeho země. Objednávka (OrderStockProviderAllStock) filtruje vždy podle země košíku / objednávky, bez omezení je jen košík / objednávka bez země. - Fallback: pokud by po omezení pro danou zemi nezůstal žádný sklad, omezení se neuplatní a vrací se všechny sklady. Prázdný seznam skladů by jinak vedl k nekonzistentnímu chování (zobrazení všech produktů, chyba při načtení uživatele, nemožnost vytvořit rezervaci).
Filtr se aplikuje ve výchozích implementacích:
PublishedProductStockProviderImpl.getAvailableStocks– země zActualCatalogContext(aktuální region katalogu). Ovlivňuje skladovost a dostupnost na FE, viditelnost ve výpisech, feedy a fulltext.OrderStockProviderAllStock– země košíku / objednávky (OrderCommonInfo#getCountry()). Ovlivňuje kontrolu košíku, rezervace kusů a úpravu objednávky v administraci.
Pro vlastní filtrování je k dispozici sdílená metoda Stock.filterByCountry(...) (včetně fallbacku), resp.
Stock.isAllowedForCountry(...). Projekty s vlastní implementací PublishedProductStockProvider nebo
OrderStockProvider si filtr musí doplnit samy.
Po nasazení není nutná republikace produktů ani full reindex – skladovost se přes sklady sčítá při čtení a nový atribut skladu se do schématu doplní automaticky (sklady bez omezení atribut nemají, což znamená „bez omezení“). Změna omezení skladu vyvolá republikaci skladu. Fulltext a feedy však vyhodnocují dostupné sklady v okamžiku indexace / generování, po změně omezení je proto potřeba přeindexovat fulltext a přegenerovat feedy.
Sklady přihlášeného uživatele (UserWithStocks#getStocks(), v REST API uživatele stocks) se drží v session spolu
se zemí regionu, pro kterou byly načteny. Při přechodu stejné session na web jiné země se načtou znovu
(UserWithStocksDataLoader, EshopUserConverter); cache profilu uživatele (/profiles/{login}) má zemi regionu
v klíči. Frontend, který si sklady uživatele drží sám (např. juan v JWT cookie user), je musí při změně webu / regionu
načíst znovu.
Projektově lze určit, které sklady jsou pro zákazníka dostupné. Toto se zajistí implementací těchto rozhraní:
com.fg.cps.eshop.productQuantity.service.PublishedProductStockProvidercom.fg.cps.eshop.order.service.stock.OrderStockProvider
PublishedProductStockProvider
Provider, který poskytuje seznam skladů, pro které se má zohledňovat skladové množství u publikovaných produktů na FE. To je důležité pro zobrazení produktů na FE, kdy skladová zásoba může ovlivňovat viditelnost produktu v seznamech a také možnost zakoupení produktu.
Pro registraci provideru je nutné implementovat rozhraní PublishedProductStockProvider a zaregistrovat ho ve Spring kontextu e-shop modulu.
Implementace tohoto rozhraní musí být sladěna s implementací com.fg.cps.eshop.order.service.stock.OrderStockProvider.
Příklady projektového použití mohou být:
- V případě, že existuje systémový (origin) katalog a ostatní katalogy z něj dědí, mohou být pro tyto katalogy k dispozici pouze skladové zásoby z některých skladů. V tomto případě je nutné implementovat provider, který bude vracet skladové zásoby podle aktuálního katalogu.
- Pro různé uživatele mohou být k dispozici různé skladové zásoby, např. na základě role uživatele nebo jiných kritérií. V tomto případě je nutné implementovat provider, který bude vracet skladové zásoby podle aktuálního uživatele.
Pro získání aktuálního uživatele, pro které jsou sklady vyhodnocený je doporučeno použít následující způsob:
// contextSimulator je spring bean `com.fg.cps.eshop.publishing.service.simulation.PublishedEntityContextSimulator`final CommonUser actualUser = ofNullable(contextSimulator.getUser()) .orElseGet(AbstractUserManager::getLoggedInUser);Pokud takto získaný uživatel je null jedná se o anonymního uživatele.
OrderStockProvider
Provider, který poskytuje seznam skladů, proti kterým se má validovat skladové množství a vytvářet rezervace pro danou objednávku/košík.
Výchozí implementace vrací všechny aktivní sklady omezené na zemi košíku / objednávky (viz Omezení skladu na země).
Pro registraci provideru je nutné implementovat rozhraní OrderStockProvider a zaregistrovat ho ve Spring kontextu e-shop modulu.
Implementace tohoto rozhraní musí být sladěna s implementací com.fg.cps.eshop.productQuantity.service.PublishedProductStockProvider.
Rezervace objednaného zboží
S vyhodnocením dostupného skladového množství souvisí i rezervace objednaného zboží.
Rezervace objednaného množství zajišťuje, že nelze objednat více kusů produktu, než je skladem, v okamžiku, kdy vznikla objednávka, ale ještě se nestihlo aktualizovat skladové množství. Rezervace se vždy váže ke konkrétnímu skladu a produktu. Dále pak ke konkrétní objednávce nebo rezervaci na prodejně.
Strategie vytváření rezervací je určena implementací com.fg.cps.eshop.order.service.stock.ProductQuantityBookingStrategy. Výchozí chování je takové, že rezervace vzniká v momentě vytvoření objednávky a vzniká na dostupných skladech postupně podle priority skladů. Tedy nejprve se vytvoří rezervace na primárním skladě, pokud je dostupné množství na primárním skladě nedostatečné, tak se vytvoří rezervace na sekundárním skladě a tak dále.
Toto výchozí chování lze ovlivnit vlastní implementací ProductQuantityBookingStrategy a registrací této implementace ve Spring kontextu e-shop modulu.
Princip rezervace objednaného množství je následující:
- Při vytvoření objednávky se vytvoří rezervace na množství objednávaného produktu. Rezervace je ve stavu
BOOKED. Tento stav znamená, že rezervace ponižuje dostupné skladové množství na odpovídajících skladech. V případě, že dojde ke změně skladového množství na skladě, tak tato změna nemá vliv na rezervace, protože objednávka nebyla ještě zpracována a tedy ani nedošlo k aktualizaci skladového množství (např. v ERP). - Z tohoto stavu může rezervace přejít do stavu
CANCELLED, v případě zrušení objednávky, nebo do stavuORDERED. - Stav
ORDEREDznamená, že objednávka byla zpracována (standardně v ERP), a tedy došlo k ponížení skladového množství v primárním skladovém systému. Pokud tedy následně dojde k aktualizaci skladového množství na e-shopovém skladě, dojde i k uvolnění rezervace a již se s ní nepočítá. - Do stavu
ORDEREDse objednávka dostává v případě, že je u objednávky nastaven údajbookingExpiration(k tomu slouží modifikaceOrderModificationSetBookingExpiration). Ve výchozím nastavení k této události dochází v momentě, kdy je objednávka převedena do stavuprocessing-order(zpracovaná). Toto výchozí chování lze konfiguračně ovlivnit, viz. konfigurace objednávek a atributbookingExpirationState.
Příklad vytvoření standardní rezervace objednaného zboží:
- Produkt "A" je skladem 5 ks.
- V čase T vznikne objednávka na 3 ks produktu "A".
- Dostupné množství produktu "A" se sníží na 2 ks a vytvoří se rezervace na 3 ks produktu "A". Rezervace je ve stavu
BOOKED. - V čase T+1 dojde k aktualizaci skladového množství na 4 ks. Rezervace zůstává na 3 ks a je stále ve stavu
BOOKED. Dostupné množství je 1 ks, tedy 4 ks skladem - 3 ks rezervace. - V čase T+2 dojde k aktualizaci objednávky na stav
processing-order. Rezervace se převede do stavuORDERED. Rezervace je stále na 3 ks, skladem 4 ks a dostupné množství je 1 ks. - V čase T+3 dojde k opětovné aktualizaci skladového množství na 1 ks. Rezervace je ve stavu
ORDERED, dochází k uvolnění rezervace, protože je již započítána do aktuálního skladového množství, a přepnutí do stavuFINISHED. Dostupné množství je tedy 1 ks.
