Preisupdate in eigenen controller verlagert
[kivitendo-erp.git] / SL / Controller / PartsPriceUpdate.pm
1 package SL::Controller::PartsPriceUpdate;
2
3 use strict;
4 use parent qw(SL::Controller::Base);
5
6 use SL::DBUtils qw(prepare_query selectfirst_array_query prepare_query do_statement do_query);
7 use SL::JSON;
8 use SL::Helper::Flash qw(flash);
9 use SL::DB;
10 use SL::DB::Part;
11 use SL::DB::Pricegroup;
12 use SL::Locale::String qw(t8);
13
14 use Rose::Object::MakeMethods::Generic (
15   'scalar --get_set_init' => [ qw(pricegroups pricegroups_by_id filter) ],
16 );
17
18 __PACKAGE__->run_before('check_rights');
19
20
21 sub action_search_update_prices {
22   my ($self) = @_;
23
24   $self->render('ic/search_update_prices',
25     title => t8('Update Prices'),
26   );
27 }
28
29 sub action_confirm_price_update {
30   my ($self) = @_;
31
32   my @errors;
33   my $found;
34
35   for my $key (keys %{ $self->filter->{prices} || {} }) {
36     my $row = $self->filter->{prices}{$key};
37
38     next if $row->{price_as_number} eq '';
39
40     my $type   = $row->{type};
41     my $value  = $::form->parse_amount(\%::myconfig, $row->{price_as_number});
42     my $name   = $key =~ /^\d+$/      ? $self->pricegroups_by_id->{$key}->pricegroup
43                : $key eq 'sellprice'  ? t8('Sell Price')
44                : $key eq 'listprice'  ? t8('List Price')
45                :                        '';
46
47     if (0 > $value && ($type eq 'percent')) {
48       push @errors, t8('You cannot adjust the price for pricegroup "#1" by a negative percentage.', $name);
49     } elsif (!$value) {
50       push @errors, t8('No valid number entered for pricegroup "#1".', $name);
51     } elsif (0 < $value) {
52       $found = 1;
53     }
54   }
55
56   push @errors, t8('No prices will be updated because no prices have been entered.') if !$found;
57
58   my $num_matches = $self->get_num_matches_for_priceupdate();
59
60   if (@errors) {
61     flash('error', $_) for @errors;
62     return $self->action_search_update_prices;
63   } else {
64
65     my $key = $::auth->create_unique_sesion_value(SL::JSON::to_json($self->filter));
66
67     $self->render('ic/confirm_price_update',
68       num_matches => $num_matches,
69       filter_key  => $key,
70     );
71   }
72 }
73
74 sub action_update_prices {
75   my ($self) = @_;
76
77   my $num_updated = $self->do_update_prices;
78
79   if ($num_updated) {
80     $::form->redirect(t8('#1 prices were updated.', $num_updated));
81   } else {
82     $::form->error(t8('Could not update prices!'));
83   }
84 }
85
86 sub _create_filter_for_priceupdate {
87   my ($self) = @_;
88   my $filter = $self->filter;
89
90   my @where_values;
91   my $where = '1 = 1';
92
93   for my $item (qw(partnumber drawing microfiche make model pg.partsgroup description serialnumber)) {
94     my $column = $item;
95     $column =~ s/.*\.//;
96     next unless $filter->{$column};
97
98     $where .= qq| AND $item ILIKE ?|;
99     push @where_values, "%$filter->{$column}%";
100   }
101
102   # items which were never bought, sold or on an order
103   if ($filter->{itemstatus} eq 'orphaned') {
104     $where .=
105       qq| AND (p.onhand = 0)
106           AND p.id NOT IN
107             (
108               SELECT DISTINCT parts_id FROM invoice
109               UNION
110               SELECT DISTINCT parts_id FROM assembly
111               UNION
112               SELECT DISTINCT parts_id FROM orderitems
113               UNION
114               SELECT DISTINCT parts_id FROM delivery_order_items
115             )|;
116
117   } elsif ($filter->{itemstatus} eq 'active') {
118     $where .= qq| AND p.obsolete = '0'|;
119
120   } elsif ($filter->{itemstatus} eq 'obsolete') {
121     $where .= qq| AND p.obsolete = '1'|;
122
123   } elsif ($filter->{itemstatus} eq 'onhand') {
124     $where .= qq| AND p.onhand > 0|;
125
126   } elsif ($filter->{itemstatus} eq 'short') {
127     $where .= qq| AND p.onhand < p.rop|;
128
129   }
130
131   for my $column (qw(make model)) {
132     next unless ($filter->{$column});
133     $where .= qq| AND p.id IN (SELECT DISTINCT parts_id FROM makemodel WHERE $column ILIKE ?|;
134     push @where_values, "%$filter->{$column}%";
135   }
136
137   return ($where, @where_values);
138 }
139
140 sub get_num_matches_for_priceupdate {
141   my ($self)   = @_;
142   my $filter   = $self->filter;
143   my $dbh      = SL::DB->client->dbh;
144   my ($where, @where_values) = $self->_create_filter_for_priceupdate;
145
146   my $num_updated = 0;
147   my $query;
148
149   for my $column (qw(sellprice listprice)) {
150     next if $filter->{prices}{$column}{price_as_number} eq "";
151
152     $query =
153       qq|SELECT COUNT(*)
154          FROM parts
155          WHERE id IN
156            (SELECT p.id
157             FROM parts p
158             LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
159             WHERE $where)|;
160     my ($result)  = selectfirst_array_query($::form, $dbh, $query, @where_values);
161     $num_updated += $result if (0 <= $result);
162   }
163
164   my @ids = grep { $filter->{prices}{$_}{price_as_number} } map { $_->id } @{ $self->pricegroups };
165   if (@ids) {
166     $query =
167       qq|SELECT COUNT(*)
168          FROM prices
169          WHERE parts_id IN
170            (SELECT p.id
171             FROM parts p
172             LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
173             WHERE $where)
174          AND pricegroup_id IN (@{[ join ',', ('?')x@ids ]})|;
175
176     my ($result)  = selectfirst_array_query($::form, $dbh, $query, @where_values, @ids);
177     $num_updated += $result if (0 <= $result);
178   }
179
180   return $num_updated;
181 }
182
183 sub do_update_prices {
184   SL::DB->client->with_transaction(\&_update_prices, $_[0]);
185 }
186
187 sub _update_prices {
188   my ($self) = @_;
189   my $filter_json = $::auth->get_session_value($::form->{filter_key});
190   my $filter = SL::JSON::from_json($filter_json);
191   $self->filter($filter);
192   die "missing filter" unless $filter;
193
194   my ($where, @where_values) = $self->_create_filter_for_priceupdate;
195   my $num_updated = 0;
196
197   # connect to database
198   my $dbh = SL::DB->client->dbh;
199
200   for my $column (qw(sellprice listprice)) {
201     my $row = $filter->{prices}{$column};
202     next if ($row->{price_as_number} eq "");
203
204     my $value = $::form->parse_amount(\%::myconfig, $row->{price_as_number});
205     my $operator = '+';
206
207     if ($row->{type} eq "percent") {
208       $value = ($value / 100) + 1;
209       $operator = '*';
210     }
211
212     my $query =
213       qq|UPDATE parts SET $column = $column $operator ?
214          WHERE id IN
215            (SELECT p.id
216             FROM parts p
217             LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
218             WHERE $where)|;
219     my $result    = do_query($::form, $dbh, $query, $value, @where_values);
220     $num_updated += $result if 0 <= $result;
221   }
222
223   my $q_add =
224     qq|UPDATE prices SET price = price + ?
225        WHERE parts_id IN
226          (SELECT p.id
227           FROM parts p
228           LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
229           WHERE $where) AND (pricegroup_id = ?)|;
230   my $sth_add = prepare_query($::form, $dbh, $q_add);
231
232   my $q_multiply =
233     qq|UPDATE prices SET price = price * ?
234        WHERE parts_id IN
235          (SELECT p.id
236           FROM parts p
237           LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
238           WHERE $where) AND (pricegroup_id = ?)|;
239   my $sth_multiply = prepare_query($::form, $dbh, $q_multiply);
240
241   for my $pg (@{ $self->pricegroups }) {
242     my $row = $filter->{prices}{$pg->id};
243     next if $row->{price_as_number} eq "";
244
245     my $value = $::form->parse_amount(\%::myconfig, $row->{price_as_number});
246     my $result;
247
248     if ($row->{type} eq "percent") {
249       $result = do_statement($::form, $sth_multiply, $q_multiply, ($value / 100) + 1, @where_values, $pg->id);
250     } else {
251       $result = do_statement($::form, $sth_add, $q_add, $value, @where_values, $pg->id);
252     }
253
254     $num_updated += $result if (0 <= $result);
255   }
256
257   $sth_add->finish;
258   $sth_multiply->finish;
259
260   1;
261 }
262
263 sub init_pricegroups {
264   SL::DB::Manager::Pricegroup->get_all_sorted(query => [
265     obsolete => 0,
266   ]);
267 }
268
269 sub init_pricegroups_by_id {
270   +{ map { $_->id => $_ } @{ $_[0]->pricegroups } }
271 }
272
273 sub check_rights {
274   $::auth->assert('part_service_assembly_edit');
275 }
276
277 sub init_filter {
278   $::form->{filter} || {};
279 }
280
281 1;