55from viur .core .bones import BaseBone
66from viur .core .prototypes import Tree
77from viur .core .prototypes .tree import SkelType
8+ from viur .core .session import Session
89from viur .core .skeleton import Skeleton , SkeletonInstance
910from viur .shop .modules .abstract import ShopModuleAbstract
1011from viur .shop .types import *
1112from viur .shop .types .exceptions import InvalidStateError
12- from ..globals import SENTINEL , SHOP_LOGGER
13+ from ..globals import SENTINEL , SHOP_INSTANCE , SHOP_LOGGER
1314from ..services import EVENT_SERVICE , Event
1415from ..skeletons .cart import CartItemSkel , CartNodeSkel
1516from ..skeletons .order import OrderSkel
@@ -40,9 +41,9 @@ def baseSkel(
4041 if sub_skel is None :
4142 return cls () # noqa
4243 if isinstance (sub_skel , list ):
43- return cls .subSkel (* sub_skel )
44+ return cls .subskel (* sub_skel )
4445 else :
45- return cls .subSkel (sub_skel )
46+ return cls .subskel (sub_skel )
4647
4748 def canView (self , skelType : SkelType , skel : SkeletonInstance ) -> bool :
4849 if super ().canView (skelType , skel ):
@@ -63,7 +64,7 @@ def current_session_cart_key(self) -> db.Key | None:
6364 def get_current_session_cart_key (self , * , create_if_missing : bool = False ) -> db .Key | None :
6465 if user := current .user .get ():
6566 user_skel = conf .main_app .vi .user .viewSkel ()
66- user_skel .fromDB (user ["key" ])
67+ user_skel .read (user ["key" ])
6768 if user_skel ["basket" ]:
6869 self .session ["session_cart_key" ] = user_skel ["basket" ]["dest" ]["key" ]
6970 current .session .get ().markChanged ()
@@ -74,7 +75,7 @@ def get_current_session_cart_key(self, *, create_if_missing: bool = False) -> db
7475 @property
7576 def current_session_cart (self ) -> SkeletonInstance_T [CartNodeSkel ]: # TODO: Caching
7677 skel = self .viewSkel ("node" )
77- if not skel .fromDB (self .get_current_session_cart_key (create_if_missing = True )):
78+ if not skel .read (self .get_current_session_cart_key (create_if_missing = True )):
7879 logger .critical (f"Invalid session_cart_key { self .current_session_cart_key } ?! Not in DB!" )
7980 self .detach_session_cart ()
8081 return self .current_session_cart
@@ -87,8 +88,8 @@ def _ensure_current_session_cart(self) -> db.Key:
8788 root_node ["is_root_node" ] = True
8889 root_node ["name" ] = root_node .name .getDefaultValue (root_node )
8990 root_node ["cart_type" ] = CartType .BASKET
90- key = root_node .toDB ()
91- self .session ["session_cart_key" ] = key
91+ root_node .write ()
92+ self .session ["session_cart_key" ] = root_node [ " key" ]
9293 current .session .get ().markChanged ()
9394 # Store basket at the user skel, it will be shared over multiple sessions / devices
9495 if user := current .user .get ():
@@ -106,9 +107,9 @@ def detach_session_cart(self) -> db.Key:
106107 @staticmethod
107108 def _set_basket_txn (user_key : db .Key , basket_key : db .Key | None ) -> SkeletonInstance :
108109 user_skel = conf .main_app .vi .user .editSkel ()
109- user_skel .fromDB (user_key )
110+ user_skel .read (user_key )
110111 user_skel .setBoneValue ("basket" , basket_key )
111- user_skel .toDB ()
112+ user_skel .write ()
112113 return user_skel
113114
114115 def get_available_root_nodes (self , * args , ** kwargs ) -> list [dict [t .Literal ["name" , "key" ], str ]]:
@@ -160,7 +161,7 @@ def is_valid_node(
160161 """
161162 # TODO: return (okay_status, reason, skel) tuple/Dataclass?
162163 skel = self .viewSkel ("node" )
163- if not skel .fromDB (node_key ):
164+ if not skel .read (node_key ):
164165 logger .debug (f"fail reason: 404" )
165166 return False
166167 logger .debug (f'{ skel = } ' )
@@ -224,7 +225,7 @@ def cart_get(
224225 if not isinstance (cart_key , db .Key ):
225226 raise TypeError (f"cart_key must be an instance of db.Key" )
226227 skel = self .viewSkel (skel_type )
227- if not skel .fromDB (cart_key ):
228+ if not skel .read (cart_key ):
228229 logger .debug (f"Cart { cart_key } does not exist" )
229230 return None
230231 if not self .canView (skel_type , skel ):
@@ -278,13 +279,13 @@ def add_or_update_article(
278279 res = skel .setBoneValue ("article" , article_key )
279280 skel ["parententry" ] = parent_cart_key
280281 parent_skel = self .viewSkel ("node" )
281- assert parent_skel .fromDB (parent_cart_key )
282+ assert parent_skel .read (parent_cart_key )
282283 if parent_skel ["is_root_node" ]:
283284 skel ["parentrepo" ] = parent_skel ["key" ]
284285 else :
285286 skel ["parentrepo" ] = parent_skel ["parentrepo" ]
286287 article_skel : SkeletonInstance = self .shop .article_skel ()
287- if not article_skel .fromDB (article_key ):
288+ if not article_skel .read (article_key ):
288289 raise errors .NotFound (f"Article with key { article_key = } does not exist!" )
289290 if not article_skel ["shop_listed" ]:
290291 # logger.debug(f"not listed: {article_skel=}")
@@ -337,7 +338,7 @@ def add_or_update_article(
337338 descr_appendix = f'Quantity of free article cannot be greater than 1! (reached { skel ["quantity" ]} )'
338339 )
339340 skel = self .additional_add_or_update_article (skel , ** kwargs )
340- key = skel .toDB ()
341+ skel .write ()
341342 EVENT_SERVICE .call (Event .ARTICLE_CHANGED , skel = skel , deleted = False )
342343 self .clear_children_cache ()
343344 # TODO: Validate quantity with hook (stock availability)
@@ -346,7 +347,7 @@ def add_or_update_article(
346347 skel = parent_skel
347348 )
348349 EVENT_SERVICE .call (Event .CART_CHANGED , skel = parent_skel , deleted = False )
349- parent_skel .toDB ()
350+ parent_skel .write ()
350351
351352 return skel
352353
@@ -370,7 +371,7 @@ def move_article(
370371 parent_skel = self .viewSkel ("node" )
371372 if not self .is_valid_node (new_parent_cart_key ):
372373 raise e .InvalidArgumentException ("parent_cart_key" , parent_cart_key )
373- if not parent_skel .fromDB (new_parent_cart_key ):
374+ if not parent_skel .read (new_parent_cart_key ):
374375 raise e .InvalidArgumentException (
375376 "new_parent_cart_key" , new_parent_cart_key ,
376377 f"Target cart node does not exist"
@@ -381,7 +382,7 @@ def move_article(
381382 f"Target cart node is inside a different repo"
382383 )
383384 skel ["parententry" ] = new_parent_cart_key
384- skel .toDB ()
385+ skel .write ()
385386 EVENT_SERVICE .call (Event .ARTICLE_CHANGED , skel = skel , deleted = False )
386387 return skel
387388
@@ -415,7 +416,7 @@ def cart_add(
415416 discount_key = discount_key ,
416417 )
417418 skel = self .additional_cart_add (skel , ** kwargs )
418- skel .toDB ()
419+ skel .write ()
419420 EVENT_SERVICE .call (Event .CART_CHANGED , skel = skel , deleted = False )
420421 self .onAdded ("node" , skel )
421422 return skel
@@ -445,7 +446,7 @@ def cart_update(
445446 # TODO: must be inside a own root node ...
446447 # if not self.canEdit(skel):
447448 # raise errors.Forbidden
448- assert skel .fromDB (cart_key )
449+ assert skel .read (cart_key )
449450 skel = self ._cart_set_values (
450451 skel = skel ,
451452 parent_cart_key = parent_cart_key ,
@@ -456,7 +457,7 @@ def cart_update(
456457 discount_key = discount_key ,
457458 )
458459 self .additional_cart_update (skel , ** kwargs )
459- skel .toDB ()
460+ skel .write ()
460461 EVENT_SERVICE .call (Event .CART_CHANGED , skel = skel , deleted = False )
461462 return skel
462463
@@ -482,7 +483,7 @@ def _cart_set_values(
482483 if not self .is_valid_node (parent_cart_key ):
483484 raise e .InvalidArgumentException ("parent_cart_key" , parent_cart_key )
484485 parent_skel = self .viewSkel ("node" )
485- assert parent_skel .fromDB (parent_cart_key )
486+ assert parent_skel .read (parent_cart_key )
486487 if parent_skel ["is_root_node" ]:
487488 skel ["parentrepo" ] = parent_skel ["key" ]
488489 else :
@@ -531,10 +532,12 @@ def cart_remove(
531532 self ,
532533 cart_key : db .Key ,
533534 ) -> None :
534- self .deleteRecursive (cart_key )
535535 skel = self .editSkel ("node" )
536- if not skel .fromDB (cart_key ):
536+ if not skel .read (cart_key ):
537537 raise errors .NotFound
538+ # This delete could fail if the cart is used by an order
539+ skel .delete ()
540+ self .deleteRecursive (cart_key )
538541 if skel ["parententry" ] is None or skel ["is_root_node" ]:
539542 logger .info (f"{ skel ['key' ]} was a root node!" )
540543 # raise NotImplementedError("Cannot delete root node")
@@ -543,7 +546,6 @@ def cart_remove(
543546 self .detach_session_cart ()
544547 # del self.session["session_cart_key"]
545548 # current.session.get().markChanged()
546- skel .delete ()
547549 EVENT_SERVICE .call (Event .CART_CHANGED , skel = skel , deleted = True )
548550
549551 # --- Hooks ---------------------------------------------------------------
@@ -629,14 +631,14 @@ def freeze_cart(
629631 # ensure each article still exists and shop_listed is True
630632 return NotImplemented
631633 cart_skel = self .editSkel ("node" )
632- assert cart_skel .fromDB (cart_key )
634+ assert cart_skel .read (cart_key )
633635 """
634636 skel["shop_vat_rate_value"] = self.shop.vat_rate.get_vat_rate_for_country(
635637 country=order_skel["billing_address"]["dest"]["country"],
636638 category=article_skel["shop_vat_rate_category"],
637639 )
638640 """
639- cart_skel .toDB ()
641+ cart_skel .write ()
640642
641643 # -------------------------------------------------------------------------
642644
@@ -646,13 +648,13 @@ def get_discount_for_leaf(
646648 ) -> list [SkeletonInstance ]:
647649 if isinstance (leaf_key_or_skel , db .Key ):
648650 skel = self .viewSkel ("leaf" )
649- skel .fromDB (leaf_key_or_skel )
651+ skel .read (leaf_key_or_skel )
650652 else :
651653 skel = leaf_key_or_skel
652654 discounts = []
653655 while (pk := skel ["parententry" ]):
654656 skel = self .viewSkel ("node" , sub_skel = "discount" )
655- if not skel .fromDB (pk ):
657+ if not skel .read (pk ):
656658 raise InvalidStateError (f"{ pk = } doesn't exist!" )
657659 if discount := skel ["discount" ]:
658660 discounts .append (discount ["dest" ])
@@ -665,10 +667,32 @@ def add_new_parent(self, leaf_skel, **kwargs):
665667 for key , value in kwargs .items ():
666668 new_parent_skel [key ] = value # TODO: use .setBoneValue?
667669 self .onAdd ("node" , new_parent_skel )
668- new_parent_skel .toDB ()
670+ new_parent_skel .write ()
669671 self .onAdded ("node" , new_parent_skel )
670672 EVENT_SERVICE .call (Event .CART_CHANGED , skel = new_parent_skel , deleted = False )
671673 leaf_skel ["parententry" ] = new_parent_skel ["key" ]
672- leaf_skel .toDB ()
674+ leaf_skel .write ()
673675 EVENT_SERVICE .call (Event .ARTICLE_CHANGED , skel = leaf_skel , deleted = False )
674676 return new_parent_skel
677+
678+
679+ try :
680+ Session .on_delete
681+ except AttributeError : # backward compatibility for viur-core
682+ from viur .core .version import __version__
683+
684+ logger .warning (f"viur-core { __version__ } has no Session.on_delete" )
685+ Session .on_delete = lambda * _ , ** __ : None
686+
687+
688+ @Session .on_delete
689+ def delete_guest_cart (session : db .Entity ) -> None :
690+ """Delete carts from guest sessions to avoid orphaned carts"""
691+ if session ["user" ] != Session .GUEST_USER :
692+ return
693+ try :
694+ cart = session ["data" ]["shop" ]["cart" ]["session_cart_key" ]
695+ except (KeyError , TypeError ):
696+ return
697+ SHOP_INSTANCE .get ().cart .cart_remove (cart )
698+ logger .debug (f"Deleted { cart = } and children after deleting { session = } " )
0 commit comments