Revision e2db2d12
Von Kivitendo Admin vor fast 8 Jahren hinzugefügt
SL/Dev/Inventory.pm | ||
---|---|---|
9 | 9 |
use SL::DB::Inventory; |
10 | 10 |
use SL::DB::TransferType; |
11 | 11 |
use SL::DB::Employee; |
12 |
use SL::WH; |
|
13 |
use DateTime; |
|
14 |
use Data::Dumper; |
|
12 | 15 |
|
13 | 16 |
sub create_warehouse_and_bins { |
14 | 17 |
my (%params) = @_; |
... | ... | |
23 | 26 |
} |
24 | 27 |
|
25 | 28 |
sub set_stock { |
26 |
my ($part, %params) = @_; |
|
29 |
my (%params) = @_; |
|
30 |
|
|
31 |
die "param part is missing or not an SL::DB::Part object" unless ref($params{part}) eq 'SL::DB::Part'; |
|
32 |
my $part = delete $params{part}; |
|
33 |
die "qty is missing" unless $params{qty} or $params{abs_qty}; |
|
34 |
die "need a bin or default bin" unless $part->warehouse_id or $part->bin_id or $params{bin} or $params{bin_id}; |
|
35 |
|
|
36 |
my ($warehouse_id, $bin_id); |
|
27 | 37 |
|
28 |
die "first argument is not a part" unless ref($part) eq 'SL::DB::Part'; |
|
38 |
if ( $params{bin} ) { |
|
39 |
die "illegal param bin: " . Dumper($params{bin}) unless ref($params{bin}) eq 'SL::DB::Bin'; |
|
40 |
my $bin = delete $params{bin}; |
|
41 |
$bin_id = $bin->id; |
|
42 |
$warehouse_id = $bin->warehouse_id; |
|
43 |
} elsif ( $params{bin_id} ) { |
|
44 |
my $bin = SL::DB::Manager::Bin->find_by(id => delete $params{bin_id}); |
|
45 |
$bin_id = $bin->id; |
|
46 |
$warehouse_id = $bin->warehouse_id; |
|
47 |
} elsif ( $part->bin_id ) { |
|
48 |
$bin_id = $part->bin_id; |
|
49 |
$warehouse_id = $part->warehouse_id; |
|
50 |
} else { |
|
51 |
die "can't determine bin and warehouse"; |
|
52 |
} |
|
29 | 53 |
|
30 |
die "no default warehouse" unless $part->warehouse_id or $part->bin_id; |
|
54 |
my $employee_id = delete $params{employee_id} // SL::DB::Manager::Employee->current->id; |
|
55 |
die "Can't determine employee" unless $employee_id; |
|
31 | 56 |
|
32 |
die "Can't determine employee" unless SL::DB::Manager::Employee->current;
|
|
57 |
my $qty = delete $params{qty};
|
|
33 | 58 |
|
34 |
die "qty is missing or not positive" unless $params{qty} and $params{qty} > 0; |
|
59 |
my $transfer_type_description; |
|
60 |
my $transfer_type; |
|
61 |
if ( $params{abs_qty} ) { |
|
62 |
# determine the current qty and calculate the qty diff that needs to be applied |
|
63 |
# if abs_qty is set then any value that was in $params{qty} is ignored/overwritten |
|
64 |
my %get_stock_params; |
|
65 |
$get_stock_params{bin_id} = $bin_id if $bin_id; |
|
66 |
# $get_stock_params{warehouse_id} = $warehouse_id if $warehouse_id; # redundant |
|
67 |
my $current_qty = $part->get_stock(%get_stock_params); |
|
68 |
$qty = $params{abs_qty} - $current_qty; |
|
69 |
} |
|
35 | 70 |
|
36 |
my $transfer_type_description = delete $params{transfer_type} || 'stock'; |
|
37 |
my $transfer_type = SL::DB::Manager::TransferType->find_by( description => $transfer_type_description, direction => 'in' ); |
|
71 |
if ( $qty > 0 ) { |
|
72 |
$transfer_type_description = delete $params{transfer_type} // 'stock'; |
|
73 |
$transfer_type = SL::DB::Manager::TransferType->find_by( description => $transfer_type_description, direction => 'in' ); |
|
74 |
} else { |
|
75 |
$transfer_type_description = delete $params{transfer_type} // 'shipped'; |
|
76 |
$transfer_type = SL::DB::Manager::TransferType->find_by( description => $transfer_type_description, direction => 'out' ); |
|
77 |
} |
|
78 |
die "can't determine transfer_type" unless $transfer_type; |
|
38 | 79 |
|
39 | 80 |
my $shippingdate; |
40 | 81 |
if ( $params{shippingdate} ) { |
41 |
$shippingdate = $::locale->parse_date_to_object(delete $params{shippingdate}); |
|
82 |
$shippingdate = delete $params{shippingdate}; |
|
83 |
$shippingdate = $::locale->parse_date_to_object($shippingdate) unless ref($shippingdate) eq 'DateTime'; |
|
42 | 84 |
} else { |
43 | 85 |
$shippingdate = DateTime->today; |
44 |
}; |
|
86 |
} |
|
87 |
|
|
88 |
my $unit; |
|
89 |
if ( $params{unit} ) { |
|
90 |
$unit = delete $params{unit}; |
|
91 |
$unit = SL::DB::Manager::Unit->find_by( name => $unit ) unless ref($unit) eq 'SL::DB::Unit'; |
|
92 |
$qty = $unit->convert_to($qty, $part->unit_obj); |
|
93 |
} |
|
45 | 94 |
|
46 | 95 |
my ($trans_id) = $part->db->dbh->selectrow_array("select nextval('id')", {}); |
47 | 96 |
|
48 | 97 |
SL::DB::Inventory->new( |
49 | 98 |
parts_id => $part->id, |
50 |
bin_id => $part->bin_id,
|
|
51 |
warehouse_id => $part->warehouse_id,
|
|
52 |
employee_id => $params{employee_id} || SL::DB::Manager::Employee->current->id,
|
|
99 |
bin_id => $bin_id, |
|
100 |
warehouse_id => $warehouse_id, |
|
101 |
employee_id => $employee_id,
|
|
53 | 102 |
trans_type_id => $transfer_type->id, |
54 | 103 |
comment => $params{comment}, |
55 | 104 |
shippingdate => $shippingdate, |
56 |
qty => $params{qty},
|
|
105 |
qty => $qty,
|
|
57 | 106 |
trans_id => $trans_id, |
58 | 107 |
)->save; |
59 | 108 |
} |
60 | 109 |
|
110 |
sub transfer_stock { |
|
111 |
my (%params) = @_; |
|
112 |
|
|
113 |
# check params: |
|
114 |
die "missing params" unless ( $params{parts_id} or $params{part} ) and $params{from_bin} and $params{to_bin}; |
|
115 |
|
|
116 |
my $part; |
|
117 |
if ( $params{parts_id} ) { |
|
118 |
$part = SL::DB::Manager::Part->find_by( id => delete $params{parts_id} ) or die "illegal parts_id"; |
|
119 |
} else { |
|
120 |
$part = delete $params{part}; |
|
121 |
} |
|
122 |
die "illegal part" unless ref($part) eq 'SL::DB::Part'; |
|
123 |
|
|
124 |
my $from_bin = delete $params{from_bin}; |
|
125 |
my $to_bin = delete $params{to_bin}; |
|
126 |
die "illegal bins" unless ref($from_bin) eq 'SL::DB::Bin' and ref($to_bin) eq 'SL::DB::Bin'; |
|
127 |
|
|
128 |
my $qty = delete($params{qty}); |
|
129 |
die "qty must be > 0" unless $qty > 0; |
|
130 |
|
|
131 |
# set defaults |
|
132 |
my $transfer_type = SL::DB::Manager::TransferType->find_by(description => 'transfer') or die "can't determine transfer type"; |
|
133 |
my $employee_id = delete $params{employee_id} // SL::DB::Manager::Employee->current->id; |
|
134 |
|
|
135 |
my $WH_params = { |
|
136 |
'bestbefore' => undef, |
|
137 |
'change_default_bin' => undef, |
|
138 |
'chargenumber' => '', |
|
139 |
'comment' => delete $params{comment} // '', |
|
140 |
'dst_bin_id' => $to_bin->id, |
|
141 |
'dst_warehouse_id' => $to_bin->warehouse_id, |
|
142 |
'parts_id' => $part->id, |
|
143 |
'qty' => $qty, |
|
144 |
'src_bin_id' => $from_bin->id, |
|
145 |
'src_warehouse_id' => $from_bin->warehouse_id, |
|
146 |
'transfer_type_id' => $transfer_type->id, |
|
147 |
}; |
|
148 |
|
|
149 |
WH->transfer($WH_params); |
|
150 |
|
|
151 |
return 1; |
|
152 |
|
|
153 |
# do it manually via rose: |
|
154 |
# my $trans_id; |
|
155 |
|
|
156 |
# my $db = SL::DB::Inventory->new->db; |
|
157 |
# $db->with_transaction(sub{ |
|
158 |
# ($trans_id) = $db->dbh->selectrow_array("select nextval('id')", {}); |
|
159 |
# die "no trans_id" unless $trans_id; |
|
160 |
|
|
161 |
# my %params = ( |
|
162 |
# shippingdate => delete $params{shippingdate} // DateTime->today, |
|
163 |
# employee_id => $employee_id, |
|
164 |
# trans_id => $trans_id, |
|
165 |
# trans_type_id => $transfer_type->id, |
|
166 |
# parts_id => $part->id, |
|
167 |
# comment => delete $params{comment} || 'Umlagerung', |
|
168 |
# ); |
|
169 |
|
|
170 |
# SL::DB::Inventory->new( |
|
171 |
# warehouse_id => $from_bin->warehouse_id, |
|
172 |
# bin_id => $from_bin->id, |
|
173 |
# qty => $qty * -1, |
|
174 |
# %params, |
|
175 |
# )->save; |
|
176 |
|
|
177 |
# SL::DB::Inventory->new( |
|
178 |
# warehouse_id => $to_bin->warehouse_id, |
|
179 |
# bin_id => $to_bin->id, |
|
180 |
# qty => $qty, |
|
181 |
# %params, |
|
182 |
# )->save; |
|
183 |
# }) or die $@ . "\n"; |
|
184 |
# return 1; |
|
185 |
} |
|
186 |
|
|
61 | 187 |
1; |
62 | 188 |
|
63 | 189 |
__END__ |
... | ... | |
83 | 209 |
To access the second bin: |
84 | 210 |
my $bin2 = $wh->bins->[1]; |
85 | 211 |
|
86 |
=head2 C<set_stock $part, %PARAMS> |
|
212 |
=head2 C<set_stock %PARAMS> |
|
213 |
|
|
214 |
Change the stock level of a certain part by creating an inventory event. |
|
215 |
To access the updated onhand the part object needs to be loaded afterwards. |
|
216 |
|
|
217 |
Mandatory params: |
|
218 |
part - an SL::DB::Part object or a parts_id |
|
219 |
qty | abs_qty |
|
220 |
qty : the qty to increase of decrease the stock level by |
|
221 |
abs_qty : sets stock level for a certain part to abs_qty by creating |
|
222 |
a stock event with the current difference |
|
223 |
|
|
224 |
Optional params: |
|
225 |
bin_id | bin |
|
226 |
shippingdate : may be a DateTime object or a string that needs to be parsed by parse_date_to_object. |
|
227 |
unit : SL::DB::Unit object, or the name of an SL::DB::Unit object |
|
228 |
|
|
229 |
If no bin is passed the default bin of the part is used, if that doesn't exist |
|
230 |
either there will be an error. |
|
87 | 231 |
|
88 |
Increase the stock level of a certain part by creating an inventory event. Currently |
|
89 |
only positive stock levels can be set. To access the updated onhand the part |
|
90 |
object needs to be loaded afterwards. |
|
232 |
C<set_stock> creates the SL::DB::Inventory object from scratch, rather |
|
233 |
than passing params to WH->transfer_in or WH->transfer_out. |
|
91 | 234 |
|
235 |
Examples: |
|
92 | 236 |
my $part = SL::DB::Manager::Part->find_by(partnumber => '1'); |
93 |
SL::Dev::Inventory::set_stock($part, 5); |
|
237 |
SL::Dev::Inventory::set_stock(part => $part, qty => 5); |
|
238 |
SL::Dev::Inventory::set_stock(part => $part, qty => -2); |
|
94 | 239 |
$part->load; |
240 |
$part->onhand; # 3 |
|
241 |
|
|
242 |
Set stock level of a part in a certain bin_id to 10: |
|
243 |
SL::Dev::Inventory::set_stock(part => $part, bin_id => 99, abs_qty => 10); |
|
244 |
|
|
245 |
Create 10 warehouses with 5 bins each, then create 100 parts and increase the |
|
246 |
stock qty in a random bin by a random positive qty for each of the parts: |
|
247 |
|
|
248 |
SL::Dev::Inventory::create_warehouse_and_bins(warehouse_description => "Testlager $_") for ( 1 .. 10 ); |
|
249 |
SL::Dev::Part::create_part(description => "Testpart $_")->save for ( 1 .. 100 ); |
|
250 |
my $bins = SL::DB::Manager::Bin->get_all; |
|
251 |
SL::Dev::Inventory::set_stock(part => $_, |
|
252 |
qty => int(rand(99))+1, |
|
253 |
bin => $bins->[ rand @{$bins} ], |
|
254 |
) foreach @{ SL::DB::Manager::Part->get_all() }; |
|
255 |
|
|
256 |
=head2 C<transfer_stock %PARAMS> |
|
257 |
|
|
258 |
Transfers parts from one bin to another. |
|
259 |
|
|
260 |
Mandatory params: |
|
261 |
part | parts_id - an SL::DB::Part object or a parts_id |
|
262 |
from_bin - an SL::DB::Bin object |
|
263 |
to_bin qty - an SL::DB::Bin object |
|
264 |
|
|
265 |
Optional params: shippingdate |
|
266 |
|
|
267 |
The unit is always base_unit and there is no check for negative stock values. |
|
268 |
|
|
269 |
Example: Create a warehouse and bins, a part, stock the part and then move some |
|
270 |
of the stock to a different bin inside the same warehouse: |
|
271 |
|
|
272 |
my ($wh, $bin) = SL::Dev::Inventory::create_warehouse_and_bins(); |
|
273 |
my $part = SL::Dev::Part::create_part->save; |
|
274 |
SL::Dev::Inventory::set_stock(part => $part, bin_id => $wh->bins->[2]->id, qty => 5); |
|
275 |
SL::Dev::Inventory::transfer_stock(part => $part, |
|
276 |
from_bin => $wh->bins->[2], |
|
277 |
to_bin => $wh->bins->[4], |
|
278 |
qty => 3 |
|
279 |
); |
|
280 |
$part->get_stock(bin_id => $wh->bins->[4]->id); # 3.00000 |
|
281 |
$part->get_stock(bin_id => $wh->bins->[2]->id); # 2.00000 |
|
95 | 282 |
|
96 | 283 |
=head1 BUGS |
97 | 284 |
|
Auch abrufbar als: Unified diff
SL::Dev::Inventory - set_stock mit abs_qty, transfer_stock
Man kann Lagerbestände jetzt sowohl positiv als auch negativ ändern,
oder einen gewünschten Bestand vorgeben (abs_qty).
Weiterhin kann man per transfer_stock Lagermengen zwischen Lagerplätzen
umlagern.