Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision f9422f02

Von Werner Hahn vor 1 Tag hinzugefügt

  • ID f9422f02f97d4de7deca35c4de0758e5b006873e
  • Vorgänger 48fa6dcc

XMLInvoice: gültige namespaces aus der xml holen ...

Die namespaces CrossIndustryInvoice, ReusableAggregateBusinessInformationEntity, UnqualifiedDataType
können beliebig sein und sind von ZUGfERD nicht festgelegt.
Deswegen werden die ns jetzt vorher ausgelesen.

Unterschiede anzeigen:

SL/XMLInvoice.pm
64 64
    return $self;
65 65
  }
66 66

  
67
  # Determine parser class to use
67
  # Determine parser class and namespaces to use
68 68
  my $type = first {
69 69
    $_->check_signature($self->{dom})
70 70
  } @document_modules;
......
79 79
                        );
80 80
    return $self;
81 81
  }
82

  
83 82
  bless $self, $type;
84 83

  
84
  my $namespaces = $self->namespaces($self->{dom});
85

  
86
  $self->{namespaces} =  $namespaces;
87

  
85 88
  # Implementation sanity check for child classes: make sure they are aware of
86 89
  # the keys the hash returned by their metadata() method must contain.
87 90
  my @missing_data_keys = grep { !${$self->_data_keys}{$_} } @{ $self->data_keys };
SL/XMLInvoice/Base.pm
55 55
=cut
56 56

  
57 57
sub data_keys {
58
  my $self = shift;
58 59
  my @keys = (
59 60
    'currency',      # The bill's currency, such as "EUR"
60 61
    'direct_debit',  # Boolean: whether the bill will get paid by direct debit (1) or not (0)
......
85 86
=cut
86 87

  
87 88
sub item_keys  {
89
  my $self = shift;
88 90
  my @keys = (
89 91
    'currency',
90 92
    'description',
......
144 146
  die "Children of $self must implement a check_signature() method returning 1 for supported XML, 0 for unsupported XML.";
145 147
}
146 148

  
149
=item namespaces($dom)
150

  
151
This static method takes a DOM object and returns an ArrayofHashes[ data => localname ]. C<SL::XMLInvoice> uses this method to determine which ns is valid for wich data. All child classes must implement this method.
152

  
153
=cut
154

  
155
sub namespaces {
156
  my $self = shift;
157
  die "Children of $self must implement a namespaces() method returning an aoh with the namespaces";
158
}
159

  
147 160
=item supported()
148 161

  
149 162
This static method returns an array of free-form strings describing XML invoice
......
227 240
=head1 AUTHOR
228 241

  
229 242
  Johannes Grassler <info@computer-grassler.de>
243
  Werner Hahn <wh@futureworldsearch.net>
230 244

  
231 245
=cut
232 246

  
SL/XMLInvoice/CrossIndustryDocument.pm
5 5

  
6 6
use parent qw(SL::XMLInvoice::Base);
7 7

  
8
use constant ITEMS_XPATH => '//ram:IncludedSupplyChainTradeLineItem';
9

  
10 8
=head1 NAME
11 9

  
12 10
SL::XMLInvoice::CrossIndustryDocument - XML parser for UN/CEFACT Cross Industry Document
......
51 49
=head1 AUTHOR
52 50

  
53 51
  Johannes Grassler <info@computer-grassler.de>
52
  Werner Hahn <wh@futureworldsearch.net>
54 53

  
55 54
=cut
56 55

  
......
73 72
  return 0;
74 73
}
75 74

  
75
sub namespaces {
76
  my ($self, $dom) = @_;
77
  my $rootnode = $dom->documentElement;
78
  my @nodes = $rootnode->findnodes('namespace::*');
79
  my @namespaces = map {[ $_->getData, $_->getLocalName]} @nodes;
80
  return \@namespaces;
81
}
82

  
76 83
# XML XPath expressions for global metadata
77 84
sub scalar_xpaths {
85
  my ($self) = @_;
86

  
87
  my $rsm = $self->{namespaces}->{'urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100'};
88
  my $ram = $self->{namespaces}->{'urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100'};
89
  my $udt = $self->{namespaces}->{'urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100'};
90
  $ram .= ":" if $ram;
91
  $rsm .= ":" if $rsm;
92
  $udt .= ":" if $udt;
93

  
78 94
  return {
79
    currency => ['//ram:InvoiceCurrencyCode'],
80
    direct_debit => ['//ram:SpecifiedTradeSettlementPaymentMeans/ram:TypeCode'],
81
    duedate => ['//ram:DueDateDateTime/udt:DateTimeString', '//ram:EffectiveSpecifiedPeriod/ram:CompleteDateTime/udt:DateTimeString'],
82
    gross_total => ['//ram:DuePayableAmount'],
83
    iban => ['//ram:SpecifiedTradeSettlementPaymentMeans/ram:PayeePartyCreditorFinancialAccount/ram:IBANID'],
84
    invnumber => ['//rsm:HeaderExchangedDocument/ram:ID'],
85
    net_total => ['//ram:TaxBasisTotalAmount'],
86
    transdate => ['//ram:IssueDateTime/udt:DateTimeString'],
87
    taxnumber => ['//ram:SellerTradeParty/ram:SpecifiedTaxRegistration/ram:ID[@schemeID="FC"]'],
88
    type => ['//rsm:HeaderExchangedDocument/ram:TypeCode'],
89
    ustid => ['//ram:SellerTradeParty/ram:SpecifiedTaxRegistration/ram:ID[@schemeID="VA"]'],
90
    vendor_name => ['//ram:SellerTradeParty/ram:Name'],
95
    currency     => ['//' . $ram . 'InvoiceCurrencyCode'],
96
    direct_debit => ['//' . $ram . 'SpecifiedTradeSettlementPaymentMeans/' . $ram . 'TypeCode'],
97
    duedate      => ['//' . $ram . 'DueDateDateTime/' . $udt . 'DateTimeString', '//' . $ram . 'EffectiveSpecifiedPeriod/' . $ram . 'CompleteDateTime/' . $udt . 'DateTimeString'],
98
    gross_total  => ['//' . $ram . 'DuePayableAmount'],
99
    iban         => ['//' . $ram . 'SpecifiedTradeSettlementPaymentMeans/' . $ram . 'PayeePartyCreditorFinancialAccount/' . $ram . 'IBANID'],
100
    invnumber    => ['//' . $rsm . 'HeaderExchangedDocument/' . $ram . 'ID'],
101
    net_total    => ['//' . $ram . 'TaxBasisTotalAmount'],
102
    transdate    => ['//' . $ram . 'IssueDateTime/' . $udt . 'DateTimeString'],
103
    taxnumber    => ['//' . $ram . 'SellerTradeParty/' . $ram . 'SpecifiedTaxRegistration/' . $ram . 'ID[@schemeID="FC"]'],
104
    type         => ['//' . $rsm . 'HeaderExchangedDocument/' . $ram . 'TypeCode'],
105
    ustid        => ['//' . $ram . 'SellerTradeParty/' . $ram . 'SpecifiedTaxRegistration/' . $ram . 'ID[@schemeID="VA"]'],
106
    vendor_name  => ['//' . $ram . 'SellerTradeParty/' . $ram . 'Name'],
91 107
  };
92 108
}
93 109

  
94 110
sub item_xpaths {
111
  my ($self) = @_;
112

  
113
  my $rsm = $self->{namespaces}->{'urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100'};
114
  my $ram = $self->{namespaces}->{'urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100'};
115
  my $udt = $self->{namespaces}->{'urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100'};
116
  $ram .= ":" if $ram;
117
  $rsm .= ":" if $rsm;
118
  $udt .= ":" if $udt;
119

  
95 120
  return {
96
    'currency' => ['./ram:SpecifiedSupplyChainTradeAgreement/ram:GrossPriceProductTradePrice/ram:ChargeAmount[attribute::currencyID]',
97
                   './ram:SpecifiedSupplyChainTradeAgreement/ram:GrossPriceProductTradePrice/ram:BasisAmount'],
98
    'price' => ['./ram:SpecifiedSupplyChainTradeAgreement/ram:GrossPriceProductTradePrice/ram:ChargeAmount',
99
               './ram:SpecifiedSupplyChainTradeAgreement/ram:GrossPriceProductTradePrice/ram:BasisAmount'],
100
    'description' => ['./ram:SpecifiedTradeProduct/ram:Name'],
101
    'quantity' => ['./ram:SpecifiedSupplyChainTradeDelivery/ram:BilledQuantity',],
102
    'subtotal' => ['./ram:SpecifiedSupplyChainTradeSettlement/ram:SpecifiedTradeSettlementMonetarySummation/ram:LineTotalAmount'],
103
    'tax_rate' => ['./ram:SpecifiedSupplyChainTradeSettlement/ram:ApplicableTradeTax/ram:ApplicablePercent'],
104
    'tax_scheme' => ['./ram:SpecifiedSupplyChainTradeSettlement/ram:ApplicableTradeTax/ram:TypeCode'],
105
    'vendor_partno' => ['./ram:SpecifiedTradeProduct/ram:SellerAssignedID'],
121
    'currency'      => ['./' . $ram . ':SpecifiedSupplyChainTradeAgreement/' . $ram . ':GrossPriceProductTradePrice/' . $ram . ':ChargeAmount[attribute::currencyID]',
122
                        './' . $ram . ':SpecifiedSupplyChainTradeAgreement/' . $ram . ':GrossPriceProductTradePrice/' . $ram . ':BasisAmount'],
123
    'price'         => ['./' . $ram . ':SpecifiedSupplyChainTradeAgreement/' . $ram . ':GrossPriceProductTradePrice/' . $ram . ':ChargeAmount',
124
                        './' . $ram . ':SpecifiedSupplyChainTradeAgreement/' . $ram . ':GrossPriceProductTradePrice/' . $ram . ':BasisAmount'],
125
    'description'   => ['./' . $ram . ':SpecifiedTradeProduct/' . $ram . ':Name'],
126
    'quantity'      => ['./' . $ram . ':SpecifiedSupplyChainTradeDelivery/' . $ram . ':BilledQuantity',],
127
    'subtotal'      => ['./' . $ram . ':SpecifiedSupplyChainTradeSettlement/' . $ram . ':SpecifiedTradeSettlementMonetarySummation/' . $ram . ':LineTotalAmount'],
128
    'tax_rate'      => ['./' . $ram . ':SpecifiedSupplyChainTradeSettlement/' . $ram . ':ApplicableTradeTax/' . $ram . ':ApplicablePercent'],
129
    'tax_scheme'    => ['./' . $ram . ':SpecifiedSupplyChainTradeSettlement/' . $ram . ':ApplicableTradeTax/' . $ram . ':TypeCode'],
130
    'vendor_partno' => ['./' . $ram . ':SpecifiedTradeProduct/' . $ram . ':SellerAssignedID'],
106 131
  };
107 132
}
108 133

  
134
sub items_xpath {
135
  my ($self) = @_;
136
  my $rsm = $self->{namespaces}->{'urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100'};
137
  my $ram = $self->{namespaces}->{'urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100'};
138
  my $udt = $self->{namespaces}->{'urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100'};
139
  $ram .= ":" if $ram;
140
  $rsm .= ":" if $rsm;
141
  $udt .= ":" if $udt;
142
  return '//' . $ram . 'IncludedSupplyChainTradeLineItem';
143
}
109 144

  
110 145
# Metadata accessor method
111 146
sub metadata {
......
145 180
  $self->{_metadata} = {};
146 181
  $self->{_items} = ();
147 182

  
183
  my $ram = $self->{namespaces}->{'urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100'};
184
  $ram .= ":" if $ram;
185
  my $udt = $self->{namespaces}->{'urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100'};
148 186
  # Retrieve scalar metadata from DOM
149 187
  foreach my $key ( keys %{$self->scalar_xpaths} ) {
150 188
    foreach my $xpath ( @{${$self->scalar_xpaths}{$key}} ) {
......
154 192
        next;
155 193
      }
156 194
      my $value = $self->{dom}->findnodes($xpath);
195
      unless ($udt) {
196
        $value = $self->{dom}->findnodes('//' . $ram . 'DueDateDateTime','DateTimeString') if $key eq 'duedate';
197
        $value = $self->{dom}->findnodes('//' . $ram . 'IssueDateTime','DateTimeString') if $key eq 'transdate';
198
      }
157 199
      if ( $value ) {
158 200
        # Get rid of extraneous white space
159 201
        $value = $value->string_value;
......
175 217
  my @items;
176 218
  $self->{_items} = \@items;
177 219

  
178
  foreach my $item ( $self->{dom}->findnodes(ITEMS_XPATH)) {
220
  foreach my $item ( $self->{dom}->findnodes($self->items_xpath)) {
179 221
    my %line_item;
180 222
    foreach my $key ( keys %{$self->item_xpaths} ) {
181 223
      foreach my $xpath ( @{${$self->item_xpaths}{$key}} ) {
SL/XMLInvoice/CrossIndustryInvoice.pm
5 5

  
6 6
use parent qw(SL::XMLInvoice::Base);
7 7

  
8
use constant ITEMS_XPATH => '//ram:IncludedSupplyChainTradeLineItem';
9 8

  
10 9
=head1 NAME
11 10

  
......
51 50
=head1 AUTHOR
52 51

  
53 52
  Johannes Grassler <info@computer-grassler.de>
53
  Werner Hahn <wh@futureworldsearch.net>
54 54

  
55 55
=cut
56 56

  
......
73 73
  return 0;
74 74
}
75 75

  
76
sub namespaces {
77
  my ($self, $dom) = @_;
78
  my $rootnode = $dom->documentElement;
79
  my @nodes = $rootnode->findnodes('namespace::*');
80
  my %namespaces = map { $_->getData => $_->getLocalName} @nodes;
81
  return \%namespaces;
82
}
83

  
76 84
# XML XPath expressions for global metadata
77 85
sub scalar_xpaths {
86
  my ($self) = @_;
87

  
88
  my $rsm = $self->{namespaces}->{'urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100'};
89
  my $ram = $self->{namespaces}->{'urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100'};
90
  my $udt = $self->{namespaces}->{'urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100'};
91
  $ram .= ":" if $ram;
92
  $rsm .= ":" if $rsm;
93
  $udt .= ":" if $udt;
94

  
78 95
  return {
79
    currency => '//ram:InvoiceCurrencyCode',
80
    direct_debit => '//ram:SpecifiedTradeSettlementPaymentMeans/ram:TypeCode',
81
    duedate => '//ram:DueDateDateTime/udt:DateTimeString',
82
    gross_total => '//ram:DuePayableAmount',
83
    iban => '//ram:SpecifiedTradeSettlementPaymentMeans/ram:PayeePartyCreditorFinancialAccount/ram:IBANID',
84
    invnumber => '//rsm:ExchangedDocument/ram:ID',
85
    net_total => '//ram:SpecifiedTradeSettlementHeaderMonetarySummation' . '//ram:TaxBasisTotalAmount',
86
    transdate => '//ram:IssueDateTime/udt:DateTimeString',
87
    taxnumber => '//ram:SellerTradeParty/ram:SpecifiedTaxRegistration/ram:ID[@schemeID="FC"]',
88
    type => '//rsm:ExchangedDocument/ram:TypeCode',
89
    ustid => '//ram:SellerTradeParty/ram:SpecifiedTaxRegistration/ram:ID[@schemeID="VA"]',
90
    vendor_name => '//ram:SellerTradeParty/ram:Name',
96
    currency     => '//' . $ram . 'InvoiceCurrencyCode',
97
    direct_debit => '//' . $ram . 'SpecifiedTradeSettlementPaymentMeans/' . $ram . 'TypeCode',
98
    duedate      => '//' . $ram . 'DueDateDateTime/' . $udt . 'DateTimeString',
99
    gross_total  => '//' . $ram . 'DuePayableAmount',
100
    iban         => '//' . $ram . 'SpecifiedTradeSettlementPaymentMeans/' . $ram . 'PayeePartyCreditorFinancialAccount/' . $ram . 'IBANID',
101
    invnumber    => '//' . $rsm . 'ExchangedDocument/' . $ram . 'ID',
102
    net_total    => '//' . $ram . 'SpecifiedTradeSettlementHeaderMonetarySummation' . '//' . $ram . 'TaxBasisTotalAmount',
103
    transdate    => '//' . $ram . 'IssueDateTime/' . $udt . 'DateTimeString',
104
    taxnumber    => '//' . $ram . 'SellerTradeParty/' . $ram . 'SpecifiedTaxRegistration/' . $ram . 'ID[@schemeID="FC"]',
105
    type         => '//' . $rsm . 'ExchangedDocument/' . $ram . 'TypeCode',
106
    ustid        => '//' . $ram . 'SellerTradeParty/' . $ram . 'SpecifiedTaxRegistration/' . $ram . 'ID[@schemeID="VA"]',
107
    vendor_name  => '//' . $ram . 'SellerTradeParty/' . $ram . 'Name',
91 108
  };
92 109
}
93 110

  
94 111
sub item_xpaths {
112
  my ($self) = @_;
113
  my $rsm = $self->{namespaces}->{'urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100'};
114
  my $ram = $self->{namespaces}->{'urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100'};
115
  my $udt = $self->{namespaces}->{'urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100'};
116
  $ram .= ":" if $ram;
117
  $rsm .= ":" if $rsm;
118
  $udt .= ":" if $udt;
95 119
  return {
96
    'currency' => undef, # Only global currency in CrossIndustryInvoice
97
    'price' => './ram:SpecifiedLineTradeAgreement/ram:NetPriceProductTradePrice',
98
    'description' => './ram:SpecifiedTradeProduct/ram:Name',
99
    'quantity' => './ram:SpecifiedLineTradeDelivery/ram:BilledQuantity',
100
    'subtotal' => './ram:SpecifiedLineTradeSettlement/ram:SpecifiedTradeSettlementLineMonetarySummation/ram:LineTotalAmount',
101
    'tax_rate' => './ram:SpecifiedLineTradeSettlement/ram:ApplicableTradeTax/ram:RateApplicablePercent',
102
    'tax_scheme' => './ram:SpecifiedLineTradeSettlement/ram:ApplicableTradeTax/ram:TypeCode',
103
    'vendor_partno' => './ram:SpecifiedTradeProduct/ram:SellerAssignedID',
120
    'currency'      => undef, # Only global currency in CrossIndustryInvoice
121
    'price'         => './' . $ram . 'SpecifiedLineTradeAgreement/' . $ram . 'NetPriceProductTradePrice',
122
    'description'   => './' . $ram . 'SpecifiedTradeProduct/' . $ram . 'Name',
123
    'quantity'      => './' . $ram . 'SpecifiedLineTradeDelivery/' . $ram . 'BilledQuantity',
124
    'subtotal'      => './' . $ram . 'SpecifiedLineTradeSettlement/' . $ram . 'SpecifiedTradeSettlementLineMonetarySummation/' . $ram . 'LineTotalAmount',
125
    'tax_rate'      => './' . $ram . 'SpecifiedLineTradeSettlement/' . $ram . 'ApplicableTradeTax/' . $ram . 'RateApplicablePercent',
126
    'tax_scheme'    => './' . $ram . 'SpecifiedLineTradeSettlement/' . $ram . 'ApplicableTradeTax/' . $ram . 'TypeCode',
127
    'vendor_partno' => './' . $ram . 'SpecifiedTradeProduct/' . $ram . 'SellerAssignedID',
104 128
  };
105 129
}
106 130

  
131
sub items_xpath {
132
  my ($self) = @_;
133
  my $rsm = $self->{namespaces}->{'urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100'};
134
  my $ram = $self->{namespaces}->{'urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100'};
135
  my $udt = $self->{namespaces}->{'urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100'};
136
  $ram .= ":" if $ram;
137
  $rsm .= ":" if $rsm;
138
  $udt .= ":" if $udt;
139
  return '//' . $ram . 'IncludedSupplyChainTradeLineItem';
140
}
107 141

  
108 142
# Metadata accessor method
109 143
sub metadata {
......
143 177
  $self->{_metadata} = {};
144 178
  $self->{_items} = ();
145 179

  
180
  my $ram = $self->{namespaces}->{'urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100'};
181
  $ram .= ":" if $ram;
182
  my $udt = $self->{namespaces}->{'urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100'};
183
  #foreach my $namespace (@{$self->{namespaces}}(
146 184
  # Retrieve scalar metadata from DOM
147 185
  foreach my $key ( keys %{$self->scalar_xpaths} ) {
148 186
    my $xpath = ${$self->scalar_xpaths}{$key};
......
152 190
      next;
153 191
    }
154 192
    my $value = $self->{dom}->findnodes($xpath);
193
    unless ($udt) {
194
      $value = $self->{dom}->findnodes('//' . $ram . 'DueDateDateTime','DateTimeString') if $key eq 'duedate';
195
      $value = $self->{dom}->findnodes('//' . $ram . 'IssueDateTime','DateTimeString') if $key eq 'transdate';
196
    }
155 197
    if ( $value ) {
156 198
      # Get rid of extraneous white space
157 199
      $value = $value->string_value;
......
163 205
    }
164 206
  }
165 207

  
166

  
167 208
  # Convert payment code metadata field to Boolean
168 209
  # See https://service.unece.org/trade/untdid/d16b/tred/tred4461.htm for other valid codes.
169 210
  ${$self->{_metadata}}{'direct_debit'} = ${$self->{_metadata}}{'direct_debit'} == 59 ? 1 : 0;
......
171 212
  my @items;
172 213
  $self->{_items} = \@items;
173 214

  
174
  foreach my $item ( $self->{dom}->findnodes(ITEMS_XPATH) ) {
215
  foreach my $item ( $self->{dom}->findnodes($self->items_xpath) ) {
175 216
    my %line_item;
176 217
    foreach my $key ( keys %{$self->item_xpaths} ) {
177 218
      my $xpath = ${$self->item_xpaths}{$key};

Auch abrufbar als: Unified diff