store room owner
This commit is contained in:
1 parent
c5e9b1d18a
commit
489fda27d3
6 files changed
+140
-12
No files matched your search
+27
-2
@@ -6,7 +6,6 @@ from .models import *
|
||||
|
||||
admin.site.register(Customer)
|
||||
admin.site.register(PhoneNumber)
|
||||
admin.site.register(StoreRoom)
|
||||
admin.site.register(ProductCategory)
|
||||
admin.site.register(Product)
|
||||
admin.site.register(ProductInStore)
|
||||
@@ -49,4 +48,30 @@ class StoreTransferRequestAdmin(admin.ModelAdmin):
|
||||
search_fields = ['id', 'desc', 'product__product_name', 'requester__username']
|
||||
|
||||
# ۶. غیرقابل ادیت کردن فیلدهای حساس مالی در پنل ادمین (اختیاری اما برای 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
@@ -71,12 +71,40 @@ class StoreRoom(models.Model):
|
||||
("DelStore", "DelStore")
|
||||
]
|
||||
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):
|
||||
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):
|
||||
category_name = models.CharField(max_length=50)
|
||||
|
||||
|
||||
+16
-9
@@ -739,21 +739,28 @@ def CustomerJson(request, c_id):
|
||||
|
||||
@login_required(login_url='/panel/p_login/')
|
||||
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(
|
||||
Product__id=p_id, StoreRoom=storeUser, year=1400
|
||||
).aggregate(total_in=Sum('s_in'), total_out=Sum('s_out'))
|
||||
Product_id=p_id,
|
||||
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_out = totals['total_out'] or 0
|
||||
|
||||
# ۳. محاسبه دقیق موجودی بدون ریسک بروز خطای ریاضی پایتون
|
||||
mojodi_prd = total_in - total_out
|
||||
|
||||
# ۴. بازگشت خروجی دقیقاً متناسب با ساختار متنی فرانتاَند شما
|
||||
return HttpResponse(str(mojodi_prd))
|
||||
|
||||
@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'))
|
||||
|
||||
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 = []
|
||||
|
||||
Reference in new issue
Block a user