Aktualizace nákupního košíku
Tento dokument popisuje postup aktualizace a modifikace obsahu nákupního košíku.
Aktualizace košíku se vždy provádí metodou ShoppingCartService#updateCart.
Tato operace je rozčleněna do tří částí: beforeCartUpdateInterceptor, cartUpdate a afterCartUpdateInterceptor. Všechny tyto části se však vykonávají v rámci jedné transakce.
CartUpdate
Každá aktualizace košíku (dále jen cartUpdate) implementuje rozhraní com.fg.cps.eshop.cart.service.update.CartUpdate.
CartUpdate nese parametry aktualizace, např. "přidání produktu"
nese identifikaci produktu, který se má přidat do košíku a také požadované množství.
Každá implementace CartUpdate pak musí být zaregistrována do com.fg.cps.eshop.order.OrderFeature:
final OrderFeature orderFeature = getFeature(OrderFeature.class);if (orderFeature.isFeatureEnabled() && getFeature(RestApiFeature.class).isFeatureEnabled()) { orderFeature.addCartUpdate(ActivateMarketingCampaignUpdate.class);}Důvodem je nutná inicializace serializace a deserializace cartUpdate pro RestApi.
CartUpdateAffectingPrices
V případě, že cartUpdate implementuje CartUpdateAffectingPrices,
znamená to, že cartUpdate má vliv na celkovou cenu objednávku a je nutné např. přepočítat cenové politiky. Typickým příkladem je
přidání produktu do košíku nebo uplatnění slevového poukazu. Příkladem aktualizace, která nemá vliv na cenu může být
např. změna fakturační adresy nebo přidání poznámky k objednávce.
CartUpdater
Každý cartUpdate musí mít odpovídající implementaci CartUpdater,
který zajistí zpracování aktualizace. CartUpdater je nutné zaregistrovat do aplikačního (spring) kontextu EdeeShop modulu.
V případě jednoduchých updatů (např. změna nevyžaduje žádnou servisní třídu) může cartUpdate implementovat CartUpdateWithExecute.
Pro takový cartUpdate již nemusí existovat CartUpdater.
CartUpdateInterceptor
Před každým cartUpdate je možné registrovat BeforeCartUpdateInterceptor.
Tyto interceptory jsou vhodné například pro validaci dat v cartUpdate.
Po vykonání cartUpdate je možné přidat AfterCartUpdateInterceptor.
Tyto interceptory jsou vhodné například k ověření dat v košíku, přepočtu cen a slev nebo k automatickým úpravám dat v košíku.
Ochrana proti zbytečnému založení košíku
CartContext#getOrCreateShoppingCart košík založí, pokud ještě neexistuje. Interceptor, který se zajímá o obsah
košíku, se proto musí nejdřív zeptat, jestli košík vůbec existuje — jinak už jen tím, že se na něj podívá, vytvoří
prázdný košík (a s ním i řádky v DB) pro každého návštěvníka, přes kterého takový cartUpdate proteče:
@Overridepublic void intercept(@NonNull CartContext cartContext, @NonNull CartUpdateContext cartUpdateContext, @NonNull CartUpdate cartUpdate) throws CartUpdateException { if (!cartContext.isShoppingCartExists()) { return; } // ... práce s košíkem}isShoppingCartExists() košík nezakládá — jen se ho pokusí načíst (getShoppingCartIfExists). Guard platí stejně pro
AfterCartUpdateInterceptor a pro filtry doprav/plateb; příkladem jsou SelectedShippingActuator,
SelectedPaymentActuator nebo VoucherCodeCartValidator. Výjimkou jsou interceptory, které košík zakládat mají
(typicky reakce na přidání položky).
Zpracování chyby
V případě, že cartUpdate nebo některý interceptor selže, musí být vyvolána chyba. Pokud se jedná o uživatelskou chybu,
např.: uživatel zadal neplatná data pro daný okamžik nebo na ně nemá práva, měla by být vyvolána lokalizovaná
CartUpdateException,
která se zpropaguje přímo uživateli. Pokud však nastala při zpracování neočekávaná interní chyba, lze použít klasické
výjimky např.: IllegalArgumentException.
Celá aktualizace (tj. beforeCartUpdateInterceptor, cartUpdate, afterCartUpdateInterceptor) se vykonává v jedné transakci,
tudíž pokud něco selže, provede se rollback celé aktualizace. V případě, že rollback celé aktualizace není žádoucí, je možné vyvolat výjimku CartUpdateNoRollbackException, která rollback transakce neprovede.
Zápis do dat košíku
Data košíku jsou reprezentována CartData (a jeho rozšířením).
Pro zápis existuje CartDataWriter.
Toto rozhraní je "skryté", protože nechceme umožnit měnit košík jinak než přes ShoppingCartService#updateCart.
V rámci cartUpdate a cartUpdateInterceptor je však možné CartData
přetypovat na CartDataWriter a tak do něj zapisovat.
Zápis přes CartDataWriter mimo ShoppingCartService#updateCart může způsobit nekonzistentní stav košíku.
Tento "writer" mechanismus platí i pro vnořená data košíku, tedy položky, dopravy, platby, politiky a adresy. Např. pro CartItem existuje CartItemWriter.
Poznámka: K vytváření těchto vnořených dat, je nutné používat odpovídající metody na CartContext (více viz popis nákupního košíku).
BeforeCartUpdateEvent a AfterCartUpdateEvent
Před a po aktualizaci košíku se vypalují BeforeCartUpdateEvent a AfterCartUpdateEvent.
Vytvoření vlastní aktualizace košíku
Pro vytvoření nové aktualizace košíku je potřeba:
- Vytvořit novou implementaci
com.fg.cps.eshop.cart.service.update.CartUpdate(dálecartUpdate). - Implementovat
com.fg.cps.eshop.cart.service.CartUpdater(neplatí pokudcartUpdateimplementujeCartUpdateWithExecute).
Zprávy aktualizace košíku
Během aktualizace košíku se mohou vyskytnout různé situace, o kterých je potřeba informovat zákazníka. Rejstřík všech možných zpráv se nachází zde.
Další možnosti přizpůsobení
CartContextPriceListResolver
Pomocí implementace CartContextPriceListResolver je možné ovlivnit ceníky, které se použijí při výpočtech v košíku.
Pokud není toto rozhraní implementováno, tak se použije výchozí seznam ceníků získaný pomocí PriceResolver#getPriceListCodesForCurrentUser(Catalog).
Systémové implementace aktualizace košíku
V této sekci budou popsané již existující systémové implementace aktualizace košíku a jejich JSON payload.
AddItemByIdToCart
Tato aktualizace slouží k přidání položky do košíku pomocí id produktu. Nepovinné parametry:
- adjustment
- source
- sourceId Payload:
{ "type": "AddItemByIdToCart", "productId": 123, "quantity": 1, "adjustment": "xxx", "source": "source", "sourceId": 123 }AddItemByCodeToCart
Tato aktualizace slouží k přidání položky do košíku pomocí kódu produktu. Nepovinné parametry:
- adjustment
- source
- sourceId Payload:
{ "type": "AddItemByCodeToCart", "productCode": "kolecko-cerne", "quantity": 1, "adjustment": "xxx", "source": "source", "sourceId": 123 }AddItemsByCodeToCart
Tato aktualizace slouží k přidání více položek do košíku Nepovinné parametry:
- adjustment
- source
- sourceId Payload:
{ "type": "AddItemsByCodeToCart", "items": [ { "productCode": "kolecko-cerna", "quantity": 1 }, { "productCode": "kolecko-cerna", "quantity": 1 } ], "source": "source", "sourceId": 123}AddItemsByIdToCart
Tato aktualizace slouží k přidání více položek do košíku Nepovinné parametry:
- adjustment
- source
- sourceId Payload:
{ "type": "AddItemsByIdToCart", "items": [ { "productId": 123, "quantity": 1 }, { "productCode": 321, "quantity": 1 } ], "source": "source", "sourceId": 123}SetItemQuantity
Nastaví počet kusů existující položky v košíku.
Payload:
{ "type": "SetItemQuantity", "itemId": 377957122051, "quantity": 10}SetItemsQuantity
Nastaví počet kusů existujících položek v košíku.
Payload:
{ "type": "SetItemsQuantity", "items": [ { "itemId": 377957122051, "quantity": 10 } ]}RemoveCartItem
Smazání položek z košíku, dle itemId položky.
Payload:
{ "type": "RemoveCartItem", "itemIds": [ 377957122050 ]}ContactInformationUpdate
Aktualizace kontaktních informací košíku.
Payload:
{ "type": "ContactInformationUpdate", "customerEmail": "vomacka@fg.cz", "customerPhone": "+420777888999", "billingAddress": { "fullName": "Pepa vomacka", "street": "Vomackova", "city": "Hradec kralove", "postCode": "50003", "countryCode": "CZ", "companyName": "FG", "regNumber": "", "taxRegNumber": "", "idUserAddress": 123 }, "deliveryAddress": { "fullName": "", "street": "", "city": "", "postCode": "", "countryCode": "", "phone": "", "companyName": "", "idUserAddress": 321 }, "differentDeliveryAddress": 1, "customerType": 0, "note": "Tohle je ta nejlepsi objednavka."
}InitDefaultShippingUpdate
Akce nastaví původní hodnotu pro dopravu. Payload:
{ "type": "InitDefaultShippingUpdate"}SetShippingByCodeUpdate
Akce nastaví dopravu dle kódu dopravy. Payload:
{ "type": "SetShippingByCodeUpdate", "shippingCode" : "osobni-odber"}SetShippingByIdUpdate
Akce nastaví dopravu dle id dopravy. Payload:
{ "type": "SetShippingByIdUpdate", "shippingId" : 123}SetPickupPointByCodeUpdate
Nastaví místo osobního odběru, dle kódu odběrového místa.
Payload:
{ "type": "SetPickupPointByCodeUpdate", "pickupPointCode": "xxx"}SetPickupPointByIdUpdate
Nastaví místo osobního odběru, dle id odběrového místa.
Payload:
{ "type": "SetPickupPointByIdUpdate", "pickupPointId": xxx}InitDefaultPaymentUpdate
Nastaví výchozí typ platby pro košík zákazníka
Payload:
{ "type": "InitDefaultPaymentUpdate"}SetPaymentByCodeUpdate
Nastaví platbu dle kódu pro košík zákazníka. Payload:
{ "type": "SetPaymentByCodeUpdate", "paymentCode" : "hotove"}SetPaymentByIdUpdate
Nastaví platbu dle id pro košík zákazníka. Payload:
{ "type": "SetPaymentByCodeUpdate", "paymentId" : 123}ConfirmOrder
Transformace (Potvrzení) aktuálního košíku do objednávky
Payload:
{ "type": "ConfirmOrder"}RefreshCart
Aktualizace košíku.
Payload:
{ "type": "RefreshCart"}ShoppingCartPageConfirm
Payload:
{ "type": "ShoppingCartPageConfirm"}VoucherUseUpdate
Přiřazení voucheru k aktuálnímu košíku.
Payload:
{ "type": "VoucherUseUpdate", "voucherCode": "xxx"}VoucherCancelUpdate
Smazání voucheru z aktuálního košíku. Payload:
{ "type": "VoucherCancelUpdate", "voucherCode": "xxx"}CreditUpdate
Slouží k nastavení použití kreditů v košíku.
Payload:
{ "type": "CreditUpdate", "useCredit": true}SelectBundleBonus
Vybere bonusový produkt v košíku.
Payload:
{ "type": "SelectBundleBonus", "bundleId": 1, "bonusProductId": 1, "requiredCartItemId": 1, "index": 1, "selected": true}BundleBonusChangeQuantity
Změní počet kusů bonusového produktu
Payload:
{ "type": "BundleBonusChangeQuantity", "bundleId": 1, "bonusProductId": 1, "requiredCartItemId": 1, "index": 1, "quantity": "1"}SelectBonusVariant
Vybere variantu bonusového produktu.
Payload:
{ "type": "SelectBonusVariant", "bundleId": 1, "bonusProductId": 1, "requiredCartItemId": 1, "index": 1, "selectedVariantCode": "tadada"}SelectNoBonus
Zruší vybraný bonusový produkt.
Payload:
{ "type": "SelectNoBonus", "bundleId": 1, "requiredCartItemId": 1}ZboziGdprOptionUpdate
Potvrzení GDPR v rámci košíku.
Payload:
{ "type": "ZboziGdprOptionUpdate", "zboziGdprOption" : 1}HeurekaOverenoOptionUpdate
Potvrzení heureky v rámci košíku.
Payload:
{ "type": "HeurekaOverenoOptionUpdate", "heurekaOverenoOption" : 1}