3 Commits
Author SHA1 Message Date
kurogeek 12e90b72d6 fix: drop border around signature boxes 2026-09-10 09:41:08 +00:00
kurogeek f1f3f0cc2a feat: signature boxes on the Sales Invoice print format
Append a two-column signature block (Receiver/Date on behalf of the
customer, Issuer/Date on behalf of the company) after the Bank Account
Details section.
2026-09-10 09:16:29 +00:00
kurogeek e35179d413 feat: default Sales Invoice print format
Add the "Default Standard Sales Invoice" Print Format fixture and the
Property Setter that makes it the default for Sales Invoice.

The format reproduces Frappe's standard layout and appends a Bank Account
Details section on the last page when the company has an enabled Bank
Account flagged Is Company Account + Is Default Account
(get_company_bank_account jinja method).
2026-09-10 08:04:37 +00:00
4 changed files with 85 additions and 1 deletions
@@ -0,0 +1,36 @@
[
{
"absolute_value": 0,
"align_labels_right": 0,
"css": null,
"custom_format": 1,
"default_print_language": null,
"disabled": 0,
"doc_type": "Sales 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{%- set bank_account = get_company_bank_account(doc.company) -%}\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, print_heading_template) }}\n\t</div>\n\n\t{% for section in page %}\n\t<div class=\"row section-break\" data-label=\"{{ section.label or '' | e }}\">\n\t\t{%- if doc.print_line_breaks and loop.index != 1 -%}<hr>{%- endif -%}\n\t\t{%- if doc.print_section_headings and section.label and section.has_data -%}\n\t\t<h4 class='col-sm-12'>{{ _(section.label) }}</h4>\n\t\t{%- endif -%}\n\t\t{%- set no_of_cols = section.columns|len -%}\n\t\t{% for column in section.columns %}\n\t\t\t<div class=\"col-xs-{{ (12 / no_of_cols)|int }} column-break\">\n\t\t\t{% for df in column.fields %}\n\t\t\t\t{{ render_field(df, doc, no_of_cols) }}\n\t\t\t{% endfor %}\n\t\t\t</div>\n\t\t{% endfor %}\n\t</div>\n\t{% endfor %}\n\n\t{% if loop.last and bank_account %}\n\t<div class=\"row section-break\" data-label=\"{{ _(\"Bank Account Details\") }}\">\n\t\t{%- if doc.print_line_breaks -%}<hr>{%- endif -%}\n\t\t<div class=\"col-xs-12 column-break\">\n\t\t\t<h4>{{ _(\"Bank Account Details\") }}</h4>\n\t\t\t<style>\n\t\t\t\t.bank-account-details { width: 100%; }\n\t\t\t\t.bank-account-details td { padding: 0 !important; border: 0; }\n\t\t\t</style>\n\t\t\t<table class=\"bank-account-details\">\n\t\t\t\t<tbody>\n\t\t\t\t\t<tr><td class=\"text-muted\" style=\"width: 30%; padding-right: 1em !important\">{{ _(\"Bank\") }}</td><td>{{ bank_account.bank }}</td></tr>\n\t\t\t\t\t<tr><td class=\"text-muted\">{{ _(\"Account Name\") }}</td><td>{{ bank_account.account_name }}</td></tr>\n\t\t\t\t\t{% if bank_account.bank_account_no %}\n\t\t\t\t\t<tr><td class=\"text-muted\">{{ _(\"Bank Account No\") }}</td><td>{{ bank_account.bank_account_no }}</td></tr>\n\t\t\t\t\t{% endif %}\n\t\t\t\t\t{% if bank_account.iban %}\n\t\t\t\t\t<tr><td class=\"text-muted\">{{ _(\"IBAN\") }}</td><td>{{ bank_account.iban }}</td></tr>\n\t\t\t\t\t{% endif %}\n\t\t\t\t\t{% if bank_account.branch_code %}\n\t\t\t\t\t<tr><td class=\"text-muted\">{{ _(\"Branch Code\") }}</td><td>{{ bank_account.branch_code }}</td></tr>\n\t\t\t\t\t{% endif %}\n\t\t\t\t\t{% if bank_account.swift_number %}\n\t\t\t\t\t<tr><td class=\"text-muted\">{{ _(\"SWIFT number\") }}</td><td>{{ bank_account.swift_number }}</td></tr>\n\t\t\t\t\t{% endif %}\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t</div>\n\t</div>\n\t{% endif %}\n\n\t{% if loop.last %}\n\t<div class=\"row section-break\" data-label=\"{{ _(\"Signatures\") }}\">\n\t\t<div class=\"col-xs-12 column-break\">\n\t\t\t<style>\n\t\t\t\t.signatures { width: 100%; border-collapse: collapse; margin-top: 20px; }\n\t\t\t\t.signatures td { width: 50%; border: 0; padding: 6px 0 !important; vertical-align: top; }\n\t\t\t\t.signatures .signature-lines { margin-top: 60px; }\n\t\t\t\t.signatures .signature-lines span { display: inline-block; width: 120px; border-bottom: 1px solid #000; margin-right: 12px; }\n\t\t\t\t.signatures .signature-labels span { display: inline-block; width: 120px; margin-right: 12px; }\n\t\t\t</style>\n\t\t\t<table class=\"signatures\">\n\t\t\t\t<tbody>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t<div>{{ _(\"On behalf of\") }} {{ doc.customer_name or doc.customer }}</div>\n\t\t\t\t\t\t\t<div class=\"signature-lines\"><span></span><span></span></div>\n\t\t\t\t\t\t\t<div class=\"signature-labels\"><span>{{ _(\"Receiver\") }}</span><span>{{ _(\"Date\") }}</span></div>\n\t\t\t\t\t\t</td>\n\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t<div>{{ _(\"On behalf of\") }} {{ doc.company }}</div>\n\t\t\t\t\t\t\t<div class=\"signature-lines\"><span></span><span></span></div>\n\t\t\t\t\t\t\t<div class=\"signature-labels\"><span>{{ _(\"Issuer\") }}</span><span>{{ _(\"Date\") }}</span></div>\n\t\t\t\t\t\t</td>\n\t\t\t\t\t</tr>\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t</div>\n\t</div>\n\t{% endif %}\n\n\t<div {% if print_settings.repeat_header_footer %} id=\"footer-html\" class=\"visible-pdf\" {% endif %}>\n\t\t{% if not no_letterhead and footer %}\n\t\t<div class=\"letter-head-footer\">\n\t\t\t{{ footer }}\n\t\t</div>\n\t\t{% endif %}\n\t\t{% if print_settings.repeat_header_footer %}\n\t\t\t<p class=\"text-center small page-number visible-pdf\">\n\t\t\t\t{{ _(\"Page {0} of {1}\").format('<span class=\"page\"></span>', '<span class=\"topage\"></span>') }}\n\t\t\t</p>\n\t\t{% endif %}\n\t</div>\n</div>\n{% endfor %}\n",
"line_breaks": 0,
"margin_bottom": 15.0,
"margin_left": 15.0,
"margin_right": 15.0,
"margin_top": 15.0,
"modified": "2026-09-10 16:40:27.176429",
"module": "Default Thai Company",
"name": "Default Standard Sales 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"
}
]
@@ -0,0 +1,18 @@
[
{
"default_value": null,
"doc_type": "Sales Invoice",
"docstatus": 0,
"doctype": "Property Setter",
"doctype_or_field": "DocType",
"field_name": null,
"is_system_generated": 1,
"modified": "2026-09-10 13:55:13.996942",
"module": null,
"name": "Sales Invoice-main-default_print_format",
"property": "default_print_format",
"property_type": "Data",
"row_name": null,
"value": "Default Standard Sales Invoice"
}
]
+9 -1
View File
@@ -75,7 +75,10 @@ required_apps = ["erpnext"]
# add methods and filters to jinja environment
jinja = {
"methods": ["default_thai_company.utils.get_letter_head_company"],
"methods": [
"default_thai_company.utils.get_letter_head_company",
"default_thai_company.utils.get_company_bank_account",
],
}
# Fixtures
@@ -84,6 +87,11 @@ jinja = {
fixtures = [
{"doctype": "Letter Head", "filters": [["name", "in", ["Default Letter Head"]]]},
{"doctype": "Address Template", "filters": [["name", "in", ["Thailand"]]]},
{"doctype": "Print Format", "filters": [["name", "in", ["Default Standard Sales Invoice"]]]},
{
"doctype": "Property Setter",
"filters": [["name", "in", ["Sales Invoice-main-default_print_format"]]],
},
]
# Installation
+22
View File
@@ -29,6 +29,28 @@ def get_letter_head_company(doc=None):
)
def get_company_bank_account(company=None):
"""Default company Bank Account for print formats.
Returns the enabled Bank Account flagged `Is Company Account` and
`Is Default Account` for `company`, or None when there is none.
"""
if not company:
return None
account = frappe.db.get_value(
"Bank Account",
{"company": company, "is_company_account": 1, "is_default": 1, "disabled": 0},
["account_name", "bank", "bank_account_no", "iban", "branch_code"],
as_dict=True,
)
if not account:
return None
account.swift_number = frappe.db.get_value("Bank", account.bank, "swift_number")
return account
def stash_letter_head_source(doc, method=None):
"""Fixture import re-inserts the Letter Head; remember the fixture's `source`."""
doc.flags.fixture_source = doc.source