Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision f6a17863

Von Sven Schöling vor mehr als 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
use strict;
use warnings;
use Module::Load;
use SL::Locale::String qw(t8);
use List::Util qw(first);
use XML::LibXML;
use SL::Locale::String qw(t8);
require SL::XMLInvoice::UBL;
require SL::XMLInvoice::CrossIndustryInvoice;
use SL::Locale::String qw(t8);
use SL::XMLInvoice::UBL;
use SL::XMLInvoice::CrossIndustryInvoice;
use SL::XMLInvoice::CrossIndustryDocument;
use constant RES_OK => 0;
use constant RES_XML_PARSING_FAILED => 1;
use constant RES_UNKNOWN_ROOT_NODE_TYPE => 2;
our @document_modules = qw(
SL::XMLInvoice::CrossIndustryDocument
SL::XMLInvoice::CrossIndustryInvoice
SL::XMLInvoice::UBL
);
=head1 NAME
SL::XMLInvoice - Top level factory class for XML Invoice parsers.
......
return an object exposing its data with the standardized structure outlined
below.
See L <SL::XMLInvoice::Base>
for details on the shared interface of the returned instances.
=head1 SYNOPSIS
# $xml_data contains an XML document as flat scalar
......
=cut
=head1 ATTRIBUTES
=over 4
=item dom
A XML::LibXML document object model (DOM) object created from the XML data supplied.
=item message
Will contain a detailed error message if the C<result> attribute is anything
other than C<SL::XMLInvoice::RES_OK>.
=item result
A status field indicating whether the supplied XML data could be parsed. It
can take the following values:
=item SL::XMLInvoice::RES_OK
File has been parsed successfully.
=item SL::XMLInvoice::RES_XML_PARSING FAILED
Parsing the file failed.
=item SL::XMLInvoice::RES_UNKNOWN_ROOT_NODE_TYPE
The root node is of an unknown type. Currently, C<rsm:CrossIndustryInvoice> and
C<ubl:Invoice> are supported.
=back
=cut
=head1 METHODS
=head2 Data structure definition methods (only in C<SL::XMLInvoice>)
These methods are only implemented in C<SL::XMLInvoice> itself and define the
data structures to be exposed by any child classes.
=over 4
=item data_keys()
Returns all keys the hash returned by any child's C<metadata()> method must
contain. If you add keys to this list, you need to add them to all classes
inheriting from C<SL::XMLInvoice> as well. An application may use this method
to discover the metadata keys guaranteed to be present.
=cut
sub data_keys {
my @keys = (
'currency', # The bill's currency, such as "EUR"
'direct_debit', # Boolean: whether the bill will get paid by direct debit (1) or not (0)
'duedate', # The bill's due date in YYYY-MM-DD format.
'gross_total', # The invoice's sum total with tax included
'iban', # The creditor's IBAN
'invnumber', # The invoice's number
'net_total', # The invoice's sum total without tax
'taxnumber', # The creditor's tax number (Steuernummer in Germany). May be present if
# there is no VAT ID (USTiD in Germany).
'transdate', # The date the invoice was issued in YYYY-MM-DD format.
'type', # Numeric invoice type code, e.g. 380
'ustid', # The creditor's UStID.
'vendor_name', # The vendor's company name
);
return \@keys;
}
=item item_keys()
Returns all keys the item hashes returned by any child's C<items()> method must
contain. If you add keys to this list, you need to add them to all classes
inheriting from C<SL::XMLInvoice> as well. An application may use this method
to discover the metadata keys guaranteed to be present.
=back
=cut
sub item_keys {
my @keys = (
'currency',
'description',
'price',
'quantity',
'subtotal',
'tax_rate',
'tax_scheme',
'vendor_partno',
);
return \@keys;
}
=head2 User/application facing methods
Any class inheriting from C<SL::XMLInvoice> must implement the following
methods. To ensure this happens, C<SL::XMLInvoice> contains stub functions that
raise an exception if a child class does not override them.
=over 4
=item new($xml_data)
Constructor for C<SL::XMLInvoice>. This method takes a scalar containing the
entire XML document to be parsed as a flat string as its sole argument. It will
instantiate the appropriate child class to parse the XML document in question,
call its C<parse_xml> method and return the C<SL::XMLInvoice> child object it
instantiated. From that point on, the structured data retrieved from the XML
document will be available through the object's C<metadata> and C<items()>
methods.
=item metadata()
This method returns a hash of document level metadata, such as the invoice
number, the total, or the the issuance date. Its keys are the keys returned by
the C<(data_keys()> method. Its values are plain scalars containing strings or
C<undef> for any data items not present or empty in the XML document.
=cut
sub metadata {
my $self = shift;
die "Children of $self must implement a metadata() method returning the bill's metadata as a hash.";
}
=item check_signature($dom)
This static method takes a DOM object and returns 1 if this DOM object can be
parsed by the child class in question, 0 otherwise. C<SL::XMLInvoice> uses this
method to determine which child class to instantiate for a given document. All
child classes must implement this method.
=cut
sub check_signature {
my $self = shift;
die "Children of $self must implement a check_signature() method returning 1 for supported XML, 0 for unsupported XML.";
}
=item supported()
This static method returns an array of free-form strings describing XML invoice
types parseable by the child class. C<SL::XMLInvoice> uses this method to
output a list of supported XML invoice types if its constructor fails to find
to find an appropriate child class to parse the given document with. All child
classes must implement this method.
=cut
sub supported {
my $self = shift;
die "Children of $self must implement a supported() method returning a list of supported XML invoice types.";
}
=item items()
This method returns an array of hashes containing line item metadata, such as
the quantity, price for one unit, or subtotal. These hashes' keys are the keys
returned by the C<(item_keys()> method. Its values are plain scalars containing
strings or C<undef> for any data items not present or empty in the XML
document.
=cut
sub items {
my $self = shift;
die "Children of $self must implement a item() method returning the bill's items as a hash.";
}
=item parse_xml()
This method is only implemented in child classes of C<SL::XMLInvoice> and is
called by the C<SL::XMLInvoice> constructor once the appropriate child class has been
determined and instantiated. It uses C<$self->{dom}>, an C<XML::LibXML>
instance to iterate through the XML document to parse. That XML document is
created by the C<SL::XMLInvoice> constructor.
=back
=cut
sub parse_xml {
my $self = shift;
die "Children of $self must implement a parse_xml() method.";
}
=head2 Internal methods
These methods' purpose is child classs selection and making sure child classes
implent the interface promised by C<SL::XMLInvoice>. You can safely ignore them
if you don't plan on implementing any child classes.
=over 4
=item _document_modules()
This method is implemented in C<SL::XMLInvoice> only and returns a list of
child classes, each implementing an XML invoice parser. If you add any child
classes for new XML document types you need to add them to this list to make it
available from C<SL::XMLInvoice>.
=cut
sub _document_modules {
return (
'SL::XMLInvoice::CrossIndustryDocument',
'SL::XMLInvoice::CrossIndustryInvoice',
'SL::XMLInvoice::UBL',
);
}
=item _data_keys()
Returns a list of all keys present in the hash returned by the class'
C<metadata()> method. Must be implemented in all classes inheriting from
C<SL::XMLInvoice> This list must contain the same keys as the list returned by
C<data_keys>. Omitting this method from a child class will cause an exception.
=cut
sub _data_keys {
my $self = shift;
die "Children of $self must implement a _data_keys() method returning the keys an invoice item hash will contain.";
}
=item _item_keys()
Returns a list of all keys present in the hashes returned by the class'
C<items()> method. Must be implemented in all classes inheriting from
C<SL::XMLInvoice> This list must contain the same keys as the list returned by
C<item_keys>. Omitting this method from a child class will cause an exception.
=back
=head1 AUTHOR
Johannes Grassler <info@computer-grassler.de>
=cut
sub _item_keys {
my $self = shift;
die "Children of $self must implement a _item_keys() method returning the keys an invoice item hash will contain.";
}
sub new {
my ($self, $xml_data) = @_;
my $type = undef;
$self = {};
bless $self;
my ($class, $xml_data) = @_;
my $self = {};
$self->{message} = '';
$self->{dom} = eval { XML::LibXML->load_xml(string => $xml_data) };
......
}
# Determine parser class to use
foreach my $module ( $self->_document_modules )
{
load $module;
if ( $module->check_signature($self->{dom}) ) {
$type = $module;
last;
}
}
my $type = first {
$_->check_signature($self->{dom})
} @document_modules;
unless ( $type ) {
$self->{result} = RES_UNKNOWN_ROOT_NODE_TYPE;
my @supported = ();
foreach my $module ( $self->_document_modules ) {
my @module_list = $module->supported();
push @supported, @module_list;
}
my @supported = map { $_->supported } @document_modules;
my $supported_types = join(",\n", @supported);
$self->{message} = t8("Could not parse XML Invoice: unknown XML invoice type\nsupported: #1",
$supported_types,
join ",\n", @supported
);
return $self;
}
SL/XMLInvoice/Base.pm
package SL::XMLInvoice::Base;
use strict;
use warnings;
=head1 ATTRIBUTES
=over 4
=item dom
A XML::LibXML document object model (DOM) object created from the XML data supplied.
=item message
Will contain a detailed error message if the C<result> attribute is anything
other than C<SL::XMLInvoice::RES_OK>.
=item result
A status field indicating whether the supplied XML data could be parsed. It
can take the following values:
=item SL::XMLInvoice::RES_OK
File has been parsed successfully.
=item SL::XMLInvoice::RES_XML_PARSING FAILED
Parsing the file failed.
=item SL::XMLInvoice::RES_UNKNOWN_ROOT_NODE_TYPE
The root node is of an unknown type. Currently, C<rsm:CrossIndustryInvoice> and
C<ubl:Invoice> are supported.
=back
=head1 METHODS
=head2 Data structure definition methods (only in C<SL::XMLInvoice>)
These methods are only implemented in C<SL::XMLInvoice> itself and define the
data structures to be exposed by any child classes.
=over 4
=item data_keys()
Returns all keys the hash returned by any child's C<metadata()> method must
contain. If you add keys to this list, you need to add them to all classes
inheriting from C<SL::XMLInvoice> as well. An application may use this method
to discover the metadata keys guaranteed to be present.
=cut
sub data_keys {
my @keys = (
'currency', # The bill's currency, such as "EUR"
'direct_debit', # Boolean: whether the bill will get paid by direct debit (1) or not (0)
'duedate', # The bill's due date in YYYY-MM-DD format.
'gross_total', # The invoice's sum total with tax included
'iban', # The creditor's IBAN
'invnumber', # The invoice's number
'net_total', # The invoice's sum total without tax
'taxnumber', # The creditor's tax number (Steuernummer in Germany). May be present if
# there is no VAT ID (USTiD in Germany).
'transdate', # The date the invoice was issued in YYYY-MM-DD format.
'type', # Numeric invoice type code, e.g. 380
'ustid', # The creditor's UStID.
'vendor_name', # The vendor's company name
);
return \@keys;
}
=item item_keys()
Returns all keys the item hashes returned by any child's C<items()> method must
contain. If you add keys to this list, you need to add them to all classes
inheriting from C<SL::XMLInvoice> as well. An application may use this method
to discover the metadata keys guaranteed to be present.
=back
=cut
sub item_keys {
my @keys = (
'currency',
'description',
'price',
'quantity',
'subtotal',
'tax_rate',
'tax_scheme',
'vendor_partno',
);
return \@keys;
}
=head2 User/application facing methods
Any class inheriting from C<SL::XMLInvoice> must implement the following
methods. To ensure this happens, C<SL::XMLInvoice> contains stub functions that
raise an exception if a child class does not override them.
=over 4
=item new($xml_data)
Constructor for C<SL::XMLInvoice>. This method takes a scalar containing the
entire XML document to be parsed as a flat string as its sole argument. It will
instantiate the appropriate child class to parse the XML document in question,
call its C<parse_xml> method and return the C<SL::XMLInvoice> child object it
instantiated. From that point on, the structured data retrieved from the XML
document will be available through the object's C<metadata> and C<items()>
methods.
=item metadata()
This method returns a hash of document level metadata, such as the invoice
number, the total, or the the issuance date. Its keys are the keys returned by
the C<(data_keys()> method. Its values are plain scalars containing strings or
C<undef> for any data items not present or empty in the XML document.
=cut
sub metadata {
my $self = shift;
die "Children of $self must implement a metadata() method returning the bill's metadata as a hash.";
}
=item check_signature($dom)
This static method takes a DOM object and returns 1 if this DOM object can be
parsed by the child class in question, 0 otherwise. C<SL::XMLInvoice> uses this
method to determine which child class to instantiate for a given document. All
child classes must implement this method.
=cut
sub check_signature {
my $self = shift;
die "Children of $self must implement a check_signature() method returning 1 for supported XML, 0 for unsupported XML.";
}
=item supported()
This static method returns an array of free-form strings describing XML invoice
types parseable by the child class. C<SL::XMLInvoice> uses this method to
output a list of supported XML invoice types if its constructor fails to find
to find an appropriate child class to parse the given document with. All child
classes must implement this method.
=cut
sub supported {
my $self = shift;
die "Children of $self must implement a supported() method returning a list of supported XML invoice types.";
}
=item items()
This method returns an array of hashes containing line item metadata, such as
the quantity, price for one unit, or subtotal. These hashes' keys are the keys
returned by the C<(item_keys()> method. Its values are plain scalars containing
strings or C<undef> for any data items not present or empty in the XML
document.
=cut
sub items {
my $self = shift;
die "Children of $self must implement a item() method returning the bill's items as a hash.";
}
=item parse_xml()
This method is only implemented in child classes of C<SL::XMLInvoice> and is
called by the C<SL::XMLInvoice> constructor once the appropriate child class has been
determined and instantiated. It uses C<$self->{dom}>, an C<XML::LibXML>
instance to iterate through the XML document to parse. That XML document is
created by the C<SL::XMLInvoice> constructor.
=back
=cut
sub parse_xml {
my $self = shift;
die "Children of $self must implement a parse_xml() method.";
}
=head2 Internal methods
These methods' purpose is child classs selection and making sure child classes
implent the interface promised by C<SL::XMLInvoice::Base>. You can safely ignore them
if you don't plan on implementing any child classes.
=over 4
=item _data_keys()
Returns a list of all keys present in the hash returned by the class'
C<metadata()> method. Must be implemented in all classes inheriting from
C<SL::XMLInvoice> This list must contain the same keys as the list returned by
C<data_keys>. Omitting this method from a child class will cause an exception.
=cut
sub _data_keys {
my $self = shift;
die "Children of $self must implement a _data_keys() method returning the keys an invoice item hash will contain.";
}
=item _item_keys()
Returns a list of all keys present in the hashes returned by the class'
C<items()> method. Must be implemented in all classes inheriting from
C<SL::XMLInvoice> This list must contain the same keys as the list returned by
C<item_keys>. Omitting this method from a child class will cause an exception.
=back
=head1 AUTHOR
Johannes Grassler <info@computer-grassler.de>
=cut
sub _item_keys {
my $self = shift;
die "Children of $self must implement a _item_keys() method returning the keys an invoice item hash will contain.";
}
1;
SL/XMLInvoice/CrossIndustryDocument.pm
use strict;
use warnings;
use parent qw(SL::XMLInvoice);
use parent qw(SL::XMLInvoice::Base);
use constant ITEMS_XPATH => '//ram:IncludedSupplyChainTradeLineItem';
SL/XMLInvoice/CrossIndustryInvoice.pm
use strict;
use warnings;
use parent qw(SL::XMLInvoice);
use parent qw(SL::XMLInvoice::Base);
use constant ITEMS_XPATH => '//ram:IncludedSupplyChainTradeLineItem';
SL/XMLInvoice/UBL.pm
use strict;
use warnings;
use parent qw(SL::XMLInvoice);
use parent qw(SL::XMLInvoice::Base);
use constant ITEMS_XPATH => '//cac:InvoiceLine';

Auch abrufbar als: Unified diff