Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
31 / 31
100.00% covered (success)
100.00%
1 / 1
CRAP
100.00% covered (success)
100.00%
1 / 1
ZugferdInvoiceConstraint
100.00% covered (success)
100.00%
31 / 31
100.00% covered (success)
100.00%
1 / 1
7
100.00% covered (success)
100.00%
1 / 1
 check
100.00% covered (success)
100.00%
31 / 31
100.00% covered (success)
100.00%
1 / 1
7
1<?php
2
3declare(strict_types=1);
4
5namespace Phpdftk\Pdf\Conformance\Constraint;
6
7use Phpdftk\Pdf\Conformance\Inspection\DocumentInspector;
8use Phpdftk\Pdf\Conformance\Profile\ConformanceProfile;
9use Phpdftk\Pdf\Conformance\Profile\ZugferdProfile;
10use Phpdftk\Pdf\Conformance\Result\ConformanceViolation;
11use Phpdftk\Pdf\Conformance\Result\ViolationSeverity;
12use Phpdftk\Pdf\Core\FileSpec\FileSpec;
13
14/**
15 * ZUGFeRD / Factur-X: Embedded invoice XML validation.
16 *
17 * Verifies that an XML invoice file is embedded with the correct filename
18 * (factur-x.xml or zugferd-invoice.xml) and an appropriate AFRelationship.
19 */
20final class ZugferdInvoiceConstraint implements ConformanceConstraint
21{
22    private const VALID_FILENAMES = [
23        'factur-x.xml',
24        'zugferd-invoice.xml',
25    ];
26
27    public function check(DocumentInspector $inspector, ConformanceProfile $profile): array
28    {
29        if (!$profile instanceof ZugferdProfile) {
30            return [];
31        }
32
33        if (!$inspector->hasEmbeddedFiles()) {
34            return [
35                new ConformanceViolation(
36                    clause: 'A.2',
37                    message: 'Factur-X requires an embedded XML invoice file',
38                    severity: ViolationSeverity::Error,
39                    objectPath: 'Catalog.Names.EmbeddedFiles',
40                ),
41            ];
42        }
43
44        // Check for a FileSpec with a valid invoice filename
45        $foundInvoice = false;
46        foreach ($inspector->getRegisteredObjects() as $object) {
47            if ($object instanceof FileSpec) {
48                $filename = $object->uf?->value ?? $object->f?->value ?? '';
49                if (in_array(strtolower($filename), self::VALID_FILENAMES, true)) {
50                    $foundInvoice = true;
51                    break;
52                }
53            }
54        }
55
56        if (!$foundInvoice) {
57            return [
58                new ConformanceViolation(
59                    clause: 'A.2',
60                    message: sprintf(
61                        'Factur-X requires an embedded file named %s',
62                        implode(' or ', self::VALID_FILENAMES),
63                    ),
64                    severity: ViolationSeverity::Error,
65                    objectPath: 'Catalog.Names.EmbeddedFiles',
66                ),
67            ];
68        }
69
70        return [];
71    }
72}