diff --git a/pos_next/pos_next/print_format/pos_next_eod_report/pos_next_eod_report.json b/pos_next/pos_next/print_format/pos_next_eod_report/pos_next_eod_report.json
index adb0d0dae..563ebad7a 100644
--- a/pos_next/pos_next/print_format/pos_next_eod_report/pos_next_eod_report.json
+++ b/pos_next/pos_next/print_format/pos_next_eod_report/pos_next_eod_report.json
@@ -8,7 +8,7 @@
"docstatus": 0,
"doctype": "Print Format",
"font_size": 11,
- "html": "\n\n{%- set items_sold = get_items_sold(doc) -%}\n{%- set taxes = doc.taxes or [] -%}\n{%- set payments = doc.payment_reconciliation or [] -%}\n{%- set tax_total = taxes | sum(attribute='amount') -%}\n{%- set total_expected = payments | sum(attribute='expected_amount') -%}\n{%- set total_counted = payments | sum(attribute='closing_amount') -%}\n{%- set summary_diff = total_counted - total_expected -%}\n{%- set profile_address = frappe.db.get_value('POS Profile', doc.pos_profile, 'address') -%}\n\n
\n\t
END-OF-DAY REPORT
\n\t
{{ doc.company or 'POS Next' }}
\n\t{%- if profile_address -%}\n\t
{{ profile_address }}
\n\t{%- endif -%}\n\n\t
\n\t\t
Session
\n\t\t
Profile{{ doc.pos_profile }}
\n\t\t
Cashier{{ doc.user }}
\n\t\t
Opened{{ doc.period_start_date }}
\n\t\t
Closed{{ doc.period_end_date }}
\n\t\t
Closing Shift{{ doc.name }}
\n\t
\n\n\t
\n\t\t
Sales Totals
\n\t\t
Invoice Count{{ (doc.pos_transactions or []) | length }}
\n\t\t
Total Qty{{ '%.2f' | format(doc.total_quantity or 0) }}
\n\t\t
Net{{ '%.2f' | format(doc.net_total or 0) }}
\n\t\t
Tax{{ '%.2f' | format(tax_total or 0) }}
\n\t\t
Grand Total{{ '%.2f' | format(doc.grand_total or 0) }}
\n\t
\n\n\t
\n\t\t
Items Sold
\n\t\t{%- if items_sold -%}\n\t\t\t{%- for item in items_sold -%}\n\t\t\t
\n\t\t\t\t
{{ item.item_name or item.item_code }}
\n\t\t\t\t
Qty: {{ '%.2f' | format(item.qty or 0) }}{{ '%.2f' | format(item.amount or 0) }}
\n\t\t\t
\n\t\t\t{%- endfor -%}\n\t\t{%- else -%}\n\t\t\t
No item rows found
\n\t\t{%- endif -%}\n\t
\n\n\t
\n\t\t
Taxes
\n\t\t{%- if taxes -%}\n\t\t\t{%- for tax in taxes -%}\n\t\t\t
{{ tax.account_head }} ({{ '%.2f' | format(tax.rate or 0) }}%){{ '%.2f' | format(tax.amount or 0) }}
\n\t\t\t{%- endfor -%}\n\t\t{%- else -%}\n\t\t\t
No tax rows
\n\t\t{%- endif -%}\n\t
\n\n\t
\n\t\t
Payments
\n\t\t{%- if payments -%}\n\t\t\t{%- for payment in payments -%}\n\t\t\t
\n\t\t\t\t
{{ payment.mode_of_payment }}
\n\t\t\t\t
Opening{{ '%.2f' | format(payment.opening_amount or 0) }}
\n\t\t\t\t
Expected{{ '%.2f' | format(payment.expected_amount or 0) }}
\n\t\t\t\t
Counted{{ '%.2f' | format(payment.closing_amount or 0) }}
\n\t\t\t\t
Difference{{ '%.2f' | format(payment.difference or 0) }} {% if (payment.difference or 0) < 0 %}SHORT{% elif (payment.difference or 0) > 0 %}OVER{% endif %}
\n\t\t\t
\n\t\t\t{%- endfor -%}\n\t\t{%- else -%}\n\t\t\t
No payment rows
\n\t\t{%- endif -%}\n\t
\n\n\t
\n\t\t
Final Summary
\n\t\t
\n\t\t\t
Total Expected{{ '%.2f' | format(total_expected or 0) }}
\n\t\t\t
Total Counted{{ '%.2f' | format(total_counted or 0) }}
\n\t\t\t
Short / Over{{ '%.2f' | format(summary_diff or 0) }}
\n\t\t
\n\t
\n\n\t\n
",
+ "html": "\n\n{%- set company_doc = frappe.get_cached_doc(\"Company\", doc.company) -%}\n{%- set profile_doc = frappe.get_cached_doc(\"POS Profile\", doc.pos_profile) -%}\n{%- set user_doc = frappe.get_cached_doc(\"User\", doc.user) -%}\n\n{%- set items = get_items_sold(doc) -%}\n\n{% set total_expected = namespace(v=0.0) %}\n{% set total_counted = namespace(v=0.0) %}\n\n{% for p in doc.payment_reconciliation %}\n {% set total_expected.v = total_expected.v + (p.expected_amount or 0) %}\n {% set total_counted.v = total_counted.v + (p.closing_amount or 0) %}\n{% endfor %}\n\n{% set short_over = total_counted.v - total_expected.v %}\n\n\n\n \n\n
\n End of Day Report\n
\n\n
\n
\n {{ company_doc.company_name }}\n
\n\n {% if profile_doc.company_address %}\n
\n {{ profile_doc.company_address }}\n
\n {% endif %}\n
\n\n \n\n
\n Session Information\n
\n\n
\n POS Profile\n {{ doc.pos_profile }}\n
\n\n
\n Cashier\n \n {{ user_doc.full_name or doc.user }}\n \n
\n\n
\n Opened\n \n {{ frappe.utils.format_datetime(doc.period_start_date) }}\n \n
\n\n
\n Closed\n \n {{ frappe.utils.format_datetime(doc.period_end_date) }}\n \n
\n\n
\n Closing ID\n {{ doc.name }}\n
\n\n \n\n
\n Sales Totals\n
\n\n
\n Invoices\n \n {{ doc.pos_transactions|length }}\n \n
\n\n
\n Items Sold\n \n {{ \"{:.2f}\".format(doc.total_quantity or 0) }}\n \n
\n\n
\n Net Total\n \n {{ \"{:,.2f}\".format(doc.net_total or 0) }}\n \n
\n\n {% set tax_total = doc.taxes|sum(attribute='amount') if doc.taxes else 0 %}\n\n
\n Tax Total\n \n {{ \"{:,.2f}\".format(tax_total) }}\n \n
\n\n
\n Grand Total\n \n {{ \"{:,.2f}\".format(doc.grand_total or 0) }}\n \n
\n\n \n\n {% if items %}\n\n
\n Items Sold\n
\n\n {% for it in items %}\n\n
\n\n
\n {{ it.item_name or it.item_code }}\n
\n\n
\n\n
\n\n {% endfor %}\n\n {% endif %}\n\n \n\n {% if doc.taxes %}\n\n
\n Taxes\n
\n\n {% for t in doc.taxes %}\n\n
\n\n \n {{ t.account_head }}\n {% if t.rate %}\n ({{ \"{:g}\".format(t.rate | float) }}%)\n {% endif %}\n \n\n \n {{ \"{:,.2f}\".format(t.amount or 0) }}\n \n\n
\n\n {% endfor %}\n\n {% endif %}\n\n \n\n
\n Payments\n
\n\n {% for p in doc.payment_reconciliation %}\n\n {% set diff = (p.closing_amount or 0) - (p.expected_amount or 0) %}\n\n
\n\n
\n {{ p.mode_of_payment }}\n
\n\n
\n Opening\n \n {{ \"{:,.2f}\".format(p.opening_amount or 0) }}\n \n
\n\n
\n Expected\n \n {{ \"{:,.2f}\".format(p.expected_amount or 0) }}\n \n
\n\n
\n Counted\n \n {{ \"{:,.2f}\".format(p.closing_amount or 0) }}\n \n
\n\n
\n\n \n Difference\n \n\n \n\n {{ \"{:,.2f}\".format(diff) }}\n\n {% if diff < 0 %}\n \n SHORT\n \n {% elif diff > 0 %}\n \n OVER\n \n {% endif %}\n\n \n\n
\n\n
\n\n {% endfor %}\n\n \n\n
\n Final Summary\n
\n\n
\n Total Expected\n \n {{ \"{:,.2f}\".format(total_expected.v) }}\n \n
\n\n
\n Total Counted\n \n {{ \"{:,.2f}\".format(total_counted.v) }}\n \n
\n\n
\n\n \n Short / Over\n \n\n \n\n {{ \"{:,.2f}\".format(short_over) }}\n\n {% if short_over < 0 %}\n \n SHORT\n \n {% elif short_over > 0 %}\n \n OVER\n \n {% endif %}\n\n \n\n
\n\n \n\n \n\n
",
"line_breaks": 0,
"margin_bottom": 0.0,
"margin_left": 0.0,
diff --git a/pos_next/pos_next/utils/pos_closing_print.py b/pos_next/pos_next/utils/pos_closing_print.py
index 6759d05ad..f5be90b68 100644
--- a/pos_next/pos_next/utils/pos_closing_print.py
+++ b/pos_next/pos_next/utils/pos_closing_print.py
@@ -87,6 +87,9 @@ def _fetch_items_for_targets(parent_targets: set[tuple[str, str]]) -> list[dict]
def get_items_sold(doc) -> list[dict]:
closing_doc = _as_closing_doc(doc)
parent_targets = _collect_parent_targets(closing_doc.get("pos_transactions"))
+ if not parent_targets:
+ return []
+
items = _fetch_items_for_targets(parent_targets)
return [
diff --git a/pos_next/pos_next/utils/tests/test_pos_closing_print.py b/pos_next/pos_next/utils/tests/test_pos_closing_print.py
index d5f1d0a96..2cc28dccb 100644
--- a/pos_next/pos_next/utils/tests/test_pos_closing_print.py
+++ b/pos_next/pos_next/utils/tests/test_pos_closing_print.py
@@ -75,3 +75,11 @@ def test_get_items_sold_returns_empty_when_no_transactions(self, mock_fetch):
self.assertEqual(result, [])
mock_fetch.assert_not_called()
+
+ @patch("pos_next.pos_next.utils.pos_closing_print._fetch_items_for_targets")
+ def test_get_items_sold_returns_empty_when_no_parent_targets(self, mock_fetch):
+ doc = {"pos_transactions": [{"customer": "CUST-001"}]}
+ result = get_items_sold(doc)
+
+ self.assertEqual(result, [])
+ mock_fetch.assert_not_called()
diff --git a/pos_next/translations/ar.csv b/pos_next/translations/ar.csv
index c99f44df1..b2d72273f 100644
--- a/pos_next/translations/ar.csv
+++ b/pos_next/translations/ar.csv
@@ -47,6 +47,7 @@
"Item removed from cart","تم الحذف من السلة",""
"Open Shift","فتح وردية",""
"Close Shift","إغلاق الوردية",""
+"Print EOD Report","طباعة تقرير نهاية اليوم",""
"Shift Opening","بداية الوردية",""
"Shift Closing","نهاية الوردية",""
"Opening Amount","عهدة الفتح",""
diff --git a/pos_next/translations/id.csv b/pos_next/translations/id.csv
index c6012d9bb..39ae1c4e5 100644
--- a/pos_next/translations/id.csv
+++ b/pos_next/translations/id.csv
@@ -46,6 +46,7 @@
"Item removed from cart","Dihapus dari keranjang",""
"Open Shift","Buka Shift",""
"Close Shift","Tutup Shift",""
+"Print EOD Report","Cetak Laporan Akhir Hari",""
"Shift Opening","Pembukaan Shift",""
"Shift Closing","Penutupan Shift",""
"Opening Amount","Jumlah Pembukaan Shift",""
diff --git a/pos_next/translations/pt-br.csv b/pos_next/translations/pt-br.csv
index de859ba11..683402896 100644
--- a/pos_next/translations/pt-br.csv
+++ b/pos_next/translations/pt-br.csv
@@ -44,6 +44,7 @@
"Item removed from cart","Item removido do carrinho",""
"Open Shift","Abrir Turno",""
"Close Shift","Fechar Turno",""
+"Print EOD Report","Imprimir Relatório de Fim de Dia",""
"Shift Opening","Abertura do Turno",""
"Shift Closing","Fechamento do Turno",""
"Opening Amount","Valor de Abertura",""