Projekt

Allgemein

Profil

Herunterladen (17,9 KB) Statistiken
| Zweig: | Markierung: | Revision:
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);
# TODO extend link_to_record for items, otherwise long-term no d.r.y.
foreach my $item (@{ $delivery_order->items }) {
foreach (qw(reclamation_items)) {
if ($item->{"converted_from_${_}_id"}) {
die unless $item->{id};
RecordLinks->create_links('dbh' => $self->db->dbh,
'mode' => 'ids',
'from_table' => $_,
'from_ids' => $item->{"converted_from_${_}_id"},
'to_table' => 'delivery_order_items',
'to_id' => $item->{id},
) || die;
delete $item->{"converted_from_${_}_id"};
}
}
}

$self->update_attributes(delivered => 1) unless $::instance_conf->get_shipped_qty_require_stock_out;
1;
})) {
return undef, $self->db->error->db_error->db_error;
}

return $delivery_order, undef;
}

#TODO(Werner): überprüfen ob alle Felder richtig gestetzt werden
sub new_from {
my ($class, $source, %params) = @_;
my %allowed_sources = map { $_ => 1 } qw(
SL::DB::Reclamation
SL::DB::Order
SL::DB::DeliveryOrder
SL::DB::Invoice
SL::DB::PurchaseInvoice
);
unless( $allowed_sources{ref $source} ) {
croak("Unsupported source object type '" . ref($source) . "'");
}
croak("A destination type must be given as parameter") unless $params{destination_type};

my $destination_type = delete $params{destination_type};

my @from_tos = (
#Reclamation
{ from => 'sales_reclamation', to => 'sales_reclamation', abbr => 'srsr', },
{ from => 'purchase_reclamation', to => 'purchase_reclamation', abbr => 'prpr', },
{ from => 'sales_reclamation', to => 'purchase_reclamation', abbr => 'srpr', },
{ from => 'purchase_reclamation', to => 'sales_reclamation', abbr => 'prsr', },
#Order
{ from => 'sales_order', to => 'sales_reclamation', abbr => 'sosr', },
{ from => 'purchase_order', to => 'purchase_reclamation', abbr => 'popr', },
#Delivery Order
{ from => 'sales_delivery_order', to => 'sales_reclamation', abbr => 'sdsr', },
{ from => 'purchase_delivery_order', to => 'purchase_reclamation', abbr => 'pdpr', },
#Invoice
{ from => 'invoice', to => 'sales_reclamation', abbr => 'sisr', },
{ from => 'purchase_invoice', to => 'purchase_reclamation', abbr => 'pipr', },
);
my $from_to = (grep { $_->{from} eq $source->type && $_->{to} eq $destination_type} @from_tos)[0];
if (!$from_to) {
croak("Cannot convert from '" . $source->type . "' to '" . $destination_type . "'");
}

my $is_abbr_any = sub {
any { $from_to->{abbr} eq $_ } @_;
};

my %record_args = (
record_number => undef,
employee => SL::DB::Manager::Employee->current,
closed => 0,
delivered => 0,
transdate => DateTime->today_local,
reqdate => DateTime->today_local->next_workday(),
);
if ( $is_abbr_any->(qw(srsr prpr srpr prsr)) ) { #Reclamation
map { $record_args{$_} = $source->$_ } # {{{ for vim folds
qw(
amount
contact_id
currency_id
customer_id
cv_record_number
delivery_term_id
department_id
exchangerate
globalproject_id
intnotes
language_id
netamount
notes
payment_id
salesman_id
shippingpoint
shipvia
tax_point
taxincluded
taxzone_id