[open-ils-commits] [GIT] Evergreen ILS branch master updated. b11da34c3d94b25f2a3c5877849e9484d48c6a84

Evergreen Git git at git.evergreen-ils.org
Wed Mar 13 15:12:39 EDT 2013


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "Evergreen ILS".

The branch, master has been updated
       via  b11da34c3d94b25f2a3c5877849e9484d48c6a84 (commit)
       via  f1d923a0a492dace4b134ab5529af6bd19e27d0a (commit)
       via  51c1ea70a1b4ffb9ae6253c466c7d6ec792ac8d6 (commit)
       via  80f7e135f610d4bad2df72c25d785e3d364b7fe8 (commit)
       via  0008979452c1f80ff31bb286043423270e9a8030 (commit)
       via  78c557188c0da4dd93be367d95c4f02d36a68394 (commit)
       via  4ea5b3860bcc8cca6c477f42d8634760500de63d (commit)
      from  aabc8ae3040d1b70369c865597c6f473afd70450 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit b11da34c3d94b25f2a3c5877849e9484d48c6a84
Author: Lebbeous Fogle-Weekley <lebbeous at esilibrary.com>
Date:   Wed Mar 13 15:09:31 2013 -0400

    Upgrade script numbering for acq order indentifier selector
    
    Signed-off-by: Lebbeous Fogle-Weekley <lebbeous at esilibrary.com>

diff --git a/Open-ILS/src/sql/Pg/002.schema.config.sql b/Open-ILS/src/sql/Pg/002.schema.config.sql
index a11edd5..649d665 100644
--- a/Open-ILS/src/sql/Pg/002.schema.config.sql
+++ b/Open-ILS/src/sql/Pg/002.schema.config.sql
@@ -91,7 +91,7 @@ CREATE TRIGGER no_overlapping_deps
     BEFORE INSERT OR UPDATE ON config.db_patch_dependencies
     FOR EACH ROW EXECUTE PROCEDURE evergreen.array_overlap_check ('deprecates');
 
-INSERT INTO config.upgrade_log (version, applied_to) VALUES ('0775', :eg_version); -- berick/miker
+INSERT INTO config.upgrade_log (version, applied_to) VALUES ('0778', :eg_version); -- berick/senator
 
 CREATE TABLE config.bib_source (
 	id		SERIAL	PRIMARY KEY,
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.acq-order-ident.sql b/Open-ILS/src/sql/Pg/upgrade/0776.schema.acq-order-ident.sql
similarity index 91%
rename from Open-ILS/src/sql/Pg/upgrade/XXXX.schema.acq-order-ident.sql
rename to Open-ILS/src/sql/Pg/upgrade/0776.schema.acq-order-ident.sql
index a0b09ee..2185e55 100644
--- a/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.acq-order-ident.sql
+++ b/Open-ILS/src/sql/Pg/upgrade/0776.schema.acq-order-ident.sql
@@ -1,6 +1,7 @@
-
 BEGIN;
 
+SELECT evergreen.upgrade_deps_block_check('0776', :eg_version);
+
 ALTER TABLE acq.lineitem_attr
     ADD COLUMN order_ident BOOLEAN NOT NULL DEFAULT FALSE;
 
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.data.acq-order-ident.sql b/Open-ILS/src/sql/Pg/upgrade/0777.data.acq-order-ident.sql
similarity index 99%
rename from Open-ILS/src/sql/Pg/upgrade/XXXX.data.acq-order-ident.sql
rename to Open-ILS/src/sql/Pg/upgrade/0777.data.acq-order-ident.sql
index c5d467b..4146b16 100644
--- a/Open-ILS/src/sql/Pg/upgrade/XXXX.data.acq-order-ident.sql
+++ b/Open-ILS/src/sql/Pg/upgrade/0777.data.acq-order-ident.sql
@@ -1,6 +1,7 @@
-
 BEGIN;
 
+SELECT evergreen.upgrade_deps_block_check('0777', :eg_version);
+
 -- Listed here for reference / ease of access.  The update
 -- is not applied here (see the WHERE clause).
 UPDATE action_trigger.event_definition SET template = 
diff --git a/Open-ILS/src/sql/Pg/upgrade/YYYY.schema.acq-multi-attrs.sql b/Open-ILS/src/sql/Pg/upgrade/0778.schema.acq-multi-attrs.sql
similarity index 98%
rename from Open-ILS/src/sql/Pg/upgrade/YYYY.schema.acq-multi-attrs.sql
rename to Open-ILS/src/sql/Pg/upgrade/0778.schema.acq-multi-attrs.sql
index cbe837f..1cd2880 100644
--- a/Open-ILS/src/sql/Pg/upgrade/YYYY.schema.acq-multi-attrs.sql
+++ b/Open-ILS/src/sql/Pg/upgrade/0778.schema.acq-multi-attrs.sql
@@ -1,6 +1,7 @@
-
 BEGIN;
 
+SELECT evergreen.upgrade_deps_block_check('0778', :eg_version);
+
 CREATE OR REPLACE FUNCTION extract_marc_field_set
         (TEXT, BIGINT, TEXT, TEXT) RETURNS SETOF TEXT AS $$
 DECLARE

commit f1d923a0a492dace4b134ab5529af6bd19e27d0a
Author: Bill Erickson <berick at esilibrary.com>
Date:   Wed Jan 23 15:10:42 2013 -0500

    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 <berick at esilibrary.com>
    Signed-off-by: Lebbeous Fogle-Weekley <lebbeous at esilibrary.com>

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 7be1993..093e551 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 e145206..b346956 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 <b>[% date.format(date.now, '%Y%m%d') %]</b>
     [% 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;
+    %]
     <td>[% target.id %]</td>
-    <td>[% isbn || ident %]</td>
+    <td>[% ident_value %]</td>
     <td>[% PROCESS get_li_attr attr_name = 'title' %]</td>
     <td>[% count %]</td>
     <td>[% price %]</td>
@@ -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 0000000..c5d467b
--- /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:<name>/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
+-%]
+
+<h2>Purchase Order [% target.id %]</h2>
+<br/>
+date <b>[% date.format(date.now, '%Y%m%d') %]</b>
+<br/>
+
+<style>
+    table td { padding:5px; border:1px solid #aaa;}
+    table { width:95%; border-collapse:collapse; }
+    #vendor-notes { padding:5px; border:1px solid #aaa; }
+</style>
+<table id='vendor-table'>
+  <tr>
+    <td valign='top'>Vendor</td>
+    <td>
+      <div>[% target.provider.name %]</div>
+      <div>[% target.provider.addresses.0.street1 %]</div>
+      <div>[% target.provider.addresses.0.street2 %]</div>
+      <div>[% target.provider.addresses.0.city %]</div>
+      <div>[% target.provider.addresses.0.state %]</div>
+      <div>[% target.provider.addresses.0.country %]</div>
+      <div>[% target.provider.addresses.0.post_code %]</div>
+    </td>
+    <td valign='top'>Ship to / Bill to</td>
+    <td>
+      <div>[% target.ordering_agency.name %]</div>
+      <div>[% target.ordering_agency.billing_address.street1 %]</div>
+      <div>[% target.ordering_agency.billing_address.street2 %]</div>
+      <div>[% target.ordering_agency.billing_address.city %]</div>
+      <div>[% target.ordering_agency.billing_address.state %]</div>
+      <div>[% target.ordering_agency.billing_address.country %]</div>
+      <div>[% target.ordering_agency.billing_address.post_code %]</div>
+    </td>
+  </tr>
+</table>
+
+<br/><br/>
+<fieldset id='vendor-notes'>
+    <legend>Notes to the Vendor</legend>
+    <ul>
+    [% FOR note IN target.notes %]
+        [% IF note.vendor_public == 't' %]
+            <li>[% note.value %]</li>
+        [% END %]
+    [% END %]
+    </ul>
+</fieldset>
+<br/><br/>
+
+<table>
+  <thead>
+    <tr>
+      <th>PO#</th>
+      <th>ISBN or Item #</th>
+      <th>Title</th>
+      <th>Quantity</th>
+      <th>Unit Price</th>
+      <th>Line Total</th>
+      <th>Notes</th>
+    </tr>
+  </thead>
+  <tbody>
+
+  [% subtotal = 0 %]
+  [% FOR li IN target.lineitems %]
+
+  <tr>
+    [% 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;
+    %]
+    <td>[% target.id %]</td>
+    <td>[% ident_value %]</td>
+    <td>[% PROCESS get_li_attr attr_name = 'title' %]</td>
+    <td>[% count %]</td>
+    <td>[% price %]</td>
+    <td>[% litotal %]</td>
+    <td>
+        <ul>
+        [% FOR note IN li.lineitem_notes %]
+            [% IF note.vendor_public == 't' %]
+                <li>[% note.value %]</li>
+            [% END %]
+        [% END %]
+        </ul>
+    </td>
+  </tr>
+  [% END %]
+  <tr>
+    <td/><td/><td/><td/>
+    <td>Subtotal</td>
+    <td>[% subtotal %]</td>
+  </tr>
+  </tbody>
+</table>
+
+<br/>
+
+Total Line Item Count: [% target.lineitems.size %]
+$$
+WHERE ID = 4; -- PO HTML
+
+COMMIT;

commit 51c1ea70a1b4ffb9ae6253c466c7d6ec792ac8d6
Author: Bill Erickson <berick at esilibrary.com>
Date:   Mon Mar 11 17:01:31 2013 -0400

    ACQ order identifier release notes
    
    Signed-off-by: Bill Erickson <berick at esilibrary.com>
    Signed-off-by: Lebbeous Fogle-Weekley <lebbeous at esilibrary.com>

diff --git a/docs/RELEASE_NOTES_NEXT/acq_lineitem_order_ident.txt b/docs/RELEASE_NOTES_NEXT/acq_lineitem_order_ident.txt
new file mode 100644
index 0000000..9315a3b
--- /dev/null
+++ b/docs/RELEASE_NOTES_NEXT/acq_lineitem_order_ident.txt
@@ -0,0 +1,22 @@
+Acquisitions Lineitem Order Identifiers
+=======================================
+
+Staff now have the ability to specify the identifier value to use for
+lineitems when communicating order information to vendors.  This is 
+particularly important when a lineitem has, for example, multiple ISBNs.
+Determining which ISBN is to act as the "order identifier" allows staff
+to provide the most accurate order information to vendors.  
+
+Supported identifier types include ISBN, ISSN, and UPC.  Order identifier 
+values are relayed to vendors via EDI and print PO.
+
+Upgrade Notes
+=============
+
+Two new permissions are added for this feature:
+
+ * ACQ_SET_LINEITEM_IDENTIFIER
+  ** Allows staff to apply order identifiers to lineitems
+ * ACQ_ADD_LINEITEM_IDENTIFIER
+  ** Implies that new identifiers shall be added to linked bib records, 
+     when a linkage exists.

commit 80f7e135f610d4bad2df72c25d785e3d364b7fe8
Author: Bill Erickson <berick at esilibrary.com>
Date:   Fri Jan 18 12:27:28 2013 -0500

    ACQ order identifier UI
    
    In the lineitem table show the set of possible order identifier types
    and values.  The user may enter new values when necessary.  When changes
    are applied, the selected order identifier is applied to each lineitem.
    
    Signed-off-by: Bill Erickson <berick at esilibrary.com>
    Signed-off-by: Lebbeous Fogle-Weekley <lebbeous at esilibrary.com>

diff --git a/Open-ILS/src/templates/acq/common/li_table.tt2 b/Open-ILS/src/templates/acq/common/li_table.tt2
index 83881d0..718de18 100644
--- a/Open-ILS/src/templates/acq/common/li_table.tt2
+++ b/Open-ILS/src/templates/acq/common/li_table.tt2
@@ -65,7 +65,15 @@
             <tbody style='font-weight:bold;border:1px solid #aaa;'>
                 <tr>
                     <td><span><a id='acq-lit-select-toggle' href='javascript:void(0);'>&#x2713</a></span></td>
-                    <td>[% l('Line Items') %]</td>
+                    <td>
+                        [% l('Line Items') %]
+                        <span style='padding-left: 10px;'>
+                            <a href='javascript:;' 
+                                id='acq-lit-apply-idents'>
+                                [% l('Apply Order Identifiers') %]
+                            </a>
+                        </span>
+                    </td>
                     <td>[% l('Items') %]</td>
                     <td>[% l('Notes') %]</td>
                     <td>[% l('Actions') %]</td>
@@ -102,6 +110,21 @@
                                 </tr>
                                 <tr>
                                     <td colspan='0'>
+                                        <table><tr>
+                                            <td>[% l('Order Identifier') %]</td>
+                                            <td>
+                                                <select name='order_ident_type'>
+                                                    <option value='isbn' selected='selected'>[% l('ISBN') %]</option>
+                                                    <option value='upc' >[% l('UPC') %]</option>
+                                                    <option value='issn'>[% l('ISSN') %]</option>
+                                                </select>
+                                            </td>
+                                            <td name='order_ident_value'></td>
+                                        </tr></table>
+                                    </td>
+                                </tr>
+                                <tr>
+                                    <td colspan='0'>
                                         <span name="liid"># </span> 
                                         <span name="catalog" class='hidden'> | <a title='[% l('Show In Catalog') %]' name="catalog_link" href="javascript:void(0);">[% l('&#x279F; catalog') %]</a></span> 
                                         <span name="link_to_catalog" class='hidden'> | <a title='[% l('Link To Catalog Record') %]' name="link_to_catalog_link" href="javascript:void(0);">[% l('&#x27BE; link to catalog') %]</a></span> 
diff --git a/Open-ILS/web/js/ui/default/acq/common/li_table.js b/Open-ILS/web/js/ui/default/acq/common/li_table.js
index 5f5df76..0114f87 100644
--- a/Open-ILS/web/js/ui/default/acq/common/li_table.js
+++ b/Open-ILS/web/js/ui/default/acq/common/li_table.js
@@ -14,6 +14,7 @@ dojo.require('dojo.data.ItemFileReadStore');
 dojo.require('openils.widget.ProgressDialog');
 dojo.require('openils.PermaCrud');
 dojo.require("openils.widget.PCrudAutocompleteBox");
+dojo.require('dijit.form.ComboBox');
 dojo.require('openils.CGI');
 
 if (!localeStrings) {   /* we can do this because javascript doesn't have block scope */
@@ -82,6 +83,12 @@ function AcqLiTable() {
     );
     this.vlAgent = new VLAgent();
 
+    if (dojo.byId('acq-lit-apply-idents')) {
+        dojo.byId('acq-lit-apply-idents').onclick = function() {
+            self.applyOrderIdentValues();
+        };
+    }
+
     this.focusLineitem = new openils.CGI().param('focus_li');
 
     dojo.byId("acq-lit-li-actions-selector").onchange = function() { 
@@ -434,6 +441,8 @@ function AcqLiTable() {
         dojo.query('[name=copieslink]', row)[0].onclick = function() {self.drawCopies(li.id())};
         dojo.query('[name=noteslink]', row)[0].onclick = function() {self.drawLiNotes(li)};
 
+        this.drawOrderIdentSelector(li, row);
+
         if (!this.skipInitialEligibilityCheck)
             this.fetchClaimInfo(
                 li.id(),
@@ -571,6 +580,217 @@ function AcqLiTable() {
         }
     };
 
+    this.applyOrderIdentValues = function() {
+        this._identValuesInFlight = 
+            openils.Util.objectProperties(this.liCache).length;
+        for (var liId in this.liCache) {
+            this._applyOrderIdentValue(this.liCache[liId]);
+        }
+    };
+
+    // returns true if request was sent
+    this._applyOrderIdentValue = function(li) {
+        var self = this;
+
+        console.log('applying ident value for lineitem ' + li.id());
+
+        // main row
+        var row = dojo.query('[li=' + li.id() + ']')[0];
+
+        // find the selected ident value
+        var typeSel = dojo.query('[name=order_ident_type]', row)[0];
+        var valueSel = dojo.query('[name=order_ident_value]', row)[0];
+        var name = typeSel.options[typeSel.selectedIndex].value;
+        var val = typeSel._cbox.attr('value');
+
+        console.log("selected ident is " + val);
+
+        // it differs from the existing ident value, update it
+        var oldIdent = self.getLiOrderIdent(li);
+        if (oldIdent && 
+            oldIdent.attr_name() == name &&
+            oldIdent.attr_value() == val) {
+                console.log('selected ident attr matches existing attr');
+                if (--this._identValuesInFlight == 0) 
+                    location.href = location.href;
+                return false;
+        }
+
+        // see if the selected ident value is represented
+        // by an existing lineitem attr
+
+        var args = {};
+        typeSel._cbox.store.fetch({
+            query : {attr_value : val},
+            onItem : function(item) {                                                       
+                console.log('found existing attr for ident value');
+                args.source_attr_id = li.attributes().filter(
+                    function(attr) { return attr.id() == item.id[0] }
+                )[0];
+            }                                                                      
+        }); 
+
+
+        if (!args.source_attr_id) {
+            // user entered new text in the combobox
+            // so we need to create a new attr
+            console.log('creating new ident attr');
+            args.lineitem_id = li.id();
+            args.attr_name = name;
+            args.attr_value = val;
+        }
+
+        fieldmapper.standardRequest(
+            ['open-ils.acq', 'open-ils.acq.lineitem.order_identifier.set'],
+            {   async : true,
+                params : [openils.User.authtoken, args],
+                oncomplete : function() {
+                    console.log('order_ident oncomplete');
+                    if (--self._identValuesInFlight == 0) 
+                        location.href = location.href;
+                }
+            }
+        );
+
+        return true;
+    };
+
+    this.getLiOrderIdent = function(li) {
+        var attrs = li.attributes();
+        if (!attrs) return null;
+        return attrs.filter(
+            function(attr) {
+                return (
+                    attr.attr_type() == 'lineitem_local_attr_definition' &&
+                    openils.Util.isTrue(attr.order_ident())
+                );
+            }
+        )[0];
+    };
+
+    this.drawOrderIdentSelector = function(li, row) {
+        var self = this;
+        var typeSel = dojo.query('[name=order_ident_type]', row)[0];
+        var valueSel = dojo.query('[name=order_ident_value]', row)[0];
+
+        var attrs = li.attributes();
+
+        // limit to MARC attr defs
+        attrs = attrs.filter(
+            function(attr) {
+                return (attr.attr_type() == 'lineitem_marc_attr_definition');
+            }
+        );
+
+        var identAttr = this.getLiOrderIdent(li);
+
+
+        // collect the values for each type of identifier
+        // find a reasonable default identifier type to render
+        
+        var values = {};
+        var typeSet = null;
+        dojo.forEach(['isbn', 'upc', 'issn'],
+            function(name) {
+
+                // collect the values for this attr name
+                values[name] =  attrs.filter(
+                    function(attr) {
+                        return (attr.attr_name() == name)
+                    }
+                );
+
+                // select a reasonable default name in the type-selector
+                if (!typeSet) {
+                    var useMe = false;
+                    if (identAttr) {
+                        if (identAttr.attr_name() == name)
+                            useMe = true;
+                    } else if (values[name].length) {
+                        useMe = true;
+                    }
+
+                    if (useMe) {
+                        dojo.forEach(typeSel.options, function(opt) {
+                            if (opt.value == name) {
+                                opt.selected = true;
+                                typeSet = name;
+                            }
+                        });
+                    }
+                }
+            }
+        );
+
+        function updateOrderIdent(box) {
+            console.log('updating order ident for box ' + box);
+        }
+
+        // replace the ident combobox with a new 
+        // one for the selected ident type 
+        function changeComboBox(sel) {
+            var name = sel.options[sel.selectedIndex].value;
+
+            var td = dojo.query('[name=order_ident_value]', row)[0];
+            if (td.childNodes[0]) 
+                dojo.destroy(td.childNodes[0]);
+
+            var store = new dojo.data.ItemFileWriteStore({
+                data : acqlia.toStoreData(values[name])
+            });
+
+            var cbox = new dijit.form.ComboBox(
+                {   store : store,
+                    labelAttr : 'attr_value',
+                    searchAttr : 'attr_value'
+                }, 
+                dojo.create('div', {}, td)
+            );
+
+            cbox.startup();
+
+            // set the value for the cbox
+            if (values[name].length) {
+                var orderIdent = self.getLiOrderIdent(li);
+
+                if (orderIdent && orderIdent.attr_name() == name) {
+                    cbox.attr('value', orderIdent.attr_value());
+                } else  {
+                    cbox.attr('value', values[name][0].attr_value());
+                }
+            }
+
+            if (!self.orderIdentAllowed) 
+                cbox.attr('disabled', true);
+
+            sel._cbox = cbox;
+            cbox._lineitem = li;
+            dojo.connect(cbox, 'onChange', updateOrderIdent);
+        }
+
+        changeComboBox(typeSel); // force the initial draw
+        typeSel.onchange = function() {changeComboBox(typeSel)};
+    };
+
+    this.testOrderIdentPerms = function(org, callback) {
+        var self = this;
+        new openils.User().getPermOrgList(
+            'ACQ_SET_LINEITEM_IDENTIFIER',
+            function(orgs) { 
+                console.log('found orgs = ' + orgs);
+                for (var i = 0; i < orgs.length; i++) {
+                    if (Number(orgs[i]) == Number(org)) {
+                        self.orderIdentAllowed = true;
+                        if (callback) callback();
+                        return;
+                    }
+                }
+                if (callback) callback();
+            }, 
+            true, true
+        );
+    };
+
     this.checkClaimEligibility = function(li, callback, row) {
         /* Assume always eligible, i.e. from this interface we don't care about
          * claim eligibility any more. this is where the user would force a
diff --git a/Open-ILS/web/js/ui/default/acq/picklist/view.js b/Open-ILS/web/js/ui/default/acq/picklist/view.js
index f44f743..6fa02df 100644
--- a/Open-ILS/web/js/ui/default/acq/picklist/view.js
+++ b/Open-ILS/web/js/ui/default/acq/picklist/view.js
@@ -21,7 +21,10 @@ function load() {
                 {flesh_lineitem_count:true, flesh_owner:true}],
             oncomplete: function(r) {
                 plist = openils.Util.readResponse(r);
-                drawPl(plist);
+                liTable.testOrderIdentPerms(
+                    plist.org_unit(),
+                    function() { drawPl(plist) }
+                );
             }
         }
     );
@@ -49,7 +52,7 @@ function load() {
 }
 
 function drawPl() {
-
+    
     dojo.byId("oils-acq-picklist-name").innerHTML = plist.name();
     dojo.byId("oils-acq-picklist-attr-owner").innerHTML = plist.owner().usrname();
     dojo.byId("oils-acq-picklist-attr-count").innerHTML = plist.entry_count();
diff --git a/Open-ILS/web/js/ui/default/acq/po/view_po.js b/Open-ILS/web/js/ui/default/acq/po/view_po.js
index 490de97..2b9cc4b 100644
--- a/Open-ILS/web/js/ui/default/acq/po/view_po.js
+++ b/Open-ILS/web/js/ui/default/acq/po/view_po.js
@@ -412,11 +412,17 @@ function init() {
                 /* po item table */
                 poItemTable = new PoItemTable(PO, pcrud);
 
+                liTable.testOrderIdentPerms( PO.ordering_agency(), init2);
+
                 renderPo();
             }
         }
     );
 
+}
+
+function init2() {
+
     var totalEstimated = 0;
     var zeroLi = true;
     fieldmapper.standardRequest(

commit 0008979452c1f80ff31bb286043423270e9a8030
Author: Bill Erickson <berick at esilibrary.com>
Date:   Mon Jan 21 14:51:48 2013 -0500

    ACQ API for setting the lineitem order identifier
    
    API: open-ils.acq.lineitem.order_identifier.set
    
    Given an existing lineitem_attr (typically a marc_attr), this will
    create a matching local_attr to store the name and value and mark
    the attr as the order_ident.  Any existing local_attr marked as
    order_ident is removed.  When necessary, the value (isbn, issn, upc) is
    added to the record first to create the marc attribute.  If the lineitem
    is linked to a bib record and the user has the correct permissions, any
    new data will also be applied to the bib record.
    
    Signed-off-by: Bill Erickson <berick at esilibrary.com>
    Signed-off-by: Lebbeous Fogle-Weekley <lebbeous at esilibrary.com>

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Acq/Order.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Acq/Order.pm
index e47e398..a86d4e6 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Acq/Order.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Acq/Order.pm
@@ -3437,6 +3437,7 @@ sub po_note_CUD_batch {
 
 
 # retrieves a lineitem, fleshes its PO and PL, checks perms
+# returns ($li, $evt, $org)
 sub fetch_and_check_li {
     my $e = shift;
     my $li_id = shift;
@@ -3447,18 +3448,21 @@ sub fetch_and_check_li {
         {   flesh => 1,
             flesh_fields => {jub => ['purchase_order', 'picklist']}
         }
-    ]) or return $e->die_event;
+    ]) or return (undef, $e->die_event);
 
+    my $org;
     if(my $po = $li->purchase_order) {
+        $org = $po->ordering_agency;
         my $perms = ($perm_mode eq 'read') ? 'VIEW_PURCHASE_ORDER' : 'CREATE_PURCHASE_ORDER';
-        return ($li, $e->die_event) unless $e->allowed($perms, $po->ordering_agency);
+        return ($li, $e->die_event) unless $e->allowed($perms, $org);
 
     } elsif(my $pl = $li->picklist) {
+        $org = $pl->org_unit;
         my $perms = ($perm_mode eq 'read') ? 'VIEW_PICKLIST' : 'CREATE_PICKLIST';
-        return ($li, $e->die_event) unless $e->allowed($perms, $pl->org_unit);
+        return ($li, $e->die_event) unless $e->allowed($perms, $org);
     }
 
-    return ($li);
+    return ($li, undef, $org);
 }
 
 
@@ -3593,6 +3597,185 @@ sub po_lineitems_no_copies {
     return undef;
 }
 
+__PACKAGE__->register_method(
+    method => 'set_li_order_ident',
+    api_name => 'open-ils.acq.lineitem.order_identifier.set',
+    signature => {
+        desc => q/
+            Given an existing lineitem_attr (typically a marc_attr), this will
+            create a matching local_attr to store the name and value and mark
+            the attr as the order_ident.  Any existing local_attr marked as
+            order_ident is removed.
+        /,
+        params => [
+            {desc => 'Authentication token', type => 'string'},
+            {desc => q/Args object:
+                source_attr_id : ID of the existing lineitem_attr to use as
+                    order ident.
+                lineitem_id : lineitem id
+                attr_name : name ('isbn', etc.) of a new marc_attr to add to 
+                    the lineitem to use for the order ident
+                attr_value : value for the new marc_attr
+                no_apply_bre : if set, newly added attrs will not be applied 
+                    to the lineitems' linked bib record/,
+                type => 'object'}
+        ],
+        return => {desc => q/Returns the attribute 
+            responsible for tracking the order identifier/}
+    }
+);
+
+sub set_li_order_ident {
+    my ($self, $conn, $auth, $args) = @_;
+    $args ||= {};
+
+    my $source_attr;
+    my $source_attr_id = $args->{source_attr_id};
+
+    my $e = new_editor(authtoken => $auth, xact => 1);
+    return $e->die_event unless $e->checkauth;
+
+    # fetch attr, LI, and check update permissions
+
+    my $li_id = $args->{lineitem_id};
+
+    if ($source_attr_id) {
+        $source_attr = $e->retrieve_acq_lineitem_attr($source_attr_id)
+            or return $e->die_event;
+        $li_id = $source_attr->lineitem;
+    }
+
+    my ($li, $evt, $perm_org) = fetch_and_check_li($e, $li_id, 'write');
+    return $evt if $evt;
+
+    return $e->die_event unless 
+        $e->allowed('ACQ_SET_LINEITEM_IDENTIFIER', $perm_org);
+
+    # if needed, create a new marc attr for 
+    # the lineitem to represent the ident value
+
+    ($source_attr, $evt) = apply_new_li_ident_attr(
+        $e, $li, $perm_org, $args->{attr_name}, $args->{attr_value}) 
+        unless $source_attr;
+
+    return $evt if $evt;
+
+    # remove the existing order_ident attribute if present
+
+    my $old_attr = $e->search_acq_lineitem_attr({
+        attr_type => 'lineitem_local_attr_definition',
+        lineitem => $li->id,
+        order_ident => 't'
+    })->[0];
+
+    if ($old_attr) {
+
+        # if we already have an order_ident that matches the 
+        # source attr, there's nothing left to do.
+
+        if ($old_attr->attr_name eq $source_attr->attr_name and
+            $old_attr->attr_value eq $source_attr->attr_value) {
+
+            $e->rollback;
+            return $old_attr;
+
+        } else {
+            # remove the old order_ident attribute
+            $e->delete_acq_lineitem_attr($old_attr) or return $e->die_event;
+        }
+    }
+
+    # make sure we have a local_attr_def to match the source attr def
+
+    my $local_def = $e->search_acq_lineitem_local_attr_definition({
+        code => $source_attr->attr_name
+    })->[0];
+
+    if (!$local_def) {
+        my $source_def = 
+            $e->retrieve_acq_lineitem_attr_definition($source_attr->definition);
+        $local_def = Fieldmapper::acq::lineitem_local_attr_definition->new;
+        $local_def->code($source_def->code);
+        $local_def->description($source_def->description);
+        $local_def = $e->create_acq_lineitem_local_attr_definition($local_def)
+            or return $e->die_event;
+    }
+
+    # create the new order_ident local attr
+
+    my $new_attr = Fieldmapper::acq::lineitem_attr->new;
+    $new_attr->definition($local_def->id);
+    $new_attr->attr_type('lineitem_local_attr_definition');
+    $new_attr->lineitem($li->id);
+    $new_attr->attr_name($source_attr->attr_name);
+    $new_attr->attr_value($source_attr->attr_value);
+    $new_attr->order_ident('t');
+
+    $new_attr = $e->create_acq_lineitem_attr($new_attr) 
+        or return $e->die_event;
+    
+    $e->commit;
+    return $new_attr;
+}
+
+
+# Given an isbn, issn, or upc, add the value to the lineitem marc.
+# Upon update, the value will be auto-magically represented as
+# a lineitem marc attr.
+# If the li is linked to a bib record and the user has the correct
+# permissions, update the bib record to match.
+sub apply_new_li_ident_attr {
+    my ($e, $li, $perm_org, $attr_name, $attr_value) = @_;
+
+    my %tags = (
+        isbn => '020',
+        issn => '022',
+        upc  => '024'
+    );
+
+    my $marc_field = MARC::Field->new(
+        $tags{$attr_name}, '', '','a' => $attr_value);
+
+    my $li_rec = MARC::Record->new_from_xml($li->marc, 'UTF-8', 'USMARC');
+    $li_rec->insert_fields_ordered($marc_field);
+
+    $li->marc(clean_marc($li_rec));
+    $li->editor($e->requestor->id);
+    $li->edit_time('now');
+
+    $e->update_acq_lineitem($li) or return (undef, $e->die_event);
+
+    my $source_attr = $e->search_acq_lineitem_attr({
+        attr_name => $attr_name,
+        attr_value => $attr_value,
+        attr_type => 'lineitem_marc_attr_definition'
+    })->[0];
+
+    if (!$source_attr) {
+        $logger->error("ACQ lineitem update failed to produce a matching ".
+            " marc attribute for $attr_name => $attr_value");
+        return (undef, OpenILS::Event->new('INTERNAL_SERVER_ERROR'));
+    }
+
+    return ($source_attr) unless 
+        $li->eg_bib_id and
+        $e->allowed('ACQ_ADD_LINEITEM_IDENTIFIER', $perm_org);
+
+    # li is linked to a bib record and user has the update perms
+
+    my $bre = $e->retrieve_biblio_record_entry($li->eg_bib_id);
+    my $bre_marc = MARC::Record->new_from_xml($bre->marc, 'UTF-8', 'USMARC');
+    $bre_marc->insert_fields_ordered($marc_field);
+
+    $bre->marc(clean_marc($bre_marc));
+    $bre->editor($e->requestor->id);
+    $bre->edit_date('now');
+
+    $e->update_biblio_record_entry($bre) or return (undef, $e->die_event);
+
+    return ($source_attr);
+}
+
 
 1;
 

commit 78c557188c0da4dd93be367d95c4f02d36a68394
Author: Bill Erickson <berick at esilibrary.com>
Date:   Fri Jan 18 12:27:20 2013 -0500

    ACQ lineitem order identifier SQL/IDL
    
    New 'order_ident' column on acq.lineitem_attr to represent which
    attribute should be considered the order identifier for the item.
    
    New permission ACQ_SET_LINEITEM_IDENTIFIER allowing staff to apply
    lineitem identifiers.
    
    New permission ACQ_ADD_LINEITEM_IDENTIFIER is added for allowing staff
    to update linked bib records when a order identifier is added to a
    lineitem.
    
    Signed-off-by: Bill Erickson <berick at esilibrary.com>
    Signed-off-by: Lebbeous Fogle-Weekley <lebbeous at esilibrary.com>

diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml
index ce82dea..75f0e32 100644
--- a/Open-ILS/examples/fm_IDL.xml
+++ b/Open-ILS/examples/fm_IDL.xml
@@ -7907,6 +7907,7 @@ SELECT  usr,
 			<field reporter:label="Code" name="code" reporter:datatype="text"/>
 			<field reporter:label="Description" name="description" reporter:datatype="text"/>
 			<field reporter:label="Owning Library" name="owning_lib" reporter:datatype="link"/>
+			<field reporter:label="Order Identifier" name="order_ident" reporter:datatype="bool"/>
 		</fields>
 		<links>
 			<link field="owning_lib" reltype="has_a" key="id" map="" class="aou"/>
@@ -7949,6 +7950,7 @@ SELECT  usr,
 			<field reporter:label="Name" name="attr_name" reporter:datatype="text" />
 			<field reporter:label="Value" name="attr_value" reporter:datatype="text" />
 			<field reporter:label="Definition" name="definition" reporter:datatype="text" />
+			<field reporter:label="Order Identifier" name="order_ident" reporter:datatype="bool" />
 		</fields>
 		<links>
 			<link field="lineitem" reltype="has_a" key="id" map="" class="jub"/>
diff --git a/Open-ILS/src/sql/Pg/200.schema.acq.sql b/Open-ILS/src/sql/Pg/200.schema.acq.sql
index 21fc092..126c44e 100644
--- a/Open-ILS/src/sql/Pg/200.schema.acq.sql
+++ b/Open-ILS/src/sql/Pg/200.schema.acq.sql
@@ -584,7 +584,8 @@ CREATE TABLE acq.lineitem_attr (
 	lineitem	BIGINT		NOT NULL REFERENCES acq.lineitem (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
 	attr_type	TEXT		NOT NULL,
 	attr_name	TEXT		NOT NULL,
-	attr_value	TEXT		NOT NULL
+	attr_value	TEXT		NOT NULL,
+	order_ident	BOOLEAN		NOT NULL DEFAULT FALSE
 );
 
 CREATE INDEX li_attr_li_idx ON acq.lineitem_attr (lineitem);
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 d43f4b9..e145206 100644
--- a/Open-ILS/src/sql/Pg/950.data.seed-values.sql
+++ b/Open-ILS/src/sql/Pg/950.data.seed-values.sql
@@ -1584,7 +1584,11 @@ INSERT INTO permission.perm_list ( id, code, description ) VALUES
  ( 545, 'SAVED_FILTER_DIALOG_FILTERS', oils_i18n_gettext( 545,
     'Allows users to save and load sets of filters for filter dialogs, available in certain staff interfaces', 'ppl', 'description')),
  ( 546, 'ADMIN_HOLD_CAPTURE_SORT', oils_i18n_gettext( 546,
-        'Allows a user to make changes to best-hold selection sort order', 'ppl', 'description'))
+        '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,
+        'Allows staff to change the lineitem identifier', 'ppl', 'description'))
 ;
 
 
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.acq-order-ident.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.acq-order-ident.sql
new file mode 100644
index 0000000..a0b09ee
--- /dev/null
+++ b/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.acq-order-ident.sql
@@ -0,0 +1,29 @@
+
+BEGIN;
+
+ALTER TABLE acq.lineitem_attr
+    ADD COLUMN order_ident BOOLEAN NOT NULL DEFAULT FALSE;
+
+INSERT INTO permission.perm_list ( id, code, description ) VALUES (
+    547, -- VERIFY
+    'ACQ_ADD_LINEITEM_IDENTIFIER',
+    oils_i18n_gettext(
+        547,-- VERIFY
+        'When granted, newly added lineitem identifiers will propagate to linked bib records',
+        'ppl',
+        'description'
+    )
+);
+
+INSERT INTO permission.perm_list ( id, code, description ) VALUES (
+    548, -- VERIFY
+    'ACQ_SET_LINEITEM_IDENTIFIER',
+    oils_i18n_gettext(
+        548,-- VERIFY
+        'Allows staff to change the lineitem identifier',
+        'ppl',
+        'description'
+    )
+);
+
+COMMIT;

commit 4ea5b3860bcc8cca6c477f42d8634760500de63d
Author: Bill Erickson <berick at esilibrary.com>
Date:   Tue Jan 22 14:35:22 2013 -0500

    Extract multiple like values from lineitem
    
    Make it possible to extract multiple values with the same MARC tag
    during acquisitions lineitem ingest.  Prior to this, only one (e.g.)
    ISBN for each record would be extracted as a lineitem attribute.
    
    Signed-off-by: Bill Erickson <berick at esilibrary.com>
    Signed-off-by: Lebbeous Fogle-Weekley <lebbeous at esilibrary.com>

diff --git a/Open-ILS/src/sql/Pg/002.functions.config.sql b/Open-ILS/src/sql/Pg/002.functions.config.sql
index 87b6e28..47f97c3 100644
--- a/Open-ILS/src/sql/Pg/002.functions.config.sql
+++ b/Open-ILS/src/sql/Pg/002.functions.config.sql
@@ -346,6 +346,29 @@ BEGIN
 END;
 $$ LANGUAGE PLPGSQL IMMUTABLE;
 
+CREATE OR REPLACE FUNCTION extract_marc_field_set
+        (TEXT, BIGINT, TEXT, TEXT) RETURNS SETOF TEXT AS $$
+DECLARE
+    query TEXT;
+    output TEXT;
+BEGIN
+    FOR output IN
+        SELECT x.t FROM (
+            SELECT id,t
+                FROM  oils_xpath_table(
+                    'id', 'marc', $1, $3, 'id = ' || $2)
+                AS t(id int, t text))x
+        LOOP
+        IF $4 IS NOT NULL THEN
+            SELECT INTO output (SELECT regexp_replace(output, $4, '', 'g'));
+        END IF;
+        RETURN NEXT output;
+    END LOOP;
+    RETURN;
+END;
+$$ LANGUAGE PLPGSQL IMMUTABLE;
+
+
 CREATE OR REPLACE FUNCTION extract_marc_field ( TEXT, BIGINT, TEXT ) RETURNS TEXT AS $$
     SELECT extract_marc_field($1,$2,$3,'');
 $$ LANGUAGE SQL IMMUTABLE;
diff --git a/Open-ILS/src/sql/Pg/200.schema.acq.sql b/Open-ILS/src/sql/Pg/200.schema.acq.sql
index 89c573a..21fc092 100644
--- a/Open-ILS/src/sql/Pg/200.schema.acq.sql
+++ b/Open-ILS/src/sql/Pg/200.schema.acq.sql
@@ -1019,6 +1019,11 @@ CREATE OR REPLACE FUNCTION public.extract_acq_marc_field ( BIGINT, TEXT, TEXT) R
 	SELECT extract_marc_field('acq.lineitem', $1, $2, $3);
 $$ LANGUAGE SQL;
 
+CREATE OR REPLACE FUNCTION public.extract_acq_marc_field_set ( BIGINT, TEXT, TEXT) RETURNS SETOF TEXT AS $$
+	SELECT extract_marc_field_set('acq.lineitem', $1, $2, $3);
+$$ LANGUAGE SQL;
+
+
 /*
 CREATE OR REPLACE FUNCTION public.extract_bib_marc_field ( BIGINT, TEXT ) RETURNS TEXT AS $$
 	SELECT public.extract_marc_field('biblio.record_entry', $1, $2);
@@ -1080,19 +1085,26 @@ BEGIN
                 END IF;
             ELSE
                 pos := 1;
-
                 LOOP
-    			    SELECT extract_acq_marc_field(id, xpath_string || '[' || pos || ']', adef.remove) INTO value FROM acq.lineitem WHERE id = NEW.id;
-
-    			    IF (value IS NOT NULL AND value <> '') THEN
-	    			    INSERT INTO acq.lineitem_attr (lineitem, definition, attr_type, attr_name, attr_value)
-		    			    VALUES (NEW.id, adef.id, atype, adef.code, value);
-                    ELSE
+                    -- each application of the regex may produce multiple values
+                    FOR value IN
+                        SELECT * FROM extract_acq_marc_field_set(
+                            NEW.id, xpath_string || '[' || pos || ']', adef.remove)
+                        LOOP
+
+                        IF (value IS NOT NULL AND value <> '') THEN
+                            INSERT INTO acq.lineitem_attr
+                                (lineitem, definition, attr_type, attr_name, attr_value)
+                                VALUES (NEW.id, adef.id, atype, adef.code, value);
+                        ELSE
+                            EXIT;
+                        END IF;
+                    END LOOP;
+                    IF NOT FOUND THEN
                         EXIT;
-			        END IF;
-
+                    END IF;
                     pos := pos + 1;
-                END LOOP;
+               END LOOP;
             END IF;
 
 		END IF;
diff --git a/Open-ILS/src/sql/Pg/upgrade/YYYY.schema.acq-multi-attrs.sql b/Open-ILS/src/sql/Pg/upgrade/YYYY.schema.acq-multi-attrs.sql
new file mode 100644
index 0000000..cbe837f
--- /dev/null
+++ b/Open-ILS/src/sql/Pg/upgrade/YYYY.schema.acq-multi-attrs.sql
@@ -0,0 +1,104 @@
+
+BEGIN;
+
+CREATE OR REPLACE FUNCTION extract_marc_field_set
+        (TEXT, BIGINT, TEXT, TEXT) RETURNS SETOF TEXT AS $$
+DECLARE
+    query TEXT;
+    output TEXT;
+BEGIN
+    FOR output IN
+        SELECT x.t FROM (
+            SELECT id,t
+                FROM  oils_xpath_table(
+                    'id', 'marc', $1, $3, 'id = ' || $2)
+                AS t(id int, t text))x
+        LOOP
+        IF $4 IS NOT NULL THEN
+            SELECT INTO output (SELECT regexp_replace(output, $4, '', 'g'));
+        END IF;
+        RETURN NEXT output;
+    END LOOP;
+    RETURN;
+END;
+$$ LANGUAGE PLPGSQL IMMUTABLE;
+
+
+CREATE OR REPLACE FUNCTION 
+        public.extract_acq_marc_field_set ( BIGINT, TEXT, TEXT) 
+        RETURNS SETOF TEXT AS $$
+	SELECT extract_marc_field_set('acq.lineitem', $1, $2, $3);
+$$ LANGUAGE SQL;
+
+
+CREATE OR REPLACE FUNCTION public.ingest_acq_marc ( ) RETURNS TRIGGER AS $function$
+DECLARE
+	value		TEXT;
+	atype		TEXT;
+	prov		INT;
+	pos 		INT;
+	adef		RECORD;
+	xpath_string	TEXT;
+BEGIN
+	FOR adef IN SELECT *,tableoid FROM acq.lineitem_attr_definition LOOP
+
+		SELECT relname::TEXT INTO atype FROM pg_class WHERE oid = adef.tableoid;
+
+		IF (atype NOT IN ('lineitem_usr_attr_definition','lineitem_local_attr_definition')) THEN
+			IF (atype = 'lineitem_provider_attr_definition') THEN
+				SELECT provider INTO prov FROM acq.lineitem_provider_attr_definition WHERE id = adef.id;
+				CONTINUE WHEN NEW.provider IS NULL OR prov <> NEW.provider;
+			END IF;
+			
+			IF (atype = 'lineitem_provider_attr_definition') THEN
+				SELECT xpath INTO xpath_string FROM acq.lineitem_provider_attr_definition WHERE id = adef.id;
+			ELSIF (atype = 'lineitem_marc_attr_definition') THEN
+				SELECT xpath INTO xpath_string FROM acq.lineitem_marc_attr_definition WHERE id = adef.id;
+			ELSIF (atype = 'lineitem_generated_attr_definition') THEN
+				SELECT xpath INTO xpath_string FROM acq.lineitem_generated_attr_definition WHERE id = adef.id;
+			END IF;
+
+            xpath_string := REGEXP_REPLACE(xpath_string,$re$//?text\(\)$$re$,'');
+
+            IF (adef.code = 'title' OR adef.code = 'author') THEN
+                -- title and author should not be split
+                -- FIXME: once oils_xpath can grok XPATH 2.0 functions, we can use
+                -- string-join in the xpath and remove this special case
+    			SELECT extract_acq_marc_field(id, xpath_string, adef.remove) INTO value FROM acq.lineitem WHERE id = NEW.id;
+    			IF (value IS NOT NULL AND value <> '') THEN
+				    INSERT INTO acq.lineitem_attr (lineitem, definition, attr_type, attr_name, attr_value)
+	     			    VALUES (NEW.id, adef.id, atype, adef.code, value);
+                END IF;
+            ELSE
+                pos := 1;
+                LOOP
+                    -- each application of the regex may produce multiple values
+                    FOR value IN
+                        SELECT * FROM extract_acq_marc_field_set(
+                            NEW.id, xpath_string || '[' || pos || ']', adef.remove)
+                        LOOP
+
+                        IF (value IS NOT NULL AND value <> '') THEN
+                            INSERT INTO acq.lineitem_attr
+                                (lineitem, definition, attr_type, attr_name, attr_value)
+                                VALUES (NEW.id, adef.id, atype, adef.code, value);
+                        ELSE
+                            EXIT;
+                        END IF;
+                    END LOOP;
+                    IF NOT FOUND THEN
+                        EXIT;
+                    END IF;
+                    pos := pos + 1;
+               END LOOP;
+            END IF;
+
+		END IF;
+
+	END LOOP;
+
+	RETURN NULL;
+END;
+$function$ LANGUAGE PLPGSQL;
+
+COMMIT;

-----------------------------------------------------------------------

Summary of changes:
 Open-ILS/examples/fm_IDL.xml                       |    2 +
 .../perlmods/lib/OpenILS/Application/Acq/Order.pm  |  191 ++++++++++-
 .../lib/OpenILS/Application/Trigger/Reactor.pm     |   30 ++
 Open-ILS/src/sql/Pg/002.functions.config.sql       |   23 ++
 Open-ILS/src/sql/Pg/002.schema.config.sql          |    2 +-
 Open-ILS/src/sql/Pg/200.schema.acq.sql             |   35 ++-
 Open-ILS/src/sql/Pg/950.data.seed-values.sql       |   42 ++-
 .../sql/Pg/upgrade/0776.schema.acq-order-ident.sql |   30 ++
 .../sql/Pg/upgrade/0777.data.acq-order-ident.sql   |  374 ++++++++++++++++++++
 ...litting.sql => 0778.schema.acq-multi-attrs.sql} |   57 +++-
 Open-ILS/src/templates/acq/common/li_table.tt2     |   25 ++-
 Open-ILS/web/js/ui/default/acq/common/li_table.js  |  220 ++++++++++++
 Open-ILS/web/js/ui/default/acq/picklist/view.js    |    7 +-
 Open-ILS/web/js/ui/default/acq/po/view_po.js       |    6 +
 .../acq_lineitem_order_ident.txt                   |   22 ++
 15 files changed, 1023 insertions(+), 43 deletions(-)
 create mode 100644 Open-ILS/src/sql/Pg/upgrade/0776.schema.acq-order-ident.sql
 create mode 100644 Open-ILS/src/sql/Pg/upgrade/0777.data.acq-order-ident.sql
 copy Open-ILS/src/sql/Pg/upgrade/{0455.schema.acq-tweak-lineitem-attr-splitting.sql => 0778.schema.acq-multi-attrs.sql} (55%)
 create mode 100644 docs/RELEASE_NOTES_NEXT/acq_lineitem_order_ident.txt


hooks/post-receive
-- 
Evergreen ILS


More information about the open-ils-commits mailing list