added delivery feature and payment method

main
mtyton 2023-07-23 00:48:09 +02:00
rodzic 3580a3b1e1
commit 783e04a134
9 zmienionych plików z 119 dodań i 39 usunięć

Wyświetl plik

@ -1,6 +1,9 @@
import logging
from abc import (
ABC,
abstractmethod
abstractmethod,
abstractproperty
)
from typing import (
List,
@ -13,9 +16,12 @@ from django.core import signing
from store.models import (
Product,
ProductAuthor
ProductAuthor,
DeliveryMethod
)
logger = logging.getLogger("cart_logger")
class BaseCart(ABC):
@ -34,22 +40,54 @@ class BaseCart(ABC):
def update_item_quantity(self, item_id, change):
...
@abstractmethod
def get_items(self):
@abstractproperty
def display_items(self):
...
class SessionCart(BaseCart):
def __init__(self, request: HttpRequest) -> None:
def _get_author_total_price(self, author_id: int):
author_cart = self._cart[str(author_id)]
author_price = 0
product_ids = list(int(pk) for pk in author_cart.keys())
queryset = Product.objects.filter(id__in=product_ids)
for product in queryset:
author_price += product.price * author_cart[str(product.id)]
if self._delivery_info:
author_price += self._delivery_info.price
return author_price
def _prepare_display_items(self)-> List[dict[str, dict|str]]:
items: List[dict[str, dict|str]] = []
for author_id, cart_items in self._cart.items():
author = ProductAuthor.objects.get(id=int(author_id))
products = []
for item_id, quantity in cart_items.items():
product=Product.objects.get(id=int(item_id))
products.append({"product": product, "quantity": quantity})
items.append({
"author": author,
"products": products,
"group_price": self._get_author_total_price(author_id)
})
return items
def __init__(self, request: HttpRequest, delivery: DeliveryMethod=None) -> None:
super().__init__()
self.session = request.session
self._cart = self.session.get(settings.CART_SESSION_ID, None)
if not self._cart:
self._cart = {}
self.session[settings.CART_SESSION_ID] = self._cart
self._delivery_info = delivery
self._display_items = self._prepare_display_items()
def save_cart(self):
self._display_items = self._prepare_display_items()
self.session[settings.CART_SESSION_ID] = self._cart
self.session.modified = True
@ -76,8 +114,7 @@ class SessionCart(BaseCart):
self._cart[str(author.id)].pop(str(item_id))
self.save_cart()
except KeyError:
# TODO - add logging
...
logger.exception(f"Item {item_id} not found in cart")
def update_item_quantity(self, item_id: int, new_quantity: int) -> None:
product = self.validate_and_get_product(item_id)
@ -91,17 +128,14 @@ class SessionCart(BaseCart):
self._cart[str(author.id)][str(product.id)] = new_quantity
self.save_cart()
def get_items(self) -> List[dict[str, dict|str]]:
items: List[dict[str, dict|str]] = []
for author_id, cart_items in self._cart.items():
author = ProductAuthor.objects.get(id=int(author_id))
products = []
for item_id, quantity in cart_items.items():
product=Product.objects.get(id=int(item_id))
products.append({"product": product, "quantity": quantity})
items.append({"author": author, "products": products})
return items
@property
def delivery_info(self):
return self._delivery_info
@property
def display_items(self) -> List[dict[str, dict|str]]:
return self._display_items
@property
def total_price(self):
total = 0
@ -109,6 +143,8 @@ class SessionCart(BaseCart):
for item_id, quantity in cart_items.items():
product = Product.objects.get(id=int(item_id))
total += product.price * quantity
if self._delivery_info:
total += self._delivery_info.price * len(self._cart.keys())
return total
def is_empty(self) -> bool:

Wyświetl plik

@ -51,11 +51,10 @@ class CustomerDataForm(forms.Form):
widget=forms.Select(attrs={"class": "form-control"})
)
def clean(self):
def serialize(self):
"""Clean method should return JSON serializable"""
cleaned_data = super().clean()
new_cleaned_data = {}
for key, value in cleaned_data.items():
for key, value in self.cleaned_data.items():
if isinstance(value, PhoneNumber):
new_cleaned_data[key] = str(value)
elif isinstance(value, Model):

Wyświetl plik

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-truck" viewBox="0 0 16 16">
<path d="M0 3.5A1.5 1.5 0 0 1 1.5 2h9A1.5 1.5 0 0 1 12 3.5V5h1.02a1.5 1.5 0 0 1 1.17.563l1.481 1.85a1.5 1.5 0 0 1 .329.938V10.5a1.5 1.5 0 0 1-1.5 1.5H14a2 2 0 1 1-4 0H5a2 2 0 1 1-3.998-.085A1.5 1.5 0 0 1 0 10.5v-7zm1.294 7.456A1.999 1.999 0 0 1 4.732 11h5.536a2.01 2.01 0 0 1 .732-.732V3.5a.5.5 0 0 0-.5-.5h-9a.5.5 0 0 0-.5.5v7a.5.5 0 0 0 .294.456zM12 10a2 2 0 0 1 1.732 1h.768a.5.5 0 0 0 .5-.5V8.35a.5.5 0 0 0-.11-.312l-1.48-1.85A.5.5 0 0 0 13.02 6H12v4zm-9 1a1 1 0 1 0 0 2 1 1 0 0 0 0-2zm9 0a1 1 0 1 0 0 2 1 1 0 0 0 0-2z"/>
</svg>

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 658 B

Wyświetl plik

@ -9,7 +9,7 @@
<h3 class="fw-normal mb-0 text-black">Koszyk</h3>
</div>
</div>
{% for group in cart.get_items %}
{% for group in cart.display_items %}
{% if group.products %}
<h4>Wykonawca: {{group.author.display_name}}</h4>
{% for item in group.products %}

Wyświetl plik

@ -57,20 +57,28 @@
<h3 class="fw-normal mb-0 text-black">Zamówione przedmioty</h3>
</div>
</div>
{% for group in cart.get_items %}
{% for group in cart.display_items %}
{% if group.products %}
<h4>Wykonawca: {{group.author.display_name}}</h4>
{% for item in group.products %}
{% include 'store/partials/summary_cart_item.html' %}
{% endfor %}
{% if cart.delivery_info %}
{% with delivery=cart.delivery_info %}
{% include 'store/partials/delivery_cart_item.html' %}
{% endwith %}
{% endif %}
<div class="col-sm-11 text-end">
<h5 class="fw-normal mb-0 pr-3text-black">W sumie: {{group.group_price}} zł</h5>
</div>
{% endif %}
{% endfor %}
<div class="card ">
<div class="card mt-5">
<div class="card-body">
<div class="row">
<div class="col-sm-6">
<h5 class="fw-normal mb-0 text-black">Do zapłaty: {{cart.total_price}}</h5>
<h5 class="fw-normal mb-0 text-black">Do zapłaty: {{cart.total_price}}</h5>
</div>
<div class="col-sm-6 text-end">
<form action="" method="POST">

Wyświetl plik

@ -0,0 +1,25 @@
{% load static %}
<div class="card rounded-3 mb-1">
<div class="card-body p-4">
<div class="row d-flex justify-content-between align-items-center">
<div class="col-md-2 col-lg-2 col-xl-2">
<img
src="{% static 'images/icons/truck.svg'%}"
class="rounded mx-auto d-block">
</div>
<div class="col-md-3 col-lg-3 col-xl-3">
<p class="lead fw-normal mb-2">{{delivery.name}}</p>
</div>
<div class="col-md-3 col-lg-3 col-xl-2 d-flex">
1
</div>
<div class="col-md-3 col-lg-2 col-xl-2 offset-lg-1">
<h5 class="mb-0">{{delivery.price}} zł</h5>
</div>
</div>
</div>
</div>

Wyświetl plik

@ -15,7 +15,7 @@
{{item.quantity}}
</div>
<div class="col-md-3 col-lg-2 col-xl-2 offset-lg-1">
<h5 class="mb-0">{{item.product.price}} </h5>
<h5 class="mb-0">{{item.product.price}} </h5>
</div>
</div>

Wyświetl plik

@ -126,7 +126,7 @@ class ProductTestCase(TestCase):
self.assertIsNotNone(prod)
self.assertNotEqual(prod.pk, product.pk)
self.assertFalse(prod.available)
self.assertEqual(prod.price, 13.0)
self.assertEqual(prod.price, 0)
def test_get_or_create_by_params_success_not_existing_product_no_other_products(self):
template = factories.ProductTemplateFactory()

Wyświetl plik

@ -58,7 +58,7 @@ class CartActionView(ViewSet):
def list_products(self, request):
# get cart items
cart = SessionCart(self.request)
items = cart.get_items()
items = cart.display_items
serializer = CartSerializer(instance=items, many=True)
return Response(serializer.data)
@ -69,7 +69,7 @@ class CartActionView(ViewSet):
if not serializer.is_valid():
return Response(serializer.errors, status=400)
serializer.save(cart)
items = cart.get_items()
items = cart.display_items
serializer = CartSerializer(instance=items, many=True)
return Response(serializer.data, status=201)
@ -82,7 +82,7 @@ class CartActionView(ViewSet):
except Product.DoesNotExist:
return Response({"error": "Product does not exist"}, status=400)
items = cart.get_items()
items = cart.display_items
serializer = CartSerializer(instance=items, many=True)
return Response(serializer.data, status=201)
@ -93,7 +93,7 @@ class CartActionView(ViewSet):
cart.update_item_quantity(pk, int(request.data["quantity"]))
except Product.DoesNotExist:
return Response({"error": "Product does not exist"}, status=404)
items = cart.get_items()
items = cart.display_items
serializer = CartSerializer(instance=items, many=True)
return Response(serializer.data, status=201)
@ -176,7 +176,7 @@ class OrderView(View):
context = self.get_context_data()
context["form"] = form
return render(request, self.template_name, context)
customer_data = CustomerData(data=form.cleaned_data)
customer_data = CustomerData(data=form.serialize())
request.session["customer_data"] = customer_data.data
return HttpResponseRedirect(reverse("order-confirm"))
@ -185,12 +185,19 @@ class OrderConfirmView(View):
template_name = "store/order_confirm.html"
def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
customer_data = CustomerData(
encrypted_data=self.request.session["customer_data"]
).decrypted_data
form = CustomerDataForm(
data=CustomerData(
encrypted_data=self.request.session["customer_data"]
).decrypted_data
)
if not form.is_valid():
raise Exception("Customer data is not valid")
customer_data = form.cleaned_data
return {
"cart": SessionCart(self.request),
"customer_data": customer_data
"cart": SessionCart(self.request, delivery=customer_data["delivery_method"]),
"customer_data": customer_data
}
def get(self, request, *args, **kwargs):
@ -201,10 +208,12 @@ class OrderConfirmView(View):
return render(request, self.template_name, self.get_context_data())
def post(self, request):
customer_data = request.session["customer_data"]
customer_data = CustomerData(
encrypted_data=self.request.session["customer_data"]
).decrypted_data
cart = SessionCart(self.request)
order = Order.objects.create_from_cart(
cart.get_items(),
cart.display_items,
None, customer_data
)
request.session.pop("customer_data")