Bug 26922: Regression tests
[koha.git] / Koha / Edifact / Line.pm
blob8d14ba5e5f82cd37f92c0783d4d1e3bc02afed01
1 package Koha::Edifact::Line;
3 # Copyright 2014, 2015 PTFS-Europe Ltd
5 # This file is part of Koha.
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
20 use strict;
21 use warnings;
22 use utf8;
24 use MARC::Record;
25 use MARC::Field;
26 use Carp;
28 sub new {
29 my ( $class, $data_array_ref ) = @_;
30 my $self = _parse_lines($data_array_ref);
32 bless $self, $class;
33 return $self;
36 # helper routine used by constructor
37 # creates the hashref used as a data structure by the Line object
39 sub _parse_lines {
40 my $aref = shift;
42 my $lin = shift @{$aref};
44 my $id = $lin->elem( 2, 0 ); # may be undef in ordrsp
45 my $action = $lin->elem( 1, 0 );
46 my $d = {
47 line_item_number => $lin->elem(0),
48 action_notification => $action,
49 item_number_id => $id,
50 additional_product_ids => [],
52 my @item_description;
54 foreach my $s ( @{$aref} ) {
55 if ( $s->tag eq 'PIA' ) {
56 push @{ $d->{additional_product_ids} },
58 function_code => $s->elem(0),
59 item_number => $s->elem( 1, 0 ),
60 number_type => $s->elem( 1, 1 ),
63 elsif ( $s->tag eq 'IMD' ) {
64 push @item_description, $s;
66 elsif ( $s->tag eq 'QTY' ) {
67 if ( $s->elem( 0, 0 ) eq '47' ) {
68 $d->{quantity_invoiced} = $s->elem( 0, 1 );
70 $d->{quantity} = $s->elem( 0, 1 );
72 elsif ( $s->tag eq 'DTM' ) {
73 if ( $s->elem( 0, 0 ) eq '44' ) {
74 $d->{availability_date} = $s->elem( 0, 1 );
77 elsif ( $s->tag eq 'GIR' ) {
79 # we may get a Gir for each copy if QTY > 1
80 if ( !$d->{GIR} ) {
81 $d->{GIR} = [];
82 push @{ $d->{GIR} }, extract_gir($s);
84 else {
85 my $gir = extract_gir($s);
86 if ( $gir->{copy} ) { # may have to merge
87 foreach my $g ( @{ $d->{GIR} } ) {
88 if ( $gir->{copy} eq $g->{copy} ) {
89 foreach my $field ( keys %{$gir} ) {
90 if ( !exists $g->{$field} ) {
91 $g->{$field} = $gir->{$field};
94 undef $gir;
95 last;
98 if ( defined $gir ) {
99 push @{ $d->{GIR} }, $gir;
104 elsif ( $s->tag eq 'FTX' ) {
106 my $type = $s->elem(0);
107 my $ctype = 'coded_free_text';
108 if ( $type eq 'LNO' ) { # Ingrams Oasis Internal Notes field
109 $type = 'internal_notes';
110 $ctype = 'coded_internal_note';
112 elsif ( $type eq 'LIN' ) {
113 $type = 'orderline_free_text';
114 $ctype = 'coded_orderline_text';
116 elsif ( $type eq 'SUB' ) {
117 $type = 'coded_substitute_text';
119 else {
120 $type = 'free_text';
123 my $coded_text = $s->elem(2);
124 if ( ref $coded_text eq 'ARRAY' && $coded_text->[0] ) {
125 $d->{$ctype}->{table} = $coded_text->[1];
126 $d->{$ctype}->{code} = $coded_text->[0];
129 my $ftx = $s->elem(3);
130 if ( ref $ftx eq 'ARRAY' ) { # it comes in 70 character components
131 $ftx = join q{ }, @{$ftx};
133 if ( exists $d->{$type} ) { # we can only catenate repeats
134 $d->{$type} .= q{ };
135 $d->{$type} .= $ftx;
137 else {
138 $d->{$type} = $ftx;
141 elsif ( $s->tag eq 'MOA' ) {
143 $d->{monetary_amount} = $s->elem( 0, 1 );
145 elsif ( $s->tag eq 'PRI' ) {
147 $d->{price} = $s->elem( 0, 1 );
149 elsif ( $s->tag eq 'RFF' ) {
150 my $qualifier = $s->elem( 0, 0 );
151 if ( $qualifier eq 'QLI' ) { # Suppliers unique quotation reference
152 $d->{reference} = $s->elem( 0, 1 );
154 elsif ( $qualifier eq 'LI' ) { # Buyer's unique orderline number
155 $d->{ordernumber} = $s->elem( 0, 1 );
157 elsif ( $qualifier eq 'SLI' )
158 { # Suppliers unique order line reference number
159 $d->{orderline_reference_number} = $s->elem( 0, 1 );
163 $d->{item_description} = _format_item_description(@item_description);
164 $d->{segs} = $aref;
166 return $d;
169 sub _format_item_description {
170 my @imd = @_;
171 my $bibrec = {};
173 # IMD : +Type code 'L' + characteristic code 3 char + Description in comp 3 & 4
174 foreach my $imd (@imd) {
175 my $type_code = $imd->elem(0);
176 my $ccode = $imd->elem(1);
177 my $desc = $imd->elem( 2, 3 );
178 if ( $imd->elem( 2, 4 ) ) {
179 $desc .= $imd->elem( 2, 4 );
181 if ( $type_code ne 'L' ) {
182 carp
183 "Only handles text item descriptions at present: code=$type_code";
184 next;
186 if ( exists $bibrec->{$ccode} ) {
187 $bibrec->{$ccode} .= q{ };
188 $bibrec->{$ccode} .= $desc;
190 else {
191 $bibrec->{$ccode} = $desc;
194 return $bibrec;
197 sub marc_record {
198 my $self = shift;
199 my $b = $self->{item_description};
201 my $bib = MARC::Record->new();
203 my @spec;
204 my @fields;
205 if ( exists $b->{'010'} ) {
206 @spec = qw( 100 a 011 c 012 b 013 d 014 e );
207 push @fields, new_field( $b, [ 100, 1, q{ } ], @spec );
209 if ( exists $b->{'020'} ) {
210 @spec = qw( 020 a 021 c 022 b 023 d 024 e );
211 push @fields, new_field( $b, [ 700, 1, q{ } ], @spec );
214 # corp conf
215 if ( exists $b->{'030'} ) {
216 push @fields, $self->corpcon(1);
218 if ( exists $b->{'040'} ) {
219 push @fields, $self->corpcon(7);
221 if ( exists $b->{'050'} ) {
222 @spec = qw( '050' a '060' b '065' c );
223 push @fields, new_field( $b, [ 245, 1, 0 ], @spec );
225 if ( exists $b->{100} ) {
226 @spec = qw( 100 a 101 b);
227 push @fields, new_field( $b, [ 250, q{ }, q{ } ], @spec );
229 @spec = qw( 110 a 120 b 170 c );
230 my $f = new_field( $b, [ 260, q{ }, q{ } ], @spec );
231 if ($f) {
232 push @fields, $f;
234 @spec = qw( 180 a 181 b 182 c 183 e);
235 $f = new_field( $b, [ 300, q{ }, q{ } ], @spec );
236 if ($f) {
237 push @fields, $f;
239 if ( exists $b->{190} ) {
240 @spec = qw( 190 a);
241 push @fields, new_field( $b, [ 490, q{ }, q{ } ], @spec );
244 if ( exists $b->{200} ) {
245 @spec = qw( 200 a);
246 push @fields, new_field( $b, [ 490, q{ }, q{ } ], @spec );
248 if ( exists $b->{210} ) {
249 @spec = qw( 210 a);
250 push @fields, new_field( $b, [ 490, q{ }, q{ } ], @spec );
252 if ( exists $b->{300} ) {
253 @spec = qw( 300 a);
254 push @fields, new_field( $b, [ 500, q{ }, q{ } ], @spec );
256 if ( exists $b->{310} ) {
257 @spec = qw( 310 a);
258 push @fields, new_field( $b, [ 520, q{ }, q{ } ], @spec );
260 if ( exists $b->{320} ) {
261 @spec = qw( 320 a);
262 push @fields, new_field( $b, [ 521, q{ }, q{ } ], @spec );
264 if ( exists $b->{260} ) {
265 @spec = qw( 260 a);
266 push @fields, new_field( $b, [ 600, q{ }, q{ } ], @spec );
268 if ( exists $b->{270} ) {
269 @spec = qw( 270 a);
270 push @fields, new_field( $b, [ 650, q{ }, q{ } ], @spec );
272 if ( exists $b->{280} ) {
273 @spec = qw( 280 a);
274 push @fields, new_field( $b, [ 655, q{ }, q{ } ], @spec );
277 # class
278 if ( exists $b->{230} ) {
279 @spec = qw( 230 a);
280 push @fields, new_field( $b, [ '082', q{ }, q{ } ], @spec );
282 if ( exists $b->{240} ) {
283 @spec = qw( 240 a);
284 push @fields, new_field( $b, [ '084', q{ }, q{ } ], @spec );
286 $bib->insert_fields_ordered(@fields);
288 return $bib;
291 sub corpcon {
292 my ( $self, $level ) = @_;
293 my $test_these = {
294 1 => [ '033', '032', '034' ],
295 7 => [ '043', '042', '044' ],
297 my $conf = 0;
298 foreach my $t ( @{ $test_these->{$level} } ) {
299 if ( exists $self->{item_description}->{$t} ) {
300 $conf = 1;
303 my $tag;
304 my @spec;
305 my ( $i1, $i2 ) = ( q{ }, q{ } );
306 if ($conf) {
307 $tag = ( $level * 100 ) + 11;
308 if ( $level == 1 ) {
309 @spec = qw( 030 a 031 e 032 n 033 c 034 d);
311 else {
312 @spec = qw( 040 a 041 e 042 n 043 c 044 d);
315 else {
316 $tag = ( $level * 100 ) + 10;
317 if ( $level == 1 ) {
318 @spec = qw( 030 a 031 b);
320 else {
321 @spec = qw( 040 a 041 b);
324 return new_field( $self->{item_description}, [ $tag, $i1, $i2 ], @spec );
327 sub new_field {
328 my ( $b, $tag_ind, @sfd_elem ) = @_;
329 my @sfd;
330 while (@sfd_elem) {
331 my $e = shift @sfd_elem;
332 my $c = shift @sfd_elem;
333 if ( exists $b->{$e} ) {
334 push @sfd, $c, $b->{$e};
337 if (@sfd) {
338 my $field = MARC::Field->new( @{$tag_ind}, @sfd );
339 return $field;
341 return;
344 # Accessor methods to line data
346 sub item_number_id {
347 my $self = shift;
348 return $self->{item_number_id};
351 sub line_item_number {
352 my $self = shift;
353 return $self->{line_item_number};
356 sub additional_product_ids {
357 my $self = shift;
358 return $self->{additional_product_ids};
361 sub action_notification {
362 my $self = shift;
363 my $a = $self->{action_notification};
364 if ($a) {
365 $a = _translate_action($a); # return the associated text string
367 return $a;
370 sub item_description {
371 my $self = shift;
372 return $self->{item_description};
375 sub monetary_amount {
376 my $self = shift;
377 return $self->{monetary_amount};
380 sub quantity {
381 my $self = shift;
382 return $self->{quantity};
385 sub quantity_invoiced {
386 my $self = shift;
387 return $self->{quantity_invoiced};
390 sub price {
391 my $self = shift;
392 return $self->{price};
395 sub reference {
396 my $self = shift;
397 return $self->{reference};
400 sub orderline_reference_number {
401 my $self = shift;
402 return $self->{orderline_reference_number};
405 sub ordernumber {
406 my $self = shift;
407 return $self->{ordernumber};
410 sub free_text {
411 my $self = shift;
412 return $self->{free_text};
415 sub coded_free_text {
416 my $self = shift;
417 return $self->{coded_free_text}->{code};
420 sub internal_notes {
421 my $self = shift;
422 return $self->{internal_notes};
425 sub coded_internal_note {
426 my $self = shift;
427 return $self->{coded_internal_note}->{code};
430 sub orderline_free_text {
431 my $self = shift;
432 return $self->{orderline_free_text};
435 sub coded_orderline_text {
436 my $self = shift;
437 my $code = $self->{coded_orderline_text}->{code};
438 my $table = $self->{coded_orderline_text}->{table};
439 my $txt;
440 if ( $table eq '8B' || $table eq '7B' ) {
441 $txt = translate_8B($code);
443 elsif ( $table eq '12B' ) {
444 $txt = translate_12B($code);
446 if ( !$txt || $txt eq 'no match' ) {
447 $txt = $code;
449 return $txt;
452 sub substitute_free_text {
453 my $self = shift;
454 return $self->{substitute_free_text};
457 sub coded_substitute_text {
458 my $self = shift;
459 return $self->{coded_substitute_text}->{code};
462 # This will take a standard code as returned
463 # by (orderline|substitue)-free_text (FTX seg LIN)
464 # and expand it using EditEUR code list 8B
465 sub translate_8B {
466 my ($code) = @_;
468 # list 7B is a subset of this
469 my %code_list_8B = (
470 AB => 'Publication abandoned',
471 AD => 'Apply direct',
472 AU => 'Publisher address unknown',
473 CS => 'Status uncertain',
474 FQ => 'Only available abroad',
475 HK => 'Paperback OP: Hardback available',
476 IB => 'In stock',
477 IP => 'In print and in stock at publisher',
478 MD => 'Manufactured on demand',
479 NK => 'Item not known',
480 NN => 'We do not supply this item',
481 NP => 'Not yet published',
482 NQ => 'Not stocked',
483 NS => 'Not sold separately',
484 OB => 'Temporarily out of stock',
485 OF => 'This format out of print: other format available',
486 OP => 'Out of print',
487 OR => 'Out pf print; New Edition coming',
488 PK => 'Hardback out of print: paperback available',
489 PN => 'Publisher no longer in business',
490 RE => 'Awaiting reissue',
491 RF => 'refer to other publisher or distributor',
492 RM => 'Remaindered',
493 RP => 'Reprinting',
494 RR => 'Rights restricted: cannot supply in this market',
495 SD => 'Sold',
496 SN => 'Our supplier cannot trace',
497 SO => 'Pack or set not available: single items only',
498 ST => 'Stocktaking: temporarily unavailable',
499 TO => 'Only to order',
500 TU => 'Temporarily unavailable',
501 UB => 'Item unobtainable from our suppliers',
502 UC => 'Unavailable@ reprint under consideration',
505 if ( exists $code_list_8B{$code} ) {
506 return $code_list_8B{$code};
508 else {
509 return 'no match';
513 sub translate_12B {
514 my ($code) = @_;
516 my %code_list_12B = (
517 100 => 'Order line accepted',
518 101 => 'Price query: orderline will be held awaiting customer response',
519 102 =>
520 'Discount query: order line will be held awaiting customer response',
521 103 => 'Minimum order value not reached: order line will be held',
522 104 =>
523 'Firm order required: order line will be held awaiting customer response',
524 110 => 'Order line accepted, substitute product will be supplied',
525 200 => 'Order line not accepted',
526 201 => 'Price query: order line not accepted',
527 202 => 'Discount query: order line not accepted',
528 203 => 'Minimum order value not reached: order line not accepted',
529 205 => 'Order line not accepted: quoted promotion is invalid',
530 206 => 'Order line not accepted: quoted promotion has ended',
531 207 =>
532 'Order line not accepted: customer ineligible for quoted promotion',
533 210 => 'Order line not accepted: substitute product is offered',
534 220 => 'Oustanding order line cancelled: reason unspecified',
535 221 => 'Oustanding order line cancelled: past order expiry date',
536 222 => 'Oustanding order line cancelled by customer request',
537 223 => 'Oustanding order line cancelled: unable to supply',
538 300 => 'Order line passed to new supplier',
539 301 => 'Order line passed to secondhand department',
540 400 => 'Backordered - awaiting supply',
541 401 => 'On order from our supplier',
542 402 => 'On order from abroad',
543 403 => 'Backordered, waiting to reach minimum order value',
544 404 => 'Despatched from our supplier, awaiting delivery',
545 405 => 'Our supplier sent wrong item(s), re-ordered',
546 406 => 'Our supplier sent short, re-ordered',
547 407 => 'Our supplier sent damaged item(s), re-ordered',
548 408 => 'Our supplier sent imperfect item(s), re-ordered',
549 409 => 'Our supplier cannot trace order, re-ordered',
550 410 => 'Ordered item(s) being processed by bookseller',
551 411 =>
552 'Ordered item(s) being processed by bookseller, awaiting customer action',
553 412 => 'Order line held awaiting customer instruction',
554 500 => 'Order line on hold - contact customer service',
555 800 => 'Order line already despatched',
556 900 => 'Cannot trace order line',
557 901 => 'Order line held: note title change',
558 902 => 'Order line held: note availability date delay',
559 903 => 'Order line held: note price change',
560 999 => 'Temporary hold: order action not yet determined',
563 if ( exists $code_list_12B{$code} ) {
564 return $code_list_12B{$code};
566 else {
567 return 'no match';
571 # item_desription_fields accessors
573 sub title {
574 my $self = shift;
575 my $titlefield = q{050};
576 if ( exists $self->{item_description}->{$titlefield} ) {
577 return $self->{item_description}->{$titlefield};
579 return;
582 sub author {
583 my $self = shift;
584 my $field = q{010};
585 if ( exists $self->{item_description}->{$field} ) {
586 my $a = $self->{item_description}->{$field};
587 my $forename_field = q{011};
588 if ( exists $self->{item_description}->{$forename_field} ) {
589 $a .= ', ';
590 $a .= $self->{item_description}->{$forename_field};
592 return $a;
594 return;
597 sub series {
598 my $self = shift;
599 my $field = q{190};
600 if ( exists $self->{item_description}->{$field} ) {
601 return $self->{item_description}->{$field};
603 return;
606 sub publisher {
607 my $self = shift;
608 my $field = q{120};
609 if ( exists $self->{item_description}->{$field} ) {
610 return $self->{item_description}->{$field};
612 return;
615 sub publication_date {
616 my $self = shift;
617 my $field = q{170};
618 if ( exists $self->{item_description}->{$field} ) {
619 return $self->{item_description}->{$field};
621 return;
624 sub dewey_class {
625 my $self = shift;
626 my $field = q{230};
627 if ( exists $self->{item_description}->{$field} ) {
628 return $self->{item_description}->{$field};
630 return;
633 sub lc_class {
634 my $self = shift;
635 my $field = q{240};
636 if ( exists $self->{item_description}->{$field} ) {
637 return $self->{item_description}->{$field};
639 return;
642 sub girfield {
643 my ( $self, $field, $occ ) = @_;
644 if ( $self->number_of_girs ) {
646 # defaults to occurrence 0 returns undef if occ requested > occs
647 if ( defined $occ && $occ >= @{ $self->{GIR} } ) {
648 return;
650 $occ ||= 0;
651 return $self->{GIR}->[$occ]->{$field};
653 else {
654 return;
658 sub number_of_girs {
659 my $self = shift;
660 if ( $self->{GIR} ) {
662 my $qty = @{ $self->{GIR} };
664 return $qty;
666 else {
667 return 0;
671 sub extract_gir {
672 my $s = shift;
673 my %qmap = (
674 LAC => 'barcode',
675 LAF => 'first_accession_number',
676 LAL => 'last_accession_number',
677 LCL => 'classification',
678 LCO => 'item_unique_id',
679 LCV => 'copy_value',
680 LFH => 'feature_heading',
681 LFN => 'fund_allocation',
682 LFS => 'filing_suffix',
683 LLN => 'loan_category',
684 LLO => 'branch',
685 LLS => 'label_sublocation',
686 LQT => 'part_order_quantity',
687 LRS => 'record_sublocation',
688 LSM => 'shelfmark',
689 LSQ => 'collection_code',
690 LST => 'stock_category',
691 LSZ => 'size_code',
692 LVC => 'coded_servicing_instruction',
693 LVT => 'servicing_instruction',
694 LHC => 'library_holding_code',
695 LRP => 'library_rotation_plan',
696 LSC => 'statistical_category',
697 RIC => 'reader_interest_category',
700 my $set_qualifier = $s->elem( 0, 0 ); # copy number
701 my $gir_element = { copy => $set_qualifier, };
702 my $element = 1;
703 while ( my $e = $s->elem($element) ) {
704 ++$element;
705 if ( exists $qmap{ $e->[1] } ) {
706 my $qualifier = $qmap{ $e->[1] };
707 $gir_element->{$qualifier} = $e->[0];
709 else {
711 carp "Unrecognized GIR code : $e->[1] for $e->[0]";
714 return $gir_element;
717 # mainly for invoice processing amt_ will derive from MOA price_ from PRI and tax_ from TAX/MOA pairsn
718 sub moa_amt {
719 my ( $self, $qualifier ) = @_;
720 foreach my $s ( @{ $self->{segs} } ) {
721 if ( $s->tag eq 'MOA' && $s->elem( 0, 0 ) eq $qualifier ) {
722 return $s->elem( 0, 1 );
725 return;
727 sub moa_multiple_amt {
728 my ( $self, $qualifier ) = @_;
729 # return a repeatable MOA field
730 my $amt = 0;
731 my $found = 0;
732 foreach my $s ( @{ $self->{segs} } ) {
733 if ( $s->tag eq 'MOA' && $s->elem( 0, 0 ) eq $qualifier ) {
734 $amt += $s->elem( 0, 1 );
735 $found = 1;
738 if ($found) {
739 return $amt;
741 return;
744 sub amt_discount {
745 my $self = shift;
746 return $self->moa_amt('52');
749 sub amt_prepayment {
750 my $self = shift;
751 return $self->moa_amt('113');
754 # total including allowances & tax
755 sub amt_total {
756 my $self = shift;
757 return $self->moa_amt('128');
760 # Used to give price in currency other than that given in price
761 sub amt_unitprice {
762 my $self = shift;
763 return $self->moa_amt('146');
766 # item amount after allowances excluding tax
767 sub amt_lineitem {
768 my $self = shift;
769 return $self->moa_amt('203');
771 sub amt_taxoncharge {
772 my $self = shift;
773 return $self->moa_multiple_amt('124');
776 sub pri_price {
777 my ( $self, $price_qualifier ) = @_;
778 # In practice qualifier is AAE in the quote and AAA & AAB in invoices
779 # but the following are defined
780 # AAA calculation price net (unit price excl tax but incl any allowances or charges)
781 # AAB calculation price gross (unit price excl all taxes, allowances and charges )
782 # AAE information price (incl tax but excl allowances or charges )
783 # AAF information price (including all taxes, allowances or charges)
784 foreach my $s ( @{ $self->{segs} } ) {
785 if ( $s->tag eq 'PRI' && $s->elem( 0, 0 ) eq $price_qualifier ) {
786 # in practice not all 3 fields may be present
787 # so use a temp variable to avoid runtime warnings
788 my $p = {
789 price => undef,
790 type => undef,
791 type_qualifier => undef,
793 $p->{price} = $s->elem( 0, 1 );
794 $p->{type} = $s->elem( 0, 2 );
795 $p->{type_qualifier} = $s->elem( 0, 3 );
796 return $p;
799 return;
802 # unit price that will be chaged excl tax
803 sub price_net {
804 my $self = shift;
805 my $p = $self->pri_price('AAA');
806 if ( defined $p ) {
807 return $p->{price};
809 return;
812 # unit price excluding all allowances, charges and taxes
813 sub price_gross {
814 my $self = shift;
815 my $p = $self->pri_price('AAB');
816 if ( defined $p ) {
817 return $p->{price};
819 return;
822 # information price incl tax excluding allowances, charges
823 sub price_info {
824 my $self = shift;
825 my $p = $self->pri_price('AAE');
826 if ( defined $p ) {
827 return $p->{price};
829 return;
832 # information price incl tax,allowances, charges
833 sub price_info_inclusive {
834 my $self = shift;
835 my $p = $self->pri_price('AAF');
836 if ( defined $p ) {
837 return $p->{price};
839 return;
842 sub tax {
843 my $self = shift;
844 return $self->moa_amt('124');
847 sub tax_rate {
848 my $self = shift;
849 my $tr = {};
850 foreach my $s ( @{ $self->{segs} } ) {
851 if ( $s->tag eq 'TAX' && $s->elem( 0, 0 ) == 7 ) {
852 $tr->{type} = $s->elem( 1, 0 ); # VAT, GST or IMP
853 $tr->{rate} = $s->elem( 4, 3 ); # percentage
854 # category values may be:
855 # E = exempt from tax
856 # G = export item, tax not charged
857 # H = higher rate
858 # L = lower rate
859 # S = standard rate
860 # Z = zero-rated
861 $tr->{category} = $s->elem( 5, 0 );
862 if (!defined $tr->{rate} && $tr->{category} eq 'Z') {
863 $tr->{rate} = 0;
865 return $tr;
868 return;
871 sub availability_date {
872 my $self = shift;
873 if ( exists $self->{availability_date} ) {
874 return $self->{availability_date};
876 return;
879 # return text string representing action code
880 sub _translate_action {
881 my $code = shift;
882 my %action = (
883 2 => 'cancelled',
884 3 => 'change_requested',
885 4 => 'no_action',
886 5 => 'accepted',
887 10 => 'not_found',
888 24 => 'recorded', # Order accepted but a change notified
890 if ( $code && exists $action{$code} ) {
891 return $action{$code};
893 return $code;
897 __END__
899 =head1 NAME
901 Koha::Edifact::Line
903 =head1 SYNOPSIS
905 Class to abstractly handle a Line in an Edifact Transmission
907 =head1 DESCRIPTION
909 Allows access to Edifact line elements by name
911 =head1 BUGS
913 None documented at present
915 =head1 Methods
917 =head2 new
919 Called with an array ref of segments constituting the line
921 =head1 AUTHOR
923 Colin Campbell <colin.campbell@ptfs-europe.com>
925 =head1 COPYRIGHT
927 Copyright 2014,2015 PTFS-Europe Ltd
928 This program is free software, You may redistribute it under
929 under the terms of the GNU General Public License
932 =cut