From f1d923a0a492dace4b134ab5529af6bd19e27d0a Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Wed, 23 Jan 2013 15:10:42 -0500 Subject: [PATCH] Teach ACQ EDI and print PO templates about order identifiers Update both templates to look for the preferred order identiier value instead of the first reasonable it can find. ISSN identifiers are now correctly encoded in the EDI as well. [LFW] Fix permission numbering flub. Signed-off-by: Bill Erickson Signed-off-by: Lebbeous Fogle-Weekley --- .../OpenILS/Application/Trigger/Reactor.pm | 30 ++ Open-ILS/src/sql/Pg/950.data.seed-values.sql | 38 +- .../Pg/upgrade/XXXX.data.acq-order-ident.sql | 373 ++++++++++++++++++ 3 files changed, 427 insertions(+), 14 deletions(-) create mode 100644 Open-ILS/src/sql/Pg/upgrade/XXXX.data.acq-order-ident.sql diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Reactor.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Reactor.pm index 7be19932ad..093e55184c 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Reactor.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Reactor.pm @@ -199,6 +199,36 @@ $_TT_helpers = { return @isbns; }, + get_li_order_ident => sub { + my $attrs = shift; + + # preferred identifier + my ($attr) = grep { $U->is_true($_->order_ident) } @$attrs; + return $attr if $attr; + + # note we're not using get_li_attr, since we need the + # attr object and not just the attr value + + # isbn-13 + ($attr) = grep { + $_->attr_name eq 'isbn' and + $_->attr_type eq 'lineitem_marc_attr_definition' and + length($_->attr_value) == 13 + } @$attrs; + return $attr if $attr; + + for my $name (qw/isbn issn upc/) { + ($attr) = grep { + $_->attr_name eq $name and + $_->attr_type eq 'lineitem_marc_attr_definition' + } @$attrs; + return $attr if $attr; + } + + # any 'identifier' attr + return ( grep { $_->attr_name eq 'identifier' } @$attrs)[0]; + }, + # helpers.get_li_attr('isbn_13', li.attributes) # returns matching line item attribute, or undef get_li_attr => \&get_li_attr, diff --git a/Open-ILS/src/sql/Pg/950.data.seed-values.sql b/Open-ILS/src/sql/Pg/950.data.seed-values.sql index e145206b48..b3469561da 100644 --- a/Open-ILS/src/sql/Pg/950.data.seed-values.sql +++ b/Open-ILS/src/sql/Pg/950.data.seed-values.sql @@ -1587,7 +1587,7 @@ INSERT INTO permission.perm_list ( id, code, description ) VALUES 'Allows a user to make changes to best-hold selection sort order', 'ppl', 'description')), ( 547, 'ACQ_ADD_LINEITEM_IDENTIFIER', oils_i18n_gettext(547, 'When granted, newly added lineitem identifiers will propagate to linked bib records', 'ppl', 'description')), - ( 548, 'ACQ_SET_LINEITEM_IDENTIFIER', oils_i18n_gettext(549, + ( 548, 'ACQ_SET_LINEITEM_IDENTIFIER', oils_i18n_gettext(548, 'Allows staff to change the lineitem identifier', 'ppl', 'description')) ; @@ -6586,11 +6586,12 @@ date [% date.format(date.now, '%Y%m%d') %] [% price = li.estimated_unit_price %] [% litotal = (price * count) %] [% subtotal = subtotal + litotal %] - [% isbn = PROCESS get_li_attr attr_name = 'isbn' %] - [% ident = PROCESS get_li_attr attr_name = 'identifier' %] - + [% + ident_attr = helpers.get_li_order_ident(li.attributes); + SET ident_value = ident_attr.attr_value IF ident_attr; + %] [% target.id %] - [% isbn || ident %] + [% ident_value %] [% PROCESS get_li_attr attr_name = 'title' %] [% count %] [% price %] @@ -8120,15 +8121,24 @@ $$ [%- FOR li IN target.lineitems %] { "line_index":"[% li.id %]", - "identifiers":[ [%-# li.isbns = helpers.get_li_isbns(li.attributes) %] - [% FOR isbn IN helpers.get_li_isbns(li.attributes) -%] - [% IF isbn.length == 13 -%] - {"id-qualifier":"EN","id":"[% isbn %]"}, - [% ELSE -%] - {"id-qualifier":"IB","id":"[% isbn %]"}, - [%- END %] - [% END %] - {"id-qualifier":"IN","id":"[% li.id %]"} + "identifiers":[ + [%- + idval = ''; + idqual = 'EN'; # default ISBN/UPC/EAN-13 + ident_attr = helpers.get_li_order_ident(li.attributes); + IF ident_attr; + idname = ident_attr.attr_name; + idval = ident_attr.attr_value; + IF idname == 'isbn' AND idval.length != 13; + idqual = 'IB'; + ELSIF idname == 'issn'; + idqual = 'IS'; + END; + ELSE; + idqual = 'IN'; + idval = li.id; + END -%] + {"id-qualifier":"[% idqual %]","id":"[% idval %]"} ], "price":[% li.estimated_unit_price || '0.00' %], "desc":[ diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.data.acq-order-ident.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.data.acq-order-ident.sql new file mode 100644 index 0000000000..c5d467b72c --- /dev/null +++ b/Open-ILS/src/sql/Pg/upgrade/XXXX.data.acq-order-ident.sql @@ -0,0 +1,373 @@ + +BEGIN; + +-- Listed here for reference / ease of access. The update +-- is not applied here (see the WHERE clause). +UPDATE action_trigger.event_definition SET template = +$$ +[%- USE date -%] +[% + # extract some commonly used variables + + VENDOR_SAN = target.provider.san; + VENDCODE = target.provider.edi_default.vendcode; + VENDACCT = target.provider.edi_default.vendacct; + ORG_UNIT_SAN = target.ordering_agency.mailing_address.san; + + # set the vendor / provider + + VENDOR_BT = 0; # Baker & Taylor + VENDOR_INGRAM = 0; + VENDOR_BRODART = 0; + VENDOR_MW_TAPE = 0; # Midwest Tape + VENDOR_RB = 0; # Recorded Books + VENDOR_ULS = 0; # ULS + + IF VENDOR_SAN == '1556150'; VENDOR_BT = 1; + ELSIF VENDOR_SAN == '1697684'; VENDOR_BRODART = 1; + ELSIF VENDOR_SAN == '1697978'; VENDOR_INGRAM = 1; + ELSIF VENDOR_SAN == '2549913'; VENDOR_MW_TAPE = 1; + ELSIF VENDOR_SAN == '1113984'; VENDOR_RB = 1; + ELSIF VENDOR_SAN == '1699342'; VENDOR_ULS = 1; + END; + + # if true, pass the PO name as a secondary identifier + # RFF+LI:/li_id + INC_PO_NAME = 0; + IF VENDOR_INGRAM; + INC_PO_NAME = 1; + END; + + # GIR configuration -------------------------------------- + + INC_COPIES = 1; # copies on/off switch + INC_FUND = 0; + INC_CALLNUMBER = 0; + INC_ITEM_TYPE = 1; + INC_LOCATION = 0; + INC_COLLECTION_CODE = 1; + INC_OWNING_LIB = 1; + INC_QUANTITY = 1; + INC_COPY_ID = 0; + + IF VENDOR_BT; + INC_CALLNUMBER = 1; + END; + + IF VENDOR_BRODART; + INC_FUND = 1; + END; + + IF VENDOR_MW_TAPE; + INC_FUND = 1; + INC_COLLECTION_CODE = 0; + INC_ITEM_TYPE = 0; + END; + + # END GIR configuration --------------------------------- + +-%] +[%- BLOCK big_block -%] +{ + "recipient":"[% VENDOR_SAN %]", + "sender":"[% ORG_UNIT_SAN %]", + "body": [{ + "ORDERS":[ "order", { + + "po_number":[% target.id %], + + [% IF INC_PO_NAME %] + "po_name":"[% target.name | replace('\/', ' ') | replace('"', '\"') %]", + [% END %] + + "date":"[% date.format(date.now, '%Y%m%d') %]", + + "buyer":[ + [% IF VENDOR_BT %] + {"id-qualifier": 91, "id":"[% ORG_UNIT_SAN %] [% VENDCODE %]"} + [% ELSE %] + {"id":"[% ORG_UNIT_SAN %]"}, + {"id-qualifier": 91, "id":"[% VENDACCT %]"} + [% END %] + ], + + "vendor":[ + "[% VENDOR_SAN %]", + {"id-qualifier": 92, "id":"[% target.provider.id %]"} + ], + + "currency":"[% target.provider.currency_type %]", + + "items":[ + [%- FOR li IN target.lineitems %] + { + "line_index":"[% li.id %]", + "identifiers":[ + [%- + idval = ''; + idqual = 'EN'; # default ISBN/UPC/EAN-13 + ident_attr = helpers.get_li_order_ident(li.attributes); + IF ident_attr; + idname = ident_attr.attr_name; + idval = ident_attr.attr_value; + IF idname == 'isbn' AND idval.length != 13; + idqual = 'IB'; + ELSIF idname == 'issn'; + idqual = 'IS'; + END; + ELSE; + idqual = 'IN'; + idval = li.id; + END -%] + {"id-qualifier":"[% idqual %]","id":"[% idval %]"} + ], + "price":[% li.estimated_unit_price || '0.00' %], + "desc":[ + {"BTI":"[% helpers.get_li_attr_jedi('title', '', li.attributes) %]"}, + {"BPU":"[% helpers.get_li_attr_jedi('publisher', '', li.attributes) %]"}, + {"BPD":"[% helpers.get_li_attr_jedi('pubdate', '', li.attributes) %]"}, + [% IF VENDOR_ULS -%] + {"BEN":"[% helpers.get_li_attr_jedi('edition', '', li.attributes) %]"}, + {"BAU":"[% helpers.get_li_attr_jedi('author', '', li.attributes) %]"} + [%- ELSE -%] + {"BPH":"[% helpers.get_li_attr_jedi('pagination','', li.attributes) %]"} + [%- END %] + ], + [%- ftx_vals = []; + FOR note IN li.lineitem_notes; + NEXT UNLESS note.vendor_public == 't'; + ftx_vals.push(note.value); + END; + IF VENDOR_BRODART; # look for copy-level spec code + FOR lid IN li.lineitem_details; + IF lid.note; + spec_note = lid.note.match('spec code ([a-zA-Z0-9_])'); + IF spec_note.0; ftx_vals.push(spec_note.0); END; + END; + END; + END; + IF xtra_ftx; ftx_vals.unshift(xtra_ftx); END; + + # BT & ULS want FTX+LIN for every LI, even if empty + IF ((VENDOR_BT OR VENDOR_ULS) AND ftx_vals.size == 0); + ftx_vals.unshift(''); + END; + -%] + + "free-text":[ + [% FOR note IN ftx_vals -%] "[% note %]"[% UNLESS loop.last %], [% END %][% END %] + ], + + "quantity":[% li.lineitem_details.size %], + + [%- IF INC_COPIES -%] + "copies" : [ + [%- compressed_copies = []; + FOR lid IN li.lineitem_details; + fund = lid.fund.code; + item_type = lid.circ_modifier; + callnumber = lid.cn_label; + owning_lib = lid.owning_lib.shortname; + location = lid.location; + collection_code = lid.collection_code; + + # when we have real copy data, treat it as authoritative for some fields + acp = lid.eg_copy_id; + IF acp; + item_type = acp.circ_modifier; + callnumber = acp.call_number.label; + location = acp.location.name; + END ; + + + # collapse like copies into groups w/ quantity + + found_match = 0; + IF !INC_COPY_ID; # INC_COPY_ID implies 1 copy per GIR + FOR copy IN compressed_copies; + IF (fund == copy.fund OR (!fund AND !copy.fund)) AND + (item_type == copy.item_type OR (!item_type AND !copy.item_type)) AND + (callnumber == copy.callnumber OR (!callnumber AND !copy.callnumber)) AND + (owning_lib == copy.owning_lib OR (!owning_lib AND !copy.owning_lib)) AND + (location == copy.location OR (!location AND !copy.location)) AND + (collection_code == copy.collection_code OR (!collection_code AND !copy.collection_code)); + + copy.quantity = copy.quantity + 1; + found_match = 1; + END; + END; + END; + + IF !found_match; + compressed_copies.push({ + fund => fund, + item_type => item_type, + callnumber => callnumber, + owning_lib => owning_lib, + location => location, + collection_code => collection_code, + copy_id => lid.id, # for INC_COPY_ID + quantity => 1 + }); + END; + END; + FOR copy IN compressed_copies; + + # If we assume owning_lib is required and set, + # it is safe to prepend each following copy field w/ a "," + + # B&T EDI requires expected GIR fields to be + # present regardless of whether a value exists. + # some fields are required to have a value in ACQ, + # though, so they are not forced into place below. + + %]{[%- IF INC_OWNING_LIB AND copy.owning_lib %] "owning_lib":"[% copy.owning_lib %]"[% END -%] + [%- IF INC_FUND AND copy.fund %],"fund":"[% copy.fund %]"[% END -%] + [%- IF INC_CALLNUMBER AND (VENDOR_BT OR copy.callnumber) %],"call_number":"[% copy.callnumber %]"[% END -%] + [%- IF INC_ITEM_TYPE AND (VENDOR_BT OR copy.item_type) %],"item_type":"[% copy.item_type %]"[% END -%] + [%- IF INC_LOCATION AND copy.location %],"copy_location":"[% copy.location %]"[% END -%] + [%- IF INC_COLLECTION_CODE AND (VENDOR_BT OR copy.collection_code) %],"collection_code":"[% copy.collection_code %]"[% END -%] + [%- IF INC_QUANTITY %],"quantity":"[% copy.quantity %]"[% END -%] + [%- IF INC_COPY_ID %],"copy_id":"[% copy.copy_id %]" [% END %]}[% ',' UNLESS loop.last -%] + [%- END -%] [%# FOR compressed_copies -%] + ] + [%- END -%] [%# IF INC_COPIES %] + + }[% UNLESS loop.last %],[% END -%] + + [% END %] [%# END lineitems %] + ], + "line_items":[% target.lineitems.size %] + }] [%# close ORDERS array %] + }] [%# close body array %] +} +[% END %] +[% tempo = PROCESS big_block; helpers.escape_json(tempo) %] +$$ +WHERE ID = 23 AND FALSE; -- remove 'AND FALSE' to apply this update + + +-- lineitem worksheet +UPDATE action_trigger.event_definition SET template = +$$ +[%- USE date -%] +[%- + # find a lineitem attribute by name and optional type + BLOCK get_li_attr; + FOR attr IN li.attributes; + IF attr.attr_name == attr_name; + IF !attr_type OR attr_type == attr.attr_type; + attr.attr_value; + LAST; + END; + END; + END; + END +-%] + +

Purchase Order [% target.id %]

+
+date [% date.format(date.now, '%Y%m%d') %] +
+ + + + + + + + + +
Vendor +
[% target.provider.name %]
+
[% target.provider.addresses.0.street1 %]
+
[% target.provider.addresses.0.street2 %]
+
[% target.provider.addresses.0.city %]
+
[% target.provider.addresses.0.state %]
+
[% target.provider.addresses.0.country %]
+
[% target.provider.addresses.0.post_code %]
+
Ship to / Bill to +
[% target.ordering_agency.name %]
+
[% target.ordering_agency.billing_address.street1 %]
+
[% target.ordering_agency.billing_address.street2 %]
+
[% target.ordering_agency.billing_address.city %]
+
[% target.ordering_agency.billing_address.state %]
+
[% target.ordering_agency.billing_address.country %]
+
[% target.ordering_agency.billing_address.post_code %]
+
+ +

+
+ Notes to the Vendor +
    + [% FOR note IN target.notes %] + [% IF note.vendor_public == 't' %] +
  • [% note.value %]
  • + [% END %] + [% END %] +
+
+

+ + + + + + + + + + + + + + + + [% subtotal = 0 %] + [% FOR li IN target.lineitems %] + + + [% count = li.lineitem_details.size %] + [% price = li.estimated_unit_price %] + [% litotal = (price * count) %] + [% subtotal = subtotal + litotal %] + [% + ident_attr = helpers.get_li_order_ident(li.attributes); + SET ident_value = ident_attr.attr_value IF ident_attr; + %] + + + + + + + + + [% END %] + + + + + +
PO#ISBN or Item #TitleQuantityUnit PriceLine TotalNotes
[% target.id %][% ident_value %][% PROCESS get_li_attr attr_name = 'title' %][% count %][% price %][% litotal %] +
    + [% FOR note IN li.lineitem_notes %] + [% IF note.vendor_public == 't' %] +
  • [% note.value %]
  • + [% END %] + [% END %] +
+
+ Subtotal[% subtotal %]
+ +
+ +Total Line Item Count: [% target.lineitems.size %] +$$ +WHERE ID = 4; -- PO HTML + +COMMIT; -- 2.43.2