EdeeShop · Implementace

Aktualizace nákupního košíku

Upraveno

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:

java
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:

java
@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:

  1. Vytvořit novou implementaci com.fg.cps.eshop.cart.service.update.CartUpdate (dále cartUpdate).
  2. Implementovat com.fg.cps.eshop.cart.service.CartUpdater (neplatí pokud cartUpdate implementuje CartUpdateWithExecute).

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:
json
{	"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:
json
{	"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:
json
{	"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:
json
{	"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:

json
{	"type": "SetItemQuantity",	"itemId": 377957122051,	"quantity": 10}

SetItemsQuantity

Nastaví počet kusů existujících položek v košíku.

Payload:

json
{	"type": "SetItemsQuantity",	"items": [		{			"itemId": 377957122051,			"quantity": 10		}	]}

RemoveCartItem

Smazání položek z košíku, dle itemId položky.

Payload:

json
{  "type": "RemoveCartItem",  "itemIds": [    377957122050  ]}

ContactInformationUpdate

Aktualizace kontaktních informací košíku.

Payload:

json
{	"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:

json
{	"type": "InitDefaultShippingUpdate"}

SetShippingByCodeUpdate

Akce nastaví dopravu dle kódu dopravy. Payload:

json
{	"type": "SetShippingByCodeUpdate",	"shippingCode" : "osobni-odber"}

SetShippingByIdUpdate

Akce nastaví dopravu dle id dopravy. Payload:

json
{	"type": "SetShippingByIdUpdate",	"shippingId" : 123}

SetPickupPointByCodeUpdate

Nastaví místo osobního odběru, dle kódu odběrového místa.

Payload:

json
{	"type": "SetPickupPointByCodeUpdate",	"pickupPointCode": "xxx"}

SetPickupPointByIdUpdate

Nastaví místo osobního odběru, dle id odběrového místa.

Payload:

json
{	"type": "SetPickupPointByIdUpdate",	"pickupPointId": xxx}

InitDefaultPaymentUpdate

Nastaví výchozí typ platby pro košík zákazníka

Payload:

json
{	"type": "InitDefaultPaymentUpdate"}

SetPaymentByCodeUpdate

Nastaví platbu dle kódu pro košík zákazníka. Payload:

json
{	"type": "SetPaymentByCodeUpdate",	"paymentCode" : "hotove"}

SetPaymentByIdUpdate

Nastaví platbu dle id pro košík zákazníka. Payload:

json
{	"type": "SetPaymentByCodeUpdate",	"paymentId" : 123}

ConfirmOrder

Transformace (Potvrzení) aktuálního košíku do objednávky

Payload:

json
{	"type": "ConfirmOrder"}

RefreshCart

Aktualizace košíku.

Payload:

json
{	"type": "RefreshCart"}

ShoppingCartPageConfirm

Payload:

json
{	"type": "ShoppingCartPageConfirm"}

VoucherUseUpdate

Přiřazení voucheru k aktuálnímu košíku.

Payload:

json
{	"type": "VoucherUseUpdate",	"voucherCode": "xxx"}

VoucherCancelUpdate

Smazání voucheru z aktuálního košíku. Payload:

json
{  "type": "VoucherCancelUpdate",  "voucherCode": "xxx"}

CreditUpdate

Slouží k nastavení použití kreditů v košíku.

Payload:

json
{	"type": "CreditUpdate",	"useCredit": true}

SelectBundleBonus

Vybere bonusový produkt v košíku.

Payload:

json
{	"type": "SelectBundleBonus",	"bundleId": 1,	"bonusProductId": 1,	"requiredCartItemId": 1,	"index": 1,	"selected": true}

BundleBonusChangeQuantity

Změní počet kusů bonusového produktu

Payload:

json
{	"type": "BundleBonusChangeQuantity",	"bundleId": 1,	"bonusProductId": 1,	"requiredCartItemId": 1,	"index": 1,	"quantity": "1"}

SelectBonusVariant

Vybere variantu bonusového produktu.

Payload:

json
{  "type": "SelectBonusVariant",  "bundleId": 1,  "bonusProductId": 1,  "requiredCartItemId": 1,  "index": 1,  "selectedVariantCode": "tadada"}

SelectNoBonus

Zruší vybraný bonusový produkt.

Payload:

json
{	"type": "SelectNoBonus",	"bundleId": 1,	"requiredCartItemId": 1}

ZboziGdprOptionUpdate

Potvrzení GDPR v rámci košíku.

Payload:

json
{	"type": "ZboziGdprOptionUpdate",	"zboziGdprOption" : 1}

HeurekaOverenoOptionUpdate

Potvrzení heureky v rámci košíku.

Payload:

json
{	"type": "HeurekaOverenoOptionUpdate",	"heurekaOverenoOption" : 1}