# valid parameters with default values
my %valid_params = (
- from_date => DateTime->new( day => 1, month => DateTime->today_local->month, year => DateTime->today_local->year)->subtract(months => 1)->to_kivitendo,
- to_date => DateTime->last_day_of_month(month => DateTime->today_local->month, year => DateTime->today_local->year)->subtract(months => 1)->to_kivitendo,
- customernumbers => [],
- part_id => undef,
- project_id => undef,
- rounding => 1,
- link_order => 0,
+ from_date => DateTime->new( day => 1, month => DateTime->today_local->month, year => DateTime->today_local->year)->subtract(months => 1)->to_kivitendo,
+ to_date => DateTime->last_day_of_month(month => DateTime->today_local->month, year => DateTime->today_local->year)->subtract(months => 1)->to_kivitendo,
+ customernumbers => [],
+ override_part_id => undef,
+ default_part_id => undef,
+ override_project_id => undef,
+ default_project_id => undef,
+ rounding => 1,
+ link_order => 0,
);
# check part
- if ($self->params->{part_id} && !SL::DB::Manager::Part->find_by(id => $self->params->{part_id},
- or => [obsolete => undef, obsolete => 0])) {
- die 'No valid part found by given part id';
+ if ($self->params->{override_part_id} && !SL::DB::Manager::Part->find_by(id => $self->params->{override_part_id},
+ or => [obsolete => undef, obsolete => 0])) {
+ die 'No valid part found by given override part id';
+ }
+ if ($self->params->{default_part_id} && !SL::DB::Manager::Part->find_by(id => $self->params->{default_part_id},
+ or => [obsolete => undef, obsolete => 0])) {
+ die 'No valid part found by given default part id';
}
# check project
- if ($self->params->{project_id} && !SL::DB::Manager::Project->find_by(id => $self->params->{project_id},
- active => 1, valid => 1)) {
- die 'No valid project found by given project id';
+ if ($self->params->{override_project_id} && !SL::DB::Manager::Project->find_by(id => $self->params->{override_project_id},
+ active => 1, valid => 1)) {
+ die 'No valid project found by given override project id';
+ }
+ if ($self->params->{default_project_id} && !SL::DB::Manager::Project->find_by(id => $self->params->{default_project_id},
+ active => 1, valid => 1)) {
+ die 'No valid project found by given default project id';
}
return $self->params;
push @{ $time_recordings_by_customer_id{$_->customer_id} }, $_ for @$time_recordings;
my %convert_params = (
- rounding => $self->params->{rounding},
- default_part_id => $self->params->{part_id},
+ rounding => $self->params->{rounding},
+ override_part_id => $self->params->{override_part_id},
+ default_part_id => $self->params->{default_part_id},
);
my @donumbers;
my %convert_params = (
rounding => $self->params->{rounding},
- default_part_id => $self->params->{part_id},
+ override_part_id => $self->params->{override_part_id},
+ default_part_id => $self->params->{default_part_id},
);
my @donumbers;
if (!$tr->order_id) {
# check project
my $project_id;
- #$project_id = $self->override_project_id;
- $project_id = $self->params->{project_id};
+ $project_id = $self->params->{override_project_id};
$project_id ||= $tr->project_id;
- #$project_id ||= $self->default_project_id;
+ $project_id ||= $self->params->{default_project_id};
if (!$project_id) {
$self->log_error('searching related order failed for time recording id ' . $tr->id . ' : no project id');
# check part
my $part_id;
- #$part_id = $self->override_part_id;
+ $part_id = $self->params->{override_part_id};
$part_id ||= $tr->part_id;
- #$part_id ||= $self->default_part_id;
- $part_id ||= $self->params->{part_id};
+ $part_id ||= $self->params->{default_part_id};
if (!$part_id) {
$self->log_error('searching related order failed for time recording id ' . $tr->id . ' : no part id');
return;
}
- if ($tr->project_id && $tr->project_id != ($matching_order->globalproject_id || 0)) {
+ if ($tr->project_id && !$self->params->{override_project_id} && $tr->project_id != ($matching_order->globalproject_id || 0)) {
$self->log_error('searching related order failed for time recording id ' . $tr->id . ' : project of order does not match project of time recording');
return;
}
customernumbers: [c1,22332,334343]
-=item C<part_id>
+=item C<override_part_id>
+
+The part id of a time based service which should be used to
+book the times instead of the parts which are set in the time
+recordings.
+
+=item C<default_part_id>
The part id of a time based service which should be used to
book the times if no part is set in the time recording entry.
Hint: take a look or extend the job CloseProjectsBelongingToClosedSalesOrder for
further automatisation of your organisational needs.
-=item C<project_id>
+=item C<override_project_id>
-Use this project_id instead of the project_id in the time recordings.
+Use this project id instead of the project id in the time recordings to find
+a related order. This is only used if C<link_order> is true.
+
+=item C<default_project_id>
+
+Use this project id if no project id is set in the time recording
+entry. This is only used if C<link_order> is true.
=back
numbers. E.g. (default_/override_)part_number,
(default_/override_)project_number.
-=item * part and project parameters override and default
-
-In the moment, the part id given as parameter is used as the default value.
-This means, it will be used if there is no part in the time recvording entry.
-
-The project id given is used as override parameter. It overrides the project
-given in the time recording entry.
-
-To solve this, there should be parameters named override_part_id,
-default_part_id, override_project_id and default_project_id.
-
=back
# - merge same descriptions
#
- my $default_part_id = $params{default_part_id} ? $params{default_part_id}
- : $params{default_partnumber} ? SL::DB::Manager::Part->find_by(partnumber => $params{default_partnumber})->id
- : undef;
+ my $default_part_id = $params{default_part_id} ? $params{default_part_id}
+ : $params{default_partnumber} ? SL::DB::Manager::Part->find_by(partnumber => $params{default_partnumber})->id
+ : undef;
+ my $override_part_id = $params{override_part_id} ? $params{override_part_id}
+ : $params{override_partnumber} ? SL::DB::Manager::Part->find_by(partnumber => $params{override_partnumber})->id
+ : undef;
# check parts and collect entries
my %part_by_part_id;
foreach my $source (@$sources) {
next if !$source->duration;
- my $part_id = $source->part_id ? $source->part_id
- : $default_part_id ? $default_part_id
- : undef;
+ my $part_id = $override_part_id;
+ $part_id ||= $source->part_id;
+ $part_id ||= $default_part_id;
die 'article not found for entry "' . $source->displayable_times . '"' if !$part_id;
given as C<$sources>. All time recording entries must belong to the same
customer. Time recordings are sorted by article and date. For each article
a new delivery order item is created. If no article is associated with an
-entry, a default article will be used.
+entry, a default article will be used. The article given in the time
+recording entry can be overriden.
Entries of the same date (for each article) are summed together and form a
list entry in the long description of the item.
An optional hash reference. If it exists then it is used to set
attributes of the newly created delivery order object.
+=item C<default_part_id>
+
+An optional part id which is used as default value if no part is set
+in the time recording entry.
+
+=item C<default_partnumber>
+
+Like C<default_part_id> but given as partnumber, not as id.
+
+=item C<override_part_id>
+
+An optional part id which is used instead of a value set in the time
+recording entry.
+
+=item C<override_partnumber>
+
+Like C<overrride_part_id> but given as partnumber, not as id.
+
=item C<related_order>
An optional C<SL::DB::Order> object. If it exists then it is used to
-use Test::More tests => 40;
+use Test::More tests => 52;
use strict;
clear_up();
########################################
-# two time recordings, one order linked with project_id
+# two time recordings, one order linked with project_id in time recording entry
########################################
my $part = new_service(partnumber => 'Serv1', unit => 'Std')->save;
my $project = create_project(projectnumber => 'p1', description => 'Project 1');
my %data = (
link_order => 1,
- project_id => $project->id,
from_date => '01.01.2021',
to_date => '30.04.2021',
);
########################################
-# two time recordings, one order linked with project_id
+# two time recordings, one order linked with project_id in time recording entry
# unit in order is 'min', but part is 'Std'
########################################
$part = new_service(partnumber => 'Serv1', unit => 'Std')->save;
part => $part,
)->save;
-# two time recordings, one order linked with project_id
%data = (
link_order => 1,
- project_id => $project->id,
from_date => '01.04.2021',
to_date => '30.04.2021',
);
########################################
-# two time recordings, one order linked with project_id
+# two time recordings, one order linked with project_id in time recording entry
# unit in order is 'Std', but part is 'min'
########################################
$part = new_service(partnumber => 'Serv1', unit => 'min')->save;
part => $part,
)->save;
-# two time recordings, one order linked with project_id
%data = (
link_order => 1,
- project_id => $project->id,
from_date => '01.04.2021',
to_date => '30.04.2021',
);
clear_up();
+########################################
+# override project and part
+########################################
+$part = new_service(partnumber => 'Serv1', unit => 'Std')->save;
+my $part2 = new_service(partnumber => 'Serv2', unit => 'min')->save;
+$project = create_project(projectnumber => 'p1', description => 'Project 1');
+my $project2 = create_project(projectnumber => 'p2', description => 'Project 2');
+$customer = new_customer()->save;
+
+$sales_order = create_sales_order(
+ save => 1,
+ customer => $customer,
+ globalproject => $project,
+ taxincluded => 0,
+ orderitems => [ create_order_item(part => $part, qty => 180, unit => 'min', sellprice => 70), ]
+);
+my $sales_order2 = create_sales_order(
+ save => 1,
+ customer => $customer,
+ globalproject => $project,
+ taxincluded => 0,
+ orderitems => [ create_order_item(part => $part2, qty => 180, unit => 'min', sellprice => 70), ]
+);
+
+new_time_recording(
+ start_time => DateTime->new(year => 2021, month => 4, day => 19, hour => 10, minute => 10),
+ end_time => DateTime->new(year => 2021, month => 4, day => 19, hour => 11, minute => 10),
+ customer => $customer,
+ project => $project,
+ part => $part,
+)->save;
+new_time_recording(
+ start_time => DateTime->new(year => 2021, month => 4, day => 19, hour => 11, minute => 10),
+ end_time => DateTime->new(year => 2021, month => 4, day => 19, hour => 12, minute => 10),
+ customer => $customer,
+ project => $project2,
+ part => $part2,
+)->save;
+new_time_recording(
+ start_time => DateTime->new(year => 2021, month => 4, day => 19, hour => 12, minute => 10),
+ end_time => DateTime->new(year => 2021, month => 4, day => 19, hour => 13, minute => 10),
+ customer => $customer,
+)->save;
+
+%data = (
+ link_order => 1,
+ from_date => '01.04.2021',
+ to_date => '30.04.2021',
+ override_part_id => $part->id,
+ override_project_id => $project->id,
+);
+$db_obj = SL::DB::BackgroundJob->new();
+$db_obj->set_data(%data);
+$job = SL::BackgroundJob::ConvertTimeRecordings->new;
+$ret = $job->run($db_obj);
+
+$linked_dos = $sales_order->linked_records(to => 'DeliveryOrder');
+is($linked_dos->[0]->globalproject_id, $project->id, 'overriden part and project: project in delivery order');
+
+$linked_items = $sales_order->items->[0]->linked_records(to => 'DeliveryOrderItem');
+is($linked_items->[0]->qty*1, 3, 'overriden part and project: qty in delivery order');
+is($linked_items->[0]->base_qty*1, 3, 'overriden part and project: base_qty in delivery order');
+is($linked_items->[0]->parts_id, $part->id, 'overriden part and project: part id');
+
+my $linked_dos2 = $sales_order2->linked_records(to => 'DeliveryOrder');
+is(scalar @$linked_dos2, 0, 'overriden part and project: no delivery order for unused order');
+
+# reload order and orderitems to get changes to deliverd and ship
+Rose::DB::Object::Helpers::forget_related($sales_order, 'orderitems');
+$sales_order->load;
+Rose::DB::Object::Helpers::forget_related($sales_order2, 'orderitems');
+$sales_order2->load;
+
+is($sales_order ->items->[0]->ship||0, 180, 'overriden part and project: ship in related order');
+is($sales_order2->items->[0]->ship||0, 0, 'overriden part and project: ship in not related order');
+
+clear_up();
+
+
+########################################
+# default project and part
+########################################
+$part = new_service(partnumber => 'Serv1', unit => 'Std')->save;
+$project = create_project(projectnumber => 'p1', description => 'Project 1');
+$customer = new_customer()->save;
+
+$sales_order = create_sales_order(
+ save => 1,
+ customer => $customer,
+ globalproject => $project,
+ taxincluded => 0,
+ orderitems => [ create_order_item(part => $part, qty => 180, unit => 'min', sellprice => 70), ]
+);
+
+new_time_recording(
+ start_time => DateTime->new(year => 2021, month => 4, day => 19, hour => 10, minute => 10),
+ end_time => DateTime->new(year => 2021, month => 4, day => 19, hour => 11, minute => 40),
+ customer => $customer,
+)->save;
+
+%data = (
+ link_order => 1,
+ from_date => '01.04.2021',
+ to_date => '30.04.2021',
+ default_part_id => $part->id,
+ default_project_id => $project->id,
+);
+$db_obj = SL::DB::BackgroundJob->new();
+$db_obj->set_data(%data);
+$job = SL::BackgroundJob::ConvertTimeRecordings->new;
+$ret = $job->run($db_obj);
+
+$linked_dos = $sales_order->linked_records(to => 'DeliveryOrder');
+is($linked_dos->[0]->globalproject_id, $project->id, 'default and project: project in delivery order');
+
+$linked_items = $sales_order->items->[0]->linked_records(to => 'DeliveryOrderItem');
+is($linked_items->[0]->qty*1, 1.5, 'default part and project: qty in delivery order');
+is($linked_items->[0]->base_qty*1, 1.5, 'default part and project: base_qty in delivery order');
+is($linked_items->[0]->parts_id, $part->id, 'default part and project: part id');
+
+# reload order and orderitems to get changes to deliverd and ship
+Rose::DB::Object::Helpers::forget_related($sales_order, 'orderitems');
+$sales_order->load;
+
+is($sales_order->items->[0]->ship*1, 90, 'default part and project: ship in related order');
+
+clear_up();
+
+
########################################
# check rounding
########################################
#####
%data = (
- part_id => '123',
+ override_part_id => '123',
);
$db_obj = SL::DB::BackgroundJob->new();
$err_msg = '';
eval { $ret = $job->run($db_obj); 1; } or do {$err_msg = $@};
-ok($err_msg =~ '^No valid part found by given part id', 'invalid part id detected');
+ok($err_msg =~ '^No valid part found by given override part id', 'invalid part id detected');
#####
$part = new_service(partnumber => 'Serv1', unit => 'Std', obsolete => 1)->save;
%data = (
- part_id => $part->id,
+ override_part_id => $part->id,
);
$db_obj = SL::DB::BackgroundJob->new();
$err_msg = '';
eval { $ret = $job->run($db_obj); 1; } or do {$err_msg = $@};
-ok($err_msg =~ '^No valid part found by given part id', 'obsolete part detected');
+ok($err_msg =~ '^No valid part found by given override part id', 'obsolete part detected');
#####
%data = (
- project_id => 123,
+ override_project_id => 123,
);
$db_obj = SL::DB::BackgroundJob->new();
$err_msg = '';
eval { $ret = $job->run($db_obj); 1; } or do {$err_msg = $@};
-ok($err_msg =~ '^No valid project found by given project id', 'invalid project id detected');
+ok($err_msg =~ '^No valid project found by given override project id', 'invalid project id detected');
#####
$project = create_project(projectnumber => 'p1', description => 'Project 1', valid => 0)->save;
%data = (
- project_id => $project->id,
+ override_project_id => $project->id,
);
$db_obj = SL::DB::BackgroundJob->new();
$err_msg = '';
eval { $ret = $job->run($db_obj); 1; } or do {$err_msg = $@};
-ok($err_msg =~ '^No valid project found by given project id', 'invalid project detected');
+ok($err_msg =~ '^No valid project found by given override project id', 'invalid project detected');
#####