diff --git a/inventory/utils.py b/inventory/utils.py index 4c8caf36..4cd9aa08 100644 --- a/inventory/utils.py +++ b/inventory/utils.py @@ -1329,7 +1329,9 @@ def get_finance_data(estimate, dealer): additional_services = car.get_additional_services() discounted_price = Decimal(car.marked_price) - discount vat_amount = discounted_price * vat.rate + total_services_amount=additional_services.get("total") total_services_vat = sum([x[1] for x in additional_services.get("services")]) + total_services_amount_=additional_services.get("total_") total_vat = vat_amount + total_services_vat return { "car": car, @@ -1340,9 +1342,16 @@ def get_finance_data(estimate, dealer): "discount_amount": discount, "additional_services": additional_services, "final_price": discounted_price + vat_amount, + + + "total_services_vat": total_services_vat, + "total_services_amount":total_services_amount, + "total_services_amount_":total_services_amount_, + "total_vat": total_vat, "grand_total": discounted_price + total_vat + additional_services.get("total"), + } # totals = self.calculate_totals() diff --git a/inventory/views.py b/inventory/views.py index aecbc742..99532ed3 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -4656,7 +4656,7 @@ def sales_list_view(request, dealer_slug): search_query = request.GET.get('q', None) if search_query: qs = qs.filter( - Q(order_number__icontains=search_query)| + Q(customer__phone_number__icontains=search_query)| Q(customer__customer_name__icontains=search_query) ).distinct() @@ -5092,6 +5092,7 @@ class EstimateDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView def get_context_data(self, **kwargs): dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"]) estimate = kwargs.get("object") + if estimate.get_itemtxs_data(): # calculator = CarFinanceCalculator(estimate) # finance_data = calculator.get_finance_data() @@ -5099,6 +5100,8 @@ class EstimateDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView invoice_obj = InvoiceModel.objects.all().filter(ce_model=estimate).first() kwargs["data"] = finance_data + kwargs["customer_obj"]=estimate.customer.customer_set.first() + kwargs['dealer_info']=dealer kwargs["invoice"] = invoice_obj try: @@ -5119,8 +5122,9 @@ class EstimatePrintView(EstimateDetailView): It reuses the data-fetching logic from EstimateDetailView but uses a dedicated, stripped-down print template. """ - template_name = "sales/estimates/estimate_preview.html" + + def get(self, request, *args, **kwargs): self.object = self.get_object() @@ -5128,17 +5132,20 @@ class EstimatePrintView(EstimateDetailView): # lang = request.GET.get('lang', 'ar') - - - template_path = "sales/estimates/estimate_preview.html" - - + + + if request.GET.get('lang')=='en': + template_path = "sales/estimates/estimate_preview_en.html" + else: + template_path = "sales/estimates/estimate_preview_ar.html" + + html_string = render_to_string(template_path, context) - - - pdf_file = HTML(string=html_string).write_pdf() - - + + base_url = request.build_absolute_uri('/') + pdf_file = HTML(string=html_string, base_url=base_url).write_pdf() + + response = HttpResponse(pdf_file, content_type='application/pdf') response['Content-Disposition'] = f'attachment; filename="estimate_{self.object.estimate_number}.pdf"' @@ -5522,7 +5529,7 @@ class InvoiceListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"]) entity = dealer.entity staff = getattr(self.request.user, "staff", None) - qs = [] + qs=None try: if any( [ @@ -5871,7 +5878,6 @@ class InvoicePreviewView(LoginRequiredMixin, PermissionRequiredMixin, DetailView model = InvoiceModel context_object_name = "invoice" - template_name = "sales/invoices/invoice_preview.html" permission_required = ["django_ledger.view_invoicemodel"] def get_context_data(self, **kwargs): @@ -5881,8 +5887,37 @@ class InvoicePreviewView(LoginRequiredMixin, PermissionRequiredMixin, DetailView # calculator = CarFinanceCalculator(invoice) finance_data = get_finance_data(invoice,dealer) kwargs["data"] = finance_data - kwargs["dealer"] = dealer + kwargs["dealer_info"] = dealer + kwargs["customer_obj"]=invoice.customer.customer_set.first() return super().get_context_data(**kwargs) + def get(self, request, *args, **kwargs): + + self.object = self.get_object() + context = self.get_context_data(object=self.object) + + + # lang = request.GET.get('lang', 'ar') + + + if request.GET.get('lang')=='en': + template_path = "sales/invoices/invoice_preview_en.html" + elif request.GET.get('lang')=='ar': + template_path = "sales/invoices/invoice_preview_ar.html" + else: + # just for preview not for download + return render(request,'sales/invoices/invoice_preview.html',context) + + + html_string = render_to_string(template_path, context) + + base_url = request.build_absolute_uri('/') + pdf_file = HTML(string=html_string, base_url=base_url).write_pdf() + + + response = HttpResponse(pdf_file, content_type='application/pdf') + response['Content-Disposition'] = f'attachment; filename="invoice_{self.object.invoice_number}.pdf"' + + return response # payments @@ -6221,10 +6256,10 @@ class LeadListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): | Q(last_name__icontains=query) | Q(id_car_make__name__icontains=query) | Q(id_car_model__name__icontains=query) - | Q(email__icontains=query) | Q(phone_number__icontains=query) | Q(next_action__icontains=query) - | Q(staff__name__icontains=query) + | Q(staff__first_name__icontains=query) + | Q(staff__last_name__icontains=query) ) if self.request.is_dealer: # or self.request.is_manager: @@ -11020,6 +11055,9 @@ class PurchaseOrderDetailView(LoginRequiredMixin, PermissionRequiredMixin, Detai for i in po_items_qs.values("po_total_amount", "po_item_status") if i["po_item_status"] != "cancelled" ) + items = [{"total": x.total_amount, "q": x.quantity} for x in po_model.get_itemtxs_data()[0].all()] + po_quantity = sum(item["q"] for item in items) + context['po_quantity']=po_quantity return context def get(self, request, *args, **kwargs): self.object = self.get_object() @@ -11048,12 +11086,11 @@ class PurchaseOrderDetailView(LoginRequiredMixin, PermissionRequiredMixin, Detai "purchase_orders/po_detail_ar_pdf.html", context ) - - - - # Use WeasyPrint to generate the PDF - pdf = HTML(string=html_string).write_pdf() - + + + base_url = request.build_absolute_uri('/') + pdf = HTML(string=html_string, base_url=base_url).write_pdf() + response = HttpResponse(pdf, content_type="application/pdf") response["Content-Disposition"] = f'attachment; filename="PO_{self.object.po_number}.pdf"' return response diff --git a/templates/groups/group_detail.html b/templates/groups/group_detail.html index b70abb6b..d4f269c8 100644 --- a/templates/groups/group_detail.html +++ b/templates/groups/group_detail.html @@ -35,7 +35,8 @@
-

{{ _("Group Details") }}

+

{{ _("Group Details") }}

+ {% trans "Group List" %}
diff --git a/templates/organizations/organization_list.html b/templates/organizations/organization_list.html index 8c307642..783740d8 100644 --- a/templates/organizations/organization_list.html +++ b/templates/organizations/organization_list.html @@ -125,7 +125,7 @@
- +
{{ org.created|date }} - + {% if perms.inventory.change_organization or perms.inventory.delete_organization %}
{% endif %}
diff --git a/templates/purchase_orders/po_detail_ar_pdf.html b/templates/purchase_orders/po_detail_ar_pdf.html index 1bc3809e..a1dbdc4d 100644 --- a/templates/purchase_orders/po_detail_ar_pdf.html +++ b/templates/purchase_orders/po_detail_ar_pdf.html @@ -1,6 +1,6 @@ {% load tenhal_tag %} {% load custom_filters %} - +{% load i18n static custom_filters num2words_tags %} @@ -109,50 +109,94 @@ /* Footer Styles */ .document-footer { + position: relative; + bottom: 0; + left: 0; + width: 100%; text-align: center; font-size: 10px; color: #888; - border-top: 1px solid #ddd; + padding-top: 15px; - margin-top: 30px; + margin: 0 20mm; + } .footer-flex { - display: flex; - justify-content: space-between; - align-items: center; - width: 100%; - } + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + } + .footer-logo { + text-align: center; + } + .footer-logo img { + height: 20px; + width: 20px; + } + .footer-logo p { + font-size: 9px; + font-weight: bold; + } + .footer-powered p { + font-size: 11px; + } + .footer-powered span { + font-weight: lighter; + } + .footer-powered a { + color: #112e40; + text-decoration: none; + font-size: 9px; + } + +
+ +
+

{{ dealer.name }}

+ +
+ العنوان : {{ dealer.address }}
+ البريد الإلكتروني : {{ dealer.user.email }}
+ الهاتف : {{ dealer.phone_number }}
+ رقم السجل التجاري : {{ dealer.crn }}  |  رقم ضريبة القيمة المضافة : {{ dealer.vrn }} +
+
+ +
+ +
+

أمر شراء

{{ po_model.po_number }}

-
-

{{ dealer.name }}

-
- العنوان: {{ dealer.address }}
- البريد الإلكتروني: {{ dealer.user.email }}
- الهاتف: {{ dealer.phone_number }}
- رقم السجل التجاري: {{ dealer.crn }}  |  رقم ضريبة القيمة المضافة: {{ dealer.vrn }} -
-
- +

التفاصيل:

-

رقم أمر الشراء: {{ po_model.po_number }}

-

تاريخ الإصدار: {{ po_model.date_fulfilled|date:"Y/m/d" }}

+

رقم أمر الشراء :  {{ po_model.po_number }}

+

تاريخ الإصدار :  {{ po_model.date_fulfilled|date:"Y/m/d" }}

يُرسل إلى:

-

المورد: {{ vendor.vendor_name }}

-

البريد الإلكتروني: {{ vendor.email }}

-

الهاتف: {{ vendor.phone }}

-

العنوان: {{ vendor.address_1 }}

+

المورد :  {{ vendor.vendor_name }}

+

البريد الإلكتروني :  {{ vendor.email }}

+

الهاتف :  {{ vendor.phone }}

+

العنوان :  {{ vendor.address_1 }}

@@ -170,13 +214,24 @@
-

المبلغ الإجمالي: {{ po_total_amount|floatformat:'2g' }}

+

المبلغ الإجمالي :  {{ po_total_amount|currency_format }}

+
+
+
+ + - - \ No newline at end of file diff --git a/templates/purchase_orders/po_detail_en_pdf.html b/templates/purchase_orders/po_detail_en_pdf.html index f777cca5..6e6014a9 100644 --- a/templates/purchase_orders/po_detail_en_pdf.html +++ b/templates/purchase_orders/po_detail_en_pdf.html @@ -103,22 +103,49 @@ text-align: right; } - /* Footer Styles */ + /* Footer Styles */ .document-footer { + position: relative; + bottom: 0; + left: 0; + width: 100%; text-align: center; font-size: 10px; color: #888; - border-top: 1px solid #ddd; + padding-top: 15px; - margin-top: 30px; + margin: 0 20mm; + + } + .footer-flex { + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + } + .footer-logo { + text-align: center; + } + .footer-logo img { + height: 20px; + width: 20px; + } + .footer-logo p { + font-size: 9px; + font-weight: bold; + } + .footer-powered p { + font-size: 11px; + } + .footer-powered span { + font-weight: lighter; + } + .footer-powered a { + color: #112e40; + text-decoration: none; + font-size: 9px; } - .footer-flex { - display: flex; - justify-content: space-between; - align-items: center; - width: 100%; - } @@ -126,12 +153,25 @@

{{ dealer.name }}

- Address: {{ dealer.address}}
- Email: {{ dealer.user.email }}
- Phone: {{dealer.phone_number }}
- CRN: {{dealer.crn}}  | VRN: {{dealer.vrn}} + Address : {{ dealer.address}}
+ Email : {{ dealer.user.email }}
+ Phone : {{dealer.phone_number }}
+ CRN : {{dealer.crn}}  | VRN : {{dealer.vrn}}
+ +
+ +
+

PURCHASE ORDER

{{ po_model.po_number }}

@@ -141,15 +181,15 @@

BILL TO:

-

Vendor: {{vendor.vendor_name}}

-

Email: {{vendor.email}}

-

Phone: {{vendor.phone}}

-

Address: {{vendor.address_1}}

+

Vendor : {{vendor.vendor_name}}

+

Email : {{vendor.email}}

+

Phone : {{vendor.phone}}

+

Address : {{vendor.address_1}}

DETAILS:

-

PO Number: {{ po_model.po_number }}

-

Issue Date: {{ po_model.date_fulfilled|date:"F j, Y" }}

+

PO Number :  {{ po_model.po_number }}

+

Issue Date :  {{ po_model.date_fulfilled|date:"F j, Y" }}

@@ -161,14 +201,25 @@
-

Total Amount: {{ po_total_amount|floatformat:'2g' }}

+

Total Amount :  {{ po_total_amount|currency_format }}

+
-
{{ invoice.amount_owned }} diff --git a/templates/sales/invoices/invoice_list.html b/templates/sales/invoices/invoice_list.html index 87f2cf56..8f674b6f 100644 --- a/templates/sales/invoices/invoice_list.html +++ b/templates/sales/invoices/invoice_list.html @@ -14,9 +14,9 @@
-
+ {% comment %}
{% include 'partials/search_box.html' %}
-
+
{% endcomment %}
diff --git a/templates/sales/invoices/invoice_preview.html b/templates/sales/invoices/invoice_preview.html index a2663f78..fb6a71f0 100644 --- a/templates/sales/invoices/invoice_preview.html +++ b/templates/sales/invoices/invoice_preview.html @@ -1,367 +1,333 @@ {% load i18n static custom_filters num2words_tags %} - - - - - {% trans "Invoice" %} - - - - - - - -
-
- + /* Footer Styles */ + .document-footer { + text-align: center; + font-size: 10px; + color: #888; + border-top: 1px solid #ddd; + padding-top: 15px; + margin-top: 30px; + } + .footer-logo img { + height: 20px; + width: 20px; + } + .footer-logo p { + font-size: 9px; + font-weight: bold; + margin: 5px 0 0; + } + .footer-powered p { + font-size: 11px; + margin: 0; + } + .footer-powered span { + font-weight: lighter; + } + .footer-powered a { + color: #112e40; + text-decoration: none; + font-size: 9px; + } + + + +
+
+
+

{{ dealer_info.name }}

+
+ العنوان : {{ dealer_info.address }}
+ البريد الإلكتروني : {{ dealer_info.user.email }}
+ الهاتف : {{ dealer_info.phone_number }}
+ رقم السجل التجاري : {{dealer_info.crn }}  | الرقم الضريبي : {{ dealer_info.vrn }} +
-
- +
+

عرض سعر

+

{{ estimate.estimate_number }}

-
-
-
-
-
- Invoice / فاتورة -
-
-
-
-
-
- QR Code -
-
- -
-
- - - - - - - - - - - - - - - - - - - - -
- Dealership Name - {{ request.dealer.name }} - اسم الوكالة -
- Dealership Address - {{ request.dealer.address }} - عنوان الوكالة -
- Phone - {{ request.dealer.phone_number }} - جوال -
- VAT Number - {{ request.dealer.vrn }} - الرقم الضريبي -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
- Invoice Number - {{ invoice.invoice_number }} - رقم الفاتورة -
- Date - {{ invoice.date_approved| date:"Y/m/d" }} - التاريخ -
- Customer Name - {{ invoice.customer.customer_name }} - اسم العميل -
- Email - {{ invoice.customer.email |default:"N/A" }} - البريد الإلكتروني -
- Terms - {{ invoice.get_terms_display }} - شروط الدفع -
-
-
- Car Details - تفاصيل السيارة -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Make / الصانع - - Model / الموديل - - Series / السلسلة - - Trim / الفئة - - Year / السنة - - VIN / رقم الهيكل - - Quantity / الكمية - - Unit Price / سعر الوحدة - - Discount / الخصم - - VAT / الضريبة - - Total / الإجمالي -
{{ data.car.id_car_make.name }}{{ data.car.id_car_model.name }}{{ data.car.id_car_serie.name }}{{ data.car.id_car_trim.name }}{{ data.car.year }}{{ data.car.vin }}1{{ data.car.marked_price |floatformat:2 }}{{ data.discount_amount |floatformat:2 }}{{ data.vat_amount|floatformat:2 }}{{ data.final_price|floatformat:2 }}
-
-
- Additional Services - الخدمات الإضافية -
- {% if data.additional_services %} -
- - - - - - - - - - - {% for service in data.additional_services.services %} - - - - - - - {% endfor %} - -
Type / النوعPrice / السعرService VAT / ضريبة الخدمة - Total / الإجمالي -
{{ service.0.name }}{{ service.0.price|floatformat }}{{ service.1|floatformat }}{{ service.0.price_|floatformat }}
-
- {% endif %} -
-
- - - - - - - - - - - - - - -
- Total VAT - - {{ data.total_vat|floatformat }} - - إجمالي ضريبة القيمة المضافة -
- Grand Total - - {{ data.grand_total|floatformat }}  - - الإجمالي الكلي -
- كتابةً: {{ data.grand_total|num_to_words }}  -
-
-
+ +
+
+

{{ estimate.customer.customer_name }}: إلى

+

العميل : {{ customer_obj.full_name }}

+

البريد الإلكتروني : {{ customer_obj.email |default:"N/A"}}

+

الهاتف : {{ customer_obj.phone_number|default:"N/A" }}

+

العنوان : {{ customer_obj.address|default:"N/A" }}

- + + \ No newline at end of file diff --git a/templates/sales/invoices/invoice_preview_ar.html b/templates/sales/invoices/invoice_preview_ar.html new file mode 100644 index 00000000..de8d5299 --- /dev/null +++ b/templates/sales/invoices/invoice_preview_ar.html @@ -0,0 +1,359 @@ +{% load static custom_filters num2words_tags %} + + + + + + فاتورة + + + + + +
+
+

{{ dealer_info.name }}

+
+ العنوان : {{ dealer_info.address }}
+ البريد الإلكتروني : {{ dealer_info.user.email }}
+ الهاتف : {{ dealer_info.phone_number }}
+ رقم السجل التجاري : {{dealer_info.crn }}
+ الرقم الضريبي : {{ dealer_info.vrn }} +
+
+ +
+ +
+ +
+

فاتورة

+

{{ invoice.invoice_number }}

+
+
+ +
+
+

{{ customer_obj.full_name }}: إلى

+

العميل : {{ customer_obj.full_name }}

+

البريد الإلكتروني : {{ customer_obj.email |default:"N/A"}}

+

الهاتف : {{ customer_obj.phone_number|default:"N/A" }}

+

العنوان : {{ customer_obj.address|default:"N/A" }}

+
+
+

التفاصيل:

+

رقم عرض السعر :  {{ invoice.invoice_number }}

+

تاريخ الإصدار :  {{ invoice.date_approved|date:"Y/m/d" }}

+

طريقة الدفع :  {{ invoice.get_terms_display }}

+
+
+ +
+
+ تفاصيل السيارة +
+ + + + + + + + + + + + + + + + + + + + + +
+ الصانع + + الموديل + + السلسلة + + الفئة + + السنة + + رقم الهيكل +
{{ data.car.id_car_make.name }}{{ data.car.id_car_model.name }}{{ data.car.id_car_serie.name }}{{ data.car.id_car_trim.name }}{{ data.car.year }}{{ data.car.vin }}
+
+ +
+
+ التفاصيل المالية +
+ + + + + + + + + + + + + + + + + + + +
+ الكمية + + سعر الوحدة + + الخصم + + الضريبة + + الإجمالي +
1{{ data.car.marked_price|currency_format }}{{ data.discount_amount|currency_format}}{{ data.vat_amount|currency_format}}{{ data.final_price|currency_format }}
+
+ + {% if data.additional_services %} +
+
+ الخدمات الإضافية +
+ + + + + + + + + + + {% for service in data.additional_services.services %} + + + + + + + {% endfor %} + + + + + + + + + + +
النوعالقيمةضريبة الخدمةالإجمالي
{{ service.0.name }}{{ service.0.price|currency_format}}{{ service.1|currency_format }}{{ service.0.price_|currency_format}}
{{ data.total_services_amount|currency_format}}{{ data.total_services_vat|currency_format}}{{ data.total_services_amount_|currency_format }}
+
+ {% endif %} + +
+
+

إجمالي ضريبة القيمة المضافة :  {{ data.total_vat|currency_format }}

+

الإجمالي الكلي :  {{ data.grand_total|currency_format}}

+

كتابةً :  {{ data.grand_total|num_to_words }}

+
+
+
+ + + + \ No newline at end of file diff --git a/templates/sales/invoices/invoice_preview_en.html b/templates/sales/invoices/invoice_preview_en.html new file mode 100644 index 00000000..80f4cc5d --- /dev/null +++ b/templates/sales/invoices/invoice_preview_en.html @@ -0,0 +1,357 @@ +{% load static custom_filters num2words_tags %} + + + + + Invoice + + + + + +
+
+

{{ dealer_info.name }}

+
+ Address : {{ dealer_info.address }}
+ Email : {{ dealer_info.user.email }}
+ Phone : {{ dealer_info.phone_number }}
+ Commercial Registration No. : {{dealer_info.crn }}
+ VAT No. : {{ dealer_info.vrn }} +
+
+ +
+ +
+ +
+

Invoice

+

{{ invoice.invoice_number }}

+
+
+ +
+
+

To: {{ customer_obj.full_name }}

+

Customer : {{ customer_obj.full_name }}

+

Email : {{ customer_obj.email |default:"N/A"}}

+

Phone : {{ customer_obj.phone_number|default:"N/A" }}

+

Address : {{ customer_obj.address|default:"N/A" }}

+
+
+

Details:

+

Quotation Number :  {{ invoice.invoice_number }}

+

Issue Date :  {{ invoice.date_approved|date:"Y/m/d" }}

+

Payment Method :  {{ invoiceget_terms_display }}

+
+
+ +
+
+ Car Details +
+ + + + + + + + + + + + + + + + + + + + + +
+ Make + + Model + + Series + + Trim + + Year + + VIN +
{{ data.car.id_car_make.name }}{{ data.car.id_car_model.name }}{{ data.car.id_car_serie.name }}{{ data.car.id_car_trim.name }}{{ data.car.year }}{{ data.car.vin }}
+
+ +
+
+ Financial Details +
+ + + + + + + + + + + + + + + + + + + +
+ Quantity + + Unit Price + + Discount + + VAT + + Total +
1{{ data.car.marked_price|currency_format}}{{ data.discount_amount|currency_format }}{{ data.vat_amount|currency_format}}{{ data.final_price|currency_format }}
+
+ + {% if data.additional_services %} +
+
+ Additional Services +
+ + + + + + + + + + + {% for service in data.additional_services.services %} + + + + + + + {% endfor %} + + + + + + + + + + +
TypeValueService VATTotal
{{ service.0.name }}{{ service.0.price|currency_format }}{{ service.1|currency_format}}{{ service.0.price_|currency_format}}
{{ data.total_services_amount|currency_format}}{{ data.total_services_vat|currency_format}}{{ data.total_services_amount_|currency_format }}
+
+ {% endif %} + +
+
+

Total VAT :  {{ data.total_vat|currency_format}}

+

Grand Total :  {{ data.grand_total|currency_format}}

+

In words :  {{ data.grand_total|num_to_words }}

+
+
+
+ + + + \ No newline at end of file diff --git a/templates/sales/sales_list.html b/templates/sales/sales_list.html index 6fc7681a..7524945a 100644 --- a/templates/sales/sales_list.html +++ b/templates/sales/sales_list.html @@ -15,9 +15,9 @@
-
+ {% comment %}
{% include 'partials/search_box.html' %}
-
+
{% endcomment %}
diff --git a/templates/vendors/vendors_list.html b/templates/vendors/vendors_list.html index c0030da3..262d52d1 100644 --- a/templates/vendors/vendors_list.html +++ b/templates/vendors/vendors_list.html @@ -101,7 +101,7 @@ {{ vendor.arabic_name }}
-

{{ vendor.name }}

+

{{ vendor.name|title }}