1 package SL::XMLInvoice;
6 use List::Util qw(first);
9 use SL::Locale::String qw(t8);
10 use SL::XMLInvoice::UBL;
11 use SL::XMLInvoice::CrossIndustryInvoice;
12 use SL::XMLInvoice::CrossIndustryDocument;
14 use constant RES_OK => 0;
15 use constant RES_XML_PARSING_FAILED => 1;
16 use constant RES_UNKNOWN_ROOT_NODE_TYPE => 2;
18 our @document_modules = qw(
19 SL::XMLInvoice::CrossIndustryDocument
20 SL::XMLInvoice::CrossIndustryInvoice
26 SL::XMLInvoice - Top level factory class for XML Invoice parsers.
30 C<SL::XMLInvoice> is an abstraction layer allowing the application to pass any
31 supported XML invoice document for parsing, with C<SL::XMLInvoice> handling all
32 details from there: depending on its document type declaration, this class will
33 pick and instatiate the appropriate child class for parsing the document and
34 return an object exposing its data with the standardized structure outlined
37 See L <SL::XMLInvoice::Base>
38 for details on the shared interface of the returned instances.
42 # $xml_data contains an XML document as flat scalar
43 my $invoice_parser = SL::XMLInvoice->new($xml_data);
45 # %metadata is a hash of document level metadata items
46 my %metadata = %{$invoice_parser->metadata};
48 # @items is an array of hashes, each representing a line
50 my @items = @{$invoice_parser->items};
55 my ($class, $xml_data) = @_;
58 $self->{message} = '';
59 $self->{dom} = eval { XML::LibXML->load_xml(string => $xml_data, expand_entities => 0) };
61 if ( ! $self->{dom} ) {
62 $self->{message} = t8("Parsing the XML data failed: #1", $xml_data);
63 $self->{result} = RES_XML_PARSING_FAILED;
67 # Determine parser class to use
69 $_->check_signature($self->{dom})
73 $self->{result} = RES_UNKNOWN_ROOT_NODE_TYPE;
75 my @supported = map { $_->supported } @document_modules;
77 $self->{message} = t8("Could not parse XML Invoice: unknown XML invoice type\nsupported: #1",
78 join ",\n", @supported
85 # Implementation sanity check for child classes: make sure they are aware of
86 # the keys the hash returned by their metadata() method must contain.
87 my @missing_data_keys = grep { !${$self->_data_keys}{$_} } @{ $self->data_keys };
88 if ( scalar(@missing_data_keys) > 0 ) {
89 die "Incomplete implementation: the following metadata keys appear to be missing from $type: " . join(", ", @missing_data_keys);
92 # Implementation sanity check for child classes: make sure they are aware of
93 # the keys the hashes returned by their items() method must contain.
94 my @missing_item_keys = ();
95 foreach my $item_key ( @{$self->item_keys} ) {
96 unless ( ${$self->_item_keys}{$item_key}) { push @missing_item_keys, $item_key; }
98 if ( scalar(@missing_item_keys) > 0 ) {
99 die "Incomplete implementation: the following item keys appear to be missing from $type: " . join(", ", @missing_item_keys);
104 # Ensure these methods are implemented in the child class
108 $self->{result} = RES_OK;