From 3360fbcad23f14339fea3aec71a26c8cfac9dd51 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Fri, 13 Feb 2026 03:03:06 +0000 Subject: [PATCH] Autosave: 20260213-030304 --- config/__pycache__/settings.cpython-311.pyc | Bin 5034 -> 5141 bytes config/__pycache__/wsgi.cpython-311.pyc | Bin 3471 -> 2438 bytes config/settings.py | 2 + config/wsgi.py | 31 ++-------- core/__pycache__/views.cpython-311.pyc | Bin 133681 -> 136795 bytes core/templates/base.html | 1 - core/views.py | 62 ++++++++++++++++++-- manage.py | 29 ++------- test_pos_render.py | 52 ++++++++++++++++ test_render.py | 57 ++++++++++++++++++ 10 files changed, 179 insertions(+), 55 deletions(-) create mode 100644 test_pos_render.py create mode 100644 test_render.py diff --git a/config/__pycache__/settings.cpython-311.pyc b/config/__pycache__/settings.cpython-311.pyc index 4ea532974b069805fb54a356d1720d7b2b37544f..d5408c1eae873cf40085dac1db5453e382c58d9f 100644 GIT binary patch delta 386 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 delta 7035 zcmb_hd3aP+vQORa+ex|u*%K17GznxO3j_$r61I=U2uN5I9D&f14#cdSgjF=?pn#(Y zT;Q+_0-~~Mll8lc|AOrJUmJjZfv)U(fm({=%eq1@Nl2d1EW~pGn!O<|)9#&S7VX{>Lmz52 zdnPhHVs8(17po0%t=M}^q4zYgZy2-p>`SnI!t4UHUM`=Hpir0Z=YLmNbY%atv`HIs zV7M1TPV^B;+UClR+SvpC{ze}jWv(a(RFZr?ivqP~2LshlnK8xH_TY8$T7_n+NXF1n z#A>ZiRgAiidGlTQRg$a+nOqFFBN`Ig=g1FA6>09nE$&wTFe|<8avVKCv`=e$Y#3E( zvyQbtOW=#bQ`Lt0kWuN$0c|&$hOH$69BgRUt1mr2#JUxsk*MM%Sd9 z&#_2*tvXbDx5meEkw0opu3a_z70bVv{>kK8c)>?euP{xnwU^qO{f;pyjsw01e50NH zAtu~#H~;}RjuE31C$#9=FNz`eL1nupj3USk)2XI>&PS37kxq_6l5(m~L> z1L~d_xN)MF*agvMh>V%hXy@LTcK-fan?{{`BnY|UkN#EgDm}D`C&CslrXbaezjL=# zP==*G7xJown%RtM^hDFIWsoN&)3>shCv9zRRK*Cz3PZ;DvVrnZv>|Dc&3(r?N+;wN z6i>+(&XQtLCYrEt337D}xzbm8(eoy&A2WA?Im;Mf+QI5*eUuMb74ntio6a7S${5o4v^|Lm6i!PB!~tpXU+1BAbQi%g5{^o z`oLhyRjl#+aS`5LhPNR7P$3tP~W3T4R8VyUaO2eTqzdl{CaX7Yyw z>M8>gDO65vNf)WRyq!p4@|jlTZSBRX@vM5i9aIza4_Z+aQD^yO91XPgVPOhfb(PgA z6s-4*r+lJ~@|^^-tNmHnUav}^M52DM7-P+3RyXMPUSg+U-Wa@P-_}%1-^=tQ8mbOp zac}ub61`)o?@;$-YGr$r*$<=D&m>Q$P;c8HX58)aN68d#%VNgBdJVEuker@Esh;&} zazQ8s%FGlBunlFstOr?grBJMGIBN`lkVa}MNAoyqjJ(H}R&v~1eEjB z1b9+zY)fO*(M*rho1{^)spb6|P}`4DaTL%DFiMW@KrR2%)`AYyPjzsEJpDum+C(%; z&gn?elr7hH)$z*+hN1CTtHz=Y8rcQgvF`enUR=GB?RX;Go;jbFh?m9WI1fF6O z({6K)qs$>+>_P+7iL5n4|FR459yMPs%b=O+6lN{gqq@^OMAPKwJ*a!&G-kPN6Uqt; zvU!r^6^$1&&$U0#ied@iKA?Sh?LDAp&qGm;F^m|oJDSRipVQ-is!)b9@Mu_X0PnWh7J_{k&U@9 z7p} zLC;~}v~u@6L>E9qY{G_A`%RowT;?dtD=x||npF6dQwR^9zKIs25F!Foc|M>LZC{4P zzaX>8miL&xq3VCjqC89JJP2|b+&+!=@^WP22x^frA40>(yU_40a~76lH>i~k7Yo6* zHgK}V88};vW=rJk5fs})g1$fT;4NcFj1}La0ZzCg2f@}CgQ-*CEteNZP^6===PS_k zuYgFvO2F%YRRAPF1OkH5dNlxL6RQDh0H~RG17K9E0#tpqdK=Z+49-=wY|P$T@ZJQx z1$Y~<4zM0zOp*PuWRlESQ&%(#QG`JBugKuLQ0CuoF8*`&zoG+rvB2h;w@GFJJ; zuOuh8O+iUJP`FGVcux6NBqY~=!nSb@*($)rZ_(sxwpnk#_+GOy0d>wb1()k}px298pAF_}{`JinZ=xwQ{Hx%he@3n2s0G%DRh5{eD+oOo`S{ zS!*t9iF9LEmAtEZ%R|NVJnfc4ODK-5O)H^Dbsy`!Dp!=ybXEM7gOO1ac?5mITuslM zNXt!hNS-UBTU`z@OH=`l0FDEiBKohGD)r(V+w1@8ZktD;09dAnOs4q?eIwURWz#2^ zrR#^M(iz3)OXy&5^b@D$$InnpIwotL;l2Kt^qoN&{BO_7x=rPFfn^EAir z6k2_aqUOTCRc60HE8-ft=OKs4?OWoQR>2N#d6_zgV(2?LWDY$PZipKkhzoFK1zePCSKJcfowo3W&&a0@&mU4~`; zs_%x;>k;@gGG=7Q$kPpZ`E6=OKS|3vY8&3zA7i;`i3W8&lUeI1(sC2_J!R=SifLn{ z$FO_9NhlesMk!({1MvFsk9CxiR0P@M;N&s5z4;_in2lqPAnv6Y1&QG#jAX?e3X$#B zQ-C#)WiKEDlVvdyFnvAU_AYNsfYF_+Fm3dSiTa^%jY+pSt+UWS*U38L5y}+o@&CDkDf#=fTNE?A^UY1af9JX)R)doaASrh+8&(8 zXjyfLni&&+gXwO@WY*`DpvJNNw)(T5(@%<;!gM!%>H&I4p_X#={}26+HTr!w?&RZqe`?Q`#_B_kQ=rMy2}^tr z$#+wahMsPAQ&Qa$X>Q2WB{y}jrE|*?RKi9WSK)Hbc=FSSeM5gDYA-8JP;2!eR(VlB zd4l?wA{D0VCcN30&2H~;4pCOO4k@yGHN`9JdVDp-G`OB$O<8rry6&glJ++Z=F*1Lx;J<1}sf=_<2oDAQt0`7Ux*4Zpt_{4>aASGl`}ugMt*sHe$z zUzFDK{BqNmIh*v@^R&W5irjUPLLNh_>HMlBiiKS`$2kfL_{L{1brd+e*!$IuGrziH zVrQ8C9?*C%=)$et*1XcZqS7))(Kx4w!sJq9>q``qYz*}JZ6^YJV~IAR8jDY3OEIG6 zO=;>SN>el0+%CQ15_u`qU+Vv&L6!l`I_afbFH^9Y7E1e7UQ=F@DrvfR;WXyhQzqEO<4_&}7>O1pdpEmyLcaS8 zbyr54w5-}tX}(QS<;LY|4AlPETagXkDva*2pwL&|p(ac!$}g?EWI4co3Siug>hr*# zeq0H+MK}wKCp-C_dJ__q54~}K9Doy$%bfd|FaC9J zmpUlC-isZ8U4Tyjcqtcn4;Od|7kKFwc-I!E0C$UL`~U%fAOKzrMGHVYpfw;F&=$}E&@kd~b7ud=LHc=4+zy=|(GYBmC0xPk=A}X*R3aoenOOwE2 zA~0hG=BU7&5khV^E0F=OFwH*sXyxzWam7SLa)_$fb(^Yur%Icz5+(g?N=a;!ugLd` gqEONm4~DBI55;d*6#g}t)%=PXd>?(KP1$MvH;1DlCIA2c 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()