Projekt

Allgemein

Profil

Herunterladen (40 KB) Statistiken
| Zweig: | Markierung: | Revision:
use strict;

use List::MoreUtils qw(any none uniq);
use List::Util qw(sum first);
use POSIX qw(strftime);

use SL::DB::BankAccount;
use SL::DB::SepaExport;
use SL::Chart;
use SL::CT;
use SL::Form;
use SL::GenericTranslations;
use SL::Locale::String qw(t8);
use SL::ReportGenerator;
use SL::SEPA;
use SL::SEPA::XML;
use SL::SEPA::SwissXML;
use SL::Helper::QrBillParser;
use SL::Helper::ISO3166;

use SL::Helper::QrBillFunctions qw(
get_street_name_from_address_line
get_building_number_from_address_line
);

require "bin/mozilla/common.pl";
require "bin/mozilla/reportgenerator.pl";

sub bank_transfer_add {
$main::lxdebug->enter_sub();

my $form = $main::form;
my $locale = $main::locale;
my $vc = $form->{vc} eq 'customer' ? 'customer' : 'vendor';
my $vc_no = $form->{vc} eq 'customer' ? $::locale->text('VN') : $::locale->text('CN');

my $swiss_export = $::instance_conf->get_sepa_swiss_xml_export;

$form->{title} = $vc eq 'customer' ?
$::locale->text('Prepare bank collection via SEPA XML') :
$swiss_export ?
$locale->text('Prepare bank transfer via swiss XML') :
$locale->text('Prepare bank transfer via SEPA XML');

my $bank_accounts = SL::DB::Manager::BankAccount->get_all_sorted( query => [ obsolete => 0 ] );

if (!scalar @{ $bank_accounts }) {
$form->error($locale->text('You have not added bank accounts yet.'));
}

my $invoices = SL::SEPA->retrieve_open_invoices(vc => $vc);

if (!scalar @{ $invoices }) {
$form->show_generic_information($locale->text('Either there are no open invoices, or you have already initiated bank transfers ' .
'with the open amounts for those that are still open.'));
$main::lxdebug->leave_sub();
return;
}

# Only include those per default that require manual action from our
# side. For sales invoices these are the ones for which direct debit
# has been selected. For purchase invoices it's the other way
# around: if direct debit is active then the vendor will collect
# from us automatically and we don't have to send money manually.
$_->{checked} = ($vc eq 'customer' ? $_->{direct_debit} : !$_->{direct_debit}) for @{ $invoices };

my $translation_list = GenericTranslations->list(translation_type => 'sepa_remittance_info_pfx');
my %translations = map { ( ($_->{language_id} || 'default') => $_->{translation} ) } @{ $translation_list };

foreach my $invoice (@{ $invoices }) {
my $prefix = $translations{ $invoice->{language_id} } || $translations{default} || $::locale->text('Invoice');
$prefix .= ' ' unless $prefix =~ m/ $/;
$invoice->{reference_prefix} = $prefix;

# add c_vendor_id or v_vendor_id as a prefix if a entry exists
next unless $invoice->{vc_vc_id};

my $prefix_vc_number = $translations{ $invoice->{language_id} } || $translations{default} || $vc_no;
$prefix_vc_number .= ' ' unless $prefix_vc_number =~ m/ $/;
$invoice->{reference_prefix_vc} = ' ' . $prefix_vc_number unless $prefix_vc_number =~ m/^ /;
}

# for swiss export override database check because of different cases
if ($swiss_export) {
foreach my $invoice (@{ $invoices }) {
# determine invoice type
if ($invoice->{qrbill_data}) {
$invoice->{type} = 'QRBILL';

# vendor iban comes from qrbill data
# no further checks needed
$invoice->{vc_bank_info_ok} = 1;

} elsif ($invoice->{vc_iban} =~ m/^(CH|LI)/) {
$invoice->{type} = 'DOMESTIC';

# vendor iban is needed
$invoice->{vc_bank_info_ok} = $invoice->{vc_iban} ? 1 : 0;

} else {
$invoice->{type} = 'SEPA';

# vendor iban and bic are needed
$invoice->{vc_bank_info_ok} = $invoice->{vc_iban} && $invoice->{vc_bic} ? 1 : 0
}
}
}

setup_sepa_add_transfer_action_bar();

$form->header();
print $form->parse_html_template('sepa/bank_transfer_add',
{ 'INVOICES' => $invoices,
'BANK_ACCOUNTS' => $bank_accounts,
'vc' => $vc,
});

$main::lxdebug->leave_sub();
}

sub bank_transfer_create {
$main::lxdebug->enter_sub();

my $form = $main::form;
my $locale = $main::locale;
my $myconfig = \%main::myconfig;
my $vc = $form->{vc} eq 'customer' ? 'customer' : 'vendor';

my $swiss_export = $::instance_conf->get_sepa_swiss_xml_export;

$form->{title} = $vc eq 'customer' ?
$::locale->text('Create bank collection via SEPA XML') :
$swiss_export ?
$locale->text('Create bank transfer via swiss XML') :
$locale->text('Create bank transfer via SEPA XML');

my $bank_accounts = SL::DB::Manager::BankAccount->get_all_sorted( query => [ obsolete => 0 ] );
if (!scalar @{ $bank_accounts }) {
$form->error($locale->text('You have not added bank accounts yet.'));
}

my $bank_account = SL::DB::Manager::BankAccount->find_by( id => $form->{bank_account} );

unless ( $bank_account ) {
$form->error($locale->text('The selected bank account does not exist anymore.'));
}

my $arap_id = $vc eq 'customer' ? 'ar_id' : 'ap_id';
my $invoices = SL::SEPA->retrieve_open_invoices(vc => $vc);

# Load all open invoices (again), but grep out the ones that were selected with checkboxes beforehand ($_->selected).
# At this stage we again have all the invoice information, including dropdown with payment_type options.
# All the information from retrieve_open_invoices is then ADDED to what was passed via @{ $form->{bank_transfers} }.
# Parse amount from the entry in the form, but take skonto_amount from PT again.
# The map inserts the values of invoice_map directly into the array of hashes.
my %selected_ids = map { ($_ => 1) } @{ $form->{ids} || [] };
my %invoices_map = map { $_->{id} => $_ } @{ $invoices };
my @bank_transfers =
map +{ %{ $invoices_map{ $_->{$arap_id} } }, %{ $_ } },
grep { ($_->{selected} || $selected_ids{$_->{$arap_id}}) && (0 < $_->{amount}) && $invoices_map{ $_->{$arap_id} } && !($invoices_map{ $_->{$arap_id} }->{is_sepa_blocked}) }
map { $_->{amount} = $form->parse_amount($myconfig, $_->{amount}); $_ }
@{ $form->{bank_transfers} || [] };

# override default payment_type selection and set it to the one chosen by the user
# in the previous step, so that we don't need the logic in the template
my $subtract_days = $::instance_conf->get_sepa_set_skonto_date_buffer_in_days;
my $set_skonto_date = $::instance_conf->get_sepa_set_skonto_date_as_default_exec_date;
my $set_duedate = $::instance_conf->get_sepa_set_duedate_as_default_exec_date;
foreach my $bt (@bank_transfers) {
# add a good recommended exec date
# set to skonto date if exists or to duedate
# in both cases subtract the same buffer (if configured, default 0)
$bt->{recommended_execution_date} =
$set_skonto_date && $bt->{payment_type} eq 'with_skonto_pt' ?
DateTime->from_kivitendo($bt->{skonto_date})->subtract(days => $subtract_days)->to_kivitendo
: $set_duedate && $bt->{duedate} ?
DateTime->from_kivitendo($bt->{duedate} )->subtract(days => $subtract_days)->to_kivitendo
: undef;


foreach my $type ( @{$bt->{payment_select_options}} ) {
if ( $type->{payment_type} eq $bt->{payment_type} ) {
$type->{selected} = 1;
} else {
$type->{selected} = 0;
};
};
};

if (!scalar @bank_transfers) {
$form->error($locale->text('You have selected none of the invoices.'));
}

my $total_trans = sum map { $_->{open_amount} } @bank_transfers;

my ($vc_bank_info);
my $error_message;
my @bank_columns = qw(iban bic);

# separate validation for swiss export
if (!$swiss_export) {
push @bank_columns, qw(mandator_id mandate_date_of_signature) if $vc eq 'customer';

if ($form->{confirmation}) {
$vc_bank_info = { map { $_->{id} => $_ } @{ $form->{vc_bank_info} || [] } };

foreach my $info (values %{ $vc_bank_info }) {
if (any { !$info->{$_} } @bank_columns) {
$error_message = $locale->text('The bank information must not be empty.');
last;
}
}
}
} else {
($error_message, $vc_bank_info) = validate_vendors_swiss_export(\@bank_transfers);
}

if ($error_message || !$form->{confirmation}) {
if (!$swiss_export) {
my @vc_ids = uniq map { $_->{vc_id} } @bank_transfers;

$vc_bank_info ||= CT->get_bank_info('vc' => $vc, 'id' => \@vc_ids);
}

my @vc_bank_info = sort { lc $a->{name} cmp lc $b->{name} } values %{ $vc_bank_info };

setup_sepa_create_transfer_action_bar(is_vendor => $vc eq 'vendor');

$form->header();
print $form->parse_html_template('sepa/bank_transfer_create',
{ 'BANK_TRANSFERS' => \@bank_transfers,
'BANK_ACCOUNTS' => $bank_accounts,
'VC_BANK_INFO' => \@vc_bank_info,
'bank_account' => $bank_account,
'error_message' => $error_message,
'vc' => $vc,
'total_trans' => $total_trans,
});

} else {
foreach my $bank_transfer (@bank_transfers) {
foreach (@bank_columns) {
$bank_transfer->{"vc_${_}"} = $vc_bank_info->{ $bank_transfer->{vc_id} }->{$_};
$bank_transfer->{"our_${_}"} = $bank_account->{$_};
}

$bank_transfer->{chart_id} = $bank_account->{chart_id};
}

my $id = SL::SEPA->create_export('employee' => $::myconfig{login},
'bank_transfers' => \@bank_transfers,
'vc' => $vc);

$form->header();
print $form->parse_html_template('sepa/bank_transfer_created',
{
'id' => $id,
'vc' => $vc,
});
}

$main::lxdebug->leave_sub();
}

sub validate_vendors_swiss_export {
my ($bank_transfers) = @_;

my $form = $main::form;
my $locale = $main::locale;
my $myconfig = \%main::myconfig;

# determine unique vendor types
my %unique_vendor_types;
for my $bt (@$bank_transfers) {
my $uid = "$bt->{vc_id}_$bt->{type}";

# if qr-bill get iban from qr-bill data to display in vendor account information,
# important for manual verification
my $qr_iban;
if ($bt->{type} eq 'QRBILL') {
my $qr_bill_data = SL::Helper::QrBillParser->new($bt->{qrbill_data});
$qr_iban = $qr_bill_data->{creditor_information}->{iban};
}

$unique_vendor_types{$uid} = {
vc_id => $bt->{vc_id},
type => $bt->{type},
qr_iban => $qr_iban,
} unless defined $unique_vendor_types{$uid};
}

# get bank info for unique vendor types
my $vendors = $form->{vc_bank_info} ?
{ map { $_->{id} => $_ } @{ $form->{vc_bank_info} } } :
my @options = ();
push @options, ($vc eq 'customer' ? $::locale->text('Customer') : $locale->text('Vendor')) . ' : ' . $form->{f_vc} if ($form->{f_vc});
push @options, $locale->text('Invoice number') . ' : ' . $form->{f_invnumber} if ($form->{f_invnumber});
push @options, $locale->text('SEPA message ID') . ' : ' . $form->{f_message_id} if (length $form->{f_message_id});
push @options, $locale->text('Export date from') . ' : ' . $form->{f_export_date_from} if ($form->{f_export_date_from});
push @options, $locale->text('Export date to') . ' : ' . $form->{f_export_date_to} if ($form->{f_export_date_to});
push @options, $locale->text('Requested execution date from') . ' : ' . $form->{f_requested_execution_date_from} if ($form->{f_requested_execution_date_from});
push @options, $locale->text('Requested execution date to') . ' : ' . $form->{f_requested_execution_date_to} if ($form->{f_requested_execution_date_to});
push @options, $locale->text('Execution date from') . ' : ' . $form->{f_execution_date_from} if ($form->{f_execution_date_from});
push @options, $locale->text('Execution date to') . ' : ' . $form->{f_execution_date_to} if ($form->{f_execution_date_to});
push @options, $form->{l_executed} ? $locale->text('executed') : $locale->text('not yet executed') if ($form->{l_executed} != $form->{l_not_executed});
push @options, $form->{l_closed} ? $locale->text('closed') : $locale->text('open') if ($form->{l_open} != $form->{l_closed});

$report->set_options('top_info_text' => join("\n", @options),
'raw_top_info_text' => $form->