From 51822816f62c791e9b0d49395ed98c38b6958329 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Fri, 13 Feb 2026 05:35:53 +0000 Subject: [PATCH] Autosave: 20260213-053553 --- Aptfile | 7 - Procfile | 1 - append_reports.py | 24 -- apply_patch.py | 58 ---- config/wsgi.py | 15 - core/__pycache__/forms_import.cpython-311.pyc | Bin 569 -> 569 bytes core/__pycache__/views.cpython-311.pyc | Bin 141609 -> 141704 bytes core/__pycache__/views_import.cpython-311.pyc | Bin 11916 -> 11916 bytes core/patch_views.py | 76 ----- core/patch_views_missing.py | 92 ------- core/patch_views_pos.py | 35 --- core/patch_views_sales_list.py | 31 --- core/patch_views_vat.py | 161 ----------- core/pdf_utils.py | 55 ++++ core/views.py | 6 +- debug_accounting.py | 31 --- debug_request.py | 54 ---- debug_settings.py | 38 --- debug_url.py | 22 -- force_reset_admin.py | 32 --- manage.py | 17 +- manage_trace.txt | 1 - manual_db_fix.py | 32 --- move_project.py | 1 - patch_base_html.py | 49 ---- patch_expense_categories.py | 43 --- patch_invoice_list.py | 52 ---- patch_models_timestamp.py | 43 --- patch_payments_v2.py | 211 -------------- patch_pos_view.py | 34 --- patch_returns_setup.py | 17 -- patch_returns_v3.py | 159 ----------- patch_settings_html.py | 260 ------------------ patch_views.py | 85 ------ patch_views_expense_edit.py | 30 -- patch_views_fixes.py | 134 --------- patch_views_sales.py | 206 -------------- restore_views.py | 220 --------------- settings_trace.txt | 1 - startup_status.txt | 18 -- test_pos_render.py | 52 ---- test_render.py | 57 ---- test_write.txt | 1 - wsgi_crash.txt | 30 -- wsgi_status.txt | 9 - wsgi_trace.txt | 1 - 46 files changed, 60 insertions(+), 2441 deletions(-) delete mode 100644 Aptfile delete mode 100644 Procfile delete mode 100644 append_reports.py delete mode 100644 apply_patch.py delete mode 100644 core/patch_views.py delete mode 100644 core/patch_views_missing.py delete mode 100644 core/patch_views_pos.py delete mode 100644 core/patch_views_sales_list.py delete mode 100644 core/patch_views_vat.py create mode 100644 core/pdf_utils.py delete mode 100644 debug_accounting.py delete mode 100644 debug_request.py delete mode 100644 debug_settings.py delete mode 100644 debug_url.py delete mode 100644 force_reset_admin.py delete mode 100644 manage_trace.txt delete mode 100644 manual_db_fix.py delete mode 100644 move_project.py delete mode 100644 patch_base_html.py delete mode 100644 patch_expense_categories.py delete mode 100644 patch_invoice_list.py delete mode 100644 patch_models_timestamp.py delete mode 100644 patch_payments_v2.py delete mode 100644 patch_pos_view.py delete mode 100644 patch_returns_setup.py delete mode 100644 patch_returns_v3.py delete mode 100644 patch_settings_html.py delete mode 100644 patch_views.py delete mode 100644 patch_views_expense_edit.py delete mode 100644 patch_views_fixes.py delete mode 100644 patch_views_sales.py delete mode 100644 restore_views.py delete mode 100644 settings_trace.txt delete mode 100644 startup_status.txt delete mode 100644 test_pos_render.py delete mode 100644 test_render.py delete mode 100644 test_write.txt delete mode 100644 wsgi_crash.txt delete mode 100644 wsgi_status.txt delete mode 100644 wsgi_trace.txt 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 200f5f2eb9dd65f116b881bfc06cf96987f393cf..2f953e93b8aefb4dcce92480294cd706c8fe9f56 100644 GIT binary patch delta 20 acmdnVvXg~-IWI340}u#r@7u_&#{>W~LIjln delta 20 acmdnVvXg~-IWI340}!YlZr#YO#{>W~+XSir diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index 68a3e28a56821a3174ad4bb5b9aab009d1b28634..4e9bdf3b046cfe968deca9fc578e700364f04cf9 100644 GIT binary patch delta 24088 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�vg_Lke}eoGHplZVYQi!DBjTCFEW(P^TXsB6>hY83B~K?7 zqcwHd5HV|%2+~06Tix@V=o0f*Q9=%kgDTdfrC6SaER72E$0}wwmJW&~0IWsskHZhv zy~Q=llo>X8MXYZz`wghB0iV|baOBQ5!*+B%$~gXvYJlF3n29!=v5C((-DmJ}}+JQVmotJA6OBVf%5JKqu+sh)N)4VO7VO-bmV z7JoK$UdoukPlaFo+#A6{IjU4Ttl>J0b_M`ByOS%f`_F9TKnkl?h&2z zkKYwV;$Ho`cPRq|-(JHmq`LHUTy`%Ivy^AK?B1w2wFY6^JWTcqJZenh!D^mj0+V%G z8sxEC+JcW^?i?GM4=@ zn3E-}4?G}-h_GIJ;D3;7KM+nP*Z!#ZujM)@xm@WB3{t%=#I7w?tiwJKuFN4cvMpr9 zfdTrFJz{G3@ejnO!Zy&ZUsEee!jFC^?ldnvD3jg|-ZaBc% zzP(*^c8nr+cI)r7i`yc(IuhGD5t75GB64phnOt1xEWrYza*@NSwbd1&8YGLl=xaSn z6r%}gFMQBh^dQeBP)ls`pPsQ!CMLR%MJqfDpt237JWXyjN@o;=^upqWNU#%Xsn zO5ilfA%JCq@!VIcv>4crT;);hk0pi=!uo_zn!MyQ@s2GyyHmjKREyo8F}TVk?Eai! zbHEvY^M>s!YaFrlCD{7AX<+YvA^xvoYl6HEd~Am?Sfa#9Z;d<))?&uXY=BV&I%Pc! zo|4ISP!eY2Tbp7<)I{X1*@Cy))Y^tL1-vCW%h_Lwu2~ak06$;=SuEF1deaojH*Wiq zF2)ojSk5e+@6(87_+%r4KL*4>7wU{ud=7^Ki%mx=c*_#I0YHmm(TR9Ino>?tj?$-|&?j58x#`kE+Z zz|JU*nnJSlryNqwVt)h8LVRxp(Zak7fX9oyqcW zVwf0#;cPH$(~ZRO%O`ZBw1p}0>!Xtx#5 zhfIuY`j{r&7xGH};-Zs^l)fM z*xP;wZrG0CGz?@M1IvAMmc1S0^rLOO)MPWt_-tt@o**;Yu`O4RNu(Sl6g^6% zMXXiDh3Q3~^=kAl^U{l4JmppaI|$GMunb^1zzY4}IkrmYN<3HNeL;0!CeM_JCE;rc z4lfR1-89o*&=uf@hm0D3RkwuhD*Fe} z1&@VFKik8j3T>-J=sk$%Qh<2?uL1lS06~s=|Mdi((lIB?00se!1{e!40bnA)6#$4l z_`_uU%{KmMn!FKZYXLL>e3?ppEb4v{qQ?jD`?mo2G7o<&hd;6+U&7Dd1N;Htb%5Of zdjJjqddKjTngxLSsqzYd`2e`H`O23QgTYy|KFECs*@ zPU7~NTm`TiU=0AS2E`nVZoN{68l?;ZI>JZfT&v{)|Ci4B|VAtK^bTi WM_~v3#U3_S_`x2wHL3g=#{UPw8?bQz delta 23785 zcmb_k34ByV(ofH1CM1)aBL_z&gaCsCL;+Dj4naWyK@d>TagrHwkjxA{6Cn`^c%KM9 z@qyRkx}ORvg3-;2_qVICyNaN!;!{>x*V_eec5%_K{?%{hO@;y8{eAgq(XWr{>Zgw*f{*5jhU+l;9m%xg&z3FAUI{&AQ>U!tUIaFw5`hAhQ>X1+RO)u8)l+x7 z%J;a+ccquhdF`TGfh%Vrl@Z;JXejDyysf-hEY#MP^%rxrXUYbNS=z^Cy;z<=Lod)ECBPB_M>o)3D0gV<`g+nqfwsSI zsrFvq%c@%G!^pWHSmTQZYD2QVHQ?W9^;>f^8d0a+S25TcdqTfgrtWt`g+r_9=b
  • d3nR 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 Tt@o<5xjSr?wky;5kn;Zkq99-o diff --git a/core/__pycache__/views_import.cpython-311.pyc b/core/__pycache__/views_import.cpython-311.pyc index 2c4af01f1db8372319d0d841605555b26cd01c0e..0010464fd2b2cf7c678dbfc1594303ddfc235fd7 100644 GIT binary patch delta 20 acmeB)?TO`H&dbZi00hF@`!;en>Hz>b>IG~7 delta 20 acmeB)?TO`H&dbZi00cJYIyQ1Q>Hz>dbp@sX 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 %} -
    - - -
    -
    - {% csrf_token %} - - - -
    -
    -
    - {% 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 "Connected Devices" %}
    - -
    -
    -
    - - - - - - - - - - - - - {% for device in devices %} - - - - - - - - - - - - - - - - {% empty %} - - - - {% endfor %} - -
    {% trans "Device Name" %}{% trans "Type" %}{% trans "Connection" %}{% trans "IP / Port" %}{% trans "Status" %}{% trans "Actions" %}
    {{ 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 "No devices configured." %} -
    -
    -
    -
    -
    -
    - " - - 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