From ac51be40dcd49f2831840db5917b36f23a5a8cc2 Mon Sep 17 00:00:00 2001 From: hassan12ammar Date: Sat, 6 Nov 2021 02:47:00 +0300 Subject: [PATCH] Upload My Solution --- .idea/03-commerce.iml | 2 +- .idea/misc.xml | 2 +- account/account.http | 118 +++++++++- account/migrations/0002_alter_user_options.py | 17 ++ commerce/controllers.py | 209 +++++++++++++++--- commerce/schemas.py | 34 +++ config/urls.py | 4 +- requirements.txt | 2 + 8 files changed, 343 insertions(+), 45 deletions(-) create mode 100644 account/migrations/0002_alter_user_options.py diff --git a/.idea/03-commerce.iml b/.idea/03-commerce.iml index f602895..695c4b6 100644 --- a/.idea/03-commerce.iml +++ b/.idea/03-commerce.iml @@ -16,7 +16,7 @@ - + diff --git a/.idea/misc.xml b/.idea/misc.xml index 0c95c56..d1e22ec 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/account/account.http b/account/account.http index 6a94851..eb8f613 100644 --- a/account/account.http +++ b/account/account.http @@ -1,7 +1,15 @@ -GET localhost:8000/api/auth +POST localhost:8000/api/auth/signup Content-Type: application/json Accept: application/json -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwayI6ImY0MzkyMDQ1LTRmOTktNDk0NC05ZjNhLThlNGNhZTEzMjk3MCJ9.kmsdiH0zSlYyB-_8mV-sVdE0pDBEJSzszCPyJ5zQHMk + +{ + "first_name": "Hassan", + "last_name": "Ammar", + "email": "user@example.com", + "password1": "123456789", + "password2": "123456789" +} + ### POST localhost:8000/api/auth/signin @@ -10,28 +18,30 @@ Accept: application/json { "email": "user@example.com", - "password": "!!123123!!" + "password": "123456789" } + ### GET localhost:8000/api/auth Content-Type: application/json Accept: application/json -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwayI6ImY0MzkyMDQ1LTRmOTktNDk0NC05ZjNhLThlNGNhZTEzMjk3MCJ9.kmsdiH0zSlYyB-_8mV-sVdE0pDBEJSzszCPyJ5zQHMk +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwayI6IjE1MWI0NDAxLTNhMWItNGQwOS04N2YxLTViNzc2ZmVkMTIwMiJ9.hrnl9RQ4o8ryx29XNr_iEFLWEFS7F_FLZGCVD3ThyHA + ### PUT localhost:8000/api/auth Content-Type: application/json Accept: application/json -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwayI6ImY0MzkyMDQ1LTRmOTktNDk0NC05ZjNhLThlNGNhZTEzMjk3MCJ9.kmsdiH0zSlYyB-_8mV-sVdE0pDBEJSzszCPyJ5zQHMk +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwayI6IjE1MWI0NDAxLTNhMWItNGQwOS04N2YxLTViNzc2ZmVkMTIwMiJ9.hrnl9RQ4o8ryx29XNr_iEFLWEFS7F_FLZGCVD3ThyHA { - "first_name": "Layth", - "last_name": "Zahid", - "phone_number": "077", - "address1": "Anything", + "first_name": "Hassan1", + "last_name": "Ammar1", + "phone_number": "07745678923", + "address1": "Anything Haha", "address2": "", - "company_name": "", + "company_name": "UOITC", "company_website": "" } @@ -43,7 +53,91 @@ Accept: application/json Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwayI6ImY0MzkyMDQ1LTRmOTktNDk0NC05ZjNhLThlNGNhZTEzMjk3MCJ9.kmsdiH0zSlYyB-_8mV-sVdE0pDBEJSzszCPyJ5zQHMk { - "old_password": "string!!", + "old_password": "123456789", "new_password1": "!!123123!!", "new_password2": "!!123123!!" -} \ No newline at end of file +} + +### +POST localhost:8000/api/orders/add-to-cart +Content-Type: application/json +Accept: application/json +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwayI6IjE1MWI0NDAxLTNhMWItNGQwOS04N2YxLTViNzc2ZmVkMTIwMiJ9.hrnl9RQ4o8ryx29XNr_iEFLWEFS7F_FLZGCVD3ThyHA + +{ + "product_id": "cea150dc-2097-47f5-9d16-e47b57a5cc0e", + "item_qty": 1 +} + + +### +POST localhost:8000/api/orders/add-to-cart +Content-Type: application/json +Accept: application/json +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwayI6IjE1MWI0NDAxLTNhMWItNGQwOS04N2YxLTViNzc2ZmVkMTIwMiJ9.hrnl9RQ4o8ryx29XNr_iEFLWEFS7F_FLZGCVD3ThyHA + +{ + "product_id": "a301be65-4b40-4ddf-ba84-881afc59a637", + "item_qty": 1 +} + + +### +GET localhost:8000/api/orders/cart +Content-Type: application/json +Accept: application/json +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwayI6IjE1MWI0NDAxLTNhMWItNGQwOS04N2YxLTViNzc2ZmVkMTIwMiJ9.hrnl9RQ4o8ryx29XNr_iEFLWEFS7F_FLZGCVD3ThyHA + + +### +POST localhost:8000/api/orders/create-order +Content-Type: application/json +Accept: application/json +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwayI6IjE1MWI0NDAxLTNhMWItNGQwOS04N2YxLTViNzc2ZmVkMTIwMiJ9.hrnl9RQ4o8ryx29XNr_iEFLWEFS7F_FLZGCVD3ThyHA + + +### +GET localhost:8000/api/orders/view-order +Content-Type: application/json +Accept: application/json +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwayI6IjE1MWI0NDAxLTNhMWItNGQwOS04N2YxLTViNzc2ZmVkMTIwMiJ9.hrnl9RQ4o8ryx29XNr_iEFLWEFS7F_FLZGCVD3ThyHA + + +### +POST localhost:8000/api/addresses/add-adress +Content-Type: application/json +Accept: application/json +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwayI6IjE1MWI0NDAxLTNhMWItNGQwOS04N2YxLTViNzc2ZmVkMTIwMiJ9.hrnl9RQ4o8ryx29XNr_iEFLWEFS7F_FLZGCVD3ThyHA + + +{ + "work_address": true, + "address1": "aiskan2", + "address2": "12string!@", + "city_id": "437153e2-4aa7-4fcf-88df-312bdd70baee", + "phone": "03812342" +} + +### +GET localhost:8000/api/addresses +Content-Type: application/json +Accept: application/json +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwayI6IjE1MWI0NDAxLTNhMWItNGQwOS04N2YxLTViNzc2ZmVkMTIwMiJ9.hrnl9RQ4o8ryx29XNr_iEFLWEFS7F_FLZGCVD3ThyHA + +### +PUT localhost:8000/api/checkout/checkout +Content-Type: application/json +Accept: application/json +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwayI6IjE1MWI0NDAxLTNhMWItNGQwOS04N2YxLTViNzc2ZmVkMTIwMiJ9.hrnl9RQ4o8ryx29XNr_iEFLWEFS7F_FLZGCVD3ThyHA + + +{ + "address_id": "3f758a13-1565-42aa-96a3-441fe9ca50ac", + "note": "This is it I'm acully Done! " +} + +### +GET localhost:8000/api/checkout/view-processing-order +Content-Type: application/json +Accept: application/json +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwayI6IjE1MWI0NDAxLTNhMWItNGQwOS04N2YxLTViNzc2ZmVkMTIwMiJ9.hrnl9RQ4o8ryx29XNr_iEFLWEFS7F_FLZGCVD3ThyHA diff --git a/account/migrations/0002_alter_user_options.py b/account/migrations/0002_alter_user_options.py new file mode 100644 index 0000000..3f536c0 --- /dev/null +++ b/account/migrations/0002_alter_user_options.py @@ -0,0 +1,17 @@ +# Generated by Django 3.2.8 on 2021-11-04 18:56 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0001_initial'), + ] + + operations = [ + migrations.AlterModelOptions( + name='user', + options={'verbose_name': 'user', 'verbose_name_plural': 'users'}, + ), + ] diff --git a/commerce/controllers.py b/commerce/controllers.py index a8a551a..205c5c3 100644 --- a/commerce/controllers.py +++ b/commerce/controllers.py @@ -9,17 +9,20 @@ from pydantic import UUID4 from account.authorization import GlobalAuth -from commerce.models import Product, Category, City, Vendor, Item, Order, OrderStatus -from commerce.schemas import ProductOut, CitiesOut, CitySchema, VendorOut, ItemOut, ItemSchema, ItemCreate +from commerce.models import Product, City, Vendor, Item, Order, OrderStatus, Address +from commerce.schemas import ProductOut, CitiesOut, CitySchema, VendorOut, ItemOut, ItemCreate, OrderShemaCreat, \ + OrderOut, AdressOut, CreateAdress from config.utils.schemas import MessageOut products_controller = Router(tags=['products']) address_controller = Router(tags=['addresses']) vendor_controller = Router(tags=['vendors']) order_controller = Router(tags=['orders']) +checkout_controller = Router(tags=['checkout']) User = get_user_model() + @vendor_controller.get('', response=List[VendorOut]) def list_vendors(request): return Vendor.objects.all() @@ -113,10 +116,55 @@ def list_products( """ +""" +finish the addresses CRUD operations +/api/addresses +""" + -@address_controller.get('') +@address_controller.get('', auth=GlobalAuth(), response={ + 200: List[AdressOut], + 404: MessageOut +}) def list_addresses(request): - pass + user = get_object_or_404(User, id=request.auth['pk']) + adress_qs = Address.objects.filter(user=user).all() + if adress_qs: + return 200, adress_qs + + return 404, {'detail': 'No adresses found'} + + +@address_controller.post("add-adress", auth=GlobalAuth(), response={ + 200: AdressOut +}) +def add_adress(request, adress_in: CreateAdress): + user = get_object_or_404(User, id=request.auth['pk']) + adress = Address(**adress_in.dict(), user=user) + adress.save() + return 200, adress + + +@address_controller.delete("delete-adress{id}/", response={ + 200: MessageOut +}) +def delete_adress(request, adress_in: CreateAdress): + adress = get_object_or_404(Address, id=id) + adress.delete() + return 200, {"dital": ""} + + +@address_controller.put('update-adress/{id}', auth=GlobalAuth(), response={ + 200: AdressOut, + 400: MessageOut +}) +def update_adress(request, id: UUID4, adress_in: CreateAdress): + user = get_object_or_404(User, id=request.auth['pk']) + adress = get_object_or_404(Address, id=id) + adress.delete() + adress = Address(id=id, **adress_in.dict(), user=user) + adress.save() + return 200, adress # @products_controller.get('categories', response=List[CategoryOut]) @@ -150,7 +198,7 @@ def retrieve_city(request, id: UUID4): 400: MessageOut }) def create_city(request, city_in: CitySchema): - city = City(**city_in.dict()) + city = City(**city_in.dict() ) city.save() return 201, city @@ -175,12 +223,12 @@ def delete_city(request, id: UUID4): return 204, {'detail': ''} -@order_controller.get('cart', response={ +@order_controller.get('cart', auth=GlobalAuth(), response={ 200: List[ItemOut], 404: MessageOut }) def view_cart(request): - cart_items = Item.objects.filter(user=User.objects.first(), ordered=False) + cart_items = Item.objects.filter(user=get_object_or_404(User, id=request.auth['pk']), ordered=False) if cart_items: return cart_items @@ -188,26 +236,29 @@ def view_cart(request): return 404, {'detail': 'Your cart is empty, go shop like crazy!'} -@order_controller.post('add-to-cart', response={ +@order_controller.post('add-to-cart', auth=GlobalAuth(), response={ 200: MessageOut, # 400: MessageOut }) def add_update_cart(request, item_in: ItemCreate): + user = get_object_or_404(User, id=request.auth['pk']) try: - item = Item.objects.get(product_id=item_in.product_id, user=User.objects.first()) + item = Item.objects.get(product_id=item_in.product_id, user=user, ordered=False) item.item_qty += 1 item.save() - except Item.DoesNotExist: - Item.objects.create(**item_in.dict(), user=User.objects.first()) + return 200, {'detail': 'Added from cart successfully'} - return 200, {'detail': 'Added to cart successfully'} + except Item.DoesNotExist: + Item.objects.create(**item_in.dict(), user=user) + return 200, {'detail': 'Item Created into cart successfully'} -@order_controller.post('item/{id}/reduce-quantity', response={ +@order_controller.post('item/{id}/reduce-quantity', auth=GlobalAuth(), response={ 200: MessageOut, }) def reduce_item_quantity(request, id: UUID4): - item = get_object_or_404(Item, id=id, user=User.objects.first()) + user = get_object_or_404(User, id=request.auth['pk']) + item = get_object_or_404(Item, id=id, user=user) if item.item_qty <= 1: item.delete() return 200, {'detail': 'Item deleted!'} @@ -221,7 +272,8 @@ def reduce_item_quantity(request, id: UUID4): 204: MessageOut }) def delete_item(request, id: UUID4): - item = get_object_or_404(Item, id=id, user=User.objects.first()) + user = get_object_or_404(User, id=request.auth['pk']) + item = get_object_or_404(Item, id=id, user=user) item.delete() return 204, {'detail': 'Item deleted!'} @@ -231,27 +283,124 @@ def generate_ref_code(): return ''.join(random.sample(string.ascii_letters + string.digits, 6)) -@order_controller.post('create-order', auth=GlobalAuth(), response=MessageOut) +""" +'create-order' endpoint : +- create a new order +- set ref_code to a randomly generated 6 alphanumeric value +- take all current items (ordered=False) and add them to the recently created order +- set added items (ordered field) to be True +/api/orders/create +""" + + +@order_controller.post('create-order', auth=GlobalAuth(), response={ + 200: MessageOut, + 404: MessageOut +}) def create_order(request): - ''' + """ * add items and mark (ordered) field as True * add ref_number * add NEW status * calculate the total - ''' + """ + user = get_object_or_404(User, id=request.auth['pk']) + + orders = Order.objects.filter(user=user, ordered=False) + user_items = Item.objects.filter(user=user).filter(ordered=False) + + for order in orders: + order_items = Item.objects.filter(order=order.id) + + # Check If we have Precreated Order + if user_items: + if orders: + for item in user_items: + for order_item in order_items: + if order_item.product_id == item.product_id is not None: + order_item.item_qty += item.item_qty + order_item.save() + items_ = Item.objects.filter(id=item.id) + items_.delete() - order_qs = Order.objects.create( - user=User.objects.first(), - status=OrderStatus.objects.get(is_default=True), - ref_code=generate_ref_code(), - ordered=False, - ) + order.total = order.order_total + order.save() - user_items = Item.objects.filter(user=User.objects.first()).filter(ordered=False) + # Check if we have a new items to add + user_items = Item.objects.filter(user=user, ordered=False) + if user_items: + order.items.add(*user_items) + order.total = order.order_total + + order.save() + user_items.update(ordered=True) + + return 200, {"detail": f"Order with ID: {order.id}, Has been Updated "} + + # create New Order + new_order = Order(user=user, + status=OrderStatus.objects.get(is_default=True), + ref_code=generate_ref_code(), ordered=False) + + new_order.save() + + new_order.items.add(*user_items) + new_order.total = new_order.order_total + + new_order.save() + user_items.update(ordered=True) + return 200, {"detail": f"Order Has been Created with ID: {new_order.id}"} + return 404, {"detail": "No Items Found"} + + +@order_controller.get("view-order", auth=GlobalAuth(), response={ + 200: List[OrderOut], + 404: MessageOut +}) +def view_order(request): + order = Order.objects.filter(ordered=False, user=get_object_or_404(User, id=request.auth['pk'])) + + if order: + return 200, order.all() + + return 404, {"detail": "No Items Found"} + + +""" +checkout: + if this user has an active order + add address + accept note + update the status + mark order.ordered field as True +""" + + +@checkout_controller.put("checkout", auth=GlobalAuth(), response={ + 200: MessageOut, + 404: MessageOut +}) +def checkout(request, order_in: OrderShemaCreat): + user = get_object_or_404(User, id=request.auth['pk']) + order = Order.objects.filter(user=user, ordered=False) + + if order: + order.update(address=order_in.address_id, note=order_in.note, + ordered=True, status=OrderStatus.objects.get(title="PROCESSING")) + + return 200, {"detail": "Your Order Now been Processed"} + + return 404, {"detail": "No Order Found"} + + +@checkout_controller.get("view-processing-order", auth=GlobalAuth(), response={ + 200: List[OrderOut], + 404: MessageOut +}) +def view_processing_order(request): + order = Order.objects.filter(user = get_object_or_404(User, id=request.auth['pk']), ordered = True) - order_qs.items.add(*user_items) - order_qs.total = order_qs.order_total - user_items.update(ordered=True) - order_qs.save() + if order: + return 200, order.all() - return {'detail': 'order created successfully'} + return 404, {"detail": "No Items Found"} diff --git a/commerce/schemas.py b/commerce/schemas.py index 5d68396..085eed9 100644 --- a/commerce/schemas.py +++ b/commerce/schemas.py @@ -91,3 +91,37 @@ class ItemOut(UUIDSchema, ItemSchema): pass +class OrderStatusShcema(Schema): + title: str + is_default: bool + + +class CreateAdress(Schema): + work_address: bool + address1: str + address2: str + city_id: UUID4 + phone: str + + +class AdressOut(CreateAdress, UUIDSchema): + pass + + +class OrderShema(Schema): + address_id: UUID4 = None + note: str = None + + +class OrderOut(OrderShema): + items: List[ItemOut] + status_id: UUID4 = None + total: float + ref_code: str + ordered: bool + + +class OrderShemaCreat(OrderShema): + address_id: UUID4 + + diff --git a/config/urls.py b/config/urls.py index 3851026..54fee40 100644 --- a/config/urls.py +++ b/config/urls.py @@ -19,7 +19,8 @@ from ninja import NinjaAPI from account.controllers import account_controller -from commerce.controllers import products_controller, address_controller, vendor_controller, order_controller +from commerce.controllers import products_controller, address_controller, vendor_controller, order_controller, \ + checkout_controller from config import settings api = NinjaAPI() @@ -29,6 +30,7 @@ api.add_router('vendors', vendor_controller) api.add_router('orders', order_controller) api.add_router('auth', account_controller) +api.add_router('checkout', checkout_controller) urlpatterns = [ path('admin/', admin.site.urls), diff --git a/requirements.txt b/requirements.txt index 5d025cd..d568d65 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,3 +6,5 @@ pydantic==1.8.2 pytz==2021.3 sqlparse==0.4.2 typing-extensions==3.10.0.2 + +jose~=1.0.0 \ No newline at end of file