View | Details | Raw Unified | Return to bug 40391
Collapse All | Expand All

(-)a/t/Edifact.t (-1 / +46 lines)
Lines 4-10 use warnings; Link Here
4
use FindBin qw( $Bin );
4
use FindBin qw( $Bin );
5
5
6
use Test::NoWarnings;
6
use Test::NoWarnings;
7
use Test::More tests => 46;
7
use Test::More tests => 53;
8
use Koha::EDI;
8
use Koha::EDI;
9
9
10
BEGIN {
10
BEGIN {
Lines 209-211 is( $message->purchase_order_number, undef, 'RFF+ON after LIN segment is ignored Link Here
209
209
210
$message = Koha::Edifact::Message->new( [ $header, $bgm, @datasegs ] );
210
$message = Koha::Edifact::Message->new( [ $header, $bgm, @datasegs ] );
211
is( $message->purchase_order_number, 'CORRECT_PO_NUM', 'Correct RFF+ON extracted when multiple RFF segments present' );
211
is( $message->purchase_order_number, 'CORRECT_PO_NUM', 'Correct RFF+ON extracted when multiple RFF segments present' );
212
213
# Test LSL (Library Sub-Location) field extraction from GIR segments
214
my $mock_line_data = {
215
    GIR => [
216
        {
217
            copy              => '001',
218
            sub_location_code => 'FICTION',    # LSL field
219
            sequence_code     => 'ADULT',      # LSQ field
220
            branch            => 'MAIN'
221
        },
222
        {
223
            copy              => '002',
224
            sub_location_code => 'REFERENCE',    # LSL field
225
            branch            => 'BRANCH2'
226
227
                # sequence_code missing for this item
228
        }
229
    ]
230
};
231
232
my $mock_line = bless $mock_line_data, 'Koha::Edifact::Line';
233
234
# Test LSL field access via girfield method
235
$y = $mock_line->girfield('sub_location_code');
236
is( $y, 'FICTION', 'LSL field (sub_location_code) returned for first occurrence' );
237
238
$y = $mock_line->girfield( 'sub_location_code', 0 );
239
is( $y, 'FICTION', 'LSL field returned for explicit occurrence 0' );
240
241
$y = $mock_line->girfield( 'sub_location_code', 1 );
242
is( $y, 'REFERENCE', 'LSL field returned for occurrence 1' );
243
244
$y = $mock_line->girfield( 'sub_location_code', 2 );
245
is( $y, undef, 'LSL field returns undef for non-existent occurrence' );
246
247
# Test that both LSL and LSQ can coexist
248
$y = $mock_line->girfield( 'sequence_code', 0 );
249
is( $y, 'ADULT', 'LSQ field still works when LSL is present' );
250
251
$y = $mock_line->girfield( 'sequence_code', 1 );
252
is( $y, undef, 'LSQ field correctly returns undef when not present for occurrence' );
253
254
# Test LSL field when LSQ is missing
255
$y = $mock_line->girfield( 'sub_location_code', 1 );
256
is( $y, 'REFERENCE', 'LSL field works correctly when LSQ is missing for that occurrence' );
(-)a/t/db_dependent/Koha/EDI.t (-1 / +292 lines)
Lines 21-27 use Modern::Perl; Link Here
21
use FindBin qw( $Bin );
21
use FindBin qw( $Bin );
22
22
23
use Test::NoWarnings;
23
use Test::NoWarnings;
24
use Test::More tests => 6;
24
use Test::More tests => 8;
25
use Test::MockModule;
25
use Test::MockModule;
26
26
27
use t::lib::Mocks;
27
use t::lib::Mocks;
Lines 1616-1618 subtest 'create_edi_order_logging' => sub { Link Here
1616
1616
1617
    $schema->storage->txn_rollback;
1617
    $schema->storage->txn_rollback;
1618
};
1618
};
1619
1620
subtest 'LSL and LSQ validation functions' => sub {
1621
    plan tests => 16;
1622
1623
    $schema->storage->txn_begin;
1624
1625
    # Mock quote message object for testing
1626
    {
1627
1628
        package MockQuoteMessage;
1629
1630
        sub new {
1631
            my $class = shift;
1632
            return bless { errors => [] }, $class;
1633
        }
1634
1635
        sub add_to_edifact_errors {
1636
            my ( $self, $error ) = @_;
1637
            push @{ $self->{errors} }, $error;
1638
        }
1639
    }
1640
1641
    # Create test authorised values
1642
    my $loc_av = $builder->build(
1643
        {
1644
            source => 'AuthorisedValue',
1645
            value  => {
1646
                category         => 'LOC',
1647
                authorised_value => 'FICTION',
1648
                lib              => 'Fiction Section'
1649
            }
1650
        }
1651
    );
1652
1653
    my $ccode_av = $builder->build(
1654
        {
1655
            source => 'AuthorisedValue',
1656
            value  => {
1657
                category         => 'CCODE',
1658
                authorised_value => 'ADULT',
1659
                lib              => 'Adult Collection'
1660
            }
1661
        }
1662
    );
1663
1664
    my $quote_message = MockQuoteMessage->new();
1665
1666
    # Test valid location code validation
1667
    ok(
1668
        Koha::EDI::_validate_location_code( 'FICTION', $quote_message ),
1669
        'Valid location code passes validation'
1670
    );
1671
    is( scalar @{ $quote_message->{errors} }, 0, 'No errors for valid location code' );
1672
1673
    # Test invalid location code validation
1674
    ok(
1675
        !Koha::EDI::_validate_location_code( 'INVALID_LOC', $quote_message ),
1676
        'Invalid location code fails validation'
1677
    );
1678
    is( scalar @{ $quote_message->{errors} }, 1, 'One error logged for invalid location code' );
1679
    like(
1680
        $quote_message->{errors}->[0]->{details}, qr/Invalid location code 'INVALID_LOC'/,
1681
        'Error details contain correct location code'
1682
    );
1683
1684
    # Reset errors for collection code tests
1685
    $quote_message = MockQuoteMessage->new();
1686
1687
    # Test valid collection code validation
1688
    ok(
1689
        Koha::EDI::_validate_collection_code( 'ADULT', $quote_message ),
1690
        'Valid collection code passes validation'
1691
    );
1692
    is( scalar @{ $quote_message->{errors} }, 0, 'No errors for valid collection code' );
1693
1694
    # Test invalid collection code validation
1695
    ok(
1696
        !Koha::EDI::_validate_collection_code( 'INVALID_CCODE', $quote_message ),
1697
        'Invalid collection code fails validation'
1698
    );
1699
    is( scalar @{ $quote_message->{errors} }, 1, 'One error logged for invalid collection code' );
1700
    like(
1701
        $quote_message->{errors}->[0]->{details}, qr/Invalid collection code 'INVALID_CCODE'/,
1702
        'Error details contain correct collection code'
1703
    );
1704
1705
    # Test empty/null handling
1706
    $quote_message = MockQuoteMessage->new();
1707
    ok(
1708
        Koha::EDI::_validate_location_code( '', $quote_message ),
1709
        'Empty location code passes validation'
1710
    );
1711
    ok(
1712
        Koha::EDI::_validate_location_code( undef, $quote_message ),
1713
        'Undefined location code passes validation'
1714
    );
1715
    ok(
1716
        Koha::EDI::_validate_collection_code( '', $quote_message ),
1717
        'Empty collection code passes validation'
1718
    );
1719
    ok(
1720
        Koha::EDI::_validate_collection_code( undef, $quote_message ),
1721
        'Undefined collection code passes validation'
1722
    );
1723
    is( scalar @{ $quote_message->{errors} }, 0, 'No errors for empty/undefined values' );
1724
1725
    # Test error message format
1726
    $quote_message = MockQuoteMessage->new();
1727
    Koha::EDI::_validate_location_code( 'TEST_ERROR', $quote_message );
1728
    my $error = $quote_message->{errors}->[0];
1729
    is( $error->{section}, 'GIR+LSQ/LSL validation', 'Error has correct section identifier' );
1730
1731
    $schema->storage->txn_rollback;
1732
};
1733
1734
subtest 'LSL and LSQ field copy to item_hash' => sub {
1735
    plan tests => 11;
1736
1737
    $schema->storage->txn_begin;
1738
1739
    # Setup test data
1740
    my $test_san = '5013546098818';
1741
    my $dirname  = ( $Bin =~ /^(.*\/t\/)/ ? $1 . 'edi_testfiles/' : q{} );
1742
1743
    my $active_period = $builder->build(
1744
        {
1745
            source => 'Aqbudgetperiod',
1746
            value  => { budget_period_active => 1 }
1747
        }
1748
    );
1749
1750
    my $budget = $builder->build(
1751
        {
1752
            source => 'Aqbudget',
1753
            value  => {
1754
                budget_amount     => 1000,
1755
                budget_encumb     => 0,
1756
                budget_expend     => 0,
1757
                budget_period_id  => $active_period->{budget_period_id},
1758
                budget_code       => 'TESTBUDGET',
1759
                budget_name       => 'Test Budget',
1760
                budget_branchcode => undef,
1761
            }
1762
        }
1763
    );
1764
1765
    my $branch = $builder->build(
1766
        {
1767
            source => 'Branch',
1768
        }
1769
    );
1770
1771
    my $vendor = $builder->build(
1772
        {
1773
            source => 'Aqbookseller',
1774
            value  => { name => 'Test Vendor', }
1775
        }
1776
    );
1777
1778
    my $edi_account = $builder->build(
1779
        {
1780
            source => 'VendorEdiAccount',
1781
            value  => {
1782
                vendor_id => $vendor->{id},
1783
                san       => $test_san,
1784
            }
1785
        }
1786
    );
1787
1788
    # Create test authorised values for LSL and LSQ
1789
    my $loc_av = $builder->build(
1790
        {
1791
            source => 'AuthorisedValue',
1792
            value  => {
1793
                category         => 'LOC',
1794
                authorised_value => 'TESTLOC',
1795
                lib              => 'Test Location'
1796
            }
1797
        }
1798
    );
1799
1800
    my $ccode_av = $builder->build(
1801
        {
1802
            source => 'AuthorisedValue',
1803
            value  => {
1804
                category         => 'CCODE',
1805
                authorised_value => 'TESTCCODE',
1806
                lib              => 'Test Collection'
1807
            }
1808
        }
1809
    );
1810
1811
    # Test Case 1: LSL maps to location, LSQ maps to ccode
1812
    t::lib::Mocks::mock_preference( 'EdifactLSL',    'location' );
1813
    t::lib::Mocks::mock_preference( 'EdifactLSQ',    'ccode' );
1814
    t::lib::Mocks::mock_preference( 'AcqCreateItem', 'ordering' );
1815
1816
    # Create a biblio for testing
1817
    my $biblio = $builder->build_sample_biblio();
1818
1819
    # Create test item with LSL and LSQ data
1820
    my $item = $builder->build_sample_item(
1821
        {
1822
            biblionumber => $biblio->biblionumber,
1823
            homebranch   => $branch->{branchcode},
1824
            location     => 'TESTLOC',
1825
            ccode        => 'TESTCCODE',
1826
        }
1827
    );
1828
1829
    # Verify item has both fields set
1830
    is( $item->location, 'TESTLOC',   'Item location set to TESTLOC' );
1831
    is( $item->ccode,    'TESTCCODE', 'Item ccode set to TESTCCODE' );
1832
1833
    # Test Case 2: LSL maps to ccode, LSQ maps to location (swapped)
1834
    t::lib::Mocks::mock_preference( 'EdifactLSL', 'ccode' );
1835
    t::lib::Mocks::mock_preference( 'EdifactLSQ', 'location' );
1836
1837
    # Create another item for swapped test
1838
    my $item2 = $builder->build_sample_item(
1839
        {
1840
            biblionumber => $biblio->biblionumber,
1841
            homebranch   => $branch->{branchcode},
1842
            location     => 'TESTLOC',
1843
            ccode        => 'TESTCCODE',
1844
        }
1845
    );
1846
1847
    is( $item2->location, 'TESTLOC',   'Item location set with swapped config' );
1848
    is( $item2->ccode,    'TESTCCODE', 'Item ccode set with swapped config' );
1849
1850
    # Test Case 3: Only LSL field present (LSQ empty)
1851
    t::lib::Mocks::mock_preference( 'EdifactLSL', 'location' );
1852
    t::lib::Mocks::mock_preference( 'EdifactLSQ', '' );
1853
1854
    my $item3 = $builder->build_sample_item(
1855
        {
1856
            biblionumber => $biblio->biblionumber,
1857
            homebranch   => $branch->{branchcode},
1858
            location     => 'TESTLOC',
1859
        }
1860
    );
1861
1862
    isnt( $item3->location, undef, 'Item location set when only LSL configured' );
1863
    is( $item3->location, 'TESTLOC', 'Item location value correct when only LSL configured' );
1864
1865
    # Test Case 4: Only LSQ field present (LSL empty)
1866
    t::lib::Mocks::mock_preference( 'EdifactLSL', '' );
1867
    t::lib::Mocks::mock_preference( 'EdifactLSQ', 'ccode' );
1868
1869
    my $item4 = $builder->build_sample_item(
1870
        {
1871
            biblionumber => $biblio->biblionumber,
1872
            homebranch   => $branch->{branchcode},
1873
            ccode        => 'TESTCCODE',
1874
        }
1875
    );
1876
1877
    isnt( $item4->ccode, undef, 'Item ccode set when only LSQ configured' );
1878
    is( $item4->ccode, 'TESTCCODE', 'Item ccode value correct when only LSQ configured' );
1879
1880
    # Test Case 5: Both LSL and LSQ map to location (edge case)
1881
    t::lib::Mocks::mock_preference( 'EdifactLSL', 'location' );
1882
    t::lib::Mocks::mock_preference( 'EdifactLSQ', 'location' );
1883
1884
    my $item5 = $builder->build_sample_item(
1885
        {
1886
            biblionumber => $biblio->biblionumber,
1887
            homebranch   => $branch->{branchcode},
1888
            location     => 'TESTLOC',
1889
        }
1890
    );
1891
1892
    is( $item5->location, 'TESTLOC', 'Item location set when both map to location' );
1893
1894
    # Test Case 6: Both disabled
1895
    t::lib::Mocks::mock_preference( 'EdifactLSL', '' );
1896
    t::lib::Mocks::mock_preference( 'EdifactLSQ', '' );
1897
1898
    my $item6 = $builder->build_sample_item(
1899
        {
1900
            biblionumber => $biblio->biblionumber,
1901
            homebranch   => $branch->{branchcode},
1902
        }
1903
    );
1904
1905
    ok( defined $item6, 'Item created successfully when both LSL and LSQ disabled' );
1906
    is( $item6->location, undef, 'Item location is undef when both LSL and LSQ disabled' );
1907
1908
    $schema->storage->txn_rollback;
1909
};
(-)a/t/db_dependent/Koha/Edifact/Order.t (-2 / +134 lines)
Lines 20-26 Link Here
20
use Modern::Perl;
20
use Modern::Perl;
21
21
22
use Test::NoWarnings;
22
use Test::NoWarnings;
23
use Test::More tests => 5;
23
use Test::More tests => 6;
24
24
25
use Koha::Edifact::Order;
25
use Koha::Edifact::Order;
26
26
Lines 147-152 subtest 'order_line() tests' => sub { Link Here
147
    # Set EdifactLSQ field to default
147
    # Set EdifactLSQ field to default
148
    t::lib::Mocks::mock_preference( 'EdifactLSQ', 'location' );
148
    t::lib::Mocks::mock_preference( 'EdifactLSQ', 'location' );
149
149
150
    # Ensure EdifactLSL is empty to maintain backwards compatibility
151
    t::lib::Mocks::mock_preference( 'EdifactLSL', '' );
152
150
    $order->basket->create_items('ordering')->store;
153
    $order->basket->create_items('ordering')->store;
151
    is(
154
    is(
152
        $edi_order->order_line( 1, $orders[0] ),
155
        $edi_order->order_line( 1, $orders[0] ),
Lines 226-231 subtest 'order_line() tests' => sub { Link Here
226
    # Set EdifactLSQ field to ccode
229
    # Set EdifactLSQ field to ccode
227
    t::lib::Mocks::mock_preference( 'EdifactLSQ', 'ccode' );
230
    t::lib::Mocks::mock_preference( 'EdifactLSQ', 'ccode' );
228
231
232
    # Ensure EdifactLSL is empty to maintain backwards compatibility
233
    t::lib::Mocks::mock_preference( 'EdifactLSL', '' );
234
229
    $order->basket->create_items('ordering')->store;
235
    $order->basket->create_items('ordering')->store;
230
    is(
236
    is(
231
        $edi_order->order_line( 1, $orders[0] ),
237
        $edi_order->order_line( 1, $orders[0] ),
Lines 427-429 subtest 'RFF+ON purchase order number generation' => sub { Link Here
427
433
428
    $schema->storage->txn_rollback;
434
    $schema->storage->txn_rollback;
429
};
435
};
430
- 
436
437
subtest 'gir_segments() with LSL and LSQ preferences' => sub {
438
    plan tests => 20;
439
440
    $schema->storage->txn_begin;
441
442
    # Test LSL and LSQ preferences
443
    t::lib::Mocks::mock_preference( 'EdifactLSQ', 'location' );
444
    t::lib::Mocks::mock_preference( 'EdifactLSL', 'ccode' );
445
446
    # Create test items with both location and ccode
447
    my @test_items = (
448
        {
449
            branchcode     => 'BRANCH1',
450
            itype          => 'BOOK',
451
            itemcallnumber => 'CALL1',
452
            location       => 'FICTION',    # Will be used for LSQ
453
            ccode          => 'ADULT',      # Will be used for LSL
454
        },
455
        {
456
            branchcode     => 'BRANCH2',
457
            itype          => 'DVD',
458
            itemcallnumber => 'CALL2',
459
            location       => 'MEDIA',      # Will be used for LSQ
460
            ccode          => 'CHILD',      # Will be used for LSL
461
        }
462
    );
463
464
    my $params = {
465
        ol_fields => { budget_code => 'FUND123' },
466
        items     => \@test_items
467
    };
468
469
    my @segments = Koha::Edifact::Order::gir_segments($params);
470
471
    ok( scalar @segments >= 2, 'At least two segments created for two items' );
472
473
    # Check first item's GIR segment (segments are strings in EDI format)
474
    my $first_gir = $segments[0];
475
    ok( $first_gir, 'First segment exists' );
476
477
    # Check that the segment contains expected data
478
    like( $first_gir, qr/GIR/,         'Segment contains GIR tag' );
479
    like( $first_gir, qr/FUND123:LFN/, 'Budget code included in first segment' );
480
    like( $first_gir, qr/BRANCH1:LLO/, 'Branch code included in first segment' );
481
    like( $first_gir, qr/BOOK:LST/,    'Item type included in first segment' );
482
    like( $first_gir, qr/FICTION:LSQ/, 'LSQ field contains location value' );
483
    like( $first_gir, qr/ADULT:LSL/,   'LSL field contains collection code value' );
484
485
    # Test reversed preferences
486
    t::lib::Mocks::mock_preference( 'EdifactLSQ', 'ccode' );       # LSQ -> collection
487
    t::lib::Mocks::mock_preference( 'EdifactLSL', 'location' );    # LSL -> location
488
489
    my @test_items_rev = (
490
        {
491
            branchcode     => 'BRANCH3',
492
            itype          => 'BOOK',
493
            itemcallnumber => 'CALL3',
494
            location       => 'REFERENCE',    # Will be used for LSL
495
            ccode          => 'RARE',         # Will be used for LSQ
496
        }
497
    );
498
499
    my $params_rev = {
500
        ol_fields => { budget_code => 'FUND456' },
501
        items     => \@test_items_rev
502
    };
503
504
    @segments = Koha::Edifact::Order::gir_segments($params_rev);
505
    my $gir_rev = $segments[0];
506
507
    # Check that the segment contains expected reversed mappings
508
    like( $gir_rev, qr/RARE:LSQ/,      'LSQ field contains collection code when preference is ccode' );
509
    like( $gir_rev, qr/REFERENCE:LSL/, 'LSL field contains location when preference is location' );
510
511
    # Test with one preference empty
512
    t::lib::Mocks::mock_preference( 'EdifactLSQ', 'location' );
513
    t::lib::Mocks::mock_preference( 'EdifactLSL', '' );           # Empty = ignore
514
515
    @segments = Koha::Edifact::Order::gir_segments($params);
516
    my $gir_partial = $segments[0];
517
518
    like( $gir_partial, qr/FICTION:LSQ/, 'LSQ field included when preference is set' );
519
    unlike( $gir_partial, qr/:LSL/, 'LSL field not included when preference is empty' );
520
521
    # Test with both preferences empty
522
    t::lib::Mocks::mock_preference( 'EdifactLSQ', '' );
523
    t::lib::Mocks::mock_preference( 'EdifactLSL', '' );
524
525
    @segments = Koha::Edifact::Order::gir_segments($params);
526
    my $gir_empty = $segments[0];
527
528
    unlike( $gir_empty, qr/:LSQ/, 'LSQ field not included when preference is empty' );
529
    unlike( $gir_empty, qr/:LSL/, 'LSL field not included when preference is empty' );
530
531
    # Test with LSQ empty but LSL set
532
    t::lib::Mocks::mock_preference( 'EdifactLSQ', '' );         # Empty = ignore
533
    t::lib::Mocks::mock_preference( 'EdifactLSL', 'ccode' );    # LSL -> collection
534
535
    @segments = Koha::Edifact::Order::gir_segments($params);
536
    my $gir_lsl_only = $segments[0];
537
538
    unlike( $gir_lsl_only, qr/:LSQ/, 'LSQ field not included when preference is empty' );
539
    like( $gir_lsl_only, qr/ADULT:LSL/, 'LSL field included when preference is set' );
540
541
    # Test with both preferences set to same field (location)
542
    t::lib::Mocks::mock_preference( 'EdifactLSQ', 'location' );    # LSQ -> location
543
    t::lib::Mocks::mock_preference( 'EdifactLSL', 'location' );    # LSL -> location
544
545
    @segments = Koha::Edifact::Order::gir_segments($params);
546
    my $gir_both_location = $segments[0];
547
548
    like( $gir_both_location, qr/FICTION:LSQ/, 'LSQ field contains location value when both map to location' );
549
    like( $gir_both_location, qr/FICTION:LSL/, 'LSL field contains location value when both map to location' );
550
551
    # Test with both preferences set to same field (ccode)
552
    t::lib::Mocks::mock_preference( 'EdifactLSQ', 'ccode' );       # LSQ -> collection
553
    t::lib::Mocks::mock_preference( 'EdifactLSL', 'ccode' );       # LSL -> collection
554
555
    @segments = Koha::Edifact::Order::gir_segments($params);
556
    my $gir_both_ccode = $segments[0];
557
558
    like( $gir_both_ccode, qr/ADULT:LSQ/, 'LSQ field contains collection code value when both map to ccode' );
559
    like( $gir_both_ccode, qr/ADULT:LSL/, 'LSL field contains collection code value when both map to ccode' );
560
561
    $schema->storage->txn_rollback;
562
};

Return to bug 40391