use SL::DB::Tax;
use SL::DB::Draft;
use SL::DB::BankAccount;
+use SL::DBUtils qw(like);
use SL::Presenter;
use List::Util qw(max);
push @where, (transdate => { lt => $todate }) if ($todate);
my $bank_account = SL::DB::Manager::BankAccount->find_by( id => $::form->{filter}{bank_account} );
# bank_transactions no younger than starting date,
+ # including starting date (same search behaviour as fromdate)
# but OPEN invoices to be matched may be from before
if ( $bank_account->reconciliation_starting_date ) {
- push @where, (transdate => { gt => $bank_account->reconciliation_starting_date });
+ push @where, (transdate => { ge => $bank_account->reconciliation_starting_date });
};
my $bank_transactions = SL::DB::Manager::BankTransaction->get_all(where => [ amount => {ne => \'invoice_amount'},
$self->{transaction} = SL::DB::Manager::BankTransaction->find_by(id => $::form->{bt_id});
my $vendor_of_transaction = SL::DB::Manager::Vendor->find_by(account_number => $self->{transaction}->{remote_account_number});
+ my $use_vendor_filter = $self->{transaction}->{remote_account_number} && $vendor_of_transaction;
+
my $drafts = SL::DB::Manager::Draft->get_all(where => [ module => 'ap'] , with_objects => 'employee');
my @filtered_drafts;
}
#Filter drafts
- @filtered_drafts = grep { $_->{vendor_id} == $vendor_of_transaction->id } @filtered_drafts if $vendor_of_transaction;
+ @filtered_drafts = grep { $_->{vendor_id} == $vendor_of_transaction->id } @filtered_drafts if $use_vendor_filter;
my $all_vendors = SL::DB::Manager::Vendor->get_all();
$self->render('bank_transactions/create_invoice', { layout => 0 },
title => t8('Create invoice'),
DRAFTS => \@filtered_drafts,
- vendor_id => $vendor_of_transaction ? $vendor_of_transaction->id : undef,
- vendor_name => $vendor_of_transaction ? $vendor_of_transaction->name : undef,
+ vendor_id => $use_vendor_filter ? $vendor_of_transaction->id : undef,
+ vendor_name => $use_vendor_filter ? $vendor_of_transaction->name : undef,
ALL_VENDORS => $all_vendors,
limit => $myconfig{vclimit},
callback => $self->url_for(action => 'list',
# create an HTML blob to be used by the js function add_invoices in templates/webpages/bank_transactions/list.html
# and return encoded as JSON
- my $bt = SL::DB::Manager::BankTransaction->find_by( id => $::form->{bt_id} );
- my $invoice = SL::DB::Manager::Invoice->find_by( id => $::form->{prop_id} );
- $invoice = SL::DB::Manager::PurchaseInvoice->find_by( id => $::form->{prop_id} ) unless $invoice;
+ my $bt = SL::DB::Manager::BankTransaction->find_by( id => $::form->{bt_id} );
+ my $invoice = SL::DB::Manager::Invoice->find_by( id => $::form->{prop_id} ) || SL::DB::Manager::PurchaseInvoice->find_by( id => $::form->{prop_id} );
die unless $bt and $invoice;
my $html;
$html .= SL::Presenter->input_tag('invoice_ids.' . $::form->{bt_id} . '[]', $::form->{prop_id} , type => 'hidden');
- $html .= SL::Presenter->escape( $invoice->invnumber );
+ # better in template code - but how to ajax this
+ $html .= SL::Presenter->escape(t8('Invno.') . ': ' . $invoice->invnumber . ' ');
+ $html .= SL::Presenter->escape(t8('Open amount') . ': ' . $::form->format_amount(\%::myconfig, $invoice->open_amount, 2) . ' ');
$html .= SL::Presenter->select_tag('invoice_skontos.' . $::form->{bt_id} . '[]', \@select_options,
value_key => 'payment_type',
title_key => 'display' ) if @select_options;
foreach my $draft ( @{ $drafts } ) {
my $draft_as_object = YAML::Load($draft->form);
+ next unless $draft_as_object->{vendor_id}; # we cannot filter for vendor name, if this is a gl draft
my $vendor = SL::DB::Manager::Vendor->find_by(id => $draft_as_object->{vendor_id});
$draft->{vendor} = $vendor->name;
$draft->{vendor_id} = $vendor->id;
my @where_purchase = (amount => { ne => \'paid' });
if ($::form->{invnumber}) {
- push @where_sale, (invnumber => { ilike => '%' . $::form->{invnumber} . '%'});
- push @where_purchase, (invnumber => { ilike => '%' . $::form->{invnumber} . '%'});
+ push @where_sale, (invnumber => { ilike => like($::form->{invnumber})});
+ push @where_purchase, (invnumber => { ilike => like($::form->{invnumber})});
}
if ($::form->{amount}) {
}
if ($::form->{vcnumber}) {
- push @where_sale, ('customer.customernumber' => { ilike => '%' . $::form->{vcnumber} . '%'});
- push @where_purchase, ('vendor.vendornumber' => { ilike => '%' . $::form->{vcnumber} . '%'});
+ push @where_sale, ('customer.customernumber' => { ilike => like($::form->{vcnumber})});
+ push @where_purchase, ('vendor.vendornumber' => { ilike => like($::form->{vcnumber})});
}
if ($::form->{vcname}) {
- push @where_sale, ('customer.name' => { ilike => '%' . $::form->{vcname} . '%'});
- push @where_purchase, ('vendor.name' => { ilike => '%' . $::form->{vcname} . '%'});
+ push @where_sale, ('customer.name' => { ilike => like($::form->{vcname})});
+ push @where_purchase, ('vendor.name' => { ilike => like($::form->{vcname})});
}
if ($::form->{transdatefrom}) {
my @selected_invoices;
foreach my $invoice_id (@{ $::form->{invoice_id} || [] }) {
- my $invoice_object = SL::DB::Manager::Invoice->find_by(id => $invoice_id);
- $invoice_object ||= SL::DB::Manager::PurchaseInvoice->find_by(id => $invoice_id);
-
+ my $invoice_object = SL::DB::Manager::Invoice->find_by(id => $invoice_id) || SL::DB::Manager::PurchaseInvoice->find_by(id => $invoice_id);
push @selected_invoices, $invoice_object;
}
my ($self) = @_;
my $invoice_hash = delete $::form->{invoice_ids}; # each key (the bt line with a bt_id) contains an array of invoice_ids
+
+ # e.g. three partial payments with bt_ids 54, 55 and 56 for invoice with id 74:
+ # $invoice_hash = {
+ # '55' => [
+ # '74'
+ # ],
+ # '54' => [
+ # '74'
+ # ],
+ # '56' => [
+ # '74'
+ # ]
+ # };
+ #
+ # or if the payment with bt_id 44 is used to pay invoices with ids 50, 51 and 52
+ # $invoice_hash = {
+ # '44' => [ '50', '51', 52' ]
+ # };
+
my $skonto_hash = delete $::form->{invoice_skontos} || {}; # array containing the payment type, could be empty
# a bank_transaction may be assigned to several invoices, i.e. a customer
# might pay several open invoices with one transaction
while ( my ($bt_id, $invoice_ids) = each(%$invoice_hash) ) {
- my $bank_transaction = SL::DB::Manager::BankTransaction->find_by(id => $bt_id);
- my $sign = $bank_transaction->amount < 0 ? -1 : 1;
+ my $bank_transaction = SL::DB::Manager::BankTransaction->find_by(id => $bt_id);
+ my $sign = $bank_transaction->amount < 0 ? -1 : 1;
my $amount_of_transaction = $sign * $bank_transaction->amount;
my @invoices;
foreach my $invoice_id (@{ $invoice_ids }) {
push @invoices, (SL::DB::Manager::Invoice->find_by(id => $invoice_id) || SL::DB::Manager::PurchaseInvoice->find_by(id => $invoice_id));
}
- @invoices = sort { return 1 if ($a->is_sales and $a->amount > 0);
- return 1 if (!$a->is_sales and $a->amount < 0);
- return -1; } @invoices if $bank_transaction->amount > 0;
- @invoices = sort { return -1 if ($a->is_sales and $a->amount > 0);
+ @invoices = sort { return 1 if ( $a->is_sales and $a->amount > 0);
+ return 1 if (!$a->is_sales and $a->amount < 0);
+ return -1;
+ } @invoices if $bank_transaction->amount > 0;
+ @invoices = sort { return -1 if ( $a->is_sales and $a->amount > 0);
return -1 if (!$a->is_sales and $a->amount < 0);
- return 1; } @invoices if $bank_transaction->amount < 0;
+ return 1;
+ } @invoices if $bank_transaction->amount < 0;
+
+ my $max_invoices = scalar(@invoices);
+ my $n_invoices = 0;
foreach my $invoice (@invoices) {
+ $n_invoices++ ;
# Check if bank_transaction already has a link to the invoice, may only be linked once per invoice
# This might be caused by the user reloading a page and resending the form
die t8("Bank transaction with id #1 has already been linked to #2.", $bank_transaction->id, $invoice->displayable_name)
last;
}
# pay invoice or go to the next bank transaction if the amount is not sufficiently high
- if ($invoice->amount <= $amount_of_transaction) {
+ if ($invoice->open_amount <= $amount_of_transaction && $n_invoices < $max_invoices) {
+ # first calculate new bank transaction amount ...
+ if ($invoice->is_sales) {
+ $amount_of_transaction -= $sign * $invoice->open_amount;
+ $bank_transaction->invoice_amount($bank_transaction->invoice_amount + $invoice->open_amount);
+ } else {
+ $amount_of_transaction += $sign * $invoice->open_amount;
+ $bank_transaction->invoice_amount($bank_transaction->invoice_amount - $invoice->open_amount);
+ }
+ # ... and then pay the invoice
$invoice->pay_invoice(chart_id => $bank_transaction->local_bank_account->chart_id,
trans_id => $invoice->id,
- amount => $invoice->amount,
+ amount => $invoice->open_amount,
payment_type => $payment_type,
transdate => $bank_transaction->transdate->to_kivitendo);
- if ($invoice->is_sales) {
- $amount_of_transaction -= $sign * $invoice->amount;
- $bank_transaction->invoice_amount($bank_transaction->invoice_amount + $invoice->amount);
- } else {
- $amount_of_transaction += $sign * $invoice->amount if (!$invoice->is_sales);
- $bank_transaction->invoice_amount($bank_transaction->invoice_amount - $invoice->amount);
- }
- } else {
+ } else { # use the whole amount of the bank transaction for the invoice, overpay the invoice if necessary
$invoice->pay_invoice(chart_id => $bank_transaction->local_bank_account->chart_id,
trans_id => $invoice->id,
amount => $amount_of_transaction,
payment_type => $payment_type,
transdate => $bank_transaction->transdate->to_kivitendo);
- $bank_transaction->invoice_amount($bank_transaction->amount) if $invoice->is_sales;
- $bank_transaction->invoice_amount($bank_transaction->amount) if !$invoice->is_sales;
+ $bank_transaction->invoice_amount($bank_transaction->amount);
$amount_of_transaction = 0;
}
);
SL::DB::RecordLink->new(@props)->save;
+
+ # "close" a sepa_export_item if it exists
+ # code duplicated in action_save_proposals!
+ # currently only works, if there is only exactly one open sepa_export_item
+ if ( my $seis = $invoice->find_sepa_export_items({ executed => 0 }) ) {
+ if ( scalar @$seis == 1 ) {
+ # moved the execution and the check for sepa_export into a method,
+ # this isn't part of a transaction, though
+ $seis->[0]->set_executed if $invoice->id == $seis->[0]->arap_id;
+ };
+ };
+
}
$bank_transaction->save;
}
);
SL::DB::RecordLink->new(@props)->save;
+
+ # code duplicated in action_save_invoices!
+ # "close" a sepa_export_item if it exists
+ # currently only works, if there is only exactly one open sepa_export_item
+ if ( my $seis = $arap->find_sepa_export_items({ executed => 0 }) ) {
+ if ( scalar @$seis == 1 ) {
+ # moved the execution and the check for sepa_export into a method,
+ # this isn't part of a transaction, though
+ $seis->[0]->set_executed if $arap->id == $seis->[0]->arap_id;
+ };
+ };
}
flash('ok', t8('#1 proposal(s) saved.', scalar @{ $::form->{proposal_ids} }));