diff --git a/config/__pycache__/settings.cpython-311.pyc b/config/__pycache__/settings.cpython-311.pyc
index 4ea5329..d5408c1 100644
Binary files a/config/__pycache__/settings.cpython-311.pyc and b/config/__pycache__/settings.cpython-311.pyc differ
diff --git a/config/__pycache__/wsgi.cpython-311.pyc b/config/__pycache__/wsgi.cpython-311.pyc
index cc90925..2513855 100644
Binary files a/config/__pycache__/wsgi.cpython-311.pyc and b/config/__pycache__/wsgi.cpython-311.pyc differ
diff --git a/config/settings.py b/config/settings.py
index bc8b083..f3194d3 100644
--- a/config/settings.py
+++ b/config/settings.py
@@ -155,6 +155,8 @@ SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SESSION_COOKIE_SAMESITE = "None"
CSRF_COOKIE_SAMESITE = "None"
+SECURE_CROSS_ORIGIN_OPENER_POLICY = None
+SECURE_REFERRER_POLICY = 'no-referrer-when-downgrade'
# X_FRAME_OPTIONS = 'SAMEORIGIN'
# Email Settings
diff --git a/config/wsgi.py b/config/wsgi.py
index 28f3157..325a2b3 100644
--- a/config/wsgi.py
+++ b/config/wsgi.py
@@ -20,12 +20,11 @@ try:
except ImportError:
pass
-# --- FIX: Preload libraries for WeasyPrint/Pango ---
-# Manually load libraries using absolute paths
-import ctypes
-import ctypes.util
+# --- WeasyPrint Library Preloading ---
import traceback
import sys
+import ctypes
+import ctypes.util
lib_paths = [
'/usr/lib/x86_64-linux-gnu/libglib-2.0.so.0',
@@ -42,31 +41,13 @@ for path in lib_paths:
except OSError:
pass
-# Try to import weasyprint
try:
import weasyprint
except Exception as e:
- # Log error to file and stderr
error_msg = f"WeasyPrint Import Error: {str(e)}\n{traceback.format_exc()}"
- sys.stderr.write(error_msg)
- try:
- with open("weasyprint_wsgi_error.log", "w") as f:
- f.write(error_msg)
- except Exception:
- pass
-
- # Fallback to mock
- import types
- class MockHTML:
- def __init__(self, string=None, base_url=None):
- pass
- def write_pdf(self, target=None):
- raise OSError("WeasyPrint system dependencies are missing. PDF generation is disabled.")
-
- mock_wp = types.ModuleType("weasyprint")
- mock_wp.HTML = MockHTML
- sys.modules["weasyprint"] = mock_wp
-# ---------------------------------------------------
+ with open("/tmp/weasyprint_wsgi_error.log", "w") as f:
+ f.write(error_msg)
+# -------------------------------------
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc
index 50c3078..78c0708 100644
Binary files a/core/__pycache__/views.cpython-311.pyc and b/core/__pycache__/views.cpython-311.pyc differ
diff --git a/core/templates/base.html b/core/templates/base.html
index d72fcb3..e8c5dcc 100644
--- a/core/templates/base.html
+++ b/core/templates/base.html
@@ -34,7 +34,6 @@
{% block head %}{% endblock %}
-
{% if user.is_authenticated %}
diff --git a/core/views.py b/core/views.py
index 0641668..601c940 100644
--- a/core/views.py
+++ b/core/views.py
@@ -636,7 +636,12 @@ def sale_receipt(request, pk):
@login_required
def quotations(request):
- quotations = Quotation.objects.all().order_by('-created_at')
+ quotations_qs = Quotation.objects.all().order_by('-created_at')
+
+ paginator = Paginator(quotations_qs, 20)
+ page_number = request.GET.get('page')
+ quotations = paginator.get_page(page_number)
+
return render(request, 'core/quotations.html', {'quotations': quotations})
@login_required
@@ -690,11 +695,60 @@ def delete_quotation(request, pk):
messages.success(request, _("Quotation deleted."))
return redirect('quotations')
-@csrf_exempt
@login_required
def create_quotation_api(request):
- # Simplified API stub
- return JsonResponse({'success': True})
+ if request.method != 'POST':
+ return JsonResponse({'success': False, 'error': 'Method not allowed'})
+
+ try:
+ data = json.loads(request.body)
+ customer_id = data.get('customer_id')
+ quotation_number = data.get('quotation_number')
+ items = data.get('items', [])
+ total_amount = data.get('total_amount', 0)
+ discount = data.get('discount', 0)
+ valid_until = data.get('valid_until')
+ terms_and_conditions = data.get('terms_and_conditions', '')
+ notes = data.get('notes', '')
+
+ if not items:
+ return JsonResponse({'success': False, 'error': _('Cannot save an empty quotation.')})
+
+ with transaction.atomic():
+ customer = None
+ if customer_id:
+ customer = Customer.objects.get(id=customer_id)
+
+ quotation = Quotation.objects.create(
+ customer=customer,
+ quotation_number=quotation_number,
+ total_amount=total_amount,
+ discount=discount,
+ valid_until=valid_until if valid_until else None,
+ terms_and_conditions=terms_and_conditions,
+ notes=notes,
+ created_by=request.user
+ )
+
+ for item in items:
+ product = Product.objects.get(id=item['id'])
+ QuotationItem.objects.create(
+ quotation=quotation,
+ product=product,
+ quantity=item['quantity'],
+ unit_price=item['price'],
+ line_total=item['line_total']
+ )
+
+ messages.success(request, _("Quotation created successfully."))
+ return JsonResponse({'success': True, 'quotation_id': quotation.id})
+ except Customer.DoesNotExist:
+ return JsonResponse({'success': False, 'error': _('Customer not found.')})
+ except Product.DoesNotExist:
+ return JsonResponse({'success': False, 'error': _('One or more products not found.')})
+ except Exception as e:
+ logger.error(f"Error creating quotation: {str(e)}")
+ return JsonResponse({'success': False, 'error': str(e)})
# --- Sales Returns ---
diff --git a/manage.py b/manage.py
index fcd4440..f3a0730 100755
--- a/manage.py
+++ b/manage.py
@@ -14,9 +14,7 @@ def main():
except ImportError:
pass
- # --- FIX: Preload libraries for WeasyPrint/Pango ---
- # Manually load libraries using absolute paths since LD_LIBRARY_PATH
- # changes don't affect dlopen() in the running process.
+ # --- WeasyPrint Library Preloading ---
import ctypes
import ctypes.util
import traceback
@@ -34,34 +32,15 @@ def main():
try:
ctypes.CDLL(path)
except OSError:
- # Continue even if one fails
pass
- # Try to import weasyprint to see if it works
try:
import weasyprint
except Exception as e:
- # Log the specific error for debugging
error_msg = f"WeasyPrint Import Error: {str(e)}\n{traceback.format_exc()}"
- sys.stderr.write(error_msg)
- try:
- with open("weasyprint_error.log", "w") as f:
- f.write(error_msg)
- except Exception:
- pass
-
- # Fallback to mock if system dependencies are missing or import fails
- import types
- class MockHTML:
- def __init__(self, string=None, base_url=None):
- pass
- def write_pdf(self, target=None):
- raise OSError("WeasyPrint system dependencies are missing. PDF generation is disabled.")
-
- mock_wp = types.ModuleType("weasyprint")
- mock_wp.HTML = MockHTML
- sys.modules["weasyprint"] = mock_wp
- # ---------------------------------------------------
+ with open("/tmp/weasyprint_error.log", "w") as f:
+ f.write(error_msg)
+ # -------------------------------------
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
try:
diff --git a/test_pos_render.py b/test_pos_render.py
new file mode 100644
index 0000000..c1d7964
--- /dev/null
+++ b/test_pos_render.py
@@ -0,0 +1,52 @@
+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
new file mode 100644
index 0000000..f444fd7
--- /dev/null
+++ b/test_render.py
@@ -0,0 +1,57 @@
+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()