1 package SL::Controller::DispositionManager;
5 use parent qw(SL::Controller::Base);
7 use SL::Controller::Helper::GetModels;
8 use SL::Controller::Helper::ReportGenerator;
10 use SL::DB::PurchaseBasketItem;
12 use SL::DB::OrderItem;
15 use SL::Locale::String qw(t8);
16 use SL::Helper::Flash qw(flash flash_later);
21 use Rose::Object::MakeMethods::Generic (
22 'scalar --get_set_init' => [ qw(models) ],
25 sub action_list_parts {
27 $self->prepare_report(t8('Reorder Level List'), $::form->{noshow} ? 1 : 0 );
29 my $objects = $::form->{noshow} ? [] : $self->models->get;
31 $self->_setup_list_action_bar;
32 $self->report_generator_list_objects(
33 report => $self->{report}, objects => $objects);
37 my ($self, $title, $noshow ) = @_;
39 my $report = SL::ReportGenerator->new(\%::myconfig, $::form);
40 $self->{report} = $report;
43 partnumber description available onhand rop ordered
46 partnumber description available onhand rop ordered
48 my @sortable = qw(partnumber description);
52 sub => sub { $_[0]->partnumber },
53 text => t8('Part Number'),
54 obj_link => sub { $_[0]->presenter->link_to },
57 sub => sub { $_[0]->description },
58 text => t8('Part Description'),
59 obj_link => sub { $_[0]->presenter->link_to },
62 sub => sub { $::form->format_amount(\%::myconfig,$_[0]->onhandqty,2); },
63 text => t8('Available Stock'),
66 sub => sub { $::form->format_amount(\%::myconfig,$_[0]->stockqty,2); },
67 text => t8('Total Stock'),
70 sub => sub { $::form->format_amount(\%::myconfig,$_[0]->rop,2); },
74 sub => sub { $::form->format_amount(
75 \%::myconfig,$_[0]->get_open_ordered_qty,2); },
76 text => t8('Ordered purchase'),
80 map { $column_defs{$_}->{visible} = 1 } @visible;
83 controller_class => 'DispositionManager',
84 output_format => 'HTML',
86 allow_pdf_export => 0,
87 allow_csv_export => 0,
88 allow_chart_export => 0,
89 no_data_message => !$noshow,
91 $report->set_columns(%column_defs);
92 $report->set_column_order(@columns);
95 if ($report->{options}{output_format} =~ /^(pdf|csv)$/i) {
96 $self->models->disable_plugin('paginated');
98 $self->models->finalize; # for filter laundering
99 $self->models->set_report_generator_sort_options(
100 report => $report, sortable_columns => \@sortable
103 my $parts = $self->_get_parts(0);
104 my $top = $self->render('disposition_manager/list_parts', { output => 0 },
107 title => t8('Short onhand Ordered'),
109 my $bottom = $noshow ? undef : $self->render(
110 'disposition_manager/reorder_level_list/report_bottom',
111 { output => 0}, models => $self->models );
112 $report->set_options(
113 raw_top_info_text => $top,
114 raw_bottom_info_text => $bottom,
118 sub action_add_to_purchase_basket{
121 my $employee = SL::DB::Manager::Employee->current;
123 my $parts_to_add = delete($::form->{ids}) || [];
124 foreach my $id (@{ $parts_to_add }) {
125 my $part = SL::DB::Manager::Part->find_by(id => $id)
126 or die "Can't find part with id: $id\n";
127 my $needed_qty = $part->order_qty < ($part->rop - $part->onhandqty) ?
128 $part->rop - $part->onhandqty
130 my $basket_part = SL::DB::PurchaseBasketItem->new(
131 part_id => $part->id,
133 orderer_id => $employee->id,
138 controller => 'DispositionManager',
139 action => 'show_basket',
144 sub action_show_basket {
147 $::request->{layout}->add_javascripts(
148 'kivi.DispositionManager.js', 'kivi.Part.js'
150 my $basket_items = SL::DB::Manager::PurchaseBasketItem->get_all(
151 query => [ cleared => 'F' ],
152 with_objects => [ 'part', 'part.makemodels' ]
154 $self->_setup_show_basket_action_bar;
156 'disposition_manager/show_purchase_basket',
157 BASKET_ITEMS => $basket_items,
158 title => t8('Purchase basket'),
162 sub action_show_vendor_items {
165 my $makemodels_parts;
166 if ($::form->{vendor_id}) {
167 $makemodels_parts = SL::DB::Manager::Part->get_all(
169 'purchase_basket_item.id' => undef,
170 'makemodels.make' => $::form->{vendor_id},
173 with_objects => [ 'makemodels', 'purchase_basket_item' ]
178 'disposition_manager/_show_vendor_parts',
180 MAKEMODEL_ITEMS => $makemodels_parts
184 sub action_transfer_to_purchase_order {
188 my $basket_item_ids = $::form->{ids};
189 my $vendor_item_ids = $::form->{vendor_part_ids};
191 unless (($basket_item_ids && scalar @{ $basket_item_ids})
192 || ( $vendor_item_ids && scalar @{ $vendor_item_ids}))
194 $self->js->flash('error', t8('There are no items selected'));
195 return $self->js->render();
198 # check for same vendor
199 my %basket_id_vendor_id_map =
200 map {$::form->{basket_ids}->[$_] => $::form->{vendor_ids}->[$_]}
201 (0..$#{$::form->{vendor_ids}});
203 my $vendor_id = $::form->{vendor_id_selected} || $basket_id_vendor_id_map{@{$basket_item_ids}[0]} || $basket_id_vendor_id_map{@{$basket_item_ids}[0]};
205 my @different_vendor_ids =
206 grep { $basket_id_vendor_id_map{$_} ne $vendor_id }
208 if (scalar @different_vendor_ids) {
209 $self->js->flash('error', t8('There are mulitple vendors selected'));
210 return $self->js->render();
214 controller => 'Order',
215 action => 'add_from_purchase_basket',
216 type => 'purchase_order',
217 basket_item_ids => $basket_item_ids || [],
218 vendor_item_ids => $vendor_item_ids || [],
219 vendor_id => $vendor_id,
223 sub action_delete_purchase_basket_items {
228 my $basket_item_ids = $::form->{ids};
230 if ($basket_item_ids && scalar @{ $basket_item_ids}) {
231 SL::DB::Manager::PurchaseBasketItem->delete_all(
232 where => [ id => $basket_item_ids]);
234 $self->js->flash('error', t8('There are no items selected'));
235 return $self->js->render();
238 flash_later('info', t8('Selected items deleted'));
241 controller => 'DispositionManager',
242 action => 'show_basket',
247 my ($self, $ordered) = @_;
251 SELECT inv.parts_id, sum(qty) as sum
253 LEFT JOIN warehouse w ON inv.warehouse_id = w.id
255 GROUP BY inv.parts_id
259 SELECT p.id, 0 as sum
261 WHERE p.id NOT IN ( SELECT distinct parts_id from inventory)
268 LEFT JOIN available ava ON ava.parts_id = p.id
269 WHERE ( ava.sum < p.rop )
270 AND p.id NOT IN ( SELECT part_id FROM purchase_basket_items )
272 ORDER BY p.partnumber
274 my @ids = selectall_array_query($::form, $::form->get_standard_dbh, $query);
275 return unless scalar @ids;
276 my $parts = SL::DB::Manager::Part->get_all( query => [ id => \@ids ] );
277 my $parts_to_order = [ grep { !$_->get_open_ordered_qty } @{$parts} ];
278 return $parts_to_order if !$ordered;
279 my $parts_ordered = [
280 map { $_->id } grep { $_->get_open_ordered_qty } @{$parts}
282 return $parts_ordered if $ordered;
287 my $parts1 = $self->_get_parts(1) || [];
288 my @parts = @{$parts1};
289 my $get_models = SL::Controller::Helper::GetModels->new(
297 partnumber => $::locale->text('Part Number'),
298 description => $::locale->text('Description'),
301 (id => \@parts) x !!@parts,
302 (id => undef) x !@parts,
305 form_params => [ qw(page per_page) ],
314 sub _setup_list_action_bar {
316 for my $bar ($::request->layout->get('actionbar')) {
319 t8('Purchasebasket'),
321 '#form', { action => "DispositionManager/add_to_purchase_basket" } ],
322 tooltip => t8('Add to purchase basket'),
328 sub _setup_show_basket_action_bar {
330 for my $bar ($::request->layout->get('actionbar')) {
334 link => $self->url_for(
335 controller => 'DispositionManager',
336 action => 'show_basket',
341 call => [ 'kivi.DispositionManager.create_purchase_order' ],
342 tooltip => t8('Create purchase order'),
346 call => [ 'kivi.DispositionManager.delete_purchase_basket_items' ],
347 tooltip => t8('Delete selected from purchase basket'),
360 SL::Controller::DispositionManager Controller to manage purchase orders for parts
364 This controller shows a list of parts using the filter minimum stock (rop).
365 From this list it is possible to put parts in a purchase basket to order.
366 It's also possible to put parts from the parts edit form in the purchase basket.
368 From the purchase basket you can create a purchase order by using the filter vendor.
369 The quantity to order will be prefilled by the value min_qty_to_order from parts or
370 makemodel(vendor_parts) or default to qty 1.
376 =item purchase_basket
395 =item C<action_list_parts>
397 List the parts by the filter min stock (rop) and not in an open purchase order.
399 =item C<action_add_to_purchase_basket>
401 Adds one or more parts to the purchase basket.
403 =item C<action_show_basket>
405 Shows a list with parts which are in the basket.
406 This list can be filtered by vendor. Then you can create a purchase order.
407 When filtered by vendor, a table with the parts from the vendor of the purchase basket and
408 a table with all parts from the vendor will be shown. From there you can mark
409 the parts and create an order
411 =item C<action_transfer_to_purchase_order>
413 Transfers the marked and by vendor filtered parts to a purchase order.
414 Deletes the entry in the purchase basket.
424 W. Hahn E<lt>wh@futureworldsearch.netE<gt>