]> wagnertech.de Git - mfinanz.git/blob - SL/XMLInvoice.pm
date error in mapping
[mfinanz.git] / SL / XMLInvoice.pm
1 package SL::XMLInvoice;
2
3 use strict;
4 use warnings;
5
6 use List::Util qw(first);
7 use XML::LibXML;
8
9 use SL::Locale::String qw(t8);
10 use SL::XMLInvoice::UBL;
11 use SL::XMLInvoice::CrossIndustryInvoice;
12 use SL::XMLInvoice::CrossIndustryDocument;
13
14 use constant RES_OK => 0;
15 use constant RES_XML_PARSING_FAILED => 1;
16 use constant RES_UNKNOWN_ROOT_NODE_TYPE => 2;
17
18 our @document_modules = qw(
19   SL::XMLInvoice::CrossIndustryDocument
20   SL::XMLInvoice::CrossIndustryInvoice
21   SL::XMLInvoice::UBL
22 );
23
24 =head1 NAME
25
26 SL::XMLInvoice - Top level factory class for XML Invoice parsers.
27
28 =head1 DESCRIPTION
29
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
35 below.
36
37 See L <SL::XMLInvoice::Base>
38 for details on the shared interface of the returned instances.
39
40 =head1 SYNOPSIS
41
42   # $xml_data contains an XML document as flat scalar
43   my $invoice_parser = SL::XMLInvoice->new($xml_data);
44
45   # %metadata is a hash of document level metadata items
46   my %metadata = %{$invoice_parser->metadata};
47
48   # @items is an array of hashes, each representing a line
49   # item on the bill
50   my @items = @{$invoice_parser->items};
51
52 =cut
53
54 sub new {
55   my ($class, $xml_data) = @_;
56   my $self = {};
57
58   $self->{message} = '';
59   $self->{dom} = eval { XML::LibXML->load_xml(string => $xml_data, expand_entities => 0) };
60
61   if ( ! $self->{dom} ) {
62     $self->{message} = t8("Parsing the XML data failed: #1", $xml_data);
63     $self->{result} = RES_XML_PARSING_FAILED;
64     return $self;
65   }
66
67   # Determine parser class to use
68   my $type = first {
69     $_->check_signature($self->{dom})
70   } @document_modules;
71
72   unless ( $type ) {
73     $self->{result} = RES_UNKNOWN_ROOT_NODE_TYPE;
74
75     my @supported = map { $_->supported } @document_modules;
76
77     $self->{message} =  t8("Could not parse XML Invoice: unknown XML invoice type\nsupported: #1",
78                            join ",\n", @supported
79                         );
80     return $self;
81   }
82
83   bless $self, $type;
84
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);
90   }
91
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; }
97   }
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);
100   }
101
102   $self->parse_xml;
103
104   # Ensure these methods are implemented in the child class
105   $self->metadata;
106   $self->items;
107
108   $self->{result} = RES_OK;
109   return $self;
110 }
111
112 1;
113