Skip to content

Commit 6156386

Browse files
committed
fix: Use vat rate based on the current cart's shipping address country
1 parent 99a1e4a commit 6156386

2 files changed

Lines changed: 46 additions & 1 deletion

File tree

src/viur/shop/modules/cart.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,30 @@ def add_new_parent(self, leaf_skel, **kwargs):
696696
EVENT_SERVICE.call(Event.ARTICLE_CHANGED, skel=leaf_skel, deleted=False)
697697
return new_parent_skel
698698

699+
def get_cached_cart_skel(self, key: db.Key) -> SkeletonInstance_T[CartNodeSkel]:
700+
cache = current.request_data.get().setdefault("shop_cache_cart_skel", {})
701+
key = db.keyHelper(key, CartNodeSkel.kindName)
702+
try:
703+
parent_skel = cache[key]
704+
except KeyError:
705+
parent_skel = self.viewSkel("node")
706+
assert parent_skel.read(key)
707+
cache[key] = parent_skel
708+
return parent_skel
709+
710+
def get_closest_node(
711+
self,
712+
start: SkeletonInstance_T[CartNodeSkel | CartItemSkel],
713+
condition: (lambda skel: True),
714+
) -> SkeletonInstance_T[CartNodeSkel] | None:
715+
while True:
716+
parent_skel = self.get_cached_cart_skel(start["parententry"])
717+
if condition(parent_skel):
718+
return parent_skel # type:ignore
719+
if parent_skel["is_root_node"]:
720+
return None # NotFound
721+
start = parent_skel
722+
699723

700724
try:
701725
Session.on_delete

src/viur/shop/types/price.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,11 +268,17 @@ def vat_rate_percentage(self) -> float:
268268
269269
:return: VAT rate as float between 0.0 and 1.0.
270270
"""
271+
try:
272+
country = self._shipping_address["dest"]["country"]
273+
except (KeyError, TypeError):
274+
country = None
275+
# logger.debug(f"Using country: {country}")
271276
try:
272277
# FIXME: self.article_skel has here sometimes renderPreparation set,
273278
# but toolkit.without_render_preparation is already called in __init__
274279
# What's going on here?
275280
vat_rate = SHOP_INSTANCE.get().vat_rate.get_vat_rate_for_country(
281+
country=country,
276282
category=toolkit.without_render_preparation(self.article_skel)["shop_vat_rate_category"],
277283
)
278284
except ConfigurationError as e: # TODO(discussion): Or re-raise or implement fallback?
@@ -292,6 +298,21 @@ def vat_included(self) -> float:
292298
except TypeError: # One value is None
293299
return 0.0
294300

301+
@functools.cached_property
302+
def _shipping_address(self):
303+
"""
304+
Returns the shipping address for the closest cart node.
305+
"""
306+
if not self.is_in_cart:
307+
return None
308+
res = SHOP_INSTANCE.get().cart.get_closest_node(
309+
self.cart_leaf,
310+
condition=lambda skel: skel["shipping_address"] is not None,
311+
)
312+
if res:
313+
return res["shipping_address"]
314+
return res
315+
295316
def to_dict(self) -> dict:
296317
"""
297318
Serializes the relevant pricing fields to a dictionary, suitable for frontend or API use.
@@ -302,7 +323,7 @@ def to_dict(self) -> dict:
302323
return {
303324
attr_name: getattr(self, attr_name)
304325
for attr_name, attr_value in vars(self.__class__).items()
305-
if isinstance(attr_value, (property, functools.cached_property))
326+
if isinstance(attr_value, (property, functools.cached_property)) and not attr_name.startswith("_")
306327
} | utils.json.loads(json.dumps({ # must be JSON serializable for vi renderer
307328
"cart_discounts": self.cart_discounts,
308329
"article_discount": self.article_discount,

0 commit comments

Comments
 (0)