Skip to content

feat: implement XML attachment file naming functionality#253

Open
dafrose wants to merge 2 commits intoalyf-de:developfrom
dafrose:feat-xml-auto-name
Open

feat: implement XML attachment file naming functionality#253
dafrose wants to merge 2 commits intoalyf-de:developfrom
dafrose:feat-xml-auto-name

Conversation

@dafrose
Copy link
Copy Markdown
Member

@dafrose dafrose commented Apr 24, 2026

  • Added E Invoice Settings field for global autonaming pattern for XML attachements.
  • Added get_xml_attachment_file_base_name function to resolve file names.
  • Introduced unit tests for various naming scenarios in test_xml_attachment_naming.py.
  • Updated Sales Invoice logic to utilize the new naming function for XML file attachments.
  • Enhanced E Invoice Settings to include a new field for auto naming format of XML files.

closes #245

- Added E Invoice Settings field for global autonaming pattern for XML attachements.
- Added `get_xml_attachment_file_base_name` function to resolve file names.
- Introduced unit tests for various naming scenarios in `test_xml_attachment_naming.py`.
- Updated Sales Invoice logic to utilize the new naming function for XML file attachments.
- Enhanced E Invoice Settings to include a new field for auto naming format of XML files.
@dafrose dafrose changed the title feat: implement XML attachment file naming functionality #245 feat: implement XML attachment file naming functionality Apr 24, 2026
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Apr 24, 2026

Greptile Summary

This PR introduces a configurable XML attachment file naming pattern for E Invoice Settings, allowing users to define a dot-separated naming series (with date codes and field references) that is resolved at submit time via frappe.model.naming.parse_naming_series. The implementation includes a clean fallback chain (error → doc.name) and a dedicated test suite covering the main paths.

Confidence Score: 5/5

Safe to merge — all remaining findings are P2 style/UX observations with no correctness impact on the happy path.

The core logic is correct, the fallback chain is robust, and the test suite covers the main scenarios. Both flagged items are P2: one is a code-organisation nit (redundant cached doc fetch) and the other is an edge-case UX issue with patterns that mix hash segments and separator literals — neither affects correctness for patterns described in the UI documentation.

eu_einvoice/xml_attachment_naming.py — dangling separator edge case; eu_einvoice/european_e_invoice/custom/sales_invoice.py — redundant settings fetch in _attach_xml_file.

Important Files Changed

Filename Overview
eu_einvoice/xml_attachment_naming.py New module implementing the naming-pattern resolver; solid fallback chain, but _sanitize_base only replaces / and hash-segment stripping can leave dangling separator literals.
eu_einvoice/european_e_invoice/custom/sales_invoice.py Integrates the new naming function cleanly; introduces a redundant get_cached_doc("E Invoice Settings") call inside _attach_xml_file when the caller already holds the doc.
eu_einvoice/test_xml_attachment_naming.py Good coverage of empty/whitespace, field substitution, hash-stripping, and slash sanitization; exception-fallback path and dangling-separator edge case are not exercised.
eu_einvoice/european_e_invoice/doctype/e_invoice_settings/e_invoice_settings.json Adds the new auto_name_format_for_xml_file Data field with depends_on guard; description is detailed and includes examples.
eu_einvoice/european_e_invoice/doctype/e_invoice_settings/e_invoice_settings.py Adds the `auto_name_format_for_xml_file: DF.Data

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Sales Invoice submitted] --> B{auto_attach_xml?}
    B -- No --> Z[Exit]
    B -- Yes --> C[get_cached_doc E Invoice Settings]
    C --> D[get_einvoice XML bytes]
    D --> E[_attach_xml_file doc, xml, field_name]
    E --> F[get_cached_doc E Invoice Settings again]
    F --> G[get_xml_attachment_file_base_name doc, pattern]
    G --> H{pattern empty?}
    H -- Yes --> I[sanitize doc.name]
    H -- No --> J[strip hash-only dot segments]
    J --> K{pattern now empty?}
    K -- Yes --> I
    K -- No --> L[parse_naming_series pattern.split dot]
    L --> M{exception?}
    M -- Yes --> N[log_error + fallback doc.name]
    M -- No --> O{base empty?}
    O -- Yes --> I
    O -- No --> P[sanitize base]
    I --> Q[base_name + .xml]
    P --> Q
    Q --> R[Create File doc + save]
Loading

Reviews (2): Last reviewed commit: "refactor: enhance XML attachment naming ..." | Re-trigger Greptile

Comment thread eu_einvoice/xml_attachment_naming.py Outdated
Comment on lines +45 to +46
def _sanitize_base(base: str) -> str:
return base.replace("/", "-")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 _sanitize_base only strips /

File names that include characters like :, *, ?, ", <, >, |, or \ can cause problems on some operating systems or web servers (e.g. Windows-based deployments, certain cloud storage backends). A field value such as "Invoice: 001" or "Q1 > Q2" resolved into the pattern would produce a file name that passes through unmodified. Expanding the sanitization pattern guards against this edge case.

Suggested change
def _sanitize_base(base: str) -> str:
return base.replace("/", "-")
_UNSAFE_CHARS = str.maketrans({c: "-" for c in r'/\:*?"<>|'})
def _sanitize_base(base: str) -> str:
return base.translate(_UNSAFE_CHARS)

Copy link
Copy Markdown
Member Author

@dafrose dafrose Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I compared the suggestion to how Frappe does in their exposed API:

Naming resolution does not sanitize arbitrary field text: parse_naming_series / _format_autoname interpolate values as-is, and validate_name only rejects < and > for document names.1

Partial overlap with File: when the attachment is saved, Frappe already rewrites / \ % ? # to _ for the on-disk / URL basename via get_safe_file_name2 (used from save_file_on_filesystem3). It does not cover the full Windows-reserved set (: * ? " < > |, etc.), so broader sanitization in our app would be stricter than core today, not redundant with naming.

Suggested change: reuse get_safe_file_name here so the XML base name matches what File will apply anyway, instead of only replacing /. Prefer adding from frappe.core.doctype.file.utils import get_safe_file_name next to the other imports at the top of this module and keeping _sanitize_base as a thin wrapper (shown below with a local import so the suggestion applies to the existing hunk only).

Suggested change
def _sanitize_base(base: str) -> str:
return base.replace("/", "-")
def _sanitize_base(base: str) -> str:
from frappe.core.doctype.file.utils import get_safe_file_name
return get_safe_file_name(base)

Footnotes

  1. https://github.com/frappe/frappe/blob/develop/frappe/model/naming.py#L469-L496

  2. https://github.com/frappe/frappe/blob/develop/frappe/core/doctype/file/utils.py#L481-L482

  3. https://github.com/frappe/frappe/blob/develop/frappe/core/doctype/file/file.py#L785-L792

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, but if the wrapper only calls get_safe_file_name we might as well drop the wrapper and call it directly.

@dafrose
Copy link
Copy Markdown
Member Author

dafrose commented Apr 24, 2026

Here is, how this looks in Desk:

  1. Attachement disabled:
image
  1. Attachement enabled:
image
  1. Filename in Sales Invoice:
image

Comment thread eu_einvoice/xml_attachment_naming.py Outdated
@HenningWendtland
Copy link
Copy Markdown
Member

@dafrose I think allowing endings like .#### creates new (global) naming series counters in the database and is something that we do not need. Normally a user wants to:

  • use the docname or
  • a custom Invoice ID (may be a different field)

in the .xml name + some string like "Rechnung-", which makes the .xml prefix usually unique per Invoice, so no further numbering is needed

- Updated `get_xml_attachment_file_base_name` to strip segments consisting only of '#' characters.
- Added new test cases in `test_xml_attachment_naming.py` for various naming scenarios.
- Improved documentation in the E Invoice Settings JSON for clarity on naming patterns.
@dafrose
Copy link
Copy Markdown
Member Author

dafrose commented Apr 24, 2026

I removed the mention of .#### counters in the field description and added code to strip hashtag-segments entirely in aca4527.

@0xD0M1M0
Copy link
Copy Markdown
Contributor

Just thinking of the manual "download"-button. Is there a reason, why, if the admin chooses to rename automatically, we would exclude it on this button?

return _sanitize_base(cstr(doc.name))

try:
base = parse_naming_series(pattern.split("."), doc=doc)
Copy link
Copy Markdown
Member

@HenningWendtland HenningWendtland Apr 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
base = parse_naming_series(pattern.split("."), doc=doc)
base = parse_naming_series([p.lstrip("#") for p in pattern.split(".")], doc=doc)

parse naming series checks for parts starting with "#", so it would make sense to filter the exact same pattern and not just parts containing only #. Also condensing this into one line makes _strip_hash_only_dot_segments and the _HASH_ONLY_SEGMENT unneeded

Comment on lines +32 to +36
pattern = _strip_hash_only_dot_segments(pattern)
pattern = pattern.strip()
if not pattern:
return _sanitize_base(cstr(doc.name))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pattern = _strip_hash_only_dot_segments(pattern)
pattern = pattern.strip()
if not pattern:
return _sanitize_base(cstr(doc.name))

See comment below

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: Auto Name Field for Auto-attach XML File feature

4 participants