1 package Koha::Acquisition::Order;
3 # This file is part of Koha.
5 # Koha is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
10 # Koha is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with Koha; if not, see <http://www.gnu.org/licenses>.
22 use C4::Biblio qw( DelBiblio );
24 use Koha::Acquisition::Baskets;
25 use Koha::Acquisition::Funds;
26 use Koha::Acquisition::Invoices;
27 use Koha::Acquisition::Order::Claims;
29 use Koha::DateUtils qw( dt_from_string );
30 use Koha::Exceptions::Object;
34 use Koha::Subscriptions;
36 use base qw(Koha::Object);
40 Koha::Acquisition::Order Object class
48 Overloaded I<new> method for backwards compatibility.
53 my ( $self, $params ) = @_;
55 my $schema = Koha::Database->new->schema;
56 my @columns = $schema->source('Aqorder')->columns;
59 { map { exists $params->{$_} ? ( $_ => $params->{$_} ) : () } @columns };
60 return $self->SUPER::new($values);
65 Overloaded I<store> method for backwards compatibility.
72 my $schema = Koha::Database->new->schema;
73 # Override quantity for standing orders
74 $self->quantity(1) if ( $self->basketno && $schema->resultset('Aqbasket')->find( $self->basketno )->is_standing );
76 # if these parameters are missing, we can't continue
77 for my $key (qw( basketno quantity biblionumber budget_id )) {
78 croak "Cannot insert order: Mandatory parameter $key is missing"
82 if (not defined $self->{created_by}) {
83 my $userenv = C4::Context->userenv;
85 $self->created_by($userenv->{number});
89 $self->quantityreceived(0) unless $self->quantityreceived;
90 $self->entrydate(dt_from_string) unless $self->entrydate;
92 $self->ordernumber(undef) unless $self->ordernumber;
93 $self = $self->SUPER::store( $self );
95 unless ( $self->parent_ordernumber ) {
96 $self->set( { parent_ordernumber => $self->ordernumber } );
97 $self = $self->SUPER::store( $self );
106 { [ reason => $reason,
107 delete_biblio => $delete_biblio ]
111 This method marks an order as cancelled, optionally using the I<reason> parameter.
112 As the order is cancelled, the (eventual) items linked to it are removed.
113 If I<delete_biblio> is passed, it will try to remove the linked biblio.
115 If either the items or biblio removal fails, an error message is added to the object
116 so the caller can take appropriate actions.
121 my ($self, $params) = @_;
123 my $delete_biblio = $params->{delete_biblio};
124 my $reason = $params->{reason};
126 # Delete the related items
127 my $items = $self->items;
128 while ( my $item = $items->next ) {
129 my $deleted = $item->safe_delete;
130 unless ( ref($deleted) eq 'Koha::Item' ) {
133 message => 'error_delitem',
134 payload => { item => $item, reason => $deleted }
140 my $biblio = $self->biblio;
141 if ( $biblio and $delete_biblio ) {
144 $biblio->active_orders->search(
145 { ordernumber => { '!=' => $self->ordernumber } }
147 and $biblio->subscriptions->count == 0
148 and $biblio->items->count == 0
152 my $error = DelBiblio( $biblio->id );
155 message => 'error_delbiblio',
156 payload => { biblio => $biblio, reason => $error }
164 if ( $biblio->active_orders->search(
165 { ordernumber => { '!=' => $self->ordernumber } }
167 $message = 'error_delbiblio_active_orders';
169 elsif ( $biblio->subscriptions->count > 0 ) {
170 $message = 'error_delbiblio_subscriptions';
172 else { # $biblio->items->count > 0
173 $message = 'error_delbiblio_items';
179 payload => { biblio => $biblio }
185 # Update order status
188 cancellationreason => $reason,
189 datecancellationprinted => \'NOW()',
190 orderstatus => 'cancelled',
199 $order->add_item( $itemnumber );
201 Link an item to this order.
206 my ( $self, $itemnumber ) = @_;
208 my $schema = Koha::Database->new->schema;
209 my $rs = $schema->resultset('AqordersItem');
210 $rs->create({ ordernumber => $self->ordernumber, itemnumber => $itemnumber });
215 my $basket = $order->basket;
217 Returns the I<Koha::Acquisition::Basket> object for the basket associated
224 my $basket_rs = $self->_result->basket;
225 return Koha::Acquisition::Basket->_new_from_dbic( $basket_rs );
230 my $fund = $order->fund;
232 Returns the I<Koha::Acquisition::Fund> object for the fund (aqbudgets)
233 associated to the order.
239 my $fund_rs = $self->_result->fund;
240 return Koha::Acquisition::Fund->_new_from_dbic( $fund_rs );
245 my $invoice = $order->invoice;
247 Returns the I<Koha::Acquisition::Invoice> object for the invoice associated
250 It returns B<undef> if no linked invoice is found.
256 my $invoice_rs = $self->_result->invoice;
257 return unless $invoice_rs;
258 return Koha::Acquisition::Invoice->_new_from_dbic( $invoice_rs );
263 my $subscription = $order->subscription
265 Returns the I<Koha::Subscription> object for the subscription associated
268 It returns B<undef> if no linked subscription is found.
274 my $subscription_rs = $self->_result->subscription;
275 return unless $subscription_rs;
276 return Koha::Subscription->_new_from_dbic( $subscription_rs );
279 =head3 current_item_level_holds
281 my $holds = $order->current_item_level_holds;
283 Returns the current item-level holds associated to the order. It returns a I<Koha::Holds>
288 sub current_item_level_holds {
291 my $items_rs = $self->_result->aqorders_items;
292 my @item_numbers = $items_rs->get_column('itemnumber')->all;
293 my $biblio = $self->biblio;
295 unless ( $biblio and @item_numbers ) {
296 return Koha::Holds->new->empty;
299 return $biblio->current_holds->search(
302 -in => \@item_numbers
310 my $items = $order->items
312 Returns the items associated to the order.
318 # aqorders_items is not a join table
319 # There is no FK on items (may have been deleted)
320 my $items_rs = $self->_result->aqorders_items;
321 my @itemnumbers = $items_rs->get_column( 'itemnumber' )->all;
322 return Koha::Items->search({ itemnumber => \@itemnumbers });
327 my $biblio = $order->biblio
329 Returns the bibliographic record associated to the order
335 my $biblio_rs= $self->_result->biblio;
336 return unless $biblio_rs;
337 return Koha::Biblio->_new_from_dbic( $biblio_rs );
342 my $claims = $order->claims
344 Return the claims history for this order
350 my $claims_rs = $self->_result->aqorders_claims;
351 return Koha::Acquisition::Order::Claims->_new_from_dbic( $claims_rs );
356 my $claim = $order->claim
358 Do claim for this order
364 my $claim_rs = $self->_result->create_related('aqorders_claims', {});
365 return Koha::Acquisition::Order::Claim->_new_from_dbic($claim_rs);
370 my $nb_of_claims = $order->claims_count;
372 This is the equivalent of $order->claims->count. Keeping it for retrocompatibilty.
378 return $self->claims->count;
383 my $last_claim_date = $order->claimed_date;
385 This is the equivalent of $order->claims->last->claimed_on. Keeping it for retrocompatibilty.
391 my $last_claim = $self->claims->last;
392 return unless $last_claim;
393 return $last_claim->claimed_on;
398 my $duplicated_order = $order->duplicate_to($basket, [$default_values]);
400 Duplicate an existing order and attach it to a basket. $default_values can be specified as a hashref
401 that contain default values for the different order's attributes.
402 Items will be duplicated as well but barcodes will be set to null.
407 my ( $self, $basket, $default_values ) = @_;
409 $default_values //= {};
410 Koha::Database->schema->txn_do(
412 my $order_info = $self->unblessed;
413 undef $order_info->{ordernumber};
420 datecancellationprinted
429 undef $order_info->{$field};
431 $order_info->{placed_on} = dt_from_string;
432 $order_info->{entrydate} = dt_from_string;
433 $order_info->{orderstatus} = 'new';
434 $order_info->{quantityreceived} = 0;
435 while ( my ( $field, $value ) = each %$default_values ) {
436 $order_info->{$field} = $value;
439 my $userenv = C4::Context->userenv;
440 $order_info->{created_by} = $userenv->{number};
441 $order_info->{basketno} = $basket->basketno;
443 $new_order = Koha::Acquisition::Order->new($order_info)->store;
445 if ( ! $self->subscriptionid && $self->basket->effective_create_items eq 'ordering') { # Do copy items if not a subscription order AND if items are created on ordering
446 my $items = $self->items;
447 while ( my ($item) = $items->next ) {
448 my $item_info = $item->unblessed;
449 undef $item_info->{itemnumber};
450 undef $item_info->{barcode};
451 my $new_item = Koha::Item->new($item_info)->store;
452 $new_order->add_item( $new_item->itemnumber );
460 =head3 to_api_mapping
462 This method returns the mapping for representing a Koha::Acquisition::Order object
469 basketno => 'basket_id',
470 biblionumber => 'biblio_id',
471 budget_id => 'fund_id',
472 budgetdate => undef, # unused
473 cancellationreason => 'cancellation_reason',
474 claimed_date => 'last_claim_date',
475 datecancellationprinted => 'cancellation_date',
476 datereceived => 'date_received',
477 discount => 'discount_rate',
478 entrydate => 'entry_date',
479 freight => 'shipping_cost',
480 invoiceid => 'invoice_id',
481 line_item_id => undef, # EDIFACT related
482 listprice => 'list_price',
483 order_internalnote => 'internal_note',
484 order_vendornote => 'vendor_note',
485 ordernumber => 'order_id',
486 orderstatus => 'status',
487 parent_ordernumber => 'parent_order_id',
488 purchaseordernumber => undef, # obsolete
489 quantityreceived => 'quantity_received',
490 replacementprice => 'replacement_price',
491 sort1 => 'statistics_1',
492 sort1_authcat => 'statistics_1_authcat',
493 sort2 => 'statistics_2',
494 sort2_authcat => 'statistics_2_authcat',
495 subscriptionid => 'subscription_id',
496 suppliers_reference_number => undef, # EDIFACT related
497 suppliers_reference_qualifier => undef, # EDIFACT related
498 suppliers_report => undef, # EDIFACT related
499 tax_rate_bak => undef, # unused
500 tax_value_bak => undef, # unused
501 uncertainprice => 'uncertain_price',
502 unitprice => 'unit_price',
503 unitprice_tax_excluded => 'unit_price_tax_excluded',
504 unitprice_tax_included => 'unit_price_tax_included'
508 =head2 Internal methods