Bug 36486: Add tests for Koha::DateTime::Format::SQL
[koha.git] / acqui / addorder.pl
1 #!/usr/bin/perl
2
3 #script to add an order into the system
4 #written 29/2/00 by chris@katipo.co.nz
5
6 # Copyright 2000-2002 Katipo Communications
7 #
8 # This file is part of Koha.
9 #
10 # Koha is free software; you can redistribute it and/or modify it
11 # under the terms of the GNU General Public License as published by
12 # the Free Software Foundation; either version 3 of the License, or
13 # (at your option) any later version.
14 #
15 # Koha is distributed in the hope that it will be useful, but
16 # WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
19 #
20 # You should have received a copy of the GNU General Public License
21 # along with Koha; if not, see <http://www.gnu.org/licenses>.
22
23 =head1 NAME
24
25 addorder.pl
26
27 =head1 DESCRIPTION
28
29 this script allows to add an order.
30 It is called by :
31
32 =over
33
34 =item neworderempty.pl to add an order from an existing record or from nothing.
35
36 =item newordersuggestion.pl to add an order from an existing suggestion.
37
38 =back
39
40 =head1 CGI PARAMETERS
41
42 All of the cgi parameters below are related to the new order.
43
44 =over
45
46 =item C<ordernumber>
47 the number of this new order.
48
49 =item C<basketno>
50 the number of this new basket
51
52 =item C<booksellerid>
53 the bookseller the librarian has to pay.
54
55 =item C<existing>
56
57 =item C<title>
58 the title of the record ordered.
59
60 =item C<author>
61 the author of the record ordered.
62
63 =item C<copyrightdate>
64 the copyrightdate of the record ordered.
65
66 =item C<ISBN>
67 the ISBN of the record ordered.
68
69 =item C<format>
70
71 =item C<quantity>
72 the quantity to order.
73
74 =item C<listprice>
75 the price of this order.
76
77 =item C<uncertainprice>
78 uncertain price, can't close basket until prices of all orders are known.
79
80 =item C<branch>
81 the branch where this order will be received.
82
83 =item C<series>
84
85 =item C<notes>
86 Notes on this basket.
87
88 =item C<budget_id>
89 budget_id used to pay this order.
90
91 =item C<sort1> & C<sort2>
92
93 =item C<rrp>
94
95 =item C<ecost>
96
97 =item C<GST>
98
99 =item C<budget>
100
101 =item C<cost>
102
103 =item C<sub>
104
105 =item C<invoice>
106 the number of the invoice for this order.
107
108 =item C<publishercode>
109
110 =item C<suggestionid>
111 if it is an order from an existing suggestion : the id of this suggestion.
112
113 =item C<donation>
114
115 =back
116
117 =cut
118
119 use Modern::Perl;
120 use CGI qw ( -utf8 );
121 use JSON qw ( to_json encode_json );
122
123 use C4::Acquisition qw( FillWithDefaultValues ModOrderUsers );
124 use C4::Auth qw( get_template_and_user );
125 use C4::Barcodes;
126 use C4::Biblio qw( AddBiblio GetMarcFromKohaField TransformHtmlToXml TransformKohaToMarc );
127 use C4::Budgets qw( GetBudget GetBudgetSpent GetBudgetOrdered FieldsForCalculatingFundValues );
128 use C4::Items qw( AddItemFromMarc );
129 use C4::Log qw( logaction );
130 use C4::Output qw( output_html_with_http_headers );
131 use C4::Suggestions qw( ModSuggestion );
132 use Koha::Acquisition::Baskets;
133 use Koha::Acquisition::Currencies qw( get_active );
134 use Koha::Acquisition::Orders;
135 use Koha::AdditionalFields;
136 use Koha::DateUtils qw( dt_from_string );
137
138 ### "-------------------- addorder.pl ----------"
139
140 # FIXME: This needs to do actual error checking and possibly return user to the same form,
141 # not just blindly call C4 functions and print a redirect.
142
143 my $input = CGI->new;
144
145 my $op = $input->param('op') // q{};
146
147 if ( $op eq 'cud-order' ) {
148     my $use_ACQ_framework = $input->param('use_ACQ_framework');
149
150     # Check if order total amount exceed allowed budget
151     my $confirm_budget_exceeding = $input->param('confirm_budget_exceeding');
152     unless ($confirm_budget_exceeding) {
153         my $budget_id      = $input->param('budget_id');
154         my $total          = $input->param('total');
155         my $budget         = GetBudget($budget_id);
156         my $budget_spent   = GetBudgetSpent($budget_id);
157         my $budget_ordered = GetBudgetOrdered($budget_id);
158
159         my $ordernumber = $input->param('ordernumber');
160         if ($ordernumber) {
161
162             # modifying an existing order so remove order price from $budget_ordered
163             my $order = Koha::Acquisition::Orders->find($ordernumber);
164
165             # get correct unitprice and ecost fields
166             my ( $unitprice_field, $ecost_field ) = FieldsForCalculatingFundValues();
167             $budget_ordered = $budget_ordered - ( $order->$ecost_field * $order->quantity );
168         }
169
170         my $budget_used        = $budget_spent + $budget_ordered;
171         my $budget_remaining   = $budget->{budget_amount} - $budget_used;
172         my $budget_encumbrance = $budget->{budget_amount} * $budget->{budget_encumb} / 100;
173         my $budget_expenditure = $budget->{budget_expend};
174
175         if (   $total > $budget_remaining
176             || ( ( $budget_encumbrance + 0 ) && ( $budget_used + $total ) > $budget_encumbrance )
177             || ( ( $budget_expenditure + 0 ) && ( $budget_used + $total ) > $budget_expenditure ) )
178         {
179             my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
180                 {
181                     template_name => "acqui/addorder.tt",
182                     query         => $input,
183                     type          => "intranet",
184                     flagsrequired => { acquisition => 'order_manage' },
185                 }
186             );
187
188             my $url = $input->referer();
189             unless ( defined $url ) {
190                 my $basketno = $input->param('basketno');
191                 $url = "/cgi-bin/koha/acqui/basket.pl?basketno=$basketno";
192             }
193
194             my $vars = $input->Vars;
195             my @vars_loop;
196             foreach ( keys %$vars ) {
197                 push @vars_loop, {
198                     name   => $_,
199                     values => [ $input->multi_param($_) ],
200                 };
201             }
202
203             if (   ( $budget_encumbrance + 0 )
204                 && ( $budget_used + $total ) > $budget_encumbrance
205                 && $total <= $budget_remaining )
206             {
207                 $template->param(
208                     encumbrance_exceeded => 1,
209                     encumbrance          => sprintf( "%.2f", $budget->{'budget_encumb'} ),
210                 );
211             }
212             if (   ( $budget_expenditure + 0 )
213                 && ( $budget_used + $total ) > $budget_expenditure
214                 && $total <= $budget_remaining )
215             {
216                 my $currency = Koha::Acquisition::Currencies->get_active;
217                 $template->param(
218                     expenditure_exceeded => 1,
219                     expenditure          => sprintf( "%.2f", $budget_expenditure ),
220                     currency             => ($currency) ? $currency->symbol : '',
221                 );
222             }
223             if ( $total > $budget_remaining ) {
224                 $template->param( budget_exceeded => 1 );
225             }
226
227             $template->param(
228                 not_enough_budget => 1,
229                 referer           => $url,
230                 vars_loop         => \@vars_loop,
231             );
232             output_html_with_http_headers $input, $cookie, $template->output;
233             exit;
234         }
235     }
236
237     # get_template_and_user used only to check auth & get user id
238     my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
239         {
240             template_name => "acqui/booksellers.tt",
241             query         => $input,
242             type          => "intranet",
243             flagsrequired => { acquisition => 'order_manage' },
244         }
245     );
246
247     # get CGI parameters
248     my $orderinfo = {
249         ordernumber             => scalar $input->param('ordernumber'),
250         basketno                => scalar $input->param('basketno'),
251         biblionumber            => scalar $input->param('biblionumber'),
252         invoiceid               => scalar $input->param('invoiceid'),
253         quantity                => scalar $input->param('quantity'),
254         budget_id               => scalar $input->param('budget_id'),
255         currency                => scalar $input->param('currency'),
256         listprice               => scalar $input->param('listprice'),
257         uncertainprice          => scalar $input->param('uncertainprice'),
258         tax_rate_on_ordering    => scalar $input->param('tax_rate'),
259         discount                => scalar $input->param('discount'),
260         rrp                     => scalar $input->param('rrp'),
261         replacementprice        => scalar $input->param('replacementprice'),
262         ecost                   => scalar $input->param('ecost'),
263         unitprice               => scalar $input->param('unitprice'),
264         order_internalnote      => scalar $input->param('order_internalnote'),
265         order_vendornote        => scalar $input->param('order_vendornote'),
266         sort1                   => scalar $input->param('sort1'),
267         sort2                   => scalar $input->param('sort2'),
268         subscriptionid          => scalar $input->param('subscriptionid'),
269         estimated_delivery_date => scalar $input->param('estimated_delivery_date'),
270     };
271
272     $orderinfo->{uncertainprice} ||= 0;
273     $orderinfo->{subscriptionid} ||= undef;
274
275     my $user     = $input->remote_user;
276     my $basketno = $$orderinfo{basketno};
277     my $basket   = Koha::Acquisition::Baskets->find($basketno);
278
279     # Order related fields we're going to log
280     my @log_order_fields = (
281         'quantity',
282         'listprice',
283         'unitprice',
284         'unitprice_tax_excluded',
285         'unitprice_tax_included',
286         'rrp',
287         'replacementprice',
288         'rrp_tax_excluded',
289         'rrp_tax_included',
290         'ecost',
291         'ecost_tax_excluded',
292         'ecost_tax_included',
293         'tax_rate_on_ordering'
294     );
295
296     # create, modify or delete biblio
297     # create if $quantity>0 and $existing='no'
298     # modify if $quantity>0 and $existing='yes'
299     if ( $basket->{is_standing} || $orderinfo->{quantity} ne '0' ) {
300
301         #TODO:check to see if biblio exists
302         unless ( $$orderinfo{biblionumber} ) {
303
304             my $record;
305             if ($use_ACQ_framework) {
306                 my @tags         = $input->multi_param('bib_tag');
307                 my @subfields    = $input->multi_param('bib_subfield');
308                 my @field_values = $input->multi_param('bib_field_value');
309                 my $xml          = TransformHtmlToXml( \@tags, \@subfields, \@field_values );
310                 $record = MARC::Record::new_from_xml( $xml, 'UTF-8' );
311             } else {
312
313                 #if it doesn't create it
314                 $record = TransformKohaToMarc(
315                     {
316                         "biblio.title"                 => $input->param('title')            || '',
317                         "biblio.author"                => $input->param('author')           || '',
318                         "biblio.seriestitle"           => $input->param('series')           || '',
319                         "biblioitems.isbn"             => $input->param('isbn')             || '',
320                         "biblioitems.ean"              => $input->param('ean')              || '',
321                         "biblioitems.publishercode"    => $input->param('publishercode')    || '',
322                         "biblioitems.publicationyear"  => $input->param('publicationyear')  || '',
323                         "biblio.copyrightdate"         => $input->param('publicationyear')  || '',
324                         "biblioitems.itemtype"         => $input->param('itemtype')         || '',
325                         "biblioitems.editionstatement" => $input->param('editionstatement') || '',
326                     }
327                 );
328             }
329             C4::Acquisition::FillWithDefaultValues($record);
330
331             # create the record in catalogue, with framework ''
332             my ( $biblionumber, $bibitemnum ) = AddBiblio( $record, '' );
333
334             $orderinfo->{biblionumber} = $biblionumber;
335         }
336
337         # change suggestion status if applicable
338         if ( my $suggestionid = $input->param('suggestionid') ) {
339             ModSuggestion(
340                 {
341                     suggestionid => $suggestionid,
342                     biblionumber => $orderinfo->{biblionumber},
343                     STATUS       => 'ORDERED',
344                 }
345             );
346             if ( C4::Context->preference('PlaceHoldsOnOrdersFromSuggestions') ) {
347                 my $suggestion = Koha::Suggestions->find($suggestionid);
348                 if ($suggestion) {
349                     $suggestion->place_hold();
350                 }
351             }
352         }
353
354         $orderinfo->{unitprice} = $orderinfo->{ecost}
355             if not defined $orderinfo->{unitprice}
356             or $orderinfo->{unitprice} eq '';
357
358         my $order;
359         my $log_action_name;
360
361         if ( $orderinfo->{ordernumber} ) {
362             $order = Koha::Acquisition::Orders->find( $orderinfo->{ordernumber} );
363             $order->set($orderinfo);
364             $log_action_name = 'MODIFY_ORDER';
365         } else {
366             $order           = Koha::Acquisition::Order->new($orderinfo);
367             $log_action_name = 'CREATE_ORDER';
368         }
369         $order->populate_with_prices_for_ordering();
370         $order->store;
371
372         # Log the order creation
373         if ( C4::Context->preference("AcquisitionLog") ) {
374             my $infos = {};
375             foreach my $field (@log_order_fields) {
376                 $infos->{$field} = $order->$field;
377             }
378             logaction(
379                 'ACQUISITIONS',
380                 $log_action_name,
381                 $order->ordernumber,
382                 encode_json($infos)
383             );
384         }
385         my $order_users_ids = $input->param('users_ids');
386         my @order_users     = split( /:/, $order_users_ids );
387         ModOrderUsers( $order->ordernumber, @order_users );
388
389         # Retrieve and save additional fields values
390         my @additional_fields = Koha::AdditionalFields->search( { tablename => 'aqorders' } )->as_list;
391         my @additional_field_values;
392         foreach my $af (@additional_fields) {
393             my $id    = $af->id;
394             my $value = $input->param("additional_field_$id");
395             push @additional_field_values, {
396                 id    => $id,
397                 value => $value,
398             };
399         }
400         $order->set_additional_fields( \@additional_field_values );
401
402         # now, add items if applicable
403         if ( $basket->effective_create_items eq 'ordering' ) {
404
405             my @tags         = $input->multi_param('tag');
406             my @subfields    = $input->multi_param('subfield');
407             my @field_values = $input->multi_param('field_value');
408             my @serials      = $input->multi_param('serial');
409             my @itemid       = $input->multi_param('itemid');
410
411             #Rebuilding ALL the data for items into a hash
412             # parting them on $itemid.
413
414             my %itemhash;
415             my $countdistinct;
416             my $range = scalar(@itemid);
417             for ( my $i = 0 ; $i < $range ; $i++ ) {
418                 unless ( $itemhash{ $itemid[$i] } ) {
419                     $countdistinct++;
420                 }
421                 push @{ $itemhash{ $itemid[$i] }->{'tags'} },         $tags[$i];
422                 push @{ $itemhash{ $itemid[$i] }->{'subfields'} },    $subfields[$i];
423                 push @{ $itemhash{ $itemid[$i] }->{'field_values'} }, $field_values[$i];
424             }
425             foreach my $item ( keys %itemhash ) {
426                 my $xml = TransformHtmlToXml(
427                     $itemhash{$item}->{'tags'},
428                     $itemhash{$item}->{'subfields'},
429                     $itemhash{$item}->{'field_values'},
430                     undef,
431                     undef,
432                     'ITEM'
433                 );
434                 my $record = MARC::Record::new_from_xml( $xml, 'UTF-8' );
435                 my ( $barcodefield, $barcodesubfield ) = GetMarcFromKohaField('items.barcode');
436                 next unless ( defined $barcodefield && defined $barcodesubfield );
437                 my $barcode = $record->subfield( $barcodefield, $barcodesubfield ) || '';
438                 my $aBpref  = C4::Context->preference('autoBarcode');
439                 if ( $barcode eq '' && $aBpref ne 'OFF' ) {
440                     my $barcodeobj;
441                     if ( $aBpref eq 'hbyymmincr' ) {
442                         my ( $homebranchfield, $homebranchsubfield ) = GetMarcFromKohaField('items.homebranch');
443                         my $homebranch = $record->subfield( $homebranchfield, $homebranchsubfield );
444                         $barcodeobj = C4::Barcodes->new( $aBpref, $homebranch );
445                     } else {
446                         $barcodeobj = C4::Barcodes->new($aBpref);
447                     }
448                     $barcode = $barcodeobj->value();
449                     $record->field($barcodefield)->delete_subfield( code => $barcodesubfield );
450                     $record->field($barcodefield)->add_subfields( $barcodesubfield => $barcode );
451                 }
452                 my ( $biblionumber, $bibitemnum, $itemnumber ) = AddItemFromMarc( $record, $$orderinfo{biblionumber} );
453                 $order->add_item($itemnumber);
454             }
455         }
456
457     }
458
459     if ( C4::Context->preference("AcquisitionLog") && $basketno ) {
460         my $modified = Koha::Acquisition::Baskets->find($basketno);
461         logaction(
462             'ACQUISITIONS',
463             'MODIFY_BASKET',
464             $basketno,
465             to_json( $modified->unblessed )
466         );
467     }
468
469     my $booksellerid = $$orderinfo{booksellerid};
470     if ( my $import_batch_id = $input->param('import_batch_id') ) {
471         print $input->redirect(
472             "/cgi-bin/koha/acqui/addorderiso2709.pl?import_batch_id=$import_batch_id&basketno=$basketno&booksellerid=$booksellerid"
473         );
474     } elsif ( defined $orderinfo->{invoiceid} ) {
475         print $input->redirect( "/cgi-bin/koha/acqui/parcel.pl?invoiceid=" . $orderinfo->{invoiceid} );
476     } else {
477         print $input->redirect("/cgi-bin/koha/acqui/basket.pl?basketno=$basketno");
478     }
479
480 } else {
481     my $basketno = $input->param('basketno');
482     print $input->redirect("/cgi-bin/koha/acqui/basket.pl?basketno=$basketno");
483     exit;
484 }
485