Bug 38689: Improve test coverage

This patch drastically enhances the existing tests.  We add tests for
the various codepaths for handling EDI QUOTES including LIN segment
splitting for multi-fund orders.

Signed-off-by: Katrin Fischer <katrin.fischer@bsz-bw.de>
This commit is contained in:
Martin Renvoize 2025-01-13 18:12:39 +00:00 committed by Katrin Fischer
parent be9d6b7469
commit 13caf0dec9
No known key found for this signature in database
GPG Key ID: 0EF6E2C03357A834
4 changed files with 604 additions and 247 deletions

View File

@ -20,7 +20,7 @@
use Modern::Perl;
use FindBin qw( $Bin );
use Test::More tests => 3;
use Test::More tests => 2;
use Test::Warn;
use Test::MockModule;
@ -30,305 +30,617 @@ use t::lib::TestBuilder;
use Koha::EDI qw(process_quote process_invoice);
use Koha::Edifact::Transport;
use Koha::Edifact::File::Errors;
use Koha::DateUtils qw(dt_from_string);
my $schema = Koha::Database->new->schema;
my $builder = t::lib::TestBuilder->new;
my $logger = t::lib::Mocks::Logger->new();
subtest 'process_quote' => sub {
plan tests => 16;
plan tests => 5;
$schema->storage->txn_begin;
# Add our test quote file to the database for testing against
my $account = $builder->build(
{
source => 'VendorEdiAccount',
value => {
description => 'test vendor', transport => 'FILE',
}
}
);
my $dirname = ( $Bin =~ /^(.*\/t\/)/ ? $1 . 'edi_testfiles/' : q{} );
my $filename = 'QUOTES_SMALL.CEQ';
ok( -e $dirname . $filename, 'File QUOTES_SMALL.CEQ found' );
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 } );
# Test quote expects REF to be a valid and active fund code
# Common setup for all test cases
my $test_san = '5013546098818';
my $dirname = ( $Bin =~ /^(.*\/t\/)/ ? $1 . 'edi_testfiles/' : q{} );
my $active_period = $builder->build(
{
source => 'Aqbudgetperiod',
value => { budget_period_active => 1 }
}
);
my $fund = $builder->build(
{
source => 'Aqbudget',
value => {
budget_code => 'REF',
budget_period_id => $active_period->{budget_period_id}
}
}
);
# The quote expects a ROT1 stock rotation roata to exist
my $rota = $builder->build_object(
{
class => 'Koha::StockRotationRotas',
value => { title => 'ROT1' }
}
);
$builder->build(
{
source => 'Stockrotationstage',
value => { rota_id => $rota->rota_id },
}
);
t::lib::Mocks::mock_preference( 'CataloguingLog', 0 );
# Process the test quote file
my $error;
eval {
process_quote($quote);
1;
} or do {
$error = $@;
# Test 1: Basic Quote Processing
subtest 'basic_quote_processing' => sub {
plan tests => 27;
$schema->storage->txn_begin;
my $account = $builder->build(
{
source => 'VendorEdiAccount',
value => {
description => 'test vendor',
transport => 'FILE',
plugin => '',
san => $test_san,
orders_enabled => 1,
auto_orders => 0,
}
}
);
my $ean = $builder->build(
{
source => 'EdifactEan',
value => {
description => 'test ean',
branchcode => undef,
ean => $test_san
}
}
);
my $filename = 'QUOTES_SMALL.CEQ';
ok( -e $dirname . $filename, 'File QUOTES_SMALL.CEQ found' );
# Setup the fund code
my $fund = $builder->build(
{
source => 'Aqbudget',
value => {
budget_code => 'REF',
budget_period_id => $active_period->{budget_period_id}
}
}
);
# Setup stock rotation
my $rota = $builder->build_object(
{
class => 'Koha::StockRotationRotas',
value => { title => 'ROT1' }
}
);
$builder->build(
{
source => 'Stockrotationstage',
value => { rota_id => $rota->rota_id },
}
);
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 in database' );
is( $quote->status, 'new', 'Initial quote status is new' );
t::lib::Mocks::mock_preference( 'AcqCreateItem', 'ordering' );
# Process quote and check results
my $die;
eval {
process_quote($quote);
1;
} or do {
$die = $@;
};
ok( !$die, 'Basic quote processed without dying' );
# Test for expected logs for the passed quote file
is( $logger->count, 8, "8 log lines recorded for passed quote file" );
#$logger->diag();
$logger->trace_like(
qr/Created basket:.*/,
"Trace recorded adding basket"
)->trace_like(
qr/Checking db for matches.*/,
"Trace recorded checking db for matches"
)->trace_like(
qr/Added biblio:.*/,
"Trace recoded adding new biblio"
)->trace_like(
qr/Order created:.*/,
"Trace recorded adding order"
)->trace_like(
qr/Added item:.*/,
"Trace recorded adding new item"
)->trace_like(
qr/Item added to rota.*/,
"Trace recrded adding item to rota"
)->debug_like(
qr/.*specialUpdate biblioserver$/,
"Trace recorded biblioserver index"
)->clear();
# No errors expected for QUOTE_SMALL
my $errors = Koha::Edifact::File::Errors->search();
is( $errors->count, 0, '0 errors recorded for simple quote' );
# Status changed to received
$quote->get_from_storage;
is( $quote->status, 'received', 'Quote status set to received' );
# Generate basket
my $baskets = Koha::Acquisition::Baskets->search( { booksellerid => $account->{vendor_id} } );
is( $baskets->count, 1, "One basket created for single message quote file" );
my $basket = $baskets->next;
is( $basket->basketname, $filename, "Basket uses EDI filename for name" );
# Order lines
my $orders = $basket->orders;
is( $orders->count, 1, "One order line created for single record quote file" );
my $order = $orders->next;
ok( $order->biblionumber, 'Biblionumber assigned to order' );
is( $order->entrydate, dt_from_string()->ymd(), 'Entry date set correctly' );
# Fund allocation
$fund = $order->fund;
ok( $fund, 'Fund allocated to order' );
is( $fund->budget_code, 'REF', 'Correct fund allocated for order' );
# Test 008 handling
my $biblio = $order->biblio;
my $record = $biblio->record;
my $record_field = $record->field('008');
is( exists( $record_field->{_data} ), 1, '008 field added when missing from quote' );
# Item allocation
my $items = $order->items;
is(
$items->count, 1,
"One item created when AcqCreateItem is set to 'ordering' for single record, single quantity quote file"
);
# Test stock rotation handling
my $item = $items->next;
my $on_rota = Koha::StockRotationItems->search( { itemnumber_id => $item->itemnumber } );
is( $on_rota->count, 1, "Item added to rotation" );
is( $on_rota->next->stage->rota->id, $rota->id, "Correct rotation assigned" );
# Confirm no ORDER message generated for 'auto_orders => 0'
is( $basket->closedate, undef, 'Basket left open for auto_orders disabled' );
my $edi_orders = $schema->resultset('EdifactMessage')->search(
{
message_type => 'ORDERS',
basketno => $basket->basketno,
}
);
is( $edi_orders->count, 0, 'ORDER message not created for auto_orders disabled' );
$logger->clear();
$schema->storage->txn_rollback;
};
ok( !$error, 'process_quote completed without dying' );
# Test for expected warnings for the passed quote file
is( $logger->count, 8, "8 log lines recorded for passed quote file" );
# Test 2: Auto Orders Processing
subtest 'auto_orders_processing' => sub {
plan tests => 7;
#$logger->diag();
$logger->trace_like(
qr/Created basket:.*/,
"Trace recorded adding basket"
)->trace_like(
qr/Checking db for matches.*/,
"Trace recorded checking db for matches"
)->trace_like(
qr/New biblio added.*/,
"Trace recoded adding new biblio"
)->trace_like(
qr/Order created:.*/,
"Trace recorded adding order"
)->trace_like(
qr/Added item:.*/,
"Trace recorded adding new item"
)->trace_like(
qr/Item added to rota.*/,
"Trace recrded adding item to rota"
)->debug_like(
qr/.*specialUpdate biblioserver$/,
"Trace recorded biblioserver index"
)->clear();
$schema->storage->txn_begin;
my $account = $builder->build(
{
source => 'VendorEdiAccount',
value => {
description => 'auto order vendor',
transport => 'FILE',
plugin => '',
san => $test_san,
orders_enabled => 1,
auto_orders => 1,
}
}
);
my $ean = $builder->build(
{
source => 'EdifactEan',
value => {
description => 'test ean',
branchcode => undef,
ean => $test_san
}
}
);
# Test for quote status change
$quote->get_from_storage;
is( $quote->status, 'received', 'Quote status was set to received' );
# Setup the fund
$builder->build(
{
source => 'Aqbudget',
value => {
budget_code => 'REF',
budget_period_id => $active_period->{budget_period_id}
}
}
);
# Tests for generated basket for passed quote file
my $baskets = Koha::Acquisition::Baskets->search( { booksellerid => $account->{vendor_id} } );
is( $baskets->count, 1, "1 basket created for a single quote file" );
my $basket = $baskets->next;
my $filename = 'QUOTES_SMALL.CEQ';
my $trans = Koha::Edifact::Transport->new( $account->{id} );
$trans->working_directory($dirname);
my $orders = $basket->orders;
is( $orders->count, 1, "1 order line attached to basket when only 1 order is in the edi message" );
my $order = $orders->next;
my $mhash = $trans->message_hash();
$mhash->{message_type} = 'QUOTE';
$trans->ingest( $mhash, $filename );
my $biblio = $order->biblio;
my $quote = $schema->resultset('EdifactMessage')->find( { filename => $filename } );
my $items = $order->items;
is( $items->count, 1, "1 item added when AcqCreateItem eq ordering and 1 item is in the EDI quote" );
my $item = $items->next;
# Process quote and check results
my $die;
eval {
process_quote($quote);
1;
} or do {
$die = $@;
};
ok( !$die, 'Basic quote processed, with auto_orders enabled, without dying' );
# Test that item is added to rota appropriately
my $on_rota = Koha::StockRotationItems->search( { itemnumber_id => $item->itemnumber } );
is( $on_rota->count, 1, "Item added to stockrotation rota" );
my $baskets = Koha::Acquisition::Baskets->search( { booksellerid => $account->{vendor_id} } );
is( $baskets->count, 1, "Basket created" );
my $rota_item = $on_rota->next;
is( $rota_item->stage->rota->id, $rota->id, "Item is on correct rota" );
my $basket = $baskets->next;
ok( $basket->closedate, 'Basket automatically closed for auto_orders' );
$schema->storage->txn_rollback;
};
my $orders = $basket->orders;
is( $orders->count, 1, "One order created" );
subtest '_handle_008_field' => sub {
plan tests => 4;
my $order = $orders->next;
ok( $order->biblionumber, 'Biblionumber assigned to order' );
$schema->storage->txn_begin;
# Check EDI order generation
my $edi_orders = $schema->resultset('EdifactMessage')->search(
{
message_type => 'ORDERS',
basketno => $basket->basketno,
}
);
is( $edi_orders->count, 1, 'EDI order message created' );
is( $edi_orders->next->status, 'Pending', 'EDI order status is Pending' );
# Add our test quote file to the database for testing against
my $account = $builder->build(
{
source => 'VendorEdiAccount',
value => {
description => 'test vendor', transport => 'FILE',
$logger->clear();
$schema->storage->txn_rollback;
};
# Test 3: Multiple item quote
subtest 'multi-item quote' => sub {
plan tests => 18;
$schema->storage->txn_begin;
# Create vendor EDI account
my $account = $builder->build(
{
source => 'VendorEdiAccount',
value => {
description => 'multi-item vendor',
transport => 'FILE',
plugin => '',
}
}
);
my $ean = $builder->build(
{
source => 'EdifactEan',
value => {
description => 'test ean',
branchcode => undef,
ean => $test_san
}
}
);
# Setup multiple funds
my %funds;
for my $code (qw(REF LOAN)) {
$funds{$code} = $builder->build(
{
source => 'Aqbudget',
value => {
budget_code => $code,
budget_period_id => $active_period->{budget_period_id}
}
}
);
}
# Setup multiple rota
my %rota;
for my $title (qw(TEST FAST SLOW)) {
$rota{$title} = $builder->build_object(
{
class => 'Koha::StockRotationRotas',
value => { title => $title }
}
);
$builder->build(
{
source => 'Stockrotationstage',
value => { rota_id => $rota{$title}->rota_id },
}
);
}
my $description = <<~"END";
Loaded QUOTE file with 1 Message that contains 2 LIN segments with the first
consistent of 1 item and the second with 3 items with 2 different funds.
END
diag($description);
my $filename = 'QUOTES_MULTI.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 } );
process_quote($quote);
#$logger->diag();
my $baskets = Koha::Acquisition::Baskets->search( { booksellerid => $account->{vendor_id} } );
is( $baskets->count, 1, "One basket created for quote" );
my $basket = $baskets->next;
my $orders = $basket->orders;
is( $orders->count, 3, "Three order lines created for quote" );
my %biblios;
my $orderline = 0;
while ( my $order = $orders->next ) {
$orderline++;
diag( "Looking at order: " . $orderline );
if ( $orderline == 1 ) {
# Fund allocation
my $fund = $order->fund;
is( $fund->budget_code, 'REF', 'Correct fund allocated for first orderline' );
# Check biblio
ok( $order->biblionumber, 'Biblionumber assigned to order' );
$biblios{ $order->biblionumber }++;
# Check items created
my $items = $order->items;
is( $items->count, 1, 'One item created for the first orderline' );
# Check first order GIR details
my $item = $items->next;
my $rota = $item->stockrotationitem;
ok( $rota, 'Item was assigned to a rota' );
is( $rota->stage->rota->title, 'TEST', "Item was assigned to the correct rota" );
} elsif ( $orderline == 2 ) {
# Fund allocation
my $fund = $order->fund;
is( $fund->budget_code, 'LOAN', 'Correct fund allocated for second orderline' );
# Check biblio
ok( $order->biblionumber, 'Biblionumber assigned to order' );
$biblios{ $order->biblionumber }++;
# Check items created
my $items = $order->items;
is( $items->count, 2, 'Two items created for the second orderline' );
# Check second order GIR details
my %rotas;
while ( my $item = $items->next ) {
my $rota = $item->stockrotationitem;
ok( $rota, 'Item was assigned to a rota' );
$rotas{ $rota->stage->rota->title }++;
}
is( $rotas{'FAST'}, 1, "One item added to 'FAST' rota" );
is( $rotas{'SLOW'}, 1, "One item added to 'SLOW' rota" );
} elsif ( $orderline == 3 ) {
diag("Second LIN split into 2 Orderlines, one for each Fund");
# Fund allocation
my $fund = $order->fund;
is( $fund->budget_code, 'REF', 'Correct fund allocated for third orderline' );
# Check biblio
ok( $order->biblionumber, 'Biblionumber assigned to order' );
$biblios{ $order->biblionumber }++;
# Check items created
my $items = $order->items;
is( $items->count, 1, 'One item created for the third orderline' );
# Check first order GIR details
my $item = $items->next;
my $rota = $item->stockrotationitem;
ok( !$rota, 'Item was not assigned to a rota' );
}
}
);
my $dirname = ( $Bin =~ /^(.*\/t\/)/ ? $1 . 'edi_testfiles/' : q{} );
my $filename = 'QUOTES_SMALL.CEQ';
ok( -e $dirname . $filename, 'File QUOTES_SMALL.CEQ found' );
my $trans = Koha::Edifact::Transport->new( $account->{id} );
$trans->working_directory($dirname);
$logger->clear();
$schema->storage->txn_rollback;
};
my $mhash = $trans->message_hash();
$mhash->{message_type} = 'QUOTE';
$trans->ingest( $mhash, $filename );
# Test 4: Invalid Fund Error Handling
subtest 'invalid_fund_handling' => sub {
plan tests => 21;
my $quote = $schema->resultset('EdifactMessage')->find( { filename => $filename } );
$schema->storage->txn_begin;
# Test quote expects REF to be a valid and active fund code
my $active_period = $builder->build(
{
source => 'Aqbudgetperiod',
value => { budget_period_active => 1 }
}
);
my $fund = $builder->build(
{
source => 'Aqbudget',
value => {
budget_code => 'REF',
budget_period_id => $active_period->{budget_period_id}
my $account = $builder->build(
{
source => 'VendorEdiAccount',
value => {
description => 'error test vendor',
transport => 'FILE',
}
}
}
);
# The quote expects a ROT1 stock rotation roata to exist
my $rota = $builder->build_object(
{
class => 'Koha::StockRotationRotas',
value => { title => 'ROT1' }
}
);
$builder->build(
{
source => 'Stockrotationstage',
value => { rota_id => $rota->rota_id },
}
);
# Process the test quote file
process_quote($quote);
$quote->get_from_storage;
# Tests for generated basket for passed quote file
my $baskets = Koha::Acquisition::Baskets->search( { booksellerid => $account->{vendor_id} } );
my $basket = $baskets->next;
my $orders = $basket->orders;
my $order = $orders->next;
my $biblio = $order->biblio;
my $record = $biblio->record;
my $record_field = $record->field('008');
is( exists( $record_field->{_data} ), 1, 'Field has been added' );
# Test without calling the 008 handler
$account = $builder->build(
{
source => 'VendorEdiAccount',
value => {
description => 'test vendor', transport => 'FILE',
);
my $ean = $builder->build(
{
source => 'EdifactEan',
value => {
description => 'test ean',
branchcode => undef,
ean => $test_san
}
}
}
);
$dirname = ( $Bin =~ /^(.*\/t\/)/ ? $1 . 'edi_testfiles/' : q{} );
$filename = 'QUOTES_SMALL_2.CEQ';
ok( -e $dirname . $filename, 'File QUOTES_SMALL_2.CEQ found' );
);
$trans = Koha::Edifact::Transport->new( $account->{id} );
$trans->working_directory($dirname);
my $description = <<~"END";
Loading QUOTE file with 1 Message that contains 2 LIN segments with the first
consistent of 1 item and the second with 3 items with 2 different undefined funds.
END
diag($description);
$mhash = $trans->message_hash();
$mhash->{message_type} = 'QUOTE';
$trans->ingest( $mhash, $filename );
my $filename = 'QUOTES_MULTI.CEQ';
my $trans = Koha::Edifact::Transport->new( $account->{id} );
$trans->working_directory($dirname);
$quote = $schema->resultset('EdifactMessage')->find( { filename => $filename } );
my $mhash = $trans->message_hash();
$mhash->{message_type} = 'QUOTE';
$trans->ingest( $mhash, $filename );
# Test quote expects REF to be a valid and active fund code
$active_period = $builder->build(
{
source => 'Aqbudgetperiod',
value => { budget_period_active => 1 }
}
);
$fund = $builder->build(
{
source => 'Aqbudget',
value => {
budget_code => 'REF2',
budget_period_id => $active_period->{budget_period_id}
my $quote = $schema->resultset('EdifactMessage')->find( { filename => $filename } );
process_quote($quote);
#$logger->diag();
$logger->trace_like(
qr/Created basket:.*/,
"Trace recorded basket added"
)->trace_is(
qq/Checking db for matches with 9781529923766/,
"Trace recorded isbn lookup"
)->trace_like(
qr/Added biblio:.*/,
"Trace recorded adding new biblio"
)->trace_is(
qq/Skipping orderline with invalid budget: REF/,
"Trace recorded skipping orderline with invalid fund"
)->trace_is(
qq/Checking db for matches with 9781785044342/,
"Trace recorded isbn lookup"
)->trace_like(
qr/Added biblio:.*/,
"Trace recorded adding new biblio"
)->trace_is(
qq/Skipping item with invalid budget: LOAN/,
"Trace recorded skipping item with invalid fund"
)->trace_is(
qq/Skipping item with invalid budget: REF/,
"Trace recorded skipping item with invalid fund"
)->trace_is(
qq/Skipping item with invalid budget: LOAN/,
"Trace recorded skipping item with invalid fund"
);
# Errors should be recorded for skipped sections
my $errors = Koha::Edifact::File::Errors->search();
is( $errors->count, 4, '4 errors recorded for missing funds in quote' );
my $error = $errors->next;
ok( $error->section, 'First error section is present' );
is( $error->details, 'Skipped orderline line with invalid budget: REF', 'First error details is correct' );
$error = $errors->next;
ok( $error->section, 'Second error section is present' );
is( $error->details, 'Skipped GIR line with invalid budget: LOAN', 'Second error details is correct' );
$error = $errors->next;
ok( $error->section, 'Third error section is present' );
is( $error->details, 'Invalid budget REF found', 'Third error details is correct' );
$error = $errors->next;
ok( $error->section, 'Fourth error section is present' );
is( $error->details, 'Invalid budget LOAN found', 'Fourth error details is correct' );
$quote->get_from_storage;
is( $quote->status, 'error', 'Quote status set to error for invalid fund' );
my $baskets = Koha::Acquisition::Baskets->search( { booksellerid => $account->{vendor_id} } );
is( $baskets->count, 1, 'Basket still created despite errors' );
my $orders = $baskets->next->orders;
is( $orders->count, 0, 'No orders created with invalid fund' );
$logger->clear();
$schema->storage->txn_rollback;
};
# Test 5: Multiple message quote file
subtest 'multiple_message_handling' => sub {
plan tests => 3;
$schema->storage->txn_begin;
my $account = $builder->build(
{
source => 'VendorEdiAccount',
value => {
description => 'error test vendor',
transport => 'FILE',
}
}
}
);
);
my $ean = $builder->build(
{
source => 'EdifactEan',
value => {
description => 'test ean',
branchcode => undef,
ean => $test_san
}
}
);
# The quote expects a ROT1 stock rotation roata to exist
$rota = $builder->build_object(
{
class => 'Koha::StockRotationRotas',
value => { title => 'ROT2' }
}
);
$builder->build(
{
source => 'Stockrotationstage',
value => { rota_id => $rota->rota_id },
}
);
my $description = <<~"END";
Loading QUOTE file with 2 messages
END
diag($description);
# Process the test quote file
my $edi_module = Test::MockModule->new('Koha::EDI');
$edi_module->mock(
'_check_for_existing_bib',
sub {
my $bib_record = shift;
return;
}
);
$edi_module->mock(
'_handle_008_field',
sub {
my $bib_record = shift;
return $bib_record;
}
);
process_quote($quote);
my $filename = 'QUOTES_BIG.CEQ';
my $trans = Koha::Edifact::Transport->new( $account->{id} );
$trans->working_directory($dirname);
$quote->get_from_storage;
my $mhash = $trans->message_hash();
$mhash->{message_type} = 'QUOTE';
$trans->ingest( $mhash, $filename );
# Tests for generated basket for passed quote file
$baskets = Koha::Acquisition::Baskets->search( { booksellerid => $account->{vendor_id} } );
$basket = $baskets->next;
my $quote = $schema->resultset('EdifactMessage')->find( { filename => $filename } );
$orders = $basket->orders;
$order = $orders->next;
process_quote($quote);
$biblio = $order->biblio;
$record = $biblio->record;
$record_field = $record->field('008');
#$logger->diag();
is( $record_field->{_data}, undef, 'Field has not been added' );
my $baskets = Koha::Acquisition::Baskets->search( { booksellerid => $account->{vendor_id} } );
is( $baskets->count, 2, 'Two baskets added for file containing 2 messages' );
# First basket
my $basket = $baskets->next;
is( $basket->basketname, $filename, "Basket uses EDI filename for name" );
# Second basket
$basket = $baskets->next;
is( $basket->basketname, $filename, "Basket uses EDI filename for name" );
$logger->clear();
$schema->storage->txn_rollback;
};
# Clean up
$logger->clear();
$schema->storage->txn_rollback;
};
subtest 'process_invoice' => sub {
plan tests => 19;
plan tests => 32;
$schema->storage->txn_begin;
@ -392,8 +704,8 @@ subtest 'process_invoice' => sub {
my $invoice_message = $schema->resultset('EdifactMessage')->find( { filename => $filename } );
my $raw_msg = $invoice_message->raw_msg;
$raw_msg =~ s/P12345/$ordernumber1/g;
$raw_msg =~ s/P54321/$ordernumber2/g;
$raw_msg =~ s/ORDERNUMBER1/$ordernumber1/g;
$raw_msg =~ s/ORDERNUMBER2/$ordernumber2/g;
$invoice_message->update( { raw_msg => $raw_msg } );
# Process the test invoice file
@ -403,6 +715,7 @@ subtest 'process_invoice' => sub {
1;
} or do {
$error = $@;
diag($error);
};
ok( !$error, 'process_invoice completed without dying' );
@ -453,6 +766,49 @@ subtest 'process_invoice' => sub {
'Warn recorded for unmatched item'
)->clear();
# Errors should be recorded for skipped sections
my $errors = Koha::Edifact::File::Errors->search();
is( $errors->count, 6, '6 errors recorded for invoice' );
my @expected_errors = (
{
'section' =>
"QTY+47:5\nGIR+001+DIT:LLO+34148000123456:LAC+P28837:LCO+DITATB:LFN\nPRI+AAA:9.99\nPRI+AAB:12.99\nMOA+203:49.95\nMOA+52:15.00",
'details' => 'Skipped invoice line 1, missing ordernumber'
},
{
'section' =>
"QTY+47:5\nGIR+001+HLE:LLO+34148000123457:LAC+P28838:LCO+HLEATB:LFN\nPRI+AAA:9.99\nPRI+AAB:12.99\nMOA+203:49.95\nMOA+52:15.00\nRFF+LI:P28837",
'details' => 'Skipped invoice line 2, cannot find order with ordernumber P28837'
},
{
'section' =>
"QTY+47:10\nGIR+001+RUN:LLO+34148000123458:LAC+P28839:LCO+RUNATB:LFN\nPRI+AAA:15.00\nPRI+AAB:18.00\nMOA+203:150.00\nMOA+52:30.00\nRFF+LI:$ordernumber1",
'details' => "Skipped invoice line 3, cannot find biblio for ordernumber $ordernumber1"
},
{
'section' =>
"QTY+47:1\nGIR+001+WID:LLO+34148000123459:LAC+P28840:LCO+WIDATB:LFN\nPRI+AAA:30.00\nPRI+AAB:35.00\nMOA+203:600.00\nMOA+52:5.00\nRFF+LI:$ordernumber2",
'details' => 'No matching item found for invoice line 4:0 at branch WID'
},
{
'section' =>
"QTY+47:1\nGIR+001+DIT:LLO+34148000123460:LAC+P54322:LCO+DITATB:LFN\nPRI+AAA:5.00\nPRI+AAB:6.00\nMOA+203:5.00\nMOA+52:1.00\nRFF+LI:$ordernumber2",
'details' => 'No matching item found for invoice line 5:0 at branch DIT'
},
{
'section' => "NAD+SU+9999999999999",
'details' => 'Skipped invoice INV00002 with unmatched vendor san: 9999999999999'
}
);
my $index = 0;
while ( my $error = $errors->next ) {
is( $error->section, $expected_errors[$index]->{section}, "Error $index section is correct" );
is( $error->details, $expected_errors[$index]->{details}, "Error $index details is correct" );
$index++;
}
my $invoice3 = Koha::Acquisition::Invoices->search( { invoicenumber => 'INV00003' }, { rows => 1 } )->single;
ok( $invoice3, "Invoice added to database" );
is( $invoice3->booksellerid, $account->{vendor_id}, 'Invoice has test booksellerid' );

View File

@ -1,2 +1 @@
UNA:+.? 'UNB+UNOC:3+5013546027173+5013546098818+230101:0000+0000000002'UNH+00003+INVOIC:D:96A:UN'BGM+380+INV00003+9'DTM+137:20231210:102'NAD+BY+12345::9'NAD+SU+5013546027173::9'LIN+1++987654321:EN'QTY+47:5'PRI+AAA:9.99'MOA+203:49.95'LIN+2++987654321:EN'QTY+47:5'PRI+AAA:9.99'MOA+203:49.95'RFF+LI:P28837'LIN+3++999999999:EN'QTY+47:10'PRI+AAA:15.00'MOA+203:150.00'RFF+LI:P12345'LIN+4++123456789:EN'QTY+47:1'PRI+AAA:30.00'MOA+203:600.00'RFF+LI:P54321'LIN+5++987654123:EN'QTY+47:1'PRI+AAA:5.00'MOA+203:5.00'RFF+LI:P54321'UNS+S'CNT+4:4'UNT+15+00003'UNZ+1+0000000002'
UNA:+.? 'UNB+UNOC:3+9999999999999+5013546098818+230101:0000+0000000001'UNH+00002+INVOIC:D:96A:UN'BGM+380+INV00002+9'DTM+137:20231210:102'NAD+BY+54321::9'NAD+SU+9999999999999::9'UNS+S'CNT+1:1'UNT+11+00002'
UNA:+.? 'UNB+UNOC:3+5013546027173+5013546098818+230101:0000+0000000002'UNH+00003+INVOIC:D:96A:UN'BGM+380+INV00003+9'DTM+137:20231210:102'NAD+BY+12345::9'NAD+SU+5013546027173::9'LIN+1++987654321:EN'QTY+47:5'GIR+001+DIT:LLO+34148000123456:LAC+P28837:LCO+DITATB:LFN'PRI+AAA:9.99'PRI+AAB:12.99'MOA+203:49.95'MOA+52:15.00'LIN+2++987654321:EN'QTY+47:5'GIR+001+HLE:LLO+34148000123457:LAC+P28838:LCO+HLEATB:LFN'PRI+AAA:9.99'PRI+AAB:12.99'MOA+203:49.95'MOA+52:15.00'RFF+LI:P28837'LIN+3++999999999:EN'QTY+47:10'GIR+001+RUN:LLO+34148000123458:LAC+P28839:LCO+RUNATB:LFN'PRI+AAA:15.00'PRI+AAB:18.00'MOA+203:150.00'MOA+52:30.00'RFF+LI:ORDERNUMBER1'LIN+4++123456789:EN'QTY+47:1'GIR+001+WID:LLO+34148000123459:LAC+P28840:LCO+WIDATB:LFN'PRI+AAA:30.00'PRI+AAB:35.00'MOA+203:600.00'MOA+52:5.00'RFF+LI:ORDERNUMBER2'LIN+5++987654123:EN'QTY+47:1'GIR+001+DIT:LLO+34148000123460:LAC+P54322:LCO+DITATB:LFN'PRI+AAA:5.00'PRI+AAB:6.00'MOA+203:5.00'MOA+52:1.00'RFF+LI:ORDERNUMBER2'UNS+S'CNT+4:4'MOA+79:854.90'MOA+129:854.90'MOA+122:854.90'UNT+15+00003'UNZ+1+0000000002''UNA:+.? 'UNB+UNOC:3+9999999999999+5013546098818+230101:0000+0000000001'UNH+00002+INVOIC:D:96A:UN'BGM+380+INV00002+9'DTM+137:20231210:102'NAD+BY+54321::9'NAD+SU+9999999999999::9'UNS+S'CNT+1:1'UNT+11+00002'''

View File

@ -0,0 +1 @@
UNA:+.? 'UNB+UNOC:3+5013546027173+5013546098818+240123:0945+1044416+ASKEDI:+QUOTES++++'UNH+1044416001+QUOTES:D:96A:UN:EAN002'BGM+31C::28+WO0800131+9'DTM+137:20240123:102'RFF+ON:orders 23/1'CUX+2:GBP:12'NAD+BY+5013546098818::9'NAD+SU+5013546027173::9'LIN+1++9781529923766:EN'PIA+5+152992376X:IB'IMD+L+010+:::Thompson'IMD+L+011+:::Louise'IMD+L+050+:::Lucky'IMD+L+060+:::learning to live again'IMD+L+120+:::Ebury Spotlight'IMD+L+170+:::2024'IMD+L+180+:::304'IMD+L+220+:::Hbk'IMD+L+230+:::791.45'IMD+L+250+:::AN'QTY+1:1'GIR+001+BOOK:LST+ANF:LSQ+CPL:LLO+REF:LFN+791.45:LCL'GIR+001+22.00:LCV'GIR+001+ROT1:LRP'PRI+AAE:22.00'RFF+QLI:WO0800131000001'UNS+S'CNT+2:1'UNT+27+1044416001'UNZ+1+1044416'UNA:+.? 'UNB+UNOC:3+5013546027173+5013546098818+230713:1031+1044417+ASKEDI+QUOTES++++'UNH+1+QUOTES:D:96A:UN:EAN002'BGM+31C::28+WO0800132+9'DTM+137:20230713:102'NAD+BY+5412345000013::9'NAD+SU+4012345000094::9'CUX+2:GBP:9'LIN+1++9781529923766:EN'PIA+1+152992376X:SA'IMD+L+010+:::Thompson'IMD+L+011+:::Louise'IMD+L+050+:::Lucky'IMD+L+060+:::Learning to live again'GIR+001+BOOK:LST+ANF:LSQ+CPL:LLO+REF:LFN+791.45:LCL'GIR+001+22.00:LCV'GIR+001+TEST:LRP'QTY+21:1:PCE'MOA+203:25.99'RFF+ABO:REF'LIN+2++9781785044342:EN'PIA+5+1785044346:IB'IMD+L+010+:::Hansen'IMD+L+011+:::Anders'IMD+L+050+:::The attention fix'IMD+L+060+:::how to focus in a world that wants to distract you'QTY+1:3'GIR+001+BOOK:LST+ANF:LSQ+CPL:LLO+LOAN:LFN+153.733:LCL'GIR+001+14.99:LCV'GIR+001+FAST:LRP'GIR+002+BOOK:LST+ANF:LSQ+FFL:LLO+REF:LFN+153.733:LCL'GIR+002+14.99:LCV'GIR+003+BOOK:LST+ANF:LSQ+FPL:LLO+LOAN:LFN+153.733:LCL'GIR+003+14.99:LCV'GIR+003+SLOW:LRP'PRI+AAE:14.99'RFF+QLI:WO0774049000002'UNS+S'MOA+86:87.97'UNT+34+1'UNZ+1+1'

View File

@ -0,0 +1 @@
UNA:+.? 'UNB+UNOC:3+5013546027173+5013546098818+230713:1031+1044417+ASKEDI+QUOTES++++'UNH+1+QUOTES:D:96A:UN:EAN002'BGM+31C::28+WO0800132+9'DTM+137:20230713:102'NAD+BY+5412345000013::9'NAD+SU+4012345000094::9'CUX+2:GBP:9'LIN+1++9781529923766:EN'PIA+1+152992376X:SA'IMD+L+010+:::Thompson'IMD+L+011+:::Louise'IMD+L+050+:::Lucky'IMD+L+060+:::Learning to live again'GIR+001+BOOK:LST+ANF:LSQ+CPL:LLO+REF:LFN+791.45:LCL'GIR+001+22.00:LCV'GIR+001+TEST:LRP'QTY+21:1:PCE'MOA+203:25.99'RFF+ABO:REF'LIN+2++9781785044342:EN'PIA+5+1785044346:IB'IMD+L+010+:::Hansen'IMD+L+011+:::Anders'IMD+L+050+:::The attention fix'IMD+L+060+:::how to focus in a world that wants to distract you'QTY+1:3'GIR+001+BOOK:LST+ANF:LSQ+CPL:LLO+LOAN:LFN+153.733:LCL'GIR+001+14.99:LCV'GIR+001+FAST:LRP'GIR+002+BOOK:LST+ANF:LSQ+FFL:LLO+REF:LFN+153.733:LCL'GIR+002+14.99:LCV'GIR+003+BOOK:LST+ANF:LSQ+FPL:LLO+LOAN:LFN+153.733:LCL'GIR+003+14.99:LCV'GIR+003+SLOW:LRP'PRI+AAE:14.99'RFF+QLI:WO0774049000002'UNS+S'MOA+86:87.97'UNT+34+1'UNZ+1+1'