feat: removed entire API
This commit is contained in:
@@ -1,36 +0,0 @@
|
|||||||
from typing import Dict
|
|
||||||
|
|
||||||
|
|
||||||
class CRUDPermissionsMixin:
|
|
||||||
view_permission_codes: tuple = ()
|
|
||||||
create_permission_codes: tuple = ()
|
|
||||||
destroy_permission_codes: tuple = ()
|
|
||||||
update_permission_codes: tuple = ()
|
|
||||||
action_permissions: Dict[str, tuple] = {}
|
|
||||||
|
|
||||||
def get_default_action_permissions(self):
|
|
||||||
return {
|
|
||||||
"list": self.view_permission_codes,
|
|
||||||
"retrieve": self.view_permission_codes,
|
|
||||||
"create": self.create_permission_codes,
|
|
||||||
"update": self.update_permission_codes,
|
|
||||||
"partial_update": self.update_permission_codes,
|
|
||||||
"destroy": self.destroy_permission_codes,
|
|
||||||
}
|
|
||||||
|
|
||||||
def has_perm_for_action(self, request, action) -> bool:
|
|
||||||
default_action_permissions = self.get_default_action_permissions()
|
|
||||||
action_permissions = {**default_action_permissions, **self.action_permissions}
|
|
||||||
perms = action_permissions.get(action, ())
|
|
||||||
return request.user.has_perms(perms)
|
|
||||||
|
|
||||||
def has_permission(self, request, view):
|
|
||||||
user = request.user
|
|
||||||
|
|
||||||
if user.is_superuser:
|
|
||||||
return True
|
|
||||||
|
|
||||||
if not user.is_staff:
|
|
||||||
return False
|
|
||||||
|
|
||||||
return self.has_perm_for_action(request=request, action=view.action)
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
from django.urls import include, path
|
|
||||||
from drf_spectacular.views import SpectacularRedocView, SpectacularSwaggerView
|
|
||||||
|
|
||||||
from config.api.v1.views import APISchema
|
|
||||||
|
|
||||||
urlpatterns = [
|
|
||||||
path("schema/", APISchema.as_view(), name="schema"),
|
|
||||||
path(
|
|
||||||
"swagger/", SpectacularSwaggerView.as_view(url_name="schema"), name="swagger-ui"
|
|
||||||
),
|
|
||||||
path("redoc/", SpectacularRedocView.as_view(url_name="schema"), name="redoc"),
|
|
||||||
path("shop/", include("shop.api.v1.routers", namespace="shop_api")),
|
|
||||||
path("files/", include("files.api.v1.urls", namespace="files")),
|
|
||||||
path("auth/", include("users.api.v1.urls", namespace="auth")),
|
|
||||||
]
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
from drf_spectacular.views import SpectacularAPIView
|
|
||||||
|
|
||||||
|
|
||||||
class APISchema(SpectacularAPIView):
|
|
||||||
api_version = "v1"
|
|
||||||
@@ -7,7 +7,6 @@ from django.urls import include, path
|
|||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("", include("web.urls", namespace="web")),
|
path("", include("web.urls", namespace="web")),
|
||||||
path("admin/", admin.site.urls),
|
path("admin/", admin.site.urls),
|
||||||
path("api/v1/", include("config.api.v1.urls")),
|
|
||||||
path("watchman/", include("watchman.urls")),
|
path("watchman/", include("watchman.urls")),
|
||||||
path("auth/", include("users.urls", namespace="users")),
|
path("auth/", include("users.urls", namespace="users")),
|
||||||
path("tpv/", include("shop.urls", namespace="shop")),
|
path("tpv/", include("shop.urls", namespace="shop")),
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
from rest_framework.permissions import BasePermission
|
|
||||||
|
|
||||||
from config.api.v1.mixins import CRUDPermissionsMixin
|
|
||||||
|
|
||||||
|
|
||||||
class ProductPermissions(CRUDPermissionsMixin, BasePermission):
|
|
||||||
view_permission_codes = ("shop.view_product",)
|
|
||||||
create_permission_codes = ("shop.add_product",)
|
|
||||||
destroy_permission_codes = ("shop.delete_product",)
|
|
||||||
update_permission_codes = ("shop.change_product",)
|
|
||||||
|
|
||||||
|
|
||||||
class ProductPricePermissions(CRUDPermissionsMixin, BasePermission):
|
|
||||||
view_permission_codes = ("shop.view_productprice",)
|
|
||||||
create_permission_codes = ("shop.add_productprice",)
|
|
||||||
destroy_permission_codes = ("shop.delete_productprice",)
|
|
||||||
update_permission_codes = ("shop.change_productprice",)
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
from rest_framework.routers import DefaultRouter
|
|
||||||
|
|
||||||
from shop.api.v1.viewsets import (
|
|
||||||
BrandViewSet,
|
|
||||||
OrderLineViewSet,
|
|
||||||
OrderViewSet,
|
|
||||||
ProductBatchViewSet,
|
|
||||||
ProductPriceViewSet,
|
|
||||||
ProductViewSet,
|
|
||||||
ProviderViewSet,
|
|
||||||
TagViewSet,
|
|
||||||
TaxViewSet,
|
|
||||||
)
|
|
||||||
|
|
||||||
app_name = "shop"
|
|
||||||
|
|
||||||
|
|
||||||
router = DefaultRouter()
|
|
||||||
|
|
||||||
router.register("taxes", TaxViewSet)
|
|
||||||
router.register("tags", TagViewSet)
|
|
||||||
router.register("brands", BrandViewSet)
|
|
||||||
router.register("providers", ProviderViewSet)
|
|
||||||
router.register("products", ProductViewSet)
|
|
||||||
router.register("products/(?P<product_id>[^/.]+)/prices", ProductPriceViewSet)
|
|
||||||
router.register("product-batches", ProductBatchViewSet)
|
|
||||||
router.register("orders", OrderViewSet)
|
|
||||||
router.register("orders/(?P<order_id>[^/.]+)/lines", OrderLineViewSet)
|
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = router.urls
|
|
||||||
@@ -1,252 +0,0 @@
|
|||||||
from django.utils import timezone
|
|
||||||
from rest_framework import serializers
|
|
||||||
|
|
||||||
from files.api.v1.serializers import FileUploadSerializer, NoIDFileUploadSerializer
|
|
||||||
from shop.models import (
|
|
||||||
Brand,
|
|
||||||
Order,
|
|
||||||
OrderLine,
|
|
||||||
Product,
|
|
||||||
ProductBatch,
|
|
||||||
ProductPrice,
|
|
||||||
Provider,
|
|
||||||
Tag,
|
|
||||||
Tax,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class TaxSerializer(serializers.ModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = Tax
|
|
||||||
fields = (
|
|
||||||
"id",
|
|
||||||
"code",
|
|
||||||
"value",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class TagSerializer(serializers.ModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = Tag
|
|
||||||
fields = (
|
|
||||||
"id",
|
|
||||||
"name",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ProductPriceSerializer(serializers.ModelSerializer):
|
|
||||||
tax = TaxSerializer()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = ProductPrice
|
|
||||||
fields = (
|
|
||||||
"price",
|
|
||||||
"current",
|
|
||||||
"date",
|
|
||||||
"tax",
|
|
||||||
"price_with_tax",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class CreateProductPriceSerializer(serializers.ModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = ProductPrice
|
|
||||||
fields = (
|
|
||||||
"id",
|
|
||||||
"price",
|
|
||||||
"tax",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ProductSerializer(serializers.ModelSerializer):
|
|
||||||
images = FileUploadSerializer(many=True, read_only=True)
|
|
||||||
price = ProductPriceSerializer()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Product
|
|
||||||
fields = (
|
|
||||||
"id",
|
|
||||||
"sku",
|
|
||||||
"name",
|
|
||||||
"description",
|
|
||||||
"stock",
|
|
||||||
"is_digital_asset",
|
|
||||||
"images",
|
|
||||||
"price",
|
|
||||||
"tags",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ListProductSerializer(serializers.ModelSerializer):
|
|
||||||
price = ProductPriceSerializer()
|
|
||||||
tags = TagSerializer(many=True)
|
|
||||||
images = NoIDFileUploadSerializer(many=True, read_only=True)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Product
|
|
||||||
fields = (
|
|
||||||
"id",
|
|
||||||
"sku",
|
|
||||||
"name",
|
|
||||||
"description",
|
|
||||||
"stock",
|
|
||||||
"is_digital_asset",
|
|
||||||
"price",
|
|
||||||
"tags",
|
|
||||||
"images",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class CreateProductSerializer(serializers.ModelSerializer):
|
|
||||||
price = serializers.DecimalField(
|
|
||||||
max_digits=13, decimal_places=4, required=False, write_only=True
|
|
||||||
)
|
|
||||||
tax = serializers.PrimaryKeyRelatedField(
|
|
||||||
queryset=Tax.objects.all(), write_only=True
|
|
||||||
)
|
|
||||||
|
|
||||||
def create(self, validated_data):
|
|
||||||
instance = Product.objects.create(
|
|
||||||
sku=validated_data.get("sku"),
|
|
||||||
name=validated_data.get("name"),
|
|
||||||
description=validated_data.get("description"),
|
|
||||||
stock=validated_data.get("stock"),
|
|
||||||
is_digital_asset=validated_data.get("is_digital_asset"),
|
|
||||||
url=validated_data.get("url"),
|
|
||||||
)
|
|
||||||
|
|
||||||
tags = validated_data.get("tags")
|
|
||||||
for tag in tags:
|
|
||||||
instance.tags.add(tag)
|
|
||||||
|
|
||||||
ProductPrice.objects.create(
|
|
||||||
product=instance,
|
|
||||||
date=timezone.now(),
|
|
||||||
price=validated_data.get("price"),
|
|
||||||
tax=validated_data.get("tax"),
|
|
||||||
current=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
return instance
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Product
|
|
||||||
fields = (
|
|
||||||
"id",
|
|
||||||
"sku",
|
|
||||||
"name",
|
|
||||||
"description",
|
|
||||||
"stock",
|
|
||||||
"is_digital_asset",
|
|
||||||
"url",
|
|
||||||
"price",
|
|
||||||
"tax",
|
|
||||||
"tags",
|
|
||||||
"images",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class UpdateProductSerializer(serializers.ModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = Product
|
|
||||||
fields = (
|
|
||||||
"id",
|
|
||||||
"sku",
|
|
||||||
"name",
|
|
||||||
"description",
|
|
||||||
"stock",
|
|
||||||
"is_digital_asset",
|
|
||||||
"url",
|
|
||||||
"tags",
|
|
||||||
"images",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ProductBatchSerializer(serializers.ModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = ProductBatch
|
|
||||||
fields = (
|
|
||||||
"id",
|
|
||||||
"code",
|
|
||||||
"product",
|
|
||||||
"quantity",
|
|
||||||
"creation_date",
|
|
||||||
"expiration_date",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ProviderSerializer(serializers.ModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = Provider
|
|
||||||
fields = (
|
|
||||||
"id",
|
|
||||||
"vat_id",
|
|
||||||
"name",
|
|
||||||
"email",
|
|
||||||
"phone",
|
|
||||||
"address",
|
|
||||||
"city",
|
|
||||||
"state",
|
|
||||||
"country",
|
|
||||||
"zip",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class BrandSerializer(serializers.ModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = Brand
|
|
||||||
fields = (
|
|
||||||
"id",
|
|
||||||
"name",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class OrderSerializer(serializers.ModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = Order
|
|
||||||
fields = (
|
|
||||||
"uuid",
|
|
||||||
"creation_date",
|
|
||||||
"last_modification_date",
|
|
||||||
"base_total",
|
|
||||||
"total",
|
|
||||||
"user",
|
|
||||||
"billing_address",
|
|
||||||
"billing_city",
|
|
||||||
"billing_state",
|
|
||||||
"billing_country",
|
|
||||||
"billing_zip",
|
|
||||||
"shipping_address",
|
|
||||||
"shipping_city",
|
|
||||||
"shipping_state",
|
|
||||||
"shipping_country",
|
|
||||||
"shipping_zip",
|
|
||||||
"contact_phone",
|
|
||||||
)
|
|
||||||
read_only_fields = (
|
|
||||||
"uuid",
|
|
||||||
"base_total",
|
|
||||||
"total",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class OrderLineSerializer(serializers.ModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = OrderLine
|
|
||||||
fields = (
|
|
||||||
"id",
|
|
||||||
"product",
|
|
||||||
"quantity",
|
|
||||||
"price",
|
|
||||||
"base_total",
|
|
||||||
"total",
|
|
||||||
"taxes",
|
|
||||||
"tax_value",
|
|
||||||
)
|
|
||||||
read_only_fields = (
|
|
||||||
"price",
|
|
||||||
"base_total",
|
|
||||||
"total",
|
|
||||||
"taxes",
|
|
||||||
"tax_value",
|
|
||||||
)
|
|
||||||
@@ -1,194 +0,0 @@
|
|||||||
from decimal import Decimal
|
|
||||||
|
|
||||||
from django.db import transaction
|
|
||||||
from django.db.models.deletion import ProtectedError
|
|
||||||
from django.shortcuts import get_object_or_404
|
|
||||||
from django.utils.text import gettext_lazy as _
|
|
||||||
from rest_framework.exceptions import ValidationError
|
|
||||||
from rest_framework.permissions import IsAdminUser
|
|
||||||
from rest_framework.viewsets import ModelViewSet
|
|
||||||
|
|
||||||
from shop.api.v1.permissions import ProductPermissions, ProductPricePermissions
|
|
||||||
from shop.api.v1.serializers import (
|
|
||||||
BrandSerializer,
|
|
||||||
CreateProductPriceSerializer,
|
|
||||||
CreateProductSerializer,
|
|
||||||
ListProductSerializer,
|
|
||||||
OrderLineSerializer,
|
|
||||||
OrderSerializer,
|
|
||||||
ProductBatchSerializer,
|
|
||||||
ProductPriceSerializer,
|
|
||||||
ProductSerializer,
|
|
||||||
ProviderSerializer,
|
|
||||||
TagSerializer,
|
|
||||||
TaxSerializer,
|
|
||||||
UpdateProductSerializer,
|
|
||||||
)
|
|
||||||
from shop.filters import ProductFilter
|
|
||||||
from shop.models import (
|
|
||||||
Brand,
|
|
||||||
Order,
|
|
||||||
OrderLine,
|
|
||||||
Product,
|
|
||||||
ProductBatch,
|
|
||||||
ProductPrice,
|
|
||||||
Provider,
|
|
||||||
Tag,
|
|
||||||
Tax,
|
|
||||||
)
|
|
||||||
from shop.utils import delete_product_batch
|
|
||||||
|
|
||||||
|
|
||||||
class ProductViewSet(ModelViewSet):
|
|
||||||
serializer_class = ProductSerializer
|
|
||||||
queryset = (
|
|
||||||
Product.objects.prefetch_related("prices")
|
|
||||||
.prefetch_related("tags")
|
|
||||||
.prefetch_related("images")
|
|
||||||
.all()
|
|
||||||
)
|
|
||||||
permission_classes = (
|
|
||||||
IsAdminUser,
|
|
||||||
ProductPermissions,
|
|
||||||
)
|
|
||||||
search_fields = ("name",)
|
|
||||||
filterset_class = ProductFilter
|
|
||||||
|
|
||||||
def get_serializer_class(self):
|
|
||||||
if self.action == "create":
|
|
||||||
return CreateProductSerializer
|
|
||||||
elif self.action == "list":
|
|
||||||
return ListProductSerializer
|
|
||||||
elif self.action in ("update", "partial_update"):
|
|
||||||
return UpdateProductSerializer
|
|
||||||
return self.serializer_class
|
|
||||||
|
|
||||||
def perform_destroy(self, instance):
|
|
||||||
try:
|
|
||||||
super().perform_destroy(instance)
|
|
||||||
except ProtectedError:
|
|
||||||
raise ValidationError(_("No se puede borrar el producto"))
|
|
||||||
|
|
||||||
|
|
||||||
class ProductPriceViewSet(ModelViewSet):
|
|
||||||
serializer_class = ProductPriceSerializer
|
|
||||||
queryset = ProductPrice.objects.all()
|
|
||||||
permission_classes = (
|
|
||||||
IsAdminUser,
|
|
||||||
ProductPricePermissions,
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
return super().get_queryset().filter(product_id=self.kwargs.get("product_id"))
|
|
||||||
|
|
||||||
def get_serializer_class(self):
|
|
||||||
if self.action == "create":
|
|
||||||
return CreateProductPriceSerializer
|
|
||||||
return self.serializer_class
|
|
||||||
|
|
||||||
def perform_create(self, serializer):
|
|
||||||
product = get_object_or_404(Product, pk=self.kwargs.get("product_id"))
|
|
||||||
product.prices.all().update(current=False)
|
|
||||||
serializer.save(product=product, current=True)
|
|
||||||
|
|
||||||
|
|
||||||
class ProductBatchViewSet(ModelViewSet):
|
|
||||||
serializer_class = ProductBatchSerializer
|
|
||||||
queryset = ProductBatch.objects.all()
|
|
||||||
permission_classes = (IsAdminUser,)
|
|
||||||
|
|
||||||
def perform_create(self, serializer):
|
|
||||||
instance = serializer.save()
|
|
||||||
product = instance.product
|
|
||||||
product.stock += instance.quantity
|
|
||||||
product.save()
|
|
||||||
|
|
||||||
def perform_destroy(self, instance):
|
|
||||||
delete_product_batch(instance)
|
|
||||||
|
|
||||||
def perform_update(self, serializer):
|
|
||||||
instance = self.get_object()
|
|
||||||
product = instance.product
|
|
||||||
product.stock -= instance.quantity
|
|
||||||
|
|
||||||
updated_batch = serializer.save()
|
|
||||||
product.stock += updated_batch.quantity
|
|
||||||
product.save()
|
|
||||||
|
|
||||||
|
|
||||||
class ProviderViewSet(ModelViewSet):
|
|
||||||
serializer_class = ProviderSerializer
|
|
||||||
queryset = Provider.objects.all()
|
|
||||||
permission_classes = (IsAdminUser,)
|
|
||||||
|
|
||||||
|
|
||||||
class TaxViewSet(ModelViewSet):
|
|
||||||
serializer_class = TaxSerializer
|
|
||||||
queryset = Tax.objects.all()
|
|
||||||
permission_classes = (IsAdminUser,)
|
|
||||||
search_fields = ("code",)
|
|
||||||
|
|
||||||
|
|
||||||
class OrderViewSet(ModelViewSet):
|
|
||||||
serializer_class = OrderSerializer
|
|
||||||
queryset = Order.objects.all()
|
|
||||||
lookup_field = "uuid"
|
|
||||||
|
|
||||||
|
|
||||||
class OrderLineViewSet(ModelViewSet):
|
|
||||||
serializer_class = OrderLineSerializer
|
|
||||||
queryset = OrderLine.objects.all()
|
|
||||||
|
|
||||||
def perform_create(self, serializer):
|
|
||||||
with transaction.atomic():
|
|
||||||
order = get_object_or_404(Order, uuid=self.kwargs.get("order_id"))
|
|
||||||
product = serializer.validated_data.get("product")
|
|
||||||
price = product.prices.last()
|
|
||||||
tax = price.tax.value
|
|
||||||
quantity = serializer.validated_data.get("quantity")
|
|
||||||
base_total = quantity * price.price
|
|
||||||
taxes = base_total * (tax / Decimal("100"))
|
|
||||||
total = base_total + taxes
|
|
||||||
|
|
||||||
instance = serializer.save(
|
|
||||||
order=order,
|
|
||||||
price=price.price,
|
|
||||||
base_total=base_total,
|
|
||||||
tax_value=tax,
|
|
||||||
taxes=taxes,
|
|
||||||
total=total,
|
|
||||||
)
|
|
||||||
|
|
||||||
order.base_total += instance.base_total
|
|
||||||
order.total += instance.total
|
|
||||||
order.save()
|
|
||||||
|
|
||||||
def perform_destroy(self, instance):
|
|
||||||
order = instance.order
|
|
||||||
|
|
||||||
with transaction.atomic():
|
|
||||||
order.base_total -= instance.base_total
|
|
||||||
order.total -= instance.total
|
|
||||||
instance.delete()
|
|
||||||
order.save()
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
return (
|
|
||||||
super()
|
|
||||||
.get_queryset()
|
|
||||||
.select_related("order")
|
|
||||||
.filter(order__uuid=self.kwargs.get("order_id"))
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class TagViewSet(ModelViewSet):
|
|
||||||
serializer_class = TagSerializer
|
|
||||||
queryset = Tag.objects.all()
|
|
||||||
permission_classes = (IsAdminUser,)
|
|
||||||
|
|
||||||
|
|
||||||
class BrandViewSet(ModelViewSet):
|
|
||||||
serializer_class = BrandSerializer
|
|
||||||
queryset = Brand.objects.all()
|
|
||||||
permission_classes = (IsAdminUser,)
|
|
||||||
search_fields = ("name",)
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
from rest_framework import status
|
|
||||||
from rest_framework.test import APITestCase
|
|
||||||
|
|
||||||
from config.tests.mixins import TestUserAuthenticationMixin
|
|
||||||
from shop.models import Brand
|
|
||||||
|
|
||||||
|
|
||||||
class TestBrandsAPI(APITestCase, TestUserAuthenticationMixin):
|
|
||||||
model_name = "brand"
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
self.create_user()
|
|
||||||
|
|
||||||
def create_brands(self):
|
|
||||||
Brand.objects.create(
|
|
||||||
name="Marca",
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_fetch_brands(self):
|
|
||||||
self.create_brands()
|
|
||||||
self.login()
|
|
||||||
response = self.client.get("/api/v1/shop/brands/")
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
assert len(response.data.get("results")) == 1
|
|
||||||
|
|
||||||
def test_create_retrieve_brand(self):
|
|
||||||
self.login()
|
|
||||||
response = self.client.post("/api/v1/shop/brands/", {"name": "Marca 1"})
|
|
||||||
assert response.status_code == status.HTTP_201_CREATED
|
|
||||||
pk = response.data.get("id")
|
|
||||||
|
|
||||||
response = self.client.get(f"/api/v1/shop/brands/{pk}/")
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
|
|
||||||
def test_create_update_brand(self):
|
|
||||||
self.login()
|
|
||||||
response = self.client.post("/api/v1/shop/brands/", {"name": "Marca 1"})
|
|
||||||
assert response.status_code == status.HTTP_201_CREATED
|
|
||||||
pk = response.data.get("id")
|
|
||||||
|
|
||||||
response = self.client.get(f"/api/v1/shop/brands/{pk}/")
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
|
|
||||||
response = self.client.patch(f"/api/v1/shop/brands/{pk}/", {"name": "Marca 2"})
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
|
|
||||||
response = self.client.get(f"/api/v1/shop/brands/{pk}/")
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
|
|
||||||
assert response.data.get("name") == "Marca 2"
|
|
||||||
@@ -1,206 +0,0 @@
|
|||||||
from decimal import Decimal
|
|
||||||
|
|
||||||
from django.contrib.auth import get_user_model
|
|
||||||
from rest_framework import status
|
|
||||||
from rest_framework.test import APITestCase
|
|
||||||
|
|
||||||
from config.tests.mixins import TestUserAuthenticationMixin
|
|
||||||
from shop.models import CustomerAddress, Product, ProductPrice, Tax
|
|
||||||
|
|
||||||
User = get_user_model()
|
|
||||||
|
|
||||||
|
|
||||||
class TestOrdersAPI(APITestCase, TestUserAuthenticationMixin):
|
|
||||||
model_name = "productprice"
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
self.create_user()
|
|
||||||
self.tax = Tax.objects.create(
|
|
||||||
code="IVA",
|
|
||||||
value=21,
|
|
||||||
)
|
|
||||||
self.customer = get_user_model().objects.create_user(
|
|
||||||
username="11111111H",
|
|
||||||
first_name="Darth",
|
|
||||||
last_name="Maull",
|
|
||||||
email="darth@maul.com",
|
|
||||||
password="dathomir",
|
|
||||||
)
|
|
||||||
|
|
||||||
self.customer_shipping_address = CustomerAddress.objects.create(
|
|
||||||
user=self.customer,
|
|
||||||
address="Dathomir",
|
|
||||||
address_town="Dathomir",
|
|
||||||
address_zip="00001",
|
|
||||||
address_state="Dathomir",
|
|
||||||
address_phone="900000000",
|
|
||||||
address_type=CustomerAddress.Types.SHIPPING,
|
|
||||||
)
|
|
||||||
|
|
||||||
self.customer_billing_address = CustomerAddress.objects.create(
|
|
||||||
user=self.customer,
|
|
||||||
address="Dathomir",
|
|
||||||
address_town="Dathomir",
|
|
||||||
address_zip="00001",
|
|
||||||
address_state="Dathomir",
|
|
||||||
address_phone="900000000",
|
|
||||||
address_type=CustomerAddress.Types.BILLING,
|
|
||||||
)
|
|
||||||
self.create_products()
|
|
||||||
|
|
||||||
def create_products(self):
|
|
||||||
self.product = Product.objects.create(
|
|
||||||
name="Papafritas",
|
|
||||||
description="Las mejores papafritas",
|
|
||||||
is_digital_asset=False,
|
|
||||||
url="",
|
|
||||||
)
|
|
||||||
|
|
||||||
ProductPrice.objects.create(
|
|
||||||
product=self.product,
|
|
||||||
price=Decimal("1.20"),
|
|
||||||
tax=self.tax,
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_create_retrieve_order(self):
|
|
||||||
self.login()
|
|
||||||
|
|
||||||
response = self.client.post(
|
|
||||||
f"/api/v1/shop/orders/",
|
|
||||||
{
|
|
||||||
"user": self.user.pk,
|
|
||||||
"billing_address": self.customer_billing_address.address,
|
|
||||||
"billing_city": self.customer_billing_address.address_town,
|
|
||||||
"billing_state": self.customer_billing_address.address_state,
|
|
||||||
"billing_country": self.customer_billing_address.address_country,
|
|
||||||
"billing_zip": self.customer_billing_address.address_zip,
|
|
||||||
"shipping_address": self.customer_shipping_address.address,
|
|
||||||
"shipping_city": self.customer_shipping_address.address_town,
|
|
||||||
"shipping_state": self.customer_shipping_address.address_state,
|
|
||||||
"shipping_country": self.customer_shipping_address.address_country,
|
|
||||||
"shipping_zip": self.customer_shipping_address.address_zip,
|
|
||||||
"contact_phone": self.customer_shipping_address.address_phone,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == status.HTTP_201_CREATED
|
|
||||||
pk = response.data.get("uuid")
|
|
||||||
|
|
||||||
response = self.client.get(f"/api/v1/shop/orders/{pk}/")
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
|
|
||||||
def test_create_retrieve_order_with_lines(self):
|
|
||||||
self.login()
|
|
||||||
|
|
||||||
response = self.client.post(
|
|
||||||
f"/api/v1/shop/orders/",
|
|
||||||
{
|
|
||||||
"user": self.user.pk,
|
|
||||||
"billing_address": self.customer_billing_address.address,
|
|
||||||
"billing_city": self.customer_billing_address.address_town,
|
|
||||||
"billing_state": self.customer_billing_address.address_state,
|
|
||||||
"billing_country": self.customer_billing_address.address_country,
|
|
||||||
"billing_zip": self.customer_billing_address.address_zip,
|
|
||||||
"shipping_address": self.customer_shipping_address.address,
|
|
||||||
"shipping_city": self.customer_shipping_address.address_town,
|
|
||||||
"shipping_state": self.customer_shipping_address.address_state,
|
|
||||||
"shipping_country": self.customer_shipping_address.address_country,
|
|
||||||
"shipping_zip": self.customer_shipping_address.address_zip,
|
|
||||||
"contact_phone": self.customer_shipping_address.address_phone,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == status.HTTP_201_CREATED
|
|
||||||
pk = response.data.get("uuid")
|
|
||||||
|
|
||||||
response = self.client.get(f"/api/v1/shop/orders/{pk}/")
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
assert Decimal(response.data.get("base_total")) == Decimal("0.00")
|
|
||||||
assert Decimal(response.data.get("total")) == Decimal("0.00")
|
|
||||||
|
|
||||||
response = self.client.post(
|
|
||||||
f"/api/v1/shop/orders/{pk}/lines/",
|
|
||||||
{
|
|
||||||
"product": self.product.pk,
|
|
||||||
"quantity": "5",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == status.HTTP_201_CREATED
|
|
||||||
response = self.client.get(f"/api/v1/shop/orders/{pk}/lines/")
|
|
||||||
assert len(response.data.get("results")) == 1
|
|
||||||
|
|
||||||
response = self.client.get(f"/api/v1/shop/orders/{pk}/")
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
|
|
||||||
assert Decimal(response.data.get("base_total")) == Decimal("6.00")
|
|
||||||
assert Decimal(response.data.get("total")) == Decimal("6.00") + (
|
|
||||||
Decimal("6.00") * Decimal("0.21")
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_create_delete_order_with_lines(self):
|
|
||||||
self.login()
|
|
||||||
|
|
||||||
# Create order
|
|
||||||
response = self.client.post(
|
|
||||||
f"/api/v1/shop/orders/",
|
|
||||||
{
|
|
||||||
"user": self.user.pk,
|
|
||||||
"billing_address": self.customer_billing_address.address,
|
|
||||||
"billing_city": self.customer_billing_address.address_town,
|
|
||||||
"billing_state": self.customer_billing_address.address_state,
|
|
||||||
"billing_country": self.customer_billing_address.address_country,
|
|
||||||
"billing_zip": self.customer_billing_address.address_zip,
|
|
||||||
"shipping_address": self.customer_shipping_address.address,
|
|
||||||
"shipping_city": self.customer_shipping_address.address_town,
|
|
||||||
"shipping_state": self.customer_shipping_address.address_state,
|
|
||||||
"shipping_country": self.customer_shipping_address.address_country,
|
|
||||||
"shipping_zip": self.customer_shipping_address.address_zip,
|
|
||||||
"contact_phone": self.customer_shipping_address.address_phone,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == status.HTTP_201_CREATED
|
|
||||||
pk = response.data.get("uuid")
|
|
||||||
|
|
||||||
response = self.client.get(f"/api/v1/shop/orders/{pk}/")
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
|
|
||||||
# Assert order is created with 0.00€
|
|
||||||
assert Decimal(response.data.get("base_total")) == Decimal("0.00")
|
|
||||||
assert Decimal(response.data.get("total")) == Decimal("0.00")
|
|
||||||
|
|
||||||
# Create order line
|
|
||||||
response = self.client.post(
|
|
||||||
f"/api/v1/shop/orders/{pk}/lines/",
|
|
||||||
{
|
|
||||||
"product": self.product.pk,
|
|
||||||
"quantity": "5",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
line_pk = response.data.get("id")
|
|
||||||
|
|
||||||
assert response.status_code == status.HTTP_201_CREATED
|
|
||||||
response = self.client.get(f"/api/v1/shop/orders/{pk}/lines/")
|
|
||||||
assert len(response.data.get("results")) == 1
|
|
||||||
|
|
||||||
response = self.client.get(f"/api/v1/shop/orders/{pk}/")
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
|
|
||||||
# Assert order totals are set
|
|
||||||
assert Decimal(response.data.get("base_total")) == Decimal("6.00")
|
|
||||||
assert Decimal(response.data.get("total")) == Decimal("6.00") + (
|
|
||||||
Decimal("6.00") * Decimal("0.21")
|
|
||||||
)
|
|
||||||
|
|
||||||
# Delete line
|
|
||||||
response = self.client.delete(f"/api/v1/shop/orders/{pk}/lines/{line_pk}/")
|
|
||||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
|
||||||
|
|
||||||
response = self.client.get(f"/api/v1/shop/orders/{pk}/")
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
|
|
||||||
# Assert order totals are 0.00
|
|
||||||
assert Decimal(response.data.get("base_total")) == Decimal("0.00")
|
|
||||||
assert Decimal(response.data.get("total")) == Decimal("0.00")
|
|
||||||
@@ -1,120 +0,0 @@
|
|||||||
from decimal import Decimal
|
|
||||||
|
|
||||||
from rest_framework import status
|
|
||||||
from rest_framework.test import APITestCase
|
|
||||||
|
|
||||||
from config.tests.mixins import TestUserAuthenticationMixin
|
|
||||||
from shop.models import Product, Provider
|
|
||||||
|
|
||||||
|
|
||||||
class TestProductBatchesAPI(APITestCase, TestUserAuthenticationMixin):
|
|
||||||
model_name = "productbatch"
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
self.create_user()
|
|
||||||
|
|
||||||
def create_provider(self):
|
|
||||||
return Provider.objects.create(
|
|
||||||
vat_id="11111111H",
|
|
||||||
name="Mandalorians",
|
|
||||||
email="din@djarin.com",
|
|
||||||
phone="612345678",
|
|
||||||
address="Mandalore",
|
|
||||||
city="Mandalore",
|
|
||||||
state="Mandalore",
|
|
||||||
country="Mandalore",
|
|
||||||
zip="12345",
|
|
||||||
)
|
|
||||||
|
|
||||||
def create_product(self):
|
|
||||||
self.product = Product.objects.create(
|
|
||||||
name="Papafritas",
|
|
||||||
description="Las mejores papafritas",
|
|
||||||
is_digital_asset=False,
|
|
||||||
url="",
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_create_retrieve_product_batch(self):
|
|
||||||
self.login()
|
|
||||||
self.create_product()
|
|
||||||
provider = self.create_provider()
|
|
||||||
response = self.client.post(
|
|
||||||
"/api/v1/shop/product-batches/",
|
|
||||||
{
|
|
||||||
"code": "B00001",
|
|
||||||
"product": self.product.pk,
|
|
||||||
"quantity": "5",
|
|
||||||
"provider": provider.pk,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
assert response.status_code == status.HTTP_201_CREATED
|
|
||||||
pk = response.data.get("id")
|
|
||||||
|
|
||||||
response = self.client.get(f"/api/v1/shop/product-batches/{pk}/")
|
|
||||||
self.product.refresh_from_db()
|
|
||||||
assert self.product.stock == Decimal("5")
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
|
|
||||||
def test_create_update_product_batch(self):
|
|
||||||
self.login()
|
|
||||||
self.create_product()
|
|
||||||
provider = self.create_provider()
|
|
||||||
|
|
||||||
response = self.client.post(
|
|
||||||
"/api/v1/shop/product-batches/",
|
|
||||||
{
|
|
||||||
"code": "B00001",
|
|
||||||
"product": self.product.pk,
|
|
||||||
"quantity": "5",
|
|
||||||
"provider": provider.pk,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
assert response.status_code == status.HTTP_201_CREATED
|
|
||||||
pk = response.data.get("id")
|
|
||||||
|
|
||||||
response = self.client.get(f"/api/v1/shop/product-batches/{pk}/")
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
|
|
||||||
self.product.refresh_from_db()
|
|
||||||
assert self.product.stock == Decimal("5")
|
|
||||||
|
|
||||||
response = self.client.patch(
|
|
||||||
f"/api/v1/shop/product-batches/{pk}/",
|
|
||||||
{
|
|
||||||
"quantity": "10",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
|
|
||||||
self.product.refresh_from_db()
|
|
||||||
assert self.product.stock == Decimal("10")
|
|
||||||
|
|
||||||
response = self.client.get(f"/api/v1/shop/product-batches/{pk}/")
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
assert Decimal(response.data.get("quantity")) == Decimal("10")
|
|
||||||
|
|
||||||
def test_create_delete_product_batch(self):
|
|
||||||
self.login()
|
|
||||||
self.create_product()
|
|
||||||
provider = self.create_provider()
|
|
||||||
|
|
||||||
response = self.client.post(
|
|
||||||
"/api/v1/shop/product-batches/",
|
|
||||||
{
|
|
||||||
"code": "B00001",
|
|
||||||
"product": self.product.pk,
|
|
||||||
"quantity": "5",
|
|
||||||
"provider": provider.pk,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
assert response.status_code == status.HTTP_201_CREATED
|
|
||||||
pk = response.data.get("id")
|
|
||||||
|
|
||||||
response = self.client.get(f"/api/v1/shop/product-batches/{pk}/")
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
|
|
||||||
self.product.refresh_from_db()
|
|
||||||
assert self.product.stock == Decimal("5")
|
|
||||||
|
|
||||||
response = self.client.delete(f"/api/v1/shop/product-batches/{pk}/")
|
|
||||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
from decimal import Decimal
|
|
||||||
|
|
||||||
from rest_framework import status
|
|
||||||
from rest_framework.test import APITestCase
|
|
||||||
|
|
||||||
from config.tests.mixins import TestUserAuthenticationMixin
|
|
||||||
from shop.models import Product, ProductPrice, Tax
|
|
||||||
|
|
||||||
|
|
||||||
class TestProductPricessAPI(APITestCase, TestUserAuthenticationMixin):
|
|
||||||
model_name = "productprice"
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
self.create_user()
|
|
||||||
self.tax = Tax.objects.create(
|
|
||||||
code="IVA",
|
|
||||||
value=21,
|
|
||||||
)
|
|
||||||
|
|
||||||
def create_products(self):
|
|
||||||
self.product = Product.objects.create(
|
|
||||||
name="Papafritas",
|
|
||||||
description="Las mejores papafritas",
|
|
||||||
is_digital_asset=False,
|
|
||||||
url="",
|
|
||||||
)
|
|
||||||
|
|
||||||
ProductPrice.objects.create(
|
|
||||||
product=self.product,
|
|
||||||
price=Decimal("1.20"),
|
|
||||||
tax=self.tax,
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_fetch_product_prices(self):
|
|
||||||
self.login()
|
|
||||||
self.create_products()
|
|
||||||
response = self.client.get(f"/api/v1/shop/products/{self.product.pk}/prices/")
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
assert len(response.data.get("results")) == 1
|
|
||||||
|
|
||||||
def test_create_product_prices(self):
|
|
||||||
self.login()
|
|
||||||
|
|
||||||
product = Product.objects.create(
|
|
||||||
name="Papafritas",
|
|
||||||
description="Las mejores papafritas",
|
|
||||||
is_digital_asset=False,
|
|
||||||
url="",
|
|
||||||
)
|
|
||||||
response = self.client.post(
|
|
||||||
f"/api/v1/shop/products/{product.pk}/prices/",
|
|
||||||
{
|
|
||||||
"price": "1.20",
|
|
||||||
"tax": self.tax.pk,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
assert response.status_code == status.HTTP_201_CREATED
|
|
||||||
response = self.client.get(f"/api/v1/shop/products/{product.pk}/prices/")
|
|
||||||
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
assert len(response.data.get("results")) == 1
|
|
||||||
|
|
||||||
def test_create_update_product_prices(self):
|
|
||||||
self.login()
|
|
||||||
|
|
||||||
product = Product.objects.create(
|
|
||||||
name="Papafritas",
|
|
||||||
description="Las mejores papafritas",
|
|
||||||
is_digital_asset=False,
|
|
||||||
url="",
|
|
||||||
)
|
|
||||||
response = self.client.post(
|
|
||||||
f"/api/v1/shop/products/{product.pk}/prices/",
|
|
||||||
{
|
|
||||||
"price": "1.20",
|
|
||||||
"tax": self.tax.pk,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
assert response.status_code == status.HTTP_201_CREATED
|
|
||||||
pk = response.data.get("id")
|
|
||||||
response = self.client.patch(
|
|
||||||
f"/api/v1/shop/products/{product.pk}/prices/{pk}/",
|
|
||||||
{
|
|
||||||
"price": "1.10",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
response = self.client.get(f"/api/v1/shop/products/{product.pk}/prices/{pk}/")
|
|
||||||
assert response.data.get("price") == "1.10"
|
|
||||||
|
|
||||||
def test_create_delete_product_prices(self):
|
|
||||||
self.login()
|
|
||||||
|
|
||||||
product = Product.objects.create(
|
|
||||||
name="Papafritas",
|
|
||||||
description="Las mejores papafritas",
|
|
||||||
is_digital_asset=False,
|
|
||||||
url="",
|
|
||||||
)
|
|
||||||
response = self.client.post(
|
|
||||||
f"/api/v1/shop/products/{product.pk}/prices/",
|
|
||||||
{
|
|
||||||
"price": "1.20",
|
|
||||||
"tax": self.tax.pk,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
assert response.status_code == status.HTTP_201_CREATED
|
|
||||||
pk = response.data.get("id")
|
|
||||||
|
|
||||||
response = self.client.delete(
|
|
||||||
f"/api/v1/shop/products/{product.pk}/prices/{pk}/"
|
|
||||||
)
|
|
||||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
|
||||||
|
|
||||||
response = self.client.get(f"/api/v1/shop/products/{product.pk}/prices/{pk}/")
|
|
||||||
assert response.status_code == status.HTTP_404_NOT_FOUND
|
|
||||||
@@ -1,176 +0,0 @@
|
|||||||
import random
|
|
||||||
import string
|
|
||||||
from decimal import Decimal
|
|
||||||
|
|
||||||
from django.utils import timezone
|
|
||||||
from rest_framework import status
|
|
||||||
from rest_framework.test import APITestCase
|
|
||||||
|
|
||||||
from config.tests.mixins import TestUserAuthenticationMixin
|
|
||||||
from shop.models import Product, ProductBatch, ProductPrice, Tag, Tax
|
|
||||||
|
|
||||||
|
|
||||||
class TestProductsAPI(APITestCase, TestUserAuthenticationMixin):
|
|
||||||
model_name = "product"
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
self.create_user()
|
|
||||||
self.tax = Tax.objects.create(
|
|
||||||
code="IVA",
|
|
||||||
value=21,
|
|
||||||
)
|
|
||||||
self.tag = Tag.objects.create(name="Alimentos")
|
|
||||||
|
|
||||||
def get_random_sku(self):
|
|
||||||
return "".join(random.choices(string.ascii_uppercase, k=16))
|
|
||||||
|
|
||||||
def create_products(self):
|
|
||||||
self.product = Product.objects.create(
|
|
||||||
sku=self.get_random_sku(),
|
|
||||||
name="Papafritas",
|
|
||||||
description="Las mejores papafritas",
|
|
||||||
is_digital_asset=False,
|
|
||||||
url="",
|
|
||||||
)
|
|
||||||
|
|
||||||
self.product_price = ProductPrice.objects.create(
|
|
||||||
product=self.product,
|
|
||||||
price=Decimal("1.20"),
|
|
||||||
tax=self.tax,
|
|
||||||
date=timezone.now(),
|
|
||||||
)
|
|
||||||
|
|
||||||
self.batch = ProductBatch.objects.create(
|
|
||||||
code="B0001",
|
|
||||||
product=self.product,
|
|
||||||
quantity=Decimal("10"),
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_fetch_products(self):
|
|
||||||
self.login()
|
|
||||||
self.create_products()
|
|
||||||
response = self.client.get("/api/v1/shop/products/")
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
assert len(response.data.get("results")) == 1
|
|
||||||
|
|
||||||
def test_create_product(self):
|
|
||||||
self.login()
|
|
||||||
response = self.client.post(
|
|
||||||
"/api/v1/shop/products/",
|
|
||||||
{
|
|
||||||
"sku": self.get_random_sku(),
|
|
||||||
"name": "Papafritas",
|
|
||||||
"description": "Las mejores papafritas",
|
|
||||||
"is_digital_asset": False,
|
|
||||||
"url": "",
|
|
||||||
"price": "1.20",
|
|
||||||
"stock": "100",
|
|
||||||
"tax": self.tax.pk,
|
|
||||||
"tags": [self.tag.pk],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
assert response.status_code == status.HTTP_201_CREATED
|
|
||||||
pk = response.data.get("id")
|
|
||||||
|
|
||||||
response = self.client.get(f"/api/v1/shop/products/{pk}/")
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
|
|
||||||
def test_create_update_product(self):
|
|
||||||
self.login()
|
|
||||||
response = self.client.post(
|
|
||||||
"/api/v1/shop/products/",
|
|
||||||
{
|
|
||||||
"sku": self.get_random_sku(),
|
|
||||||
"name": "Papafritas",
|
|
||||||
"description": "Las mejores papafritas",
|
|
||||||
"is_digital_asset": False,
|
|
||||||
"url": "",
|
|
||||||
"price": "1.20",
|
|
||||||
"stock": "100",
|
|
||||||
"tax": self.tax.pk,
|
|
||||||
"tags": [
|
|
||||||
self.tag.pk,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
assert response.status_code == status.HTTP_201_CREATED
|
|
||||||
pk = response.data.get("id")
|
|
||||||
|
|
||||||
response = self.client.get(f"/api/v1/shop/products/{pk}/")
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
|
|
||||||
response = self.client.patch(
|
|
||||||
f"/api/v1/shop/products/{pk}/", {"name": "Papafritas pafritas"}
|
|
||||||
)
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
response = self.client.get(f"/api/v1/shop/products/{pk}/")
|
|
||||||
assert response.data.get("name") == "Papafritas pafritas"
|
|
||||||
|
|
||||||
def test_create_delete_product(self):
|
|
||||||
self.login()
|
|
||||||
response = self.client.post(
|
|
||||||
"/api/v1/shop/products/",
|
|
||||||
{
|
|
||||||
"sku": self.get_random_sku(),
|
|
||||||
"name": "Papafritas",
|
|
||||||
"description": "Las mejores papafritas",
|
|
||||||
"is_digital_asset": False,
|
|
||||||
"url": "",
|
|
||||||
"price": "1.20",
|
|
||||||
"stock": "100",
|
|
||||||
"tax": self.tax.pk,
|
|
||||||
"tags": [
|
|
||||||
self.tag.pk,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
assert response.status_code == status.HTTP_201_CREATED
|
|
||||||
pk = response.data.get("id")
|
|
||||||
|
|
||||||
response = self.client.get(f"/api/v1/shop/products/{pk}/")
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
|
|
||||||
response = self.client.delete(f"/api/v1/shop/products/{pk}/")
|
|
||||||
assert response.status_code == status.HTTP_204_NO_CONTENT
|
|
||||||
response = self.client.get(f"/api/v1/shop/products/{pk}/")
|
|
||||||
assert response.status_code == status.HTTP_404_NOT_FOUND
|
|
||||||
|
|
||||||
def test_create_cannot_delete_product_with_a_batch(self):
|
|
||||||
self.login()
|
|
||||||
self.create_products()
|
|
||||||
|
|
||||||
response = self.client.get(f"/api/v1/shop/products/{self.product.pk}/")
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
|
|
||||||
response = self.client.delete(f"/api/v1/shop/products/{self.product.pk}/")
|
|
||||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
|
||||||
|
|
||||||
def test_create_update_product_tags(self):
|
|
||||||
self.login()
|
|
||||||
tag = Tag.objects.create(name="Alimentación")
|
|
||||||
response = self.client.post(
|
|
||||||
"/api/v1/shop/products/",
|
|
||||||
{
|
|
||||||
"sku": self.get_random_sku(),
|
|
||||||
"name": "Papafritas",
|
|
||||||
"description": "Las mejores papafritas",
|
|
||||||
"is_digital_asset": False,
|
|
||||||
"url": "",
|
|
||||||
"price": "1.20",
|
|
||||||
"stock": "100",
|
|
||||||
"tax": self.tax.pk,
|
|
||||||
"tags": [
|
|
||||||
self.tag.pk,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
assert response.status_code == status.HTTP_201_CREATED
|
|
||||||
pk = response.data.get("id")
|
|
||||||
|
|
||||||
response = self.client.get(f"/api/v1/shop/products/{pk}/")
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
|
|
||||||
response = self.client.patch(f"/api/v1/shop/products/{pk}/", {"tags": [tag.pk]})
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
response = self.client.get(f"/api/v1/shop/products/{pk}/")
|
|
||||||
assert response.data.get("tags", [])[0] == tag.pk
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
from rest_framework import status
|
|
||||||
from rest_framework.test import APITestCase
|
|
||||||
|
|
||||||
from config.tests.mixins import TestUserAuthenticationMixin
|
|
||||||
|
|
||||||
|
|
||||||
class TestProvidersAPI(APITestCase, TestUserAuthenticationMixin):
|
|
||||||
model_name = "provider"
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
self.create_user()
|
|
||||||
|
|
||||||
def test_create_retrieve_provider(self):
|
|
||||||
self.login()
|
|
||||||
response = self.client.post(
|
|
||||||
f"/api/v1/shop/providers/",
|
|
||||||
{
|
|
||||||
"vat_id": "11111111H",
|
|
||||||
"name": "Darth Maul",
|
|
||||||
"email": "darth@maul.com",
|
|
||||||
"address": "Dathomir",
|
|
||||||
"city": "Dathomir",
|
|
||||||
"state": "Dathomir",
|
|
||||||
"country": "Dathomir",
|
|
||||||
"zip": "00001",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == status.HTTP_201_CREATED
|
|
||||||
pk = response.data.get("id")
|
|
||||||
|
|
||||||
response = self.client.get(f"/api/v1/shop/providers/{pk}/")
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
|
|
||||||
def test_create_update_provider(self):
|
|
||||||
self.login()
|
|
||||||
response = self.client.post(
|
|
||||||
f"/api/v1/shop/providers/",
|
|
||||||
{
|
|
||||||
"vat_id": "11111111H",
|
|
||||||
"name": "Darth Maul",
|
|
||||||
"email": "darth@maul.com",
|
|
||||||
"address": "Dathomir",
|
|
||||||
"city": "Dathomir",
|
|
||||||
"state": "Dathomir",
|
|
||||||
"country": "Dathomir",
|
|
||||||
"zip": "00001",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == status.HTTP_201_CREATED
|
|
||||||
pk = response.data.get("id")
|
|
||||||
response = self.client.get(f"/api/v1/shop/providers/{pk}/")
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
|
|
||||||
response = self.client.patch(
|
|
||||||
f"/api/v1/shop/providers/{pk}/",
|
|
||||||
{
|
|
||||||
"address": "Mandalore",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
response = self.client.get(f"/api/v1/shop/providers/{pk}/")
|
|
||||||
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
assert response.data.get("address") == "Mandalore"
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
from rest_framework import status
|
|
||||||
from rest_framework.test import APITestCase
|
|
||||||
|
|
||||||
from config.tests.mixins import TestUserAuthenticationMixin
|
|
||||||
from shop.models import Tag
|
|
||||||
|
|
||||||
|
|
||||||
class TestTagsAPI(APITestCase, TestUserAuthenticationMixin):
|
|
||||||
model_name = "tag"
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
self.create_user()
|
|
||||||
|
|
||||||
def create_tags(self):
|
|
||||||
Tag.objects.create(name="Deportes")
|
|
||||||
|
|
||||||
def test_fetch_taxes(self):
|
|
||||||
self.create_tags()
|
|
||||||
self.login()
|
|
||||||
response = self.client.get("/api/v1/shop/tags/")
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
assert len(response.data.get("results")) == 1
|
|
||||||
|
|
||||||
def test_create_retrieve_tax(self):
|
|
||||||
self.login()
|
|
||||||
response = self.client.post("/api/v1/shop/tags/", {"name": "Videojuegos"})
|
|
||||||
assert response.status_code == status.HTTP_201_CREATED
|
|
||||||
pk = response.data.get("id")
|
|
||||||
|
|
||||||
response = self.client.get(f"/api/v1/shop/tags/{pk}/")
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
|
|
||||||
def test_create_update_tax(self):
|
|
||||||
self.login()
|
|
||||||
response = self.client.post("/api/v1/shop/tags/", {"name": "Videojuegos"})
|
|
||||||
assert response.status_code == status.HTTP_201_CREATED
|
|
||||||
pk = response.data.get("id")
|
|
||||||
|
|
||||||
response = self.client.get(f"/api/v1/shop/tags/{pk}/")
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
|
|
||||||
response = self.client.patch(
|
|
||||||
f"/api/v1/shop/tags/{pk}/", {"name": "Juegos de mesa"}
|
|
||||||
)
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
|
|
||||||
response = self.client.get(f"/api/v1/shop/tags/{pk}/")
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
|
|
||||||
assert response.data.get("name") == "Juegos de mesa"
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
from rest_framework import status
|
|
||||||
from rest_framework.test import APITestCase
|
|
||||||
|
|
||||||
from config.tests.mixins import TestUserAuthenticationMixin
|
|
||||||
from shop.models import Tax
|
|
||||||
|
|
||||||
|
|
||||||
class TestTaxesAPI(APITestCase, TestUserAuthenticationMixin):
|
|
||||||
model_name = "tax"
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
self.create_user()
|
|
||||||
|
|
||||||
def create_taxes(self):
|
|
||||||
Tax.objects.create(
|
|
||||||
code="IVA",
|
|
||||||
value=21,
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_fetch_taxes(self):
|
|
||||||
self.create_taxes()
|
|
||||||
self.login()
|
|
||||||
response = self.client.get("/api/v1/shop/taxes/")
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
assert len(response.data.get("results")) == 1
|
|
||||||
|
|
||||||
def test_create_retrieve_tax(self):
|
|
||||||
self.login()
|
|
||||||
response = self.client.post("/api/v1/shop/taxes/", {"code": "IVA5", "value": 5})
|
|
||||||
assert response.status_code == status.HTTP_201_CREATED
|
|
||||||
pk = response.data.get("id")
|
|
||||||
|
|
||||||
response = self.client.get(f"/api/v1/shop/taxes/{pk}/")
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
|
|
||||||
def test_create_update_tax(self):
|
|
||||||
self.login()
|
|
||||||
response = self.client.post("/api/v1/shop/taxes/", {"code": "IVA5", "value": 5})
|
|
||||||
assert response.status_code == status.HTTP_201_CREATED
|
|
||||||
pk = response.data.get("id")
|
|
||||||
|
|
||||||
response = self.client.get(f"/api/v1/shop/taxes/{pk}/")
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
|
|
||||||
response = self.client.patch(
|
|
||||||
f"/api/v1/shop/taxes/{pk}/", {"code": "IVA10", "value": 10}
|
|
||||||
)
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
|
|
||||||
response = self.client.get(f"/api/v1/shop/taxes/{pk}/")
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
|
|
||||||
assert response.data.get("code") == "IVA10"
|
|
||||||
assert response.data.get("value") == 10
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
from decimal import Decimal
|
|
||||||
|
|
||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
from shop.filters import ProductFilter
|
|
||||||
from shop.models import Product
|
|
||||||
from shop.tests.mixins import CreateProductsMixin
|
|
||||||
|
|
||||||
|
|
||||||
class TestProductFilter(TestCase, CreateProductsMixin):
|
|
||||||
def setUp(self):
|
|
||||||
product_a = self.create_product(
|
|
||||||
name="producto 1", sku="001", price=Decimal("40.00")
|
|
||||||
)
|
|
||||||
product_b = self.create_product(
|
|
||||||
name="producto 2", sku="002", price=Decimal("70.00")
|
|
||||||
)
|
|
||||||
product_c = self.create_product(
|
|
||||||
name="producto 3", sku="003", price=Decimal("100.00")
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_product_filter_by_price(self):
|
|
||||||
f = ProductFilter({"price_gt": "40"})
|
|
||||||
f.is_valid()
|
|
||||||
qs = f.filter_queryset(Product.objects.all())
|
|
||||||
assert qs.count() == 3
|
|
||||||
|
|
||||||
f = ProductFilter({"price_lt": "40"})
|
|
||||||
f.is_valid()
|
|
||||||
qs = f.filter_queryset(Product.objects.all())
|
|
||||||
assert qs.count() == 0
|
|
||||||
|
|
||||||
f = ProductFilter({"price_gt": "100"})
|
|
||||||
f.is_valid()
|
|
||||||
qs = f.filter_queryset(Product.objects.all())
|
|
||||||
assert qs.count() == 1
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
from decimal import Decimal
|
|
||||||
|
|
||||||
from django.contrib.auth import get_user_model
|
|
||||||
from rest_framework.test import APITestCase as TestCase
|
|
||||||
|
|
||||||
from shop.models import CustomerAddress, Product, ProductPrice, Tax
|
|
||||||
from shop.utils import create_order, create_order_line_for_product
|
|
||||||
|
|
||||||
User = get_user_model()
|
|
||||||
|
|
||||||
|
|
||||||
class ShopModelsTest(TestCase):
|
|
||||||
def setUp(self) -> None:
|
|
||||||
self.tax = Tax.objects.create(
|
|
||||||
code="IVA",
|
|
||||||
value=21,
|
|
||||||
)
|
|
||||||
self.customer = User.objects.create_user(
|
|
||||||
username="11111111H",
|
|
||||||
first_name="Darth",
|
|
||||||
last_name="Maull",
|
|
||||||
email="darth@maul.com",
|
|
||||||
password="dathomir",
|
|
||||||
)
|
|
||||||
|
|
||||||
self.customer_shipping_address = CustomerAddress.objects.create(
|
|
||||||
user=self.customer,
|
|
||||||
address="Dathomir",
|
|
||||||
address_town="Dathomir",
|
|
||||||
address_zip="00001",
|
|
||||||
address_state="Dathomir",
|
|
||||||
address_phone="900000000",
|
|
||||||
address_type=CustomerAddress.Types.SHIPPING,
|
|
||||||
)
|
|
||||||
|
|
||||||
self.customer_billing_address = CustomerAddress.objects.create(
|
|
||||||
user=self.customer,
|
|
||||||
address="Dathomir",
|
|
||||||
address_town="Dathomir",
|
|
||||||
address_zip="00001",
|
|
||||||
address_state="Dathomir",
|
|
||||||
address_phone="900000000",
|
|
||||||
address_type=CustomerAddress.Types.BILLING,
|
|
||||||
)
|
|
||||||
self.create_products()
|
|
||||||
|
|
||||||
def create_products(self):
|
|
||||||
self.potatoes = Product.objects.create(
|
|
||||||
sku="0000001",
|
|
||||||
name="Patatas",
|
|
||||||
stock=Decimal("100.00"),
|
|
||||||
)
|
|
||||||
self.gasoline = Product.objects.create(
|
|
||||||
sku="0000002",
|
|
||||||
name="Gasolina",
|
|
||||||
stock=Decimal("800.00"),
|
|
||||||
)
|
|
||||||
self.usb_c = Product.objects.create(
|
|
||||||
sku="0000003",
|
|
||||||
name="Cable USB-C",
|
|
||||||
stock=Decimal("5.00"),
|
|
||||||
)
|
|
||||||
|
|
||||||
ProductPrice.objects.create(
|
|
||||||
product=self.potatoes, price=Decimal("0.80"), tax=self.tax
|
|
||||||
)
|
|
||||||
ProductPrice.objects.create(
|
|
||||||
product=self.gasoline, price=Decimal("1.15"), tax=self.tax
|
|
||||||
)
|
|
||||||
ProductPrice.objects.create(
|
|
||||||
product=self.usb_c, price=Decimal("9.95"), tax=self.tax
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_create_order(self):
|
|
||||||
order = create_order(
|
|
||||||
customer=self.customer,
|
|
||||||
billing_address=self.customer_billing_address.address,
|
|
||||||
billing_city=self.customer_billing_address.address_town,
|
|
||||||
billing_state=self.customer_billing_address.address_state,
|
|
||||||
billing_zip=self.customer_billing_address.address_zip,
|
|
||||||
billing_country=self.customer_billing_address.address_country,
|
|
||||||
)
|
|
||||||
|
|
||||||
l1 = create_order_line_for_product(
|
|
||||||
self.potatoes,
|
|
||||||
quantity=Decimal("1.5"),
|
|
||||||
order=order,
|
|
||||||
)
|
|
||||||
l2 = create_order_line_for_product(
|
|
||||||
self.gasoline,
|
|
||||||
quantity=Decimal("40"),
|
|
||||||
order=order,
|
|
||||||
)
|
|
||||||
l3 = create_order_line_for_product(
|
|
||||||
self.usb_c,
|
|
||||||
quantity=Decimal("1.00"),
|
|
||||||
order=order,
|
|
||||||
)
|
|
||||||
order.calculate_total_from_lines()
|
|
||||||
|
|
||||||
assert order.total == l1.total + l2.total + l3.total
|
|
||||||
assert order.base_total == l1.base_total + l2.base_total + l3.base_total
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
from typing import List
|
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.contrib.auth import get_user_model
|
|
||||||
from django.contrib.auth.password_validation import (
|
|
||||||
get_password_validators,
|
|
||||||
validate_password,
|
|
||||||
)
|
|
||||||
from rest_framework import serializers
|
|
||||||
from rest_framework.exceptions import ValidationError
|
|
||||||
|
|
||||||
User = get_user_model()
|
|
||||||
|
|
||||||
|
|
||||||
AUTH_PASSWORD_VALIDATORS = getattr(settings, "AUTH_PASSWORD_VALIDATORS")
|
|
||||||
|
|
||||||
|
|
||||||
class UserSerializer(serializers.ModelSerializer):
|
|
||||||
permissions = serializers.SerializerMethodField()
|
|
||||||
|
|
||||||
def get_permissions(self, obj: User) -> List[str]:
|
|
||||||
group_permissions = obj.get_group_permissions()
|
|
||||||
user_permissions = obj.get_user_permissions()
|
|
||||||
|
|
||||||
return sorted(user_permissions.union(group_permissions))
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = User
|
|
||||||
fields = (
|
|
||||||
"username",
|
|
||||||
"email",
|
|
||||||
"first_name",
|
|
||||||
"last_name",
|
|
||||||
"is_staff",
|
|
||||||
"is_superuser",
|
|
||||||
"permissions",
|
|
||||||
)
|
|
||||||
read_only_fields = (
|
|
||||||
"username",
|
|
||||||
"code",
|
|
||||||
"is_staff",
|
|
||||||
"is_superuser",
|
|
||||||
"permissions",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class UpdatePasswordSerializer(serializers.ModelSerializer):
|
|
||||||
old_password = serializers.CharField(write_only=True)
|
|
||||||
new_password = serializers.CharField(write_only=True)
|
|
||||||
new_password2 = serializers.CharField(write_only=True)
|
|
||||||
|
|
||||||
def validate_old_password(self, password):
|
|
||||||
user = self.context.get("request").user
|
|
||||||
|
|
||||||
if not user.check_password(password):
|
|
||||||
raise ValidationError("Wrong old password")
|
|
||||||
|
|
||||||
return password
|
|
||||||
|
|
||||||
def validate_new_password(self, password):
|
|
||||||
old_password = self.context.get("request").data.get("old_password")
|
|
||||||
|
|
||||||
if password == old_password:
|
|
||||||
raise ValidationError("Password can't be the same as the old one")
|
|
||||||
|
|
||||||
password2 = self.context.get("request").data.get("new_password2")
|
|
||||||
if password != password2:
|
|
||||||
raise ValidationError("Password mismatch")
|
|
||||||
|
|
||||||
validate_password(
|
|
||||||
password,
|
|
||||||
password_validators=get_password_validators(AUTH_PASSWORD_VALIDATORS),
|
|
||||||
)
|
|
||||||
|
|
||||||
return password
|
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
|
||||||
new_password = self.validated_data.get("new_password")
|
|
||||||
self.instance.set_password(new_password)
|
|
||||||
self.instance.save()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = User
|
|
||||||
fields = (
|
|
||||||
"old_password",
|
|
||||||
"new_password",
|
|
||||||
"new_password2",
|
|
||||||
)
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
from django.urls import path
|
|
||||||
from rest_framework_simplejwt.views import (
|
|
||||||
token_obtain_pair,
|
|
||||||
token_refresh,
|
|
||||||
token_verify,
|
|
||||||
)
|
|
||||||
|
|
||||||
from users.api.v1.views import change_password, retrieve_update_me
|
|
||||||
|
|
||||||
app_name = "auth"
|
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
|
||||||
path("login/", token_obtain_pair, name="login"),
|
|
||||||
path("refresh/", token_refresh, name="refresh_jwt"),
|
|
||||||
path("verify/", token_verify, name="verify_jwt"),
|
|
||||||
path("me/", retrieve_update_me, name="user_info"),
|
|
||||||
path("change-password/", change_password, name="change_password"),
|
|
||||||
]
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
from django.contrib.auth.models import User
|
|
||||||
from rest_framework.generics import RetrieveUpdateAPIView, UpdateAPIView
|
|
||||||
from rest_framework.permissions import IsAuthenticated
|
|
||||||
|
|
||||||
from users.api.v1.serializers import UpdatePasswordSerializer, UserSerializer
|
|
||||||
|
|
||||||
|
|
||||||
class RetrieveUpdateMe(RetrieveUpdateAPIView):
|
|
||||||
"""
|
|
||||||
get: Return user info
|
|
||||||
|
|
||||||
put: Update user info
|
|
||||||
"""
|
|
||||||
|
|
||||||
serializer_class = UserSerializer
|
|
||||||
|
|
||||||
def get_object(self):
|
|
||||||
return self.request.user
|
|
||||||
|
|
||||||
|
|
||||||
class ChangePassword(UpdateAPIView):
|
|
||||||
"""
|
|
||||||
put: Update user's password
|
|
||||||
"""
|
|
||||||
|
|
||||||
serializer_class = UpdatePasswordSerializer
|
|
||||||
permission_classes = (IsAuthenticated,)
|
|
||||||
queryset = User.objects.all()
|
|
||||||
|
|
||||||
def get_object(self):
|
|
||||||
return self.request.user
|
|
||||||
|
|
||||||
|
|
||||||
retrieve_update_me = RetrieveUpdateMe.as_view()
|
|
||||||
change_password = ChangePassword.as_view()
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
from django.contrib.auth import get_user_model
|
|
||||||
from django.shortcuts import reverse
|
|
||||||
from rest_framework import status
|
|
||||||
from rest_framework.test import APITestCase
|
|
||||||
|
|
||||||
User = get_user_model()
|
|
||||||
|
|
||||||
|
|
||||||
class TestChangePassword(APITestCase):
|
|
||||||
def setUp(self) -> None:
|
|
||||||
self.password = "theonering"
|
|
||||||
self.user = User.objects.create(
|
|
||||||
username="sauron",
|
|
||||||
email="sauron@mordor.middleearth",
|
|
||||||
)
|
|
||||||
|
|
||||||
self.user.set_password(self.password)
|
|
||||||
self.user.save()
|
|
||||||
|
|
||||||
def test_login_then_change_password(self):
|
|
||||||
response = self.client.post(
|
|
||||||
reverse("auth:login"),
|
|
||||||
{
|
|
||||||
"username": self.user.username,
|
|
||||||
"password": self.password,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
jwt_token = response.data.get("access")
|
|
||||||
self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {jwt_token}")
|
|
||||||
response = self.client.put(
|
|
||||||
reverse("auth:change_password"),
|
|
||||||
{
|
|
||||||
"new_password": "barad-dur",
|
|
||||||
"new_password2": "barad-dur",
|
|
||||||
"old_password": self.password,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
|
|
||||||
def test_login_then_change_password_with_wrong_password(self):
|
|
||||||
response = self.client.post(
|
|
||||||
reverse("auth:login"),
|
|
||||||
{
|
|
||||||
"username": self.user.username,
|
|
||||||
"password": self.password,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
jwt_token = response.data.get("access")
|
|
||||||
self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {jwt_token}")
|
|
||||||
response = self.client.put(
|
|
||||||
reverse("auth:change_password"),
|
|
||||||
{
|
|
||||||
"new_password": "barad-dur",
|
|
||||||
"new_password2": "barad-dur",
|
|
||||||
"old_password": "incorrectoldpassword",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
|
||||||
|
|
||||||
def test_login_then_change_password_mismatch_password(self):
|
|
||||||
response = self.client.post(
|
|
||||||
reverse("auth:login"),
|
|
||||||
{
|
|
||||||
"username": self.user.username,
|
|
||||||
"password": self.password,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
jwt_token = response.data.get("access")
|
|
||||||
self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {jwt_token}")
|
|
||||||
response = self.client.put(
|
|
||||||
reverse("auth:change_password"),
|
|
||||||
{
|
|
||||||
"new_password": "barad-dur",
|
|
||||||
"new_password2": "mountdoom",
|
|
||||||
"old_password": self.password,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
|
||||||
|
|
||||||
def test_login_then_change_password_but_its_the_same(self):
|
|
||||||
response = self.client.post(
|
|
||||||
reverse("auth:login"),
|
|
||||||
{
|
|
||||||
"username": self.user.username,
|
|
||||||
"password": self.password,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
jwt_token = response.data.get("access")
|
|
||||||
self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {jwt_token}")
|
|
||||||
response = self.client.put(
|
|
||||||
reverse("auth:change_password"),
|
|
||||||
{
|
|
||||||
"new_password": self.password,
|
|
||||||
"new_password2": self.password,
|
|
||||||
"old_password": self.password,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
from django.contrib.auth import get_user_model
|
|
||||||
from django.shortcuts import reverse
|
|
||||||
from rest_framework import status
|
|
||||||
from rest_framework.test import APITestCase
|
|
||||||
|
|
||||||
User = get_user_model()
|
|
||||||
|
|
||||||
|
|
||||||
class TestLogin(APITestCase):
|
|
||||||
def setUp(self) -> None:
|
|
||||||
self.password = "theonering"
|
|
||||||
self.user = User.objects.create(
|
|
||||||
username="sauron",
|
|
||||||
email="sauron@mordor.middleearth",
|
|
||||||
)
|
|
||||||
|
|
||||||
self.user.set_password(self.password)
|
|
||||||
self.user.save()
|
|
||||||
|
|
||||||
def test_login(self):
|
|
||||||
response = self.client.post(
|
|
||||||
reverse("auth:login"),
|
|
||||||
{
|
|
||||||
"username": self.user.username,
|
|
||||||
"password": self.password,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
assert response.data.get("access") is not None
|
|
||||||
assert response.data.get("refresh") is not None
|
|
||||||
|
|
||||||
def test_login_then_verify(self):
|
|
||||||
response = self.client.post(
|
|
||||||
reverse("auth:login"),
|
|
||||||
{
|
|
||||||
"username": self.user.username,
|
|
||||||
"password": self.password,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
jwt_token = response.data.get("access")
|
|
||||||
response = self.client.post(
|
|
||||||
reverse("auth:verify_jwt"),
|
|
||||||
{
|
|
||||||
"token": jwt_token,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
|
|
||||||
def test_login_then_refresh(self):
|
|
||||||
response = self.client.post(
|
|
||||||
reverse("auth:login"),
|
|
||||||
{
|
|
||||||
"username": self.user.username,
|
|
||||||
"password": self.password,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
jwt_token = response.data.get("refresh")
|
|
||||||
response = self.client.post(
|
|
||||||
reverse("auth:refresh_jwt"),
|
|
||||||
{
|
|
||||||
"refresh": jwt_token,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
assert response.data.get("access") is not None
|
|
||||||
assert response.data.get("access") != jwt_token
|
|
||||||
|
|
||||||
def test_login_then_get_user_info(self):
|
|
||||||
response = self.client.post(
|
|
||||||
reverse("auth:login"),
|
|
||||||
{
|
|
||||||
"username": self.user.username,
|
|
||||||
"password": self.password,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
jwt_token = response.data.get("access")
|
|
||||||
self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {jwt_token}")
|
|
||||||
response = self.client.get(reverse("auth:user_info"))
|
|
||||||
|
|
||||||
assert response.status_code == status.HTTP_200_OK
|
|
||||||
assert response.data.get("email") == self.user.email
|
|
||||||
|
|
||||||
def test_login_failed(self):
|
|
||||||
response = self.client.post(
|
|
||||||
reverse("auth:login"),
|
|
||||||
{
|
|
||||||
"username": self.user.username,
|
|
||||||
"password": "wrongpassword",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == status.HTTP_401_UNAUTHORIZED
|
|
||||||
Reference in New Issue
Block a user