Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision f6a17863

Von Sven Schöling vor etwa 1 Jahr hinzugefügt

  • ID f6a1786378b2660e30d7548ca1bac58b44db989a
  • Vorgänger 0aab6453
  • Nachfolger 3fb912cf

SL::XMLInvoice: factory pattern etwas umgeschrieben

- braucht jetzt kein Module::Load oder runtime require mehr, weil die
Factory nicht mehr gleichzeitig die Basisklasse der Implementoren ist
- new ist jetzt nur noch in SL::XMLInvoice
- die Basisklasse für die Implementoren ist jetzt SL::XMLInvoice::Base
- _document_modules ist jetzt ein class member der factory statt eine
Methode, so muss das Array nicht ständig neu gebaut werden.
- Im Fehlerfall wird jetzt garnicht blessed, sondern einfach das Fehler
hash zurückgegeben.
- module resolution auf first umgeschrieben.

Unterschiede anzeigen:

SL/XMLInvoice.pm
3 3
use strict;
4 4
use warnings;
5 5

  
6
use Module::Load;
7

  
8
use SL::Locale::String qw(t8);
6
use List::Util qw(first);
9 7
use XML::LibXML;
10
use SL::Locale::String qw(t8);
11 8

  
12
require SL::XMLInvoice::UBL;
13
require SL::XMLInvoice::CrossIndustryInvoice;
9
use SL::Locale::String qw(t8);
10
use SL::XMLInvoice::UBL;
11
use SL::XMLInvoice::CrossIndustryInvoice;
12
use SL::XMLInvoice::CrossIndustryDocument;
14 13

  
15 14
use constant RES_OK => 0;
16 15
use constant RES_XML_PARSING_FAILED => 1;
17 16
use constant RES_UNKNOWN_ROOT_NODE_TYPE => 2;
18 17

  
18
our @document_modules = qw(
19
  SL::XMLInvoice::CrossIndustryDocument
20
  SL::XMLInvoice::CrossIndustryInvoice
21
  SL::XMLInvoice::UBL
22
);
23

  
19 24
=head1 NAME
20 25

  
21 26
SL::XMLInvoice - Top level factory class for XML Invoice parsers.
......
29 34
return an object exposing its data with the standardized structure outlined
30 35
below.
31 36

  
37
See L <SL::XMLInvoice::Base>
38
for details on the shared interface of the returned instances.
39

  
32 40
=head1 SYNOPSIS
33 41

  
34 42
  # $xml_data contains an XML document as flat scalar
......
43 51

  
44 52
=cut
45 53

  
46
=head1 ATTRIBUTES
47

  
48
=over 4
49

  
50
=item dom
51

  
52
A XML::LibXML document object model (DOM) object created from the XML data supplied.
53

  
54
=item message
55

  
56
Will contain a detailed error message if the C<result> attribute is anything
57
other than C<SL::XMLInvoice::RES_OK>.
58

  
59
=item result
60

  
61
A status field indicating whether the supplied XML data could be parsed. It
62
can take the following values:
63

  
64
=item SL::XMLInvoice::RES_OK
65

  
66
File has been parsed successfully.
67

  
68
=item SL::XMLInvoice::RES_XML_PARSING FAILED
69

  
70
Parsing the file failed.
71

  
72
=item SL::XMLInvoice::RES_UNKNOWN_ROOT_NODE_TYPE
73

  
74
The root node is of an unknown type. Currently, C<rsm:CrossIndustryInvoice> and
75
C<ubl:Invoice> are supported.
76

  
77
=back
78

  
79
=cut
80

  
81
=head1 METHODS
82

  
83
=head2 Data structure definition methods (only in C<SL::XMLInvoice>)
84

  
85
These methods are only implemented in C<SL::XMLInvoice> itself and define the
86
data structures to be exposed by any child classes.
87

  
88
=over 4
89

  
90
=item data_keys()
91

  
92
Returns all keys the hash returned by any child's C<metadata()> method must
93
contain. If you add keys to this list, you need to add them to all classes
94
inheriting from C<SL::XMLInvoice> as well. An application may use this method
95
to discover the metadata keys guaranteed to be present.
96

  
97
=cut
98

  
99
sub data_keys {
100
  my @keys = (
101
    'currency',      # The bill's currency, such as "EUR"
102
    'direct_debit',  # Boolean: whether the bill will get paid by direct debit (1) or not (0)
103
    'duedate',       # The bill's due date in YYYY-MM-DD format.
104
    'gross_total',   # The invoice's sum total with tax included
105
    'iban',          # The creditor's IBAN
106
    'invnumber',     # The invoice's number
107
    'net_total',     # The invoice's sum total without tax
108
    'taxnumber',     # The creditor's tax number (Steuernummer in Germany). May be present if
109
                     # there is no VAT ID (USTiD in Germany).
110
    'transdate',     # The date the invoice was issued in YYYY-MM-DD format.
111
    'type',          # Numeric invoice type code, e.g. 380
112
    'ustid',         # The creditor's UStID.
113
    'vendor_name',   # The vendor's company name
114
  );
115
  return \@keys;
116
}
117

  
118
=item item_keys()
119

  
120
Returns all keys the item hashes returned by any child's C<items()> method must
121
contain. If you add keys to this list, you need to add them to all classes
122
inheriting from C<SL::XMLInvoice> as well. An application may use this method
123
to discover the metadata keys guaranteed to be present.
124

  
125
=back
126

  
127
=cut
128

  
129
sub item_keys  {
130
  my @keys = (
131
    'currency',
132
    'description',
133
    'price',
134
    'quantity',
135
    'subtotal',
136
    'tax_rate',
137
    'tax_scheme',
138
    'vendor_partno',
139
  );
140
  return \@keys;
141
}
142

  
143
=head2 User/application facing methods
144

  
145
Any class inheriting from C<SL::XMLInvoice> must implement the following
146
methods. To ensure this happens, C<SL::XMLInvoice> contains stub functions that
147
raise an exception if a child class does not override them.
148

  
149
=over 4
150

  
151
=item new($xml_data)
152

  
153
Constructor for C<SL::XMLInvoice>. This method takes a scalar containing the
154
entire XML document to be parsed as a flat string as its sole argument. It will
155
instantiate the appropriate child class to parse the XML document in question,
156
call its C<parse_xml> method and return the C<SL::XMLInvoice> child object it
157
instantiated. From that point on, the structured data retrieved from the XML
158
document will be available through the object's C<metadata> and C<items()>
159
methods.
160

  
161
=item metadata()
162

  
163
This method returns a hash of document level metadata, such as the invoice
164
number, the total, or the the issuance date. Its keys are the keys returned by
165
the C<(data_keys()> method. Its values are plain scalars containing strings or
166
C<undef> for any data items not present or empty in the XML document.
167

  
168
=cut
169

  
170
sub metadata {
171
  my $self = shift;
172
  die "Children of $self must implement a metadata() method returning the bill's metadata as a hash.";
173
}
174

  
175
=item check_signature($dom)
176

  
177
This static method takes a DOM object and returns 1 if this DOM object can be
178
parsed by the child class in question, 0 otherwise. C<SL::XMLInvoice> uses this
179
method to determine which child class to instantiate for a given document. All
180
child classes must implement this method.
181

  
182
=cut
183

  
184
sub check_signature {
185
  my $self = shift;
186
  die "Children of $self must implement a check_signature() method returning 1 for supported XML, 0 for unsupported XML.";
187
}
188

  
189
=item supported()
190

  
191
This static method returns an array of free-form strings describing XML invoice
192
types parseable by the child class. C<SL::XMLInvoice> uses this method to
193
output a list of supported XML invoice types if its constructor fails to find
194
to find an appropriate child class to parse the given document with. All child
195
classes must implement this method.
196

  
197
=cut
198

  
199
sub supported {
200
  my $self = shift;
201
  die "Children of $self must implement a supported() method returning a list of supported XML invoice types.";
202
}
203

  
204

  
205
=item items()
206

  
207
This method returns an array of hashes containing line item metadata, such as
208
the quantity, price for one unit, or subtotal. These hashes' keys are the keys
209
returned by the C<(item_keys()> method. Its values are plain scalars containing
210
strings or C<undef> for any data items not present or empty in the XML
211
document.
212

  
213
=cut
214

  
215
sub items {
216
  my $self = shift;
217
  die "Children of $self must implement a item() method returning the bill's items as a hash.";
218
}
219

  
220

  
221
=item parse_xml()
222

  
223
This method is only implemented in child classes of C<SL::XMLInvoice> and is
224
called by the C<SL::XMLInvoice> constructor once the appropriate child class has been
225
determined and instantiated. It uses C<$self->{dom}>, an C<XML::LibXML>
226
instance to iterate through the XML document to parse. That XML document is
227
created by the C<SL::XMLInvoice> constructor.
228

  
229
=back
230

  
231
=cut
232

  
233
sub parse_xml {
234
  my $self = shift;
235
  die "Children of $self must implement a parse_xml() method.";
236
}
237

  
238
=head2 Internal methods
239

  
240
These methods' purpose is child classs selection and making sure child classes
241
implent the interface promised by C<SL::XMLInvoice>. You can safely ignore them
242
if you don't plan on implementing any child classes.
243

  
244
=over 4
245

  
246
=item _document_modules()
247

  
248
This method is implemented in C<SL::XMLInvoice> only and returns a list of
249
child classes, each implementing an XML invoice parser. If you add any child
250
classes for new XML document types you need to add them to this list to make it
251
available from C<SL::XMLInvoice>.
252

  
253
=cut
254

  
255
sub _document_modules {
256
  return (
257
    'SL::XMLInvoice::CrossIndustryDocument',
258
    'SL::XMLInvoice::CrossIndustryInvoice',
259
    'SL::XMLInvoice::UBL',
260
  );
261
}
262

  
263
=item _data_keys()
264

  
265
Returns a list of all keys present in the hash returned by the class'
266
C<metadata()> method. Must be implemented in all classes inheriting from
267
C<SL::XMLInvoice> This list must contain the same keys as the list returned by
268
C<data_keys>. Omitting this method from a child class will cause an exception.
269

  
270
=cut
271

  
272
sub _data_keys {
273
  my $self = shift;
274
  die "Children of $self must implement a _data_keys() method returning the keys an invoice item hash will contain.";
275
}
276

  
277
=item _item_keys()
278

  
279
Returns a list of all keys present in the hashes returned by the class'
280
C<items()> method. Must be implemented in all classes inheriting from
281
C<SL::XMLInvoice> This list must contain the same keys as the list returned by
282
C<item_keys>. Omitting this method from a child class will cause an exception.
283

  
284
=back
285

  
286
=head1 AUTHOR
287

  
288
  Johannes Grassler <info@computer-grassler.de>
289

  
290
=cut
291

  
292
sub _item_keys {
293
  my $self = shift;
294
  die "Children of $self must implement a _item_keys() method returning the keys an invoice item hash will contain.";
295
}
296

  
297

  
298 54
sub new {
299
  my ($self, $xml_data) = @_;
300
  my $type = undef;
301

  
302
  $self = {};
303

  
304
  bless $self;
55
  my ($class, $xml_data) = @_;
56
  my $self = {};
305 57

  
306 58
  $self->{message} = '';
307 59
  $self->{dom} = eval { XML::LibXML->load_xml(string => $xml_data) };
......
313 65
  }
314 66

  
315 67
  # Determine parser class to use
316
  foreach my $module ( $self->_document_modules )
317
    {
318
    load $module;
319
    if ( $module->check_signature($self->{dom}) ) {
320
      $type = $module;
321
      last;
322
      }
323
    }
68
  my $type = first {
69
    $_->check_signature($self->{dom})
70
  } @document_modules;
324 71

  
325 72
  unless ( $type ) {
326 73
    $self->{result} = RES_UNKNOWN_ROOT_NODE_TYPE;
327
    my @supported = ();
328 74

  
329
    foreach my $module ( $self->_document_modules ) {
330
      my @module_list = $module->supported();
331
      push @supported, @module_list;
332
    }
75
    my @supported = map { $_->supported } @document_modules;
333 76

  
334
    my $supported_types = join(",\n", @supported);
335 77
    $self->{message} =  t8("Could not parse XML Invoice: unknown XML invoice type\nsupported: #1",
336
                           $supported_types,
78
                           join ",\n", @supported
337 79
                        );
338 80
    return $self;
339 81
  }

Auch abrufbar als: Unified diff