quota, distribution, inventory entry, quota sale transaction, product informations, signals ,....

This commit is contained in:
2025-07-02 15:42:51 +03:30
parent 2f23c5104d
commit 279afba977
45 changed files with 1408 additions and 88 deletions

View File

@@ -4,3 +4,6 @@ from django.apps import AppConfig
class WarehouseConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'apps.warehouse'
def ready(self):
import apps.warehouse.signals

View File

@@ -0,0 +1,14 @@
from rest_framework.exceptions import APIException
from rest_framework import status
class InventoryEntryWeightException(APIException):
status_code = status.HTTP_400_BAD_REQUEST
default_detail = "مقدار وارد شده برای ورودی به انبار از مقدار کل سهمیه توزیع داده شده بیشتر است" # noqa
default_code = 'error'
class TotalInventorySaleException(APIException):
status_code = status.HTTP_400_BAD_REQUEST
default_detail = "مقدار وارد شده برای فروش از انبار از موجودی انبار بیشتر میباشد" # noqa
default_code = 'error'

View File

@@ -0,0 +1,59 @@
# Generated by Django 5.0 on 2025-06-28 10:53
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('authorization', '0019_page_is_active_permissions_is_active'),
('product', '0029_remove_quota_assigned_organizations_and_more'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='WareHouse',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('create_date', models.DateTimeField(auto_now_add=True)),
('modify_date', models.DateTimeField(auto_now=True)),
('creator_info', models.CharField(max_length=100, null=True)),
('modifier_info', models.CharField(max_length=100, null=True)),
('trash', models.BooleanField(default=False)),
('name', models.CharField(max_length=250, null=True)),
('address', models.TextField(blank=True, null=True)),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createddby', to=settings.AUTH_USER_MODEL)),
('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL)),
('user_relation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='warehouse', to='authorization.userrelations')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='InventoryEntry',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('create_date', models.DateTimeField(auto_now_add=True)),
('modify_date', models.DateTimeField(auto_now=True)),
('creator_info', models.CharField(max_length=100, null=True)),
('modifier_info', models.CharField(max_length=100, null=True)),
('trash', models.BooleanField(default=False)),
('document', models.CharField(max_length=250, null=True)),
('is_confirmed', models.BooleanField(default=False)),
('notes', models.TextField(blank=True, null=True)),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createddby', to=settings.AUTH_USER_MODEL)),
('distribution', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='inventory_entry', to='product.quotadistribution')),
('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL)),
('warehouse', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='inventory', to='warehouse.warehouse')),
],
options={
'abstract': False,
},
),
]

View File

@@ -0,0 +1,25 @@
# Generated by Django 5.0 on 2025-06-28 12:03
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('warehouse', '0001_initial'),
]
operations = [
migrations.RemoveField(
model_name='warehouse',
name='address',
),
migrations.RemoveField(
model_name='warehouse',
name='name',
),
migrations.RemoveField(
model_name='warehouse',
name='user_relation',
),
]

View File

@@ -0,0 +1,24 @@
# Generated by Django 5.0 on 2025-06-28 12:05
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('authentication', '0023_alter_organization_company_code_and_more'),
('warehouse', '0002_remove_warehouse_address_remove_warehouse_name_and_more'),
]
operations = [
migrations.RemoveField(
model_name='inventoryentry',
name='distribution',
),
migrations.AddField(
model_name='warehouse',
name='organization',
field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='warehouse', to='authentication.organization'),
),
]

View File

@@ -0,0 +1,20 @@
# Generated by Django 5.0 on 2025-06-28 12:06
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('product', '0031_broker_organization_quota_assigned_organizations_and_more'),
('warehouse', '0003_remove_inventoryentry_distribution_and_more'),
]
operations = [
migrations.AddField(
model_name='inventoryentry',
name='distribution',
field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='inventory_entry', to='product.quotadistribution'),
),
]

View File

@@ -0,0 +1,48 @@
# Generated by Django 5.0 on 2025-06-29 05:44
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('authentication', '0023_alter_organization_company_code_and_more'),
('product', '0031_broker_organization_quota_assigned_organizations_and_more'),
('warehouse', '0004_inventoryentry_distribution'),
]
operations = [
migrations.RemoveField(
model_name='inventoryentry',
name='warehouse',
),
migrations.AddField(
model_name='inventoryentry',
name='delivery_address',
field=models.TextField(blank=True, null=True),
),
migrations.AddField(
model_name='inventoryentry',
name='lading_number',
field=models.CharField(max_length=50, null=True),
),
migrations.AddField(
model_name='inventoryentry',
name='organization',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='inventory', to='authentication.organization'),
),
migrations.AddField(
model_name='inventoryentry',
name='weight',
field=models.PositiveBigIntegerField(default=0),
),
migrations.AlterField(
model_name='inventoryentry',
name='distribution',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='inventory_entry', to='product.quotadistribution'),
),
migrations.DeleteModel(
name='WareHouse',
),
]

View File

@@ -0,0 +1,68 @@
# Generated by Django 5.0 on 2025-06-29 11:18
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('authentication', '0023_alter_organization_company_code_and_more'),
('authorization', '0019_page_is_active_permissions_is_active'),
('product', '0032_quota_closed_at_quota_is_closed'),
('warehouse', '0005_remove_inventoryentry_warehouse_and_more'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='InventoryQuotaSale',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('create_date', models.DateTimeField(auto_now_add=True)),
('modify_date', models.DateTimeField(auto_now=True)),
('creator_info', models.CharField(max_length=100, null=True)),
('modifier_info', models.CharField(max_length=100, null=True)),
('trash', models.BooleanField(default=False)),
('weight', models.PositiveBigIntegerField(default=0)),
('herd_owners_number', models.PositiveBigIntegerField(default=0)),
('transactions_number', models.PositiveBigIntegerField(default=0)),
('sale_status', models.BooleanField(default=False)),
('is_active', models.BooleanField(default=0)),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createddby', to=settings.AUTH_USER_MODEL)),
('inventory_entry', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='inventory_sales', to='warehouse.inventoryentry')),
('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL)),
('quota_distribution', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='inventory_sales', to='product.quotadistribution')),
('seller_organization', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='inventory_sales', to='authentication.organization')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='InventorySaleTransaction',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('create_date', models.DateTimeField(auto_now_add=True)),
('modify_date', models.DateTimeField(auto_now=True)),
('creator_info', models.CharField(max_length=100, null=True)),
('modifier_info', models.CharField(max_length=100, null=True)),
('trash', models.BooleanField(default=False)),
('transaction_id', models.CharField(max_length=50, null=True)),
('weight', models.DecimalField(decimal_places=2, max_digits=12)),
('delivery_address', models.TextField(blank=True, null=True)),
('total_price', models.PositiveBigIntegerField(default=0)),
('description', models.TextField(blank=True, null=True)),
('buyer_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='buyer_sale_transactions', to='authorization.userrelations')),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createddby', to=settings.AUTH_USER_MODEL)),
('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL)),
('product', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='sale_transactions', to='product.product')),
('quota_sale', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='sale_transactions', to='warehouse.inventoryquotasale')),
('seller_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='seller_sale_transactions', to='authorization.userrelations')),
],
options={
'abstract': False,
},
),
]

View File

@@ -0,0 +1,21 @@
# Generated by Django 5.0 on 2025-06-29 12:19
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('warehouse', '0006_inventoryquotasale_inventorysaletransaction'),
]
operations = [
migrations.RemoveField(
model_name='inventorysaletransaction',
name='buyer_user',
),
migrations.RemoveField(
model_name='inventorysaletransaction',
name='seller_user',
),
]

View File

@@ -0,0 +1,26 @@
# Generated by Django 5.0 on 2025-06-29 12:22
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('warehouse', '0007_remove_inventorysaletransaction_buyer_user_and_more'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AddField(
model_name='inventorysaletransaction',
name='buyer_user',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='buyer_sale_transactions', to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='inventorysaletransaction',
name='seller_user',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='seller_sale_transactions', to=settings.AUTH_USER_MODEL),
),
]

View File

@@ -0,0 +1,20 @@
# Generated by Django 5.0 on 2025-06-29 12:46
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('authentication', '0023_alter_organization_company_code_and_more'),
('warehouse', '0008_inventorysaletransaction_buyer_user_and_more'),
]
operations = [
migrations.AddField(
model_name='inventorysaletransaction',
name='buyer_organization',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='sale_transactions', to='authentication.organization'),
),
]

View File

@@ -0,0 +1,47 @@
# Generated by Django 5.0 on 2025-07-01 07:05
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('warehouse', '0009_inventorysaletransaction_buyer_organization'),
]
operations = [
migrations.RemoveField(
model_name='inventorysaletransaction',
name='quota_sale',
),
migrations.RemoveField(
model_name='inventorysaletransaction',
name='buyer_organization',
),
migrations.RemoveField(
model_name='inventorysaletransaction',
name='buyer_user',
),
migrations.RemoveField(
model_name='inventorysaletransaction',
name='created_by',
),
migrations.RemoveField(
model_name='inventorysaletransaction',
name='modified_by',
),
migrations.RemoveField(
model_name='inventorysaletransaction',
name='product',
),
migrations.RemoveField(
model_name='inventorysaletransaction',
name='seller_user',
),
migrations.DeleteModel(
name='InventoryQuotaSale',
),
migrations.DeleteModel(
name='InventorySaleTransaction',
),
]

View File

@@ -0,0 +1,49 @@
# Generated by Django 5.0 on 2025-07-01 07:57
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('authentication', '0023_alter_organization_company_code_and_more'),
('product', '0035_remove_quota_quota_balance'),
('warehouse', '0010_remove_inventorysaletransaction_quota_sale_and_more'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='InventoryQuotaSaleTransaction',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('create_date', models.DateTimeField(auto_now_add=True)),
('modify_date', models.DateTimeField(auto_now=True)),
('creator_info', models.CharField(max_length=100, null=True)),
('modifier_info', models.CharField(max_length=100, null=True)),
('trash', models.BooleanField(default=False)),
('transaction_id', models.CharField(max_length=50, null=True)),
('weight', models.DecimalField(decimal_places=2, max_digits=12, null=True)),
('delivery_address', models.TextField(blank=True, null=True)),
('transaction_price', models.PositiveBigIntegerField(default=0)),
('description', models.TextField(blank=True, null=True)),
('herd_owners_number', models.PositiveBigIntegerField(default=0)),
('transactions_number', models.PositiveBigIntegerField(default=0)),
('sale_status', models.BooleanField(default=False)),
('is_active', models.BooleanField(default=0)),
('buyer_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='buyer_sale_transactions', to=settings.AUTH_USER_MODEL)),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createddby', to=settings.AUTH_USER_MODEL)),
('inventory_entry', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='inventory_sales', to='warehouse.inventoryentry')),
('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL)),
('product', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='sale_transactions', to='product.product')),
('quota_distribution', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='inventory_sales', to='product.quotadistribution')),
('seller_organization', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='inventory_sales', to='authentication.organization')),
('seller_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='seller_sale_transactions', to=settings.AUTH_USER_MODEL)),
],
options={
'abstract': False,
},
),
]

View File

@@ -0,0 +1,103 @@
from apps.product import models as product_models
from apps.authentication.models import User
from apps.core.models import BaseModel
from django.db import models
class InventoryEntry(BaseModel):
distribution = models.ForeignKey(
product_models.QuotaDistribution,
on_delete=models.CASCADE,
related_name='inventory_entry',
null=True
)
organization = models.ForeignKey(
product_models.Organization,
on_delete=models.CASCADE,
related_name="inventory",
null=True
)
weight = models.PositiveBigIntegerField(default=0)
balance = models
lading_number = models.CharField(max_length=50, null=True)
delivery_address = models.TextField(blank=True, null=True)
document = models.CharField(max_length=250, null=True)
is_confirmed = models.BooleanField(default=False)
notes = models.TextField(blank=True, null=True)
@property
def total_sold(self):
return self.inventory_sales.aggregate(total=models.Sum('weight'))['total'] or 0
@property
def remaining_weight(self):
return self.weight - self.total_sold
def __str__(self):
return f"distribution: {self.distribution.distribution_id}-{self.organization.name}"
def save(self, *args, **kwargs):
super(InventoryEntry, self).save(*args, **kwargs)
class InventoryQuotaSaleTransaction(BaseModel):
transaction_id = models.CharField(max_length=50, null=True)
seller_organization = models.ForeignKey(
product_models.Organization,
on_delete=models.CASCADE,
related_name='inventory_sales',
null=True
)
quota_distribution = models.ForeignKey(
product_models.QuotaDistribution,
on_delete=models.CASCADE,
related_name='inventory_sales',
null=True
)
inventory_entry = models.ForeignKey(
InventoryEntry,
on_delete=models.CASCADE,
related_name='inventory_sales',
null=True
)
buyer_user = models.ForeignKey(
User,
on_delete=models.CASCADE,
related_name='buyer_sale_transactions',
null=True
)
seller_user = models.ForeignKey(
User,
on_delete=models.CASCADE,
related_name='seller_sale_transactions',
null=True
)
weight = models.DecimalField(max_digits=12, decimal_places=2, null=True)
delivery_address = models.TextField(blank=True, null=True)
product = models.ForeignKey(
product_models.Product,
on_delete=models.CASCADE,
related_name='sale_transactions',
null=True
)
transaction_price = models.PositiveBigIntegerField(default=0)
description = models.TextField(blank=True, null=True)
herd_owners_number = models.PositiveBigIntegerField(default=0)
transactions_number = models.PositiveBigIntegerField(default=0)
sale_status = models.BooleanField(default=False)
is_active = models.BooleanField(default=0)
def buyers_count(self):
""" number of buyers from specific inventory """
unique_buyers_count = self.objects.filter(
inventory_entry=self.inventory_entry
).values('buyer_user').distinct().count()
return unique_buyers_count
def __str__(self):
return f"Inventory Sale: {self.transaction_id}-{self.quota_distribution.distribution_id}"
def save(self, *args, **kwargs):
super(InventoryQuotaSaleTransaction, self).save(*args, **kwargs)

36
apps/warehouse/signals.py Normal file
View File

@@ -0,0 +1,36 @@
from django.db.models import Sum
from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver
from apps.product.models import QuotaDistribution
from .models import InventoryEntry, InventoryQuotaSaleTransaction
def calculate_warehouse_entry(quota_distribution):
total_entry = quota_distribution.inventory_entry.aggregate(
total=Sum('weight')
)['total'] or 0
quota_distribution.warehouse_entry = total_entry
quota_distribution.save(update_fields=['warehouse_entry'])
def warehouse_sold_and_balance(quota_distribution):
total_sold = quota_distribution.inventory_sales.aggregate(
total=Sum('weight')
)['total'] or 0
quota_distribution.been_sold = total_sold
quota_distribution.warehouse_balance = quota_distribution.warehouse_entry - total_sold
quota_distribution.save(update_fields=['been_sold', 'warehouse_balance'])
@receiver(post_save, sender=InventoryEntry)
@receiver(post_delete, sender=InventoryEntry)
def update_distribution_warehouse_entry(sender, instance, **kwargs):
calculate_warehouse_entry(instance.distribution)
@receiver(post_save, sender=InventoryQuotaSaleTransaction)
@receiver(post_delete, sender=InventoryQuotaSaleTransaction)
def update_distribution_warehouse_sold_and_balance(sender, instance, **kwargs):
warehouse_sold_and_balance(instance.quota_distribution)

View File

@@ -1 +1,7 @@
# Your urls go here
from django.urls import path, include
urlpatterns = [
path('web/api/', include('apps.warehouse.web.api.v1.urls'))
]

View File

@@ -0,0 +1,17 @@
from apps.warehouse.web.api.v1 import serializers as warehouse_serializers
from apps.warehouse import models as warehouse_models
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework import viewsets
from django.db import transaction
from rest_framework import status
class InventoryEntryViewSet(viewsets.ModelViewSet):
queryset = warehouse_models.InventoryEntry.objects.all()
serializer_class = warehouse_serializers.InventoryEntrySerializer
class InventoryQuotaSaleTransactionViewSet(viewsets.ModelViewSet):
queryset = warehouse_models.InventoryQuotaSaleTransaction.objects.all()
serializer_class = warehouse_serializers.InventoryQuotaSaleTransactionSerializer

View File

@@ -0,0 +1,90 @@
from apps.warehouse.exceptions import (
InventoryEntryWeightException,
TotalInventorySaleException
)
from apps.product.exceptions import QuotaExpiredTimeException
from apps.warehouse import models as warehouse_models
from apps.authorization.models import UserRelations
from rest_framework import serializers
from django.db import models
class InventoryEntrySerializer(serializers.ModelSerializer):
class Meta:
model = warehouse_models.InventoryEntry
fields = '__all__'
def create(self, validated_data):
""" Custom create & set organization """
distribution = validated_data['distribution']
organization = distribution.assigned_organization
return warehouse_models.InventoryEntry.objects.create(
organization=organization,
**validated_data
)
def validate(self, attrs):
"""
check if inventory entries weight is not more than
distribution weight & check quota expired time
"""
distribution = attrs['distribution']
# check for quota expired time
if not distribution.quota.is_in_valid_time():
raise QuotaExpiredTimeException()
# total inventory entries weight
total_entered = distribution.inventory_entry.filter(is_confirmed=True).aggregate(
total=models.Sum('weight')
)['total'] or 0
if total_entered + attrs['weight'] > distribution.weight:
raise InventoryEntryWeightException()
return attrs
class InventoryQuotaSaleTransactionSerializer(serializers.ModelSerializer):
class Meta:
model = warehouse_models.InventoryQuotaSaleTransaction
fields = '__all__'
depth = 0
def validate(self, attrs):
"""
validate total inventory sale should be fewer than
inventory entry from distribution
"""
inventory_entry = attrs['inventory_entry']
distribution = attrs['quota_distribution']
total_sale_weight = inventory_entry.inventory_sales.aggregate(
total=models.Sum('weight')
)['total'] or 0
if total_sale_weight + attrs['weight'] > distribution.warehouse_balance:
raise TotalInventorySaleException()
return attrs
def create(self, validated_data):
""" Custom create & set some parameters like seller & buyer """
distribution = validated_data['quota_distribution']
seller_organization = distribution.assigned_organization
user = self.context['request'].user
buyer_user = user
seller_user = validated_data['inventory_entry'].created_by
return warehouse_models.InventoryQuotaSaleTransaction.objects.create(
seller_organization=seller_organization,
seller_user=seller_user,
buyer_user=buyer_user,
**validated_data
)

View File

@@ -0,0 +1,16 @@
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from apps.warehouse.web.api.v1 import api
router = DefaultRouter()
router.register(r'inventory_entry', api.InventoryEntryViewSet, basename='inventory_entry')
router.register(
r'inventory_sale_transaction',
api.InventoryQuotaSaleTransactionViewSet,
basename='inventory_sale_transaction'
)
urlpatterns = [
path('v1/', include(router.urls)),
]