#!/usr/bin/perl -w
use strict;

use XML::Twig;

use FindBin qw($Bin);
use lib $Bin;
use wtr2_base;

init_db();

my $DEBUG=0;

# XML::Twig can output the updated document, whith the error messages
my $CAN_OUTPUT= 1;

my @files= @ARGV || (<$dir{invoices}/*.xml>);

foreach my $file (@files)
  { my $doc= XML::Twig->new( pretty_print => 'indented')->parsefile( $file);
    
    my $errors= check_invoice( $doc);

    if( !@$errors)
      { store_invoice( $doc); }
    else
      { print "ERROR in $file\n  ", join( "\n  ", @$errors), "\n";
        if( $CAN_OUTPUT) 
          {my $rejected_file= rejected( $file);
            print "adding errors in $rejected_file\n" if( $DEBUG);
            add_errors( $doc, $errors);
            output_doc_to_check(  $rejected_file, $doc);
          }
      };
  }

exit;

sub check_invoice
  { my( $doc)= @_;
    my $root= $doc->root;
    my $errors=[];  # array ref, holds the error messages

    check_buyer(  $root->first_child( 'BuyerPartyDetails')->field( 'BuyerPartyIdentifier'),
                  $root->first_child( 'BuyerPartyDetails')->field( 'BuyerOrganisationName'),
                  $errors
               );
    check_po(     $root->first_child( 'InvoiceDetails')->field( 'OrderIdentifier'), $errors);

    my @rows= $root->children( 'InvoiceRow');

    reset_default_row_id();
    
    foreach my $row( @rows)
      { # this does not cope well with broken row numbers

        my $row_id= $row->field( 'RowIdentifier') || default_row_id();
        
        print "checking row $row_id\n" if $DEBUG;

    my $DeliveredQuantity = $row->first_child( 'DeliveredQuantity');
    my $OrderedQuantity   = $row->first_child( 'OrderedQuantity');
        my $delivered_qty  = $DeliveredQuantity ? $DeliveredQuantity->text : 0;
        my $delivered_unit = $DeliveredQuantity ? $DeliveredQuantity->att( 'QuantityUnitCode') : '';
        my $ordered_qty    = $OrderedQuantity ? $OrderedQuantity->text : 0;
        my $ordered_unit   = $OrderedQuantity ? $OrderedQuantity->att( 'QuantityUnitCode') : '';

        check_qtty( $row_id, $delivered_qty, $delivered_unit, $ordered_qty, $ordered_unit, $errors);
      }
    
    return $errors;
  }

sub store_invoice
  { my( $doc)= @_; 
    my $root= $doc->root;
    print "storing invoice " . $root->first_child( 'InvoiceDetails')->field('InvoiceNumber') . "\n";

    # build the various data structures
    my $data;

    my $invoice = $root->first_child( 'InvoiceDetails');
    $data->{invoice} = { number         => $invoice->field( 'InvoiceNumber'),
                         date           => $invoice->field( 'InvoiceDate'),
                         po             => $invoice->field( 'OrderIdentifier'),
                         amount_no_tax  => $invoice->field( 'InvoiceTotalVatExcludedAmount'),
                         tax            => $invoice->field( 'InvoiceTotalVatAmount'),
                         amount         => $invoice->field( 'InvoiceTotalVatIncludedAmount'),
                         payment_status => $root->first_child( 'PaymentStatusDetails')
                                                ->field( 'PaymentStatusCode'),
                       };

    my $seller = $root->first_child( 'SellerPartyDetails');
    $data->{seller}  = { identifier      => $seller->field( 'SellerPartyIdentifier'),
                         name            => $seller->field( 'SellerOrganisationName'),
                         tax_code        => $seller->field( 'SellerOrganisationTaxCode'),
                       };

    my $address = $root->first_child( 'SellerPartyDetails')
                       ->first_child( 'SellerPostalAddressDetails');
    $data->{address} = { street         => $address->field( 'SellerStreetName'),
                         town           => $address->field( 'SellerTownName'),
                         zip            => $address->field( 'SellerPostCodeIdentifier'),
                         country_code   => $address->field( 'CountryCode'),
                         po_box         => $address->field( 'SellerPostOfficeBoxIdentifier'),
                       };

    $data->{contact} = { name           => $root->field( 'SellerContactPersonName'),
                         phone          => $root->first_child( 'SellerCommunicationDetails')
                                    ->field( 'SellerPhoneNumberIdentifier'),
                         email          => $root->first_child( 'SellerCommunicationDetails')
                                    ->field( 'SellerEmailaddressIdentifier'),
                       };

    $data->{invoicerow} ||= [];
    reset_default_row_id();

    foreach my $invoicerow ($root->children( 'InvoiceRow'))
      { my $DeliveredQuantity= $invoicerow->first_child( 'DeliveredQuantity');
        my $qty_unit= $DeliveredQuantity ? $DeliveredQuantity->att( 'QuantityUnitCode') : '';

    push @{$data->{invoicerow}},
          { row_id        => $invoicerow->field( 'RowIdentifier') || default_row_id(),
            sku           => $invoicerow->field( 'ArticleIdentifier'),
            name          => $invoicerow->field( 'ArticleName'),
            qty           => $invoicerow->field( 'DeliveredQuantity'),
            qty_unit      => $qty_unit,
            unit_price    => $invoicerow->field( 'UnitPriceAmount'),
            amount_no_tax => $invoicerow->field( 'RowVatExcludedAmount'),
            tax           => $invoicerow->field( 'RowVatAmount'),
            amount        => $invoicerow->field( 'RowAmount'),
          }
      }


    store_all( $data);
  }

sub add_errors
  { my( $doc, $error_messages)= @_;
    my $errors= $doc->root->insert_new_elt( first_child => 'errors');
    foreach my $message (@$error_messages)
      { $errors->insert_new_elt( last_child => error => $message); }
    return $doc;
  }

sub output_doc_to_check
  { my( $file, $doc)= @_;
    open( FILE, ">$file") or die "cannot create file to check $file: $!";
    $doc->print( \*FILE);
    close FILE;
  }

__END__

=head1 NAME

wtr2_twig

=head1 SYNOPSYS

  perl wtr2_twig

=head1 DESCRIPTION

This code uses L<XML::Twig> to process the invoices. It uses the full-tree mode:
load the entire XML document through the C<parsefile> method and then process
it.

It uses mostly navigation to access the information, the C<first_child>
and C<field> methods. New elements are created using the C<insert_new_elt>
method.

This was easy to write, but you would expect so, as I wrote XML::Twig and
I am obviously quite familiar with it ;--)

=head1 AUTHOR

Michel Rodriguez <mirod@xmltwig.com>

=head1 LICENSE

This code is Copyright (c) 2003 Michel Rodriguez. All rights reserved.

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.

Comments can be sent to mirod@xmltwig.com

=head1 SEE ALSO

L<XML::Twig>

Ways to Rome 2 - Kourallinen Dollareita: http://www.xmltwig.com/article/ways_to_rome_2/
   



