Bugzilla – Attachment 185234 Details for
Bug 40391
EDI: Add support for GIR:LSL field
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 40391: Add support for GIR:LSL (Library Sub-Location) in EDIFACT processing
Bug-40391-Add-support-for-GIRLSL-Library-Sub-Locat.patch (text/plain), 16.63 KB, created by
Martin Renvoize (ashimema)
on 2025-08-07 16:37:29 UTC
(
hide
)
Description:
Bug 40391: Add support for GIR:LSL (Library Sub-Location) in EDIFACT processing
Filename:
MIME Type:
Creator:
Martin Renvoize (ashimema)
Created:
2025-08-07 16:37:29 UTC
Size:
16.63 KB
patch
obsolete
>From e401b8639b8d822cea2e52dc58f84628c6fec799 Mon Sep 17 00:00:00 2001 >From: Martin Renvoize <martin.renvoize@openfifth.co.uk> >Date: Fri, 1 Aug 2025 14:30:11 +0000 >Subject: [PATCH] Bug 40391: Add support for GIR:LSL (Library Sub-Location) in > EDIFACT processing > >This implements support for the new EDItEUR/BiC GIR:LSL (Library Sub-Location) >field to complement the existing GIR:LSQ (Library Sequence) functionality, >allowing flexible mapping of both fields to location and collection item fields. > >Changes: >- Add LSL mapping to GIR field extraction in Koha::Edifact::Line >- Update gir_segments() in Koha::Edifact::Order to handle both LSL and LSQ preferences independently >- Add validation functions in Koha::EDI using Koha::AuthorisedValues for location/collection codes >- Add EdifactLSL system preference for independent LSL field mapping >- Update EdifactLSQ to include empty option for ignore functionality >- Add admin interface configuration for LSL preference > >The implementation provides maximum flexibility: >- EdifactLSQ can be set to: location, ccode, or empty (ignore) >- EdifactLSL can be set to: location, ccode, or empty (ignore) >- Both fields can be mapped independently or to same target field >- Validation ensures codes exist in LOC/CCODE authorised values >- Maintains full backwards compatibility with existing LSQ-only setups > >Test plan: >1. Apply both patches >2. Update database: installer/data/mysql/updatedatabase.pl >3. Verify new system preferences in Administration > System preferences > Acquisitions: > - EdifactLSQ: "Map EDI sequence code (GIR+LSQ) to Koha Item field" > - EdifactLSL: "Map EDI sub-location code (GIR+LSL) to Koha Item field" >4. Create test authorised values: > - LOC category: Add values like FICTION, REFERENCE > - CCODE category: Add values like ADULT, CHILD >5. Set preferences to test combinations: > - EdifactLSQ=location, EdifactLSL=ccode > - EdifactLSQ=ccode, EdifactLSL=location > - EdifactLSQ=location, EdifactLSL="" (empty) >6. Process EDIFACT messages containing GIR+LSL and GIR+LSQ segments >7. Verify items created with correct location/ccode values per preferences >8. Test invalid codes generate appropriate error messages >9. Confirm existing LSQ-only configurations continue working unchanged >10. Run: prove t/Edifact.t t/db_dependent/Koha/EDI.t t/db_dependent/Koha/Edifact/Order.t >--- > Koha/EDI.pm | 110 +++++++++++++++++- > Koha/Edifact/Line.pm | 1 + > Koha/Edifact/Order.pm | 45 +++++-- > .../data/mysql/atomicupdate/bug_40391.pl | 31 +++++ > installer/data/mysql/mandatory/sysprefs.sql | 5 +- > .../admin/preferences/acquisitions.pref | 10 ++ > 6 files changed, 190 insertions(+), 12 deletions(-) > create mode 100755 installer/data/mysql/atomicupdate/bug_40391.pl > >diff --git a/Koha/EDI.pm b/Koha/EDI.pm >index 58b3209fd14..3058a31b260 100644 >--- a/Koha/EDI.pm >+++ b/Koha/EDI.pm >@@ -44,6 +44,7 @@ use Koha::Plugins; # Adds plugin dirs to @INC > use Koha::Plugins::Handler; > use Koha::Acquisition::Baskets; > use Koha::Acquisition::Booksellers; >+use Koha::AuthorisedValues; > use Koha::Util::FrameworkPlugin qw( biblio_008 ); > > our $VERSION = 1.1; >@@ -993,7 +994,29 @@ sub quote_item { > }; > > my $lsq_field = C4::Context->preference('EdifactLSQ'); >- $new_item->{$lsq_field} = $item->girfield( 'sequence_code', $occurrence ); >+ my $lsl_field = C4::Context->preference('EdifactLSL'); >+ >+ # Handle LSQ field >+ if ( $lsq_field && $item->girfield( 'sequence_code', $occurrence ) ) { >+ my $lsq_value = $item->girfield( 'sequence_code', $occurrence ); >+ my $valid = >+ ( $lsq_field eq 'location' ) >+ ? _validate_location_code( $lsq_value, $quote_message ) >+ : _validate_collection_code( $lsq_value, $quote_message ); >+ >+ $new_item->{$lsq_field} = $lsq_value if $valid; >+ } >+ >+ # Handle LSL field >+ if ( $lsl_field && $item->girfield( 'sub_location_code', $occurrence ) ) { >+ my $lsl_value = $item->girfield( 'sub_location_code', $occurrence ); >+ my $valid = >+ ( $lsl_field eq 'location' ) >+ ? _validate_location_code( $lsl_value, $quote_message ) >+ : _validate_collection_code( $lsl_value, $quote_message ); >+ >+ $new_item->{$lsl_field} = $lsl_value if $valid; >+ } > > if ( $new_item->{itype} ) { > $item_hash->{itype} = $new_item->{itype}; >@@ -1077,7 +1100,29 @@ sub quote_item { > homebranch => $item->girfield( 'branch', $occurrence ), > }; > my $lsq_field = C4::Context->preference('EdifactLSQ'); >- $new_item->{$lsq_field} = $item->girfield( 'sequence_code', $occurrence ); >+ my $lsl_field = C4::Context->preference('EdifactLSL'); >+ >+ # Handle LSQ mapping with validation >+ if ( $lsq_field && $item->girfield( 'sequence_code', $occurrence ) ) { >+ my $lsq_value = $item->girfield( 'sequence_code', $occurrence ); >+ my $valid = >+ ( $lsq_field eq 'location' ) >+ ? _validate_location_code( $lsq_value, $quote_message ) >+ : _validate_collection_code( $lsq_value, $quote_message ); >+ >+ $new_item->{$lsq_field} = $lsq_value if $valid; >+ } >+ >+ # Handle LSL mapping with validation >+ if ( $lsl_field && $item->girfield( 'sub_location_code', $occurrence ) ) { >+ my $lsl_value = $item->girfield( 'sub_location_code', $occurrence ); >+ my $valid = >+ ( $lsl_field eq 'location' ) >+ ? _validate_location_code( $lsl_value, $quote_message ) >+ : _validate_collection_code( $lsl_value, $quote_message ); >+ >+ $new_item->{$lsl_field} = $lsl_value if $valid; >+ } > $new_item->{biblionumber} = $bib->{biblionumber}; > $new_item->{biblioitemnumber} = $bib->{biblioitemnumber}; > my $kitem = Koha::Item->new($new_item)->store; >@@ -1127,6 +1172,54 @@ sub get_edifact_ean { > return $eans->[0]; > } > >+sub _validate_location_code { >+ my ( $location_code, $quote_message ) = @_; >+ >+ return 1 unless $location_code; # Skip if empty >+ >+ my $av = Koha::AuthorisedValues->find( >+ { >+ category => 'LOC', >+ authorised_value => $location_code >+ } >+ ); >+ >+ unless ($av) { >+ $quote_message->add_to_edifact_errors( >+ { >+ section => "GIR+LSQ/LSL validation", >+ details => "Invalid location code '$location_code' - not found in LOC authorized values" >+ } >+ ); >+ return 0; >+ } >+ return 1; >+} >+ >+sub _validate_collection_code { >+ my ( $ccode, $quote_message ) = @_; >+ >+ return 1 unless $ccode; # Skip if empty >+ >+ my $av = Koha::AuthorisedValues->find( >+ { >+ category => 'CCODE', >+ authorised_value => $ccode >+ } >+ ); >+ >+ unless ($av) { >+ $quote_message->add_to_edifact_errors( >+ { >+ section => "GIR+LSQ/LSL validation", >+ details => "Invalid collection code '$ccode' - not found in CCODE authorized values" >+ } >+ ); >+ return 0; >+ } >+ return 1; >+} >+ > # We should not need to have a routine to do this here > sub _discounted_price { > my ( $discount, $price, $discounted_price ) = @_; >@@ -1280,8 +1373,19 @@ sub _create_item_from_quote { > $item_hash->{booksellerid} = $quote->vendor_id; > $item_hash->{price} = $item_hash->{replacementprice} = $item->price; > $item_hash->{itype} = $item->girfield('stock_category'); >+ > my $lsq_field = C4::Context->preference('EdifactLSQ'); >- $item_hash->{$lsq_field} = $item->girfield('sequence_code'); >+ my $lsl_field = C4::Context->preference('EdifactLSL'); >+ >+ # Handle LSQ mapping >+ if ( $lsq_field && $item->girfield('sequence_code') ) { >+ $item_hash->{$lsq_field} = $item->girfield('sequence_code'); >+ } >+ >+ # Handle LSL mapping >+ if ( $lsl_field && $item->girfield('sub_location_code') ) { >+ $item_hash->{$lsl_field} = $item->girfield('sub_location_code'); >+ } > > my $note = {}; > >diff --git a/Koha/Edifact/Line.pm b/Koha/Edifact/Line.pm >index 4c55e06f757..6feb8c865b2 100644 >--- a/Koha/Edifact/Line.pm >+++ b/Koha/Edifact/Line.pm >@@ -658,6 +658,7 @@ sub extract_gir { > LRS => 'record_sublocation', > LSM => 'shelfmark', > LSQ => 'sequence_code', >+ LSL => 'sub_location_code', > LST => 'stock_category', > LSZ => 'size_code', > LVC => 'coded_servicing_instruction', >diff --git a/Koha/Edifact/Order.pm b/Koha/Edifact/Order.pm >index 0bf67829cae..ea6ac9689ec 100644 >--- a/Koha/Edifact/Order.pm >+++ b/Koha/Edifact/Order.pm >@@ -371,6 +371,7 @@ sub order_line { > # we dont currently support this in koha > # GIR copy-related data > my $lsq_field = C4::Context->preference('EdifactLSQ'); >+ my $lsl_field = C4::Context->preference('EdifactLSL'); > my @items; > if ( $basket->effective_create_items eq 'ordering' ) { > >@@ -378,12 +379,23 @@ sub order_line { > foreach my $item (@linked_itemnumbers) { > my $i_obj = $schema->resultset('Item')->find( $item->itemnumber ); > if ( defined $i_obj ) { >- push @items, { >+ my $item_data = { > branchcode => $i_obj->get_column('homebranch'), > itype => $i_obj->effective_itemtype, >- $lsq_field => $i_obj->$lsq_field, > itemcallnumber => $i_obj->itemcallnumber, > }; >+ >+ # Handle LSQ mapping >+ if ($lsq_field) { >+ $item_data->{$lsq_field} = $i_obj->$lsq_field; >+ } >+ >+ # Handle LSL mapping >+ if ($lsl_field) { >+ $item_data->{$lsl_field} = $i_obj->$lsl_field; >+ } >+ >+ push @items, $item_data; > } > } > } else { >@@ -404,13 +416,23 @@ sub order_line { > > my $item_fields = []; > for my $item (@items) { >- push @{$item_fields}, >- { >+ my $item_field_data = { > branchcode => $item->{branchcode}, > itype => $item->{itype}, >- $lsq_field => $item->{$lsq_field}, > itemcallnumber => $item->{itemcallnumber}, >- }; >+ }; >+ >+ # Handle LSQ mapping >+ if ( $lsq_field && $item->{$lsq_field} ) { >+ $item_field_data->{$lsq_field} = $item->{$lsq_field}; >+ } >+ >+ # Handle LSL mapping >+ if ( $lsl_field && $item->{$lsl_field} ) { >+ $item_field_data->{$lsl_field} = $item->{$lsl_field}; >+ } >+ >+ push @{$item_fields}, $item_field_data; > } > $self->add_seg( > gir_segments( >@@ -541,6 +563,7 @@ sub gir_segments { > my @segments; > my $sequence_no = 1; > my $lsq_field = C4::Context->preference('EdifactLSQ'); >+ my $lsl_field = C4::Context->preference('EdifactLSL'); > foreach my $item (@onorderitems) { > my $elements_added = 0; > my @gir_elements; >@@ -556,10 +579,18 @@ sub gir_segments { > push @gir_elements, > { identity_number => 'LST', data => $item->{itype} }; > } >- if ( $item->{$lsq_field} ) { >+ >+ # Handle LSQ mapping >+ if ( $lsq_field && $item->{$lsq_field} ) { > push @gir_elements, > { identity_number => 'LSQ', data => $item->{$lsq_field} }; > } >+ >+ # Handle LSL mapping >+ if ( $lsl_field && $item->{$lsl_field} ) { >+ push @gir_elements, >+ { identity_number => 'LSL', data => $item->{$lsl_field} }; >+ } > if ( $item->{itemcallnumber} ) { > push @gir_elements, > { identity_number => 'LSM', data => $item->{itemcallnumber} }; >diff --git a/installer/data/mysql/atomicupdate/bug_40391.pl b/installer/data/mysql/atomicupdate/bug_40391.pl >new file mode 100755 >index 00000000000..2bf91805c70 >--- /dev/null >+++ b/installer/data/mysql/atomicupdate/bug_40391.pl >@@ -0,0 +1,31 @@ >+use Modern::Perl; >+use Koha::Installer::Output qw(say_warning say_success say_info); >+ >+return { >+ bug_number => "40391", >+ description => "Add EdifactLSL system preference for GIR:LSL (Library Sub-Location) field mapping", >+ up => sub { >+ my ($args) = @_; >+ my ( $dbh, $out ) = @$args{qw(dbh out)}; >+ >+ # Update existing EdifactLSQ preference to include empty option >+ $dbh->do( >+ q{ >+ UPDATE systempreferences >+ SET options = 'location|ccode|', explanation = 'Map EDI sequence code (GIR+LSQ) to Koha Item field, empty to ignore' >+ WHERE variable = 'EdifactLSQ' >+ } >+ ); >+ >+ # Add new EdifactLSL preference >+ $dbh->do( >+ q{ >+ INSERT IGNORE INTO systempreferences (variable, value, options, explanation, type) >+ VALUES ('EdifactLSL', '', 'location|ccode|', 'Map EDI sub-location code (GIR+LSL) to Koha Item field, empty to ignore', 'Choice') >+ } >+ ); >+ >+ say_success( $out, "Updated EdifactLSQ system preference to include empty option" ); >+ say_success( $out, "Added EdifactLSL system preference for GIR:LSL field mapping" ); >+ }, >+}; >diff --git a/installer/data/mysql/mandatory/sysprefs.sql b/installer/data/mysql/mandatory/sysprefs.sql >index 76954d20229..549ff29dc90 100644 >--- a/installer/data/mysql/mandatory/sysprefs.sql >+++ b/installer/data/mysql/mandatory/sysprefs.sql >@@ -233,7 +233,8 @@ INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` > ('EasyAnalyticalRecords','0','','If on, display in the catalogue screens tools to easily setup analytical record relationships','YesNo'), > ('EDIFACT','0',NULL,'Enables EDIFACT acquisitions functions','YesNo'), > ('EdifactInvoiceImport', 'automatic', 'automatic|manual', "If on, don't auto-import EDI invoices, just keep them in the database with the status 'new'", 'Choice'), >-('EdifactLSQ', 'location', 'location|ccode', 'Map EDI sequence code (GIR+LSQ) to Koha Item field', 'Choice'), >+('EdifactLSL', 'ccode', 'location|ccode|', 'Map EDI sub-location code (GIR+LSL) to Koha Item field, empty to ignore', 'Choice'), >+('EdifactLSQ', 'location', 'location|ccode|', 'Map EDI sequence code (GIR+LSQ) to Koha Item field, empty to ignore', 'Choice'), > ('ElasticsearchBoostFieldMatch', '0', NULL, 'Add a "match" query to es when searching, will follow indexes chosen in advanced search, or use title-cover for generic keyword or title index search', 'YesNo'), > ('ElasticsearchCrossFields', '1', '', 'Enable "cross_fields" option for searches using Elastic search.', 'YesNo'), > ('ElasticsearchIndexStatus_authorities', '0', 'Authorities index status', NULL, NULL), >@@ -889,4 +890,4 @@ INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` > ('z3950AuthorAuthFields','701,702,700',NULL,'Define the MARC biblio fields for Personal Name Authorities to fill biblio.author','free'), > ('z3950NormalizeAuthor','0','','If ON, Personal Name Authorities will replace authors in biblio.author','YesNo'), > ('z3950Status','','','This syspref allows to define custom YAML based rules for marking items unavailable in z3950 results.','Textarea') >-; >\ No newline at end of file >+; >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/acquisitions.pref b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/acquisitions.pref >index 7f1a8860956..96b3b6b2b1d 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/acquisitions.pref >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/acquisitions.pref >@@ -185,4 +185,14 @@ Acquisitions: > choices: > location: "location" > ccode: "collection" >+ "": "ignore" >+ - " in items." >+ - >+ - "Map sub-location code (GIR:LSL) field to " >+ - pref: EdifactLSL >+ default: "" >+ choices: >+ location: "location" >+ ccode: "collection" >+ "": "ignore" > - " in items." >-- >2.50.1
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 40391
:
185031
|
185032
|
185233
| 185234