|
package SL::DB::Order;
|
|
|
|
use utf8;
|
|
use strict;
|
|
|
|
use Carp;
|
|
use DateTime;
|
|
use List::Util qw(max);
|
|
use List::MoreUtils qw(any);
|
|
|
|
use SL::DBUtils ();
|
|
use SL::DB::MetaSetup::Order;
|
|
use SL::DB::Manager::Order;
|
|
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::DB::Helper::Payment qw(forex);
|
|
use SL::DB::Helper::RecordLink qw(RECORD_ID RECORD_TYPE_REF RECORD_ITEM_ID RECORD_ITEM_TYPE_REF);
|
|
use SL::Locale::String qw(t8);
|
|
use SL::RecordLinks;
|
|
use Rose::DB::Object::Helpers qw(as_tree strip);
|
|
|
|
__PACKAGE__->meta->add_relationship(
|
|
orderitems => {
|
|
type => 'one to many',
|
|
class => 'SL::DB::OrderItem',
|
|
column_map => { id => 'trans_id' },
|
|
manager_args => {
|
|
with_objects => [ 'part' ]
|
|
}
|
|
},
|
|
periodic_invoices_config => {
|
|
type => 'one to one',
|
|
class => 'SL::DB::PeriodicInvoicesConfig',
|
|
column_map => { id => 'oe_id' },
|
|
},
|
|
custom_shipto => {
|
|
type => 'one to one',
|
|
class => 'SL::DB::Shipto',
|
|
column_map => { id => 'trans_id' },
|
|
query_args => [ module => 'OE' ],
|
|
},
|
|
exchangerate_obj => {
|
|
type => 'one to one',
|
|
class => 'SL::DB::Exchangerate',
|
|
column_map => { currency_id => 'currency_id', transdate => 'transdate' },
|
|
},
|
|
phone_notes => {
|
|
type => 'one to many',
|
|
class => 'SL::DB::Note',
|
|
column_map => { id => 'trans_id' },
|
|
query_args => [ trans_module => 'oe' ],
|
|
manager_args => {
|
|
with_objects => [ 'employee' ],
|
|
sort_by => 'notes.itime',
|
|
}
|
|
},
|
|
order_version => {
|
|
type => 'one to many',
|
|
class => 'SL::DB::OrderVersion',
|
|
column_map => { id => 'oe_id' },
|
|
},
|
|
);
|
|
|
|
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_ord_quo_number');
|
|
__PACKAGE__->before_save('_before_save_create_new_project');
|
|
__PACKAGE__->before_save('_before_save_remove_empty_custom_shipto');
|
|
__PACKAGE__->before_save('_before_save_set_custom_shipto_module');
|
|
__PACKAGE__->after_save('_after_save_link_records');
|
|
|
|
# hooks
|
|
|
|
sub _before_save_set_ord_quo_number {
|
|
my ($self) = @_;
|
|
|
|
# ordnumber is 'NOT NULL'. Therefore make sure it's always set to at
|
|
# least an empty string, even if we're saving a quotation.
|
|
$self->ordnumber('') if !$self->ordnumber;
|
|
|
|
my $field = $self->quotation ? 'quonumber' : 'ordnumber';
|
|
$self->create_trans_number if !$self->$field;
|
|
|
|
return 1;
|
|
}
|
|
sub _before_save_create_new_project {
|
|
my ($self) = @_;
|
|
|
|
# force new project, if not set yet
|
|
if ($::instance_conf->get_order_always_project && !$self->globalproject_id && ($self->type eq 'sales_order')) {
|
|
|
|
die t8("Error while creating project with project number of new order number, project number #1 already exists!", $self->ordnumber)
|
|
if SL::DB::Manager::Project->find_by(projectnumber => $self->ordnumber);
|
|
|
|
eval {
|
|
my $new_project = SL::DB::Project->new(
|
|
projectnumber => $self->ordnumber,
|
|
description => $self->customer->name,
|
|
customer_id => $self->customer->id,
|
|
active => 1,
|
|
project_type_id => $::instance_conf->get_project_type_id,
|
|
project_status_id => $::instance_conf->get_project_status_id,
|
|
);
|
|
$new_project->save;
|
|
$self->globalproject_id($new_project->id);
|
|
} or die t8('Could not create new project #1', $@);
|
|
}
|
|
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('OE') if $self->custom_shipto;
|
|
|
|
return 1;
|
|
}
|
|
|
|
sub _after_save_link_records {
|
|
my ($self) = @_;
|
|
|
|
my @allowed_record_sources = qw(SL::DB::Reclamation SL::DB::Order);
|
|
my @allowed_item_sources = qw(SL::DB::ReclamationItem SL::DB::OrderItem);
|
|
|
|
SL::DB::Helper::RecordLink::link_records(
|
|
$self,
|
|
\@allowed_record_sources,
|
|
\@allowed_item_sources,
|
|
close_source_quotations => 1,
|
|
);
|
|
}
|
|
|
|
|
|
# methods
|
|
|
|
sub items { goto &orderitems; }
|
|
sub add_items { goto &add_orderitems; }
|
|
sub record_number { goto &number; }
|
|
|
|
sub type {
|
|
my $self = shift;
|
|
|
|
return 'sales_order' if $self->customer_id && ! $self->quotation;
|
|
return 'purchase_order' if $self->vendor_id && ! $self->quotation;
|
|
return 'sales_quotation' if $self->customer_id && $self->quotation;
|
|
return 'request_quotation' if $self->vendor_id && $self->quotation;
|
|
|
|
return;
|
|
}
|
|
|
|
sub is_type {
|
|
return shift->type eq shift;
|
|
}
|
|
|
|
sub deliverydate {
|
|
# oe doesn't have deliverydate, but it does have reqdate.
|
|
# But this has a different meaning for sales quotations.
|
|
# deliverydate can be used to determine tax if tax_point isn't set.
|
|
|
|
return $_[0]->reqdate if $_[0]->type ne 'sales_quotation';
|
|
}
|
|
|
|
sub effective_tax_point {
|
|
my ($self) = @_;
|
|
|
|
return $self->tax_point || $self->deliverydate || $self->transdate;
|
|
}
|
|
|
|
sub displayable_type {
|
|
my $type = shift->type;
|
|
|
|
return $::locale->text('Sales quotation') if $type eq 'sales_quotation';
|
|
return $::locale->text('Request quotation') if $type eq 'request_quotation';
|
|
return $::locale->text('Sales Order') if $type eq 'sales_order';
|
|
return $::locale->text('Purchase Order') if $type eq 'purchase_order';
|
|