| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277 |
- from pathlib import Path
- from typing import NoReturn
- import shortuuid
- from app.schema import CartUpdate, DocumentProduct, CartSessionItem, Settings, ProductInfoCode
- from app.template import make_product_info_code
- from app.db import get_product_by_product_id, get_products_id_db
- from app.parser import parse_file
- def product_exist_in_cart(
- product_id: str,
- size: str | None,
- style: str | None,
- product_variation_id: str | None,
- session_cart_items: dict[str, dict[str, str | dict[str, str]]],
- ) -> bool:
- """Check if given product exists in the cart already by constructing
- a full label using the product's id and eventual size and style
- options.
- """
- if product_id in session_cart_items or product_variation_id in session_cart_items:
- return True
- return False
- def initialize_cart(session, currency: str) -> dict[int, str] | NoReturn:
- """Initialize cart if no cart object is present yet in session."""
- if "cart" not in session:
- order_id = shortuuid.uuid()
- session["cart"] = {
- "meta": {
- "order_id": order_id,
- "currency": currency,
- "subtotal": 0,
- "total_weight": 0,
- "shipping": 0,
- "total": 0,
- "item_amount": 0,
- },
- "items": {},
- }
- def get_inventory_amount(session_item, products, settings) -> int | None:
- """Returns the inventory amount value for any product by matching it
- against the list of selected products in the cart. We need to make
- use of the selected product's style and size options in order to
- retrieve the correct inventory amount value.
- """
- inventory_amount = None
- product = parse_file(settings.git_repo, Path(products["products"][session_item["product_id"]]), settings.block_types)
- for variation in product.meta.inventory:
- if variation.product_variation_id == session_item["options"]["product_variation_id"]:
- return variation.amount
- def update_cart_meta(
- session,
- shipping_amount: int,
- settings,
- ) -> NoReturn:
- """Update Meta key of Session Cart (subtotal, shipping, total).
- Re-compute Meta's item_count, subtotal, shipping and total from
- each item so that we don't need to check for inventory's item
- amount a second time (we did it when updating
- each item in update_cart).
- """
- session_meta_item_amount = 0
- session_meta_subtotal = 0
- session_meta_weight = 0
- products = get_products_id_db(settings)
- for item in session["items"].values():
- if products is not None:
- # when doing GET /checkout, checks that user's cart can
- # proceed with the payment step or if not:
- # - either remove any sold out item from the cart
- # - or adjust user's cart to the max available number of
- # items set in the inventory of the given item
- inventory_amount = get_inventory_amount(item, products, settings)
- if inventory_amount == 0:
- # if inventory amount for given item has meanwhile gone to 0,
- # don't update the meta fields
- pass
- else:
- # else re-compute meta values
- item_quantity = 0
- if item["quantity"] >= inventory_amount:
- item_quantity = inventory_amount
- else:
- item_quantity = item["quantity"]
- session_meta_item_amount += item_quantity
- session_meta_subtotal += item_quantity * item["price"]
- session_meta_weight += item_quantity * item["options"]["weight"]
- # -- update also each item quantity and availability under session.cart.items
- if item["options"]["product_variation_id"]:
- cart_product_id = item["options"]["product_variation_id"]
- else:
- cart_product_id = item["product_id"]
- session["items"][cart_product_id]["quantity"] = item_quantity
- session["items"][cart_product_id]["availability"] = inventory_amount > 0
- else:
- # when doing POST /checkout, re-compute meta values
- session_meta_item_amount += item["quantity"]
- session_meta_subtotal += item["quantity"] * item["price"]
- session_meta_weight += item["quantity"] * item["options"]["weight"]
- session["meta"]["item_amount"] = session_meta_item_amount
- session["meta"]["subtotal"] = session_meta_subtotal
- session["meta"]["total_weight"] = session_meta_weight
- # check items amount and eventually reset shipping value
- if len(session["items"]) > 0:
- session["meta"]["shipping"] = shipping_amount
- else:
- session["meta"]["shipping"] = 0
- # update session meta total
- session["meta"]["total"] = session["meta"]["subtotal"] + shipping_amount
- def update_cart(
- session,
- form: CartUpdate,
- product_exists: bool,
- shipping_amount: int,
- quantity: int,
- inventory_amount: int,
- settings,
- ) -> NoReturn:
- """Update (add / remove) Session Cart with given product_id + options
- item: if Cart contains the product, increase / decrease product's
- quantity, else add / remove the product to / from the Cart.
- """
- if form.options.product_variation_id:
- product_id = form.options.product_variation_id # variation ID is used for item session storage if it exists
- else:
- product_id = form.product_id
- if product_exists:
- # get currently updated product
- s = session["items"][product_id]
- if form.operation == "add":
- # make sure the sum of user's existing cart's product
- # quantity plus the newly requested quantity does not go
- # over the product's available quantity (inventory amount)
- if (s["quantity"] + quantity) <= inventory_amount:
- s["quantity"] = s["quantity"] + 1
- elif form.operation == "remove":
- if s["quantity"] > 1:
- s["quantity"] = s["quantity"] - 1
- else:
- del session["items"][product_id]
- elif form.operation == "delete":
- del session["items"][product_id]
- elif form.operation == "manual":
- if quantity > 0:
- # cap manual input amount against available inventory amount
- if quantity <= inventory_amount:
- s["quantity"] = quantity
- else:
- # else round up to the inventory's amount value
- s["quantity"] = inventory_amount
- else:
- del session["items"][product_id]
- # update item's availability
- s["availability"] = inventory_amount > 0
- else:
- if form.operation == "add":
- if quantity <= inventory_amount:
- update_data = {
- "product_id": form.product_id,
- "options": {
- "product_variation_id": form.options.product_variation_id,
- "size": form.options.size,
- "style": form.options.style,
- "weight": form.options.weight,
- },
- "price": form.price,
- "quantity": 1,
- "availability": inventory_amount > 0,
- }
- session["items"][product_id] = update_data
- update_cart_meta(session, shipping_amount, settings)
- def clear_cart(session, currency: str) -> NoReturn:
- """Clear cart from saved items.
- Usually this is run after a successful payment.
- """
- order_id = shortuuid.uuid()
- session["cart"] = {
- "meta": {
- "order_id": order_id,
- "currency": currency,
- "subtotal": 0,
- "total_weight": 0,
- "shipping": 0,
- "total": 0,
- "item_amount": 0,
- },
- "items": {},
- }
- def calculate_quantity_requested_by_user(
- session_cart_items: dict[str, dict[str, str | dict[str, str]]],
- product_id: str,
- size: str | None = None,
- style: str | None = None,
- ) -> int:
- """Helper function to retrieve the number of items of a specific
- product (and variation) that the user has in their cart.
- """
- quantity_requested_by_user = 0
- for item in session_cart_items.values():
- if item["product_id"] == product_id:
- item_size = item.get("options", {}).get("size")
- item_style = item.get("options", {}).get("style")
- if (size is None or item_size == size) and (style is None or item_style == style):
- quantity_requested_by_user += item["quantity"]
- return quantity_requested_by_user
- def create_product_list(settings: Settings, cart_items: dict[CartSessionItem]) -> str:
- """
- Return a string containing all the selected product in the user's
- cart as a "comma-based list".
- """
- product_list = []
- products = get_products_id_db(settings)
-
- for item in cart_items.values():
- product = get_product_by_product_id(settings, item.product_id, products)
- product_path = str(Path(product.meta.path).stem)
- item = ProductInfoCode(path=product_path,
- options=item.options,
- quantity=item.quantity)
- product_info = make_product_info_code(item)
- product_list.append(product_info)
- return ",".join(product_list)
|