2 use Test::Deep qw(cmp_deeply ignore superhashof);
8 use SL::Dev::Part qw(new_part new_assembly);
9 use SL::Dev::Inventory qw(create_warehouse_and_bins set_stock);
10 use SL::Dev::Record qw(create_sales_order);
12 use_ok 'Support::TestSetup';
14 use_ok 'SL::DB::Part';
15 use_ok 'SL::DB::Warehouse';
16 use_ok 'SL::DB::Inventory';
18 use_ok 'SL::Helper::Inventory';
20 Support::TestSetup::login();
22 my ($wh, $bin1, $bin2, $assembly1, $part1, $part2, $wh_moon, $bin_moon);
25 create_standard_stock();
28 # simple stock in, get_stock, get_onhand
35 is(SL::Helper::Inventory::get_stock(part => $part1), "25.00000", 'simple get_stock works');
36 is(SL::Helper::Inventory::get_onhand(part => $part1), "25.00000", 'simple get_onhand works');
38 # stock on some more, get_stock, get_onhand
41 parts_id => $part1->id,
43 transfer_type => 'stock',
44 dst_warehouse_id => $bin1->warehouse_id,
45 dst_bin_id => $bin1->id,
50 parts_id => $part1->id,
52 transfer_type => 'stock',
53 chargenumber => '298345',
54 dst_warehouse_id => $bin1->warehouse_id,
55 dst_bin_id => $bin1->id,
59 is(SL::Helper::Inventory::get_stock(part => $part1), "60.00000", 'normal get_stock works');
60 is(SL::Helper::Inventory::get_onhand(part => $part1), "60.00000", 'normal get_onhand works');
64 my @allocations = SL::Helper::Inventory::allocate(
69 is_deeply(\%{ $allocations[0] }, {
73 parts_id => $part1->id,
75 warehouse_id => $wh->id,
76 comment => undef, # comment is not a partition so is not set by allocate
77 for_object_id => undef,
78 }, 'allocation works');
80 # allocate something where more than one result will match
82 @allocations = SL::Helper::Inventory::allocate(
87 is_deeply(\@allocations, [
92 parts_id => $part1->id,
94 warehouse_id => $wh->id,
96 for_object_id => undef,
101 chargenumber => '298345',
102 parts_id => $part1->id,
104 warehouse_id => $wh->id,
106 for_object_id => undef,
108 ], 'complex allocation works');
110 # try to allocate too much
113 SL::Helper::Inventory::allocate(part => $part1, qty => 100)
115 "allocate too much dies");
120 create_standard_stock();
134 my @alloc1 = SL::Helper::Inventory::allocate(part => $part1, qty => 3);
135 my @alloc2 = SL::Helper::Inventory::allocate(part => $part2, qty => 3);
137 SL::Helper::Inventory::produce_assembly(
140 allocations => [ @alloc1, @alloc2 ],
144 chargenumber => "537",
147 is(SL::Helper::Inventory::get_stock(part => $assembly1), "3.00000", 'produce works');
148 is(SL::Helper::Inventory::get_stock(part => $part1), "2.00000", 'and consumes...');
149 is(SL::Helper::Inventory::get_stock(part => $part2), "7.00000", '..the materials');
151 # produce the same using auto_allocation
153 local $::locale = Locale->new('en');
155 create_standard_stock();
168 SL::Helper::Inventory::produce_assembly(
175 chargenumber => "537",
178 is(SL::Helper::Inventory::get_stock(part => $assembly1), "3.00000", 'produce with auto allocation works');
179 is(SL::Helper::Inventory::get_stock(part => $part1), "2.00000", 'and consumes...');
180 is(SL::Helper::Inventory::get_stock(part => $part2), "7.00000", '..the materials');
182 # check comments and warehouses
183 $::form->{l_comment} = 'Y';
184 $::form->{l_warehouse_from} = 'Y';
185 $::form->{l_warehouse_to} = 'Y';
186 local $::instance_conf->data->{produce_assembly_same_warehouse} = 1;
188 my @contents = WH->get_warehouse_journal(sort => 'date');
190 cmp_deeply(\@contents,
191 [ ignore(), ignore(),
193 'comment' => 'Used for assembly '. $assembly1->partnumber .' Test Assembly',
194 'warehouse_from' => 'Warehouse'
197 'comment' => 'Used for assembly '. $assembly1->partnumber .' Test Assembly',
198 'warehouse_from' => 'Warehouse'
201 'part_type' => 'assembly',
202 'warehouse_to' => 'Warehouse'
205 "Comments for assembly productions are ok"
213 # try to produce something for our lunar warehouse, but parts are only available on earth
215 SL::Helper::Inventory::produce_assembly(
221 chargenumber => "Lunar Dust inside",
223 }, "producing for wrong warehouse dies");
225 # same test, but check exception class
227 SL::Helper::Inventory::produce_assembly(
233 chargenumber => "Lunar Dust inside",
235 } "SL::X::Inventory::Allocation", "producing for wrong warehouse throws correct error class";
237 # same test, but check user feedback for the error message
239 SL::Helper::Inventory::produce_assembly(
245 chargenumber => "Lunar Dust inside",
247 } qr/Part ap (1|2) Testpart (1|2) exists in warehouse Warehouse, but not in warehouse Our warehouse location at the moon/, "producing for wrong warehouse throws correct error message";
249 # try to produce without allocations dies
252 SL::Helper::Inventory::produce_assembly(
258 chargenumber => "537",
260 }, "producing without allocations dies");
262 # try to produce with insufficient allocations dies
264 @alloc1 = SL::Helper::Inventory::allocate(part => $part1, qty => 1);
265 @alloc2 = SL::Helper::Inventory::allocate(part => $part2, qty => 1);
268 SL::Helper::Inventory::produce_assembly(
271 allocations => [ @alloc1, @alloc2 ],
275 chargenumber => "537",
277 }, "producing with insufficient allocations dies");
284 create_standard_stock();
299 SL::Helper::Inventory::produce_assembly(
305 chargenumber => "537",
306 bestbefore => DateTime->today->clone->add(days => -14), # expired 2 weeks ago
307 shippingdate => DateTime->today->clone->add(days => 1),
310 is(SL::Helper::Inventory::get_stock(part => $assembly1), "3.00000", 'produce with bestbefore works');
311 is(SL::Helper::Inventory::get_onhand(part => $assembly1), "3.00000", 'produce with bestbefore works');
312 is(SL::Helper::Inventory::get_stock(
314 bestbefore => DateTime->today,
315 ), undef, 'get_stock with bestbefore date skips expired');
317 local $::instance_conf->data->{show_bestbefore} = 1;
318 is(SL::Helper::Inventory::get_onhand(
320 ), undef, 'get_onhand with bestbefore skips expired as of today');
324 local $::instance_conf->data->{show_bestbefore} = 0;
325 is(SL::Helper::Inventory::get_onhand(
327 ), "3.00000", 'get_onhand without bestbefore finds all');
332 SL::DB::Manager::Order->delete_all(all => 1);
333 SL::DB::Manager::Inventory->delete_all(all => 1);
334 SL::DB::Manager::Assembly->delete_all(all => 1);
335 SL::DB::Manager::Part->delete_all(all => 1);
336 SL::DB::Manager::Bin->delete_all(all => 1);
337 SL::DB::Manager::Warehouse->delete_all(all => 1);
340 sub create_standard_stock {
341 ($wh, $bin1) = create_warehouse_and_bins();
342 ($wh_moon, $bin_moon) = create_warehouse_and_bins(
343 warehouse_description => 'Our warehouse location at the moon',
344 bin_description => 'Lunar crater',
346 $bin2 = SL::DB::Bin->new(description => "Bin 2", warehouse => $wh)->save;
349 $assembly1 = new_assembly(number_of_parts => 2)->save;
350 ($part1, $part2) = map { $_->part } $assembly1->assemblies;