# Copyright 2011 BibLibre SARL
# This file is part of Koha.
#
-# Koha is free software; you can redistribute it and/or modify it under the
-# terms of the GNU General Public License as published by the Free Software
-# Foundation; either version 2 of the License, or (at your option) any later
-# version.
+# Koha is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
#
-# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
-# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+# Koha is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
#
-# You should have received a copy of the GNU General Public License along
-# with Koha; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+# You should have received a copy of the GNU General Public License
+# along with Koha; if not, see <http://www.gnu.org/licenses>.
=head1 NAME
=cut
-use strict;
-use warnings;
+use Modern::Perl;
-use CGI;
-use C4::Auth;
-use C4::Output;
-use C4::Acquisition;
-use C4::Bookseller qw/GetBookSellerFromId/;
-use C4::Budgets;
+use CGI qw ( -utf8 );
+use C4::Auth qw( get_template_and_user );
+use C4::Output qw( output_and_exit output_html_with_http_headers );
+use C4::Acquisition qw( CloseInvoice ReopenInvoice ModInvoice MergeInvoices DelInvoice GetInvoice GetInvoiceDetails get_rounded_price );
+use C4::Budgets qw( GetBudgetHierarchy GetBudget CanUserUseBudget );
+use JSON qw( encode_json );
+use C4::Log qw(logaction);
-my $input = new CGI;
+use Koha::Acquisition::Booksellers;
+use Koha::Acquisition::Currencies qw( get_active );
+use Koha::AdditionalFields;
+use Koha::DateUtils qw( output_pref );
+use Koha::Misc::Files;
+use Koha::Acquisition::Invoice::Adjustments;
+use Koha::Acquisition::Invoices;
+
+my $input = CGI->new;
my ( $template, $loggedinuser, $cookie, $flags ) = get_template_and_user(
{
- template_name => 'acqui/invoice.tmpl',
+ template_name => 'acqui/invoice.tt',
query => $input,
type => 'intranet',
- authnotrequired => 0,
flagsrequired => { 'acquisition' => '*' },
- debug => 1,
}
);
+my $logged_in_patron = Koha::Patrons->find( $loggedinuser );
my $invoiceid = $input->param('invoiceid');
my $op = $input->param('op');
+output_and_exit( $input, $cookie, $template, 'insufficient_permission' )
+ if $op
+ && ! $logged_in_patron->has_permission( { acquisition => 'edit_invoices' } )
+ && ! $logged_in_patron->has_permission( { acquisition => 'reopen_closed_invoices' } )
+ && ! $logged_in_patron->has_permission( { acquisition => 'merge_invoices' } )
+ && ! $logged_in_patron->has_permission( { acquisition => 'delete_invoices' } );
+
+my $invoice_files;
+if ( C4::Context->preference('AcqEnableFiles') ) {
+ $invoice_files = Koha::Misc::Files->new(
+ tabletag => 'aqinvoices', recordid => $invoiceid );
+}
+
if ( $op && $op eq 'close' ) {
- CloseInvoice($invoiceid);
+ output_and_exit( $input, $cookie, $template, 'insufficient_permission' )
+ unless $logged_in_patron->has_permission( { acquisition => 'edit_invoices' } );
+ my @invoiceid = $input->multi_param('invoiceid');
+ foreach my $invoiceid ( @invoiceid ) {
+ CloseInvoice($invoiceid);
+ }
my $referer = $input->param('referer');
if ($referer) {
print $input->redirect($referer);
}
}
elsif ( $op && $op eq 'reopen' ) {
- ReopenInvoice($invoiceid);
+ output_and_exit( $input, $cookie, $template, 'insufficient_permission' )
+ unless $logged_in_patron->has_permission( { acquisition => 'reopen_closed_invoices' } );
+ my @invoiceid = $input->multi_param('invoiceid');
+ foreach my $invoiceid ( @invoiceid ) {
+ ReopenInvoice($invoiceid);
+ }
my $referer = $input->param('referer');
if ($referer) {
print $input->redirect($referer);
}
}
elsif ( $op && $op eq 'mod' ) {
- my $shipmentdate = $input->param('shipmentdate');
- my $billingdate = $input->param('billingdate');
my $shipmentcost = $input->param('shipmentcost');
my $shipment_budget_id = $input->param('shipment_budget_id');
+ my $invoicenumber = $input->param('invoicenumber');
ModInvoice(
invoiceid => $invoiceid,
- shipmentdate => C4::Dates->new($shipmentdate)->output("iso"),
- billingdate => C4::Dates->new($billingdate)->output("iso"),
+ invoicenumber => $invoicenumber,
+ shipmentdate => scalar $input->param('shipmentdate'),
+ billingdate => scalar $input->param('billingdate'),
shipmentcost => $shipmentcost,
shipmentcost_budgetid => $shipment_budget_id
);
if ($input->param('reopen')) {
- ReopenInvoice($invoiceid);
+ ReopenInvoice($invoiceid)
+ if $logged_in_patron->has_permission( { acquisition => 'reopen_closed_invoices' } );
} elsif ($input->param('close')) {
+
+ output_and_exit( $input, $cookie, $template, 'insufficient_permission' )
+ unless $logged_in_patron->has_permission( { acquisition => 'edit_invoices' } );
+
CloseInvoice($invoiceid);
+ } elsif ($input->param('merge')) {
+
+ output_and_exit( $input, $cookie, $template, 'insufficient_permission' )
+ unless $logged_in_patron->has_permission( { acquisition => 'merge_invoices' } );
+
+ my @sources = $input->multi_param('merge');
+ MergeInvoices($invoiceid, \@sources);
+ defined($invoice_files) && $invoice_files->MergeFileRecIds(@sources);
}
+
+ my @additional_fields;
+ my $invoice_fields = Koha::AdditionalFields->search({ tablename => 'aqinvoices' });
+ while ( my $field = $invoice_fields->next ) {
+ my $value = $input->param('additional_field_' . $field->id);
+ push @additional_fields, {
+ id => $field->id,
+ value => $value,
+ };
+ }
+ Koha::Acquisition::Invoices->find($invoiceid)->set_additional_fields(\@additional_fields);
+
$template->param( modified => 1 );
}
+elsif ( $op && $op eq 'delete' ) {
+
+ output_and_exit( $input, $cookie, $template, 'insufficient_permission' )
+ unless $logged_in_patron->has_permission( { acquisition => 'delete_invoices' } );
+
+ DelInvoice($invoiceid);
+ defined($invoice_files) && $invoice_files->DelAllFiles();
+ my $referer = $input->param('referer') || 'invoices.pl';
+ if ($referer) {
+ print $input->redirect($referer);
+ exit 0;
+ }
+}
+elsif ( $op && $op eq 'del_adj' ) {
+
+ output_and_exit( $input, $cookie, $template, 'insufficient_permission' )
+ unless $logged_in_patron->has_permission( { acquisition => 'edit_invoices' } );
+
+ my $adjustment_id = $input->param('adjustment_id');
+ my $del_adj = Koha::Acquisition::Invoice::Adjustments->find( $adjustment_id );
+ if ($del_adj) {
+ if (C4::Context->preference("AcquisitionLog")) {
+ my $infos = {
+ invoiceid => $del_adj->invoiceid,
+ budget_id => $del_adj->budget_id,
+ encumber_open => $del_adj->encumber_open,
+ adjustment => $del_adj->adjustment,
+ reason => $del_adj->reason
+ };
+ logaction(
+ 'ACQUISITIONS',
+ 'DELETE_INVOICE_ADJUSTMENT',
+ $adjustment_id,
+ encode_json($infos)
+ );
+ }
+ $del_adj->delete();
+ }
+}
+elsif ( $op && $op eq 'mod_adj' ) {
+
+ output_and_exit( $input, $cookie, $template, 'insufficient_permission' )
+ unless $logged_in_patron->has_permission( { acquisition => 'edit_invoices' } );
+
+ my @adjustment_id = $input->multi_param('adjustment_id');
+ my @adjustment = $input->multi_param('adjustment');
+ my @reason = $input->multi_param('reason');
+ my @note = $input->multi_param('note');
+ my @budget_id = $input->multi_param('budget_id');
+ my @encumber_open = $input->multi_param('encumber_open');
+ my %e_open = map { $_ => 1 } @encumber_open;
+
+ my @keys = ('adjustment', 'reason', 'budget_id', 'encumber_open');
+ for( my $i=0; $i < scalar @adjustment; $i++ ){
+ if( $adjustment_id[$i] eq 'new' ){
+ next unless ( $adjustment[$i] || $reason[$i] );
+ my $adj = {
+ invoiceid => $invoiceid,
+ adjustment => $adjustment[$i],
+ reason => $reason[$i],
+ note => $note[$i],
+ budget_id => $budget_id[$i] || undef,
+ encumber_open => defined $e_open{ $adjustment_id[$i] } ? 1 : 0,
+ };
+ my $new_adj = Koha::Acquisition::Invoice::Adjustment->new($adj);
+ $new_adj->store();
+ # Log this addition
+ if (C4::Context->preference("AcquisitionLog")) {
+ logaction(
+ 'ACQUISITIONS',
+ 'CREATE_INVOICE_ADJUSTMENT',
+ $new_adj->adjustment_id,
+ encode_json($adj)
+ );
+ }
+ }
+ else {
+ my $old_adj = Koha::Acquisition::Invoice::Adjustments->find( $adjustment_id[$i] );
+ unless ( $old_adj->adjustment == $adjustment[$i] && $old_adj->reason eq $reason[$i] && $old_adj->budget_id == $budget_id[$i] && $old_adj->encumber_open == $e_open{$adjustment_id[$i]} && $old_adj->note eq $note[$i] ){
+ # Log this modification
+ if (C4::Context->preference("AcquisitionLog")) {
+ my $infos = {
+ adjustment => $adjustment[$i],
+ reason => $reason[$i],
+ budget_id => $budget_id[$i],
+ encumber_open => $e_open{$adjustment_id[$i]},
+ adjustment_old => $old_adj->adjustment,
+ reason_old => $old_adj->reason,
+ budget_id_old => $old_adj->budget_id,
+ encumber_open_old => $old_adj->encumber_open
+ };
+ logaction(
+ 'ACQUISITIONS',
+ 'UPDATE_INVOICE_ADJUSTMENT',
+ $adjustment_id[$i],
+ encode_json($infos)
+ );
+ }
+ $old_adj->timestamp(undef);
+ $old_adj->adjustment( $adjustment[$i] );
+ $old_adj->reason( $reason[$i] );
+ $old_adj->note( $note[$i] );
+ $old_adj->budget_id( $budget_id[$i] || undef );
+ $old_adj->encumber_open( $e_open{$adjustment_id[$i]} ? 1 : 0 );
+ $old_adj->update();
+ }
+ }
+ }
+}
-my $details = GetInvoiceDetails($invoiceid);
-my $bookseller = GetBookSellerFromId( $details->{booksellerid} );
+my $active_currency = Koha::Acquisition::Currencies->get_active,
+my $details = GetInvoiceDetails($invoiceid);
+my $bookseller = Koha::Acquisition::Booksellers->find( $details->{booksellerid} );
my @orders_loop = ();
-my $orders = $details->{'orders'};
-my $qty_total;
-my @books_loop;
-my @book_foot_loop;
+my $orders = $details->{'orders'};
+my @foot_loop;
my %foot;
+my $shipmentcost = $details->{shipmentcost} || 0;
my $total_quantity = 0;
-my $total_rrp = 0;
-my $total_est = 0;
-
+my $total_tax_excluded = 0;
+my $total_tax_included = 0;
+my $total_tax_value = 0;
+my $has_invoice_unitprice;
foreach my $order (@$orders) {
- my $line = get_infos( $order, $bookseller );
+ my $line = get_infos( $order, $bookseller);
+
+ $line->{total_tax_excluded} = get_rounded_price($line->{unitprice_tax_excluded}) * $line->{quantity};
+ $line->{total_tax_included} = get_rounded_price($line->{unitprice_tax_included}) * $line->{quantity};
+
+ $line->{tax_value} = $line->{tax_value_on_receiving};
+ $line->{tax_rate} = $line->{tax_rate_on_receiving};
+ $foot{$$line{tax_rate}}{tax_rate} = $$line{tax_rate};
+ $foot{$$line{tax_rate}}{tax_value} += get_rounded_price($$line{tax_value});
+ $total_tax_value += $$line{tax_value};
+ $foot{$$line{tax_rate}}{quantity} += $$line{quantity};
$total_quantity += $$line{quantity};
- $total_rrp += $order->{quantity} * $order->{rrp};
- $total_est += $order->{quantity} * $order->{'ecost'};
+ $foot{$$line{tax_rate}}{total_tax_excluded} += get_rounded_price($$line{total_tax_excluded});
+ $total_tax_excluded += get_rounded_price($$line{total_tax_excluded});
+ $foot{$$line{tax_rate}}{total_tax_included} += get_rounded_price($$line{total_tax_included});
+ $total_tax_included += get_rounded_price($$line{total_tax_included});
- my %row = ( %$order, %$line );
- push @orders_loop, \%row;
+ $line->{orderline} = $line->{parent_ordernumber};
+ $has_invoice_unitprice = 1 if $line->{invoice_currency} ne $active_currency->currency;
+ push @orders_loop, $line;
}
-my $gist = $bookseller->{gstrate} // C4::Context->preference("gist") // 0;
-my $discount =
- $bookseller->{'discount'} ? ( $bookseller->{discount} / 100 ) : 0;
-my $total_est_gste;
-my $total_est_gsti;
-my $total_rrp_gsti; # RRP Total, GST included
-my $total_rrp_gste; # RRP Total, GST excluded
-my $gist_est;
-my $gist_rrp;
-if ($gist) {
+push @foot_loop, map {$_} values %foot;
- # if we have GST
- if ( $bookseller->{'listincgst'} ) {
-
- # if prices already includes GST
+my $shipmentcost_budgetid = $details->{shipmentcost_budgetid};
- # we know $total_rrp_gsti
- $total_rrp_gsti = $total_rrp;
+# build budget list
+my $budget_loop = [];
+my $budgets = GetBudgetHierarchy();
+foreach my $r ( @{$budgets} ) {
+ next unless ( CanUserUseBudget( $loggedinuser, $r, $flags ) );
- # and can reverse compute other values
- $total_rrp_gste = $total_rrp_gsti / ( $gist + 1 );
+ my $selected = $shipmentcost_budgetid ? $r->{budget_id} eq $shipmentcost_budgetid : 0;
- $gist_rrp = $total_rrp_gsti - $total_rrp_gste;
- $total_est_gste = $total_rrp_gste - ( $total_rrp_gste * $discount );
- $total_est_gsti = $total_est;
- }
- else {
- # if prices does not include GST
-
- # then we use the common way to compute other values
- $total_rrp_gste = $total_rrp;
- $gist_rrp = $total_rrp_gste * $gist;
- $total_rrp_gsti = $total_rrp_gste + $gist_rrp;
- $total_est_gste = $total_est;
- $total_est_gsti = $total_rrp_gsti - ( $total_rrp_gsti * $discount );
- }
- $gist_est = $gist_rrp - ( $gist_rrp * $discount );
-}
-else {
- $total_rrp_gste = $total_rrp_gsti = $total_rrp;
- $total_est_gste = $total_est_gsti = $total_est;
- $gist_rrp = $gist_est = 0;
+ push @{$budget_loop},
+ {
+ b_id => $r->{budget_id},
+ b_txt => $r->{budget_name},
+ b_active => $r->{budget_period_active},
+ selected => $selected,
+ b_sort1_authcat => $r->{'sort1_authcat'},
+ b_sort2_authcat => $r->{'sort2_authcat'},
+ };
}
-my $total_gsti_shipment = $total_est_gsti + $details->{shipmentcost};
-my $format = "%.2f";
+@{$budget_loop} =
+ sort { uc( $a->{b_txt} ) cmp uc( $b->{b_txt} ) } @{$budget_loop};
+
+my $adjustments = Koha::Acquisition::Invoice::Adjustments->search({ invoiceid => $details->{'invoiceid'} });
+if ( $adjustments ) { $template->param( adjustments => $adjustments ); }
+
+my $invoice = Koha::Acquisition::Invoices->find($invoiceid);
$template->param(
- total_rrp_gste => sprintf( $format, $total_rrp_gste ),
- total_rrp_gsti => sprintf( $format, $total_rrp_gsti ),
- total_est_gste => sprintf( $format, $total_est_gste ),
- total_est_gsti => sprintf( $format, $total_est_gsti ),
- gist_rrp => sprintf( $format, $gist_rrp ),
- gist_est => sprintf( $format, $gist_est ),
- total_gsti_shipment => sprintf( $format, $total_gsti_shipment ),
- gist => sprintf( $format, $gist * 100 ),
+ available_additional_fields => Koha::AdditionalFields->search( { tablename => 'aqinvoices' } ),
+ additional_field_values => { map {
+ $_->field->id => $_->value
+ } $invoice->additional_field_values->as_list },
);
-my $budgets = GetBudgets();
-my @budgets_loop;
-my $shipmentcost_budgetid = $details->{shipmentcost_budgetid};
-foreach my $budget (@$budgets) {
- next unless CanUserUseBudget( $loggedinuser, $budget, $flags );
- my %line = %{$budget};
- if ( $shipmentcost_budgetid
- and $budget->{budget_id} == $shipmentcost_budgetid )
- {
- $line{selected} = 1;
- }
- push @budgets_loop, \%line;
-}
-
$template->param(
- invoiceid => $details->{'invoiceid'},
- invoicenumber => $details->{'invoicenumber'},
- suppliername => $details->{'suppliername'},
- supplierid => $details->{'booksellerid'},
- datereceived => $details->{'datereceived'},
- shipmentdate => $details->{'shipmentdate'},
- billingdate => $details->{'billingdate'},
- invoiceclosedate => $details->{'closedate'},
- shipmentcost => sprintf( $format, $details->{'shipmentcost'} || 0 ),
- orders_loop => \@orders_loop,
- total_quantity => $total_quantity,
- invoiceincgst => $bookseller->{invoiceincgst},
- currency => $bookseller->{listprice},
- budgets_loop => \@budgets_loop,
+ invoiceid => $details->{'invoiceid'},
+ invoicenumber => $details->{'invoicenumber'},
+ suppliername => $details->{'suppliername'},
+ booksellerid => $details->{'booksellerid'},
+ shipmentdate => $details->{'shipmentdate'},
+ shipment_budget_id => $shipmentcost_budgetid,
+ billingdate => $details->{'billingdate'},
+ invoiceclosedate => $details->{'closedate'},
+ shipmentcost => $shipmentcost,
+ orders_loop => \@orders_loop,
+ foot_loop => \@foot_loop,
+ total_quantity => $total_quantity,
+ total_tax_excluded => $total_tax_excluded,
+ total_tax_included => $total_tax_included,
+ total_tax_value => $total_tax_value,
+ total_tax_excluded_shipment => $total_tax_excluded + $shipmentcost,
+ total_tax_included_shipment => $total_tax_included + $shipmentcost,
+ invoiceincgst => $bookseller->invoiceincgst,
+ currency => $active_currency,
+ budgets => $budget_loop,
+ budget => GetBudget( $shipmentcost_budgetid ),
+ has_invoice_unitprice => $has_invoice_unitprice,
);
+defined( $invoice_files ) && $template->param( files => $invoice_files->GetFilesInfo() );
+
+# FIXME
+# Fonction dupplicated from basket.pl
+# Code must to be exported. Where ??
sub get_infos {
- my $order = shift;
+ my $order = shift;
my $bookseller = shift;
- my $qty = $order->{'quantity'} || 0;
+ my $qty = $order->{'quantity'} || 0;
if ( !defined $order->{quantityreceived} ) {
$order->{quantityreceived} = 0;
}
my $budget = GetBudget( $order->{'budget_id'} );
- my %line = %{$order};
+ my %line = %{ $order };
$line{order_received} = ( $qty == $order->{'quantityreceived'} );
$line{budget_name} = $budget->{budget_name};
- $line{total} = $qty * $order->{ecost};
- if ( $line{uncertainprice} ) {
- $line{rrp} .= ' (Uncertain)';
- }
if ( $line{'title'} ) {
my $volume = $order->{'volume'};
my $seriestitle = $order->{'seriestitle'};
$line{'title'} .= " / $seriestitle" if $seriestitle;
$line{'title'} .= " / $volume" if $volume;
}
- else {
- $line{'title'} = "Deleted bibliographic notice, can't find title.";
- }
return \%line;
}