- 后端
- 企业应用
【免费下载链接】erpnext
Free and Open Source Enterprise Resource Planning (ERP)
本文以 ERPNext 仓库中的 地址模板说明文档 为骨架,系统讲解如何为特定国家新增地址打印模板:从文件命名规则、Jinja 模板语法、可用字段,到模板的安装导入机制、渲染调用链与测试验证。读完本文,你将掌握在 ERPNext 中为一国地址定制打印版式(如邮编与城市排列、州省略规则、电话/传真展示等)的完整开发与调试方法。
一、Address Template 解决什么问题
在 ERPNext 中,Address(地址)是贯穿客户、供应商、公司、发货与开票的核心单据,凡是需要打印地址的场景——发票抬头、收货地址、公司信纸、发货标签——最终都以纯文本或多行 HTML 的形式呈现。但不同国家对地址书写格式有各自的习惯:
- 美国习惯
City, State Zip一行式排列,且本国地址不重复输出国家名; - 德国习惯
邮编 城市且城市名大写,国家名大写置于末尾; - 台湾地区习惯国家 → 邮编 → 县市 → 地址逐级递减的倒序排版,并附带电话/传真/邮箱。
ERPNext 通过「地区地址模板(Address Template)」机制解决这一差异:每个国家可以拥有自己的一份Jinja 模板,系统根据地址所属国家自动选择对应模板来渲染地址显示文本。
二、如何为你的国家添加地址模板
根据 官方说明,只需两步:
2.1 文件命名规则
在erpnext/regional/address_template/templates/目录下放置一个新文件:
- 文件名为:
your_country.html,使用小写字母 + 下划线格式; - 文件内容为:一份 Jinja Template 模板。
例如美国对应united_states.html,德国对应germany.html。文件名会被自动转换为国家显示名(见下文安装机制中的country()函数),因此请确保文件名与国家名称一一对应。
2.2 模板内容与可用字段
模板中可以直接使用Address 单据的全部字段,包括通过自定义字段(Custom Fields)新增的字段。官方文档给出的基础示例:
{{ address_line1 }}<br> {% if address_line2 %}{{ address_line2 }}<br>{% endif -%} {{ city }}<br> {% if state %}{{ state }}<br>{% endif -%} {% if pincode %} PIN: {{ pincode }}<br>{% endif -%} {{ country }}<br> {% if phone %}Phone: {{ phone }}<br>{% endif -%} {% if fax %}Fax: {{ fax }}<br>{% endif -%} {% if email_id %}Email: {{ email_id }}<br>{% endif -%}从示例与仓库内置模板中可以看到,地址模板常用字段包括:address_line1、address_line2、city、state、pincode、country、county、phone、fax、email_id等。
Jinja 模板要点
- 普通输出:
{{ field_name }},如{{ address_line1 }}; - 条件输出:
{% if field %}...{% endif -%},字段为空时跳过整段内容; - 行尾
-%}是 Jinja 的空白控制符,用于去除紧跟其后的换行,避免生成多余空行; - 可调用 Jinja 内置过滤器,如
{{ city | upper }}将城市名转为大写; - 可用
{% if country != "United States" %}这类条件表达式做逻辑判断。
三、仓库内置模板实例解析
仓库在erpnext/regional/address_template/templates/下内置了 9 个国家/地区的模板,是学习地址格式差异的最佳范本:
| 文件 | 格式特点 |
|---|---|
germany.html | 邮编 城市(城市名大写)+ 国家名大写,标准德语地址格式 |
denmark.html、sweden.html | 同德国式邮编 城市+ 国家名大写 |
united_states.html | City, State Zip单行式;{% if country != "United States" %}在本国地址中省略国家名 |
taiwan.html | 倒序排版:国家 → 邮编 → 县市(county)→ 地址行,末尾追加电话/传真/邮箱 |
bosnia_and_herzegovina.html、croatia.html、luxembourg.html、switzerland.html | 各按本国习惯排列基础字段 |
以 德国模板 为例:
{{ address_line1 }}<br> {% if address_line2 %}{{ address_line2 }}<br>{% endif -%} {{ pincode }} {{ city | upper }}<br> {{ country | upper }}而以 美国模板 为例,它用条件判断在地址属于美国本土时省略国家字段,非常典型:
{{ address_line1 }}<br> {% if address_line2 %}{{ address_line2 }}<br>{% endif -%} {{ city }}, {% if state %}{{ state }}{% endif -%}{% if pincode %} {{ pincode }}{% endif -%}<br> {% if country != "United States" %}{{ country }}{% endif -%}需要说明的是,county(县/郡)等字段来自 Frappe 框架contacts模块中Address单据的定义,因此字段是否可用以实际启用的 Address 表单为准;你在自定义字段中添加的任何字段同样会被传入模板。
四、模板的安装与导入机制(源码级)
仅仅放置 HTML 文件还不够,ERPNext 会在安装/初始化时把这些文件写入数据库中的Address Template单据(DocType)。这段逻辑位于 setup.py:
def set_up_address_templates(default_country=None): for country, html in get_address_templates(): is_default = 1 if country == default_country else 0 update_address_template(country, html, is_default)其工作流程分三步:
4.1 扫描templates/目录
get_address_templates()遍历erpnext/regional/address_template/templates/目录下所有.html文件,并做两个转换:
def country(file_name): """Convert 'united_states.html' to 'United States'.""" suffix_pos = file_name.find(".html") country_snake_case = file_name[:suffix_pos] country_title_case = " ".join(country_snake_case.split("_")).title() return country_title_caseunited_states.html→United Statesbosnia_and_herzegovina.html→Bosnia And Herzegovina
这正解释了命名规则中「小写 + 下划线」的要求——文件名必须能被程序自动还原为国家标题。
4.2 校验并写入Address Template单据
update_address_template(country, html, is_default)负责持久化:
- 若
Country单据中不存在该国,记录错误日志后跳过(防止脏数据); - 若
Address Template已存在,则用frappe.db.set_value更新template与is_default字段; - 若不存在,则
frappe.get_doc(...).insert()新建单据。
4.3 在安装流程中被调用
在 install_fixtures.py 的初始化阶段(第 361 行),安装向导会调用:
set_up_address_templates(default_country=country)default_country由安装向导选择的默认国家传入,该国模板会被标记为is_default = 1;该文件第 30~31 行同时确保即使没有地区模板,也至少为安装国创建一个空白的Address Template单据。
五、模板如何被渲染使用
模板的最终消费点在 ERPNext 对Address的扩展类中:accounts/custom/address.py 的get_shipping_address白名单方法:
address_as_dict = address[0] name, address_template = get_address_templates(address_as_dict) return address_as_dict.get("name"), frappe.render_template( address_template, address_as_dict, restrict_globals=True )get_address_templates(address_as_dict)来自 Frappe 框架frappe.contacts.doctype.address.address,它会根据地址的国家字段匹配对应的Address Template单据并取回模板内容;frappe.render_template(template, context, restrict_globals=True)使用 Jinja 渲染模板,address_as_dict作为上下文,因此 Address 的所有字段(含自定义字段)都能在模板中以{{ 字段名 }}直接引用;restrict_globals=True限制模板可访问的全局对象,防止任意代码执行,属于安全加固。
注意区分两个同名的
get_address_templates:setup.py中的用于导入(读取本地 HTML 文件),address.py中从 Frappe contacts 模块导入的用于渲染(按国家查数据库模板)。二者分别在开发期和运行期发挥作用。
六、如何验证你的模板
仓库提供了完整的单元测试:test_regional_address_template.py,覆盖导入与持久化两个环节:
def test_get_address_templates(self): """Get the countries and paths from the templates directory.""" templates = get_address_templates() self.assertIsInstance(templates, list) self.assertIsInstance(templates[0], tuple) def test_create_address_template(self): """Create a new Address Template.""" country = ensure_country("Germany") update_address_template(country.name, "TEST") doc = frappe.get_doc("Address Template", country.name) self.assertEqual(doc.template, "TEST") def test_update_address_template(self): """Update an existing Address Template.""" ... update_address_template(country.name, "NEW") doc = frappe.get_doc("Address Template", country.name) self.assertEqual(doc.template, "NEW")测试验证了三点关键行为:目录扫描能正确返回(国家, HTML内容)元组列表;新建国家模板后数据库记录与文件内容一致;重复导入会更新而非重复创建。开发新模板时,可以仿照此测试补充针对你自己国家模板的断言。
七、端到端实操清单
- 编写模板:在
erpnext/regional/address_template/templates/下新建your_country.html,参考 README 示例 与 德国模板、美国模板 的写法; - 保证命名对应:确认文件名下划线命名可逆转为
Country单据中已存在的国家名,否则导入时会被update_address_template跳过并记入错误日志; - 触发导入:运行
bench migrate或在安装向导中重新执行初始化(set_up_address_templates),模板将被写入Address Template单据;也可以在系统内直接编辑该单据的template字段; - 验证渲染:为某个 Address 记录设置对应国家,通过调用
get_shipping_address或在相关单据的地址打印处观察输出; - 跑测试:执行
bench run-tests --module erpnext.regional.address_template(或按项目测试约定运行ERPNextTestSuite)确认导入逻辑无回归。
八、小结
ERPNext 的地区地址模板是一个「约定优于配置」的轻量扩展点:放一个符合命名约定的 Jinja 文件到templates/目录,安装时自动入库,运行时按地址国家自动选模板渲染。它既能覆盖各国千差万别的地址排版习惯,又通过自定义字段透传保留了充分的灵活性,是理解 ERPNext「区域化定制」设计模式的绝佳入口。
- 后端
- 企业应用
【免费下载链接】erpnext
Free and Open Source Enterprise Resource Planning (ERP)
相关推荐
OpenCart 地址格式(Address Formats)完全指南:模板占位符、国家分配与默认格式配置
OpenCart 地址格式(Address Formats)完全指南:模板占位符、国家分配与默认格式配置 导读 OpenCart 的 System → Loca
电商后端地址处理模块:fuels-ts区块链地址格式转换与验证
地址处理模块:fuels ts区块链地址格式转换与验证 概述 在区块链开发中,地址处理是基础但至关重要的环节。fuels ts的 @fuel ts/addres
区块链Web3地址解析完全手册:用Address-Parse轻松搞定中文地址智能识别
地址解析完全手册:用Address Parse轻松搞定中文地址智能识别 🌏 还在为处理杂乱无章的中文地址信息而烦恼吗?地址解析神器Address Parse来
数据清洗后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考