Revision dfe28d97
Von Werner Hahn vor mehr als 1 Jahr hinzugefügt
SL/Controller/DispositionsManager.pm | ||
---|---|---|
1 |
package SL::Controller::DispositionsManager; |
|
2 |
|
|
3 |
|
|
4 |
use strict; |
|
5 |
|
|
6 |
use parent qw(SL::Controller::Base); |
|
7 |
|
|
8 |
use SL::DB::Part; |
|
9 |
use SL::DB::PurchaseBasket; |
|
10 |
use SL::Locale::String qw(t8); |
|
11 |
|
|
12 |
sub action_list_parts { |
|
13 |
my ( $self ) = @_; |
|
14 |
|
|
15 |
my $query = <<SQL; |
|
16 |
SELECT * FROM parts WHERE onhand <= rop AND rop != 0 AND id NOT IN( SELECT parts_id FROM purchase_basket) AND NOT obsolete |
|
17 |
SQL |
|
18 |
|
|
19 |
my $parts = SL::DB::Manager::Part->get_objects_from_sql( sql => $query ); |
|
20 |
$self->_setup_list_action_bar; |
|
21 |
$self->render('dispositionsmanager/list_parts', title => t8('Parts short onhand'), PARTS => $parts); |
|
22 |
|
|
23 |
|
|
24 |
} |
|
25 |
|
|
26 |
sub action_add_to_purchase_basket{ |
|
27 |
my ( $self ) = @_; |
|
28 |
|
|
29 |
my $parts_to_add = delete($::form->{id}) || []; |
|
30 |
foreach my $id (@{ $parts_to_add }) { |
|
31 |
my $part = SL::DB::Manager::Part->get_first( query => [ id => $id ] ); |
|
32 |
my $basket_part = SL::DB::PurchaseBasket->new( |
|
33 |
parts_id => $part->id, |
|
34 |
qty => $part->min_order_qty, |
|
35 |
description => $part->description, |
|
36 |
); |
|
37 |
$basket_part->save; |
|
38 |
} |
|
39 |
$self->action_show_basket; |
|
40 |
|
|
41 |
} |
|
42 |
|
|
43 |
sub action_show_basket { |
|
44 |
my ( $self ) = @_; |
|
45 |
|
|
46 |
$::request->{layout}->add_javascripts('kivi.DispositionsManager.js'); |
|
47 |
my $basket = SL::DB::Manager::PurchaseBasket->get_all( query => [ cleared => 'F' ], with_objects => [ 'parts', 'parts.makemodels' ]); |
|
48 |
$self->_setup_show_basket_action_bar; |
|
49 |
$self->render('dispositionsmanager/show_purchase_basket', PARTS => $basket ); |
|
50 |
} |
|
51 |
|
|
52 |
sub action_transfer_to_purchase_order { |
|
53 |
my ( $self ) = @_; |
|
54 |
|
|
55 |
|
|
56 |
} |
|
57 |
|
|
58 |
sub _setup_list_action_bar { |
|
59 |
my ($self) = @_; |
|
60 |
for my $bar ($::request->layout->get('actionbar')) { |
|
61 |
$bar->add( |
|
62 |
action => [ |
|
63 |
t8('Action'), |
|
64 |
submit => [ '#form', { action => "DispositionsManager/add_to_purchase_basket" } ], |
|
65 |
tooltip => t8('Add to purchase basket'), |
|
66 |
], |
|
67 |
); |
|
68 |
} |
|
69 |
} |
|
70 |
|
|
71 |
sub _setup_show_basket_action_bar { |
|
72 |
my ($self) = @_; |
|
73 |
for my $bar ($::request->layout->get('actionbar')) { |
|
74 |
$bar->add( |
|
75 |
action => [ |
|
76 |
t8('Reload'), |
|
77 |
submit => [ '#form', { action => "DispositionsManager/show_basket" } ], |
|
78 |
], |
|
79 |
action => [ |
|
80 |
t8('Action'), |
|
81 |
submit => [ '#form', { action => "DispositionsManager/transfer_to_purchase_order" } ], |
|
82 |
tooltip => t8('Create purchase order'), |
|
83 |
], |
|
84 |
); |
|
85 |
} |
|
86 |
} |
|
87 |
1; |
|
88 |
|
|
89 |
__END__ |
|
90 |
|
|
91 |
=encoding utf-8 |
|
92 |
|
|
93 |
=head1 NAME |
|
94 |
|
|
95 |
SL::Controller::DispositionsManager Controller to manage purchaseorders for parts |
|
96 |
|
|
97 |
=head1 DESCRIPTION |
|
98 |
|
|
99 |
This Controller shows a list of parts using the filter minimum stock (rop). |
|
100 |
From this list it is possible to put parts in a purchase basket to order. |
|
101 |
It's also possible to put parts from the parts edit form in the purchase basket. |
|
102 |
|
|
103 |
From the purchase basket you can create a purchaseorder by using the filter vendor. |
|
104 |
The quantity to order will be prefilled by the value min_qty_to_order from parts or |
|
105 |
makemodel(vendor_parts) or default qty 1. |
|
106 |
|
|
107 |
Tables: |
|
108 |
|
|
109 |
=over 2 |
|
110 |
|
|
111 |
=item purchase_basket |
|
112 |
|
|
113 |
=back |
|
114 |
|
|
115 |
Depencies: |
|
116 |
|
|
117 |
=over 2 |
|
118 |
|
|
119 |
=item parts |
|
120 |
|
|
121 |
=item makemodels |
|
122 |
|
|
123 |
|
|
124 |
=back |
|
125 |
|
|
126 |
=head1 URL ACTIONS |
|
127 |
|
|
128 |
=over 4 |
|
129 |
|
|
130 |
=item C<action_list_parts> |
|
131 |
|
|
132 |
List the parts by the filter min stock (rop) and not in an open purchase order. |
|
133 |
|
|
134 |
=item C<action_add_to_purchase_basket> |
|
135 |
|
|
136 |
Adds one or more parts to the purchase basket. |
|
137 |
|
|
138 |
=item C<action_show_basket> |
|
139 |
|
|
140 |
Shows a list with parts wich are in the basket. |
|
141 |
This List can be filtered by vendor. Then you can create a purchaseorder. |
|
142 |
|
|
143 |
=item C<action_transfer_to_purchase_order> |
|
144 |
|
|
145 |
Transfers the marked and by vendor filtered parts to a purchase order. |
|
146 |
Deletes the entry in the purchasebasket. |
|
147 |
|
|
148 |
=back |
|
149 |
|
|
150 |
=head1 BUGS |
|
151 |
|
|
152 |
None yet. :) |
|
153 |
|
|
154 |
=head1 AUTHOR |
|
155 |
|
|
156 |
W. Hahn E<lt>wh@futureworldsearch.netE<gt> |
|
157 |
|
|
158 |
=cut |
SL/Controller/Part.pm | ||
---|---|---|
513 | 513 |
part_longdescription => '', |
514 | 514 |
lastcost => 0, |
515 | 515 |
sortorder => $position, |
516 |
min_order_qty => '1', |
|
516 | 517 |
) or die "Can't create MakeModel object"; |
517 | 518 |
|
518 | 519 |
my $row_as_html = $self->p->render('part/_makemodel_row', |
... | ... | |
986 | 987 |
|
987 | 988 |
my $position = 0; |
988 | 989 |
my $makemodels = delete($::form->{makemodels}) || []; |
990 |
$main::lxdebug->dump(0, 'WH:MMFORM ', $makemodels); |
|
989 | 991 |
foreach my $makemodel ( @{$makemodels} ) { |
990 | 992 |
next unless $makemodel->{make}; |
991 | 993 |
$position++; |
... | ... | |
1178 | 1180 |
my $position = 0; |
1179 | 1181 |
my @makemodel_array = (); |
1180 | 1182 |
my $makemodels = delete($::form->{makemodels}) || []; |
1183 |
$main::lxdebug->dump(0, 'WH:MM ', $makemodels); |
|
1181 | 1184 |
|
1182 | 1185 |
foreach my $makemodel ( @{$makemodels} ) { |
1183 | 1186 |
next unless $makemodel->{make}; |
... | ... | |
1190 | 1193 |
part_longdescription => $makemodel->{part_longdescription} || '', |
1191 | 1194 |
lastcost => $::form->parse_amount(\%::myconfig, $makemodel->{lastcost_as_number} || 0), |
1192 | 1195 |
sortorder => $position, |
1196 |
min_order_qty => $::form->parse_amount(\%::myconfig, $makemodel->{min_order_qty_as_number} || 0), |
|
1193 | 1197 |
) or die "Can't create mm"; |
1194 | 1198 |
# $mm->id($makemodel->{id}) if $makemodel->{id}; |
1195 | 1199 |
push(@makemodel_array, $mm); |
SL/DB/Helper/Mappings.pm | ||
---|---|---|
187 | 187 |
project_roles => 'project_role', |
188 | 188 |
project_statuses => 'project_status', |
189 | 189 |
project_types => 'project_type', |
190 |
purchase_basket => 'purchase_basket', |
|
190 | 191 |
reclamations => 'Reclamation', |
191 | 192 |
reclamation_items => 'ReclamationItem', |
192 | 193 |
reclamation_reasons => 'ReclamationReason', |
SL/DB/Manager/PurchaseBasket.pm | ||
---|---|---|
1 |
# This file has been auto-generated only because it didn't exist. |
|
2 |
# Feel free to modify it at will; it will not be overwritten automatically. |
|
3 |
|
|
4 |
package SL::DB::Manager::PurchaseBasket; |
|
5 |
|
|
6 |
use strict; |
|
7 |
|
|
8 |
use parent qw(SL::DB::Helper::Manager); |
|
9 |
|
|
10 |
sub object_class { 'SL::DB::PurchaseBasket' } |
|
11 |
|
|
12 |
__PACKAGE__->make_manager_methods; |
|
13 |
|
|
14 |
1; |
SL/DB/MetaSetup/MakeModel.pm | ||
---|---|---|
14 | 14 |
lastcost => { type => 'numeric', precision => 15, scale => 5 }, |
15 | 15 |
lastupdate => { type => 'date' }, |
16 | 16 |
make => { type => 'integer' }, |
17 |
min_order_qty => { type => 'numeric', precision => 15, scale => 5 }, |
|
17 | 18 |
model => { type => 'text' }, |
18 | 19 |
mtime => { type => 'timestamp' }, |
19 | 20 |
part_description => { type => 'text' }, |
SL/DB/MetaSetup/PurchaseBasket.pm | ||
---|---|---|
1 |
# This file has been auto-generated. Do not modify it; it will be overwritten |
|
2 |
# by rose_auto_create_model.pl automatically. |
|
3 |
package SL::DB::PurchaseBasket; |
|
4 |
|
|
5 |
use strict; |
|
6 |
|
|
7 |
use parent qw(SL::DB::Object); |
|
8 |
|
|
9 |
__PACKAGE__->meta->table('purchase_basket'); |
|
10 |
|
|
11 |
__PACKAGE__->meta->columns( |
|
12 |
cleared => { type => 'boolean', default => 'false', not_null => 1 }, |
|
13 |
description => { type => 'text' }, |
|
14 |
id => { type => 'serial', not_null => 1 }, |
|
15 |
parts_id => { type => 'integer' }, |
|
16 |
qty => { type => 'numeric', precision => 15, scale => 5 }, |
|
17 |
); |
|
18 |
|
|
19 |
__PACKAGE__->meta->primary_key_columns([ 'id' ]); |
|
20 |
|
|
21 |
__PACKAGE__->meta->foreign_keys( |
|
22 |
parts => { |
|
23 |
class => 'SL::DB::Part', |
|
24 |
key_columns => { parts_id => 'id' }, |
|
25 |
}, |
|
26 |
); |
|
27 |
|
|
28 |
1; |
|
29 |
; |
SL/DB/Part.pm | ||
---|---|---|
12 | 12 |
use SL::DB::MetaSetup::Part; |
13 | 13 |
use SL::DB::Manager::Part; |
14 | 14 |
use SL::DB::Chart; |
15 |
use SL::DB::Vendor; |
|
15 | 16 |
use SL::DB::Helper::AttrHTML; |
16 | 17 |
use SL::DB::Helper::AttrSorted; |
17 | 18 |
use SL::DB::Helper::TransNumberGenerator; |
... | ... | |
578 | 579 |
return 1; |
579 | 580 |
} |
580 | 581 |
|
582 |
sub vendor_dropdown { |
|
583 |
my ( $self ) = @_; |
|
584 |
|
|
585 |
my @vendor_dd; |
|
586 |
# $main::lxdebug->dump(0, 'WH:MakeModels ', $_[0]->makemodels); |
|
587 |
|
|
588 |
foreach my $mm ( @{$_[0]->makemodels} ){ |
|
589 |
my $vendor = SL::DB::Manager::Vendor->get_first( where => [ id => $mm->make ] ); |
|
590 |
my @tmp = ({ title => $vendor->name, value => $vendor->{id} }); |
|
591 |
push @vendor_dd, @tmp; |
|
592 |
} |
|
593 |
return \@vendor_dd; |
|
594 |
} |
|
595 |
|
|
581 | 596 |
1; |
582 | 597 |
|
583 | 598 |
__END__ |
SL/DB/PurchaseBasket.pm | ||
---|---|---|
1 |
# This file has been auto-generated only because it didn't exist. |
|
2 |
# Feel free to modify it at will; it will not be overwritten automatically. |
|
3 |
|
|
4 |
package SL::DB::PurchaseBasket; |
|
5 |
|
|
6 |
use strict; |
|
7 |
|
|
8 |
use SL::DB::MetaSetup::PurchaseBasket; |
|
9 |
use SL::DB::Manager::PurchaseBasket; |
|
10 |
|
|
11 |
__PACKAGE__->meta->initialize; |
|
12 |
|
|
13 |
|
|
14 |
1; |
js/kivi.DispositionsManager.js | ||
---|---|---|
1 |
namespace('kivi.DispositionsManager', function(ns) { |
|
2 |
ns.sort_vendors = function() { |
|
3 |
$("table tr").each(function(index) { |
|
4 |
if ( index !== 0 ) { |
|
5 |
$row = $(this); |
|
6 |
// alert( $row.find('#vendor_id').val() + '!=' + $('#cv_id').val()); |
|
7 |
if( $row.find('#vendor_id').val() != $('#cv_id').val()) { |
|
8 |
$row.remove(); |
|
9 |
} |
|
10 |
} |
|
11 |
}); |
|
12 |
} |
|
13 |
}); |
menus/user/10-dispositionsmanager.yaml | ||
---|---|---|
1 |
--- |
|
2 |
- parent: ap |
|
3 |
id: ap_disp_manager |
|
4 |
name: Dispositionsmanager |
|
5 |
icon: rfq_add |
|
6 |
order: 150 |
|
7 |
access: request_quotation_edit |
|
8 |
- parent: ap_disp_manager |
|
9 |
id: ap_disp_manager_onhand |
|
10 |
name: List short onhand |
|
11 |
order: 160 |
|
12 |
params: |
|
13 |
action: DispositionsManager/list_parts |
|
14 |
- parent: ap_disp_manager |
|
15 |
id: ap_disp_manager_basket |
|
16 |
name: Show purchase basket |
|
17 |
order: 170 |
|
18 |
params: |
|
19 |
action: DispositionsManager/show_basket |
sql/Pg-upgrade2/purchase_basket.sql | ||
---|---|---|
1 |
-- @tag: purchase_basket |
|
2 |
-- @description: Tabelle für den Dispostionsmanager |
|
3 |
-- @depends: release_3_5_1 |
|
4 |
-- @ignore: 0 |
|
5 |
|
|
6 |
CREATE TABLE purchase_basket ( |
|
7 |
id SERIAL PRIMARY KEY, |
|
8 |
parts_id INTEGER REFERENCES parts(id), |
|
9 |
qty NUMERIC(15,5), |
|
10 |
description text, |
|
11 |
cleared BOOLEAN NOT NULL DEFAULT FALSE |
|
12 |
); |
|
13 |
|
|
14 |
ALTER TABLE parts ADD COLUMN min_order_qty NUMERIC(15,5); |
|
15 |
ALTER TABLE makemodel ADD COLUMN min_order_qty NUMERIC(15,5); |
templates/webpages/dispositionsmanager/list_parts.html | ||
---|---|---|
1 |
[%- USE HTML -%][%- USE LxERP -%][%- USE L -%][%- USE T8 -%] |
|
2 |
[% USE Dumper %] |
|
3 |
[%- INCLUDE 'common/flash.html' %] |
|
4 |
<h1>[% title %]</h1> |
|
5 |
<hr> |
|
6 |
<h2>[% 'Short onhand' | $T8 %]</h2> |
|
7 |
<form id="form"> |
|
8 |
<table> |
|
9 |
<thead> |
|
10 |
<tr class="listheading"> |
|
11 |
<th>[% L.checkbox_tag('check_all') %][% 'Purchasebasket' | $T8 %] </th> |
|
12 |
<th>[% 'Partnumber' | $T8 %] </th> |
|
13 |
<th>[% 'Description' | $T8 %] </th> |
|
14 |
<th>[% 'Onhand' | $T8 %] </th> |
|
15 |
<th>[% 'Rop' | $T8 %] </th> |
|
16 |
<th>[% 'Minimum order quantity' | $T8 %] </th> |
|
17 |
</tr> |
|
18 |
</thead> |
|
19 |
[% FOREACH part = PARTS %] |
|
20 |
[% IF !part.get_ordered_qty(part.id) %] |
|
21 |
<tr class="listrow"> |
|
22 |
<td>[% IF part.makemodels.size %][% L.checkbox_tag('id[]', checked = '1', value=part.id) %][% ELSE %][% 'No Vendor' | $T8 %][% END %]</td> |
|
23 |
<td>[% HTML.escape(part.partnumber) %] </td> |
|
24 |
<td>[% HTML.escape(part.description) %]</td> |
|
25 |
<td>[% part.onhand_as_number %] </td> |
|
26 |
<td>[% part.rop_as_number %] </td> |
|
27 |
<td>[% part.min_order_qty_as_number %] </td> |
|
28 |
</tr> |
|
29 |
[% END %] |
|
30 |
[% END %] |
|
31 |
</table> |
|
32 |
</form> |
|
33 |
<hr> |
|
34 |
<h2>[% 'Short onhand Ordered' | $T8 %]</h2> |
|
35 |
<table> |
|
36 |
<thead> |
|
37 |
<tr class="listheading"> |
|
38 |
<th>[% 'Partnumber' | $T8 %] </th> |
|
39 |
<th>[% 'Description' | $T8 %] </th> |
|
40 |
<th>[% 'onhand' | $T8 %] </th> |
|
41 |
<th>[% 'rop' | $T8 %] </th> |
|
42 |
<th>[% 'Ordered purchase' | $T8 %] </th> |
|
43 |
</tr> |
|
44 |
</thead> |
|
45 |
[% FOREACH part = PARTS %] |
|
46 |
[% IF part.get_ordered_qty(part.id) %] |
|
47 |
<tr class="listrow"> |
|
48 |
<td>[% HTML.escape(part.partnumber) %] </td> |
|
49 |
<td>[% HTML.escape(part.description) %] </td> |
|
50 |
<td>[% part.onhand_as_number %] </td> |
|
51 |
<td>[% part.rop_as_number %] </td> |
|
52 |
<td>[% part.get_ordered_qty(part.id) %] </td> |
|
53 |
</tr> |
|
54 |
[% END %] |
|
55 |
[% END %] |
|
56 |
</table> |
|
57 |
<script type="text/javascript"> |
|
58 |
<!-- |
|
59 |
|
|
60 |
kivi.DispositionsManager.sort_vendors(); |
|
61 |
$(function() { |
|
62 |
alert('hallo9'); |
|
63 |
$('#check_all').checkall('INPUT[name^="id"]'); |
|
64 |
kivi.DispositionsManager.sort_vendors(); |
|
65 |
}); |
|
66 |
--> |
|
67 |
</script> |
templates/webpages/dispositionsmanager/show_purchase_basket.html | ||
---|---|---|
1 |
[%- USE HTML -%][%- USE LxERP -%][%- USE L -%][%- USE T8 -%][% USE P %] |
|
2 |
[% USE Dumper %] |
|
3 |
[%- INCLUDE 'common/flash.html' %] |
|
4 |
<h1>[% title %]</h1> |
|
5 |
<hr> |
|
6 |
[% # Dumper.dump_html(PARTS) %] |
|
7 |
<form id="form"> |
|
8 |
[% P.customer_vendor.picker('vendor_id2', '', type='vendor', fat_set_item=1) %]<br> |
|
9 |
<div>id from change: <span id='change3'></span></div> |
|
10 |
[% L.hidden_tag('cv_id', '') %] |
|
11 |
<table id="baskettable"> |
|
12 |
<thead> |
|
13 |
<tr class="listheading"> |
|
14 |
<th>[% L.checkbox_tag('check_all') %][% 'Purchasebasket' | $T8 %] </th> |
|
15 |
<th>[% 'Partnumber' | $T8 %] </th> |
|
16 |
<th>[% 'Description' | $T8 %] </th> |
|
17 |
<th>[% 'Onhand' | $T8 %] </th> |
|
18 |
<th>[% 'Rop' | $T8 %] </th> |
|
19 |
<th>[% 'Order quantity' | $T8 %] </th> |
|
20 |
<th>[% 'Vendor' | $T8 %] </th> |
|
21 |
</tr> |
|
22 |
</thead> |
|
23 |
<tbody> |
|
24 |
[% FOREACH part = PARTS %] |
|
25 |
[% SET select_size = part.parts.vendor_dropdown.size %] |
|
26 |
[% IF !part.part.get_ordered_qty(part.id) %] |
|
27 |
<tr class="listrow"> |
|
28 |
<td>[% L.checkbox_tag('id[]', checked = '1', value=part.id) %]</td> |
|
29 |
<td>[% HTML.escape(part.parts.partnumber) %] </td> |
|
30 |
<td>[% HTML.escape(part.parts.description) %]</td> |
|
31 |
<td>[% part.parts.onhand_as_number %] </td> |
|
32 |
<td>[% part.parts.rop_as_number %] </td> |
|
33 |
<td>[% part.parts.min_order_qty %] </td> |
|
34 |
<td>[% L.select_tag('vendor_id', part.parts.vendor_dropdown, value_key = 'value', title_key = 'title', default = part.parts.makemodels.item(0).make, size = select_size, style='width: 350px;' ) %]</td> |
|
35 |
</tr> |
|
36 |
[% END %] |
|
37 |
[% END %] |
|
38 |
</tbody> |
|
39 |
</table> |
|
40 |
</form> |
|
41 |
<script type="text/javascript"> |
|
42 |
<!-- |
|
43 |
|
|
44 |
$('#vendor_id2').change(function() { $('#change3').html($('#vendor_id2').val()) }) |
|
45 |
$('#vendor_id2').on('set_item:CustomerVendorPicker', function(e,o) { |
|
46 |
$('#cv_id').val(o.id) |
|
47 |
kivi.DispositionsManager.sort_vendors(); |
|
48 |
}) |
|
49 |
$(function() { |
|
50 |
$('#check_all').checkall('INPUT[name^="id"]'); |
|
51 |
}); |
|
52 |
--> |
|
53 |
</script> |
templates/webpages/part/_basic_data.html | ||
---|---|---|
178 | 178 |
</td> |
179 | 179 |
</tr> |
180 | 180 |
[%- END %] |
181 |
<tr> |
|
182 |
<th align="right" nowrap="true">[% 'Minimum order Quantity' | $T8 %]</th> |
|
183 |
<td>[% L.input_tag("part.min_order_qty_as_number", SELF.part.min_order_qty_as_number, size=10, class="reformat_number numeric") %]</td> |
|
184 |
</tr> |
|
181 | 185 |
<tr> |
182 | 186 |
<th align="right" nowrap="true">[% 'Verrechnungseinheit' | $T8 %]</th> |
183 | 187 |
<td>[% L.input_tag("part.ve", SELF.part.ve, size=10) %]</td> |
Auch abrufbar als: Unified diff
DispositionsManager: Prototyp ohne Auftrag erstellen