validate_fa2_invoice
Validates KSeF FA(2) XML invoices by performing XSD schema validation and Polish business-rule checks, returning errors and warnings.
Instructions
Validate a KSeF FA(2) XML invoice.
Runs XSD validation (when the official schema is present) and Polish business-rule checks. Returns a DocumentValidationResult with errors and warnings.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| xml_content | Yes |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| valid | Yes | ||
| errors | No | ||
| warnings | No | ||
| metadata | No |
Implementation Reference
- src/mcp_ksef_pl/server.py:78-86 (handler)The `validate_fa2_invoice` tool handler (MCP tool) — accepts xml_content string and delegates to FA2Validator.validate()
@mcp.tool async def validate_fa2_invoice(xml_content: str) -> DocumentValidationResult: """Validate a KSeF FA(2) XML invoice. Runs XSD validation (when the official schema is present) and Polish business-rule checks. Returns a DocumentValidationResult with errors and warnings. """ return await _fa2_validator.validate(xml_content) - src/mcp_ksef_pl/server.py:78-85 (registration)Tool registration via @mcp.tool decorator on validate_fa2_invoice function
@mcp.tool async def validate_fa2_invoice(xml_content: str) -> DocumentValidationResult: """Validate a KSeF FA(2) XML invoice. Runs XSD validation (when the official schema is present) and Polish business-rule checks. Returns a DocumentValidationResult with errors and warnings. """ return await _fa2_validator.validate(xml_content) - src/mcp_ksef_pl/validator.py:44-86 (handler)FA2Validator.validate() — actual validation logic: XSD schema validation (optional) + business-rule checks
async def validate(self, xml_content: str) -> DocumentValidationResult: errors: list[str] = [] warnings: list[str] = [] metadata: dict[str, object] = {"schema_version": _SCHEMA_VERSION} # --- XSD validation (optional — requires lxml + schema file) --- has_lxml, etree = _load_lxml() schema_path = self.get_schema_path() if has_lxml and schema_path and etree is not None: try: xsd_doc = etree.parse(schema_path) schema = etree.XMLSchema(xsd_doc) doc = etree.fromstring(xml_content.encode()) if not schema.validate(doc): xsd_errors = [str(e) for e in schema.error_log] raise XSDValidationError( message="FA(2) XSD validation failed", schema_version=_SCHEMA_VERSION, lxml_errors=xsd_errors, ) metadata["xsd_validated"] = True except XSDValidationError: raise except Exception as exc: warnings.append(f"XSD validation skipped due to parse error: {exc}") metadata["xsd_validated"] = False else: warnings.append( "XSD validation skipped: " + ("lxml not installed." if not has_lxml else f"schema file not found at {_SCHEMA_FILENAME}.") ) metadata["xsd_validated"] = False # --- Business rules (always run) --- errors.extend(self._check_business_rules(xml_content)) return DocumentValidationResult( valid=len(errors) == 0, errors=errors, warnings=warnings, metadata=metadata, ) - src/mcp_ksef_pl/validator.py:88-128 (helper)_check_business_rules() — KSeF business rule checks for mandatory elements, namespace, dates, etc.
def _check_business_rules(self, xml_content: str) -> list[str]: errors: list[str] = [] # Namespace presence if _FA2_NS not in xml_content: errors.append(f"Expected FA(2) namespace '{_FA2_NS}' not found.") # Mandatory header fields for tag in ("<KodFormularza", "<WariantFormularza>", "<DataWytworzenieFa>"): if tag not in xml_content: errors.append(f"Missing mandatory header element: {tag.strip('<')}.") # Seller NIP (Podmiot1 block) if "<Podmiot1>" not in xml_content: errors.append("Missing seller block <Podmiot1>.") # Buyer block if "<Podmiot2>" not in xml_content: errors.append("Missing buyer block <Podmiot2>.") # Invoice date P_1 if "<P_1>" not in xml_content: errors.append("Missing invoice date <P_1>.") else: match = re.search(r"<P_1>(\d{4}-\d{2}-\d{2})</P_1>", xml_content) if not match: errors.append("<P_1> must contain a date in YYYY-MM-DD format.") # Invoice number P_2 if "<P_2>" not in xml_content: errors.append("Missing invoice number <P_2>.") # Gross total P_15 if "<P_15>" not in xml_content: errors.append("Missing gross total <P_15>.") # Invoice lines if "<FaWiersze>" not in xml_content: errors.append("Missing invoice lines block <FaWiersze>.") return errors - src/mcp_ksef_pl/validator.py:1-21 (helper)Module docstring, constants (namespace, schema version/filename) and _load_lxml helper
"""FA(2) XML validator — XSD schema + KSeF business rules. The official XSD is published by the Polish Ministry of Finance at: https://www.podatki.gov.pl/ksef/dokumentacja-techniczna-ksef/ Download FA_VAT_v1-0E.xsd and place it at src/mcp_ksef_pl/schemas/FA_VAT_v1-0E.xsd to enable full XSD validation. Without the file, only structural/business checks run. """ import re from importlib.resources import files from pathlib import Path from mcp_einvoicing_core import ( BaseDocumentValidator, DocumentValidationResult, XSDValidationError, ) _FA2_NS = "http://crd.gov.pl/wzor/2023/06/29/12648/" _SCHEMA_VERSION = "FA(2) v1-0E" _SCHEMA_FILENAME = "FA_VAT_v1-0E.xsd"