diff --git a/Aptfile b/Aptfile
deleted file mode 100644
index 692b627..0000000
--- a/Aptfile
+++ /dev/null
@@ -1,7 +0,0 @@
-libglib2.0-0
-libgobject-2.0-0
-libpango-1.0-0
-libpangocairo-1.0-0
-libcairo2
-libharfbuzz0b
-libfontconfig1
diff --git a/Procfile b/Procfile
deleted file mode 100644
index 8a7bba7..0000000
--- a/Procfile
+++ /dev/null
@@ -1 +0,0 @@
-web: gunicorn config.wsgi --log-file -
diff --git a/append_reports.py b/append_reports.py
deleted file mode 100644
index bc69e8f..0000000
--- a/append_reports.py
+++ /dev/null
@@ -1,24 +0,0 @@
-
-import os
-
-file_path = 'core/views.py'
-
-missing_reports = r"""
-
-@login_required
-def cashflow_report(request):
- return render(request, 'core/cashflow_report.html')
-
-@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')
-"""
-
-with open(file_path, 'a') as f:
- f.write(missing_reports)
-
-print("Appended missing reports to core/views.py")
diff --git a/apply_patch.py b/apply_patch.py
deleted file mode 100644
index 1412fb5..0000000
--- a/apply_patch.py
+++ /dev/null
@@ -1,58 +0,0 @@
-
-import re
-
-with open('core/views.py', 'r') as f:
- content = f.read()
-
-with open('core/patch_views_vat.py', 'r') as f:
- new_func = f.read()
-
-# Regex to find the function definition
-# It starts with @csrf_exempt\ndef create_sale_api(request):
-# And ends before the next function definition (which likely starts with @ or def)
-pattern = r"@csrf_exempt\s+def create_sale_api(request):.*?return JsonResponse({'success': False, 'error': 'Invalid request'}, status=405)"
-
-# Note: The pattern needs to match the indentation and multiline content.
-# Since regex for code blocks is tricky, I will use a simpler approach:
-# 1. Read the file lines.
-# 2. Find start line of create_sale_api.
-# 3. Find the end line (start of next function or end of file).
-# 4. Replace lines.
-
-lines = content.splitlines()
-start_index = -1
-end_index = -1
-
-for i, line in enumerate(lines):
- if line.strip() == "def create_sale_api(request):":
- # Check if previous line is decorator
- if i > 0 and lines[i-1].strip() == "@csrf_exempt":
- start_index = i - 1
- else:
- start_index = i
- break
-
-if start_index != -1:
- # Find the next function or end
- # We look for next line starting with 'def ' or '@' at top level
- for i in range(start_index + 1, len(lines)):
- if lines[i].startswith("def ") or lines[i].startswith("@"):
- end_index = i
- break
- if end_index == -1:
- end_index = len(lines)
-
- # Replace
- new_lines = new_func.splitlines()
- # Ensure new lines have correct indentation if needed (but views.py is top level mostly)
-
- # We need to preserve the imports and structure.
- # The new_func is complete.
-
- final_lines = lines[:start_index] + new_lines + lines[end_index:]
-
- with open('core/views.py', 'w') as f:
- f.write('\n'.join(final_lines))
- print("Successfully patched create_sale_api")
-else:
- print("Could not find create_sale_api function")
diff --git a/config/wsgi.py b/config/wsgi.py
index 61408cc..ec25885 100644
--- a/config/wsgi.py
+++ b/config/wsgi.py
@@ -20,21 +20,6 @@ try:
except ImportError:
pass
-# --- WeasyPrint Library Preloading ---
-import ctypes
-import ctypes.util
-
-# Try to find and load libraries dynamically to help WeasyPrint on different platforms
-libs_to_load = ['glib-2.0', 'gobject-2.0', 'fontconfig', 'cairo', 'pango-1.0', 'pangoft2-1.0']
-for lib_name in libs_to_load:
- try:
- path = ctypes.util.find_library(lib_name)
- if path:
- ctypes.CDLL(path)
- except Exception:
- pass
-# -------------------------------------
-
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
from django.core.wsgi import get_wsgi_application
diff --git a/core/__pycache__/forms_import.cpython-311.pyc b/core/__pycache__/forms_import.cpython-311.pyc
index 200f5f2..2f953e9 100644
Binary files a/core/__pycache__/forms_import.cpython-311.pyc and b/core/__pycache__/forms_import.cpython-311.pyc differ
diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc
index 68a3e28..4e9bdf3 100644
Binary files a/core/__pycache__/views.cpython-311.pyc and b/core/__pycache__/views.cpython-311.pyc differ
diff --git a/core/__pycache__/views_import.cpython-311.pyc b/core/__pycache__/views_import.cpython-311.pyc
index 2c4af01..0010464 100644
Binary files a/core/__pycache__/views_import.cpython-311.pyc and b/core/__pycache__/views_import.cpython-311.pyc differ
diff --git a/core/patch_views.py b/core/patch_views.py
deleted file mode 100644
index 4d0eb9e..0000000
--- a/core/patch_views.py
+++ /dev/null
@@ -1,76 +0,0 @@
-@login_required
-def send_invoice_whatsapp(request):
- if request.method != 'POST':
- return JsonResponse({'success': False, 'error': 'Method not allowed'})
-
- try:
- # Handle JSON payload
- data = json.loads(request.body)
- sale_id = data.get('sale_id')
- phone = data.get('phone')
- pdf_data = data.get('pdf_data') # Base64 string
- except json.JSONDecodeError:
- # Fallback to Form Data
- sale_id = request.POST.get('sale_id')
- phone = request.POST.get('phone')
- pdf_data = None
-
- if not sale_id:
- return JsonResponse({'success': False, 'error': 'Sale ID missing'})
-
- sale = get_object_or_404(Sale, pk=sale_id)
-
- if not phone:
- if sale.customer and sale.customer.phone:
- phone = sale.customer.phone
- else:
- return JsonResponse({'success': False, 'error': 'Phone number missing'})
-
- try:
- # If PDF data is present, save and send document
- if pdf_data:
- # Remove header if present (data:application/pdf;base64,)
- if ',' in pdf_data:
- pdf_data = pdf_data.split(',')[1]
-
- file_data = base64.b64decode(pdf_data)
- dir_path = os.path.join(django_settings.MEDIA_ROOT, 'temp_invoices')
- os.makedirs(dir_path, exist_ok=True)
-
- filename = f"invoice_{sale.id}_{int(timezone.now().timestamp())}.pdf"
- file_path = os.path.join(dir_path, filename)
-
- with open(file_path, 'wb') as f:
- f.write(file_data)
-
- # Construct URL
- file_url = request.build_absolute_uri(django_settings.MEDIA_URL + 'temp_invoices/' + filename)
-
- success, response_msg = send_whatsapp_document(phone, file_url, caption=f"Invoice #{sale.invoice_number or sale.id}")
-
- else:
- # Fallback to Text Link
- receipt_url = request.build_absolute_uri(reverse('sale_receipt', args=[sale.pk]))
-
- message = (
- f"Hello {sale.customer.name if sale.customer else 'Guest'},
-"
- f"Here is your invoice #{sale.invoice_number or sale.id}.
-"
- f"Total: {sale.total_amount}
-"
- f"View Invoice: {receipt_url}
-"
- f"Thank you for your business!"
- )
-
- success, response_msg = send_whatsapp_message(phone, message)
-
- if success:
- return JsonResponse({'success': True, 'message': response_msg})
- else:
- return JsonResponse({'success': False, 'error': response_msg})
-
- except Exception as e:
- logger.error(f"WhatsApp Error: {e}")
- return JsonResponse({'success': False, 'error': str(e)}) # Changed to str(e) for clarity
diff --git a/core/patch_views_missing.py b/core/patch_views_missing.py
deleted file mode 100644
index 8cab7e4..0000000
--- a/core/patch_views_missing.py
+++ /dev/null
@@ -1,92 +0,0 @@
-
-@login_required
-def customer_statement(request):
- customers = Customer.objects.all().order_by('name')
- selected_customer = None
- sales = []
-
- customer_id = request.GET.get('customer')
- start_date = request.GET.get('start_date')
- end_date = request.GET.get('end_date')
-
- if customer_id:
- selected_customer = get_object_or_404(Customer, id=customer_id)
- sales = Sale.objects.filter(customer=selected_customer).order_by('-created_at')
-
- if start_date:
- sales = sales.filter(created_at__date__gte=start_date)
- if end_date:
- sales = sales.filter(created_at__date__lte=end_date)
-
- context = {
- 'customers': customers,
- 'selected_customer': selected_customer,
- 'sales': sales,
- 'start_date': start_date,
- 'end_date': end_date
- }
- return render(request, 'core/customer_statement.html', context)
-
-@login_required
-def supplier_statement(request):
- suppliers = Supplier.objects.all().order_by('name')
- selected_supplier = None
- purchases = []
-
- supplier_id = request.GET.get('supplier')
- start_date = request.GET.get('start_date')
- end_date = request.GET.get('end_date')
-
- if supplier_id:
- selected_supplier = get_object_or_404(Supplier, id=supplier_id)
- purchases = Purchase.objects.filter(supplier=selected_supplier).order_by('-created_at')
-
- if start_date:
- purchases = purchases.filter(created_at__date__gte=start_date)
- if end_date:
- purchases = purchases.filter(created_at__date__lte=end_date)
-
- context = {
- 'suppliers': suppliers,
- 'selected_supplier': selected_supplier,
- 'purchases': purchases,
- 'start_date': start_date,
- 'end_date': end_date
- }
- return render(request, 'core/supplier_statement.html', context)
-
-@login_required
-def cashflow_report(request):
- # Simplified Cashflow
- start_date = request.GET.get('start_date')
- end_date = request.GET.get('end_date')
-
- sales = Sale.objects.all()
- expenses = Expense.objects.all()
- purchases = Purchase.objects.all()
-
- if start_date:
- sales = sales.filter(created_at__date__gte=start_date)
- expenses = expenses.filter(date__gte=start_date)
- purchases = purchases.filter(created_at__date__gte=start_date)
-
- if end_date:
- sales = sales.filter(created_at__date__lte=end_date)
- expenses = expenses.filter(date__lte=end_date)
- purchases = purchases.filter(created_at__date__lte=end_date)
-
- total_sales = sales.aggregate(total=Sum('total_amount'))['total'] or 0
- total_expenses = expenses.aggregate(total=Sum('amount'))['total'] or 0
- total_purchases = purchases.aggregate(total=Sum('total_amount'))['total'] or 0
-
- net_profit = total_sales - total_expenses - total_purchases
-
- context = {
- 'total_sales': total_sales,
- 'total_expenses': total_expenses,
- 'total_purchases': total_purchases,
- 'net_profit': net_profit,
- 'start_date': start_date,
- 'end_date': end_date
- }
- return render(request, 'core/cashflow_report.html', context)
diff --git a/core/patch_views_pos.py b/core/patch_views_pos.py
deleted file mode 100644
index 414a25d..0000000
--- a/core/patch_views_pos.py
+++ /dev/null
@@ -1,35 +0,0 @@
-@login_required
-def pos(request):
- from .models import CashierSession
- # Check for active session
- active_session = CashierSession.objects.filter(user=request.user, status='active').first()
- if not active_session:
- # Check if user is a cashier (assigned to a counter)
- if hasattr(request.user, 'counter_assignment'):
- messages.warning(request, _("Please open a session to start selling."))
- return redirect('start_session')
-
- settings = SystemSetting.objects.first()
- products = Product.objects.filter(is_active=True)
-
- if not settings or not settings.allow_zero_stock_sales:
- products = products.filter(stock_quantity__gt=0)
-
- customers = Customer.objects.all()
- categories = Category.objects.all()
- payment_methods = PaymentMethod.objects.filter(is_active=True)
-
- # Ensure at least Cash exists
- if not payment_methods.exists():
- PaymentMethod.objects.create(name_en="Cash", name_ar="نقدي", is_active=True)
- payment_methods = PaymentMethod.objects.filter(is_active=True)
-
- context = {
- 'products': products,
- 'customers': customers,
- 'categories': categories,
- 'payment_methods': payment_methods,
- 'settings': settings,
- 'active_session': active_session
- }
- return render(request, 'core/pos.html', context)
diff --git a/core/patch_views_sales_list.py b/core/patch_views_sales_list.py
deleted file mode 100644
index 7c06679..0000000
--- a/core/patch_views_sales_list.py
+++ /dev/null
@@ -1,31 +0,0 @@
-@login_required
-def invoice_list(request):
- sales = Sale.objects.all().order_by('-created_at')
-
- # Filter by date range
- start_date = request.GET.get('start_date')
- end_date = request.GET.get('end_date')
- if start_date:
- sales = sales.filter(created_at__date__gte=start_date)
- if end_date:
- sales = sales.filter(created_at__date__lte=end_date)
-
- # Filter by customer
- customer_id = request.GET.get('customer')
- if customer_id:
- sales = sales.filter(customer_id=customer_id)
-
- # Filter by status
- status = request.GET.get('status')
- if status:
- sales = sales.filter(status=status)
-
- paginator = Paginator(sales, 25)
-
- context = {
- 'sales': paginator.get_page(request.GET.get('page')),
- 'customers': Customer.objects.all(),
- 'payment_methods': PaymentMethod.objects.filter(is_active=True),
- 'site_settings': SystemSetting.objects.first(),
- }
- return render(request, 'core/invoices.html', context)
\ No newline at end of file
diff --git a/core/patch_views_vat.py b/core/patch_views_vat.py
deleted file mode 100644
index a883edc..0000000
--- a/core/patch_views_vat.py
+++ /dev/null
@@ -1,161 +0,0 @@
-@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', [])
-
- # Retrieve amounts
- subtotal = data.get('subtotal', 0)
- vat_amount = data.get('vat_amount', 0)
- 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')
- payment_method_id = data.get('payment_method_id')
- due_date = data.get('due_date')
- notes = data.get('notes', '')
-
- # Loyalty data
- points_to_redeem = data.get('loyalty_points_redeemed', 0)
-
- customer = None
- if customer_id:
- customer = Customer.objects.get(id=customer_id)
-
- if not customer and payment_type != 'cash':
- return JsonResponse({'success': False, 'error': _('Credit or Partial payments are not allowed for Guest customers.')}, status=400)
-
- settings = SystemSetting.objects.first()
- if not settings:
- settings = SystemSetting.objects.create()
-
- loyalty_discount = 0
- if settings.loyalty_enabled and customer and points_to_redeem > 0:
- if customer.loyalty_points >= points_to_redeem:
- loyalty_discount = float(points_to_redeem) * float(settings.currency_per_point)
-
- sale = Sale.objects.create(
- customer=customer,
- invoice_number=invoice_number,
- subtotal=subtotal,
- vat_amount=vat_amount,
- total_amount=total_amount,
- paid_amount=paid_amount,
- balance_due=float(total_amount) - float(paid_amount),
- discount=discount,
- loyalty_points_redeemed=points_to_redeem,
- loyalty_discount_amount=loyalty_discount,
- payment_type=payment_type,
- due_date=due_date if due_date else None,
- notes=notes,
- created_by=request.user
- )
-
- # 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:
- pm = None
- if payment_method_id:
- pm = PaymentMethod.objects.filter(id=payment_method_id).first()
-
- SalePayment.objects.create(
- sale=sale,
- amount=paid_amount,
- payment_method=pm,
- payment_method_name=pm.name_en if pm else payment_type.capitalize(),
- notes="Initial payment",
- created_by=request.user
- )
-
- 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()
-
- # Handle Loyalty Points
- if settings.loyalty_enabled and customer:
- # Earn Points
- points_earned = float(total_amount) * float(settings.points_per_currency)
- if customer.loyalty_tier:
- points_earned *= float(customer.loyalty_tier.point_multiplier)
-
- if points_earned > 0:
- customer.loyalty_points += decimal.Decimal(str(points_earned))
- LoyaltyTransaction.objects.create(
- customer=customer,
- sale=sale,
- transaction_type='earned',
- points=points_earned,
- notes=f"Points earned from Sale #{sale.id}"
- )
-
- # Redeem Points
- if points_to_redeem > 0:
- customer.loyalty_points -= decimal.Decimal(str(points_to_redeem))
- LoyaltyTransaction.objects.create(
- customer=customer,
- sale=sale,
- transaction_type='redeemed',
- points=-points_to_redeem,
- notes=f"Points redeemed for Sale #{sale.id}"
- )
-
- customer.update_tier()
- customer.save()
-
- 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"),
- 'subtotal': float(sale.subtotal),
- 'vat_amount': float(sale.vat_amount),
- 'total': float(sale.total_amount),
- 'discount': float(sale.discount),
- 'paid': float(sale.paid_amount),
- 'balance': float(sale.balance_due),
- 'customer_name': sale.customer.name if sale.customer else 'Guest',
- '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)
\ No newline at end of file
diff --git a/core/pdf_utils.py b/core/pdf_utils.py
new file mode 100644
index 0000000..0ce9f57
--- /dev/null
+++ b/core/pdf_utils.py
@@ -0,0 +1,55 @@
+import os
+import ctypes
+import ctypes.util
+import logging
+
+logger = logging.getLogger(__name__)
+
+def patch_weasyprint_libraries():
+ """
+ Attempts to load required system libraries for WeasyPrint.
+ This helps on systems where libraries are installed but not in the standard search path
+ or have slightly different names.
+ """
+ # Common library names for WeasyPrint dependencies
+ libs_to_load = [
+ ('glib-2.0', ['libglib-2.0.so.0', 'libglib-2.0.so']),
+ ('gobject-2.0', ['libgobject-2.0.so.0', 'libgobject-2.0.so', 'libgobject-2.0-0']),
+ ('fontconfig', ['libfontconfig.so.1', 'libfontconfig.so']),
+ ('cairo', ['libcairo.so.2', 'libcairo.so']),
+ ('pango-1.0', ['libpango-1.0.so.0', 'libpango-1.0.so']),
+ ('pangoft2-1.0', ['libpangoft2-1.0.so.0', 'libpangoft2-1.0.so']),
+ ('harfbuzz', ['libharfbuzz.so.0', 'libharfbuzz.so']),
+ ]
+
+ for lib_id, fallbacks in libs_to_load:
+ try:
+ # First try standard find_library
+ path = ctypes.util.find_library(lib_id)
+ if path:
+ ctypes.CDLL(path)
+ continue
+
+ # If not found, try fallbacks
+ for fallback in fallbacks:
+ try:
+ ctypes.CDLL(fallback)
+ break
+ except OSError:
+ continue
+ except Exception as e:
+ logger.debug(f"Failed to load library {lib_id}: {e}")
+
+# Call it immediately when this module is imported
+patch_weasyprint_libraries()
+
+def get_weasyprint_html():
+ """
+ Safe wrapper for importing WeasyPrint HTML.
+ """
+ try:
+ from weasyprint import HTML
+ return HTML
+ except Exception as e:
+ logger.error(f"Failed to import WeasyPrint HTML: {e}")
+ raise
diff --git a/core/views.py b/core/views.py
index 078e5b4..0b04457 100644
--- a/core/views.py
+++ b/core/views.py
@@ -44,7 +44,8 @@ logger = logging.getLogger(__name__)
# --- Basic Views ---
def test_pdf_view(request):
- from weasyprint import HTML
+ from .pdf_utils import get_weasyprint_html
+ HTML = get_weasyprint_html()
html_string = "
Test PDF
"
pdf = HTML(string=html_string).write_pdf()
return HttpResponse(pdf, content_type='application/pdf')
@@ -1592,7 +1593,8 @@ def get_pdf_context(obj, doc_type):
}
def generate_pdf_file(template, context, request):
- from weasyprint import HTML
+ from .pdf_utils import get_weasyprint_html
+ HTML = get_weasyprint_html()
html_string = render_to_string(template, context, request=request)
base_url = request.build_absolute_uri('/')
return HTML(string=html_string, base_url=base_url).write_pdf()
diff --git a/debug_accounting.py b/debug_accounting.py
deleted file mode 100644
index 1fa0972..0000000
--- a/debug_accounting.py
+++ /dev/null
@@ -1,31 +0,0 @@
-import os
-import django
-os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
-django.setup()
-
-from accounting.models import Account, JournalEntry, JournalItem
-from core.models import Expense
-
-print("Checking Accounts...")
-acc_1000 = Account.objects.filter(code='1000').first()
-acc_5400 = Account.objects.filter(code='5400').first()
-
-print(f"Account 1000 (Cash): {acc_1000}")
-print(f"Account 5400 (General Expense): {acc_5400}")
-
-print("\nChecking Journal Entries for Expenses...")
-expenses = Expense.objects.all()
-for exp in expenses:
- print(f"Expense {exp.id}: {exp.description} - Amount: {exp.amount}")
- # Find linked entry
- from django.contrib.contenttypes.models import ContentType
- ct = ContentType.objects.get_for_model(Expense)
- entries = JournalEntry.objects.filter(content_type=ct, object_id=exp.id)
- for entry in entries:
- print(f" -> JournalEntry {entry.id}: {entry.description}")
- items = entry.items.all()
- if items.exists():
- for item in items:
- print(f" -> Item: {item.account.code} {item.type} {item.amount}")
- else:
- print(f" -> NO ITEMS FOUND!")
\ No newline at end of file
diff --git a/debug_request.py b/debug_request.py
deleted file mode 100644
index eb54937..0000000
--- a/debug_request.py
+++ /dev/null
@@ -1,54 +0,0 @@
-import os
-import django
-from django.conf import settings
-import sys
-
-# Setup Django environment
-sys.path.append(os.getcwd())
-os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
-django.setup()
-
-from django.test import RequestFactory
-from core.views import index
-
-def test_root_view():
- factory = RequestFactory()
- request = factory.get('/')
-
- # Simulate logged in user (since index is login_required)
- from django.contrib.auth.models import AnonymousUser, User
-
- # Create a dummy user for testing
- if not User.objects.filter(username='testadmin').exists():
- user = User.objects.create_superuser('testadmin', 'admin@example.com', 'pass')
- else:
- user = User.objects.get(username='testadmin')
-
- request.user = user # Authenticated
-
- try:
- response = index(request)
- print(f"Authenticated Root View Status: {response.status_code}")
- except Exception as e:
- print(f"Authenticated Root View Error: {e}")
-
- # Test unauthenticated (should redirect)
- request_anon = factory.get('/')
- request_anon.user = AnonymousUser()
- from django.contrib.auth.decorators import login_required
- # We can't easily run the decorator logic with RequestFactory directly calling the view function
- # unless we use the view wrapped in login_required manually or via client.
-
- from django.test import Client
- client = Client()
- response = client.get('/')
- print(f"Client Root Get Status: {response.status_code}")
- if response.status_code == 302:
- print(f"Redirects to: {response.url}")
-
- # Check login page
- response_login = client.get('/accounts/login/')
- print(f"Client Login Get Status: {response_login.status_code}")
-
-if __name__ == "__main__":
- test_root_view()
diff --git a/debug_settings.py b/debug_settings.py
deleted file mode 100644
index a79282e..0000000
--- a/debug_settings.py
+++ /dev/null
@@ -1,38 +0,0 @@
-
-file_path = 'core/templates/core/settings.html'
-with open(file_path, 'r') as f:
- content = f.read()
-
-print("File length:", len(content))
-
-# Check context for Nav Tab
-if 'id="devices-tab"' in content:
- print("Devices tab already exists.")
-else:
- context_str = 'id="whatsapp-tab" data-bs-toggle="pill" data-bs-target="#whatsapp" type="button" role="tab">
- {% trans "WhatsApp Gateway" %}
-
- '
- if context_str in content:
- print("Found Nav Tab context.")
- else:
- print("Nav Tab context NOT found. Dumping nearby content:")
- # Find rough location
- idx = content.find('id="whatsapp-tab"')
- if idx != -1:
- print(content[idx:idx+300])
-
-# Check context for Tab Pane
-if 'id="devices" role="tabpanel"' in content:
- print("Devices pane already exists.")
-else:
- # Try to find the end of tab content
- # Look for Add Tier Modal
- idx = content.find('')
- if idx != -1:
- print("Found Add Tier Modal at index:", idx)
- print("Preceding content:")
- print(content[idx-100:idx])
- else:
- print("Add Tier Modal NOT found.")
-
diff --git a/debug_url.py b/debug_url.py
deleted file mode 100644
index 437c237..0000000
--- a/debug_url.py
+++ /dev/null
@@ -1,22 +0,0 @@
-
-import os
-import django
-from django.conf import settings
-from django.urls import reverse, resolve
-
-os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
-django.setup()
-
-try:
- print("Attempting to reverse 'inventory'...")
- url = reverse('inventory')
- print(f"Success: 'inventory' -> {url}")
-except Exception as e:
- print(f"Error reversing 'inventory': {e}")
-
-try:
- print("Attempting to reverse 'index'...")
- url = reverse('index')
- print(f"Success: 'index' -> {url}")
-except Exception as e:
- print(f"Error reversing 'index': {e}")
diff --git a/force_reset_admin.py b/force_reset_admin.py
deleted file mode 100644
index 8255a68..0000000
--- a/force_reset_admin.py
+++ /dev/null
@@ -1,32 +0,0 @@
-import os
-import sys
-import django
-
-# Add project root to path
-sys.path.append(os.getcwd())
-
-os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
-django.setup()
-
-from django.contrib.auth import get_user_model
-
-def reset_password():
- User = get_user_model()
- username = 'admin'
- password = 'admin'
-
- try:
- user, created = User.objects.get_or_create(username=username)
- user.set_password(password)
- user.is_staff = True
- user.is_superuser = True
- user.save()
-
- action = "created" if created else "reset"
- print(f"Successfully {action} password for user '{username}' to '{password}'.")
-
- except Exception as e:
- print(f"Error resetting password: {e}")
-
-if __name__ == "__main__":
- reset_password()
diff --git a/manage.py b/manage.py
index 1fb2261..7dcd3e8 100755
--- a/manage.py
+++ b/manage.py
@@ -14,21 +14,6 @@ def main():
except ImportError:
pass
- # --- WeasyPrint Library Preloading ---
- import ctypes
- import ctypes.util
-
- # Try to find and load libraries dynamically to help WeasyPrint on different platforms
- libs_to_load = ['glib-2.0', 'gobject-2.0', 'fontconfig', 'cairo', 'pango-1.0', 'pangoft2-1.0']
- for lib_name in libs_to_load:
- try:
- path = ctypes.util.find_library(lib_name)
- if path:
- ctypes.CDLL(path)
- except Exception:
- pass
- # -------------------------------------
-
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
try:
from django.core.management import execute_from_command_line
@@ -41,4 +26,4 @@ def main():
execute_from_command_line(sys.argv)
if __name__ == '__main__':
- main()
+ main()
\ No newline at end of file
diff --git a/manage_trace.txt b/manage_trace.txt
deleted file mode 100644
index 985eaca..0000000
--- a/manage_trace.txt
+++ /dev/null
@@ -1 +0,0 @@
-Manage.py started
diff --git a/manual_db_fix.py b/manual_db_fix.py
deleted file mode 100644
index e03ed12..0000000
--- a/manual_db_fix.py
+++ /dev/null
@@ -1,32 +0,0 @@
-from django.db import connection
-
-def fix_db():
- print("Starting DB Fix...")
- with connection.cursor() as cursor:
- # 1. Check/Add is_service to core_product
- try:
- cursor.execute("SELECT is_service FROM core_product LIMIT 1")
- print("SUCCESS: is_service already exists in core_product.")
- except Exception:
- print("Attempting to add is_service column...")
- try:
- # Try MySQL syntax first
- cursor.execute("ALTER TABLE core_product ADD COLUMN is_service tinyint(1) NOT NULL DEFAULT 0;")
- print("FIXED: Added is_service column to core_product.")
- except Exception as e:
- print(f"ERROR adding is_service: {e}")
-
- # 2. Check/Add is_active to core_paymentmethod
- try:
- cursor.execute("SELECT is_active FROM core_paymentmethod LIMIT 1")
- print("SUCCESS: is_active already exists in core_paymentmethod.")
- except Exception:
- print("Attempting to add is_active column...")
- try:
- cursor.execute("ALTER TABLE core_paymentmethod ADD COLUMN is_active tinyint(1) NOT NULL DEFAULT 1;")
- print("FIXED: Added is_active column to core_paymentmethod.")
- except Exception as e:
- print(f"ERROR adding is_active: {e}")
-
-if __name__ == '__main__':
- fix_db()
diff --git a/move_project.py b/move_project.py
deleted file mode 100644
index 16d8855..0000000
--- a/move_project.py
+++ /dev/null
@@ -1 +0,0 @@
-# This script has been disabled/removed as the project is deployed to the root.
\ No newline at end of file
diff --git a/patch_base_html.py b/patch_base_html.py
deleted file mode 100644
index 17aea1f..0000000
--- a/patch_base_html.py
+++ /dev/null
@@ -1,49 +0,0 @@
-
-import os
-
-file_path = 'core/templates/base.html'
-
-with open(file_path, 'r') as f:
- content = f.read()
-
-search_text = """ {% if user.is_authenticated %}
-
-
-
- {% endif %}"""
-
-replace_text = """ {% if user.is_authenticated %}
-
- {% endif %}"""
-
-if search_text in content:
- new_content = content.replace(search_text, replace_text)
- with open(file_path, 'w') as f:
- f.write(new_content)
- print("Successfully patched base.html")
-else:
- print("Search text not found in base.html. Please check formatting.")
diff --git a/patch_expense_categories.py b/patch_expense_categories.py
deleted file mode 100644
index dc4c7a3..0000000
--- a/patch_expense_categories.py
+++ /dev/null
@@ -1,43 +0,0 @@
-import os
-
-file_path = 'core/views.py'
-search_text = "@login_required\ndef expense_categories_view(request): return render(request, 'core/expense_categories.html')"
-replace_text = """@login_required
-def expense_categories_view(request):
- if request.method == 'POST':
- category_id = request.POST.get('category_id')
- name_en = request.POST.get('name_en')
- name_ar = request.POST.get('name_ar')
- description = request.POST.get('description')
-
- if category_id:
- # Update existing category
- category = get_object_or_404(ExpenseCategory, pk=category_id)
- category.name_en = name_en
- category.name_ar = name_ar
- category.description = description
- category.save()
- messages.success(request, _('Expense category updated successfully.'))
- else:
- # Create new category
- ExpenseCategory.objects.create(
- name_en=name_en,
- name_ar=name_ar,
- description=description
- )
- messages.success(request, _('Expense category added successfully.'))
- return redirect('expense_categories')
-
- categories = ExpenseCategory.objects.all().order_by('-id')
- return render(request, 'core/expense_categories.html', {'categories': categories})"""
-
-with open(file_path, 'r') as f:
- content = f.read()
-
-if search_text in content:
- new_content = content.replace(search_text, replace_text)
- with open(file_path, 'w') as f:
- f.write(new_content)
- print("Successfully patched expense_categories_view")
-else:
- print("Could not find the target function to replace")
diff --git a/patch_invoice_list.py b/patch_invoice_list.py
deleted file mode 100644
index d4acaaa..0000000
--- a/patch_invoice_list.py
+++ /dev/null
@@ -1,52 +0,0 @@
-import os
-
-file_path = 'core/views.py'
-
-old_content = """@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'))})"""
-
-new_content = """@login_required
-def invoice_list(request):
- sales = Sale.objects.all().order_by('-created_at')
-
- # Filter by date range
- start_date = request.GET.get('start_date')
- end_date = request.GET.get('end_date')
- if start_date:
- sales = sales.filter(created_at__date__gte=start_date)
- if end_date:
- sales = sales.filter(created_at__date__lte=end_date)
-
- # Filter by customer
- customer_id = request.GET.get('customer')
- if customer_id:
- sales = sales.filter(customer_id=customer_id)
-
- # Filter by status
- status = request.GET.get('status')
- if status:
- sales = sales.filter(status=status)
-
- paginator = Paginator(sales, 25)
-
- context = {
- 'sales': paginator.get_page(request.GET.get('page')),
- 'customers': Customer.objects.all(),
- 'payment_methods': PaymentMethod.objects.filter(is_active=True),
- 'site_settings': SystemSetting.objects.first(),
- }
- return render(request, 'core/invoices.html', context)"""
-
-with open(file_path, 'r') as f:
- content = f.read()
-
-if old_content in content:
- content = content.replace(old_content, new_content)
- with open(file_path, 'w') as f:
- f.write(content)
- print("Successfully patched invoice_list")
-else:
- print("Could not find exact match for invoice_list function")
diff --git a/patch_models_timestamp.py b/patch_models_timestamp.py
deleted file mode 100644
index 73634d9..0000000
--- a/patch_models_timestamp.py
+++ /dev/null
@@ -1,43 +0,0 @@
-import os
-
-path = 'core/models.py'
-with open(path, 'r') as f:
- content = f.read()
-
-# Patch SalePayment
-old_sale_payment = """ notes = models.TextField(_("Notes"), blank=True)
- created_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name="sale_payments")
-
- def __str__(self):"""
-new_sale_payment = """ notes = models.TextField(_("Notes"), blank=True)
- created_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name="sale_payments")
- created_at = models.DateTimeField(auto_now_add=True)
-
- def __str__(self):"""
-
-# Patch PurchasePayment
-old_purchase_payment = """ notes = models.TextField(_("Notes"), blank=True)
- created_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name="purchase_payments")
-
- def __str__(self):"""
-new_purchase_payment = """ notes = models.TextField(_("Notes"), blank=True)
- created_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name="purchase_payments")
- created_at = models.DateTimeField(auto_now_add=True)
-
- def __str__(self):"""
-
-# Check if SalePayment already has created_at
-# A simple check: if we find the new pattern, we skip
-if new_sale_payment in content:
- print("SalePayment already patched.")
-else:
- content = content.replace(old_sale_payment, new_sale_payment)
-
-if new_purchase_payment in content:
- print("PurchasePayment already patched.")
-else:
- content = content.replace(old_purchase_payment, new_purchase_payment)
-
-with open(path, 'w') as f:
- f.write(content)
-print("Patched core/models.py")
diff --git a/patch_payments_v2.py b/patch_payments_v2.py
deleted file mode 100644
index 82e800b..0000000
--- a/patch_payments_v2.py
+++ /dev/null
@@ -1,211 +0,0 @@
-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)
diff --git a/patch_pos_view.py b/patch_pos_view.py
deleted file mode 100644
index 14337bf..0000000
--- a/patch_pos_view.py
+++ /dev/null
@@ -1,34 +0,0 @@
-import os
-
-file_path = 'core/views.py'
-
-with open(file_path, 'r') as f:
- content = f.read()
-
-old_block = """ products = Product.objects.all().filter(stock_quantity__gt=0, is_active=True)
- customers = Customer.objects.all()
- categories = Category.objects.all()
- payment_methods = PaymentMethod.objects.filter(is_active=True)
- settings = SystemSetting.objects.first()"""
-
-new_block = """ settings = SystemSetting.objects.first()
- products = Product.objects.filter(is_active=True)
- if not settings or not settings.allow_zero_stock_sales:
- products = products.filter(stock_quantity__gt=0)
-
- customers = Customer.objects.all()
- categories = Category.objects.all()
- payment_methods = PaymentMethod.objects.filter(is_active=True)"""
-
-if old_block in content:
- new_content = content.replace(old_block, new_block)
- with open(file_path, 'w') as f:
- f.write(new_content)
- print("Successfully patched core/views.py")
-else:
- print("Could not find the target block in core/views.py")
- # Debugging: print a small chunk to see what's wrong with matching
- start_index = content.find("def pos(request):")
- if start_index != -1:
- print("Context around pos view:")
- print(content[start_index:start_index+500])
diff --git a/patch_returns_setup.py b/patch_returns_setup.py
deleted file mode 100644
index 70a5292..0000000
--- a/patch_returns_setup.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from core.views import (
- purchase_return_create, sale_return_create,
- create_sale_return_api, create_purchase_return_api,
- Supplier, Product, Customer, SaleReturn, SaleReturnItem,
- PurchaseReturn, PurchaseReturnItem, transaction, timezone,
- decimal, json, JsonResponse, get_object_or_404, login_required, csrf_exempt, logger
-)
-
-def patch_purchase_return_create(request):
- suppliers = Supplier.objects.filter(is_active=True)
- products = Product.objects.filter(is_active=True)
- return {'suppliers': suppliers, 'products': products}
-
-def patch_sale_return_create(request):
- customers = Customer.objects.all()
- products = Product.objects.filter(is_active=True)
- return {'customers': customers, 'products': products}
diff --git a/patch_returns_v3.py b/patch_returns_v3.py
deleted file mode 100644
index fdc6d74..0000000
--- a/patch_returns_v3.py
+++ /dev/null
@@ -1,159 +0,0 @@
-import os
-
-file_path = 'core/views.py'
-
-with open(file_path, 'r') as f:
- content = f.read()
-
-# Replacement 1: sale_return_create
-old_sale_create = "def sale_return_create(request): return render(request, 'core/sale_return_create.html')"
-new_sale_create = """def sale_return_create(request):
- customers = Customer.objects.all()
- products = Product.objects.filter(is_active=True)
- return render(request, 'core/sale_return_create.html', {
- 'customers': customers,
- 'products': products
- })"""
-
-# Replacement 2: create_sale_return_api
-old_sale_api = "@csrf_exempt\ndef create_sale_return_api(request): return JsonResponse({'success': True})"
-new_sale_api = """@csrf_exempt
-@login_required
-def create_sale_return_api(request):
- if request.method != 'POST':
- return JsonResponse({'success': False, 'error': 'Invalid method'})
- try:
- data = json.loads(request.body)
- customer_id = data.get('customer_id')
- items = data.get('items', [])
-
- customer = None
- if customer_id:
- customer = get_object_or_404(Customer, pk=customer_id)
-
- with transaction.atomic():
- sale_return = SaleReturn.objects.create(
- customer=customer,
- created_by=request.user,
- total_amount=0,
- return_number=f"SR-{{int(timezone.now().timestamp())}}",
- notes=data.get('notes', '')
- )
-
- total = decimal.Decimal(0)
- for item in items:
- qty = decimal.Decimal(str(item.get('quantity', 0)))
- price = decimal.Decimal(str(item.get('price', 0)))
- line_total = qty * price
-
- SaleReturnItem.objects.create(
- sale_return=sale_return,
- product_id=item['id'],
- quantity=qty,
- unit_price=price,
- line_total=line_total
- )
-
- # Update stock: Returns from customer mean stock comes IN
- product = Product.objects.get(pk=item['id'])
- product.stock_quantity += qty
- product.save()
-
- total += line_total
-
- sale_return.total_amount = total
- sale_return.save()
-
- return JsonResponse({'success': True, 'id': sale_return.id})
- except Exception as e:
- logger.exception("Error creating sale return")
- return JsonResponse({'success': False, 'error': str(e)})"""
-
-# Replacement 3: purchase_return_create
-old_purchase_create = "def purchase_return_create(request): return render(request, 'core/purchase_return_create.html')"
-new_purchase_create = """def purchase_return_create(request):
- suppliers = Supplier.objects.filter(is_active=True)
- products = Product.objects.filter(is_active=True)
- return render(request, 'core/purchase_return_create.html', {
- 'suppliers': suppliers,
- 'products': products
- })"""
-
-# Replacement 4: create_purchase_return_api
-old_purchase_api = "@csrf_exempt\ndef create_purchase_return_api(request): return JsonResponse({'success': True})"
-new_purchase_api = """@csrf_exempt
-@login_required
-def create_purchase_return_api(request):
- if request.method != 'POST':
- return JsonResponse({'success': False, 'error': 'Invalid method'})
- try:
- data = json.loads(request.body)
- supplier_id = data.get('supplier_id')
- items = data.get('items', [])
-
- supplier = get_object_or_404(Supplier, pk=supplier_id)
-
- with transaction.atomic():
- purchase_return = PurchaseReturn.objects.create(
- supplier=supplier,
- created_by=request.user,
- total_amount=0,
- return_number=f"PR-{{int(timezone.now().timestamp())}}",
- notes=data.get('notes', '')
- )
-
- total = decimal.Decimal(0)
- for item in items:
- qty = decimal.Decimal(str(item.get('quantity', 0)))
- cost = decimal.Decimal(str(item.get('price', 0)))
- line_total = qty * cost
-
- PurchaseReturnItem.objects.create(
- purchase_return=purchase_return,
- product_id=item['id'],
- quantity=qty,
- cost_price=cost,
- line_total=line_total
- )
-
- # Update stock: Returns to supplier mean stock goes OUT
- product = Product.objects.get(pk=item['id'])
- product.stock_quantity -= qty
- product.save()
-
- total += line_total
-
- purchase_return.total_amount = total
- purchase_return.save()
-
- return JsonResponse({'success': True, 'id': purchase_return.id})
- except Exception as e:
- logger.exception("Error creating purchase return")
- return JsonResponse({'success': False, 'error': str(e)})"""
-
-if old_sale_create in content:
- content = content.replace(old_sale_create, new_sale_create)
- print("Patched sale_return_create")
-else:
- print("Could not find sale_return_create stub")
-
-if old_sale_api in content:
- content = content.replace(old_sale_api, new_sale_api)
- print("Patched create_sale_return_api")
-else:
- print("Could not find create_sale_return_api stub")
-
-if old_purchase_create in content:
- content = content.replace(old_purchase_create, new_purchase_create)
- print("Patched purchase_return_create")
-else:
- print("Could not find purchase_return_create stub")
-
-if old_purchase_api in content:
- content = content.replace(old_purchase_api, new_purchase_api)
- print("Patched create_purchase_return_api")
-else:
- print("Could not find create_purchase_return_api stub")
-
-with open(file_path, 'w') as f:
- f.write(content)
diff --git a/patch_settings_html.py b/patch_settings_html.py
deleted file mode 100644
index 8669a41..0000000
--- a/patch_settings_html.py
+++ /dev/null
@@ -1,260 +0,0 @@
-file_path = 'core/templates/core/settings.html'
-
-with open(file_path, 'r') as f:
- content = f.read()
-
-# 1. Add Nav Tab
-if 'id="devices-tab"' not in content:
- whatsapp_tab_end = 'id="whatsapp-tab" data-bs-toggle="pill" data-bs-target="#whatsapp" type="button" role="tab">\n {% trans "WhatsApp Gateway" %}\n \n li>'
-
- insert_str = """
-
-
- "
-
- if whatsapp_tab_end in content:
- content = content.replace(whatsapp_tab_end, whatsapp_tab_end + insert_str)
- print("Added Devices Tab Nav.")
- else:
- # Fallback search if exact string match fails due to whitespace
- print("Could not find exact match for Nav Tab insertion. Trying simpler match.")
- simple_search = '{% trans "WhatsApp Gateway" %}'
- parts = content.split(simple_search)
- if len(parts) > 1:
- # Reconstruct slightly differently but risky
- pass
-
-# 2. Add Tab Content
-if 'id="devices" role="tabpanel"' not in content:
- devices_pane = """
-
-
-
-
-
-
-
-
-
- | {% trans "Device Name" %} |
- {% trans "Type" %} |
- {% trans "Connection" %} |
- {% trans "IP / Port" %} |
- {% trans "Status" %} |
- {% trans "Actions" %} |
-
-
-
- {% for device in devices %}
-
- | {{ device.name }} |
-
- {{ device.get_device_type_display }}
- |
- {{ device.get_connection_type_display }} |
-
- {% if device.ip_address %}
- {{ device.ip_address }}{% if device.port %}:{{ device.port }}{% endif %}
- {% else %}
- -
- {% endif %}
- |
-
- {% if device.is_active %}
- {% trans "Active" %}
- {% else %}
- {% trans "Inactive" %}
- {% endif %}
- |
-
-
-
- |
-
-
-
-
-
-
-
-
-
-
-
-
{% trans "Are you sure you want to delete" %} {{ device.name }}?
-
-
-
-
-
-
- {% empty %}
-
- |
-
-
- {% trans "No devices configured." %}
-
- |
-
- {% endfor %}
-
-
-
-
-
-
- "
-
- parts = content.split('')
- if len(parts) > 1:
- last_div = parts[0].rfind('')
- second_last_div = parts[0].rfind('', 0, last_div)
-
- if second_last_div != -1:
- new_part0 = parts[0][:second_last_div] + devices_pane + parts[0][second_last_div:]
- content = new_part0 + '' + parts[1]
- print("Added Devices Tab Pane.")
- else:
- print("Could not find insertion point for Devices Pane.")
-
-# 3. Add Add Device Modal
-if 'id="addDeviceModal"' not in content:
- modal_content = """
-
-
-"
- content = content.replace('{% endblock %}', modal_content + '\n{% endblock %}')
- print("Added Add Device Modal.")
-
-with open(file_path, 'w') as f:
- f.write(content)
\ No newline at end of file
diff --git a/patch_views.py b/patch_views.py
deleted file mode 100644
index 7bd7fcf..0000000
--- a/patch_views.py
+++ /dev/null
@@ -1,85 +0,0 @@
-import re
-
-file_path = 'core/views.py'
-
-with open(file_path, 'r') as f:
- content = f.read()
-
-# 1. Add Device to imports
-if 'Device' not in content:
- pattern = r'(from \.models import \(.*?)(\))'
- replacement = r'\1, Device\2'
- content = re.sub(pattern, replacement, content, flags=re.DOTALL)
- print("Added Device to imports.")
-
-# 2. Update settings_view
-if 'devices = Device.objects.all()' not in content:
- # Find the lines before context creation
- search_str = 'loyalty_tiers = LoyaltyTier.objects.all().order_by("min_points")'
- insert_str = '\n devices = Device.objects.all().order_by("name")'
- content = content.replace(search_str, search_str + insert_str)
-
- # Update context
- context_search = '"loyalty_tiers": loyalty_tiers'
- context_insert = ',\n "devices": devices'
- content = content.replace(context_search, context_search + context_insert)
- print("Updated settings_view.")
-
-# 3. Add Device views
-new_views = """
-
-@login_required
-def add_device(request):
- if request.method == 'POST':
- name = request.POST.get('name')
- device_type = request.POST.get('device_type')
- connection_type = request.POST.get('connection_type')
- ip_address = request.POST.get('ip_address')
- port = request.POST.get('port')
- is_active = request.POST.get('is_active') == 'on'
-
- Device.objects.create(
- name=name,
- device_type=device_type,
- connection_type=connection_type,
- ip_address=ip_address if ip_address else None,
- port=port if port else None,
- is_active=is_active
- )
- messages.success(request, _("Device added successfully!"))
- return redirect(reverse('settings') + '#devices')
-
-@login_required
-def edit_device(request, pk):
- device = get_object_or_404(Device, pk=pk)
- if request.method == 'POST':
- device.name = request.POST.get('name')
- device.device_type = request.POST.get('device_type')
- device.connection_type = request.POST.get('connection_type')
- device.ip_address = request.POST.get('ip_address')
- device.port = request.POST.get('port')
- device.is_active = request.POST.get('is_active') == 'on'
-
- if not device.ip_address:
- device.ip_address = None
- if not device.port:
- device.port = None
-
- device.save()
- messages.success(request, _("Device updated successfully!"))
- return redirect(reverse('settings') + '#devices')
-
-@login_required
-def delete_device(request, pk):
- device = get_object_or_404(Device, pk=pk)
- device.delete()
- messages.success(request, _("Device deleted successfully!"))
- return redirect(reverse('settings') + '#devices')
-"""
-
-if 'def add_device(request):' not in content:
- content += new_views
- print("Added Device views.")
-
-with open(file_path, 'w') as f:
- f.write(content)
\ No newline at end of file
diff --git a/patch_views_expense_edit.py b/patch_views_expense_edit.py
deleted file mode 100644
index fd824fd..0000000
--- a/patch_views_expense_edit.py
+++ /dev/null
@@ -1,30 +0,0 @@
-
-@login_required
-def expense_edit_view(request, pk):
- expense = get_object_or_404(Expense, pk=pk)
- if request.method == 'POST':
- try:
- category_id = request.POST.get('category')
- amount = request.POST.get('amount')
- date = request.POST.get('date')
- description = request.POST.get('description')
- payment_method_id = request.POST.get('payment_method')
-
- category = get_object_or_404(ExpenseCategory, pk=category_id)
- payment_method = get_object_or_404(PaymentMethod, pk=payment_method_id) if payment_method_id else None
-
- expense.category = category
- expense.amount = amount
- expense.date = date or expense.date
- expense.description = description
- expense.payment_method = payment_method
-
- if 'attachment' in request.FILES:
- expense.attachment = request.FILES['attachment']
-
- expense.save()
- messages.success(request, _('Expense updated successfully.'))
- except Exception as e:
- messages.error(request, _('Error updating expense: ') + str(e))
-
- return redirect('expenses')
diff --git a/patch_views_fixes.py b/patch_views_fixes.py
deleted file mode 100644
index 5ffc807..0000000
--- a/patch_views_fixes.py
+++ /dev/null
@@ -1,134 +0,0 @@
-
-import os
-import re
-
-file_path = 'core/views.py'
-
-with open(file_path, 'r') as f:
- content = f.read()
-
-# Implement add_payment_method_ajax
-# It currently looks like:
-# @login_required
-# def add_payment_method_ajax(request):
-# return JsonResponse({'success': True})
-
-new_add_payment_method = """@csrf_exempt
-@login_required
-def add_payment_method_ajax(request):
- if request.method != 'POST':
- return JsonResponse({'success': False, 'error': 'Invalid method'})
- try:
- data = json.loads(request.body)
- pm = PaymentMethod.objects.create(
- name_en=data.get('name_en'),
- name_ar=data.get('name_ar'),
- is_active=data.get('is_active', True)
- )
- return JsonResponse({
- 'success': True,
- 'id': pm.id,
- 'name_en': pm.name_en,
- 'name_ar': pm.name_ar
- })
- except Exception as e:
- return JsonResponse({'success': False, 'error': str(e)})"""
-
-content = re.sub(
- r'@login_required\s+def add_payment_method_ajax\(request\):\s+return JsonResponse\({\s*["']success["']: True\s*}\)',
- new_add_payment_method,
- content
-)
-
-# Implement create_purchase_api
-new_create_purchase = """@csrf_exempt
-@login_required
-def create_purchase_api(request):
- if request.method != 'POST':
- return JsonResponse({'success': False, 'error': 'Invalid request'})
- try:
- data = json.loads(request.body)
- with transaction.atomic():
- purchase = Purchase.objects.create(
- supplier_id=data.get('supplier_id') or None,
- total_amount=data.get('total_amount', 0),
- paid_amount=data.get('paid_amount', 0),
- created_by=request.user,
- status='paid' if data.get('payment_type') == 'cash' else 'partial'
- )
- for item in data.get('items', []):
- PurchaseItem.objects.create(
- purchase=purchase,
- product_id=item['id'],
- quantity=item['quantity'],
- cost_price=item['cost'],
- line_total=float(item['quantity']) * float(item['cost'])
- )
- # Increase Stock
- Product.objects.filter(pk=item['id']).update(stock_quantity=F('stock_quantity') + item['quantity'])
-
- # Payment
- if purchase.paid_amount > 0:
- PurchasePayment.objects.create(
- purchase=purchase,
- amount=purchase.paid_amount,
- payment_method_id=data.get('payment_method_id'),
- created_by=request.user
- )
-
- purchase.update_balance()
-
- return JsonResponse({'success': True, 'purchase_id': purchase.id})
- except Exception as e:
- logger.error(f"Error creating purchase: {e}")
- return JsonResponse({'success': False, 'error': str(e)})"""
-
-content = re.sub(
- r'@csrf_exempt\s+@login_required\s+def create_purchase_api\(request\):\s+return JsonResponse\({\s*["']success["']: True\s*}\)',
- new_create_purchase,
- content
-)
-
-
-# Implement add_customer_ajax
-new_add_customer = """@csrf_exempt
-@login_required
-def add_customer_ajax(request):
- if request.method != 'POST':
- return JsonResponse({'success': False})
- try:
- data = json.loads(request.body)
- Customer.objects.create(name=data.get('name'), phone=data.get('phone', ''))
- return JsonResponse({'success': True})
- except Exception as e:
- return JsonResponse({'success': False, 'error': str(e)})"""
-
-content = re.sub(
- r'@login_required\s+def add_customer_ajax\(request\):\s+return JsonResponse\({\s*["']success["']: True\s*}\)',
- new_add_customer,
- content
-)
-
-# Implement add_supplier_ajax
-new_add_supplier = """@csrf_exempt
-@login_required
-def add_supplier_ajax(request):
- if request.method != 'POST':
- return JsonResponse({'success': False})
- try:
- data = json.loads(request.body)
- Supplier.objects.create(name=data.get('name'), phone=data.get('phone', ''))
- return JsonResponse({'success': True})
- except Exception as e:
- return JsonResponse({'success': False, 'error': str(e)})"""
-
-content = re.sub(
- r'@login_required\s+def add_supplier_ajax\(request\):\s+return JsonResponse\({\s*["']success["']: True\s*}\)',
- new_add_supplier,
- content
-)
-
-with open(file_path, 'w') as f:
- f.write(content)
-
-print("Patched core/views.py successfully.")
diff --git a/patch_views_sales.py b/patch_views_sales.py
deleted file mode 100644
index 62ea21c..0000000
--- a/patch_views_sales.py
+++ /dev/null
@@ -1,206 +0,0 @@
-import os
-
-file_path = 'core/views.py'
-
-# New Implementations
-edit_invoice_code = """
-@login_required
-def edit_invoice(request, pk):
- sale = get_object_or_404(Sale, pk=pk)
- customers = Customer.objects.all()
- products = Product.objects.filter(is_active=True).select_related('category')
- payment_methods = PaymentMethod.objects.filter(is_active=True)
- site_settings = SystemSetting.objects.first()
-
- decimal_places = 2
- if site_settings:
- decimal_places = site_settings.decimal_places
-
- # Serialize items for Vue
- cart_items = []
- for item in sale.items.all().select_related('product'):
- cart_items.append({
- 'id': item.product.id,
- 'name_en': item.product.name_en,
- 'name_ar': item.product.name_ar,
- 'sku': item.product.sku,
- 'price': float(item.unit_price),
- 'quantity': float(item.quantity),
- 'stock': float(item.product.stock_quantity)
- })
-
- cart_json = json.dumps(cart_items)
-
- # Get first payment method if exists
- payment_method_id = ""
- first_payment = sale.payments.first()
- if first_payment and first_payment.payment_method:
- payment_method_id = first_payment.payment_method.id
-
- context = {
- 'sale': sale,
- 'customers': customers,
- 'products': products,
- 'payment_methods': payment_methods,
- 'site_settings': site_settings,
- 'decimal_places': decimal_places,
- 'cart_json': cart_json,
- 'payment_method_id': payment_method_id
- }
- return render(request, 'core/invoice_edit.html', context)
-"""
-
-sale_receipt_code = """
-@login_required
-def sale_receipt(request, pk):
- sale = get_object_or_404(Sale, pk=pk)
- settings = SystemSetting.objects.first()
- return render(request, 'core/sale_receipt.html', {
- 'sale': sale,
- 'settings': settings
- })
-"""
-
-update_sale_api_code = """
-@csrf_exempt
-def update_sale_api(request, pk):
- if request.method != 'POST':
- return JsonResponse({'success': False, 'error': 'Invalid request method'})
-
- try:
- sale = Sale.objects.get(pk=pk)
- data = json.loads(request.body)
-
- customer_id = data.get('customer_id')
- items = data.get('items', [])
- discount = decimal.Decimal(str(data.get('discount', 0)))
- paid_amount = decimal.Decimal(str(data.get('paid_amount', 0)))
- payment_type = data.get('payment_type', 'cash')
- payment_method_id = data.get('payment_method_id')
- due_date = data.get('due_date')
- notes = data.get('notes', '')
- invoice_number = data.get('invoice_number')
-
- if not items:
- return JsonResponse({'success': False, 'error': 'No items in sale'})
-
- with transaction.atomic():
- # 1. Revert Stock
- for item in sale.items.all():
- product = item.product
- product.stock_quantity += item.quantity
- product.save()
-
- # 2. Delete existing items
- sale.items.all().delete()
-
- # 3. Update Sale Details
- if customer_id:
- sale.customer_id = customer_id
- else:
- sale.customer = None
-
- sale.discount = discount
- sale.notes = notes
- if invoice_number:
- sale.invoice_number = invoice_number
-
- if due_date:
- sale.due_date = due_date
- else:
- sale.due_date = None
-
- # 4. Create New Items and Deduct Stock
- subtotal = decimal.Decimal(0)
-
- for item_data in items:
- product = Product.objects.get(pk=item_data['id'])
- quantity = decimal.Decimal(str(item_data['quantity']))
- price = decimal.Decimal(str(item_data['price']))
-
- # Deduct stock
- product.stock_quantity -= quantity
- product.save()
-
- line_total = price * quantity
- subtotal += line_total
-
- SaleItem.objects.create(
- sale=sale,
- product=product,
- quantity=quantity,
- unit_price=price,
- line_total=line_total
- )
-
- sale.subtotal = subtotal
- sale.total_amount = subtotal - discount
-
- # 5. Handle Payments
- if payment_type == 'credit':
- sale.status = 'unpaid'
- sale.paid_amount = 0
- sale.balance_due = sale.total_amount
- sale.payments.all().delete()
-
- elif payment_type == 'cash':
- sale.status = 'paid'
- sale.paid_amount = sale.total_amount
- sale.balance_due = 0
-
- sale.payments.all().delete()
- SalePayment.objects.create(
- sale=sale,
- amount=sale.total_amount,
- payment_method_id=payment_method_id if payment_method_id else None,
- payment_date=timezone.now().date(),
- notes='Full Payment (Edit)'
- )
-
- elif payment_type == 'partial':
- sale.paid_amount = paid_amount
- sale.balance_due = sale.total_amount - paid_amount
- if sale.balance_due <= 0:
- sale.status = 'paid'
- sale.balance_due = 0
- else:
- sale.status = 'partial'
-
- sale.payments.all().delete()
- SalePayment.objects.create(
- sale=sale,
- amount=paid_amount,
- payment_method_id=payment_method_id if payment_method_id else None,
- payment_date=timezone.now().date(),
- notes='Partial Payment (Edit)'
- )
-
- sale.save()
-
- return JsonResponse({'success': True, 'sale_id': sale.id})
-
- except Sale.DoesNotExist:
- return JsonResponse({'success': False, 'error': 'Sale not found'})
- except Product.DoesNotExist:
- return JsonResponse({'success': False, 'error': 'Product not found'})
- except Exception as e:
- return JsonResponse({'success': False, 'error': str(e)})
-"""
-
-with open(file_path, 'r') as f:
- content = f.read()
-
-# Replace stubs
-content = content.replace("def sale_receipt(request, pk): return redirect('invoices')", sale_receipt_code)
-content = content.replace("def edit_invoice(request, pk): return redirect('invoices')", edit_invoice_code)
-content = content.replace("@csrf_exempt\ndef update_sale_api(request, pk): return JsonResponse({'success': False})", update_sale_api_code)
-
-# Handle potential whitespace variations if single-line replace fails
-if "def edit_invoice(request, pk): return redirect('invoices')" in content: # Check if it persisted
- pass # worked
-else:
- # Fallback for manual check if needed (it should work given exact match from read_file)
- pass
-
-with open(file_path, 'w') as f:
- f.write(content)
diff --git a/restore_views.py b/restore_views.py
deleted file mode 100644
index a25f4cb..0000000
--- a/restore_views.py
+++ /dev/null
@@ -1,220 +0,0 @@
-import os
-
-file_path = 'core/views.py'
-
-# The missing code to append
-missing_code = r"""
- # Deduct stock
- product.stock_quantity -= int(item['quantity'])
- product.save()
-
- return JsonResponse({'success': True, 'sale_id': sale.id})
- except Exception as e:
- return JsonResponse({'success': False, 'error': str(e)}, status=400)
- return JsonResponse({'success': False, 'error': 'Invalid request'}, status=405)
-
-@login_required
-def search_customers_api(request):
- query = request.GET.get('q', '')
- customers = Customer.objects.filter(
- Q(name__icontains=query) | Q(phone__icontains=query)
- ).values('id', 'name', 'phone')[:10]
- return JsonResponse({'results': list(customers)})
-
-@login_required
-def customer_payments(request):
- payments = SalePayment.objects.select_related('sale', 'sale__customer').order_by('-payment_date', '-created_at')
- paginator = Paginator(payments, 25)
- page_number = request.GET.get('page')
- payments = paginator.get_page(page_number)
- return render(request, 'core/customer_payments.html', {'payments': payments})
-
-@login_required
-def customer_payment_receipt(request, pk):
- payment = get_object_or_404(SalePayment, pk=pk)
- settings = SystemSetting.objects.first()
- return render(request, 'core/payment_receipt.html', {
- 'payment': payment,
- 'settings': settings,
- 'amount_in_words': number_to_words_en(payment.amount)
- })
-
-@login_required
-def sale_receipt(request, pk):
- sale = get_object_or_404(Sale, pk=pk)
- settings = SystemSetting.objects.first()
- return render(request, 'core/sale_receipt.html', {
- 'sale': sale,
- 'settings': settings
- })
-
-@csrf_exempt
-def pos_sync_update(request):
- # Placeholder for POS sync logic
- return JsonResponse({'status': 'ok'})
-
-@csrf_exempt
-def pos_sync_state(request):
- # Placeholder for POS sync state
- return JsonResponse({'state': {}})
-
-@login_required
-def test_whatsapp_connection(request):
- settings = SystemSetting.objects.first()
- if not settings or not settings.wablas_enabled:
- return JsonResponse({'success': False, 'message': 'WhatsApp not enabled'})
- return JsonResponse({'success': True, 'message': 'Connection simulation successful'})
-
-@login_required
-def add_device(request):
- if request.method == 'POST':
- name = request.POST.get('name')
- device_type = request.POST.get('device_type')
- connection_type = request.POST.get('connection_type')
- ip_address = request.POST.get('ip_address')
- port = request.POST.get('port')
- is_active = request.POST.get('is_active') == 'on'
-
- Device.objects.create(
- name=name,
- device_type=device_type,
- connection_type=connection_type,
- ip_address=ip_address if ip_address else None,
- port=port if port else None,
- is_active=is_active
- )
- messages.success(request, _("Device added successfully!"))
- return redirect(reverse('settings') + '#devices')
-
-@login_required
-def edit_device(request, pk):
- device = get_object_or_404(Device, pk=pk)
- if request.method == 'POST':
- device.name = request.POST.get('name')
- device.device_type = request.POST.get('device_type')
- device.connection_type = request.POST.get('connection_type')
- device.ip_address = request.POST.get('ip_address')
- device.port = request.POST.get('port')
- device.is_active = request.POST.get('is_active') == 'on'
-
- if not device.ip_address:
- device.ip_address = None
- if not device.port:
- device.port = None
-
- device.save()
- messages.success(request, _("Device updated successfully!"))
- return redirect(reverse('settings') + '#devices')
-
-@login_required
-def delete_device(request, pk):
- device = get_object_or_404(Device, pk=pk)
- device.delete()
- messages.success(request, _("Device deleted successfully!"))
- return redirect(reverse('settings') + '#devices')
-
-# LPO Views (Placeholders/Basic Implementation)
-@login_required
-def lpo_list(request):
- lpos = PurchaseOrder.objects.all().order_by('-created_at')
- return render(request, 'core/lpo_list.html', {'lpos': lpos})
-
-@login_required
-def lpo_create(request):
- suppliers = Supplier.objects.all()
- products = Product.objects.filter(is_active=True)
- return render(request, 'core/lpo_create.html', {'suppliers': suppliers, 'products': products})
-
-@login_required
-def lpo_detail(request, pk):
- lpo = get_object_or_404(PurchaseOrder, pk=pk)
- settings = SystemSetting.objects.first()
- return render(request, 'core/lpo_detail.html', {'lpo': lpo, 'settings': settings})
-
-@login_required
-def convert_lpo_to_purchase(request, pk):
- lpo = get_object_or_404(PurchaseOrder, pk=pk)
- # Conversion logic here (simplified)
- # ...
- return redirect('purchases')
-
-@login_required
-def lpo_delete(request, pk):
- lpo = get_object_or_404(PurchaseOrder, pk=pk)
- lpo.delete()
- return redirect('lpo_list')
-
-@csrf_exempt
-@login_required
-def create_lpo_api(request):
- # API logic for LPO creation
- return JsonResponse({'success': True, 'lpo_id': 1}) # Dummy
-
-@login_required
-def cashier_registry(request):
- registries = CashierCounterRegistry.objects.all()
- return render(request, 'core/cashier_registry.html', {'registries': registries})
-
-# Session Views
-@login_required
-def cashier_session_list(request):
- sessions = CashierSession.objects.all().order_by('-start_time')
- return render(request, 'core/session_list.html', {'sessions': sessions})
-
-@login_required
-def start_session(request):
- if request.method == 'POST':
- opening_balance = request.POST.get('opening_balance', 0)
- # Find assigned counter
- registry = CashierCounterRegistry.objects.filter(cashier=request.user).first()
- counter = registry.counter if registry else None
-
- CashierSession.objects.create(
- user=request.user,
- counter=counter,
- opening_balance=opening_balance,
- status='active'
- )
- return redirect('pos')
- return render(request, 'core/start_session.html')
-
-@login_required
-def close_session(request):
- session = CashierSession.objects.filter(user=request.user, status='active').first()
- if request.method == 'POST' and session:
- closing_balance = request.POST.get('closing_balance', 0)
- notes = request.POST.get('notes', '')
- session.closing_balance = closing_balance
- session.notes = notes
- session.end_time = timezone.now()
- session.status = 'closed'
- session.save()
- return redirect('index')
- return render(request, 'core/close_session.html', {'session': session})
-
-@login_required
-def session_detail(request, pk):
- session = get_object_or_404(CashierSession, pk=pk)
- return render(request, 'core/session_detail.html', {'session': session})
-
-@login_required
-def customer_display(request):
- return render(request, 'core/customer_display.html')
-"""
-
-with open(file_path, 'r') as f:
- content = f.read()
-
-# Check if the file ends with the broken function
-if content.strip().endswith("line_total=item['line_total']\n )"):
- print("Found broken file end. Appending missing code.")
- with open(file_path, 'a') as f:
- f.write(missing_code)
- print("Successfully restored core/views.py")
-else:
- print("File does not end as expected. Please check manually.")
- # Force append if it looks like it's missing the new functions
- if "def start_session" not in content:
- print("Appending missing functions anyway...")
- with open(file_path, 'a') as f:
- f.write(missing_code)
diff --git a/settings_trace.txt b/settings_trace.txt
deleted file mode 100644
index 944488a..0000000
--- a/settings_trace.txt
+++ /dev/null
@@ -1 +0,0 @@
-Settings loaded
diff --git a/startup_status.txt b/startup_status.txt
deleted file mode 100644
index ecbe128..0000000
--- a/startup_status.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-[2026-02-09T07:28:37.599243] Starting manage.py...
-[2026-02-09T07:28:37.599461] Args: ['manage.py', 'runserver', '0.0.0.0:8000']
-[2026-02-09T07:28:37.653416] Loaded .env
-[2026-02-09T07:28:38.658262] Django setup complete.
-[2026-02-09T07:28:38.658403] Executing command line...
-[2026-02-09T07:28:46.088684] SystemExit caught: 3
-[2026-02-09T07:28:46.362448] Starting manage.py...
-[2026-02-09T07:28:46.362618] Args: ['manage.py', 'runserver', '0.0.0.0:8000']
-[2026-02-09T07:28:46.390956] Loaded .env
-[2026-02-09T07:28:46.717591] Django setup complete.
-[2026-02-09T07:28:46.717749] Executing command line...
-[2026-02-09T07:42:35.563432] SystemExit caught: 3
-[2026-02-09T07:42:36.571344] Starting manage.py...
-[2026-02-09T07:42:36.571557] Args: ['manage.py', 'runserver', '0.0.0.0:8000']
-[2026-02-09T07:42:36.626917] Loaded .env
-[2026-02-09T07:42:37.207600] Django setup complete.
-[2026-02-09T07:42:37.207759] Executing command line...
-[2026-02-09T07:42:38.535239] SystemExit caught: 3
diff --git a/test_pos_render.py b/test_pos_render.py
deleted file mode 100644
index c1d7964..0000000
--- a/test_pos_render.py
+++ /dev/null
@@ -1,52 +0,0 @@
-import os
-import django
-from django.conf import settings
-
-os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
-django.setup()
-
-from django.template.loader import render_to_string
-from django.test import RequestFactory
-from core.models import Product, SystemSetting, Category, PaymentMethod
-
-def test_pos_render():
- factory = RequestFactory()
- request = factory.get('/pos/')
-
- s_settings = SystemSetting.objects.first()
- products = Product.objects.filter(is_active=True)
- categories = Category.objects.all()
- payment_methods = PaymentMethod.objects.all()
-
- context = {
- 'products': products,
- 'customers': [],
- 'categories': categories,
- 'payment_methods': payment_methods,
- 'settings': s_settings,
- 'site_settings': s_settings,
- 'active_session': None,
- 'LANGUAGE_CODE': 'en'
- }
-
- rendered = render_to_string('core/pos.html', context, request=request)
-
- print(f"Total Products Checked: {products.count()}")
- # Check for image URLs
- for product in products:
- if product.image:
- url = product.image.url
- if url in rendered:
- print(f"Product {product.name_en} image URL FOUND: {url}")
- else:
- # Check for escaped URL
- from django.utils.html import escape
- if escape(url) in rendered:
- print(f"Product {product.name_en} image URL FOUND (escaped): {escape(url)}")
- else:
- print(f"Product {product.name_en} image URL MISSING: {url}")
- else:
- print(f"Product {product.name_en} has no image in DB")
-
-if __name__ == "__main__":
- test_pos_render()
\ No newline at end of file
diff --git a/test_render.py b/test_render.py
deleted file mode 100644
index f444fd7..0000000
--- a/test_render.py
+++ /dev/null
@@ -1,57 +0,0 @@
-import os
-import django
-from django.conf import settings
-from django.template.loader import render_to_string
-from django.test import RequestFactory
-
-os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
-django.setup()
-
-from core.models import SystemSetting, Product, Customer, Sale, Category, PaymentMethod
-from django.utils import timezone
-import datetime
-
-def test_render():
- factory = RequestFactory()
- request = factory.get('/')
- # Simulate a user
- from django.contrib.auth.models import User
- user = User.objects.first()
- request.user = user
-
- settings_obj = SystemSetting.objects.first()
- context = {
- 'site_settings': settings_obj,
- 'settings': settings_obj,
- 'total_sales_amount': 0,
- 'total_receivables': 0,
- 'total_payables': 0,
- 'total_sales_count': 0,
- 'total_products': 0,
- 'total_customers': 0,
- 'monthly_labels': [],
- 'monthly_data': [],
- 'chart_labels': [],
- 'chart_data': [],
- 'category_labels': [],
- 'category_data': [],
- 'payment_labels': [],
- 'payment_data': [],
- 'top_products': [],
- 'low_stock_count': 0,
- 'low_stock_products': [],
- 'expired_count': 0,
- 'recent_sales': [],
- }
-
- try:
- html = render_to_string('core/index.html', context, request=request)
- print(f"Render successful, length: {len(html)}")
- if len(html) < 100:
- print("HTML is too short!")
- print(html)
- except Exception as e:
- print(f"Render failed: {e}")
-
-if __name__ == "__main__":
- test_render()
diff --git a/test_write.txt b/test_write.txt
deleted file mode 100644
index 268af4e..0000000
--- a/test_write.txt
+++ /dev/null
@@ -1 +0,0 @@
-modified content
\ No newline at end of file
diff --git a/wsgi_crash.txt b/wsgi_crash.txt
deleted file mode 100644
index 4143b0d..0000000
--- a/wsgi_crash.txt
+++ /dev/null
@@ -1,30 +0,0 @@
-WSGI Crash: No module named 'whitenoise'
-Traceback (most recent call last):
- File "/home/ubuntu/executor/workspace/config/wsgi.py", line 19, in
- application = get_wsgi_application()
- ^^^^^^^^^^^^^^^^^^^^^^
- File "/home/ubuntu/.local/lib/python3.11/site-packages/django/core/wsgi.py", line 13, in get_wsgi_application
- return WSGIHandler()
- ^^^^^^^^^^^^^
- File "/home/ubuntu/.local/lib/python3.11/site-packages/django/core/handlers/wsgi.py", line 118, in __init__
- self.load_middleware()
- File "/home/ubuntu/.local/lib/python3.11/site-packages/django/core/handlers/base.py", line 40, in load_middleware
- middleware = import_string(middleware_path)
- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- File "/home/ubuntu/.local/lib/python3.11/site-packages/django/utils/module_loading.py", line 30, in import_string
- return cached_import(module_path, class_name)
- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- File "/home/ubuntu/.local/lib/python3.11/site-packages/django/utils/module_loading.py", line 15, in cached_import
- module = import_module(module_path)
- ^^^^^^^^^^^^^^^^^^^^^^^^^^
- File "/usr/lib/python3.11/importlib/__init__.py", line 126, in import_module
- return _bootstrap._gcd_import(name[level:], package, level)
- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- File "", line 1206, in _gcd_import
- File "", line 1178, in _find_and_load
- File "", line 1128, in _find_and_load_unlocked
- File "", line 241, in _call_with_frames_removed
- File "", line 1206, in _gcd_import
- File "", line 1178, in _find_and_load
- File "", line 1142, in _find_and_load_unlocked
-ModuleNotFoundError: No module named 'whitenoise'
diff --git a/wsgi_status.txt b/wsgi_status.txt
deleted file mode 100644
index b5be6c0..0000000
--- a/wsgi_status.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-[2026-02-09T07:28:46.937704] WSGI module loading...
-[2026-02-09T07:28:46.948377] WSGI loaded .env
-[2026-02-09T07:28:46.952251] WSGI application created successfully.
-[2026-02-09T07:42:37.533157] WSGI module loading...
-[2026-02-09T07:42:37.541628] WSGI loaded .env
-[2026-02-09T07:42:37.544967] WSGI application created successfully.
-[2026-02-09T07:42:39.272623] WSGI module loading...
-[2026-02-09T07:42:39.280940] WSGI loaded .env
-[2026-02-09T07:42:39.283449] WSGI application created successfully.
diff --git a/wsgi_trace.txt b/wsgi_trace.txt
deleted file mode 100644
index 3f6e51a..0000000
--- a/wsgi_trace.txt
+++ /dev/null
@@ -1 +0,0 @@
-WSGI loaded