store room owner

This commit is contained in:
javad committed 2026-07-05 14:32:01 +03:30
1 parent c5e9b1d18a
commit 489fda27d3
6 files changed
+140 -12

No files matched your search

+27 -2
View File
@@ -6,7 +6,6 @@ from .models import *
admin.site.register(Customer) admin.site.register(Customer)
admin.site.register(PhoneNumber) admin.site.register(PhoneNumber)
admin.site.register(StoreRoom)
admin.site.register(ProductCategory) admin.site.register(ProductCategory)
admin.site.register(Product) admin.site.register(Product)
admin.site.register(ProductInStore) admin.site.register(ProductInStore)
@@ -49,4 +48,30 @@ class StoreTransferRequestAdmin(admin.ModelAdmin):
search_fields = ['id', 'desc', 'product__product_name', 'requester__username'] search_fields = ['id', 'desc', 'product__product_name', 'requester__username']
# ۶. غیرقابل ادیت کردن فیلدهای حساس مالی در پنل ادمین (اختیاری اما برای Enterprise الزامی است) # ۶. غیرقابل ادیت کردن فیلدهای حساس مالی در پنل ادمین (اختیاری اما برای Enterprise الزامی است)
readonly_fields = ['created_at', 'updated_at'] readonly_fields = ['created_at', 'updated_at']
class StoreRoomOwnerInline(admin.TabularInline):
"""
این کلاس باعث می‌شود مالکین انبار به صورت یک جدول کوچک
در داخل همان صفحه مدیریت انبار نمایش داده شوند.
"""
model = StoreRoomOwner
extra = 1 # تعداد ردیف‌های خالی برای افزودن مالک جدید
verbose_name = "مالک انبار"
verbose_name_plural = "مالکین انبار"
@admin.register(StoreRoom)
class StoreRoomAdmin(admin.ModelAdmin):
list_display = ('store_name', 'get_owners_count')
search_fields = ('store_name',)
# فیلد user یک M2M معمولی است، پس filter_horizontal روی آن عالی کار می‌کند
filter_horizontal = ('user',)
# فیلد owners چون جدول واسط دارد، باید از طریق Inline مدیریت شود
inlines = [StoreRoomOwnerInline]
def get_owners_count(self, obj):
"""نمایش تعداد مالکین در لیست اصلی انبارها"""
return obj.owners.count()
get_owners_count.short_description = "تعداد مالکین"
@@ -0,0 +1,24 @@
# Generated by Django 3.2.25 on 2026-07-05 10:26
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('admin_panel', '0050_storetransferrequest'),
]
operations = [
migrations.AlterModelOptions(
name='storetransferrequest',
options={'ordering': ['-created_at']},
),
migrations.AlterField(
model_name='storeroom',
name='user',
field=models.ManyToManyField(blank=True, help_text='از این برای دسترسی مشاهده آن اتبار استفاده می شود', to=settings.AUTH_USER_MODEL, verbose_name='دسترسی مشاهده انبار احتمالا'),
),
]
@@ -0,0 +1,24 @@
# Generated by Django 3.2.25 on 2026-07-05 10:28
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('admin_panel', '0051_auto_20260705_1356'),
]
operations = [
migrations.CreateModel(
name='StoreRoomOwner',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('storeroom', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='admin_panel.storeroom')),
('user', models.OneToOneField(error_messages={'unique': 'این کاربر در حال حاضر مالک انبار دیگری است.'}, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]
@@ -0,0 +1,20 @@
# Generated by Django 3.2.25 on 2026-07-05 10:29
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('admin_panel', '0052_storeroomowner'),
]
operations = [
migrations.AddField(
model_name='storeroom',
name='owners',
field=models.ManyToManyField(blank=True, help_text='این کاربران اگر فاکتور بزنند از این انبار کسر می\u200cگردد', related_name='owned_storerooms', through='admin_panel.StoreRoomOwner', to=settings.AUTH_USER_MODEL, verbose_name='مالکین انبار در فاکتور و موجودی و...'),
),
]
+29 -1
View File
@@ -71,12 +71,40 @@ class StoreRoom(models.Model):
("DelStore", "DelStore") ("DelStore", "DelStore")
] ]
store_name = models.CharField(max_length=200) store_name = models.CharField(max_length=200)
user = models.ManyToManyField(User, blank=True) user = models.ManyToManyField(User,
blank=True,
verbose_name="دسترسی مشاهده انبار احتمالا",
help_text="از این برای دسترسی مشاهده آن اتبار استفاده می شود"
)
owners = models.ManyToManyField(
User,
through='StoreRoomOwner', # کلید حل مشکل بدون شکستن کدهای قبلی
blank=True,
related_name="owned_storerooms",
verbose_name="مالکین انبار در فاکتور و موجودی و...",
help_text="این کاربران اگر فاکتور بزنند از این انبار کسر می‌گردد"
)
def __str__(self): def __str__(self):
return self.store_name return self.store_name
class StoreRoomOwner(models.Model):
"""
این جدول واسط، پشت صحنه قرار می‌گیرد تا فقط قانون مالکیت را مدیریت کند.
کدهای دیگر پروژه اصلاً نیازی ندارند مستقیم با این جدول درگیر شوند.
"""
storeroom = models.ForeignKey(StoreRoom, on_delete=models.CASCADE)
# OneToOneField تضمین می‌کند یک کاربر نمی‌تواند در این جدول (و هیچ انبار دیگری) تکرار شود
user = models.OneToOneField(
User,
on_delete=models.CASCADE,
error_messages={
'unique': 'این کاربر در حال حاضر مالک انبار دیگری است.'
}
)
class ProductCategory(models.Model): class ProductCategory(models.Model):
category_name = models.CharField(max_length=50) category_name = models.CharField(max_length=50)
+16 -9
View File
@@ -739,21 +739,28 @@ def CustomerJson(request, c_id):
@login_required(login_url='/panel/p_login/') @login_required(login_url='/panel/p_login/')
def productMojodi(request, p_id): def productMojodi(request, p_id):
storeUser = StoreRoom.objects.get(user__id=request.user.id)
# ۱. تجمیع ورودی و خروجی در یک کوئری بهینه برای افزایش سرعت دیتابیس # پیدا کردن انبار کاربر با متد filter به جای get برای جلوگیری از کرش سیستم
store_user = StoreRoom.objects.filter(owners=request.user).first()
# اگر کاربر هنوز مالک هیچ انباری نشده است
if not store_user:
return HttpResponse("کاربر مالکیت انبار ندارد") # یا هر مقداری که فرانت‌اند انتظار دارد
# محاسبه موجودی برای انبار پیدا شده
totals = ProductInStore.objects.filter( totals = ProductInStore.objects.filter(
Product__id=p_id, StoreRoom=storeUser, year=1400 Product_id=p_id,
).aggregate(total_in=Sum('s_in'), total_out=Sum('s_out')) StoreRoom=store_user,
year=1400
).aggregate(
total_in=Sum('s_in'),
total_out=Sum('s_out')
)
# ۲. رفع باگ قطعی NoneType: اگر رکوردی نباشد، مقدار None را به 0 تبدیل می‌کند
total_in = totals['total_in'] or 0 total_in = totals['total_in'] or 0
total_out = totals['total_out'] or 0 total_out = totals['total_out'] or 0
# ۳. محاسبه دقیق موجودی بدون ریسک بروز خطای ریاضی پایتون
mojodi_prd = total_in - total_out mojodi_prd = total_in - total_out
# ۴. بازگشت خروجی دقیقاً متناسب با ساختار متنی فرانت‌اَند شما
return HttpResponse(str(mojodi_prd)) return HttpResponse(str(mojodi_prd))
@login_required(login_url='/panel/p_login/') @login_required(login_url='/panel/p_login/')
@@ -1455,7 +1462,7 @@ def newFactor2(request, c_id):
sar_resid_date = _parse_jalali_date(request.POST.get('sarResidDate')) sar_resid_date = _parse_jalali_date(request.POST.get('sarResidDate'))
customer_obj = get_object_or_404(Customer, id=c_id) customer_obj = get_object_or_404(Customer, id=c_id)
store_room = get_object_or_404(StoreRoom, user=user_op) store_room = get_object_or_404(StoreRoom, owners=user_op)
# رفع مشکل متنی/عددی در استخراج شناسه کالاها # رفع مشکل متنی/عددی در استخراج شناسه کالاها
product_ids = [] product_ids = []