20 |
20 |
}
|
21 |
21 |
use Rose::Object::MakeMethods::Generic (
|
22 |
22 |
'scalar' => [ qw(data) ],
|
23 |
|
'scalar --get_set_init' => [ qw(rounding link_project) ],
|
|
23 |
'scalar --get_set_init' => [ qw(rounding link_order) ],
|
24 |
24 |
);
|
25 |
25 |
|
26 |
26 |
# valid parameters -> better as class members with rose generic set/get
|
... | ... | |
30 |
30 |
customernumbers => '',
|
31 |
31 |
part_id => '',
|
32 |
32 |
rounding => 1,
|
33 |
|
link_project => 0,
|
|
33 |
link_order => 0,
|
34 |
34 |
project_id => '',
|
35 |
35 |
);
|
36 |
36 |
|
... | ... | |
56 |
56 |
# TODO check user input param values - (defaults are assigned later)
|
57 |
57 |
# 1- If there are any customer numbers check if they refer to valid customers
|
58 |
58 |
# otherwise croak and do nothing
|
59 |
|
# 2 .. n Same applies for other params if used at all (rounding -> 0|1 link_project -> 0|1)
|
|
59 |
# 2 .. n Same applies for other params if used at all (rounding -> 0|1 link_order -> 0|1)
|
60 |
60 |
|
61 |
61 |
# from/to date from data. Defaults to begining and end of last month.
|
62 |
62 |
# TODO get/set see above
|
... | ... | |
89 |
89 |
|
90 |
90 |
my @donumbers;
|
91 |
91 |
|
92 |
|
if ($self->data->{link_project}) {
|
|
92 |
if ($self->data->{link_order}) {
|
93 |
93 |
my %time_recordings_by_order_id;
|
94 |
94 |
my %orders_by_order_id;
|
95 |
95 |
foreach my $tr (@$time_recordings) {
|
... | ... | |
126 |
126 |
1
|
127 |
127 |
}
|
128 |
128 |
|
129 |
|
sub init_link_project {
|
|
129 |
sub init_link_order {
|
130 |
130 |
0
|
131 |
131 |
}
|
132 |
132 |
|
... | ... | |
137 |
137 |
my %time_recordings_by_customer_id;
|
138 |
138 |
push @{ $time_recordings_by_customer_id{$_->customer_id} }, $_ for @$time_recordings;
|
139 |
139 |
|
140 |
|
my %convert_params = map { $_ => $self->data->{$_} } qw(rounding link_project project_id);
|
|
140 |
my %convert_params = map { $_ => $self->data->{$_} } qw(rounding link_order project_id);
|
141 |
141 |
$convert_params{default_part_id} = $self->data->{part_id};
|
142 |
142 |
|
143 |
143 |
my @donumbers;
|
... | ... | |
174 |
174 |
sub convert_with_linking {
|
175 |
175 |
my ($self, $time_recordings_by_order_id, $orders_by_order_id) = @_;
|
176 |
176 |
|
177 |
|
my %convert_params = map { $_ => $self->data->{$_} } qw(rounding link_project project_id);
|
|
177 |
my %convert_params = map { $_ => $self->data->{$_} } qw(rounding link_order project_id);
|
178 |
178 |
$convert_params{default_part_id} = $self->data->{part_id};
|
179 |
179 |
|
180 |
180 |
my @donumbers;
|
... | ... | |
236 |
236 |
sub get_order_for_time_recording {
|
237 |
237 |
my ($self, $tr) = @_;
|
238 |
238 |
|
239 |
|
# check project
|
240 |
|
my $project_id;
|
241 |
|
#$project_id = $self->overide_project_id;
|
242 |
|
$project_id = $self->data->{project_id};
|
243 |
|
$project_id ||= $tr->project_id;
|
244 |
|
#$project_id ||= $self->default_project_id;
|
|
239 |
my $orders;
|
|
240 |
|
|
241 |
if (!$tr->order_id) {
|
|
242 |
# check project
|
|
243 |
my $project_id;
|
|
244 |
#$project_id = $self->overide_project_id;
|
|
245 |
$project_id = $self->data->{project_id};
|
|
246 |
$project_id ||= $tr->project_id;
|
|
247 |
#$project_id ||= $self->default_project_id;
|
|
248 |
|
|
249 |
if (!$project_id) {
|
|
250 |
my $err_msg = 'ConvertTimeRecordings: searching related order failed for time recording id ' . $tr->id . ' : no project id';
|
|
251 |
$::lxdebug->message(LXDebug->WARN(), $err_msg);
|
|
252 |
push @{ $self->{job_errors} }, $err_msg;
|
|
253 |
return;
|
|
254 |
}
|
245 |
255 |
|
246 |
|
if (!$project_id) {
|
247 |
|
my $err_msg = 'ConvertTimeRecordings: searching related order failed for time recording id ' . $tr->id . ' : no project id';
|
248 |
|
$::lxdebug->message(LXDebug->WARN(), $err_msg);
|
249 |
|
push @{ $self->{job_errors} }, $err_msg;
|
250 |
|
return;
|
251 |
|
}
|
|
256 |
my $project = SL::DB::Project->load_cached($project_id);
|
252 |
257 |
|
253 |
|
my $project = SL::DB::Project->load_cached($project_id);
|
|
258 |
if (!$project) {
|
|
259 |
my $err_msg = 'ConvertTimeRecordings: searching related order failed for time recording id ' . $tr->id . ' : project not found';
|
|
260 |
$::lxdebug->message(LXDebug->WARN(), $err_msg);
|
|
261 |
push @{ $self->{job_errors} }, $err_msg;
|
|
262 |
return;
|
|
263 |
}
|
|
264 |
if (!$project->active || !$project->valid) {
|
|
265 |
my $err_msg = 'ConvertTimeRecordings: searching related order failed for time recording id ' . $tr->id . ' : project not active or not valid';
|
|
266 |
$::lxdebug->message(LXDebug->WARN(), $err_msg);
|
|
267 |
push @{ $self->{job_errors} }, $err_msg;
|
|
268 |
return;
|
|
269 |
}
|
|
270 |
if ($project->customer_id && $project->customer_id != $tr->customer_id) {
|
|
271 |
my $err_msg = 'ConvertTimeRecordings: searching related order failed for time recording id ' . $tr->id . ' : project customer does not match customer of time recording';
|
|
272 |
$::lxdebug->message(LXDebug->WARN(), $err_msg);
|
|
273 |
push @{ $self->{job_errors} }, $err_msg;
|
|
274 |
return;
|
|
275 |
}
|
254 |
276 |
|
255 |
|
if (!$project) {
|
256 |
|
my $err_msg = 'ConvertTimeRecordings: searching related order failed for time recording id ' . $tr->id . ' : project not found';
|
257 |
|
$::lxdebug->message(LXDebug->WARN(), $err_msg);
|
258 |
|
push @{ $self->{job_errors} }, $err_msg;
|
259 |
|
return;
|
260 |
|
}
|
261 |
|
if (!$project->active || !$project->valid) {
|
262 |
|
my $err_msg = 'ConvertTimeRecordings: searching related order failed for time recording id ' . $tr->id . ' : project not active or not valid';
|
263 |
|
$::lxdebug->message(LXDebug->WARN(), $err_msg);
|
264 |
|
push @{ $self->{job_errors} }, $err_msg;
|
265 |
|
return;
|
|
277 |
$orders = SL::DB::Manager::Order->get_all(where => [customer_id => $tr->customer_id,
|
|
278 |
or => [quotation => undef, quotation => 0],
|
|
279 |
globalproject_id => $project_id, ],
|
|
280 |
with_objects => ['orderitems']);
|
|
281 |
|
|
282 |
} else {
|
|
283 |
# order_id given
|
|
284 |
my $order = SL::DB::Manager::Order->find_by(id => $tr->order_id);
|
|
285 |
push @$orders, $order if $order;
|
266 |
286 |
}
|
267 |
|
if ($project->customer_id && $project->customer_id != $tr->customer_id) {
|
268 |
|
my $err_msg = 'ConvertTimeRecordings: searching related order failed for time recording id ' . $tr->id . ' : project customer does not match customer of time recording';
|
|
287 |
|
|
288 |
if (!scalar @$orders) {
|
|
289 |
my $err_msg = 'ConvertTimeRecordings: searching related order failed for time recording id ' . $tr->id . ' : no order found';
|
269 |
290 |
$::lxdebug->message(LXDebug->WARN(), $err_msg);
|
270 |
291 |
push @{ $self->{job_errors} }, $err_msg;
|
271 |
292 |
return;
|
... | ... | |
292 |
313 |
return;
|
293 |
314 |
}
|
294 |
315 |
|
295 |
|
my $orders = SL::DB::Manager::Order->get_all(where => [customer_id => $tr->customer_id,
|
296 |
|
or => [quotation => undef, quotation => 0],
|
297 |
|
globalproject_id => $project_id, ],
|
298 |
|
with_objects => ['orderitems']);
|
299 |
316 |
my @matching_orders;
|
300 |
317 |
foreach my $order (@$orders) {
|
301 |
318 |
if (any { $_->parts_id == $part_id } @{ $order->items_sorted }) {
|
... | ... | |
310 |
327 |
return;
|
311 |
328 |
}
|
312 |
329 |
|
313 |
|
return $matching_orders[0];
|
|
330 |
my $matching_order = $matching_orders[0];
|
|
331 |
|
|
332 |
if (!$matching_order->is_sales) {
|
|
333 |
my $err_msg = 'ConvertTimeRecordings: searching related order failed for time recording id ' . $tr->id . ' : found order is not a sales order';
|
|
334 |
$::lxdebug->message(LXDebug->WARN(), $err_msg);
|
|
335 |
push @{ $self->{job_errors} }, $err_msg;
|
|
336 |
return;
|
|
337 |
}
|
|
338 |
|
|
339 |
if ($matching_order->customer_id != $tr->customer_id) {
|
|
340 |
my $err_msg = 'ConvertTimeRecordings: searching related order failed for time recording id ' . $tr->id . ' : customer of order does not match customer of time recording';
|
|
341 |
$::lxdebug->message(LXDebug->WARN(), $err_msg);
|
|
342 |
push @{ $self->{job_errors} }, $err_msg;
|
|
343 |
return;
|
|
344 |
}
|
|
345 |
|
|
346 |
if ($tr->project_id && $tr->project_id != ($matching_order->globalproject_id || 0)) {
|
|
347 |
my $err_msg = 'ConvertTimeRecordings: searching related order failed for time recording id ' . $tr->id . ' : project of order does not match project of time recording';
|
|
348 |
$::lxdebug->message(LXDebug->WARN(), $err_msg);
|
|
349 |
push @{ $self->{job_errors} }, $err_msg;
|
|
350 |
return;
|
|
351 |
}
|
|
352 |
|
|
353 |
return $matching_order;
|
314 |
354 |
}
|
315 |
355 |
|
316 |
356 |
1;
|
... | ... | |
382 |
422 |
ie. 0.25h 0.5h 0.75h 1.25h ...
|
383 |
423 |
Defaults to rounding true (1).
|
384 |
424 |
|
385 |
|
=item C<link_project>
|
|
425 |
=item C<link_order>
|
386 |
426 |
|
387 |
|
If set the job tries to find a previous Order with the current
|
388 |
|
customer and project number and tries to do as much automatic
|
389 |
|
workflow processing as the UI.
|
|
427 |
If set the job links the created delivery order with with the order
|
|
428 |
given in the time recording entry. If there is no order given, then
|
|
429 |
it tries to find an order with with the current customer and project
|
|
430 |
number and tries to do as much automatic workflow processing as the
|
|
431 |
UI.
|
390 |
432 |
Defaults to off. If set to true (1) the job will fail if there
|
391 |
|
is no Sales Orders which qualifies as a predecessor.
|
|
433 |
is no sales order which qualifies as a predecessor.
|
392 |
434 |
Conditions for a predeccesor:
|
393 |
435 |
|
|
436 |
* Order given in time recording entry OR
|
394 |
437 |
* Global project_id must match time_recording.project_id OR data.project_id
|
395 |
|
* Customer name must match time_recording.customer_id OR data.customernumbers
|
|
438 |
* Customer must match customer in time recording entry
|
396 |
439 |
* The sales order must have at least one or more time related services
|
397 |
440 |
* The Project needs to be valid and active
|
398 |
441 |
|
399 |
|
The job doesn't care if the Sales Order is already delivered or closed.
|
|
442 |
The job doesn't care if the sales order is already delivered or closed.
|
400 |
443 |
If the sales order is overdelivered some organisational stuff needs to be done.
|
401 |
444 |
The sales order may also already be closed, ie the amount is fully billed, but
|
402 |
445 |
the services are not yet fully delivered (simple case: 'Payment in advance').
|
Zeiterfassung: Konvertierung: angegebenen Auftrag als Vorgänger verwenden können