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

Sponsored-by: Martin Renvoize <martin.renvoize@gmail.com>
Signed-off-by: Hannah Dunne-Howrie <hdunne-howrie@westminster.gov.uk>
Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
This commit is contained in:
Martin Renvoize 2026-02-12 18:24:11 +00:00 committed by Pedro Amorim
parent 8d1e208a7b
commit 4f2829c639
No known key found for this signature in database
GPG Key ID: 06B75BA7F3894D55
2 changed files with 83 additions and 18 deletions

View File

@ -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 );
@ -958,17 +959,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;

View File

@ -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;
@ -432,7 +434,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) {
@ -616,13 +621,38 @@ sub gir_segments {
{ identity_number => 'LSM', data => encode_text( $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 => encode_text( $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 => encode_text( $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;
@ -630,7 +660,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} );