kivitendo/SL/XMLInvoice.pm @ 7b1da9c3
335b5ab6 | Johannes Grassler | package SL::XMLInvoice;
|
||
1522aeb7 | Johannes Grassler | use strict;
|
||
use warnings;
|
||||
11624cb8 | Sven Schöling | use List::Util qw(first);
|
||
1522aeb7 | Johannes Grassler | use XML::LibXML;
|
||
11624cb8 | Sven Schöling | use SL::Locale::String qw(t8);
|
||
use SL::XMLInvoice::UBL;
|
||||
use SL::XMLInvoice::CrossIndustryInvoice;
|
||||
use SL::XMLInvoice::CrossIndustryDocument;
|
||||
350c829a | Sven Schöling | |||
1522aeb7 | Johannes Grassler | use constant RES_OK => 0;
|
||
use constant RES_XML_PARSING_FAILED => 1;
|
||||
use constant RES_UNKNOWN_ROOT_NODE_TYPE => 2;
|
||||
11624cb8 | Sven Schöling | our @document_modules = qw(
|
||
SL::XMLInvoice::CrossIndustryDocument
|
||||
SL::XMLInvoice::CrossIndustryInvoice
|
||||
SL::XMLInvoice::UBL
|
||||
);
|
||||
335b5ab6 | Johannes Grassler | =head1 NAME
|
||
SL::XMLInvoice - Top level factory class for XML Invoice parsers.
|
||||
=head1 DESCRIPTION
|
||||
C<SL::XMLInvoice> is an abstraction layer allowing the application to pass any
|
||||
supported XML invoice document for parsing, with C<SL::XMLInvoice> handling all
|
||||
details from there: depending on its document type declaration, this class will
|
||||
pick and instatiate the appropriate child class for parsing the document and
|
||||
return an object exposing its data with the standardized structure outlined
|
||||
below.
|
||||
11624cb8 | Sven Schöling | See L <SL::XMLInvoice::Base>
|
||
for details on the shared interface of the returned instances.
|
||||
335b5ab6 | Johannes Grassler | =head1 SYNOPSIS
|
||
# $xml_data contains an XML document as flat scalar
|
||||
my $invoice_parser = SL::XMLInvoice->new($xml_data);
|
||||
# %metadata is a hash of document level metadata items
|
||||
my %metadata = %{$invoice_parser->metadata};
|
||||
# @items is an array of hashes, each representing a line
|
||||
# item on the bill
|
||||
my @items = @{$invoice_parser->items};
|
||||
ba845b8d | Tamino Steinert | |||
335b5ab6 | Johannes Grassler | =cut
|
||
1522aeb7 | Johannes Grassler | sub new {
|
||
11624cb8 | Sven Schöling | my ($class, $xml_data) = @_;
|
||
my $self = {};
|
||||
335b5ab6 | Johannes Grassler | |||
$self->{message} = '';
|
||||
$self->{dom} = eval { XML::LibXML->load_xml(string => $xml_data) };
|
||||
if ( ! $self->{dom} ) {
|
||||
51c76e20 | Johannes Grassler | $self->{message} = t8("Parsing the XML data failed: #1", $xml_data);
|
||
335b5ab6 | Johannes Grassler | $self->{result} = RES_XML_PARSING_FAILED;
|
||
return $self;
|
||||
1522aeb7 | Johannes Grassler | }
|
||
335b5ab6 | Johannes Grassler | |||
# Determine parser class to use
|
||||
11624cb8 | Sven Schöling | my $type = first {
|
||
$_->check_signature($self->{dom})
|
||||
} @document_modules;
|
||||
335b5ab6 | Johannes Grassler | |||
unless ( $type ) {
|
||||
$self->{result} = RES_UNKNOWN_ROOT_NODE_TYPE;
|
||||
652aebcf | Johannes Grassler | |||
11624cb8 | Sven Schöling | my @supported = map { $_->supported } @document_modules;
|
||
652aebcf | Johannes Grassler | |||
$self->{message} = t8("Could not parse XML Invoice: unknown XML invoice type\nsupported: #1",
|
||||
11624cb8 | Sven Schöling | join ",\n", @supported
|
||
51c76e20 | Johannes Grassler | );
|
||
335b5ab6 | Johannes Grassler | return $self;
|
||
1522aeb7 | Johannes Grassler | }
|
||
335b5ab6 | Johannes Grassler | |||
bless $self, $type;
|
||||
# Implementation sanity check for child classes: make sure they are aware of
|
||||
# the keys the hash returned by their metadata() method must contain.
|
||||
51c76e20 | Johannes Grassler | my @missing_data_keys = grep { !${$self->_data_keys}{$_} } @{ $self->data_keys };
|
||
335b5ab6 | Johannes Grassler | if ( scalar(@missing_data_keys) > 0 ) {
|
||
die "Incomplete implementation: the following metadata keys appear to be missing from $type: " . join(", ", @missing_data_keys);
|
||||
}
|
||||
# Implementation sanity check for child classes: make sure they are aware of
|
||||
# the keys the hashes returned by their items() method must contain.
|
||||
my @missing_item_keys = ();
|
||||
1522aeb7 | Johannes Grassler | foreach my $item_key ( @{$self->item_keys} ) {
|
||
335b5ab6 | Johannes Grassler | unless ( ${$self->_item_keys}{$item_key}) { push @missing_item_keys, $item_key; }
|
||
1522aeb7 | Johannes Grassler | }
|
||
335b5ab6 | Johannes Grassler | if ( scalar(@missing_item_keys) > 0 ) {
|
||
die "Incomplete implementation: the following item keys appear to be missing from $type: " . join(", ", @missing_item_keys);
|
||||
}
|
||||
$self->parse_xml;
|
||||
# Ensure these methods are implemented in the child class
|
||||
$self->metadata;
|
||||
$self->items;
|
||||
$self->{result} = RES_OK;
|
||||
return $self;
|
||||
1522aeb7 | Johannes Grassler | }
|
||
335b5ab6 | Johannes Grassler | |||
1;
|