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