From 036858786568807dbedc45a4d1ebdf038b1aaeaa Mon Sep 17 00:00:00 2001 From: Martin Renvoize Date: Tue, 8 Jul 2025 15:19:10 +0100 Subject: [PATCH] Bug 20253: (follow-up) Use purchase order number in BGM segment for outgoing ORDER messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When vendors are configured to use purchase order numbers for basket naming, the purchase order number should be included in outgoing ORDER messages as the document message number in the BGM segment for proper EDI workflow continuity. According to EDIFACT specifications, the purchase order number from incoming quote messages (RFF+ON segments) should be used as the document message number in the BGM segment of outgoing ORDER messages, rather than as a separate RFF+ON segment. This follow-up ensures that: 1. Purchase order numbers from incoming QUOTE messages are preserved 2. The same purchase order numbers are sent back in ORDER messages via BGM segment 3. Vendors receive the reference numbers they expect for order processing in the correct format. Implementation: - Added get_purchase_order_number() method to extract PO number from basket name when vendor is configured with 'po_is_basketname' - Modified beginning_of_message() to accept purchase order number parameter - Uses purchase order number in BGM segment when available, falls back to basketno - Added comprehensive tests for purchase order number handling in ORDER messages Test Plan: 1. Apply patches 2. Configure a vendor EDI account with "Basket name source" = "Purchase order number" 3. Process a QUOTE message containing RFF+ON segment 4. Verify basket is created with purchase order number as name 5. Generate ORDER message from the basket 6. Verify ORDER message contains purchase order number in BGM segment 7. Run tests: prove t/db_dependent/Koha/Edifact/Order.t This completes the round-trip EDI workflow for purchase order number handling according to EDIFACT standards: Quote RFF+ON → Basket Name → Order BGM --- Koha/Edifact/Order.pm | 31 +++++- t/db_dependent/Koha/Edifact/Order.t | 153 +++++++++++++++++++++++++++- 2 files changed, 176 insertions(+), 8 deletions(-) diff --git a/Koha/Edifact/Order.pm b/Koha/Edifact/Order.pm index 0bf67829cae..b1f3f03df82 100644 --- a/Koha/Edifact/Order.pm +++ b/Koha/Edifact/Order.pm @@ -263,7 +263,8 @@ sub order_msg_header { beginning_of_message( $self->{basket}->basketno, $self->{recipient}->standard, - $self->{is_response} + $self->{is_response}, + $self->purchase_order_number ); # DTM @@ -295,10 +296,13 @@ sub order_msg_header { } sub beginning_of_message { - my $basketno = shift; - my $standard = shift; - my $response = shift; - my $document_message_no = sprintf '%011d', $basketno; + my $basketno = shift; + my $standard = shift; + my $response = shift; + my $purchase_order_number = shift; + + # Use purchase order number if available, otherwise use basketno + my $document_message_no = $purchase_order_number ? $purchase_order_number : sprintf '%011d', $basketno; # my $message_function = 9; # original 7 = retransmission # message_code values @@ -332,6 +336,19 @@ sub name_and_address { return "NAD+$qualifier_code{$party}+${id_code}::$id_agency$seg_terminator"; } +sub purchase_order_number { + my $self = shift; + + # If the vendor EDI account is configured to use purchase order numbers for basket names, + # then the basket name IS the purchase order number + my $vendor_config = $self->{recipient}; + if ( $vendor_config && $vendor_config->po_is_basketname ) { + return $self->{basket}->basketname; + } + + return; +} + sub order_line { my ( $self, $linenumber, $orderline ) = @_; @@ -805,6 +822,10 @@ Make handling of GIR segments more customizable value. Handles the fact that NAD segments encode the value for 'EAN' differently to elsewhere. +=head2 purchase_order_number + + Returns the purchase_order_number given the edi vendor configuration + =head2 order_line Creates the message segments wncoding an order line diff --git a/t/db_dependent/Koha/Edifact/Order.t b/t/db_dependent/Koha/Edifact/Order.t index 3a565a93bff..c9854f4e694 100755 --- a/t/db_dependent/Koha/Edifact/Order.t +++ b/t/db_dependent/Koha/Edifact/Order.t @@ -20,7 +20,7 @@ use Modern::Perl; use Test::NoWarnings; -use Test::More tests => 4; +use Test::More tests => 5; use Koha::Edifact::Order; @@ -32,7 +32,7 @@ my $schema = Koha::Database->new->schema; my $builder = t::lib::TestBuilder->new; subtest 'beggining_of_message tests' => sub { - plan tests => 2; + plan tests => 3; $schema->storage->txn_begin; @@ -54,6 +54,15 @@ subtest 'beggining_of_message tests' => sub { $bgm = Koha::Edifact::Order::beginning_of_message( $basketno, $dbic_edi_vendor->standard, 1 ); is( $bgm, qq{BGM+22V+$basketno+9'}, "When vendor is set to BiC standard we use 22V in BGM segment" ); + # Test BGM with purchase order number + my $purchase_order_number = 'PO123456789'; + $bgm = + Koha::Edifact::Order::beginning_of_message( $basketno, $dbic_edi_vendor->standard, 1, $purchase_order_number ); + is( + $bgm, qq{BGM+22V+$purchase_order_number+9'}, + "When purchase order number provided, it's used in BGM segment instead of basketno" + ); + $schema->storage->txn_rollback; }; @@ -264,10 +273,12 @@ subtest 'filename() tests' => sub { my @orders = $schema->resultset('Aqorder')->search( { basketno => $order->basket->basketno } )->all; + my $dbic_vendor = $schema->resultset('VendorEdiAccount')->find( $vendor->{id} ); + my $edi_order = Koha::Edifact::Order->new( { orderlines => \@orders, - vendor => $vendor, + vendor => $dbic_vendor, ean => $ean } ); @@ -280,3 +291,139 @@ subtest 'filename() tests' => sub { $schema->storage->txn_rollback; }; + +subtest 'RFF+ON purchase order number generation' => sub { + plan tests => 3; + + $schema->storage->txn_begin; + + # Create vendor with po_is_basketname set to true + my $vendor_po = $builder->build( + { + source => 'VendorEdiAccount', + value => { + description => 'Test vendor PO mode', + po_is_basketname => 1, + standard => 'EUR', + } + } + ); + + # Create vendor with po_is_basketname set to false (default) + my $vendor_filename = $builder->build( + { + source => 'VendorEdiAccount', + value => { + description => 'Test vendor filename mode', + po_is_basketname => 0, + standard => 'EUR', + } + } + ); + + # Create baskets with different naming schemes + my $basket_po = $builder->build( + { + source => 'Aqbasket', + value => { + basketname => 'PO123456789', # Purchase order number + booksellerid => $vendor_po->{vendor_id}, + } + } + ); + + my $basket_filename = $builder->build( + { + source => 'Aqbasket', + value => { + basketname => 'quote_file.ceq', # Filename + booksellerid => $vendor_filename->{vendor_id}, + } + } + ); + + # Create biblio records for the orders + my $biblio_po = $builder->build_sample_biblio(); + my $biblio_filename = $builder->build_sample_biblio(); + + # Create orders for the baskets + my $order_po = $builder->build( + { + source => 'Aqorder', + value => { + basketno => $basket_po->{basketno}, + biblionumber => $biblio_po->biblionumber, + orderstatus => 'new', + quantity => 1, + listprice => '10.00', + } + } + ); + + my $order_filename = $builder->build( + { + source => 'Aqorder', + value => { + basketno => $basket_filename->{basketno}, + biblionumber => $biblio_filename->biblionumber, + orderstatus => 'new', + quantity => 1, + listprice => '10.00', + } + } + ); + + # Create EAN object + my $ean = $builder->build( + { + source => 'EdifactEan', + value => { ean => '1234567890123' } + } + ); + + # Get database objects + my $dbic_vendor_po = $schema->resultset('VendorEdiAccount')->find( $vendor_po->{id} ); + my $dbic_vendor_filename = $schema->resultset('VendorEdiAccount')->find( $vendor_filename->{id} ); + my $dbic_ean = $schema->resultset('EdifactEan')->find( $ean->{ee_id} ); + my @orderlines_po = $schema->resultset('Aqorder')->search( { basketno => $basket_po->{basketno} } ); + my @orderlines_filename = $schema->resultset('Aqorder')->search( { basketno => $basket_filename->{basketno} } ); + + # Test order generation with purchase order number + my $order_obj_po = Koha::Edifact::Order->new( + { + orderlines => \@orderlines_po, + vendor => $dbic_vendor_po, + ean => $dbic_ean, + } + ); + + # Test order generation with filename + my $order_obj_filename = Koha::Edifact::Order->new( + { + orderlines => \@orderlines_filename, + vendor => $dbic_vendor_filename, + ean => $dbic_ean, + } + ); + + # Test that purchase order number is extracted correctly + is( + $order_obj_po->purchase_order_number, 'PO123456789', + 'Purchase order number extracted from basket name when vendor configured for PO mode' + ); + + # Test that no purchase order number is extracted when vendor uses filename mode + is( + $order_obj_filename->purchase_order_number, undef, + 'No purchase order number when vendor configured for filename mode' + ); + + # Test that purchase order number is included in BGM segment when purchase order number present + my $transmission = $order_obj_po->encode(); + like( + $transmission, qr/BGM\+220\+PO123456789\+9'/, + 'Purchase order number included in BGM segment when purchase order number present' + ); + + $schema->storage->txn_rollback; +}; -- 2.50.0