feat: print formats for Purchase Invoice, Purchase Order, Sales Order

Default Standard Purchase Invoice / Purchase Order / Sales Order, laid out
like the Sales Invoice and Quotation formats: dates (and the supplier's or
customer's reference numbers) in the heading, party block with Tax ID and
contact, essentials-only item table, right-aligned totals with the amount in
words beside Grand Total, signature lines.

taxes include: purchase "Deduct" charges (withholding tax) print negative and
"Valuation" charges are skipped so the lines add up to Grand Total; charge
descriptions are translated so the WHT category names print in Thai.
This commit is contained in:
kurogeek committed 2026-09-23 08:31:14 +00:00
1 parent e82d82dbcc
commit e7d0a545b1
4 files changed
+127 -3

No files matched your search

@@ -101,6 +101,108 @@
"show_section_headings": 0, "show_section_headings": 0,
"standard": "No" "standard": "No"
}, },
{
"absolute_value": 0,
"align_labels_right": 0,
"css": "/* Thai text needs a font with Thai glyphs; wkhtmltopdf only sees the server's fonts and a\n headless server rarely has one, so ship Sarabun (OFL, see public/fonts/OFL.txt) with the app.\n Asset URLs are made absolute for the PDF renderer by frappe.utils.scrub_urls. */\n@font-face {\n\tfont-family: \"Sarabun\";\n\tfont-weight: 400;\n\tsrc: url(\"/assets/default_thai_company/fonts/Sarabun-Regular.ttf\");\n}\n@font-face {\n\tfont-family: \"Sarabun\";\n\tfont-weight: 700;\n\tsrc: url(\"/assets/default_thai_company/fonts/Sarabun-Bold.ttf\");\n}\n.print-format {\n\tfont-family: Sarabun, Inter, \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n}\n\n/* Print Style \"Redesign\" gives .letter-head a 30px margin; tighten it. */\n.print-format .letter-head {\n\tmargin-bottom: 5px;\n}\n\n/* One vertical rhythm for every label/value line. \"Redesign\" stacks field rows with\n margin-top: 15px (doubled where Total/Taxes nest a layout .row) while the custom\n Supplier table and the heading dates had none; use line-height 1.8 everywhere\n and no row margins instead. */\n.print-format .row:not(.section-break) {\n\tmargin-top: 0 !important;\n\tline-height: 1.8;\n}\n.print-format .heading-dates,\n.print-format .supplier-details td {\n\tline-height: 1.8;\n}\n\n/* standard.css gives .table 20px top/bottom margins; keep the item table close to its neighbours. */\n.print-format [data-fieldname=\"items\"] > .table {\n\tmargin-top: 5px;\n\tmargin-bottom: 5px;\n}",
"custom_format": 1,
"default_print_language": null,
"disabled": 0,
"doc_type": "Purchase Invoice",
"docstatus": 0,
"doctype": "Print Format",
"font": null,
"font_size": 14,
"format_data": null,
"html": "{%- from \"templates/print_formats/standard_macros.html\" import add_header, render_field with context -%}\n{#- ERPNext hides taxes marked \"Is this Tax included in Basic Rate?\" unless Accounts Settings\n enables \"Show Inclusive Tax in Print\"; always show them here so the tax line and\n \"Total (Without Tax)\" appear on invoices with inclusive tax. -#}\n{%- if doc.get(\"taxes\", {\"included_in_print_rate\": 1}) -%}\n\t{%- set ignored = doc.flags.update({\"show_inclusive_tax_in_print\": 1}) -%}\n{%- endif -%}\n{#- App copies of ERPNext's totals includes, printing the posted figures: \"total\" is the net before\n the Additional Discount; \"taxes\" prints the discount's net share, Net Total and each charge after\n discount (withholding tax as a deduction), so the lines add up to Grand Total. -#}\n{%- set ignored = doc.print_templates.update({\"total\": \"default_thai_company/templates/print_formats/includes/total.html\"}) -%}\n{%- if doc.print_templates.get(\"taxes\") -%}\n\t{%- set ignored = doc.print_templates.update({\"taxes\": \"default_thai_company/templates/print_formats/includes/taxes.html\"}) -%}\n{%- endif -%}\n{#- Standard layout prints every non-empty field; drop the ones this invoice does not need.\n posting_date / due_date and the supplier's invoice reference are printed in the heading,\n supplier fields in the Supplier section, in_words in the left column of the Totals section,\n the company's billing address in the letter head. -#}\n{%- set hidden_fields = [\n\t\"total_qty\", \"tax_withholding_net_total\", \"update_outstanding_for_self\",\n\t\"update_billed_amount_in_purchase_order\", \"update_billed_amount_in_purchase_receipt\",\n\t\"billing_address\", \"billing_address_display\", \"supplier_group\", \"disable_rounded_total\",\n\t\"posting_date\", \"due_date\", \"bill_no\", \"bill_date\",\n\t\"supplier_name\", \"address_display\", \"contact_display\", \"contact_mobile\", \"contact_email\", \"tax_id\",\n\t\"in_words\",\n] -%}\n{#- rounded_total keeps a value even when rounding is disabled, so the standard layout would print it. -#}\n{%- if doc.is_rounded_total_disabled() -%}{%- set ignored = hidden_fields.append(\"rounded_total\") -%}{%- endif -%}\n{#- add_header renders this string via frappe.render_template with {\"doc\": doc}, so it is kept\n unrendered with {% raw %}. Same markup as the stock heading plus a right-aligned date block. -#}\n{%- set heading_template -%}\n{% raw %}\n<div class=\"print-heading\">\n\t<style>\n\t\t.print-heading .heading-dates { float: right; text-align: right; margin-top: 4px; white-space: nowrap; }\n\t\t.print-heading .heading-dates label { display: inline; font-weight: 700; margin: 0 4px 0 0; }\n\t\t.print-heading .heading-clear { clear: both; }\n\t</style>\n\t<div class=\"heading-dates\">\n\t\t<div><label>{{ _(doc.meta.get_label(\"posting_date\")) }}:</label> {{ doc.get_formatted(\"posting_date\") }}</div>\n\t\t{%- if doc.due_date %}\n\t\t<div><label>{{ _(doc.meta.get_label(\"due_date\")) }}:</label> {{ doc.get_formatted(\"due_date\") }}</div>\n\t\t{%- endif %}\n\t\t{%- if doc.bill_no %}\n\t\t<div><label>{{ _(doc.meta.get_label(\"bill_no\")) }}:</label> {{ doc.bill_no }}</div>\n\t\t{%- endif %}\n\t\t{%- if doc.bill_date %}\n\t\t<div><label>{{ _(doc.meta.get_label(\"bill_date\")) }}:</label> {{ doc.get_formatted(\"bill_date\") }}</div>\n\t\t{%- endif %}\n\t</div>\n\t<h2>\n\t\t<div>{{ _(doc.select_print_heading) or (_(doc.print_heading) if doc.print_heading != None else _(doc.doctype)) }}</div>\n\t\t<small class=\"sub-heading\">{{ _(doc.sub_heading) if doc.sub_heading != None else _(doc.name) }}</small>\n\t</h2>\n\t<div class=\"heading-clear\"></div>\n</div>\n{% endraw %}\n{%- endset -%}\n{#- Standard layout prints every non-empty child column; keep the item table to the essentials.\n Purchase Invoice Item marks item_code print_hide (it prints item_name instead); show the code\n as the other formats do. -#}\n{%- set item_columns = [\n\t{\"fieldname\": \"item_code\", \"print_hide\": 0},\n\t{\"fieldname\": \"description\"},\n\t{\"fieldname\": \"qty\"},\n\t{\"fieldname\": \"rate\"},\n\t{\"fieldname\": \"amount\"},\n] -%}\n\n{%- macro render_fields(fields, no_of_cols) -%}\n\t{%- for df in fields -%}\n\t\t{%- if df.fieldname not in hidden_fields -%}\n\t\t\t{%- if df.fieldname == \"items\" -%}\n\t\t\t\t{%- set df = df.as_dict() -%}\n\t\t\t\t{%- set ignored = df.update({\"visible_columns\": item_columns}) -%}\n\t\t\t{%- endif -%}\n\t\t\t{{ render_field(df, doc, no_of_cols) }}\n\t\t{%- endif -%}\n\t{%- endfor -%}\n{%- endmacro -%}\n\n{% for page in layout %}\n<div class=\"page-break\">\n\t<div {% if print_settings.repeat_header_footer %} id=\"header-html\" class=\"hidden-pdf\" {% endif %}>\n\t\t{{ add_header(loop.index, layout|len, doc, letter_head, no_letterhead, footer, print_settings, heading_template) }}\n\t</div>\n\n\t{% if loop.first %}\n\t<div class=\"row section-break\" data-label=\"{{ _(\"Supplier\") }}\">\n\t\t<div clLine truncated
"line_breaks": 0,
"margin_bottom": 15.0,
"margin_left": 15.0,
"margin_right": 15.0,
"margin_top": 15.0,
"modified": "2026-09-23 12:00:00.000000",
"module": "Default Thai Company",
"name": "Default Standard Purchase Invoice",
"page_number": "Hide",
"pdf_generator": "wkhtmltopdf",
"print_format_builder": 0,
"print_format_builder_beta": 0,
"print_format_for": "DocType",
"print_format_type": "Jinja",
"raw_commands": null,
"raw_printing": 0,
"report": null,
"show_section_headings": 0,
"standard": "No"
},
{
"absolute_value": 0,
"align_labels_right": 0,
"css": "/* Thai text needs a font with Thai glyphs; wkhtmltopdf only sees the server's fonts and a\n headless server rarely has one, so ship Sarabun (OFL, see public/fonts/OFL.txt) with the app.\n Asset URLs are made absolute for the PDF renderer by frappe.utils.scrub_urls. */\n@font-face {\n\tfont-family: \"Sarabun\";\n\tfont-weight: 400;\n\tsrc: url(\"/assets/default_thai_company/fonts/Sarabun-Regular.ttf\");\n}\n@font-face {\n\tfont-family: \"Sarabun\";\n\tfont-weight: 700;\n\tsrc: url(\"/assets/default_thai_company/fonts/Sarabun-Bold.ttf\");\n}\n.print-format {\n\tfont-family: Sarabun, Inter, \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n}\n\n/* Print Style \"Redesign\" gives .letter-head a 30px margin; tighten it. */\n.print-format .letter-head {\n\tmargin-bottom: 5px;\n}\n\n/* One vertical rhythm for every label/value line. \"Redesign\" stacks field rows with\n margin-top: 15px (doubled where Total/Taxes nest a layout .row) while the custom\n Supplier table and the heading dates had none; use line-height 1.8 everywhere\n and no row margins instead. */\n.print-format .row:not(.section-break) {\n\tmargin-top: 0 !important;\n\tline-height: 1.8;\n}\n.print-format .heading-dates,\n.print-format .supplier-details td {\n\tline-height: 1.8;\n}\n\n/* standard.css gives .table 20px top/bottom margins; keep the item table close to its neighbours. */\n.print-format [data-fieldname=\"items\"] > .table {\n\tmargin-top: 5px;\n\tmargin-bottom: 5px;\n}",
"custom_format": 1,
"default_print_language": null,
"disabled": 0,
"doc_type": "Purchase Order",
"docstatus": 0,
"doctype": "Print Format",
"font": null,
"font_size": 14,
"format_data": null,
"html": "{%- from \"templates/print_formats/standard_macros.html\" import add_header, render_field with context -%}\n{#- ERPNext hides taxes marked \"Is this Tax included in Basic Rate?\" unless Accounts Settings\n enables \"Show Inclusive Tax in Print\"; always show them here so the tax line and\n \"Total (Without Tax)\" appear on orders with inclusive tax. -#}\n{%- if doc.get(\"taxes\", {\"included_in_print_rate\": 1}) -%}\n\t{%- set ignored = doc.flags.update({\"show_inclusive_tax_in_print\": 1}) -%}\n{%- endif -%}\n{#- App copies of ERPNext's totals includes, printing the posted figures: \"total\" is the net before\n the Additional Discount; \"taxes\" prints the discount's net share, Net Total and each charge after\n discount (withholding tax as a deduction), so the lines add up to Grand Total. -#}\n{%- set ignored = doc.print_templates.update({\"total\": \"default_thai_company/templates/print_formats/includes/total.html\"}) -%}\n{%- if doc.print_templates.get(\"taxes\") -%}\n\t{%- set ignored = doc.print_templates.update({\"taxes\": \"default_thai_company/templates/print_formats/includes/taxes.html\"}) -%}\n{%- endif -%}\n{#- Purchase Order has no Tax ID field; read the supplier's. -#}\n{%- set tax_id = frappe.db.get_value(\"Supplier\", doc.supplier, \"tax_id\") -%}\n{#- Standard layout prints every non-empty field; drop the ones this order does not need.\n transaction_date / schedule_date are printed in the heading, supplier and shipping fields in the\n Supplier section, in_words in the left column of the Totals section, the company's billing\n address in the letter head. Withholding shows as a deduction in the taxes, not as its flag. -#}\n{%- set hidden_fields = [\n\t\"total_qty\", \"tax_withholding_net_total\", \"apply_tds\", \"tax_withholding_category\", \"has_unit_price_items\",\n\t\"billing_address\", \"billing_address_display\", \"disable_rounded_total\",\n\t\"transaction_date\", \"schedule_date\",\n\t\"supplier_name\", \"address_display\", \"contact_display\", \"contact_mobile\", \"contact_email\", \"shipping_address_display\",\n\t\"in_words\",\n] -%}\n{#- rounded_total keeps a value even when rounding is disabled, so the standard layout would print it. -#}\n{%- if doc.is_rounded_total_disabled() -%}{%- set ignored = hidden_fields.append(\"rounded_total\") -%}{%- endif -%}\n{#- add_header renders this string via frappe.render_template with {\"doc\": doc}, so it is kept\n unrendered with {% raw %}. Same markup as the stock heading plus a right-aligned date block. -#}\n{%- set heading_template -%}\n{% raw %}\n<div class=\"print-heading\">\n\t<style>\n\t\t.print-heading .heading-dates { float: right; text-align: right; margin-top: 4px; white-space: nowrap; }\n\t\t.print-heading .heading-dates label { display: inline; font-weight: 700; margin: 0 4px 0 0; }\n\t\t.print-heading .heading-clear { clear: both; }\n\t</style>\n\t<div class=\"heading-dates\">\n\t\t<div><label>{{ _(doc.meta.get_label(\"transaction_date\")) }}:</label> {{ doc.get_formatted(\"transaction_date\") }}</div>\n\t\t{%- if doc.schedule_date %}\n\t\t<div><label>{{ _(doc.meta.get_label(\"schedule_date\")) }}:</label> {{ doc.get_formatted(\"schedule_date\") }}</div>\n\t\t{%- endif %}\n\t</div>\n\t<h2>\n\t\t<div>{{ _(doc.select_print_heading) or (_(doc.print_heading) if doc.print_heading != None else _(doc.doctype)) }}</div>\n\t\t<small class=\"sub-heading\">{{ _(doc.sub_heading) if doc.sub_heading != None else _(doc.name) }}</small>\n\t</h2>\n\t<div class=\"heading-clear\"></div>\n</div>\n{% endraw %}\n{%- endset -%}\n{#- Standard layout prints every non-empty child column; keep the item table to the essentials -#}\n{%- set item_columns = [\n\t{\"fieldname\": \"item_code\"},\n\t{\"fieldname\": \"description\"},\n\t{\"fieldname\": \"qty\"},\n\t{\"fieldname\": \"rate\"},\n\t{\"fieldname\": \"amount\"},\n] -%}\n\n{%- macro render_fields(fields, no_of_cols) -%}\n\t{%- for df in fields -%}\n\t\t{%- if df.fieldname not in hidden_fields -%}\n\t\t\t{%- if df.fieldname == \"items\" -%}\n\t\t\t\t{%- set df = df.as_dict() -%}\n\t\t\t\t{%- set ignored = df.update({\"visible_columns\": item_columns}) -%}\n\t\t\t{%- endif -%}\n\t\t\t{{ render_field(df, doc, no_of_cols) }}\n\t\t{%- endif -%}\n\t{%- endfor -%}\n{%- endmacro -%}\n\n{% for page in layout %}\n<div class=\"page-break\">\n\t<div {% if print_settings.repeat_header_footer %} id=\"header-html\" class=\"hidden-pdf\" {% endif %}>\n\t\t{{ add_header(loop.index, layout|len, doc, letter_head, no_letterhead, footer, print_settings, heading_template) }}\n\t</div>\n\n\t{% if loop.first %}\n\t<div class=\"row section-break\" data-label=\"{{ _(\"Supplier\") }}\">\n\t\t<div class=\"col-xs-12 column-break\">\n\t\t\t<h4>{{ _(\"Supplier\") }}</h4>\n\t\t\t<style>\n\t\t\t\t.supplier-details { width: 100%; }\n\t\t\t\t.supplier-details td { padding: 0 !important; border: 0; vertical-align: top; }\n\t\t\t\t.supplier-details td.text-muted { width: 40%; padding-right: 1em !important; whLine truncated
"line_breaks": 0,
"margin_bottom": 15.0,
"margin_left": 15.0,
"margin_right": 15.0,
"margin_top": 15.0,
"modified": "2026-09-23 12:00:00.000000",
"module": "Default Thai Company",
"name": "Default Standard Purchase Order",
"page_number": "Hide",
"pdf_generator": "wkhtmltopdf",
"print_format_builder": 0,
"print_format_builder_beta": 0,
"print_format_for": "DocType",
"print_format_type": "Jinja",
"raw_commands": null,
"raw_printing": 0,
"report": null,
"show_section_headings": 0,
"standard": "No"
},
{
"absolute_value": 0,
"align_labels_right": 0,
"css": "/* Thai text needs a font with Thai glyphs; wkhtmltopdf only sees the server's fonts and a\n headless server rarely has one, so ship Sarabun (OFL, see public/fonts/OFL.txt) with the app.\n Asset URLs are made absolute for the PDF renderer by frappe.utils.scrub_urls. */\n@font-face {\n\tfont-family: \"Sarabun\";\n\tfont-weight: 400;\n\tsrc: url(\"/assets/default_thai_company/fonts/Sarabun-Regular.ttf\");\n}\n@font-face {\n\tfont-family: \"Sarabun\";\n\tfont-weight: 700;\n\tsrc: url(\"/assets/default_thai_company/fonts/Sarabun-Bold.ttf\");\n}\n.print-format {\n\tfont-family: Sarabun, Inter, \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n}\n\n/* Print Style \"Redesign\" gives .letter-head a 30px margin; tighten it. */\n.print-format .letter-head {\n\tmargin-bottom: 5px;\n}\n\n/* One vertical rhythm for every label/value line. \"Redesign\" stacks field rows with\n margin-top: 15px (doubled where Total/Taxes nest a layout .row) while the custom\n Customer table and the heading dates had none; use line-height 1.8 everywhere\n and no row margins instead. */\n.print-format .row:not(.section-break) {\n\tmargin-top: 0 !important;\n\tline-height: 1.8;\n}\n.print-format .heading-dates,\n.print-format .customer-details td {\n\tline-height: 1.8;\n}\n\n/* standard.css gives .table 20px top/bottom margins; keep the item table close to its neighbours. */\n.print-format [data-fieldname=\"items\"] > .table {\n\tmargin-top: 5px;\n\tmargin-bottom: 5px;\n}",
"custom_format": 1,
"default_print_language": null,
"disabled": 0,
"doc_type": "Sales Order",
"docstatus": 0,
"doctype": "Print Format",
"font": null,
"font_size": 14,
"format_data": null,
"html": "{%- from \"templates/print_formats/standard_macros.html\" import add_header, render_field with context -%}\n{#- ERPNext hides taxes marked \"Is this Tax included in Basic Rate?\" unless Accounts Settings\n enables \"Show Inclusive Tax in Print\"; always show them here so the tax line and\n \"Total (Without Tax)\" appear on orders with inclusive tax. -#}\n{%- if doc.get(\"taxes\", {\"included_in_print_rate\": 1}) -%}\n\t{%- set ignored = doc.flags.update({\"show_inclusive_tax_in_print\": 1}) -%}\n{%- endif -%}\n{#- App copies of ERPNext's totals includes, printing the posted figures: \"total\" is the net before\n the Additional Discount; \"taxes\" prints the discount's net share, Net Total and each charge after\n discount, so the lines add up to Grand Total whichever total the discount applies on. -#}\n{%- set ignored = doc.print_templates.update({\"total\": \"default_thai_company/templates/print_formats/includes/total.html\"}) -%}\n{%- if doc.print_templates.get(\"taxes\") -%}\n\t{%- set ignored = doc.print_templates.update({\"taxes\": \"default_thai_company/templates/print_formats/includes/taxes.html\"}) -%}\n{%- endif -%}\n{#- Standard layout prints every non-empty field; drop the ones this order does not need.\n transaction_date / delivery_date and the customer's PO reference are printed in the heading,\n customer and shipping fields in the Customer section, in_words in the left column of the Totals\n section, the company address in the letter head. -#}\n{%- set hidden_fields = [\n\t\"total_qty\", \"has_unit_price_items\", \"amount_eligible_for_commission\", \"per_picked\", \"loyalty_points\",\n\t\"company_address\", \"company_address_display\", \"disable_rounded_total\",\n\t\"transaction_date\", \"delivery_date\", \"po_no\", \"po_date\",\n\t\"customer_name\", \"tax_id\", \"address_display\", \"contact_display\", \"contact_phone\", \"contact_mobile\", \"contact_email\", \"shipping_address\",\n\t\"in_words\",\n] -%}\n{#- rounded_total keeps a value even when rounding is disabled, so the standard layout would print it. -#}\n{%- if doc.is_rounded_total_disabled() -%}{%- set ignored = hidden_fields.append(\"rounded_total\") -%}{%- endif -%}\n{#- add_header renders this string via frappe.render_template with {\"doc\": doc}, so it is kept\n unrendered with {% raw %}. Same markup as the stock heading plus a right-aligned date block. -#}\n{%- set heading_template -%}\n{% raw %}\n<div class=\"print-heading\">\n\t<style>\n\t\t.print-heading .heading-dates { float: right; text-align: right; margin-top: 4px; white-space: nowrap; }\n\t\t.print-heading .heading-dates label { display: inline; font-weight: 700; margin: 0 4px 0 0; }\n\t\t.print-heading .heading-clear { clear: both; }\n\t</style>\n\t<div class=\"heading-dates\">\n\t\t<div><label>{{ _(doc.meta.get_label(\"transaction_date\")) }}:</label> {{ doc.get_formatted(\"transaction_date\") }}</div>\n\t\t{%- if doc.delivery_date %}\n\t\t<div><label>{{ _(doc.meta.get_label(\"delivery_date\")) }}:</label> {{ doc.get_formatted(\"delivery_date\") }}</div>\n\t\t{%- endif %}\n\t\t{%- if doc.po_no %}\n\t\t<div><label>{{ _(doc.meta.get_label(\"po_no\")) }}:</label> {{ doc.po_no }}</div>\n\t\t{%- endif %}\n\t\t{%- if doc.po_date %}\n\t\t<div><label>{{ _(doc.meta.get_label(\"po_date\")) }}:</label> {{ doc.get_formatted(\"po_date\") }}</div>\n\t\t{%- endif %}\n\t</div>\n\t<h2>\n\t\t<div>{{ _(doc.select_print_heading) or (_(doc.print_heading) if doc.print_heading != None else _(doc.doctype)) }}</div>\n\t\t<small class=\"sub-heading\">{{ _(doc.sub_heading) if doc.sub_heading != None else _(doc.name) }}</small>\n\t</h2>\n\t<div class=\"heading-clear\"></div>\n</div>\n{% endraw %}\n{%- endset -%}\n{#- Standard layout prints every non-empty child column; keep the item table to the essentials -#}\n{%- set item_columns = [\n\t{\"fieldname\": \"item_code\"},\n\t{\"fieldname\": \"description\"},\n\t{\"fieldname\": \"qty\"},\n\t{\"fieldname\": \"rate\"},\n\t{\"fieldname\": \"amount\"},\n] -%}\n\n{%- macro render_fields(fields, no_of_cols) -%}\n\t{%- for df in fields -%}\n\t\t{%- if df.fieldname not in hidden_fields -%}\n\t\t\t{%- if df.fieldname == \"items\" -%}\n\t\t\t\t{%- set df = df.as_dict() -%}\n\t\t\t\t{%- set ignored = df.update({\"visible_columns\": item_columns}) -%}\n\t\t\t{%- endif -%}\n\t\t\t{{ render_field(df, doc, no_of_cols) }}\n\t\t{%- endif -%}\n\t{%- endfor -%}\n{%- endmacro -%}\n\n{% for page in layout %}\n<div class=\"page-break\">\n\t<div {% if print_settings.repeat_header_footer %} id=\"header-html\" class=\"hidden-pdf\" {% endif %}>\n\t\t{{ add_header(loop.index, layout|len, doc, letter_head, no_letterhead, footer, print_settings, heading_template) }}\n\t</div>\n\n\t{% if loop.first %}\n\t<div class=\"row section-break\" data-label=\"{{ _(\"Customer\") }}\">\n\t\t<div class=\"col-xs-12 column-break\">\n\t\t\t<h4>{{ _(\"Customer\") }}</h4>\n\t\t\t<style>\n\t\t\t\t.customer-details { width: 100%; }\n\t\t\t\t.customer-details td { Line truncated
"line_breaks": 0,
"margin_bottom": 15.0,
"margin_left": 15.0,
"margin_right": 15.0,
"margin_top": 15.0,
"modified": "2026-09-23 12:00:00.000000",
"module": "Default Thai Company",
"name": "Default Standard Sales Order",
"page_number": "Hide",
"pdf_generator": "wkhtmltopdf",
"print_format_builder": 0,
"print_format_builder_beta": 0,
"print_format_for": "DocType",
"print_format_type": "Jinja",
"raw_commands": null,
"raw_printing": 0,
"report": null,
"show_section_headings": 0,
"standard": "No"
},
{ {
"absolute_value": 0, "absolute_value": 0,
"align_labels_right": 0, "align_labels_right": 0,
+3
View File
@@ -104,6 +104,9 @@ fixtures = [
"Default Standard Sales Invoice", "Default Standard Sales Invoice",
"Default Standard Tax Invoice/Receipt", "Default Standard Tax Invoice/Receipt",
"Default Standard Quotation", "Default Standard Quotation",
"Default Standard Sales Order",
"Default Standard Purchase Order",
"Default Standard Purchase Invoice",
"Withholding Tax Certificate", "Withholding Tax Certificate",
], ],
] ]
@@ -10,7 +10,11 @@
Net Total and the charges. Otherwise the Total line is the net before the discount, so the Net Total and the charges. Otherwise the Total line is the net before the discount, so the
discount is its net share, followed by Net Total and the charges. A discount that is not discount is its net share, followed by Net Total and the charges. A discount that is not
distributed (cash / non-trade) comes off the grand total only and stays after the charges, distributed (cash / non-trade) comes off the grand total only and stays after the charges,
as upstream. -#} as upstream.
Purchase documents: a "Deduct" charge (withholding tax) is taken off the total, so it is
printed negative; a "Valuation" charge only affects stock value and is not printed. Charge
descriptions are translated: the withholding categories' are in translations/th.csv. -#}
{%- macro amount_row(label, value) -%} {%- macro amount_row(label, value) -%}
<div class="row"> <div class="row">
<div class="col-xs-5 {%- if doc.align_labels_right %} text-right{%- endif -%}"> <div class="col-xs-5 {%- if doc.align_labels_right %} text-right{%- endif -%}">
@@ -39,8 +43,8 @@
{{ amount_row(_(doc.meta.get_label("net_total")), doc.get_formatted("net_total", doc)) }} {{ amount_row(_(doc.meta.get_label("net_total")), doc.get_formatted("net_total", doc)) }}
{%- endif -%} {%- endif -%}
{%- for charge in data -%} {%- for charge in data -%}
{%- if (charge.tax_amount or print_settings.print_taxes_with_zero_amount) and (not charge.included_in_print_rate or doc.flags.show_inclusive_tax_in_print) -%} {%- if (charge.tax_amount or print_settings.print_taxes_with_zero_amount) and (not charge.included_in_print_rate or doc.flags.show_inclusive_tax_in_print) and charge.get("category") != "Valuation" -%}
{{ amount_row(charge.get_formatted("description"), charge.get_formatted("tax_amount_after_discount_amount", doc)) }} {{ amount_row(charge.get_formatted("description", translated=True), ("- " if charge.get("add_deduct_tax") == "Deduct" else "") ~ charge.get_formatted("tax_amount_after_discount_amount", doc)) }}
{%- endif -%} {%- endif -%}
{%- endfor -%} {%- endfor -%}
{%- if doc.discount_amount and not net_discount -%} {%- if doc.discount_amount and not net_discount -%}
+15
View File
@@ -13,6 +13,9 @@ Receiver,ผู้รับ
Issuer,ผู้ออก Issuer,ผู้ออก
Accepted By,ผู้อนุมัติสั่งซื้อ Accepted By,ผู้อนุมัติสั่งซื้อ
Quoted By,ผู้เสนอราคา Quoted By,ผู้เสนอราคา
Approved By,ผู้อนุมัติ
Prepared By,ผู้จัดทำ
Acknowledged By,ผู้รับใบสั่งซื้อ
Tax Invoice,ใบกำกับภาษี Tax Invoice,ใบกำกับภาษี
Tax Invoice/Receipt,ใบกำกับภาษี/ใบเสร็จรับเงิน Tax Invoice/Receipt,ใบกำกับภาษี/ใบเสร็จรับเงิน
"Commission, brokerage, agency fees - Sec. 40(2), juristic payee (P.N.D.53)",ค่านายหน้า ค่าตัวแทน - มาตรา 40(2) ผู้รับเป็นนิติบุคคล (ภ.ง.ด.53) "Commission, brokerage, agency fees - Sec. 40(2), juristic payee (P.N.D.53)",ค่านายหน้า ค่าตัวแทน - มาตรา 40(2) ผู้รับเป็นนิติบุคคล (ภ.ง.ด.53)
@@ -39,6 +42,18 @@ Sec. 40(2)-(6) income paid to non-resident individuals - Sec. 50(2) (P.N.D.3),
"Dividends to foreign companies not doing business in Thailand - Sec. 70, pre-DTA (P.N.D.54)",เงินปันผลจ่ายให้บริษัทต่างประเทศที่มิได้ประกอบกิจการในประเทศไทย - มาตรา 70 ก่อนใช้อนุสัญญาภาษีซ้อน (ภ.ง.ด.54) "Dividends to foreign companies not doing business in Thailand - Sec. 70, pre-DTA (P.N.D.54)",เงินปันผลจ่ายให้บริษัทต่างประเทศที่มิได้ประกอบกิจการในประเทศไทย - มาตรา 70 ก่อนใช้อนุสัญญาภาษีซ้อน (ภ.ง.ด.54)
Sales Invoice,ใบแจ้งหนี้ Sales Invoice,ใบแจ้งหนี้
Sales Order,ใบสั่งขาย Sales Order,ใบสั่งขาย
Purchase Invoice,ใบแจ้งหนี้ซื้อ
Purchase Order,ใบสั่งซื้อ
Required By,วันที่ต้องการรับสินค้า
Supplier Invoice No,เลขที่ใบแจ้งหนี้ผู้จัดจำหน่าย
Supplier Invoice Date,วันที่ใบแจ้งหนี้ผู้จัดจำหน่าย
Customer's Purchase Order,เลขที่ใบสั่งซื้อของลูกค้า
Customer's Purchase Order Date,วันที่ใบสั่งซื้อของลูกค้า
Contact Name,ชื่อผู้ติดต่อ
Contact Mobile No,โทรศัพท์มือถือ
Supplier Contact,ผู้ติดต่อ
Shipping Address,ที่อยู่จัดส่ง
Accepted Qty,จำนวนที่รับ
Posting Date,วันที่ Posting Date,วันที่
Payment Due Date,วันครบกำหนดชำระ Payment Due Date,วันครบกำหนดชำระ
Contact Person,ผู้ติดต่อ Contact Person,ผู้ติดต่อ
1 Net Payable After Withholding Tax ยอดชำระสุทธิหลังหักภาษี ณ ที่จ่าย
13 Issuer ผู้ออก
14 Accepted By ผู้อนุมัติสั่งซื้อ
15 Quoted By ผู้เสนอราคา
16 Approved By ผู้อนุมัติ
17 Prepared By ผู้จัดทำ
18 Acknowledged By ผู้รับใบสั่งซื้อ
19 Tax Invoice ใบกำกับภาษี
20 Tax Invoice/Receipt ใบกำกับภาษี/ใบเสร็จรับเงิน
21 Commission, brokerage, agency fees - Sec. 40(2), juristic payee (P.N.D.53) ค่านายหน้า ค่าตัวแทน - มาตรา 40(2) ผู้รับเป็นนิติบุคคล (ภ.ง.ด.53)
42 Dividends to foreign companies not doing business in Thailand - Sec. 70, pre-DTA (P.N.D.54) เงินปันผลจ่ายให้บริษัทต่างประเทศที่มิได้ประกอบกิจการในประเทศไทย - มาตรา 70 ก่อนใช้อนุสัญญาภาษีซ้อน (ภ.ง.ด.54)
43 Sales Invoice ใบแจ้งหนี้
44 Sales Order ใบสั่งขาย
45 Purchase Invoice ใบแจ้งหนี้ซื้อ
46 Purchase Order ใบสั่งซื้อ
47 Required By วันที่ต้องการรับสินค้า
48 Supplier Invoice No เลขที่ใบแจ้งหนี้ผู้จัดจำหน่าย
49 Supplier Invoice Date วันที่ใบแจ้งหนี้ผู้จัดจำหน่าย
50 Customer's Purchase Order เลขที่ใบสั่งซื้อของลูกค้า
51 Customer's Purchase Order Date วันที่ใบสั่งซื้อของลูกค้า
52 Contact Name ชื่อผู้ติดต่อ
53 Contact Mobile No โทรศัพท์มือถือ
54 Supplier Contact ผู้ติดต่อ
55 Shipping Address ที่อยู่จัดส่ง
56 Accepted Qty จำนวนที่รับ
57 Posting Date วันที่
58 Payment Due Date วันครบกำหนดชำระ
59 Contact Person ผู้ติดต่อ