add: add new invalid product model and create, list api added
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
from .wherehouse import *
|
||||
from .inventory import *
|
||||
from .stock_movement import *
|
||||
from .stock_movement import *
|
||||
from .invalid_product import *
|
||||
8
core/apps/wherehouse/admin/invalid_product.py
Normal file
8
core/apps/wherehouse/admin/invalid_product.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from core.apps.wherehouse.models import InvalidProduct
|
||||
|
||||
|
||||
@admin.register(InvalidProduct)
|
||||
class InvalidProductAdmin(admin.ModelAdmin):
|
||||
list_display = ['inventory', 'project_folder', 'amount', 'status']
|
||||
@@ -5,5 +5,5 @@ from core.apps.wherehouse.models.inventory import Inventory
|
||||
|
||||
@admin.register(Inventory)
|
||||
class InventoryAdmin(admin.ModelAdmin):
|
||||
list_display = ['quantity', 'wherehouse', 'product']
|
||||
|
||||
list_display = ['id', 'wherehouse', 'is_invalid']
|
||||
list_filter = ['wherehouse', 'is_invalid']
|
||||
40
core/apps/wherehouse/migrations/0005_invalidproduct.py
Normal file
40
core/apps/wherehouse/migrations/0005_invalidproduct.py
Normal file
@@ -0,0 +1,40 @@
|
||||
# Generated by Django 5.2.4 on 2025-08-26 17:20
|
||||
|
||||
import django.db.models.deletion
|
||||
import uuid
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('projects', '0016_estimatework_employee_estimatework_end_date_and_more'),
|
||||
('wherehouse', '0004_inventory_project_inventory_project_folder'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='InvalidProduct',
|
||||
fields=[
|
||||
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
('amount', models.PositiveIntegerField()),
|
||||
('status', models.CharField(choices=[('BROKEN', 'singan'), ('LOST', 'yoqolgan'), ('OTHER', 'boshqa')], default='other', max_length=20)),
|
||||
('created_date', models.DateField(blank=True, null=True)),
|
||||
('expiry_date', models.DateField(blank=True, null=True)),
|
||||
('comment', models.DateField(blank=True, null=True)),
|
||||
('file', models.FileField(blank=True, null=True, upload_to='invalid_product/files/')),
|
||||
('inventory', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='invalid_products', to='wherehouse.inventory')),
|
||||
('project_folder', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='invalid_products', to='projects.projectfolder')),
|
||||
('witnesses', models.ManyToManyField(related_name='invalid_products', to=settings.AUTH_USER_MODEL)),
|
||||
('work', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='invalid_products', to='projects.estimatework')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'yaroqsiz maxsulot',
|
||||
'verbose_name_plural': 'yaroqsiz maxsulotlar',
|
||||
},
|
||||
),
|
||||
]
|
||||
18
core/apps/wherehouse/migrations/0006_inventory_is_invalid.py
Normal file
18
core/apps/wherehouse/migrations/0006_inventory_is_invalid.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.2.4 on 2025-08-27 09:37
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('wherehouse', '0005_invalidproduct'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='inventory',
|
||||
name='is_invalid',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
||||
@@ -1,3 +1,4 @@
|
||||
from .inventory import *
|
||||
from .wherehouse import *
|
||||
from .stock_movemend import *
|
||||
from .stock_movemend import *
|
||||
from .invalid_product import *
|
||||
43
core/apps/wherehouse/models/invalid_product.py
Normal file
43
core/apps/wherehouse/models/invalid_product.py
Normal file
@@ -0,0 +1,43 @@
|
||||
from django.db import models
|
||||
|
||||
# shared
|
||||
from core.apps.shared.models import BaseModel
|
||||
# wherehouse
|
||||
from core.apps.wherehouse.models import WhereHouse, Inventory
|
||||
# projects
|
||||
from core.apps.projects.models import ProjectFolder, EstimateWork
|
||||
# accounts
|
||||
from core.apps.accounts.models import User
|
||||
|
||||
|
||||
class InvalidProduct(BaseModel):
|
||||
STATUS = (
|
||||
('BROKEN', 'singan'),
|
||||
('LOST', 'yoqolgan'),
|
||||
('OTHER', 'boshqa'),
|
||||
)
|
||||
|
||||
# relationship
|
||||
inventory = models.ForeignKey(Inventory, on_delete=models.CASCADE, related_name='invalid_products')
|
||||
project_folder = models.ForeignKey(
|
||||
ProjectFolder, on_delete=models.CASCADE, related_name='invalid_products'
|
||||
)
|
||||
witnesses = models.ManyToManyField(User, related_name='invalid_products')
|
||||
work = models.ForeignKey(
|
||||
EstimateWork, on_delete=models.SET_NULL, null=True, blank=True, related_name='invalid_products'
|
||||
)
|
||||
# required
|
||||
amount = models.PositiveIntegerField()
|
||||
status = models.CharField(max_length=20, choices=STATUS, default='other')
|
||||
# optional
|
||||
created_date = models.DateField(null=True, blank=True)
|
||||
expiry_date = models.DateField(null=True, blank=True)
|
||||
comment = models.DateField(null=True, blank=True)
|
||||
file = models.FileField(null=True, blank=True, upload_to='invalid_product/files/')
|
||||
|
||||
def __str__(self):
|
||||
return f'{self.amount} ta maxsulot yaroqsiz'
|
||||
|
||||
class Meta:
|
||||
verbose_name = 'yaroqsiz maxsulot'
|
||||
verbose_name_plural = 'yaroqsiz maxsulotlar'
|
||||
@@ -26,6 +26,7 @@ class Inventory(BaseModel):
|
||||
Project, on_delete=models.SET_NULL, null=True, blank=True,
|
||||
related_name='inventories'
|
||||
)
|
||||
is_invalid = models.BooleanField(default=False)
|
||||
|
||||
def __str__(self):
|
||||
return f'{self.product} in {self.wherehouse}'
|
||||
|
||||
76
core/apps/wherehouse/serializers/invalid_product.py
Normal file
76
core/apps/wherehouse/serializers/invalid_product.py
Normal file
@@ -0,0 +1,76 @@
|
||||
from django.db import transaction
|
||||
|
||||
from rest_framework import serializers
|
||||
|
||||
from core.apps.wherehouse.models import InvalidProduct, Inventory
|
||||
from core.apps.projects.models import ProjectFolder, EstimateWork
|
||||
from core.apps.wherehouse.serializers.inventory import InventoryListSerializer
|
||||
from core.apps.accounts.serializers.user import UserListSerializer
|
||||
|
||||
|
||||
class InvalidProductCreateSerializer(serializers.Serializer):
|
||||
inventory_id = serializers.UUIDField()
|
||||
project_folder_id = serializers.UUIDField()
|
||||
witnesses_ids = serializers.ListField(child=serializers.UUIDField())
|
||||
work_id = serializers.UUIDField(required=False)
|
||||
amount = serializers.IntegerField()
|
||||
status = serializers.ChoiceField(choices=InvalidProduct.STATUS)
|
||||
created_date = serializers.DateField(required=False)
|
||||
expiry_date = serializers.DateField(required=False)
|
||||
comment = serializers.CharField(required=False)
|
||||
file = serializers.FileField(required=False)
|
||||
|
||||
def validate(self, attrs):
|
||||
inventory = Inventory.objects.filter(id=attrs['inventory_id']).first()
|
||||
if not inventory:
|
||||
raise serializers.ValidationError("Inventory not found")
|
||||
project_folder = ProjectFolder.objects.filter(id=attrs['project_folder_id']).first()
|
||||
if not project_folder:
|
||||
raise serializers.ValidationError("Project Folder not found")
|
||||
if attrs.get('work_id'):
|
||||
work = EstimateWork.objects.filter(id=attrs['work_id']).first()
|
||||
if not work:
|
||||
raise serializers.ValidationError("Work not found")
|
||||
attrs['work'] = work
|
||||
attrs['inventory'] = inventory
|
||||
attrs['project_folder'] = project_folder
|
||||
return super().validate(attrs)
|
||||
|
||||
def create(self, validated_data):
|
||||
with transaction.atomic():
|
||||
witnesses_ids = validated_data.pop('witnesses_ids')
|
||||
invalid_product = InvalidProduct.objects.create(
|
||||
inventory=validated_data.get('inventory'),
|
||||
project_folder=validated_data.get('project_folder'),
|
||||
work=validated_data.get('work'),
|
||||
amount=validated_data.get('amount'),
|
||||
status=validated_data.get('status'),
|
||||
created_date=validated_data.get('created_date'),
|
||||
expiry_date=validated_data.get('expiry_date'),
|
||||
comment=validated_data.get('comment'),
|
||||
file=validated_data.get('file'),
|
||||
)
|
||||
invalid_product.witnesses.set(witnesses_ids)
|
||||
invalid_product.inventory.is_invalid = True
|
||||
invalid_product.inventory.save()
|
||||
invalid_product.save()
|
||||
return invalid_product
|
||||
|
||||
|
||||
class InvliadProductListSerializer(serializers.ModelSerializer):
|
||||
inventory = InventoryListSerializer()
|
||||
project_folder = serializers.SerializerMethodField(method_name='get_folder')
|
||||
witnesses = UserListSerializer(many=True)
|
||||
|
||||
class Meta:
|
||||
model = InvalidProduct
|
||||
fields = [
|
||||
'id', 'status', 'inventory', 'project_folder', 'witnesses', 'work', 'amount',
|
||||
'created_date', 'expiry_date', 'comment', 'file'
|
||||
]
|
||||
|
||||
def get_folder(self, obj):
|
||||
return {
|
||||
'id': obj.project_folder.id,
|
||||
'name': obj.project_folder.name,
|
||||
}
|
||||
@@ -2,6 +2,7 @@ from django.urls import path, include
|
||||
|
||||
from core.apps.wherehouse.views import wherehouse as wherehouse_views
|
||||
from core.apps.wherehouse.views import inventory as inventory_views
|
||||
from core.apps.wherehouse.views import invalid_product as invalid_product_views
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
@@ -19,4 +20,10 @@ urlpatterns = [
|
||||
path('list/', inventory_views.InventoryListApiView.as_view()),
|
||||
]
|
||||
)),
|
||||
]
|
||||
path('invalid_product/', include(
|
||||
[
|
||||
path('create/', invalid_product_views.InvalidProductCreateApiView.as_view()),
|
||||
path('list/', invalid_product_views.InvalidProductListApiView.as_view()),
|
||||
]
|
||||
)),
|
||||
]
|
||||
|
||||
45
core/apps/wherehouse/views/invalid_product.py
Normal file
45
core/apps/wherehouse/views/invalid_product.py
Normal file
@@ -0,0 +1,45 @@
|
||||
from rest_framework import generics, parsers
|
||||
from rest_framework.response import Response
|
||||
|
||||
from core.apps.accounts.permissions.permissions import HasRolePermission
|
||||
from core.apps.wherehouse.serializers import invalid_product as serializers
|
||||
from core.apps.wherehouse.models import InvalidProduct
|
||||
|
||||
|
||||
class InvalidProductCreateApiView(generics.GenericAPIView):
|
||||
serializer_class = serializers.InvalidProductCreateSerializer
|
||||
queryset = InvalidProduct.objects.all()
|
||||
permission_classes = [HasRolePermission]
|
||||
required_permissions = []
|
||||
parser_classes = [parsers.FormParser, parsers.MultiPartParser]
|
||||
|
||||
def post(self, request):
|
||||
serializer = self.serializer_class(data=request.data)
|
||||
if serializer.is_valid(raise_exception=True):
|
||||
serializer.save()
|
||||
return Response(
|
||||
{'success': True, 'message': 'invalid product created'},
|
||||
status=201
|
||||
)
|
||||
return Response(
|
||||
{'success': True, 'error_message': serializer.errors},
|
||||
status=400
|
||||
)
|
||||
|
||||
|
||||
class InvalidProductListApiView(generics.GenericAPIView):
|
||||
serializer_class = serializers.InvliadProductListSerializer
|
||||
queryset = InvalidProduct.objects.select_related(
|
||||
'inventory', 'project_folder', 'work'
|
||||
).prefetch_related('witnesses')
|
||||
permission_classes = [HasRolePermission]
|
||||
required_permissions = []
|
||||
|
||||
def get(self, request):
|
||||
invalid_products = self.queryset
|
||||
page = self.paginate_queryset(invalid_products)
|
||||
if page is not None:
|
||||
serializer = self.serializer_class(page, many=True)
|
||||
return self.get_paginated_response(serializer.data)
|
||||
serializer = self.serializer_class(invalid_products, many=True)
|
||||
return Response(serializer.data, status=200)
|
||||
@@ -12,7 +12,7 @@ from core.apps.accounts.permissions.permissions import HasRolePermission
|
||||
|
||||
class InventoryListApiView(generics.GenericAPIView):
|
||||
serializer_class = serializers.InventoryListSerializer
|
||||
queryset = Inventory.objects.select_related('product', 'unity')
|
||||
queryset = Inventory.objects.select_related('product', 'unity').exclude(is_invalid=True)
|
||||
permissions_classes = [HasRolePermission]
|
||||
required_permissions = ['wherehouse']
|
||||
filter_backends = [DjangoFilterBackend, filters.SearchFilter]
|
||||
|
||||
Reference in New Issue
Block a user