237 lines
8.8 KiB
Python
237 lines
8.8 KiB
Python
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, Customer, Supplier, Purchase, SaleItem, SystemSetting
|
|
import json
|
|
from datetime import timedelta
|
|
from django.utils import timezone
|
|
from django.contrib import messages
|
|
|
|
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')
|
|
categories = Category.objects.all()
|
|
context = {'products': products, 'categories': categories}
|
|
return render(request, 'core/inventory.html', context)
|
|
|
|
def pos(request):
|
|
products = Product.objects.all().filter(stock_quantity__gt=0)
|
|
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('sale__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)
|
|
|
|
def purchases(request):
|
|
purchases_list = Purchase.objects.all().select_related('supplier')
|
|
suppliers_list = Supplier.objects.all()
|
|
context = {'purchases': purchases_list, 'suppliers': suppliers_list}
|
|
return render(request, 'core/purchases.html', context)
|
|
|
|
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.save()
|
|
messages.success(request, "Settings updated successfully!")
|
|
return redirect('settings')
|
|
|
|
return render(request, 'core/settings.html', {'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')
|
|
items = data.get('items', [])
|
|
total_amount = data.get('total_amount', 0)
|
|
discount = data.get('discount', 0)
|
|
|
|
customer = None
|
|
if customer_id:
|
|
customer = Customer.objects.get(id=customer_id)
|
|
|
|
sale = Sale.objects.create(
|
|
customer=customer,
|
|
total_amount=total_amount,
|
|
discount=discount
|
|
)
|
|
|
|
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 -= item['quantity']
|
|
product.save()
|
|
|
|
settings = SystemSetting.objects.first()
|
|
return JsonResponse({
|
|
'success': True,
|
|
'sale_id': sale.id,
|
|
'business': {
|
|
'name': settings.business_name,
|
|
'address': settings.address,
|
|
'phone': settings.phone,
|
|
'currency': settings.currency_symbol
|
|
},
|
|
'sale': {
|
|
'id': sale.id,
|
|
'created_at': sale.created_at.strftime("%Y-%m-%d %H:%M"),
|
|
'total': float(sale.total_amount),
|
|
'items': [
|
|
{
|
|
'name_en': item.product.name_en,
|
|
'name_ar': item.product.name_ar,
|
|
'qty': item.quantity,
|
|
'price': float(item.unit_price),
|
|
'total': float(item.line_total)
|
|
} for item 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_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 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 add_purchase(request):
|
|
if request.method == 'POST':
|
|
supplier_id = request.POST.get('supplier')
|
|
total_amount = request.POST.get('total_amount')
|
|
supplier = get_object_or_404(Supplier, id=supplier_id)
|
|
Purchase.objects.create(supplier=supplier, total_amount=total_amount)
|
|
messages.success(request, "Purchase recorded successfully!")
|
|
return redirect('purchases')
|
|
|
|
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')
|
|
sku = request.POST.get('sku')
|
|
price = request.POST.get('price')
|
|
stock = request.POST.get('stock')
|
|
category = get_object_or_404(Category, id=category_id)
|
|
Product.objects.create(
|
|
name_en=name_en,
|
|
name_ar=name_ar,
|
|
category=category,
|
|
sku=sku,
|
|
price=price,
|
|
stock_quantity=stock
|
|
)
|
|
messages.success(request, "Product added successfully!")
|
|
return redirect('inventory')
|