Revision ba40069b
Von Moritz Bunkus vor mehr als 3 Jahren hinzugefügt
SL/ZUGFeRD.pm | ||
---|---|---|
9 | 9 |
use List::Util qw(first); |
10 | 10 |
use XML::LibXML; |
11 | 11 |
|
12 |
use SL::Locale::String qw(t8); |
|
13 |
|
|
14 |
use parent qw(Exporter); |
|
15 |
our @EXPORT_PROFILES = qw(PROFILE_FACTURX_EXTENDED); |
|
16 |
our @EXPORT_OK = (@EXPORT_PROFILES); |
|
17 |
our %EXPORT_TAGS = (PROFILES => \@EXPORT_PROFILES); |
|
18 |
|
|
19 |
use constant PROFILE_FACTURX_EXTENDED => 0; |
|
20 |
|
|
12 | 21 |
use constant RES_OK => 0; |
13 | 22 |
use constant RES_ERR_FILE_OPEN => 1; |
14 | 23 |
use constant RES_ERR_NO_XMP_METADATA => 2; |
... | ... | |
16 | 25 |
use constant RES_ERR_NOT_ZUGFERD => 4; |
17 | 26 |
use constant RES_ERR_UNSUPPORTED_ZUGFERD_VERSION => 5; |
18 | 27 |
|
28 |
our @customer_settings = ( |
|
29 |
[ 0, t8('Do not create Factur-X/ZUGFeRD invoices') ], |
|
30 |
[ PROFILE_FACTURX_EXTENDED() * 2 + 1, t8('Create with profile \'Factur-X 1.0.05/ZUGFeRD 2.1.1 extended\'') ], |
|
31 |
[ PROFILE_FACTURX_EXTENDED() * 2 + 2, t8('Create with profile \'Factur-X 1.0.05/ZUGFeRD 2.1.1 extended\' (test mode)') ], |
|
32 |
); |
|
33 |
|
|
34 |
sub convert_customer_setting { |
|
35 |
my ($class, $customer_setting) = @_; |
|
36 |
|
|
37 |
return () if ($customer_setting <= 0) || ($customer_setting >= scalar(@customer_settings)); |
|
38 |
|
|
39 |
return ( |
|
40 |
profile => int(($customer_setting - 1) / 2), |
|
41 |
test_mode => ($customer_setting - 1) % 2, |
|
42 |
); |
|
43 |
} |
|
44 |
|
|
19 | 45 |
sub _extract_zugferd_invoice_xml { |
20 | 46 |
my $doc = shift; |
21 | 47 |
my $names_dict = $doc->getValue($doc->getRootDict->{Names}) or return {}; |
... | ... | |
109 | 135 |
next unless $ns; |
110 | 136 |
|
111 | 137 |
if ($ns->getData =~ m{urn:zugferd:pdfa:CrossIndustryDocument:invoice:2p0}) { |
112 |
$zugferd_version = '2p0'; |
|
138 |
$zugferd_version = 'zugferd:2p0'; |
|
139 |
last; |
|
140 |
} |
|
141 |
|
|
142 |
if ($ns->getData =~ m{urn:factur-x:pdfa:CrossIndustryDocument:invoice:1p0}) { |
|
143 |
$zugferd_version = 'factur-x:1p0'; |
|
113 | 144 |
last; |
114 | 145 |
} |
115 | 146 |
|
116 |
if ($ns->getData =~ m{zugferd}i) { |
|
147 |
if ($ns->getData =~ m{zugferd|factur-x}i) {
|
|
117 | 148 |
$zugferd_version = 'unsupported'; |
118 | 149 |
last; |
119 | 150 |
} |
... | ... | |
122 | 153 |
if (!$zugferd_version) { |
123 | 154 |
return { |
124 | 155 |
result => RES_ERR_NOT_ZUGFERD(), |
125 |
message => $::locale->text('The XMP metadata does not declare the ZUGFeRD data.'), |
|
156 |
message => $::locale->text('The XMP metadata does not declare the Factur-X/ZUGFeRD data.'),
|
|
126 | 157 |
}; |
127 | 158 |
} |
128 | 159 |
|
129 |
if ($zugferd_version !~ m{^2p}) {
|
|
160 |
if ($zugferd_version eq 'unsupported') {
|
|
130 | 161 |
return { |
131 | 162 |
result => RES_ERR_UNSUPPORTED_ZUGFERD_VERSION(), |
132 |
message => $::locale->text('The ZUGFeRD version used is not supported.'), |
|
163 |
message => $::locale->text('The Factur-X/ZUGFeRD version used is not supported.'),
|
|
133 | 164 |
}; |
134 | 165 |
} |
135 | 166 |
|
... | ... | |
138 | 169 |
if (!defined $invoice_xml) { |
139 | 170 |
return { |
140 | 171 |
result => RES_ERR_NO_XML_INVOICE(), |
141 |
message => $::locale->text('The ZUGFeRD XML invoice was not found.'), |
|
172 |
message => $::locale->text('The Factur-X/ZUGFeRD XML invoice was not found.'),
|
|
142 | 173 |
}; |
143 | 174 |
} |
144 | 175 |
|
... | ... | |
159 | 190 |
|
160 | 191 |
=head1 NAME |
161 | 192 |
|
162 |
SL::ZUGFeRD - Helper functions for dealing with PDFs containing ZUGFeRD invoice data |
|
193 |
SL::ZUGFeRD - Helper functions for dealing with PDFs containing Factur-X/ZUGFeRD invoice data
|
|
163 | 194 |
|
164 | 195 |
=head1 SYNOPSIS |
165 | 196 |
|
... | ... | |
182 | 213 |
|
183 | 214 |
=item C<extract_from_pdf> C<$file_name> |
184 | 215 |
|
185 |
Opens an existing PDF in the file system and tries to extract ZUGFeRD |
|
186 |
invoice data from it. First it'll parse the XMP metadata and look for |
|
187 |
the ZUGFeRD declaration inside. If the declaration isn't found or the |
|
188 |
declared version isn't 2p0, an error is returned. |
|
216 |
Opens an existing PDF in the file system and tries to extract |
|
217 |
Factur-X/ZUGFeRD invoice data from it. First it'll parse the XMP |
|
218 |
metadata and look for the Factur-X/ZUGFeRD declaration inside. If the |
|
219 |
declaration isn't found or the declared version isn't 2p0, an error is |
|
220 |
returned. |
|
189 | 221 |
|
190 | 222 |
Otherwise it'll continue to look through all embedded files in the |
191 | 223 |
PDF. The first embedded XML file with a root node of |
... | ... | |
198 | 230 |
|
199 | 231 |
=item C<RES_OK> (0): parsing was OK; the returned hash will also |
200 | 232 |
contain the keys C<xmp_metadata> and C<invoice_xml> which will contain |
201 |
the XML text of the metadata & the ZUGFeRD invoice. |
|
233 |
the XML text of the metadata & the Factur-X/ZUGFeRD invoice.
|
|
202 | 234 |
|
203 | 235 |
=item C<RES_ERR_…> (all values E<gt> 0): parsing failed; the hash will |
204 | 236 |
also contain a key C<message> which contains a human-readable |
Auch abrufbar als: Unified diff
Factur-X/ZUGFeRD: in »Factur-X/ZUGFeRD« umbenannt
Mit ZUGFeRD-Standard Version 2.1.1 ist der offizielle Name des
EU-Standards schlicht Factur-X. ZUGFeRD ist nur noch der tolerierte
alte Name.
In der Oberfläche ist nun überall von »Factur-X/ZUGFeRD« die Rede.
Im Quellcode heißen die Module hingegen weiterhin `SL::…::ZUGFeRD`,
weil die Umstellung ansonsten zu groß und irgendwo auch nicht so nötig
ist.
Es ändern sich auch die ganzen Namen in den Metadaten des PDFs und der
XML-Datei:
• Namensraum in der für Factur-X/ZUGFeRD relevanten XML-Elemente in
den XMP-Metadaten im PDF
• Name des Dateianhangs der Rechnungs-XML im PDF (»factur-x.xml«)
• Standard-Identifier in der Rechnungs-XML