# Force reload of views.py from django.shortcuts import render, redirect, get_object_or_404 from django.contrib.auth.decorators import login_required from django.contrib import messages from django.http import JsonResponse, HttpResponse from django.urls import reverse from django.views.decorators.csrf import csrf_exempt from django.db import transaction, models from django.db.models import Sum, Count, F, Q from django.utils import timezone from django.core.paginator import Paginator from django.utils.text import slugify import json import decimal import datetime from datetime import timedelta from .models import * from .forms import * from .helpers import number_to_words_en, send_whatsapp_document, send_whatsapp_message from .views_import import import_categories, import_suppliers, import_products # ========================================== # Standard Views # ========================================== @login_required def index(request): total_sales_amount = Sale.objects.aggregate(total=Sum('total_amount'))['total'] or 0 total_sales_count = Sale.objects.count() total_products = Product.objects.count() total_customers = Customer.objects.count() total_receivables = Sale.objects.aggregate(total=Sum('balance_due'))['total'] or 0 total_payables = Purchase.objects.aggregate(total=Sum('balance_due'))['total'] or 0 today = timezone.now().date() current_year = today.year monthly_sales = (Sale.objects.filter(created_at__year=current_year) .annotate(month=models.functions.ExtractMonth('created_at')) .values('month') .annotate(total=Sum('total_amount')) .order_by('month')) monthly_labels = [] monthly_data = [] months_map = {i: 0 for i in range(1, 13)} for entry in monthly_sales: months_map[entry['month']] = float(entry['total']) import calendar for i in range(1, 13): monthly_labels.append(calendar.month_abbr[i]) monthly_data.append(months_map[i]) seven_days_ago = today - timedelta(days=6) daily_sales = (Sale.objects.filter(created_at__date__gte=seven_days_ago) .annotate(day=models.functions.ExtractDay('created_at')) .values('created_at__date') .annotate(total=Sum('total_amount')) .order_by('created_at__date')) chart_labels = [] chart_data = [] date_map = {} current_date = seven_days_ago while current_date <= today: date_map[current_date] = 0 current_date += timedelta(days=1) for entry in daily_sales: date_map[entry['created_at__date']] = float(entry['total']) for date_key in sorted(date_map.keys()): chart_labels.append(date_key.strftime('%d %b')) chart_data.append(date_map[date_key]) category_sales = (SaleItem.objects.values('product__category__name_en') .annotate(total=Sum('line_total')) .order_by('-total')[:5]) category_labels = [item['product__category__name_en'] for item in category_sales] category_data = [float(item['total']) for item in category_sales] payment_stats = (SalePayment.objects.values('payment_method_name') .annotate(total=Sum('amount')) .order_by('-total')) payment_labels = [item['payment_method_name'] if item['payment_method_name'] else 'Unknown' for item in payment_stats] payment_data = [float(item['total']) for item in payment_stats] top_products = (SaleItem.objects.values('product__name_en', 'product__name_ar') .annotate(total_qty=Sum('quantity'), total_rev=Sum('line_total')) .order_by('-total_rev')[:5]) recent_sales = Sale.objects.select_related('customer').order_by('-created_at')[:5] low_stock_products = Product.objects.filter(is_active=True, stock_quantity__lte=F('min_stock_level'))[:5] expired_products = Product.objects.filter(is_active=True, has_expiry=True, expiry_date__lt=today)[:5] context = { 'total_sales_amount': total_sales_amount, 'total_sales_count': total_sales_count, 'total_products': total_products, 'total_customers': total_customers, 'total_receivables': total_receivables, 'total_payables': total_payables, 'monthly_labels': json.dumps(monthly_labels), 'monthly_data': json.dumps(monthly_data), 'chart_labels': json.dumps(chart_labels), 'chart_data': json.dumps(chart_data), 'category_labels': json.dumps(category_labels), 'category_data': json.dumps(category_data), 'payment_labels': json.dumps(payment_labels), 'payment_data': json.dumps(payment_data), 'top_products': top_products, 'recent_sales': recent_sales, 'low_stock_products': low_stock_products, 'expired_products': expired_products, } return render(request, 'core/index.html', context) @login_required def inventory(request): products = Product.objects.filter(is_active=True).select_related('category', 'unit', 'supplier') categories = Category.objects.all() units = Unit.objects.all() suppliers = Supplier.objects.all() category_id = request.GET.get('category') if category_id: products = products.filter(category_id=category_id) query = request.GET.get('q') if query: products = products.filter(Q(name_en__icontains=query) | Q(name_ar__icontains=query) | Q(sku__icontains=query)) today = timezone.now().date() expiring_soon_products = Product.objects.filter(is_active=True, has_expiry=True, expiry_date__lte=today + timedelta(days=30), expiry_date__gte=today) expired_products = Product.objects.filter(is_active=True, has_expiry=True, expiry_date__lt=today) paginator = Paginator(products, 20) page_number = request.GET.get('page') page_obj = paginator.get_page(page_number) context = { 'products': page_obj, 'categories': categories, 'units': units, 'suppliers': suppliers, 'expiring_soon_products': expiring_soon_products, 'expired_products': expired_products, 'site_settings': SystemSetting.objects.first(), } return render(request, 'core/inventory.html', context) @login_required def customers(request): customers_list = Customer.objects.all().order_by('-created_at') query = request.GET.get('q') if query: customers_list = customers_list.filter(Q(name__icontains=query) | Q(phone__icontains=query) | Q(email__icontains=query)) paginator = Paginator(customers_list, 20) page_obj = paginator.get_page(request.GET.get('page')) return render(request, 'core/customers.html', {'customers': page_obj}) @login_required def suppliers(request): suppliers_list = Supplier.objects.all().order_by('-created_at') query = request.GET.get('q') if query: suppliers_list = suppliers_list.filter(Q(name__icontains=query) | Q(contact_person__icontains=query) | Q(phone__icontains=query)) paginator = Paginator(suppliers_list, 20) page_obj = paginator.get_page(request.GET.get('page')) return render(request, 'core/suppliers.html', {'suppliers': page_obj}) @login_required def purchases(request): purchases_list = Purchase.objects.all().select_related('supplier').order_by('-created_at') start_date = request.GET.get('start_date') end_date = request.GET.get('end_date') supplier_id = request.GET.get('supplier') if start_date: purchases_list = purchases_list.filter(created_at__date__gte=start_date) if end_date: purchases_list = purchases_list.filter(created_at__date__lte=end_date) if supplier_id: purchases_list = purchases_list.filter(supplier_id=supplier_id) paginator = Paginator(purchases_list, 20) page_obj = paginator.get_page(request.GET.get('page')) return render(request, 'core/purchases.html', {'page_obj': page_obj, 'suppliers': Supplier.objects.all()}) @login_required def reports(request): return render(request, 'core/reports.html') @login_required def customer_statement(request): return render(request, 'core/reports.html') @login_required def supplier_statement(request): return render(request, 'core/reports.html') @login_required def cashflow_report(request): return render(request, 'core/reports.html') @login_required def settings_view(request): settings = SystemSetting.objects.first() payment_methods = PaymentMethod.objects.all() devices = Device.objects.all() loyalty_tiers = LoyaltyTier.objects.all() return render(request, 'core/settings.html', { 'settings': settings, 'payment_methods': payment_methods, 'devices': devices, 'loyalty_tiers': loyalty_tiers }) @login_required def profile_view(request): return render(request, 'core/profile.html') @login_required def user_management(request): from django.contrib.auth.models import User, Group return render(request, 'core/user_management.html', {'users': User.objects.all(), 'groups': Group.objects.all()}) @login_required def group_details_api(request, pk): from django.contrib.auth.models import Group group = get_object_or_404(Group, pk=pk) data = { 'id': group.id, 'name': group.name, 'permissions': [{'id': p.id, 'name': p.name, 'codename': p.codename} for p in group.permissions.all()] } return JsonResponse(data) # ========================================== # POS & Sales # ========================================== @login_required def pos(request): session = CashierSession.objects.filter(user=request.user, status='active').last() context = { 'categories': Category.objects.all(), 'products': Product.objects.filter(is_active=True).select_related('category', 'unit'), 'customers': Customer.objects.all(), 'payment_methods': PaymentMethod.objects.filter(is_active=True), 'session': session, 'held_sales': HeldSale.objects.filter(created_by=request.user).order_by('-created_at'), } return render(request, 'core/pos.html', context) @login_required def customer_display(request): return render(request, 'core/customer_display.html') @csrf_exempt @login_required def create_sale_api(request): if request.method != 'POST': return JsonResponse({'success': False}, status=405) try: data = json.loads(request.body) customer_id = data.get('customer_id') items = data.get('items', []) payments = data.get('payments', []) if not payments: paid_amount = decimal.Decimal(str(data.get('paid_amount', 0))) if paid_amount > 0: method_name = "Cash" pm_id = data.get('payment_method_id') if pm_id: try: method_name = PaymentMethod.objects.get(id=pm_id).name_en except: pass payments.append({'method': method_name, 'amount': paid_amount}) with transaction.atomic(): customer = Customer.objects.get(id=customer_id) if customer_id else None sale = Sale.objects.create( created_by=request.user, customer=customer, invoice_number=data.get('invoice_number', ''), total_amount=0, discount=decimal.Decimal(str(data.get('discount', 0))), notes=data.get('notes', ''), status='unpaid' ) if data.get('due_date'): sale.due_date = data.get('due_date') subtotal = decimal.Decimal(0) vat_amount = decimal.Decimal(0) for item in items: product = Product.objects.select_for_update().get(id=item['id']) qty = decimal.Decimal(str(item['quantity'])) price = decimal.Decimal(str(item['price'])) setting = SystemSetting.objects.first() if not product.is_service and product.stock_quantity < qty and (not setting or not setting.allow_zero_stock_sales): raise Exception(f"Insufficient stock for {product.name_en}") line_total = price * qty subtotal += line_total vat_amount += line_total * (product.vat / 100) SaleItem.objects.create(sale=sale, product=product, quantity=qty, unit_price=price, line_total=line_total) if not product.is_service: product.stock_quantity -= qty product.save() sale.subtotal = subtotal sale.vat_amount = vat_amount sale.total_amount = subtotal + vat_amount - sale.discount paid = decimal.Decimal(0) for p in payments: amt = decimal.Decimal(str(p['amount'])) SalePayment.objects.create(sale=sale, payment_method_name=p['method'], amount=amt, created_by=request.user) paid += amt sale.paid_amount = paid sale.balance_due = sale.total_amount - paid sale.status = 'paid' if sale.balance_due <= 0 else ('partial' if paid > 0 else 'unpaid') sale.save() return JsonResponse({'success': True, 'sale_id': sale.id}) except Exception as e: return JsonResponse({'success': False, 'message': str(e)}, status=500) @csrf_exempt @login_required def hold_sale_api(request): if request.method != 'POST': return JsonResponse({'success': False}, status=405) try: data = json.loads(request.body) HeldSale.objects.create( created_by=request.user, cart_data=json.dumps(data.get('cart_data', {})), note=data.get('note', ''), customer_name=data.get('customer_name', '') ) return JsonResponse({'success': True}) except Exception as e: return JsonResponse({'success': False, 'message': str(e)}, status=500) @login_required def get_held_sales_api(request): sales = HeldSale.objects.filter(created_by=request.user).order_by('-created_at') data = [{'id': s.id, 'created_at': s.created_at.strftime('%Y-%m-%d %H:%M'), 'customer_name': s.customer_name, 'note': s.note, 'cart_data': json.loads(s.cart_data)} for s in sales] return JsonResponse({'sales': data}) @csrf_exempt @login_required def recall_held_sale_api(request, pk): s = get_object_or_404(HeldSale, pk=pk, created_by=request.user) return JsonResponse({'success': True, 'cart_data': json.loads(s.cart_data), 'customer_name': s.customer_name, 'note': s.note}) @csrf_exempt @login_required def delete_held_sale_api(request, pk): get_object_or_404(HeldSale, pk=pk, created_by=request.user).delete() return JsonResponse({'success': True}) # Invoices @login_required def invoice_list(request): sales = Sale.objects.select_related('customer', 'created_by').order_by('-created_at') if request.GET.get('status'): sales = sales.filter(status=request.GET.get('status')) if request.GET.get('start_date'): sales = sales.filter(created_at__date__gte=request.GET.get('start_date')) if request.GET.get('end_date'): sales = sales.filter(created_at__date__lte=request.GET.get('end_date')) paginator = Paginator(sales, 20) return render(request, 'core/invoices.html', { 'sales': paginator.get_page(request.GET.get('page')), 'payment_methods': PaymentMethod.objects.filter(is_active=True), 'customers': Customer.objects.all(), 'site_settings': SystemSetting.objects.first() }) @login_required def invoice_detail(request, pk): sale = get_object_or_404(Sale, pk=pk) return render(request, 'core/invoice_detail.html', {'sale': sale, 'payment_methods': PaymentMethod.objects.filter(is_active=True)}) @login_required def invoice_create(request): return render(request, 'core/invoice_create.html', { 'products': Product.objects.filter(is_active=True).select_related('category', 'unit'), 'customers': Customer.objects.all(), 'payment_methods': PaymentMethod.objects.filter(is_active=True), 'site_settings': SystemSetting.objects.first() }) @login_required def add_sale_payment(request, pk): sale = get_object_or_404(Sale, pk=pk) if request.method == 'POST': amount = decimal.Decimal(request.POST.get('amount', 0)) pm_id = request.POST.get('payment_method_id') if amount > 0: method_name = "Cash" if pm_id: try: method_name = PaymentMethod.objects.get(id=pm_id).name_en except: pass SalePayment.objects.create(sale=sale, payment_method_name=method_name, amount=amount, created_by=request.user, notes=request.POST.get('notes', '')) sale.update_balance() messages.success(request, _("Payment recorded.")) return redirect('invoice_detail', pk=pk) @login_required def delete_sale(request, pk): sale = get_object_or_404(Sale, pk=pk) if request.method == 'POST': with transaction.atomic(): for item in sale.items.all(): if not item.product.is_service: item.product.stock_quantity += item.quantity item.product.save() sale.delete() messages.success(request, _("Invoice deleted.")) return redirect('invoices') return render(request, 'core/confirm_delete.html', {'object': sale}) # Quotations @login_required def quotations(request): return render(request, 'core/quotations.html', {'quotations': Quotation.objects.all().order_by('-created_at')}) @login_required def quotation_create(request): return render(request, 'core/quotation_create.html', {'customers': Customer.objects.all(), 'products': Product.objects.filter(is_active=True)}) @login_required def quotation_detail(request, pk): return render(request, 'core/quotation_detail.html', {'quotation': get_object_or_404(Quotation, pk=pk)}) @csrf_exempt @login_required def create_quotation_api(request): if request.method != 'POST': return JsonResponse({'success': False}, status=405) try: data = json.loads(request.body) with transaction.atomic(): q = Quotation.objects.create(created_by=request.user, customer=Customer.objects.get(id=data.get('customer_id')) if data.get('customer_id') else None, total_amount=0) total = decimal.Decimal(0) for item in data.get('items', []): p = Product.objects.get(id=item['id']) qty, price = decimal.Decimal(str(item['quantity'])), decimal.Decimal(str(item['price'])) line = qty * price total += line QuotationItem.objects.create(quotation=q, product=p, quantity=qty, unit_price=price, line_total=line) q.total_amount = total; q.save() return JsonResponse({'success': True, 'id': q.id}) except Exception as e: return JsonResponse({'success': False, 'message': str(e)}) @login_required def convert_quotation_to_invoice(request, pk): q = get_object_or_404(Quotation, pk=pk) try: with transaction.atomic(): sale = Sale.objects.create(created_by=request.user, customer=q.customer, total_amount=q.total_amount, status='unpaid', balance_due=q.total_amount) for item in q.items.all(): if not item.product.is_service and item.product.stock_quantity < item.quantity: raise Exception(f"Low stock for {item.product.name_en}") SaleItem.objects.create(sale=sale, product=item.product, quantity=item.quantity, unit_price=item.unit_price, line_total=item.line_total) if not item.product.is_service: item.product.stock_quantity -= item.quantity item.product.save() q.status = 'converted'; q.save() return redirect('invoice_detail', pk=sale.id) except Exception as e: messages.error(request, str(e)) return redirect('quotation_detail', pk=pk) # Inventory @login_required def add_product(request): if request.method == 'POST': try: p = Product.objects.create( category=Category.objects.get(id=request.POST.get('category')), unit=Unit.objects.get(id=request.POST.get('unit')) if request.POST.get('unit') else None, supplier=Supplier.objects.get(id=request.POST.get('supplier')) if request.POST.get('supplier') else None, name_en=request.POST.get('name_en'), name_ar=request.POST.get('name_ar'), sku=request.POST.get('sku'), cost_price=decimal.Decimal(request.POST.get('cost_price', 0)), price=decimal.Decimal(request.POST.get('price', 0)), vat=decimal.Decimal(request.POST.get('vat', 0)), opening_stock=decimal.Decimal(request.POST.get('opening_stock', 0)), stock_quantity=decimal.Decimal(request.POST.get('stock_quantity', 0)), min_stock_level=decimal.Decimal(request.POST.get('min_stock_level', 0)), has_expiry=request.POST.get('has_expiry') == 'on', expiry_date=request.POST.get('expiry_date') or None, is_active=request.POST.get('is_active') == 'on', image=request.FILES.get('image'), description=request.POST.get('description', '') ) messages.success(request, _("Product added.")); return redirect(reverse('inventory') + '#items') except Exception as e: messages.error(request, str(e)) return redirect('inventory') @login_required def edit_product(request, pk): p = get_object_or_404(Product, pk=pk) if request.method == 'POST': try: p.category = Category.objects.get(id=request.POST.get('category')) p.unit = Unit.objects.get(id=request.POST.get('unit')) if request.POST.get('unit') else None p.supplier = Supplier.objects.get(id=request.POST.get('supplier')) if request.POST.get('supplier') else None p.name_en, p.name_ar, p.sku = request.POST.get('name_en'), request.POST.get('name_ar'), request.POST.get('sku') p.cost_price, p.price, p.vat = decimal.Decimal(request.POST.get('cost_price', 0)), decimal.Decimal(request.POST.get('price', 0)), decimal.Decimal(request.POST.get('vat', 0)) p.stock_quantity, p.min_stock_level, p.opening_stock = decimal.Decimal(request.POST.get('stock_quantity', 0)), decimal.Decimal(request.POST.get('min_stock_level', 0)), decimal.Decimal(request.POST.get('opening_stock', 0)) p.has_expiry = request.POST.get('has_expiry') == 'on' p.expiry_date = request.POST.get('expiry_date') or None p.is_active = request.POST.get('is_active') == 'on' if request.FILES.get('image'): p.image = request.FILES.get('image') p.description = request.POST.get('description', '') p.save(); messages.success(request, _("Product updated.")); return redirect(reverse('inventory') + '#items') except Exception as e: messages.error(request, str(e)) return redirect('inventory') # AJAX Save @csrf_exempt def add_category_ajax(request): if request.method == 'POST': try: data = json.loads(request.body) c = Category.objects.create(name_en=data.get('name_en'), name_ar=data.get('name_ar'), slug=slugify(data.get('name_en'))) return JsonResponse({'success': True, 'id': c.id}) except Exception as e: return JsonResponse({'success': False, 'error': str(e)}) return JsonResponse({'success': False}) @csrf_exempt def add_unit_ajax(request): if request.method == 'POST': try: data = json.loads(request.body) u = Unit.objects.create(name_en=data.get('name_en'), name_ar=data.get('name_ar'), short_name=data.get('short_name')) return JsonResponse({'success': True, 'id': u.id}) except Exception as e: return JsonResponse({'success': False, 'error': str(e)}) return JsonResponse({'success': False}) @csrf_exempt def add_customer_ajax(request): if request.method == 'POST': try: data = json.loads(request.body) c = Customer.objects.create(name=data.get('name'), phone=data.get('phone', ''), email=data.get('email', ''), address=data.get('address', '')) return JsonResponse({'success': True, 'id': c.id}) except Exception as e: return JsonResponse({'success': False, 'error': str(e)}) return JsonResponse({'success': False}) @login_required def start_session(request): if request.method == 'POST': CashierSession.objects.create(user=request.user, opening_balance=request.POST.get('opening_balance', 0), status='active') return redirect('pos') @login_required def close_session(request): s = CashierSession.objects.filter(user=request.user, status='active').last() if s and request.method == 'POST': s.closing_balance, s.end_time, s.status = request.POST.get('closing_balance', 0), timezone.now(), 'closed' s.save() return redirect('pos') # ... Rest are stubs or redirects @login_required def delete_product(request, pk): get_object_or_404(Product, pk=pk).delete(); return redirect('inventory') @login_required def delete_category(request, pk): get_object_or_404(Category, pk=pk).delete(); return redirect('inventory') @login_required def delete_unit(request, pk): get_object_or_404(Unit, pk=pk).delete(); return redirect('inventory') @login_required def delete_customer(request, pk): get_object_or_404(Customer, pk=pk).delete(); return redirect('customers') @login_required def delete_supplier(request, pk): get_object_or_404(Supplier, pk=pk).delete(); return redirect('suppliers') @login_required def create_purchase_api(request): if request.method != 'POST': return JsonResponse({'success': False}, status=405) try: data = json.loads(request.body) with transaction.atomic(): p = Purchase.objects.create(created_by=request.user, supplier=Supplier.objects.get(id=data.get('supplier_id')), total_amount=0, status='unpaid') total = decimal.Decimal(0) for item in data.get('items', []): prod = Product.objects.get(id=item['id']) qty, cost = decimal.Decimal(str(item['quantity'])), decimal.Decimal(str(item['cost'])) line = qty * cost; total += line PurchaseItem.objects.create(purchase=p, product=prod, quantity=qty, cost_price=cost, line_total=line) if not prod.is_service: if prod.stock_quantity + qty > 0: prod.cost_price = ((prod.stock_quantity * prod.cost_price) + (qty * cost)) / (prod.stock_quantity + qty) prod.stock_quantity += qty; prod.save() p.total_amount = total; p.balance_due = total; p.save() return JsonResponse({'success': True, 'id': p.id}) except Exception as e: return JsonResponse({'success': False, 'message': str(e)}) @login_required def add_purchase_payment(request, pk): p = get_object_or_404(Purchase, pk=pk) if request.method == 'POST': amt, pm_id = decimal.Decimal(request.POST.get('amount', 0)), request.POST.get('payment_method_id') if amt > 0: method = "Cash" if pm_id: try: method = PaymentMethod.objects.get(id=pm_id).name_en except: pass PurchasePayment.objects.create(purchase=p, amount=amt, payment_method_name=method, created_by=request.user) p.update_balance(); messages.success(request, _("Payment recorded.")) return redirect('purchase_detail', pk=pk) @login_required def update_purchase_api(request, pk): return JsonResponse({'success': False}, status=501) @login_required def delete_purchase(request, pk): get_object_or_404(Purchase, pk=pk).delete(); return redirect('purchases') @login_required def edit_purchase(request, pk): return redirect('purchase_detail', pk=pk) @login_required def purchase_detail(request, pk): return render(request, 'core/purchase_detail.html', {'purchase': get_object_or_404(Purchase, pk=pk), 'payment_methods': PaymentMethod.objects.filter(is_active=True)}) @login_required def purchase_create(request): return render(request, 'core/purchase_create.html', {'suppliers': Supplier.objects.all(), 'products': Product.objects.all()}) @login_required def supplier_payments(request): return redirect('purchases') @login_required def customer_payments(request): return redirect('invoices') @login_required def customer_payment_receipt(request, pk): return redirect('invoices') @login_required def sale_receipt(request, pk): return render(request, 'core/receipt.html', {'sale': get_object_or_404(Sale, pk=pk)}) @login_required def edit_invoice(request, pk): return redirect('invoice_detail', pk=pk) @login_required def expenses_view(request): return redirect('accounting:expense_list') @login_required def expense_create_view(request): return redirect('accounting:expense_create') @login_required def expense_edit_view(request, pk): return redirect('accounting:expense_edit', pk=pk) @login_required def expense_delete_view(request, pk): return redirect('accounting:expense_delete', pk=pk) @login_required def expense_categories_view(request): return redirect('accounting:expense_category_list') @login_required def expense_category_delete_view(request, pk): return redirect('accounting:expense_category_delete', pk=pk) @login_required def expense_report(request): return redirect('accounting:expense_report') @login_required def export_expenses_excel(request): return redirect('accounting:expense_list') @csrf_exempt def pos_sync_update(request): return JsonResponse({'status': 'ok'}) @csrf_exempt def pos_sync_state(request): return JsonResponse({'status': 'ok'}) @login_required def suggest_sku(request): import random; return JsonResponse({'sku': f"SKU-{random.randint(10000, 99999)}"}) @login_required def barcode_labels(request): return render(request, 'core/barcode_labels.html') @login_required def add_category(request): return redirect('inventory') @login_required def edit_category(request, pk): return redirect('inventory') @login_required def add_unit(request): return redirect('inventory') @login_required def edit_unit(request, pk): return redirect('inventory') @csrf_exempt def add_supplier_ajax(request): return JsonResponse({'success': False}) @csrf_exempt def search_customers_api(request): q = request.GET.get('q', '') res = [{'id': c.id, 'text': f"{c.name} ({c.phone})"} for c in Customer.objects.filter(Q(name__icontains=q) | Q(phone__icontains=q))] return JsonResponse({'results': res}) @login_required def add_customer(request): return redirect('customers') @login_required def edit_customer(request, pk): return redirect('customers') @login_required def add_supplier(request): return redirect('suppliers') @login_required def edit_supplier(request, pk): return redirect('suppliers') @login_required def add_payment_method(request): return redirect('settings') @login_required def edit_payment_method(request, pk): return redirect('settings') @login_required def delete_payment_method(request, pk): return redirect('settings') @csrf_exempt def add_payment_method_ajax(request): return JsonResponse({'success': False}) @login_required def add_loyalty_tier(request): return redirect('settings') @login_required def edit_loyalty_tier(request, pk): return redirect('settings') @login_required def delete_loyalty_tier(request, pk): return redirect('settings') @csrf_exempt def get_customer_loyalty_api(request, pk): return JsonResponse({'points': 0}) @csrf_exempt def send_invoice_whatsapp(request): return JsonResponse({'success': False}) @csrf_exempt def test_whatsapp_connection(request): return JsonResponse({'success': False}) @login_required def add_device(request): return redirect('settings') @login_required def edit_device(request, pk): return redirect('settings') @login_required def delete_device(request, pk): return redirect('settings') @login_required def lpo_list(request): return redirect('purchases') @login_required def lpo_create(request): return redirect('purchases') @login_required def lpo_detail(request, pk): return redirect('purchases') @login_required def convert_lpo_to_purchase(request, pk): return redirect('purchases') @login_required def lpo_delete(request, pk): return redirect('purchases') @csrf_exempt def create_lpo_api(request): return JsonResponse({'success': False}) @login_required def cashier_registry(request): return redirect('settings') @login_required def cashier_session_list(request): return render(request, 'core/cashier_sessions.html', {'sessions': CashierSession.objects.all().order_by('-start_time')}) @login_required def session_detail(request, pk): return render(request, 'core/session_detail.html', {'session': get_object_or_404(CashierSession, pk=pk)}) # Purchase Returns Stubs @login_required def purchase_returns(request): return render(request, 'core/purchase_returns.html', {'returns': PurchaseReturn.objects.all().order_by('-created_at')}) @login_required def purchase_return_create(request): return render(request, 'core/purchase_return_create.html', {'purchases': Purchase.objects.all().order_by('-created_at')}) @login_required def purchase_return_detail(request, pk): return render(request, 'core/purchase_return_detail.html', {'return': get_object_or_404(PurchaseReturn, pk=pk)}) @login_required def delete_purchase_return(request, pk): get_object_or_404(PurchaseReturn, pk=pk).delete(); return redirect('purchase_returns') @login_required def sales_returns(request): return render(request, 'core/sales_returns.html', {'returns': SaleReturn.objects.all().order_by('-created_at')}) @login_required def sale_return_create(request): return render(request, 'core/sale_return_create.html', {'invoices': Sale.objects.all().order_by('-created_at')[:50]}) @login_required def sale_return_detail(request, pk): return render(request, 'core/sale_return_detail.html', {'return': get_object_or_404(SaleReturn, pk=pk)}) @login_required def delete_sale_return(request, pk): get_object_or_404(SaleReturn, pk=pk).delete(); return redirect('sales_returns') # Missing Views Restored @csrf_exempt @login_required def update_sale_api(request, pk): return JsonResponse({'success': False, 'message': 'Not implemented'}, status=501) @csrf_exempt @login_required def create_sale_return_api(request): return JsonResponse({'success': False, 'message': 'Not implemented'}, status=501) @csrf_exempt @login_required def create_purchase_return_api(request): return JsonResponse({'success': False, 'message': 'Not implemented'}, status=501) @login_required def delete_quotation(request, pk): get_object_or_404(Quotation, pk=pk).delete() return redirect('quotations')