5 use Support::TestSetup;
10 use SL::DB::Inventory;
11 use SL::DB::TransferType;
13 use SL::DB::DeliveryOrder;
16 use SL::DB::RecordLink;
17 use SL::DB::DeliveryOrderItemsStock;
21 use SL::Dev::ALL qw(:ALL);
22 use SL::Helper::ShippedQty;
25 Support::TestSetup::login();
29 my ($customer, $vendor, @parts, $unit);
31 $customer = new_customer(name => 'Testkunde' )->save;
32 $vendor = new_vendor( name => 'Testlieferant')->save;
34 my $default_sellprice = 10;
35 my $default_lastcost = 4;
37 my ($wh) = create_warehouse_and_bins();
38 my $bin1 = SL::DB::Manager::Bin->find_by(description => "Bin 1");
39 my $bin2 = SL::DB::Manager::Bin->find_by(description => "Bin 2");
42 sellprice => $default_sellprice,
43 warehouse_id => $wh->id,
47 # create 3 parts to be used in test
48 for my $i ( 1 .. 4 ) {
49 new_part( %part_defaults, partnumber => $i, description => "part $i test" )->save;
52 my $part1 = SL::DB::Manager::Part->find_by( partnumber => '1' ) or die;
53 my $part2 = SL::DB::Manager::Part->find_by( partnumber => '2' ) or die;
54 my $part3 = SL::DB::Manager::Part->find_by( partnumber => '3' ) or die;
55 my $part4 = SL::DB::Manager::Part->find_by( partnumber => '4' ) or die;
57 my @part_ids; # list of all part_ids to run checks against
58 push( @part_ids, $_->id ) foreach ( $part1, $part2, $part3, $part4 );
59 my %default_transfer_params = ( wh => $wh, bin => $bin1, unit => 'Stck');
62 # test purchases first, so there is actually stock available when sales is tested
64 note("testing purchases, no fill_up");
66 $::form->{type} = 'purchase_order';
67 my $purchase_order = create_purchase_order(
69 orderitems => [ create_order_item(part => $part1, qty => 11),
70 create_order_item(part => $part2, qty => 12),
71 create_order_item(part => $part3, qty => 13),
75 Rose::DB::Object::Helpers::forget_related($purchase_order, 'orderitems');
76 $purchase_order->orderitems;
78 SL::Helper::ShippedQty
79 ->new(require_stock_out => 1) # should make no difference while there is no delivery order
80 ->calculate($purchase_order)
83 is($purchase_order->items_sorted->[0]->{shipped_qty}, 0, "first purchase orderitem has no shipped_qty");
84 ok(!$purchase_order->items_sorted->[0]->{delivered}, "first purchase orderitem is not delivered");
86 my $purchase_orderitem_part1 = SL::DB::Manager::OrderItem->find_by( parts_id => $part1->id, trans_id => $purchase_order->id);
88 is($purchase_orderitem_part1->shipped_qty, 0, "OrderItem shipped_qty method ok");
90 is($purchase_order->closed, 0, 'purchase order is open');
91 ok(!$purchase_order->delivered, 'purchase order is not delivered');
93 note('converting purchase order to delivery order');
94 # create purchase delivery order from purchase order
95 my $purchase_delivery_order = $purchase_order->convert_to_delivery_order;
96 is($purchase_order->closed, 0, 'purchase order is open');
97 note('purchase order is not general now delivered');
98 ok(!$purchase_order->delivered, 'purchase order is not delivered');
100 SL::Helper::ShippedQty
101 ->new(require_stock_out => 0)
102 ->calculate($purchase_order)
105 is($purchase_order->items_sorted->[0]->{shipped_qty}, 11, "require_stock_out => 0: first purchase orderitem has shipped_qty");
106 ok($purchase_order->items_sorted->[0]->{delivered}, "require_stock_out => 0: first purchase orderitem is delivered");
108 Rose::DB::Object::Helpers::forget_related($purchase_order, 'orderitems');
109 $purchase_order->orderitems;
111 SL::Helper::ShippedQty
112 ->new(require_stock_out => 1)
113 ->calculate($purchase_order)
116 is($purchase_order->items_sorted->[0]->{shipped_qty}, 0, "require_stock_out => 1: first purchase orderitem has no shipped_qty");
117 ok(!$purchase_order->items_sorted->[0]->{delivered}, "require_stock_out => 1: first purchase orderitem is not delivered");
119 # ship items from delivery order
120 transfer_purchase_delivery_order($purchase_delivery_order);
122 Rose::DB::Object::Helpers::forget_related($purchase_order, 'orderitems');
123 $purchase_order->orderitems;
125 SL::Helper::ShippedQty
126 ->new(require_stock_out => 1, keep_matches => 1) # shouldn't make a difference now after shipping
127 ->calculate($purchase_order)
130 is($purchase_order->items_sorted->[0]->{shipped_qty}, 11, "require_stock_out => 1: first purchase orderitem has shipped_qty");
131 ok($purchase_order->items_sorted->[0]->{delivered}, "require_stock_out => 1: first purchase orderitem is delivered");
133 my $purchase_orderitem_part2 = SL::DB::Manager::OrderItem->find_by(parts_id => $part1->id, trans_id => $purchase_order->id);
135 is($purchase_orderitem_part2->shipped_qty(require_stock_out => 1), 11, "OrderItem shipped_qty from helper ok");
138 note('testing sales, no fill_up');
140 $::form->{type} = 'sales_order';
141 my $sales_order = create_sales_order(
143 orderitems => [ create_order_item(part => $part1, qty => 5),
144 create_order_item(part => $part2, qty => 6),
145 create_order_item(part => $part3, qty => 7),
149 Rose::DB::Object::Helpers::forget_related($sales_order, 'orderitems');
150 $sales_order->orderitems;
152 SL::Helper::ShippedQty
153 ->new(require_stock_out => 1) # should make no difference while there is no delivery order
154 ->calculate($sales_order)
157 is($sales_order->items_sorted->[0]->{shipped_qty}, 0, "first sales orderitem has no shipped_qty");
158 ok(!$sales_order->items_sorted->[0]->{delivered}, "first sales orderitem is not delivered");
160 my $orderitem_part1 = SL::DB::Manager::OrderItem->find_by(parts_id => $part1->id, trans_id => $sales_order->id);
161 my $orderitem_part2 = SL::DB::Manager::OrderItem->find_by(parts_id => $part2->id, trans_id => $sales_order->id);
163 is($orderitem_part1->shipped_qty, 0, "OrderItem shipped_qty method ok");
165 # create sales delivery order from sales order
166 my $sales_delivery_order = $sales_order->convert_to_delivery_order;
168 SL::Helper::ShippedQty
169 ->new(require_stock_out => 0)
170 ->calculate($sales_order)
173 is($sales_order->items_sorted->[0]->{shipped_qty}, 5, "require_stock_out => 0: first sales orderitem has shipped_qty");
174 ok($sales_order->items_sorted->[0]->{delivered}, "require_stock_out => 0: first sales orderitem is delivered");
176 Rose::DB::Object::Helpers::forget_related($sales_order, 'orderitems');
177 $sales_order->orderitems;
179 SL::Helper::ShippedQty
180 ->new(require_stock_out => 1)
181 ->calculate($sales_order)
184 is($sales_order->items_sorted->[0]->{shipped_qty}, 0, "require_stock_out => 1: first sales orderitem has no shipped_qty");
185 ok(!$sales_order->items_sorted->[0]->{delivered}, "require_stock_out => 1: first sales orderitem is not delivered");
187 # ship items from delivery order
188 transfer_sales_delivery_order($sales_delivery_order);
190 Rose::DB::Object::Helpers::forget_related($sales_order, 'orderitems');
191 $sales_order->orderitems;
193 SL::Helper::ShippedQty
194 ->new(require_stock_out => 1)
195 ->calculate($sales_order)
198 is($sales_order->items_sorted->[0]->{shipped_qty}, 5, "require_stock_out => 1: first sales orderitem has no shipped_qty");
199 ok($sales_order->items_sorted->[0]->{delivered}, "require_stock_out => 1: first sales orderitem is not delivered");
201 $orderitem_part1 = SL::DB::Manager::OrderItem->find_by(parts_id => $part1->id, trans_id => $sales_order->id);
203 is($orderitem_part1->shipped_qty(require_stock_out => 1), 5, "OrderItem shipped_qty from helper ok");
207 my $number_of_linked_items = SL::DB::Manager::RecordLink->get_all_count( where => [ from_table => 'orderitems', to_table => 'delivery_order_items' ] );
208 is ($number_of_linked_items , 6, "6 record_links for items, 3 from sales order, 3 from purchase order");
210 note('testing optional orderitems');
212 my $item_optional = create_order_item(part => $part3, qty => 7, optional => 1);
213 ok($item_optional->{optional}, "optional order item");
215 my $sales_order_opt = create_sales_order(
217 orderitems => [ create_order_item(part => $part1, qty => 5),
218 create_order_item(part => $part2, qty => 6),
224 SL::Helper::ShippedQty
225 ->new(require_stock_out => 1) # should make no difference while there is no delivery order
226 ->calculate($sales_order_opt)
229 is($sales_order_opt->items_sorted->[2]->{shipped_qty}, 0, "third optional sales orderitem has no shipped_qty");
230 ok(!$sales_order_opt->items_sorted->[2]->{delivered}, "third optional sales orderitem is not delivered");
231 ok($sales_order_opt->items_sorted->[2]->{optional}, "third optional sales orderitem is optional");
233 my $orderitem_part3_opt = SL::DB::Manager::OrderItem->find_by(parts_id => $part3->id, trans_id => $sales_order_opt->id);
234 is($orderitem_part3_opt->shipped_qty, 0, "OrderItem shipped_qty method ok");
236 # create sales delivery order from sales order
237 my $sales_delivery_order_opt = $sales_order_opt->convert_to_delivery_order;
238 is(scalar @{ $sales_delivery_order_opt->items_sorted }, 3, "third optional sales delivery orderitem is there");
240 # and delete third item
241 my $optional = SL::DB::Manager::DeliveryOrderItem->find_by(parts_id => $part3->id, delivery_order_id => $sales_delivery_order_opt->id);
242 SL::DB::DeliveryOrderItem->new(id => $optional->id)->delete;
243 $sales_delivery_order_opt->save(cascade => 1);
244 my $new_sales_delivery_order_opt = SL::DB::Manager::DeliveryOrder->find_by(id => $sales_delivery_order_opt->id);
245 is(scalar @{ $new_sales_delivery_order_opt->items_sorted }, 2, "third optional sales delivery orderitem is undef");
247 SL::Helper::ShippedQty
248 ->new(require_stock_out => 0)
249 ->calculate($sales_order_opt)
252 is($sales_order_opt->items_sorted->[0]->{shipped_qty}, 5, "require_stock_out => 0: first sales orderitem has shipped_qty");
253 ok($sales_order_opt->items_sorted->[0]->{delivered}, "require_stock_out => 0: first sales orderitem is delivered");
254 ok($sales_order_opt->items_sorted->[1]->{delivered}, "require_stock_out => 0: second sales orderitem is delivered");
255 ok(!$sales_order_opt->items_sorted->[2]->{delivered}, "require_stock_out => 0: third sales orderitem is NOT delivered");
256 is($sales_order_opt->items_sorted->[2]->{shipped_qty}, 0, "require_stock_out => 0: third sales orderitem has no shipped_qty");
257 ok($sales_order_opt->{delivered}, "require_stock_out => 0: order IS delivered");
262 # legacy unlinked scenario:
264 # order with two positions of the same part, qtys: 5, 3.
265 # 3 linked delivery orders, with positions:
267 # 2: 1 linked to 1, 3 linked to 2
270 # should be resolved under fill_up as 5/3, but gets resolved as 4/4
271 my $part = new_part()->save;
272 my $order = create_sales_order(
274 create_order_item(part => $part, qty => 5),
275 create_order_item(part => $part, qty => 3),
278 my $do1 = create_sales_delivery_order(
280 create_delivery_order_item(part => $part, qty => 3),
283 my $do2 = create_sales_delivery_order(
285 create_delivery_order_item(part => $part, qty => 1),
286 create_delivery_order_item(part => $part, qty => 3),
289 my $do3 = create_sales_delivery_order(
291 create_delivery_order_item(part => $part, qty => 1),
294 $order->link_to_record($do1);
295 $order->link_to_record($do2);
296 $order->items_sorted->[0]->link_to_record($do2->items_sorted->[0]);
297 $order->items_sorted->[1]->link_to_record($do2->items_sorted->[1]);
298 $order->link_to_record($do3);
299 $order->items_sorted->[0]->link_to_record($do3->items->[0]);
301 SL::Helper::ShippedQty
302 ->new(fill_up => 1, require_stock_out => 0)
306 is $order->items_sorted->[0]->{shipped_qty}, 5, 'unlinked legacy position test 1';
307 is $order->items_sorted->[1]->{shipped_qty}, 3, 'unlinked legacy position test 2';
314 # suppose an order was delivered, and someone removes one item from the delivery order.
315 # make sure the order is then shown as not delivered.
317 my $sales_order = create_sales_order(
319 orderitems => [ create_order_item(part => new_part()->save, qty => 5),
320 create_order_item(part => new_part()->save, qty => 6),
321 create_order_item(part => new_part()->save, qty => 7),
326 my $delivery_order = SL::DB::DeliveryOrder->new_from($sales_order);
327 $delivery_order->save;
329 $delivery_order->items(@{ $delivery_order->items_sorted }[0..1]);
330 $delivery_order->save;
332 SL::Helper::ShippedQty
333 ->new(fill_up => 0, require_stock_out => 0)
334 ->calculate($sales_order)
337 ok !$sales_order->delivered, 'after deleting a position from a delivery order, the order is undelivered again';
345 foreach ( qw(Inventory DeliveryOrderItem DeliveryOrder Price OrderItem Order Part Customer Vendor Bin Warehouse) ) {
346 "SL::DB::Manager::${_}"->delete_all(all => 1);