Autosave: 20260210-152237

This commit is contained in:
Flatlogic Bot 2026-02-10 15:22:38 +00:00
parent 55c69b5fdc
commit b30330b17b
5 changed files with 82 additions and 54 deletions

View File

@ -50,11 +50,9 @@ def index(request):
except Exception as e: except Exception as e:
logger.error(f"Migration Fix Failed: {e}") logger.error(f"Migration Fix Failed: {e}")
settings = None
try:
settings = SystemSetting.objects.first() settings = SystemSetting.objects.first()
except Exception as e: if not settings:
logger.error(f"Failed to load settings in index: {e}") settings = SystemSetting.objects.create()
today = timezone.now().date() today = timezone.now().date()
@ -178,13 +176,9 @@ def inventory(request):
next_30_days = today + datetime.timedelta(days=30) next_30_days = today + datetime.timedelta(days=30)
expired_products = products.filter(has_expiry=True, expiry_date__lt=today) expired_products = products.filter(has_expiry=True, expiry_date__lt=today)
expiring_soon_products = products.filter(has_expiry=True, expiry_date__gte=today, expiry_date__lte=next_30_days)
settings = None
try:
settings = SystemSetting.objects.first() settings = SystemSetting.objects.first()
except Exception: if not settings:
pass settings = SystemSetting.objects.create()
context = { context = {
'products': products, 'products': products,
@ -209,14 +203,9 @@ def suppliers(request):
@login_required @login_required
def settings_view(request): def settings_view(request):
settings = None
try:
settings = SystemSetting.objects.first() settings = SystemSetting.objects.first()
if not settings: if not settings:
settings = SystemSetting.objects.create() settings = SystemSetting.objects.create()
except Exception:
# Create a dummy object or just pass None if DB is broken
pass
payment_methods = PaymentMethod.objects.filter(is_active=True) payment_methods = PaymentMethod.objects.filter(is_active=True)
expense_categories = ExpenseCategory.objects.all() expense_categories = ExpenseCategory.objects.all()
@ -325,11 +314,9 @@ def pos(request):
messages.warning(request, _("Please open a session to start selling.")) messages.warning(request, _("Please open a session to start selling."))
return redirect('start_session') return redirect('start_session')
settings = None
try:
settings = SystemSetting.objects.first() settings = SystemSetting.objects.first()
except Exception: if not settings:
pass settings = SystemSetting.objects.create()
products = Product.objects.filter(is_active=True) products = Product.objects.filter(is_active=True)
@ -398,14 +385,11 @@ def invoice_list(request):
Q(invoice_number__icontains=query) Q(invoice_number__icontains=query)
) )
paginator = Paginator(sales, 25)
settings = None
try:
settings = SystemSetting.objects.first() settings = SystemSetting.objects.first()
except Exception: if not settings:
pass settings = SystemSetting.objects.create()
paginator = Paginator(sales, 20)
context = { context = {
'sales': paginator.get_page(request.GET.get('page')), 'sales': paginator.get_page(request.GET.get('page')),
'customers': Customer.objects.all(), 'customers': Customer.objects.all(),
@ -415,9 +399,25 @@ def invoice_list(request):
} }
return render(request, 'core/invoices.html', context) return render(request, 'core/invoices.html', context)
@login_required
@login_required @login_required
def invoice_create(request): def invoice_create(request):
return redirect('pos') customers = Customer.objects.all().order_by('name')
products = Product.objects.filter(is_active=True).select_related('category')
payment_methods = PaymentMethod.objects.filter(is_active=True)
site_settings = SystemSetting.objects.first()
if not site_settings:
site_settings = SystemSetting.objects.create()
decimal_places = site_settings.decimal_places or 2
return render(request, 'core/invoice_create.html', {
'customers': customers,
'products': products,
'payment_methods': payment_methods,
'site_settings': site_settings,
'decimal_places': decimal_places,
})
@login_required @login_required
def invoice_detail(request, pk): def invoice_detail(request, pk):
@ -430,11 +430,9 @@ def edit_invoice(request, pk):
customers = Customer.objects.all() customers = Customer.objects.all()
products = Product.objects.filter(is_active=True).select_related('category') products = Product.objects.filter(is_active=True).select_related('category')
payment_methods = PaymentMethod.objects.filter(is_active=True) payment_methods = PaymentMethod.objects.filter(is_active=True)
site_settings = None
try:
site_settings = SystemSetting.objects.first() site_settings = SystemSetting.objects.first()
except Exception: if not site_settings:
pass site_settings = SystemSetting.objects.create()
decimal_places = 2 decimal_places = 2
if site_settings: if site_settings:
@ -638,11 +636,19 @@ def purchases(request):
purchases = Purchase.objects.all().order_by('-created_at') purchases = Purchase.objects.all().order_by('-created_at')
return render(request, 'core/purchases.html', {'purchases': purchases}) return render(request, 'core/purchases.html', {'purchases': purchases})
@login_required
@login_required @login_required
def purchase_create(request): def purchase_create(request):
suppliers = Supplier.objects.all() suppliers = Supplier.objects.all()
products = Product.objects.filter(is_active=True) products = Product.objects.filter(is_active=True)
return render(request, 'core/purchase_create.html', {'suppliers': suppliers, 'products': products}) payment_methods = PaymentMethod.objects.filter(is_active=True)
settings = SystemSetting.objects.first()
return render(request, 'core/purchase_create.html', {
'suppliers': suppliers,
'products': products,
'payment_methods': payment_methods,
'decimal_places': settings.decimal_places if settings else 3
})
@login_required @login_required
def purchase_detail(request, pk): def purchase_detail(request, pk):
@ -888,10 +894,10 @@ def cashflow_report(request):
@login_required @login_required
def add_product(request): def add_product(request):
if request.method == 'POST': if request.method == 'POST':
form = ProductForm(request.POST, request.FILES, instance=product) form = ProductForm(request.POST, request.FILES)
if form.is_valid(): if form.is_valid():
form.save() form.save()
messages.success(request, _("Product updated.")) messages.success(request, _("Product added."))
return redirect(reverse('inventory') + '#items') return redirect(reverse('inventory') + '#items')
return redirect('inventory') return redirect('inventory')
@ -1125,7 +1131,7 @@ def send_invoice_whatsapp(request):
receipt_url = request.build_absolute_uri(reverse('sale_receipt', args=[sale.pk])) receipt_url = request.build_absolute_uri(reverse('sale_receipt', args=[sale.pk]))
message = ( message = (
f"Hello {sale.customer.name if sale.customer else 'Guest'}, f"Hello {sale.customer.name if sale.customer else 'Guest'},\n"
f"Here is your invoice #{sale.invoice_number or sale.id}.\n" f"Here is your invoice #{sale.invoice_number or sale.id}.\n"
f"Total: {sale.total_amount}\n" f"Total: {sale.total_amount}\n"
f"View Invoice: {receipt_url}\n" f"View Invoice: {receipt_url}\n"
@ -1226,13 +1232,18 @@ def create_sale_api(request):
with transaction.atomic(): with transaction.atomic():
sale = Sale.objects.create( sale = Sale.objects.create(
customer_id=data.get('customer_id') or None, customer_id=data.get('customer_id') or None,
invoice_number=data.get('invoice_number', ''),
subtotal=data.get('subtotal', 0),
vat_amount=data.get('vat_amount', 0),
total_amount=data.get('total_amount', 0), total_amount=data.get('total_amount', 0),
paid_amount=data.get('paid_amount', 0), paid_amount=data.get('paid_amount', 0),
payment_type=data.get('payment_type', 'cash'), payment_type=data.get('payment_type', 'cash'),
created_by=request.user, created_by=request.user,
status='paid' if data.get('payment_type') == 'cash' else 'partial', status='paid' if data.get('payment_type') == 'cash' else 'partial',
discount=data.get('discount', 0), discount=data.get('discount', 0),
loyalty_points_redeemed=data.get('loyalty_points_redeemed', 0) loyalty_points_redeemed=data.get('loyalty_points_redeemed', 0),
notes=data.get('notes', ''),
due_date=data.get('due_date') if data.get('due_date') else None
) )
for item in data.get('items', []): for item in data.get('items', []):
SaleItem.objects.create( SaleItem.objects.create(
@ -1367,20 +1378,36 @@ def create_purchase_api(request):
try: try:
data = json.loads(request.body) data = json.loads(request.body)
with transaction.atomic(): with transaction.atomic():
payment_type = data.get('payment_type', 'cash')
paid_amount = decimal.Decimal(str(data.get('paid_amount', 0)))
total_amount = decimal.Decimal(str(data.get('total_amount', 0)))
status = 'paid'
if payment_type == 'credit':
status = 'unpaid'
elif payment_type == 'partial' or (paid_amount < total_amount and paid_amount > 0):
status = 'partial'
elif paid_amount == 0 and total_amount > 0:
status = 'unpaid'
purchase = Purchase.objects.create( purchase = Purchase.objects.create(
supplier_id=data.get('supplier_id') or None, supplier_id=data.get('supplier_id') or None,
total_amount=data.get('total_amount', 0), invoice_number=data.get('invoice_number', ''),
paid_amount=data.get('paid_amount', 0), total_amount=total_amount,
paid_amount=paid_amount,
payment_type=payment_type,
due_date=data.get('due_date') or None,
notes=data.get('notes', ''),
created_by=request.user, created_by=request.user,
status='paid' if data.get('payment_type') == 'cash' else 'partial' status=status
) )
for item in data.get('items', []): for item in data.get('items', []):
PurchaseItem.objects.create( PurchaseItem.objects.create(
purchase=purchase, purchase=purchase,
product_id=item['id'], product_id=item['id'],
quantity=item['quantity'], quantity=item['quantity'],
cost_price=item['cost'], cost_price=item['price'],
line_total=float(item['quantity']) * float(item['cost']) line_total=float(item['quantity']) * float(item['price'])
) )
# Increase Stock # Increase Stock
Product.objects.filter(pk=item['id']).update(stock_quantity=F('stock_quantity') + item['quantity']) Product.objects.filter(pk=item['id']).update(stock_quantity=F('stock_quantity') + item['quantity'])

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

View File

@ -57,6 +57,7 @@ body {
position: sticky; position: sticky;
top: 0; top: 0;
height: 100vh; height: 100vh;
overflow-y: auto;
} }
#sidebar.active { #sidebar.active {
@ -64,16 +65,16 @@ body {
} }
#sidebar .sidebar-header { #sidebar .sidebar-header {
padding: 20px; padding: 15px 20px;
background: var(--meezan-sidebar-bg); background: var(--meezan-sidebar-bg);
} }
#sidebar ul.components { #sidebar ul.components {
padding: 20px 0; padding: 10px 0;
} }
#sidebar ul li a { #sidebar ul li a {
padding: 12px 25px; padding: 8px 25px;
font-size: 1rem; font-size: 1rem;
display: flex; display: flex;
align-items: center; align-items: center;
@ -103,7 +104,7 @@ body {
/* Collapsible Sidebar Styles */ /* Collapsible Sidebar Styles */
#sidebar ul.components li.sidebar-group-header > a { #sidebar ul.components li.sidebar-group-header > a {
padding: 12px 25px; padding: 8px 25px;
font-size: 0.85rem; font-size: 0.85rem;
text-transform: uppercase; text-transform: uppercase;
font-weight: 700; font-weight: 700;

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB