Bug 20253: (follow-up) Add duplicate purchase order number validation

When processing EDI quotes with purchase order numbers, validate that the
purchase order number is unique for the vendor to prevent operational
confusion. The validation:

- Checks for existing baskets with the same purchase order number and vendor
- Includes both open and closed baskets in the uniqueness check
- Logs an error with the raw RFF+ON segment and details to the edifact_errors table
- Continues processing (non-blocking) to prevent data loss
- Provides system log warnings for monitoring

The error logging includes:
- Section: Raw RFF+ON segment (e.g., "RFF+ON:orders 23/1")
- Details: Clear description of the duplicate purchase order number issue

This ensures administrators can identify and resolve duplicate purchase order
numbers while maintaining system functionality and audit trails.

Signed-off-by: Hannah Dunne-Howrie <hdunne-howrie@westminster.gov.uk>
Sponsored-by: Westminster City Council <https://www.westminster.gov.uk/>
Sponsored-by: Royal Borough of Kensington and Chelsea <https://www.rbkc.gov.uk/>
Sponsored-by: OpenFifth <https://openfifth.co.uk/>

Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
This commit is contained in:
Martin Renvoize 2025-07-11 13:47:33 +01:00 committed by Lucas Gass
parent 4179211ce8
commit 458608b9c4
No known key found for this signature in database
GPG Key ID: 60B447398B0D36CA
2 changed files with 88 additions and 1 deletions

View File

@ -667,6 +667,32 @@ sub process_quote {
my $purchase_order_number = $msg->purchase_order_number;
if ($purchase_order_number) {
$basket_name = $purchase_order_number;
# Check for duplicate purchase order numbers for this vendor (including closed baskets)
my $existing_basket = $schema->resultset('Aqbasket')->search(
{
basketname => $basket_name,
booksellerid => $quote_message->vendor_id,
}
)->first;
if ($existing_basket) {
# Log error for duplicate purchase order number but continue processing
my $rff_segment = "RFF+ON:$purchase_order_number";
$quote_message->add_to_edifact_errors(
{
section => $rff_segment,
details => "Duplicate purchase order number '$purchase_order_number' found for vendor "
. $quote_message->vendor_id . "."
}
);
$logger->warn( "Duplicate purchase order number '$purchase_order_number' for vendor "
. $quote_message->vendor_id
. " (existing basket: "
. $existing_basket->basketno
. ")" );
}
}
}

View File

@ -38,7 +38,7 @@ my $builder = t::lib::TestBuilder->new;
my $logger = t::lib::Mocks::Logger->new();
subtest 'process_quote' => sub {
plan tests => 6;
plan tests => 7;
$schema->storage->txn_begin;
@ -914,6 +914,67 @@ subtest 'process_quote' => sub {
$schema->storage->txn_rollback;
};
# Test 7: Duplicate purchase order number validation
subtest 'duplicate_purchase_order_validation' => sub {
plan tests => 5;
$schema->storage->txn_begin;
# Create vendor EDI account with po_is_basketname set to true
my $account = $builder->build(
{
source => 'VendorEdiAccount',
value => {
description => 'test vendor duplicate po',
transport => 'FILE',
po_is_basketname => 1,
},
}
);
# Create first basket with purchase order number "orders 23/1" (same as in QUOTES_SMALL.CEQ)
my $first_basket = $builder->build(
{
source => 'Aqbasket',
value => {
basketname => 'orders 23/1',
booksellerid => $account->{vendor_id},
closedate => undef, # Open basket
},
}
);
# Use existing test file that contains RFF+ON:orders 23/1
my $filename = 'QUOTES_SMALL.CEQ';
my $trans = Koha::Edifact::Transport->new( $account->{id} );
$trans->working_directory($dirname);
my $mhash = $trans->message_hash();
$mhash->{message_type} = 'QUOTE';
$trans->ingest( $mhash, $filename );
my $quote = $schema->resultset('EdifactMessage')->find( { filename => $filename } );
ok( $quote, 'Quote message created successfully' );
# Process the quote (this should trigger duplicate detection)
process_quote($quote);
# Check that duplicate purchase order error was logged
my $errors = $quote->edifact_errors;
ok( $errors->count >= 1, 'At least one error logged during quote processing' );
# Find the specific duplicate purchase order error
my $duplicate_error = $errors->search( { section => 'RFF+ON:orders 23/1' } )->first;
ok( $duplicate_error, 'Duplicate purchase order error found' );
is( $duplicate_error->section, 'RFF+ON:orders 23/1', 'Error section contains the RFF+ON segment' );
like(
$duplicate_error->details, qr/Duplicate purchase order number 'orders 23\/1' found for vendor/,
'Error details describe the duplicate issue'
);
$schema->storage->txn_rollback;
};
# Clean up
$logger->clear();
$schema->storage->txn_rollback;