Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision f05c4f4d

Von Tamino Steinert vor mehr als 1 Jahr hinzugefügt

  • ID f05c4f4d705729e8664051911ff458276930cb5f
  • Vorgänger 22afa8c9
  • Nachfolger 44292086

ZUGFeRD: Kreditorenbuchung direkt aus ZUGFeRD-XML erstellen

Unterschiede anzeigen:

SL/DB/Helper/ZUGFeRD.pm
use utf8;
use parent qw(Exporter);
our @EXPORT = qw(create_zugferd_data create_zugferd_xmp_data);
our @EXPORT_CREATE = qw(create_zugferd_data create_zugferd_xmp_data);
our @EXPORT_IMPORT = qw(import_zugferd_xml);
our @EXPORT_OK = (@EXPORT_CREATE, @EXPORT_IMPORT);
our %EXPORT_TAGS = (
ALL => (@EXPORT_CREATE, @EXPORT_IMPORT),
CREATE => \@EXPORT_CREATE,
IMPORT => \@EXPORT_IMPORT,
);
use SL::DB::BankAccount;
use SL::DB::GenericTranslation;
use SL::DB::Tax;
use SL::DB::TaxKey;
use SL::DB::RecordTemplate;
use SL::Helper::ISO3166;
use SL::Helper::ISO4217;
use SL::Helper::UNECERecommendation20;
......
};
}
sub import_zugferd_xml {
my ($self, $zugferd_xml) = @_;
# 1. check if ZUGFeRD SellerTradeParty has a VAT-ID
my $ustid = $zugferd_xml->findnodes(
'//ram:SellerTradeParty/ram:SpecifiedTaxRegistration'
)->string_value;
die t8("No VAT Info for this Factur-X/ZUGFeRD invoice," .
" please ask your vendor to add this for his Factur-X/ZUGFeRD data.") unless $ustid;
$ustid = SL::VATIDNr->normalize($ustid);
# 1.1 check if we a have a vendor with this VAT-ID (vendor.ustid)
my $vendor_name = $zugferd_xml->findnodes('//ram:SellerTradeParty/ram:Name')->string_value;
my $vendor = SL::DB::Manager::Vendor->find_by(
ustid => $ustid,
or => [
obsolete => undef,
obsolete => 0,
]);
if (!$vendor) {
# 1.2 If no vendor with the exact VAT ID number is found, the
# number might be stored slightly different in the database
# (e.g. with spaces breaking up groups of numbers). Iterate over
# all existing vendors with VAT ID numbers, normalize their
# representation and compare those.
my $vendors = SL::DB::Manager::Vendor->get_all(
where => [
'!ustid' => undef,
'!ustid' => '',
or => [
obsolete => undef,
obsolete => 0,
],
]);
foreach my $other_vendor (@{ $vendors }) {
next unless SL::VATIDNr->normalize($other_vendor->ustid) eq $ustid;
$vendor = $other_vendor;
last;
}
}
die t8("Please add a valid VAT-ID for this vendor: #1", $vendor_name)
unless (ref $vendor eq 'SL::DB::Vendor');
# 2. check if we have a ap record template for this vendor (TODO only the oldest template is choosen)
my $template_ap = SL::DB::Manager::RecordTemplate->get_first(where => [vendor_id => $vendor->id]);
die t8("No AP Record Template for vendor #1 found, please add one", $vendor_name)
unless (ref $template_ap eq 'SL::DB::RecordTemplate');
# 3. parse the zugferd data and fill the ap record template
# -> no need to check sign (credit notes will be negative) just record thei ZUGFeRD type in ap.notes
# -> check direct debit (defaults to no)
# -> set amount (net amount) and unset taxincluded
# (template and user cares for tax and if there is more than one booking accno)
# -> date (can be empty)
# -> duedate (may be empty)
# -> compare record iban and generate a warning if this differs from vendor's master data iban
my $total = $zugferd_xml->findnodes(
'//ram:SpecifiedTradeSettlementHeaderMonetarySummation' .
'/ram:TaxBasisTotalAmount'
)->string_value;
my $invnumber = $zugferd_xml->findnodes(
'//rsm:ExchangedDocument/ram:ID'
)->string_value;
# parse dates to kivi if set/valid
my %dates = (
transdate => {
key => '//ram:IssueDateTime',
value => undef,
},
duedate => {
key => '//ram:DueDateDateTime',
value => undef,
},
);
foreach my $date (keys %dates) {
my $string_value = $zugferd_xml->findnodes($dates{$date}->{key})->string_value;
$string_value =~ s/^\s+|\s+$//g;
if ($string_value =~ /^[0-9]{8}$/) {
$dates{$date}->{value} = DateTime->new(
year => substr($string_value,0,4),
month => substr ($string_value,4,2),
day => substr($string_value,6,2)
)->to_kivitendo;
}
}
my $transdate = $dates{transdate}->{value};
my $duedate = $dates{duedate}->{value};
my $type = $zugferd_xml->findnodes(
'//rsm:ExchangedDocument/ram:TypeCode'
)->string_value;
my $dd = $zugferd_xml->findnodes(
'//ram:ApplicableHeaderTradeSettlement' .
'/ram:SpecifiedTradeSettlementPaymentMeans/ram:TypeCode'
)->string_value;
my $direct_debit = $dd == 59 ? 1 : 0;
my $iban = $zugferd_xml->findnodes(
'//ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeSettlementPaymentMeans' .
'/ram:PayeePartyCreditorFinancialAccount/ram:IBANID'
)->string_value;
my $ibanmessage;
$ibanmessage = $iban ne $vendor->iban ?
"Record IBAN $iban doesn't match vendor IBAN " . $vendor->iban
: $iban if $iban;
# write values to self
my $today = DateTime->today_local;
my $additional_notes = "ZUGFeRD Import. Type: $type\nIBAN: " . $ibanmessage;
my %params = (
invoice => 0,
vendor_id => $vendor->id,
taxzone_id => $vendor->taxzone_id,
currency_id => $template_ap->currency_id,
direct_debit => $direct_debit,
globalproject_id => $template_ap->project_id,
payment_id => $template_ap->payment_id,
invnumber => $invnumber,
transdate => $transdate || $today->to_kivitendo,
duedate => $duedate || (
$template_ap->vendor->payment ?
$template_ap->vendor->payment->calc_date(reference_date => $today)->to_kivitendo
: $today->to_kivitendo
),
department_id => $template_ap->department_id,
ordnumber => $template_ap->ordnumber,
taxincluded => 0,
notes => join("\n", $template_ap->notes, $additional_notes),
transactions => [],
transaction_description => $template_ap->transaction_description,
);
$self->assign_attributes(%params);
foreach my $template_item (@{$template_ap->items}) {
my %line_params = (
amount => $total,
project_id => $template_item->project_id,
tax_id => $template_item->tax_id,
chart => $template_item->chart,
);
$self->add_ap_amount_row(%line_params);
}
$self->recalculate_amounts();
my $ap_chart = SL::DB::Manager::Chart->get_first(
where => [id => $template_ap->ar_ap_chart_id]
);
$self->create_ap_row(chart => $ap_chart);
return $self;
}
1;

Auch abrufbar als: Unified diff