CTR: Warnungen zu Fehlern hochgestuft - Variablenbenennung wie in CPI
[kivitendo-erp.git] / SL / BackgroundJob / ConvertTimeRecordings.pm
1 package SL::BackgroundJob::ConvertTimeRecordings;
2
3 use strict;
4
5 use parent qw(SL::BackgroundJob::Base);
6
7 use SL::DB::DeliveryOrder;
8 use SL::DB::TimeRecording;
9
10 use SL::Locale::String qw(t8);
11
12 use DateTime;
13 use Try::Tiny;
14
15 sub create_job {
16   $_[0]->create_standard_job('7 3 1 * *'); # every first day of month at 03:07
17 }
18
19
20 #
21 # If job does not throw an error,
22 # success in background_job_histories is 'success'.
23 # It is 'failure' otherwise.
24 #
25 # Return value goes to result in background_job_histories.
26 #
27 sub run {
28   my ($self, $db_obj) = @_;
29
30   my $data;
31   $data = $db_obj->data_as_hash if $db_obj;
32
33   $self->{$_} = [] for qw(job_errors);
34   # from/to date from data. Defaults to begining and end of last month.
35   my $from_date;
36   my $to_date;
37   # handle errors with a catch handler
38   try {
39     $from_date   = DateTime->from_kivitendo($data->{from_date}) if $data->{from_date};
40     $to_date     = DateTime->from_kivitendo($data->{to_date})   if $data->{to_date};
41   } catch {
42     die "Cannot convert date from string $data->{from_date} $data->{to_date}\n Details :\n $_"; # not $@
43   };
44   $from_date ||= DateTime->new( day => 1,    month => DateTime->today_local->month, year => DateTime->today_local->year)->subtract(months => 1);
45   $to_date   ||= DateTime->last_day_of_month(month => DateTime->today_local->month, year => DateTime->today_local->year)->subtract(months => 1);
46
47   $to_date->add(days => 1); # to get all from the to_date, because of the time part (15.12.2020 23.59 > 15.12.2020)
48
49   my %customer_where;
50   %customer_where = ('customer.customernumber' => $data->{customernumbers}) if 'ARRAY' eq ref $data->{customernumbers};
51
52   my $time_recordings = SL::DB::Manager::TimeRecording->get_all(where        => [end_time => { ge_lt => [ $from_date, $to_date ]},
53                                                                                  or => [booked => 0, booked => undef],
54                                                                                  %customer_where],
55                                                                 with_objects => ['customer']);
56   my %time_recordings_by_customer_id;
57   push @{ $time_recordings_by_customer_id{$_->customer_id} }, $_ for @$time_recordings;
58
59   my @donumbers;
60   foreach my $customer_id (keys %time_recordings_by_customer_id) {
61     my $do;
62     if (!eval {
63       $do = SL::DB::DeliveryOrder->new_from_time_recordings($time_recordings_by_customer_id{$customer_id});
64       1;
65     }) {
66       $::lxdebug->message(LXDebug->WARN(),
67                           "ConvertTimeRecordings: creating delivery order failed ($@) for time recording ids " . join ', ', map { $_->id } @{$time_recordings_by_customer_id{$customer_id}});
68       push @{ $self->{job_errors} }, "ConvertTimeRecordings: creating delivery order failed ($@) for time recording ids " . join ', ', map { $_->id } @{$time_recordings_by_customer_id{$customer_id}};
69
70     }
71
72     if ($do) {
73       if (!SL::DB->client->with_transaction(sub {
74         $do->save;
75         $_->update_attributes(booked => 1) for @{$time_recordings_by_customer_id{$customer_id}};
76         1;
77       })) {
78         $::lxdebug->message(LXDebug->WARN(),
79                             "ConvertTimeRecordings: saving delivery order failed for time recording ids " . join ', ', map { $_->id } @{$time_recordings_by_customer_id{$customer_id}});
80       push @{ $self->{job_errors} }, "ConvertTimeRecordings: saving delivery order failed for time recording ids " . join ', ', map { $_->id } @{$time_recordings_by_customer_id{$customer_id}};
81       } else {
82         push @donumbers, $do->donumber;
83       }
84     }
85   }
86
87   my $msg  = t8('Number of delivery orders created:');
88   $msg    .= ' ';
89   $msg    .= scalar @donumbers;
90   $msg    .= ' (';
91   $msg    .= join ', ', @donumbers;
92   $msg    .= ').';
93   # die if errors exists
94   if (@{ $self->{job_errors} }) {
95     $msg  .= ' ' . t8('The following errors occurred:');
96     $msg  .= join "\n", @{ $self->{job_errors} };
97     return $msg;
98   }
99   return $msg;
100 }
101
102 1;
103
104 # possible data
105 # from_date: 01.12.2020
106 # to_date: 15.12.2020
107 # customernumbers: [1,2,3]
108 __END__
109
110 =pod
111
112 =encoding utf8
113
114 =head1 NAME
115
116 SL::BackgroundJob::ConvertTimeRecordings - Convert time recording
117 entries into delivery orders
118
119 =head1 SYNOPSIS
120
121 Get all time recording entries for the given period and customer numbers
122 and create delivery ordes out of that (using
123 C<SL::DB::DeliveryOrder-E<gt>new_from_time_recordings>).
124
125 =head1 CONFIGURATION
126
127 Some data can be provided to configure this backgroung job.
128
129 =over 4
130
131 =item C<from_date>
132
133 The date from which on time recordings should be collected. It defaults
134 to the first day of the previous month.
135
136 Example (format depends on your settings):
137
138 from_date: 01.12.2020
139
140 =item C<to_date>
141
142 The date till which time recordings should be collected. It defaults
143 to the last day of the previous month.
144
145 Example (format depends on your settings):
146
147 to_date: 15.12.2020
148
149 =item C<customernumbers>
150
151 An array with the customer numbers for which time recordings should
152 be collected. If not given, time recordings for customers are
153 collected. This is the default.
154
155 Example (format depends on your settings):
156
157 customernumbers: [c1,22332,334343]
158
159 =back
160
161 =head1 AUTHOR
162
163 Bernd Bleßmann E<lt>bernd@kivitendo-premium.deE<gt>
164
165 =cut