CsvImport Lieferscheine: Zusammenfassen von Lieferpositionen
authorBernd Bleßmann <bernd@kivitendo-premium.de>
Mon, 6 Sep 2021 14:25:50 +0000 (16:25 +0200)
committerBernd Bleßmann <bernd@kivitendo-premium.de>
Fri, 10 Sep 2021 13:19:41 +0000 (15:19 +0200)
Wenn in mehreren Positionen Teilmengen einer Position eines Auftrags
geliefert werden, dann diese Zusammenfassen.

SL/Controller/CsvImport/DeliveryOrder.pm
t/controllers/csvimport/delivery_orders.t

index b662787..b93455a 100644 (file)
@@ -711,6 +711,10 @@ sub handle_order_sources {
     push @{ $entry->{errors} }, $::locale->text('Error: More than one source order found');
   }
 
+  foreach my $order (@$orders) {
+    $self->{remaining_source_qtys_by_item_id} = { map { $_->id => $_->qty } @{ $order->items } };
+  }
+
   $record->{source_orders} = $orders;
 }
 
@@ -722,9 +726,38 @@ sub handle_item_source {
 
   return if !@{ $record->{source_orders} };
 
+  # Todo: units?
+
   foreach my $order (@{ $record->{source_orders} }) {
-    $item->{source_item} = first { $item->parts_id == $_->parts_id && $item->qty == $_->qty} @{ $order->items_sorted };
-    last if $item->{source_item};
+    # First: Excact matches and source order position is still complete.
+    $item->{source_item} = first {
+         $item->parts_id                                     == $_->parts_id
+      && $item->qty                                          == $_->qty
+      && $self->{remaining_source_qtys_by_item_id}->{$_->id} == $_->qty
+    } @{ $order->items_sorted };
+    if ($item->{source_item}) {
+      $self->{remaining_source_qtys_by_item_id}->{$item->{source_item}->id} -= $item->qty;
+      last;
+    }
+
+    # Second: Smallest remaining order qty greater or equal delivery order qty.
+    $item->{source_item} = first {
+         $item->parts_id                                     == $_->parts_id
+      && $self->{remaining_source_qtys_by_item_id}->{$_->id} >= $item->qty
+    } sort { $self->{remaining_source_qtys_by_item_id}->{$a->id} <=> $self->{remaining_source_qtys_by_item_id}->{$b->id} } @{ $order->items_sorted };
+    if ($item->{source_item}) {
+      $self->{remaining_source_qtys_by_item_id}->{$item->{source_item}->id} -= $item->qty;
+      last;
+    }
+
+    # Last: Overdelivery?
+    # $item->{source_item} = first {
+    #      $item->parts_id == $_->parts_id
+    # } @{ $order->items_sorted };
+    # if ($item->{source_item}) {
+    #   $self->{remaining_source_qtys_by_item_id}->{$item->{source_item}->id} -= $item->qty;
+    #   last;
+    # }
   }
 }
 
index e7e7914..ca38c2d 100644 (file)
@@ -1,4 +1,4 @@
-use Test::More tests => 15;
+use Test::More tests => 28;
 
 use strict;
 
@@ -117,7 +117,8 @@ clear_up;
     ordnumber  => '1234',
     orderitems => [ create_order_item(part => $parts[0], qty =>  3, sellprice => 70),
                     create_order_item(part => $parts[1], qty => 10, sellprice => 50),
-                    create_order_item(part => $parts[2], qty =>  8, sellprice => 80)
+                    create_order_item(part => $parts[2], qty =>  8, sellprice => 80),
+                    create_order_item(part => $parts[2], qty => 11, sellprice => 80)
     ]
   )
 );
@@ -131,12 +132,23 @@ OrderItem;TestPart1;5
 OrderItem;TestPart2;10
 OrderItem;TestPart3;7
 OrderItem;TestPart3;1
+OrderItem;TestPart3;11
 EOL
 
+  1;                            # make emacs happy
+
+# should be:
+# delivery oder pos/qty <- order pos/qty
+#       1/5             <-       -
+#       2/10            <-      2/10
+#       3/7             <-      3/7
+#       4/1             <-      3/1
+#       5/11            <-      4/11
+
 $entries = do_import($file);
+$orders[0]->load;               # reload order to get correct delivered status
 
 $entry = $entries->[0];
-
 is $entry->{object}->ordnumber, '1234', 'with source order: ordnumber';
 
 my $linked = $orders[0]->linked_records(to => 'DeliveryOrder');
@@ -159,11 +171,104 @@ ok($linked->[0]->id == $orders[0]->items_sorted->[1]->id, 'with source order: sa
 
 $entry = $entries->[3];
 $linked = $entry->{object}->linked_records(from => 'OrderItem');
-ok(scalar @$linked == 0, 'with source order: delivered qty < ordered qty: delivery order item not linked');
+ok(scalar @$linked == 1, 'with source order: delivered qty < ordered qty: delivery order item linked (fill up)');
+ok($linked->[0]->position == 3, 'with source order: delivered qty < ordered qty: order position ok (fill up)');
+
+$entry = $entries->[4];
+$linked = $entry->{object}->linked_records(from => 'OrderItem');
+ok(scalar @$linked == 1, 'with source order: delivered qty < ordered qty: delivery order item linked (fill up) 2');
+ok($linked->[0]->position == 3, 'with source order: delivered qty < ordered qty: order position ok (fill up) 2');
+
+$entry = $entries->[5];
+$linked = $entry->{object}->linked_records(from => 'OrderItem');
+ok(scalar @$linked == 1, 'with source order: delivered qty == ordered qty: found exact match after fill up');
+ok($linked->[0]->position == 4, 'with source order: delivered qty == ordered qty: order position ok after fill up');
+
+ok(!$orders[0]->delivered, 'with source order: order not completely delivered');
+
+$entries = undef;
+clear_up;
 
 #####
+@customers = (new_customer(name => 'TestCustomer1', discount => 0)->save);
+@parts = (
+  new_part(description => 'TestPart1', ean => '')->save,
+  new_part(description => 'TestPart2', ean => '')->save,
+  new_part(description => 'TestPart3', ean => '')->save
+);
+@orders = (
+  create_sales_order(
+    save       => 1,
+    customer   => $customers[0],
+    ordnumber  => '1234',
+    orderitems => [ create_order_item(part => $parts[0], qty =>  3, sellprice => 70),
+                    create_order_item(part => $parts[1], qty => 10, sellprice => 50),
+                    create_order_item(part => $parts[2], qty =>  8, sellprice => 80),
+                    create_order_item(part => $parts[2], qty => 11, sellprice => 80)
+    ]
+  )
+);
+
+$file = \<<EOL;
+datatype;customer;ordnumber
+datatype;description;qty
+datatype
+DeliveryOrder;TestCustomer1;1234
+OrderItem;TestPart1;1
+OrderItem;TestPart2;10
+OrderItem;TestPart1;2
+OrderItem;TestPart3;7
+OrderItem;TestPart3;11
+OrderItem;TestPart3;1
+
+EOL
+
+  1;                            # make emacs happy
+
+# should be:
+# delivery oder pos/qty <- order pos/qty
+#       1/1             <-      1/1
+#       2/10            <-      2/10
+#       3/2             <-      1/2
+#       4/7             <-      3/7
+#       5/11            <-      4/11
+#       6/1             <-      3/1
+
+$entries = do_import($file);
+$orders[0]->load;               # reload order to get correct delivered status
+
+$entry = $entries->[1];
+$linked = $entry->{object}->linked_records(from => 'OrderItem');
+ok($linked->[0]->position == 1, 'with source order: mixed qtys and fill up: order position ok 1');
+
+$entry = $entries->[2];
+$linked = $entry->{object}->linked_records(from => 'OrderItem');
+ok($linked->[0]->position == 2, 'with source order: mixed qtys and fill up: order position ok 2');
+
+$entry = $entries->[3];
+$linked = $entry->{object}->linked_records(from => 'OrderItem');
+ok($linked->[0]->position == 1, 'with source order: mixed qtys and fill up: order position ok 3');
+
+$entry = $entries->[4];
+$linked = $entry->{object}->linked_records(from => 'OrderItem');
+ok($linked->[0]->position == 3, 'with source order: mixed qtys and fill up: order position ok 4');
+
+$entry = $entries->[5];
+$linked = $entry->{object}->linked_records(from => 'OrderItem');
+ok($linked->[0]->position == 4, 'with source order: mixed qtys and fill up: order position ok 5');
+
+$entry = $entries->[6];
+$linked = $entry->{object}->linked_records(from => 'OrderItem');
+ok($linked->[0]->position == 3, 'with source order: mixed qtys and fill up: order position ok 6');
+
+ok(!!$orders[0]->delivered, 'with source order: mixed qtys and fill up: order completely delivered');
+
+#####
+$entries = undef;
 clear_up;
 
+#####
+
 $::myconfig{numberformat} = $old_numberformat;
 $::locale                 = $old_locale;