From 441b04ab7806ca948b979d25837f369d4e85e191 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Tue, 10 Feb 2026 03:46:50 +0000 Subject: [PATCH] after changes --- core/__pycache__/views.cpython-311.pyc | Bin 43732 -> 74279 bytes core/views.py | 850 ++++++++++++++++++++----- patch_payments_v2.py | 211 ++++++ 3 files changed, 885 insertions(+), 176 deletions(-) create mode 100644 patch_payments_v2.py diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index 6874f2f114f67fb1a1d7e05eafc8a2a90a3ebc73..b57f53e9ef57c761816fb0851f8459e4abfb3f38 100644 GIT binary patch literal 74279 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 literal 43732 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 q7$r9nMz!Hnq709U#pfa8HIpWh$xshR 0 else 'unpaid') + purchase.save() + + if pay_amount > 0: + PurchasePayment.objects.create( + purchase=purchase, + amount=pay_amount, + payment_method_id=data.get('payment_method_id'), + created_by=request.user + ) + + return JsonResponse({'success': True, 'purchase_id': purchase.id}) + except Exception as e: + return JsonResponse({'success': False, 'error': str(e)}) + +@login_required +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, + 'payment_methods': PaymentMethod.objects.filter(is_active=True) + }) + +@login_required +def delete_purchase(request, pk): + get_object_or_404(Purchase, pk=pk).delete() + messages.success(request, "Purchase deleted") + return redirect('purchases') + +@login_required +def edit_purchase(request, pk): + # Stub for now + return redirect('purchases') # --- Quotations --- @login_required -def quotations(request): return render(request, 'core/quotations.html') +def quotations(request): + quotations = Quotation.objects.all().order_by('-created_at') + return render(request, 'core/quotations.html', {'quotations': quotations}) + @login_required -def quotation_create(request): return render(request, 'core/quotation_create.html') -@login_required -def quotation_detail(request, pk): return render(request, 'core/quotation_detail.html') -@login_required -def convert_quotation_to_invoice(request, pk): return redirect('invoices') -@login_required -def delete_quotation(request, pk): return redirect('quotations') +def quotation_create(request): + return render(request, 'core/quotation_create.html', { + 'customers': Customer.objects.all(), + 'products': Product.objects.all(), + 'site_settings': SystemSetting.objects.first() + }) + @csrf_exempt -def create_quotation_api(request): return JsonResponse({'success': True}) +@login_required +def create_quotation_api(request): + if request.method != 'POST': return JsonResponse({'success': False}) + try: + data = json.loads(request.body) + with transaction.atomic(): + q = Quotation.objects.create( + customer_id=data.get('customer_id'), + total_amount=0, + created_by=request.user, + notes=data.get('notes', ''), + quotation_number=f"QT-{timezone.now().strftime('%Y%m%d%H%M%S')}" + ) + total = 0 + for item in data.get('items', []): + qty = decimal.Decimal(str(item['quantity'])) + price = decimal.Decimal(str(item['price'])) + line = qty * price + total += line + QuotationItem.objects.create(quotation=q, product_id=item['id'], quantity=qty, unit_price=price, line_total=line) + q.total_amount = total + q.save() + return JsonResponse({'success': True, 'quotation_id': q.id}) + except Exception as e: + return JsonResponse({'success': False, 'error': str(e)}) + +@login_required +def quotation_detail(request, pk): + quotation = get_object_or_404(Quotation, pk=pk) + return render(request, 'core/quotation_detail.html', { + 'quotation': quotation, + 'settings': SystemSetting.objects.first(), + 'amount_in_words': number_to_words_en(quotation.total_amount) + }) + +@login_required +def convert_quotation_to_invoice(request, pk): + # Logic to convert quotation to sale would go here + # For now, just stub it + messages.info(request, "Conversion logic not yet fully implemented") + return redirect('quotations') + +@login_required +def delete_quotation(request, pk): + get_object_or_404(Quotation, pk=pk).delete() + return redirect('quotations') # --- Invoices (Sales) --- @login_required -def invoice_create(request): return render(request, 'core/invoice_create.html') +def invoice_create(request): + settings = SystemSetting.objects.first() + context = { + 'products': Product.objects.filter(is_active=True), + 'customers': Customer.objects.all(), + 'payment_methods': PaymentMethod.objects.filter(is_active=True), + 'site_settings': settings, + 'decimal_places': settings.decimal_places if settings else 3 + } + return render(request, 'core/invoice_create.html', context) + @login_required -def invoice_detail(request, pk): return render(request, 'core/invoice_detail.html') +def invoice_detail(request, pk): + sale = get_object_or_404(Sale, pk=pk) + settings = SystemSetting.objects.first() + amount_in_words = number_to_words_en(sale.total_amount) + return render(request, 'core/invoice_detail.html', { + 'sale': sale, + 'settings': settings, + 'amount_in_words': amount_in_words, + 'payment_methods': PaymentMethod.objects.filter(is_active=True) + }) + @login_required -def add_sale_payment(request, pk): return redirect('invoices') -@login_required -def delete_sale(request, pk): return redirect('invoices') -@login_required -def sale_receipt(request, pk): return render(request, 'core/sale_receipt.html') -@login_required -def edit_invoice(request, pk): return redirect('invoices') +def delete_sale(request, pk): + get_object_or_404(Sale, pk=pk).delete() + return redirect('invoices') + @csrf_exempt def update_sale_api(request, pk): return JsonResponse({'success': True}) -@login_required -def customer_payments(request): return render(request, 'core/customer_payments.html') -@login_required -def customer_payment_receipt(request, pk): return render(request, 'core/payment_receipt.html') - -# --- Held Sales --- -@csrf_exempt -def hold_sale_api(request): return JsonResponse({'success': True}) -@csrf_exempt -def get_held_sales_api(request): return JsonResponse({'sales': []}) -@csrf_exempt -def recall_held_sale_api(request, pk): return JsonResponse({'success': True}) -@csrf_exempt -def delete_held_sale_api(request, pk): return JsonResponse({'success': True}) # --- Expenses --- @login_required -def expenses_view(request): return render(request, 'core/expenses.html') +def expenses_view(request): + expenses = Expense.objects.all().order_by('-date') + return render(request, 'core/expenses.html', { + 'expenses': expenses, + 'categories': ExpenseCategory.objects.all(), + 'payment_methods': PaymentMethod.objects.all() + }) + @login_required -def expense_create_view(request): return redirect('expenses') +def expense_create_view(request): + if request.method == 'POST': + form = ExpenseForm(request.POST, request.FILES) + if form.is_valid(): + form.save() + messages.success(request, "Expense added") + return redirect('expenses') + return redirect('expenses') + @login_required def expense_edit_view(request, pk): return redirect('expenses') @login_required -def expense_delete_view(request, pk): return redirect('expenses') +def expense_delete_view(request, pk): + get_object_or_404(Expense, pk=pk).delete() + return redirect('expenses') @login_required def expense_categories_view(request): return render(request, 'core/expense_categories.html') @login_required @@ -544,21 +874,223 @@ def test_whatsapp_connection(request): return JsonResponse({'success': True}) # --- LPO --- @login_required -def lpo_list(request): return render(request, 'core/lpo_list.html') +def lpo_list(request): return render(request, 'core/lpo_list.html', {'lpos': PurchaseOrder.objects.all()}) @login_required -def lpo_create(request): return render(request, 'core/lpo_create.html') +def lpo_create(request): + return render(request, 'core/lpo_create.html', { + 'suppliers': Supplier.objects.all(), + 'products': Product.objects.all(), + 'site_settings': SystemSetting.objects.first() + }) + +@csrf_exempt @login_required -def lpo_detail(request, pk): return render(request, 'core/lpo_detail.html') +def create_lpo_api(request): + if request.method != 'POST': return JsonResponse({'success': False}) + try: + data = json.loads(request.body) + with transaction.atomic(): + lpo = PurchaseOrder.objects.create( + supplier_id=data.get('supplier_id'), + total_amount=0, + created_by=request.user, + lpo_number=f"LPO-{timezone.now().strftime('%Y%m%d%H%M%S')}" + ) + total = 0 + for item in data.get('items', []): + qty = decimal.Decimal(str(item['quantity'])) + cost = decimal.Decimal(str(item.get('price', 0))) + line = qty * cost + total += line + PurchaseOrderItem.objects.create(purchase_order=lpo, product_id=item['id'], quantity=qty, cost_price=cost, line_total=line) + lpo.total_amount = total + lpo.save() + return JsonResponse({'success': True, 'lpo_id': lpo.id}) + except Exception as e: + return JsonResponse({'success': False, 'error': str(e)}) + +@login_required +def lpo_detail(request, pk): + lpo = get_object_or_404(PurchaseOrder, pk=pk) + return render(request, 'core/lpo_detail.html', { + 'lpo': lpo, + 'settings': SystemSetting.objects.first() + }) + @login_required def convert_lpo_to_purchase(request, pk): return redirect('lpo_list') @login_required -def lpo_delete(request, pk): return redirect('lpo_list') +def lpo_delete(request, pk): + get_object_or_404(PurchaseOrder, pk=pk).delete() + return redirect('lpo_list') + +# --- Sales Returns --- +@login_required +def sales_returns(request): return render(request, 'core/sales_returns.html', {'returns': SaleReturn.objects.all()}) +@login_required +def sale_return_create(request): return render(request, 'core/sale_return_create.html') +@login_required +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 + }) +@login_required +def delete_sale_return(request, pk): return redirect('sales_returns') @csrf_exempt -def create_lpo_api(request): return JsonResponse({'success': True}) +def create_sale_return_api(request): return JsonResponse({'success': True}) + +# --- Purchase Returns --- +@login_required +def purchase_returns(request): return render(request, 'core/purchase_returns.html', {'returns': PurchaseReturn.objects.all()}) +@login_required +def purchase_return_create(request): return render(request, 'core/purchase_return_create.html') +@login_required +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 + }) +@login_required +def delete_purchase_return(request, pk): return redirect('purchase_returns') +@csrf_exempt +def create_purchase_return_api(request): return JsonResponse({'success': True}) + +# --- Other Stubs --- +@login_required +def customer_statement(request): return render(request, 'core/customer_statement.html') +@login_required +def supplier_statement(request): return render(request, 'core/supplier_statement.html') +@login_required +def cashflow_report(request): return render(request, 'core/cashflow_report.html') +@login_required +def expense_list(request): return render(request, 'core/expenses.html') +@login_required +def purchase_list(request): return render(request, 'core/purchases.html') +@login_required +def suppliers_list(request): return render(request, 'core/suppliers.html') +@login_required +def customers_list(request): return render(request, 'core/customers.html') +@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') +@csrf_exempt +def pos_sync_update(request): return JsonResponse({'status': 'ok'}) +@csrf_exempt +def pos_sync_state(request): return JsonResponse({'state': {}}) +@login_required +def customer_display(request): return render(request, 'core/customer_display.html') +@login_required +def supplier_payments(request): return render(request, 'core/supplier_payments.html') +@csrf_exempt +def update_purchase_api(request, pk): return JsonResponse({'success': True}) + +@login_required +def add_sale_payment(request, pk): + sale = get_object_or_404(Sale, pk=pk) + if request.method == 'POST': + try: + amount = decimal.Decimal(request.POST.get('amount', 0)) + payment_method_id = request.POST.get('payment_method_id') + notes = request.POST.get('notes', '') + + if amount > 0: + with transaction.atomic(): + SalePayment.objects.create( + sale=sale, + amount=amount, + payment_method_id=payment_method_id, + created_by=request.user, + notes=notes + ) + + # Recalculate totals + total_paid = SalePayment.objects.filter(sale=sale).aggregate(Sum('amount'))['amount__sum'] or 0 + sale.paid_amount = total_paid + sale.balance_due = sale.total_amount - total_paid + + if sale.balance_due <= 0: + sale.status = 'paid' + elif sale.paid_amount > 0: + sale.status = 'partial' + else: + sale.status = 'unpaid' + + sale.save() + messages.success(request, f"Payment of {amount} recorded successfully.") + else: + messages.error(request, "Amount must be greater than 0.") + except Exception as e: + messages.error(request, f"Error recording payment: {e}") + + return redirect('invoices') + +@login_required +def sale_receipt(request, pk): return render(request, 'core/sale_receipt.html') +@login_required +def edit_invoice(request, pk): return redirect('invoices') +@login_required +def customer_payments(request): return render(request, 'core/customer_payments.html') +@login_required +def customer_payment_receipt(request, pk): return render(request, 'core/payment_receipt.html') +@csrf_exempt +def hold_sale_api(request): return JsonResponse({'success': True}) +@csrf_exempt +def get_held_sales_api(request): return JsonResponse({'sales': []}) +@csrf_exempt +def recall_held_sale_api(request, pk): return JsonResponse({'success': True}) +@csrf_exempt +def delete_held_sale_api(request, pk): return JsonResponse({'success': True}) + +@login_required +def add_purchase_payment(request, pk): + purchase = get_object_or_404(Purchase, pk=pk) + if request.method == 'POST': + try: + amount = decimal.Decimal(request.POST.get('amount', 0)) + payment_method_id = request.POST.get('payment_method_id') + notes = request.POST.get('notes', '') + + if amount > 0: + with transaction.atomic(): + PurchasePayment.objects.create( + purchase=purchase, + amount=amount, + payment_method_id=payment_method_id, + created_by=request.user, + notes=notes + ) + + # Recalculate totals + total_paid = PurchasePayment.objects.filter(purchase=purchase).aggregate(Sum('amount'))['amount__sum'] or 0 + purchase.paid_amount = total_paid + purchase.balance_due = purchase.total_amount - total_paid + + if purchase.balance_due <= 0: + purchase.status = 'paid' + elif purchase.paid_amount > 0: + purchase.status = 'partial' + else: + purchase.status = 'unpaid' + + purchase.save() + messages.success(request, f"Payment of {amount} recorded successfully.") + else: + messages.error(request, "Amount must be greater than 0.") + except Exception as e: + messages.error(request, f"Error recording payment: {e}") + + return redirect('purchases') + @login_required def cashier_registry(request): return render(request, 'core/cashier_registry.html') - -# --- Sessions --- @login_required def cashier_session_list(request): return render(request, 'core/session_list.html') @login_required @@ -567,53 +1099,19 @@ def start_session(request): return redirect('pos') def close_session(request): return redirect('index') @login_required def session_detail(request, pk): return render(request, 'core/session_detail.html') - -# --- Reports --- @login_required def reports(request): return render(request, 'core/reports.html') @login_required def expense_report(request): return render(request, 'core/expense_report.html') @login_required def export_expenses_excel(request): return redirect('expenses') - -# --- Sales Returns --- @login_required -def sales_returns(request): return render(request, 'core/sales_returns.html') +def profile_view(request): return render(request, 'core/profile.html') @login_required -def sale_return_create(request): return render(request, 'core/sale_return_create.html') -@login_required -def sale_return_detail(request, pk): return render(request, 'core/sale_return_detail.html') -@login_required -def delete_sale_return(request, pk): return redirect('sales_returns') -@csrf_exempt -def create_sale_return_api(request): return JsonResponse({'success': True}) - -# --- Purchase Returns --- -@login_required -def purchase_returns(request): return render(request, 'core/purchase_returns.html') -@login_required -def purchase_return_create(request): return render(request, 'core/purchase_return_create.html') -@login_required -def purchase_return_detail(request, pk): return render(request, 'core/purchase_return_detail.html') -@login_required -def delete_purchase_return(request, pk): return redirect('purchase_returns') -@csrf_exempt -def create_purchase_return_api(request): return JsonResponse({'success': True}) - -# --- MISSING VIEWS --- -@login_required -def profile_view(request): - return render(request, 'core/profile.html') - -@login_required -def user_management(request): - return render(request, 'core/user_management.html') - +def user_management(request): return render(request, 'core/user_management.html') @csrf_exempt @login_required -def group_details_api(request, pk): - return JsonResponse({'success': True, 'group': {}}) - +def group_details_api(request, pk): return JsonResponse({'success': True, 'group': {}}) @csrf_exempt @login_required def search_customers_api(request): diff --git a/patch_payments_v2.py b/patch_payments_v2.py new file mode 100644 index 0000000..82e800b --- /dev/null +++ b/patch_payments_v2.py @@ -0,0 +1,211 @@ +import os +import decimal +from django.db import transaction +from django.db.models import Sum + +file_path = 'core/views.py' +with open(file_path, 'r') as f: + content = f.read() + +# 1. Update invoice_list +old_invoice_list = """@login_required +def invoice_list(request): + sales = Sale.objects.all().order_by('-created_at') + paginator = Paginator(sales, 25) + return render(request, 'core/invoices.html', { + 'sales': paginator.get_page(request.GET.get('page')), + 'customers': Customer.objects.all(), + 'site_settings': SystemSetting.objects.first() + })""" + +new_invoice_list = """@login_required +def invoice_list(request): + sales = Sale.objects.all().order_by('-created_at') + paginator = Paginator(sales, 25) + return render(request, 'core/invoices.html', { + 'sales': paginator.get_page(request.GET.get('page')), + 'customers': Customer.objects.all(), + 'site_settings': SystemSetting.objects.first(), + 'payment_methods': PaymentMethod.objects.filter(is_active=True) + })""" + +if old_invoice_list in content: + content = content.replace(old_invoice_list, new_invoice_list) +else: + print("Could not find old_invoice_list") + +# 2. Update purchases +old_purchases = """@login_required +def purchases(request): + purchases = Purchase.objects.all().order_by('-created_at') + paginator = Paginator(purchases, 25) + return render(request, 'core/purchases.html', {'purchases': paginator.get_page(request.GET.get('page'))})""" + +new_purchases = """@login_required +def purchases(request): + purchases = Purchase.objects.all().order_by('-created_at') + paginator = Paginator(purchases, 25) + return render(request, 'core/purchases.html', { + 'purchases': paginator.get_page(request.GET.get('page')), + 'payment_methods': PaymentMethod.objects.filter(is_active=True) + })""" + +if old_purchases in content: + content = content.replace(old_purchases, new_purchases) +else: + print("Could not find old_purchases") + +# 3. Update invoice_detail +old_invoice_detail = """@login_required +def invoice_detail(request, pk): + sale = get_object_or_404(Sale, pk=pk) + settings = SystemSetting.objects.first() + amount_in_words = number_to_words_en(sale.total_amount) + return render(request, 'core/invoice_detail.html', { + 'sale': sale, + 'settings': settings, + 'amount_in_words': amount_in_words + })""" + +new_invoice_detail = """@login_required +def invoice_detail(request, pk): + sale = get_object_or_404(Sale, pk=pk) + settings = SystemSetting.objects.first() + amount_in_words = number_to_words_en(sale.total_amount) + return render(request, 'core/invoice_detail.html', { + 'sale': sale, + 'settings': settings, + 'amount_in_words': amount_in_words, + 'payment_methods': PaymentMethod.objects.filter(is_active=True) + })""" + +if old_invoice_detail in content: + content = content.replace(old_invoice_detail, new_invoice_detail) +else: + print("Could not find old_invoice_detail") + +# 4. Update purchase_detail +old_purchase_detail = """@login_required +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 + })""" + +new_purchase_detail = """@login_required +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, + 'payment_methods': PaymentMethod.objects.filter(is_active=True) + })""" + +if old_purchase_detail in content: + content = content.replace(old_purchase_detail, new_purchase_detail) +else: + print("Could not find old_purchase_detail") + +# 5. Replace add_sale_payment stub +old_add_sale_payment = """@login_required +def add_sale_payment(request, pk): return redirect('invoices')""" + +new_add_sale_payment = """@login_required +def add_sale_payment(request, pk): + sale = get_object_or_404(Sale, pk=pk) + if request.method == 'POST': + try: + amount = decimal.Decimal(request.POST.get('amount', 0)) + payment_method_id = request.POST.get('payment_method_id') + notes = request.POST.get('notes', '') + + if amount > 0: + with transaction.atomic(): + SalePayment.objects.create( + sale=sale, + amount=amount, + payment_method_id=payment_method_id, + created_by=request.user, + notes=notes + ) + + # Recalculate totals + total_paid = SalePayment.objects.filter(sale=sale).aggregate(Sum('amount'))['amount__sum'] or 0 + sale.paid_amount = total_paid + sale.balance_due = sale.total_amount - total_paid + + if sale.balance_due <= 0: + sale.status = 'paid' + elif sale.paid_amount > 0: + sale.status = 'partial' + else: + sale.status = 'unpaid' + + sale.save() + messages.success(request, f"Payment of {amount} recorded successfully.") + else: + messages.error(request, "Amount must be greater than 0.") + except Exception as e: + messages.error(request, f"Error recording payment: {e}") + + return redirect('invoices')""" + +if old_add_sale_payment in content: + content = content.replace(old_add_sale_payment, new_add_sale_payment) +else: + print("Could not find old_add_sale_payment") + +# 6. Replace add_purchase_payment stub +old_add_purchase_payment = """@login_required +def add_purchase_payment(request, pk): return redirect('purchases')""" + +new_add_purchase_payment = """@login_required +def add_purchase_payment(request, pk): + purchase = get_object_or_404(Purchase, pk=pk) + if request.method == 'POST': + try: + amount = decimal.Decimal(request.POST.get('amount', 0)) + payment_method_id = request.POST.get('payment_method_id') + notes = request.POST.get('notes', '') + + if amount > 0: + with transaction.atomic(): + PurchasePayment.objects.create( + purchase=purchase, + amount=amount, + payment_method_id=payment_method_id, + created_by=request.user, + notes=notes + ) + + # Recalculate totals + total_paid = PurchasePayment.objects.filter(purchase=purchase).aggregate(Sum('amount'))['amount__sum'] or 0 + purchase.paid_amount = total_paid + purchase.balance_due = purchase.total_amount - total_paid + + if purchase.balance_due <= 0: + purchase.status = 'paid' + elif purchase.paid_amount > 0: + purchase.status = 'partial' + else: + purchase.status = 'unpaid' + + purchase.save() + messages.success(request, f"Payment of {amount} recorded successfully.") + else: + messages.error(request, "Amount must be greater than 0.") + except Exception as e: + messages.error(request, f"Error recording payment: {e}") + + return redirect('purchases')""" + +if old_add_purchase_payment in content: + content = content.replace(old_add_purchase_payment, new_add_purchase_payment) +else: + print("Could not find old_add_purchase_payment") + +with open(file_path, 'w') as f: + f.write(content)