doc: Sicherheitshinweise in Bezug auf SQL-Injections und XSS
[kivitendo-erp.git] / t / helper / shipped_qty.t
1 use strict;
2 use Test::More;
3
4 use lib 't';
5 use Support::TestSetup;
6 use Carp;
7 use Test::Exception;
8 use Data::Dumper;
9 use SL::DB::Part;
10 use SL::DB::Inventory;
11 use SL::DB::TransferType;
12 use SL::DB::Order;
13 use SL::DB::DeliveryOrder;
14 use SL::DB::Customer;
15 use SL::DB::Vendor;
16 use SL::DB::RecordLink;
17 use SL::DB::DeliveryOrderItemsStock;
18 use SL::DB::Bin;
19 use SL::WH;
20 use SL::AM;
21 use SL::Dev::ALL qw(:ALL);
22 use SL::Helper::ShippedQty;
23 use DateTime;
24
25 Support::TestSetup::login();
26
27 clear_up();
28
29 my ($customer, $vendor, @parts, $unit);
30
31 $customer = new_customer(name => 'Testkunde'    )->save;
32 $vendor   = new_vendor(  name => 'Testlieferant')->save;
33
34 my $default_sellprice = 10;
35 my $default_lastcost  =  4;
36
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");
40
41 my %part_defaults = (
42     sellprice    => $default_sellprice,
43     warehouse_id => $wh->id,
44     bin_id       => $bin1->id
45 );
46
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;
50 };
51
52 my $part1 = SL::DB::Manager::Part->find_by( partnumber => '1' );
53 my $part2 = SL::DB::Manager::Part->find_by( partnumber => '2' );
54 my $part3 = SL::DB::Manager::Part->find_by( partnumber => '3' );
55 my $part4 = SL::DB::Manager::Part->find_by( partnumber => '4' );
56
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');
60
61
62 # test purchases first, so there is actually stock available when sales is tested
63
64 note("testing purchases, no fill_up");
65
66 my $purchase_order = create_purchase_order(
67   save       => 1,
68   orderitems => [ create_order_item(part => $part1, qty => 11),
69                   create_order_item(part => $part2, qty => 12),
70                   create_order_item(part => $part3, qty => 13),
71                 ]
72 );
73
74 Rose::DB::Object::Helpers::forget_related($purchase_order, 'orderitems');
75 $purchase_order->orderitems;
76
77 SL::Helper::ShippedQty
78   ->new(require_stock_out => 1)  # should make no difference while there is no delivery order
79   ->calculate($purchase_order)
80   ->write_to_objects;
81
82 is($purchase_order->items_sorted->[0]->{shipped_qty}, 0, "first purchase orderitem has no shipped_qty");
83 ok(!$purchase_order->items_sorted->[0]->{delivered},     "first purchase orderitem is not delivered");
84
85 my $purchase_orderitem_part1 = SL::DB::Manager::OrderItem->find_by( parts_id => $part1->id, trans_id => $purchase_order->id);
86
87 is($purchase_orderitem_part1->shipped_qty, 0, "OrderItem shipped_qty method ok");
88
89 is($purchase_order->closed,     0, 'purchase order is open');
90 ok(!$purchase_order->delivered,    'purchase order is not delivered');
91
92 note('converting purchase order to delivery order');
93 # create purchase delivery order from purchase order
94 my $purchase_delivery_order = $purchase_order->convert_to_delivery_order;
95 is($purchase_order->closed,    0, 'purchase order is open');
96 ok($purchase_order->delivered,    'purchase order is now delivered');
97
98 SL::Helper::ShippedQty
99   ->new(require_stock_out => 0)
100   ->calculate($purchase_order)
101   ->write_to_objects;
102
103 is($purchase_order->items_sorted->[0]->{shipped_qty}, 11, "require_stock_out => 0: first purchase orderitem has shipped_qty");
104 ok($purchase_order->items_sorted->[0]->{delivered},       "require_stock_out => 0: first purchase orderitem is delivered");
105
106 Rose::DB::Object::Helpers::forget_related($purchase_order, 'orderitems');
107 $purchase_order->orderitems;
108
109 SL::Helper::ShippedQty
110   ->new(require_stock_out => 1)
111   ->calculate($purchase_order)
112   ->write_to_objects;
113
114 is($purchase_order->items_sorted->[0]->{shipped_qty}, 0,  "require_stock_out => 1: first purchase orderitem has no shipped_qty");
115 ok(!$purchase_order->items_sorted->[0]->{delivered},      "require_stock_out => 1: first purchase orderitem is not delivered");
116
117 # ship items from delivery order
118 transfer_purchase_delivery_order($purchase_delivery_order);
119
120 Rose::DB::Object::Helpers::forget_related($purchase_order, 'orderitems');
121 $purchase_order->orderitems;
122
123 SL::Helper::ShippedQty
124   ->new(require_stock_out => 1, keep_matches => 1)  # shouldn't make a difference now after shipping
125   ->calculate($purchase_order)
126   ->write_to_objects;
127
128 is($purchase_order->items_sorted->[0]->{shipped_qty}, 11, "require_stock_out => 1: first purchase orderitem has shipped_qty");
129 ok($purchase_order->items_sorted->[0]->{delivered},       "require_stock_out => 1: first purchase orderitem is delivered");
130
131 my $purchase_orderitem_part2 = SL::DB::Manager::OrderItem->find_by(parts_id => $part1->id, trans_id => $purchase_order->id);
132
133 is($purchase_orderitem_part2->shipped_qty(require_stock_out => 1), 11, "OrderItem shipped_qty from helper ok");
134
135
136 note('testing sales, no fill_up');
137
138 my $sales_order = create_sales_order(
139   save       => 1,
140   orderitems => [ create_order_item(part => $part1, qty => 5),
141                   create_order_item(part => $part2, qty => 6),
142                   create_order_item(part => $part3, qty => 7),
143                 ]
144 );
145
146 Rose::DB::Object::Helpers::forget_related($sales_order, 'orderitems');
147 $sales_order->orderitems;
148
149 SL::Helper::ShippedQty
150   ->new(require_stock_out => 1)  # should make no difference while there is no delivery order
151   ->calculate($sales_order)
152   ->write_to_objects;
153
154 is($sales_order->items_sorted->[0]->{shipped_qty}, 0,  "first sales orderitem has no shipped_qty");
155 ok(!$sales_order->items_sorted->[0]->{delivered},      "first sales orderitem is not delivered");
156
157 my $orderitem_part1 = SL::DB::Manager::OrderItem->find_by(parts_id => $part1->id, trans_id => $sales_order->id);
158 my $orderitem_part2 = SL::DB::Manager::OrderItem->find_by(parts_id => $part2->id, trans_id => $sales_order->id);
159
160 is($orderitem_part1->shipped_qty, 0, "OrderItem shipped_qty method ok");
161
162 # create sales delivery order from sales order
163 my $sales_delivery_order = $sales_order->convert_to_delivery_order;
164
165 SL::Helper::ShippedQty
166   ->new(require_stock_out => 0)
167   ->calculate($sales_order)
168   ->write_to_objects;
169
170 is($sales_order->items_sorted->[0]->{shipped_qty}, 5, "require_stock_out => 0: first sales orderitem has shipped_qty");
171 ok($sales_order->items_sorted->[0]->{delivered},      "require_stock_out => 0: first sales orderitem is delivered");
172
173 Rose::DB::Object::Helpers::forget_related($sales_order, 'orderitems');
174 $sales_order->orderitems;
175
176 SL::Helper::ShippedQty
177   ->new(require_stock_out => 1)
178   ->calculate($sales_order)
179   ->write_to_objects;
180
181 is($sales_order->items_sorted->[0]->{shipped_qty}, 0,  "require_stock_out => 1: first sales orderitem has no shipped_qty");
182 ok(!$sales_order->items_sorted->[0]->{delivered},      "require_stock_out => 1: first sales orderitem is not delivered");
183
184 # ship items from delivery order
185 transfer_sales_delivery_order($sales_delivery_order);
186
187 Rose::DB::Object::Helpers::forget_related($sales_order, 'orderitems');
188 $sales_order->orderitems;
189
190 SL::Helper::ShippedQty
191   ->new(require_stock_out => 1)
192   ->calculate($sales_order)
193   ->write_to_objects;
194
195 is($sales_order->items_sorted->[0]->{shipped_qty}, 5, "require_stock_out => 1: first sales orderitem has no shipped_qty");
196 ok($sales_order->items_sorted->[0]->{delivered},      "require_stock_out => 1: first sales orderitem is not delivered");
197
198 $orderitem_part1 = SL::DB::Manager::OrderItem->find_by(parts_id => $part1->id, trans_id => $sales_order->id);
199
200 is($orderitem_part1->shipped_qty(require_stock_out => 1), 5, "OrderItem shipped_qty from helper ok");
201
202
203 note('misc tests');
204 my $number_of_linked_items = SL::DB::Manager::RecordLink->get_all_count( where => [ from_table => 'orderitems', to_table => 'delivery_order_items' ] );
205 is ($number_of_linked_items , 6, "6 record_links for items, 3 from sales order, 3 from purchase order");
206
207 clear_up();
208
209 {
210 #  legacy unlinked scenario:
211 #
212 #  order with two positions of the same part, qtys: 5, 3.
213 #  3 linked delivery orders, with positions:
214 #    1:  3 unlinked
215 #    2:  1 linked to 1, 3 linked to 2
216 #    3:  1 linked to 1
217 #
218 #  should be resolved under fill_up as 5/3, but gets resolved as 4/4
219   my $part = new_part()->save;
220   my $order = create_sales_order(
221     orderitems => [
222       create_order_item(part => $part, qty => 5),
223       create_order_item(part => $part, qty => 3),
224     ],
225   )->save;
226   my $do1 = create_sales_delivery_order(
227     orderitems => [
228       create_delivery_order_item(part => $part, qty => 3),
229     ],
230   );
231   my $do2 = create_sales_delivery_order(
232     orderitems => [
233       create_delivery_order_item(part => $part, qty => 1),
234       create_delivery_order_item(part => $part, qty => 3),
235     ],
236   );
237   my $do3 = create_sales_delivery_order(
238     orderitems => [
239       create_delivery_order_item(part => $part, qty => 1),
240     ],
241   );
242   $order->link_to_record($do1);
243   $order->link_to_record($do2);
244   $order->items_sorted->[0]->link_to_record($do2->items_sorted->[0]);
245   $order->items_sorted->[1]->link_to_record($do2->items_sorted->[1]);
246   $order->link_to_record($do3);
247   $order->items_sorted->[0]->link_to_record($do3->items->[0]);
248
249   SL::Helper::ShippedQty
250     ->new(fill_up => 1, require_stock_out => 0)
251     ->calculate($order)
252     ->write_to_objects;
253
254   is $order->items_sorted->[0]->{shipped_qty}, 5, 'unlinked legacy position test 1';
255   is $order->items_sorted->[1]->{shipped_qty}, 3, 'unlinked legacy position test 2';
256 }
257
258 clear_up();
259
260 done_testing;
261
262 sub clear_up {
263   foreach ( qw(Inventory DeliveryOrderItem DeliveryOrder Price OrderItem Order Part Customer Vendor Bin Warehouse) ) {
264     "SL::DB::Manager::${_}"->delete_all(all => 1);
265   }
266 };