From 0ae23a0f6d32aa9e059b7342ede47d669240ca86 Mon Sep 17 00:00:00 2001 From: Martin Renvoize Date: Thu, 12 Feb 2026 18:24:11 +0000 Subject: [PATCH] Bug 30144: Process and send servicing instructions in EDI messages This patch updates the EDIFACT processing to handle multiple servicing instruction groups in both incoming quotes and outgoing orders. Changes to Koha/EDI.pm (quote_item): - Reads both LVC (coded) and LVT (freetext) instructions from incoming quotes - Validates LVC codes against EDIFACT_SI authorized values - Stores instructions as JSON array structure - Groups instructions that work together Changes to Koha/Edifact/Order.pm (gir_segments): - Parses JSON servicing instruction data from order - Generates multiple GIR segments for outgoing orders - Each instruction becomes a separate GIR element - Logs JSON parse errors to EDI log using Koha::Logger - Fixed element counter reset bug when segments exceed 5 elements Structure allows vendors to specify: - Single coded instruction: [[{type:"LVC",value:"BB"}]] - Single text instruction: [[{type:"LVT",value:"Handle with care"}]] - Combined instructions: [[{type:"LVC",value:"BB"},{type:"LVT",value:"Apply on spine"}]] - Multiple services: [[{type:"LVC",value:"BB"}],[{type:"LVC",value:"BI"}]] Test plan: 1. Receive EDIFACT quote with servicing instructions 2. Verify instructions are stored correctly in aqorders 3. Create EDI order from basket 4. Verify GIR segments are generated correctly in outgoing message 5. Check EDI log for any JSON parse errors --- Koha/EDI.pm | 52 +++++++++++++++++++++++++++++++++++-------- Koha/Edifact/Order.pm | 49 ++++++++++++++++++++++++++++++++-------- 2 files changed, 83 insertions(+), 18 deletions(-) diff --git a/Koha/EDI.pm b/Koha/EDI.pm index 5fa71cac675..f3023e7494b 100644 --- a/Koha/EDI.pm +++ b/Koha/EDI.pm @@ -34,6 +34,7 @@ use utf8; use English qw{ -no_match_vars }; use Business::ISBN; use DateTime; +use JSON qw( encode_json ); use C4::Context; use Koha::Database; use Koha::DateUtils qw( dt_from_string ); @@ -916,17 +917,50 @@ sub quote_item { $order_hash->{line_item_id} = $item->item_number_id; } - if ( $item->girfield('servicing_instruction') ) { - my $occ = 0; - my $txt = q{}; - my $si; - while ( $si = $item->girfield( 'servicing_instruction', $occ ) ) { - if ($occ) { - $txt .= q{: }; + # Extract servicing instructions (LVT freetext and/or LVC coded) + # Build JSON array structure: [[{type,value},...]] + my @servicing_groups; + my @current_group; + + # Read LVC (coded) instructions + my $occ = 0; + my $lvc_val; + while ( $lvc_val = $item->girfield( 'coded_servicing_instruction', $occ ) ) { + + # Validate against EDIFACT_SI authorized values + my $av = Koha::AuthorisedValues->find( + { + category => 'EDIFACT_SI', + authorised_value => $lvc_val } - $txt .= $si; - ++$occ; + ); + if ($av) { + push @current_group, { type => 'LVC', value => $lvc_val }; + } else { + $quote_message->add_to_edifact_errors( + { + section => "GIR+LVC validation", + details => + "Invalid servicing instruction code '$lvc_val' - not found in EDIFACT_SI authorized values" + } + ); + $logger->trace("Invalid servicing instruction code: $lvc_val"); } + ++$occ; + } + + # Read LVT (freetext) instructions + $occ = 0; + my $lvt_val; + while ( $lvt_val = $item->girfield( 'servicing_instruction', $occ ) ) { + push @current_group, { type => 'LVT', value => $lvt_val }; + ++$occ; + } + + # Store as JSON if we have any instructions + if (@current_group) { + push @servicing_groups, \@current_group; + $order_hash->{servicing_instruction} = encode_json( \@servicing_groups ); } if ($order_note) { $order_hash->{order_vendornote} = $order_note; diff --git a/Koha/Edifact/Order.pm b/Koha/Edifact/Order.pm index c6c98cee842..158bdeaf6d1 100644 --- a/Koha/Edifact/Order.pm +++ b/Koha/Edifact/Order.pm @@ -26,7 +26,9 @@ use DateTime; use Readonly qw( Readonly ); use Koha::Database; use Koha::DateUtils qw( dt_from_string ); -use C4::Budgets qw( GetBudget ); +use Koha::Logger; +use C4::Budgets qw( GetBudget ); +use JSON qw( decode_json ); use Koha::Acquisition::Orders; @@ -429,7 +431,10 @@ sub order_line { } } my $budget = GetBudget( $orderline->budget_id ); - my $ol_fields = { budget_code => $budget->{budget_code}, }; + my $ol_fields = { + budget_code => $budget->{budget_code}, + servicing_instruction => $orderline->servicing_instruction, + }; my $item_fields = []; for my $item (@items) { @@ -613,13 +618,38 @@ sub gir_segments { { identity_number => 'LSM', data => $item->{itemcallnumber} }; } - # itemcallnumber -> shelfmark + # Servicing instructions (LVT freetext and/or LVC coded) + # Format: JSON array of arrays: [[{type,value},...],[{type,value},...]] + # Each inner array is a group of instructions that work together if ( $orderfields->{servicing_instruction} ) { - push @gir_elements, - { - identity_number => 'LVT', - data => $orderfields->{servicing_instruction} - }; + my $si_data = $orderfields->{servicing_instruction}; + my $servicing_groups; + + eval { $servicing_groups = decode_json($si_data); }; + if ($@) { + my $logger = Koha::Logger->get( { interface => 'edi' } ); + $logger->warn("Failed to parse servicing_instruction JSON: $@"); + } + + # Add all servicing instruction groups + if ( ref $servicing_groups eq 'ARRAY' ) { + foreach my $group (@$servicing_groups) { + if ( ref $group eq 'ARRAY' ) { + foreach my $instruction (@$group) { + if ( ref $instruction eq 'HASH' + && $instruction->{type} + && $instruction->{value} ) + { + push @gir_elements, + { + identity_number => $instruction->{type}, + data => $instruction->{value} + }; + } + } + } + } + } } my $e_cnt = 0; # count number of elements so we dont exceed 5 per segment my $copy_no = sprintf 'GIR+%03d', $sequence_no; @@ -627,7 +657,8 @@ sub gir_segments { foreach my $e (@gir_elements) { if ( $e_cnt == 5 ) { push @segments, $seg; - $seg = $copy_no; + $seg = $copy_no; + $e_cnt = 0; } $seg .= add_gir_identity_number( $e->{identity_number}, $e->{data} ); -- 2.53.0