|  | package SL::DB::Reclamation;
 | 
  
    |  | 
 | 
  
    |  | use utf8;
 | 
  
    |  | use strict;
 | 
  
    |  | 
 | 
  
    |  | use Carp;
 | 
  
    |  | use DateTime;
 | 
  
    |  | use List::Util qw(max sum0);
 | 
  
    |  | use List::MoreUtils qw(any);
 | 
  
    |  | 
 | 
  
    |  | use SL::DB::MetaSetup::Reclamation;
 | 
  
    |  | use SL::DB::Manager::Reclamation;
 | 
  
    |  | use SL::DB::Helper::Attr;
 | 
  
    |  | use SL::DB::Helper::AttrHTML;
 | 
  
    |  | use SL::DB::Helper::AttrSorted;
 | 
  
    |  | use SL::DB::Helper::FlattenToForm;
 | 
  
    |  | use SL::DB::Helper::LinkedRecords;
 | 
  
    |  | use SL::DB::Helper::PriceTaxCalculator;
 | 
  
    |  | use SL::DB::Helper::PriceUpdater;
 | 
  
    |  | use SL::DB::Helper::TransNumberGenerator;
 | 
  
    |  | use SL::Locale::String qw(t8);
 | 
  
    |  | use SL::RecordLinks;
 | 
  
    |  | use Rose::DB::Object::Helpers qw(as_tree);
 | 
  
    |  | 
 | 
  
    |  | __PACKAGE__->meta->add_relationship(
 | 
  
    |  | 
 | 
  
    |  |   reclamation_items => {
 | 
  
    |  |     type         => 'one to many',
 | 
  
    |  |     class        => 'SL::DB::ReclamationItem',
 | 
  
    |  |     column_map   => { id => 'reclamation_id' },
 | 
  
    |  |     manager_args => {
 | 
  
    |  |       with_objects => [ 'part', 'reason' ]
 | 
  
    |  |     }
 | 
  
    |  |   },
 | 
  
    |  |   custom_shipto            => {
 | 
  
    |  |     type                   => 'one to one',
 | 
  
    |  |     class                  => 'SL::DB::Shipto',
 | 
  
    |  |     column_map             => { id => 'trans_id' },
 | 
  
    |  |     query_args             => [ module => 'Reclamation' ],
 | 
  
    |  |   },
 | 
  
    |  |   exchangerate_obj         => {
 | 
  
    |  |     type                   => 'one to one',
 | 
  
    |  |     class                  => 'SL::DB::Exchangerate',
 | 
  
    |  |     column_map             => { currency_id => 'currency_id', transdate => 'transdate' },
 | 
  
    |  |   },
 | 
  
    |  | );
 | 
  
    |  | 
 | 
  
    |  | SL::DB::Helper::Attr::make(__PACKAGE__, daily_exchangerate => 'numeric');
 | 
  
    |  | 
 | 
  
    |  | __PACKAGE__->meta->initialize;
 | 
  
    |  | 
 | 
  
    |  | __PACKAGE__->attr_html('notes');
 | 
  
    |  | __PACKAGE__->attr_sorted('items');
 | 
  
    |  | 
 | 
  
    |  | __PACKAGE__->before_save('_before_save_set_record_number');
 | 
  
    |  | __PACKAGE__->before_save('_before_save_remove_empty_custom_shipto');
 | 
  
    |  | __PACKAGE__->before_save('_before_save_set_custom_shipto_module');
 | 
  
    |  | 
 | 
  
    |  | # hooks
 | 
  
    |  | 
 | 
  
    |  | sub _before_save_set_record_number {
 | 
  
    |  |   my ($self) = @_;
 | 
  
    |  | 
 | 
  
    |  |   $self->create_trans_number if !$self->record_number;
 | 
  
    |  | 
 | 
  
    |  |   return 1;
 | 
  
    |  | }
 | 
  
    |  | 
 | 
  
    |  | sub _before_save_remove_empty_custom_shipto {
 | 
  
    |  |   my ($self) = @_;
 | 
  
    |  | 
 | 
  
    |  |   $self->custom_shipto(undef) if $self->custom_shipto && $self->custom_shipto->is_empty;
 | 
  
    |  | 
 | 
  
    |  |   return 1;
 | 
  
    |  | }
 | 
  
    |  | 
 | 
  
    |  | sub _before_save_set_custom_shipto_module {
 | 
  
    |  |   my ($self) = @_;
 | 
  
    |  | 
 | 
  
    |  |   $self->custom_shipto->module('Reclamation') if $self->custom_shipto;
 | 
  
    |  | 
 | 
  
    |  |   return 1;
 | 
  
    |  | }
 | 
  
    |  | 
 | 
  
    |  | # methods
 | 
  
    |  | 
 | 
  
    |  | sub items { goto &reclamation_items; }
 | 
  
    |  | sub add_items { goto &add_reclamation_items; }
 | 
  
    |  | sub record_items { goto &reclamation_items; }
 | 
  
    |  | 
 | 
  
    |  | sub type {
 | 
  
    |  |   my ($self) = @_;
 | 
  
    |  | 
 | 
  
    |  |   return 'sales_reclamation'    if $self->customer_id;
 | 
  
    |  |   return 'purchase_reclamation' if $self->vendor_id;
 | 
  
    |  | 
 | 
  
    |  |   return;
 | 
  
    |  | }
 | 
  
    |  | 
 | 
  
    |  | sub is_type {
 | 
  
    |  |   my ($self, $type) = @_;
 | 
  
    |  |   return $self->type eq $type;
 | 
  
    |  | }
 | 
  
    |  | 
 | 
  
    |  | sub effective_tax_point {
 | 
  
    |  |   my ($self) = @_;
 | 
  
    |  | 
 | 
  
    |  |   return $self->tax_point || $self->reqdate || $self->transdate;
 | 
  
    |  | }
 | 
  
    |  | 
 | 
  
    |  | sub displayable_type {
 | 
  
    |  |   my $type = shift->type;
 | 
  
    |  | 
 | 
  
    |  |   return $::locale->text('Sales Reclamation')    if $type eq 'sales_reclamation';
 | 
  
    |  |   return $::locale->text('Purchase Reclamation') if $type eq 'purchase_reclamation';
 | 
  
    |  | 
 | 
  
    |  |   die 'invalid type';
 | 
  
    |  | }
 | 
  
    |  | 
 | 
  
    |  | sub displayable_name {
 | 
  
    |  |   join ' ', grep $_, map $_[0]->$_, qw(displayable_type record_number);
 | 
  
    |  | };
 | 
  
    |  | 
 | 
  
    |  | sub is_sales {
 | 
  
    |  |   croak 'not an accessor' if @_ > 1;
 | 
  
    |  |   return !!shift->customer_id;
 | 
  
    |  | }
 | 
  
    |  | 
 | 
  
    |  | sub daily_exchangerate {
 | 
  
    |  |   my ($self, $val) = @_;
 | 
  
    |  | 
 | 
  
    |  |   return 1 if $self->currency_id == $::instance_conf->get_currency_id;
 | 
  
    |  | 
 | 
  
    |  |   my $rate = (any { $self->is_type($_) } qw(sales_reclamation))    ? 'buy'
 | 
  
    |  |            : (any { $self->is_type($_) } qw(purchase_reclamation)) ? 'sell'
 | 
  
    |  |            : undef;
 | 
  
    |  |   return if !$rate;
 | 
  
    |  | 
 | 
  
    |  |   if (defined $val) {
 | 
  
    |  |     croak t8('exchange rate has to be positive') if $val <= 0;
 | 
  
    |  |     if (!$self->exchangerate_obj) {
 | 
  
    |  |       $self->exchangerate_obj(SL::DB::Exchangerate->new(
 | 
  
    |  |         currency_id => $self->currency_id,
 | 
  
    |  |         transdate   => $self->transdate,
 | 
  
    |  |         $rate       => $val,
 | 
  
    |  |       ));
 | 
  
    |  |     } elsif (!defined $self->exchangerate_obj->$rate) {
 | 
  
    |  |       $self->exchangerate_obj->$rate($val);
 | 
  
    |  |     } else {
 | 
  
    |  |       croak t8('exchange rate already exists, no update allowed');
 | 
  
    |  |     }
 | 
  
    |  |   }
 | 
  
    |  |   return $self->exchangerate_obj->$rate if $self->exchangerate_obj;
 | 
  
    |  | }
 | 
  
    |  | 
 | 
  
    |  | sub taxes {
 | 
  
    |  |   my ($self) = @_;
 | 
  
    |  |   # add taxes to recalmation
 | 
  
    |  |   my %pat = $self->calculate_prices_and_taxes();
 | 
  
    |  |   my @taxes;
 | 
  
    |  |   foreach my $tax_id (keys %{ $pat{taxes_by_tax_id} }) {
 | 
  
    |  |     my $netamount = sum0 map { $pat{amounts}->{$_}->{amount} } grep { $pat{amounts}->{$_}->{tax_id} == $tax_id } keys %{ $pat{amounts} };
 | 
  
    |  |     push(@taxes, { amount    => $pat{taxes_by_tax_id}->{$tax_id},
 | 
  
    |  |                                 netamount => $netamount,
 | 
  
    |  |                                 tax       => SL::DB::Tax->new(id => $tax_id)->load });
 | 
  
    |  |   }
 | 
  
    |  |   return \@taxes;
 | 
  
    |  | }
 | 
  
    |  | 
 | 
  
    |  | sub displayable_state {
 | 
  
    |  |   my ($self) = @_;
 | 
  
    |  | 
 | 
  
    |  |   return $self->closed ? $::locale->text('closed') : $::locale->text('open');
 | 
  
    |  | }
 | 
  
    |  | 
 | 
  
    |  | sub valid_reclamation_reasons {
 | 
  
    |  |   my ($self) = @_;
 | 
  
    |  | 
 | 
  
    |  |   my $valid_for_type = ($self->type =~ m{sales} ? 'valid_for_sales' : 'valid_for_purchase');
 | 
  
    |  |   return SL::DB::Manager::ReclamationReason->get_all_sorted(
 | 
  
    |  |       where => [  $valid_for_type => 1 ]);
 | 
  
    |  | }
 | 
  
    |  | 
 | 
  
    |  | sub convert_to_order {
 | 
  
    |  |   my ($self, %params) = @_;
 | 
  
    |  | 
 | 
  
    |  |   my $order;
 | 
  
    |  |   $params{destination_type} = $self->is_sales ? 'sales_order'
 | 
  
    |  |                                               : 'purchase_order';
 | 
  
    |  |   if (!$self->db->with_transaction(sub {
 | 
  
    |  |     require SL::DB::Order;
 | 
  
    |  |     $order = SL::DB::Order->new_from($self, %params);
 | 
  
    |  |     $order->save;
 | 
  
    |  |     $self->link_to_record($order);
 | 
  
    |  |     foreach my $item (@{ $order->items }) {
 | 
  
    |  |       foreach (qw(reclamation_item)) {
 | 
  
    |  |         if ($item->{"converted_from_${_}_id"}) {
 | 
  
    |  |           die unless $item->{id};
 | 
  
    |  |           RecordLinks->create_links('dbh'        => $self->db->dbh,
 | 
  
    |  |                                     'mode'       => 'ids',
 | 
  
    |  |                                     'from_table' => 'reclamation_items',
 | 
  
    |  |                                     'from_ids'   => $item->{"converted_from_${_}_id"},
 | 
  
    |  |                                     'to_table'   => 'orderitems',
 | 
  
    |  |                                     'to_id'      => $item->{id},
 | 
  
    |  |           ) || die;
 | 
  
    |  |           delete $item->{"converted_from_${_}_id"};
 | 
  
    |  |         }
 | 
  
    |  |       }
 | 
  
    |  |     }
 | 
  
    |  | 
 | 
  
    |  |     1;
 | 
  
    |  |   })) {
 | 
  
    |  |     return undef;
 | 
  
    |  |   }
 | 
  
    |  | 
 | 
  
    |  |   return $order;
 | 
  
    |  | }
 | 
  
    |  | 
 | 
  
    |  | sub convert_to_delivery_order {
 | 
  
    |  |   my ($self, %params) = @_;
 | 
  
    |  | 
 | 
  
    |  |   my $delivery_order;
 | 
  
    |  |   if (!$self->db->with_transaction(sub {
 | 
  
    |  |     require SL::DB::DeliveryOrder;
 | 
  
    |  |     $delivery_order = SL::DB::DeliveryOrder->new_from($self, %params);
 | 
  
    |  |     $delivery_order->save;
 | 
  
    |  |     $self->link_to_record($delivery_order);
 |