import random import string from django.shortcuts import render, get_object_or_404, redirect from django.db.models import Sum, Count, F from django.db.models.functions import TruncDate, TruncMonth from django.http import JsonResponse from django.views.decorators.csrf import csrf_exempt from .models import ( Product, Sale, Category, Unit, Customer, Supplier, Purchase, PurchaseItem, PurchasePayment, SaleItem, SalePayment, SystemSetting, Quotation, QuotationItem, SaleReturn, SaleReturnItem, PurchaseReturn, PurchaseReturnItem ) import json from datetime import timedelta from django.utils import timezone from django.contrib import messages from django.utils.text import slugify import openpyxl def index(request): """ Enhanced Meezan Dashboard View """ # Summary Stats total_products = Product.objects.count() total_sales_count = Sale.objects.count() total_sales_amount = Sale.objects.aggregate(total=Sum('total_amount'))['total'] or 0 total_customers = Customer.objects.count() # Stock Alert (Low stock < 5) low_stock_products = Product.objects.filter(stock_quantity__lt=5) # Recent Transactions recent_sales = Sale.objects.order_by('-created_at')[:5] # Chart Data: Sales for the last 7 days seven_days_ago = timezone.now().date() - timedelta(days=6) sales_over_time = Sale.objects.filter(created_at__date__gte=seven_days_ago) \ .annotate(date=TruncDate('created_at')) \ .values('date') \ .annotate(total=Sum('total_amount')) \ .order_by('date') # Prepare data for Chart.js chart_labels = [] chart_data = [] date_dict = {s['date']: float(s['total']) for s in sales_over_time} for i in range(7): date = seven_days_ago + timedelta(days=i) chart_labels.append(date.strftime('%b %d')) chart_data.append(date_dict.get(date, 0)) context = { 'total_products': total_products, 'total_sales_count': total_sales_count, 'total_sales_amount': total_sales_amount, 'total_customers': total_customers, 'low_stock_products': low_stock_products, 'recent_sales': recent_sales, 'chart_labels': json.dumps(chart_labels), 'chart_data': json.dumps(chart_data), } return render(request, 'core/index.html', context) def inventory(request): products = Product.objects.all().select_related('category', 'unit', 'supplier') categories = Category.objects.all() units = Unit.objects.all() suppliers = Supplier.objects.all() context = { 'products': products, 'categories': categories, 'units': units, 'suppliers': suppliers } return render(request, 'core/inventory.html', context) def pos(request): products = Product.objects.all().filter(stock_quantity__gt=0, is_active=True) customers = Customer.objects.all() categories = Category.objects.all() context = {'products': products, 'customers': customers, 'categories': categories} return render(request, 'core/pos.html', context) def customers(request): customers_list = Customer.objects.all().annotate(total_sales=Sum('sales__total_amount')) context = {'customers': customers_list} return render(request, 'core/customers.html', context) def suppliers(request): suppliers_list = Supplier.objects.all() context = {'suppliers': suppliers_list} return render(request, 'core/suppliers.html', context) # --- Purchase Views --- def purchases(request): purchases_list = Purchase.objects.all().select_related('supplier').order_by('-created_at') suppliers_list = Supplier.objects.all() context = {'purchases': purchases_list, 'suppliers': suppliers_list} return render(request, 'core/purchases.html', context) def purchase_create(request): products = Product.objects.filter(is_active=True) suppliers = Supplier.objects.all() return render(request, 'core/purchase_create.html', {'products': products, 'suppliers': suppliers}) def purchase_detail(request, pk): purchase = get_object_or_404(Purchase, pk=pk) settings = SystemSetting.objects.first() return render(request, 'core/purchase_detail.html', {'purchase': purchase, 'settings': settings}) @csrf_exempt def create_purchase_api(request): if request.method == 'POST': try: data = json.loads(request.body) supplier_id = data.get('supplier_id') invoice_number = data.get('invoice_number', '') items = data.get('items', []) total_amount = data.get('total_amount', 0) paid_amount = data.get('paid_amount', 0) payment_type = data.get('payment_type', 'cash') due_date = data.get('due_date') notes = data.get('notes', '') supplier = None if supplier_id: supplier = Supplier.objects.get(id=supplier_id) purchase = Purchase.objects.create( supplier=supplier, invoice_number=invoice_number, total_amount=total_amount, paid_amount=paid_amount, balance_due=float(total_amount) - float(paid_amount), payment_type=payment_type, due_date=due_date if due_date else None, notes=notes ) # Set status based on payments if float(paid_amount) >= float(total_amount): purchase.status = 'paid' elif float(paid_amount) > 0: purchase.status = 'partial' else: purchase.status = 'unpaid' purchase.save() # Record the initial payment if any if float(paid_amount) > 0: PurchasePayment.objects.create( purchase=purchase, amount=paid_amount, payment_method=payment_type.capitalize(), notes=_("Initial payment") ) for item in items: product = Product.objects.get(id=item['id']) PurchaseItem.objects.create( purchase=purchase, product=product, quantity=item['quantity'], cost_price=item['price'], line_total=item['line_total'] ) # Update Stock product.stock_quantity += int(item['quantity']) product.cost_price = item['price'] product.save() return JsonResponse({'success': True, 'purchase_id': purchase.id}) except Exception as e: return JsonResponse({'success': False, 'error': str(e)}, status=400) return JsonResponse({'success': False, 'error': 'Invalid request'}, status=405) def add_purchase_payment(request, pk): purchase = get_object_or_404(Purchase, pk=pk) if request.method == 'POST': amount = request.POST.get('amount') payment_date = request.POST.get('payment_date', timezone.now().date()) payment_method = request.POST.get('payment_method', 'Cash') notes = request.POST.get('notes', '') PurchasePayment.objects.create( purchase=purchase, amount=amount, payment_date=payment_date, payment_method=payment_method, notes=notes ) purchase.update_balance() messages.success(request, _("Payment added successfully!")) return redirect('purchases') def delete_purchase(request, pk): purchase = get_object_or_404(Purchase, pk=pk) for item in purchase.items.all(): item.product.stock_quantity -= item.quantity item.product.save() purchase.delete() messages.success(request, _("Purchase deleted successfully!")) return redirect('purchases') # --- Sale Views --- def invoice_list(request): sales = Sale.objects.all().order_by('-created_at') customers = Customer.objects.all() return render(request, 'core/invoices.html', {'sales': sales, 'customers': customers}) def invoice_create(request): products = Product.objects.filter(is_active=True) customers = Customer.objects.all() return render(request, 'core/invoice_create.html', {'products': products, 'customers': customers}) def invoice_detail(request, pk): sale = get_object_or_404(Sale, pk=pk) settings = SystemSetting.objects.first() return render(request, 'core/invoice_detail.html', {'sale': sale, 'settings': settings}) @csrf_exempt def create_sale_api(request): if request.method == 'POST': try: data = json.loads(request.body) customer_id = data.get('customer_id') invoice_number = data.get('invoice_number', '') items = data.get('items', []) total_amount = data.get('total_amount', 0) paid_amount = data.get('paid_amount', 0) discount = data.get('discount', 0) payment_type = data.get('payment_type', 'cash') due_date = data.get('due_date') notes = data.get('notes', '') customer = None if customer_id: customer = Customer.objects.get(id=customer_id) sale = Sale.objects.create( customer=customer, invoice_number=invoice_number, total_amount=total_amount, paid_amount=paid_amount, balance_due=float(total_amount) - float(paid_amount), discount=discount, payment_type=payment_type, due_date=due_date if due_date else None, notes=notes ) # Set status based on payments if float(paid_amount) >= float(total_amount): sale.status = 'paid' elif float(paid_amount) > 0: sale.status = 'partial' else: sale.status = 'unpaid' sale.save() # Record initial payment if any if float(paid_amount) > 0: SalePayment.objects.create( sale=sale, amount=paid_amount, payment_method=payment_type.capitalize(), notes=_("Initial payment") ) for item in items: product = Product.objects.get(id=item['id']) SaleItem.objects.create( sale=sale, product=product, quantity=item['quantity'], unit_price=item['price'], line_total=item['line_total'] ) product.stock_quantity -= int(item['quantity']) product.save() settings = SystemSetting.objects.first() if not settings: settings = SystemSetting.objects.create() return JsonResponse({ 'success': True, 'sale_id': sale.id, 'business': { 'name': settings.business_name, 'address': settings.address, 'phone': settings.phone, 'email': settings.email, 'currency': settings.currency_symbol, 'vat_number': settings.vat_number, 'registration_number': settings.registration_number, 'logo_url': settings.logo.url if settings.logo else None }, 'sale': { 'id': sale.id, 'invoice_number': sale.invoice_number, 'created_at': sale.created_at.strftime("%Y-%m-%d %H:%M"), 'total': float(sale.total_amount), 'paid': float(sale.paid_amount), 'balance': float(sale.balance_due), 'items': [ { 'name_en': si.product.name_en, 'name_ar': si.product.name_ar, 'qty': si.quantity, 'price': float(si.unit_price), 'total': float(si.line_total) } for si in sale.items.all() ] } }) except Exception as e: return JsonResponse({'success': False, 'error': str(e)}, status=400) return JsonResponse({'success': False, 'error': 'Invalid request'}, status=405) def add_sale_payment(request, pk): sale = get_object_or_404(Sale, pk=pk) if request.method == 'POST': amount = request.POST.get('amount') payment_date = request.POST.get('payment_date', timezone.now().date()) payment_method = request.POST.get('payment_method', 'Cash') notes = request.POST.get('notes', '') SalePayment.objects.create( sale=sale, amount=amount, payment_date=payment_date, payment_method=payment_method, notes=notes ) sale.update_balance() messages.success(request, _("Payment added successfully!")) return redirect('invoices') def delete_sale(request, pk): sale = get_object_or_404(Sale, pk=pk) for item in sale.items.all(): item.product.stock_quantity += item.quantity item.product.save() sale.delete() messages.success(request, _("Sale deleted successfully!")) return redirect('invoices') # --- Quotation Views --- def quotations(request): quotations_list = Quotation.objects.all().order_by('-created_at') customers = Customer.objects.all() return render(request, 'core/quotations.html', {'quotations': quotations_list, 'customers': customers}) def quotation_create(request): products = Product.objects.filter(is_active=True) customers = Customer.objects.all() return render(request, 'core/quotation_create.html', {'products': products, 'customers': customers}) def quotation_detail(request, pk): quotation = get_object_or_404(Quotation, pk=pk) settings = SystemSetting.objects.first() return render(request, 'core/quotation_detail.html', {'quotation': quotation, 'settings': settings}) @csrf_exempt def create_quotation_api(request): if request.method == 'POST': try: data = json.loads(request.body) customer_id = data.get('customer_id') quotation_number = data.get('quotation_number', '') items = data.get('items', []) total_amount = data.get('total_amount', 0) discount = data.get('discount', 0) valid_until = data.get('valid_until') terms_and_conditions = data.get('terms_and_conditions', '') notes = data.get('notes', '') customer = None if customer_id: customer = Customer.objects.get(id=customer_id) quotation = Quotation.objects.create( customer=customer, quotation_number=quotation_number, total_amount=total_amount, discount=discount, valid_until=valid_until if valid_until else None, terms_and_conditions=terms_and_conditions, notes=notes ) for item in items: product = Product.objects.get(id=item['id']) QuotationItem.objects.create( quotation=quotation, product=product, quantity=item['quantity'], unit_price=item['price'], line_total=item['line_total'] ) return JsonResponse({'success': True, 'quotation_id': quotation.id}) except Exception as e: return JsonResponse({'success': False, 'error': str(e)}, status=400) return JsonResponse({'success': False, 'error': 'Invalid request'}, status=405) def convert_quotation_to_invoice(request, pk): quotation = get_object_or_404(Quotation, pk=pk) if quotation.status == 'converted': messages.warning(request, _("This quotation has already been converted to an invoice.")) return redirect('invoices') # Create Sale from Quotation sale = Sale.objects.create( customer=quotation.customer, quotation=quotation, total_amount=quotation.total_amount, discount=quotation.discount, balance_due=quotation.total_amount, payment_type='cash', status='unpaid', notes=quotation.notes ) # Create SaleItems and Update Stock for item in quotation.items.all(): SaleItem.objects.create( sale=sale, product=item.product, quantity=item.quantity, unit_price=item.unit_price, line_total=item.line_total ) # Deduct Stock item.product.stock_quantity -= item.quantity item.product.save() # Update Quotation Status quotation.status = 'converted' quotation.save() messages.success(request, _("Quotation converted to Invoice successfully!")) return redirect('invoice_detail', pk=sale.pk) def delete_quotation(request, pk): quotation = get_object_or_404(Quotation, pk=pk) quotation.delete() messages.success(request, _("Quotation deleted successfully!")) return redirect('quotations') # --- Sale Return Views --- def sales_returns(request): returns = SaleReturn.objects.all().order_by('-created_at') return render(request, 'core/sales_returns.html', {'returns': returns}) def sale_return_create(request): products = Product.objects.filter(is_active=True) customers = Customer.objects.all() sales = Sale.objects.all().order_by('-created_at') return render(request, 'core/sale_return_create.html', { 'products': products, 'customers': customers, 'sales': sales }) def sale_return_detail(request, pk): sale_return = get_object_or_404(SaleReturn, pk=pk) settings = SystemSetting.objects.first() return render(request, 'core/sale_return_detail.html', {'sale_return': sale_return, 'settings': settings}) @csrf_exempt def create_sale_return_api(request): if request.method == 'POST': try: data = json.loads(request.body) sale_id = data.get('sale_id') customer_id = data.get('customer_id') return_number = data.get('return_number', '') items = data.get('items', []) total_amount = data.get('total_amount', 0) notes = data.get('notes', '') customer = None if customer_id: customer = Customer.objects.get(id=customer_id) sale = None if sale_id: sale = Sale.objects.get(id=sale_id) sale_return = SaleReturn.objects.create( sale=sale, customer=customer, return_number=return_number, total_amount=total_amount, notes=notes ) for item in items: product = Product.objects.get(id=item['id']) SaleReturnItem.objects.create( sale_return=sale_return, product=product, quantity=item['quantity'], unit_price=item['price'], line_total=item['line_total'] ) # Increase Stock for Sales Return product.stock_quantity += int(item['quantity']) product.save() return JsonResponse({'success': True, 'return_id': sale_return.id}) except Exception as e: return JsonResponse({'success': False, 'error': str(e)}, status=400) return JsonResponse({'success': False, 'error': 'Invalid request'}, status=405) def delete_sale_return(request, pk): sale_return = get_object_or_404(SaleReturn, pk=pk) for item in sale_return.items.all(): item.product.stock_quantity -= item.quantity item.product.save() sale_return.delete() messages.success(request, _("Sale return deleted successfully!")) return redirect('sales_returns') # --- Purchase Return Views --- def purchase_returns(request): returns = PurchaseReturn.objects.all().order_by('-created_at') return render(request, 'core/purchase_returns.html', {'returns': returns}) def purchase_return_create(request): products = Product.objects.filter(is_active=True) suppliers = Supplier.objects.all() purchases = Purchase.objects.all().order_by('-created_at') return render(request, 'core/purchase_return_create.html', { 'products': products, 'suppliers': suppliers, 'purchases': purchases }) def purchase_return_detail(request, pk): purchase_return = get_object_or_404(PurchaseReturn, pk=pk) settings = SystemSetting.objects.first() return render(request, 'core/purchase_return_detail.html', {'purchase_return': purchase_return, 'settings': settings}) @csrf_exempt def create_purchase_return_api(request): if request.method == 'POST': try: data = json.loads(request.body) purchase_id = data.get('purchase_id') supplier_id = data.get('supplier_id') return_number = data.get('return_number', '') items = data.get('items', []) total_amount = data.get('total_amount', 0) notes = data.get('notes', '') supplier = None if supplier_id: supplier = Supplier.objects.get(id=supplier_id) purchase = None if purchase_id: purchase = Purchase.objects.get(id=purchase_id) purchase_return = PurchaseReturn.objects.create( purchase=purchase, supplier=supplier, return_number=return_number, total_amount=total_amount, notes=notes ) for item in items: product = Product.objects.get(id=item['id']) PurchaseReturnItem.objects.create( purchase_return=purchase_return, product=product, quantity=item['quantity'], cost_price=item['price'], line_total=item['line_total'] ) # Decrease Stock for Purchase Return product.stock_quantity -= int(item['quantity']) product.save() return JsonResponse({'success': True, 'return_id': purchase_return.id}) except Exception as e: return JsonResponse({'success': False, 'error': str(e)}, status=400) return JsonResponse({'success': False, 'error': 'Invalid request'}, status=405) def delete_purchase_return(request, pk): purchase_return = get_object_or_404(PurchaseReturn, pk=pk) for item in purchase_return.items.all(): item.product.stock_quantity += item.quantity item.product.save() purchase_return.delete() messages.success(request, _("Purchase return deleted successfully!")) return redirect('purchase_returns') # --- Other Management Views --- def reports(request): """ Smart Reports View """ # Monthly Revenue monthly_sales = Sale.objects.annotate(month=TruncMonth('created_at')) \ .values('month') \ .annotate(total=Sum('total_amount')) \ .order_by('-month')[:12] # Top Selling Products top_products = SaleItem.objects.values('product__name_en', 'product__name_ar') \ .annotate(total_qty=Sum('quantity'), revenue=Sum('line_total')) \ .order_by('-total_qty')[:5] context = { 'monthly_sales': monthly_sales, 'top_products': top_products, } return render(request, 'core/reports.html', context) def settings_view(request): """ Smart Admin Settings View """ settings = SystemSetting.objects.first() if not settings: settings = SystemSetting.objects.create() if request.method == 'POST': settings.business_name = request.POST.get('business_name') settings.address = request.POST.get('address') settings.phone = request.POST.get('phone') settings.email = request.POST.get('email') settings.currency_symbol = request.POST.get('currency_symbol') settings.tax_rate = request.POST.get('tax_rate') settings.vat_number = request.POST.get('vat_number') settings.registration_number = request.POST.get('registration_number') if 'logo' in request.FILES: settings.logo = request.FILES['logo'] settings.save() messages.success(request, "Settings updated successfully!") return redirect('settings') return render(request, 'core/settings.html', {'settings': settings}) def add_customer(request): if request.method == 'POST': name = request.POST.get('name') phone = request.POST.get('phone') email = request.POST.get('email') address = request.POST.get('address') Customer.objects.create(name=name, phone=phone, email=email, address=address) messages.success(request, "Customer added successfully!") return redirect('customers') def edit_customer(request, pk): customer = get_object_or_404(Customer, pk=pk) if request.method == 'POST': customer.name = request.POST.get('name') customer.phone = request.POST.get('phone') customer.email = request.POST.get('email') customer.address = request.POST.get('address') customer.save() messages.success(request, "Customer updated successfully!") return redirect('customers') def delete_customer(request, pk): customer = get_object_or_404(Customer, pk=pk) customer.delete() messages.success(request, "Customer deleted successfully!") return redirect('customers') def add_supplier(request): if request.method == 'POST': name = request.POST.get('name') contact_person = request.POST.get('contact_person') phone = request.POST.get('phone') Supplier.objects.create(name=name, contact_person=contact_person, phone=phone) messages.success(request, "Supplier added successfully!") return redirect('suppliers') def edit_supplier(request, pk): supplier = get_object_or_404(Supplier, pk=pk) if request.method == 'POST': supplier.name = request.POST.get('name') supplier.contact_person = request.POST.get('contact_person') supplier.phone = request.POST.get('phone') supplier.save() messages.success(request, "Supplier updated successfully!") return redirect('suppliers') def delete_supplier(request, pk): supplier = get_object_or_404(Supplier, pk=pk) supplier.delete() messages.success(request, "Supplier deleted successfully!") return redirect('suppliers') def suggest_sku(request): """ API endpoint to suggest a unique SKU. """ while True: # Generate a random 8-digit number sku = "".join(random.choices(string.digits, k=8)) if not Product.objects.filter(sku=sku).exists(): return JsonResponse({"sku": sku}) def add_product(request): if request.method == 'POST': name_en = request.POST.get('name_en') name_ar = request.POST.get('name_ar') category_id = request.POST.get('category') unit_id = request.POST.get('unit') supplier_id = request.POST.get('supplier') sku = request.POST.get('sku') if not sku: while True: sku = ''.join(random.choices(string.digits, k=8)) if not Product.objects.filter(sku=sku).exists(): break cost_price = request.POST.get('cost_price', 0) price = request.POST.get('price', 0) vat = request.POST.get('vat', 0) opening_stock = request.POST.get('opening_stock', 0) stock_quantity = request.POST.get('stock_quantity', 0) is_active = request.POST.get('is_active') == 'on' category = get_object_or_404(Category, id=category_id) unit = get_object_or_404(Unit, id=unit_id) if unit_id else None supplier = get_object_or_404(Supplier, id=supplier_id) if supplier_id else None product = Product.objects.create( name_en=name_en, name_ar=name_ar, category=category, unit=unit, supplier=supplier, sku=sku, cost_price=cost_price, price=price, vat=vat, opening_stock=opening_stock, stock_quantity=stock_quantity, is_active=is_active ) if 'image' in request.FILES: product.image = request.FILES['image'] product.save() messages.success(request, "Product added successfully!") return redirect('inventory') def edit_product(request, pk): product = get_object_or_404(Product, pk=pk) if request.method == 'POST': product.name_en = request.POST.get('name_en') product.name_ar = request.POST.get('name_ar') product.sku = request.POST.get('sku') product.category = get_object_or_404(Category, id=request.POST.get('category')) unit_id = request.POST.get('unit') product.unit = get_object_or_404(Unit, id=unit_id) if unit_id else None supplier_id = request.POST.get('supplier') product.supplier = get_object_or_404(Supplier, id=supplier_id) if supplier_id else None product.cost_price = request.POST.get('cost_price', 0) product.price = request.POST.get('price', 0) product.vat = request.POST.get('vat', 0) product.opening_stock = request.POST.get('opening_stock', 0) product.stock_quantity = request.POST.get('stock_quantity', 0) product.is_active = request.POST.get('is_active') == 'on' if 'image' in request.FILES: product.image = request.FILES['image'] product.save() messages.success(request, "Product updated successfully!") return redirect('inventory') return redirect('inventory') def delete_product(request, pk): product = get_object_or_404(Product, pk=pk) product.delete() messages.success(request, "Product deleted successfully!") return redirect('inventory') def add_category(request): if request.method == 'POST': name_en = request.POST.get('name_en') name_ar = request.POST.get('name_ar') slug = slugify(name_en) Category.objects.create(name_en=name_en, name_ar=name_ar, slug=slug) messages.success(request, "Category added successfully!") return redirect('inventory') def edit_category(request, pk): category = get_object_or_404(Category, pk=pk) if request.method == 'POST': category.name_en = request.POST.get('name_en') category.name_ar = request.POST.get('name_ar') category.slug = slugify(category.name_en) category.save() messages.success(request, "Category updated successfully!") return redirect('inventory') def delete_category(request, pk): category = get_object_or_404(Category, pk=pk) category.delete() messages.success(request, "Category deleted successfully!") return redirect('inventory') def add_unit(request): if request.method == 'POST': name_en = request.POST.get('name_en') name_ar = request.POST.get('name_ar') short_name = request.POST.get('short_name') Unit.objects.create(name_en=name_en, name_ar=name_ar, short_name=short_name) messages.success(request, "Unit added successfully!") return redirect('inventory') def edit_unit(request, pk): unit = get_object_or_404(Unit, pk=pk) if request.method == 'POST': unit.name_en = request.POST.get('name_en') unit.name_ar = request.POST.get('name_ar') unit.short_name = request.POST.get('short_name') unit.save() messages.success(request, "Unit updated successfully!") return redirect('inventory') def delete_unit(request, pk): unit = get_object_or_404(Unit, pk=pk) unit.delete() messages.success(request, "Unit deleted successfully!") return redirect('inventory') def barcode_labels(request): products = Product.objects.filter(is_active=True).order_by('name_en') context = {'products': products} return render(request, 'core/barcode_labels.html', context) def import_products(request): """ Import products from an Excel (.xlsx) file. Expected columns: Name (Eng), Name (Ar), SKU, Cost Price, Sale Price """ if request.method == 'POST' and request.FILES.get('excel_file'): excel_file = request.FILES['excel_file'] if not excel_file.name.endswith('.xlsx'): messages.error(request, "Please upload a valid .xlsx file.") return redirect('inventory') try: wb = openpyxl.load_workbook(excel_file) sheet = wb.active # Get or create a default category default_category, _ = Category.objects.get_or_create( name_en="General", defaults={'name_ar': "عام", 'slug': 'general'} ) count = 0 updated_count = 0 errors = [] # Skip header row (min_row=2) for i, row in enumerate(sheet.iter_rows(min_row=2, values_only=True), start=2): if not any(row): continue # Skip empty rows # Unpack columns with fallbacks for safety # Format: name_en, name_ar, sku, cost_price, sale_price name_en = str(row[0]).strip() if row[0] else None name_ar = str(row[1]).strip() if len(row) > 1 and row[1] else name_en sku = str(row[2]).strip() if len(row) > 2 and row[2] else None cost_price = row[3] if len(row) > 3 and row[3] is not None else 0 sale_price = row[4] if len(row) > 4 and row[4] is not None else 0 if not name_en: errors.append(f"Row {i}: Missing English Name. Skipped.") continue if not sku: # Generate unique SKU if missing while True: sku = "".join(random.choices(string.digits, k=8)) if not Product.objects.filter(sku=sku).exists(): break product, created = Product.objects.update_or_create( sku=sku, defaults={ 'name_en': name_en, 'name_ar': name_ar, 'cost_price': cost_price, 'price': sale_price, 'category': default_category, 'is_active': True } ) if created: count += 1 else: updated_count += 1 if count > 0 or updated_count > 0: msg = f"Import completed: {count} new items added" if updated_count > 0: msg += f", {updated_count} items updated" messages.success(request, msg) if errors: for error in errors: messages.warning(request, error) except Exception as e: messages.error(request, f"Error processing file: {str(e)}") return redirect('inventory') @csrf_exempt def add_category_ajax(request): if request.method == 'POST': try: data = json.loads(request.body) name_en = data.get('name_en') name_ar = data.get('name_ar') if not name_en or not name_ar: return JsonResponse({'success': False, 'error': 'Missing names'}, status=400) slug = slugify(name_en) category = Category.objects.create(name_en=name_en, name_ar=name_ar, slug=slug) return JsonResponse({ 'success': True, 'id': category.id, 'name_en': category.name_en, 'name_ar': category.name_ar }) except Exception as e: return JsonResponse({'success': False, 'error': str(e)}, status=400) return JsonResponse({'success': False, 'error': 'Invalid request'}, status=405) @csrf_exempt def add_unit_ajax(request): if request.method == 'POST': try: data = json.loads(request.body) name_en = data.get('name_en') name_ar = data.get('name_ar') short_name = data.get('short_name') if not name_en or not name_ar or not short_name: return JsonResponse({'success': False, 'error': 'Missing fields'}, status=400) unit = Unit.objects.create(name_en=name_en, name_ar=name_ar, short_name=short_name) return JsonResponse({ 'success': True, 'id': unit.id, 'name_en': unit.name_en, 'name_ar': unit.name_ar }) except Exception as e: return JsonResponse({'success': False, 'error': str(e)}, status=400) return JsonResponse({'success': False, 'error': 'Invalid request'}, status=405) @csrf_exempt def add_supplier_ajax(request): if request.method == 'POST': try: data = json.loads(request.body) name = data.get('name') contact_person = data.get('contact_person', '') phone = data.get('phone', '') if not name: return JsonResponse({'success': False, 'error': 'Missing name'}, status=400) supplier = Supplier.objects.create(name=name, contact_person=contact_person, phone=phone) return JsonResponse({ 'success': True, 'id': supplier.id, 'name': supplier.name }) except Exception as e: return JsonResponse({'success': False, 'error': str(e)}, status=400) return JsonResponse({'success': False, 'error': 'Invalid request'}, status=405)