lp1777675 inventory date support SQL table addition
authorKyle Huckins <khuckins@catalyte.io>
Fri, 3 Aug 2018 17:04:41 +0000 (17:04 +0000)
committerKyle Huckins <khuckins@catalyte.io>
Tue, 14 Aug 2018 15:53:36 +0000 (15:53 +0000)
- Remove inventory date and inventory workstation columns from asset.copy table.
- Create new table asset.last_copy_inventory containing inventory date and inventory workstation columns.
- Add code to do_checkin subroutine to update last_copy_inventory table on checkin.
- Update Holdings view, Checkins table, copy buckets, and item detail grid and lists to display new
inventory date and workstation information.
- Add Update Inventory action to Item Status detail and list views.

Signed-off-by: Kyle Huckins <khuckins@catalyte.io>
 Changes to be committed:
modified:   Open-ILS/examples/fm_IDL.xml
modified:   Open-ILS/src/perlmods/lib/OpenILS/Application/Circ.pm
modified:   Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Circulate.pm
modified:   Open-ILS/src/sql/Pg/040.schema.asset.sql
modified:   Open-ILS/src/sql/Pg/800.fkeys.sql
deleted:    Open-ILS/src/sql/Pg/upgrade/XXXX-create-inventory-workstation-and-date-columns.sql
new file:   Open-ILS/src/sql/Pg/upgrade/XXXX-create-inventory-workstation-and-date.sql
modified:   Open-ILS/src/templates/staff/cat/bucket/copy/t_pending.tt2
modified:   Open-ILS/src/templates/staff/cat/bucket/copy/t_view.tt2
modified:   Open-ILS/src/templates/staff/cat/catalog/t_holdings.tt2
modified:   Open-ILS/src/templates/staff/cat/item/index.tt2
modified:   Open-ILS/src/templates/staff/cat/item/t_list.tt2
modified:   Open-ILS/src/templates/staff/cat/item/t_summary_pane.tt2
modified:   Open-ILS/src/templates/staff/circ/checkin/t_checkin.tt2
modified:   Open-ILS/src/templates/staff/circ/checkin/t_checkin_table.tt2
modified:   Open-ILS/web/js/ui/default/staff/cat/bucket/copy/app.js
modified:   Open-ILS/web/js/ui/default/staff/cat/item/app.js
modified:   Open-ILS/web/js/ui/default/staff/cat/services/holdings.js
modified:   Open-ILS/web/js/ui/default/staff/circ/checkin/app.js
modified:   Open-ILS/web/js/ui/default/staff/circ/services/circ.js
modified:   Open-ILS/web/js/ui/default/staff/circ/services/item.js

21 files changed:
Open-ILS/examples/fm_IDL.xml
Open-ILS/src/perlmods/lib/OpenILS/Application/Circ.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Circulate.pm
Open-ILS/src/sql/Pg/040.schema.asset.sql
Open-ILS/src/sql/Pg/800.fkeys.sql
Open-ILS/src/sql/Pg/upgrade/XXXX-create-inventory-workstation-and-date-columns.sql [deleted file]
Open-ILS/src/sql/Pg/upgrade/XXXX-create-inventory-workstation-and-date.sql [new file with mode: 0644]
Open-ILS/src/templates/staff/cat/bucket/copy/t_pending.tt2
Open-ILS/src/templates/staff/cat/bucket/copy/t_view.tt2
Open-ILS/src/templates/staff/cat/catalog/t_holdings.tt2
Open-ILS/src/templates/staff/cat/item/index.tt2
Open-ILS/src/templates/staff/cat/item/t_list.tt2
Open-ILS/src/templates/staff/cat/item/t_summary_pane.tt2
Open-ILS/src/templates/staff/circ/checkin/t_checkin.tt2
Open-ILS/src/templates/staff/circ/checkin/t_checkin_table.tt2
Open-ILS/web/js/ui/default/staff/cat/bucket/copy/app.js
Open-ILS/web/js/ui/default/staff/cat/item/app.js
Open-ILS/web/js/ui/default/staff/cat/services/holdings.js
Open-ILS/web/js/ui/default/staff/circ/checkin/app.js
Open-ILS/web/js/ui/default/staff/circ/services/circ.js
Open-ILS/web/js/ui/default/staff/circ/services/item.js

index 6657a47..152788d 100644 (file)
@@ -7243,11 +7243,10 @@ SELECT  usr,
                        <field reporter:label="Peer Record Maps" name="peer_record_maps" oils_persist:virtual="true" reporter:datatype="link"/>
                        <field reporter:label="Peer Records" name="peer_records" oils_persist:virtual="true" reporter:datatype="link"/>
                        <field reporter:label="Last Captured Hold" name="last_captured_hold" oils_persist:virtual="true" reporter:datatype="link"/>
+                       <field reporter:label="Last Copy Inventory" name="last_copy_inventory" oils_persist:virtual="true" reporter:datatype="link"/>
                        <field reporter:label="Has Holds" name="holds_count" oils_persist:virtual="true" reporter:datatype="link"/>
                        <field reporter:label="Copy Tags" name="tags" oils_persist:virtual="true" reporter:datatype="link"/>
                        <field reporter:label="Copy Alerts" name="copy_alerts" oils_persist:virtual="true" reporter:datatype="link"/>
-                       <field reporter:label="Inventory Date" name="inventory_date" reporter:datatype="timestamp"/>
-                       <field reporter:label="Inventory Workstation" name="inventory_workstation" reporter:datatype="link"/>
                </fields>
                <links>
                        <link field="age_protect" reltype="has_a" key="id" map="" class="crahp"/>
@@ -7271,11 +7270,11 @@ SELECT  usr,
                        <link field="peer_record_maps" reltype="has_many" key="target_copy" map="" class="bpbcm"/>
                        <link field="peer_records" reltype="has_many" key="target_copy" map="peer_record" class="bpbcm"/>
                        <link field="last_captured_hold" reltype="has_a" key="current_copy" map="" class="alhr"/>
+                       <link field="last_copy_inventory" reltype="might_have" key="copy" map="" class="alci"/>
                        <link field="floating" reltype="has_a" key="id" map="" class="cfg"/>
                        <link field="holds_count" reltype="might_have" key="id" map="" class="hasholdscount"/>
                        <link field="tags" reltype="has_many" key="copy" map="" class="acptcm"/>
                        <link field="copy_alerts" reltype="has_many" key="copy" map="" class="aca"/>
-                       <link field="inventory_workstation" reltype="has_a" key="id" map="" class="aws"/>
                </links>
         <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
             <actions>
@@ -7293,6 +7292,27 @@ SELECT  usr,
         </permacrud>
        </class>
 
+       <class id="alci" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="asset::last_copy_inventory" oils_persist:tablename="asset.last_copy_inventory" reporter:core="true" reporter:label="Last Copy Inventory">
+               <fields oils_persist:primary="id" oils_persist:sequence="asset.last_copy_inventory_id_seq">
+            <field reporter:label="Last Inventory ID" name="id" reporter:datatype="id"/>
+                       <field reporter:label="Last Inventory Date" name="inventory_date" reporter:datatype="timestamp"/>
+                       <field reporter:label="Last Inventory Workstation" name="inventory_workstation" reporter:datatype="link"/>
+                       <field reporter:label="Copy" name="copy" reporter:datatype="link"/>
+               </fields>
+               <links>
+                       <link field="inventory_workstation" reltype="has_a" key="id" map="" class="aws"/>
+                       <link field="copy" reltype="has_a" key="id" map="" class="acp"/>
+               </links>
+        <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+            <actions>
+                               <create/>
+                               <retrieve/>
+                               <update/>
+                               <delete/>
+            </actions>
+        </permacrud>
+       </class>
+
        <class id="ccat" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="config::copy_alert_type" oils_persist:tablename="config.copy_alert_type" reporter:label="Copy Alert Type" oils_persist:restrict_primary="100">
                <fields oils_persist:primary="id" oils_persist:sequence="config.copy_alert_type_id_seq">
                        <field reporter:label="Id" name="id" reporter:selector="name" reporter:datatype="id"/>
index 3a59bf9..047feeb 100644 (file)
@@ -370,6 +370,34 @@ sub new_set_circ_lost {
     return 1;
 }
 
+__PACKAGE__->register_method(
+    method    => "update_last_copy_inventory",
+    api_name  => "open-ils.circ.circulation.update_last_copy_inventory");
+
+sub update_last_copy_inventory {
+    my( $self, $conn, $auth, $args ) = @_;
+    my $e = new_editor(authtoken=>$auth, xact=>1);
+    return $e->die_event unless $e->checkauth;
+
+    my $copies = $$args{copy_list};
+    foreach my $copy (@$copies) {
+        my $existing_alci = $e->search_asset_last_copy_inventory({copy => $copy})->[0];
+
+        if($existing_alci) {
+            $existing_alci->inventory_date('now');
+            $existing_alci->inventory_workstation($e->requestor->wsid);
+            $e->update_asset_last_copy_inventory($existing_alci) or return $e->die_event;
+        } else {
+            my $alci = Fieldmapper::asset::last_copy_inventory->new;
+            $alci->inventory_date('now');
+            $alci->inventory_workstation($e->requestor->wsid);
+            $alci->copy($copy);
+            $e->create_asset_last_copy_inventory($alci) or return $e->die_event;
+        }
+    }
+    $e->commit;
+    return 1;
+}
 
 __PACKAGE__->register_method(
     method  => "set_circ_claims_returned",
index 03cb381..c3de5b7 100644 (file)
@@ -412,7 +412,8 @@ my @AUTOLOAD_FIELDS = qw/
     remote_hold
     backdate
     reservation
-    inv_update
+    do_inventory_update
+    last_copy_inventory
     copy
     copy_id
     copy_barcode
@@ -2660,10 +2661,19 @@ sub do_checkin {
         $self->dont_change_lost_zero($dont_change_lost_zero);
     }
 
-    if ($self->inv_update) {
-        $self->copy->inventory_date('now');
-        $self->copy->inventory_workstation($self->editor->requestor->wsid);
+    my $last_copy_inventory = Fieldmapper::asset::last_copy_inventory->new;
+
+    if ($self->do_inventory_update) {
+        $last_copy_inventory->inventory_date('now');
+        $last_copy_inventory->inventory_workstation($self->editor->requestor->wsid);
+        $last_copy_inventory->copy($self->copy->id());
+    } else {
+        my $alci = $self->editor->search_asset_last_copy_inventory(
+            {copy => $self->copy->id}
+        );
+        $last_copy_inventory = $alci->[0]
     }
+    $self->last_copy_inventory($last_copy_inventory);
 
     if( $self->checkin_check_holds_shelf() ) {
         $self->bail_on_events(OpenILS::Event->new('NO_CHANGE'));
@@ -3945,6 +3955,22 @@ sub checkin_flesh_events {
         );
     }
 
+    if ($self->last_copy_inventory) {
+        # flesh some workstation fields before returning
+        $self->last_copy_inventory->inventory_workstation(
+            $self->editor->retrieve_actor_workstation([$self->last_copy_inventory->inventory_workstation])
+        );
+    }
+
+    if($self->last_copy_inventory && !$self->last_copy_inventory->id) {
+        my $alci = $self->editor->search_asset_last_copy_inventory(
+            {copy => $self->last_copy_inventory->copy}
+        );
+        if($alci->[0]) {
+            $self->last_copy_inventory->id($alci->[0]->id);
+        }
+     }
+
     for my $evt (@{$self->events}) {
 
         my $payload         = {};
@@ -3958,6 +3984,8 @@ sub checkin_flesh_events {
         $payload->{patron}  = $self->patron;
         $payload->{reservation} = $self->reservation
             unless (not $self->reservation or $self->reservation->cancel_time);
+        $payload->{last_copy_inventory} = $self->last_copy_inventory;
+        if ($self->do_inventory_update) { $payload->{do_inventory_update} = 1; }
 
         $evt->{payload}     = $payload;
     }
index 2ee6e00..75b02d7 100644 (file)
@@ -102,9 +102,7 @@ CREATE TABLE asset.copy (
        status_changed_time TIMESTAMP WITH TIME ZONE,
        active_date TIMESTAMP WITH TIME ZONE,
        mint_condition      BOOL        NOT NULL DEFAULT TRUE,
-    cost    NUMERIC(8,2),
-    inventory_workstation INT                          REFERENCES actor.workstation (id) DEFERRABLE INITIALLY DEFERRED,
-    inventory_date TIMESTAMP WITH TIME ZONE    DEFAULT NOW()
+    cost    NUMERIC(8,2)
 );
 CREATE UNIQUE INDEX copy_barcode_key ON asset.copy (barcode) WHERE deleted = FALSE OR deleted IS FALSE;
 CREATE INDEX cp_cn_idx ON asset.copy (call_number);
@@ -122,6 +120,14 @@ CREATE TABLE asset.copy_part_map (
 );
 CREATE UNIQUE INDEX copy_part_map_cp_part_idx ON asset.copy_part_map (target_copy, part);
 
+CREATE TABLE asset.last_copy_inventory (
+    id                          SERIAL                      PRIMARY KEY,
+    inventory_workstation       INTEGER                     REFERENCES actor.workstation (id) DEFERRABLE INITIALLY DEFERRED,
+    inventory_date              TIMESTAMP WITH TIME ZONE    DEFAULT NOW(),
+    copy                        BIGINT                                 NOT NULL
+);
+CREATE INDEX last_copy_inventory_copy_idx ON asset.last_copy_inventory (copy);
+
 CREATE TABLE asset.opac_visible_copies (
   id        BIGSERIAL primary key,
   copy_id   BIGINT, -- copy id
index c396119..50d7927 100644 (file)
@@ -169,6 +169,18 @@ BEGIN
 END;
 $f$ LANGUAGE PLPGSQL VOLATILE COST 50;
 
+CREATE OR REPLACE FUNCTION evergreen.asset_last_copy_inventory_copy_inh_fkey() RETURNS TRIGGER AS $f$
+BEGIN
+        PERFORM 1 FROM asset.copy WHERE id = NEW.copy;
+        IF NOT FOUND THEN
+                RAISE foreign_key_violation USING MESSAGE = FORMAT(
+                        $$Referenced asset.copy id not found, copy:%s$$, NEW.copy
+                );
+        END IF;
+        RETURN NEW;
+END;
+$f$ LANGUAGE PLPGSQL VOLATILE COST 50;
+
 CREATE CONSTRAINT TRIGGER inherit_asset_copy_alert_copy_fkey
         AFTER UPDATE OR INSERT ON asset.copy_alert
         DEFERRABLE FOR EACH ROW EXECUTE PROCEDURE evergreen.asset_copy_alert_copy_inh_fkey();
@@ -177,6 +189,10 @@ CREATE CONSTRAINT TRIGGER inherit_asset_copy_tag_copy_map_copy_fkey
         AFTER UPDATE OR INSERT ON asset.copy_tag_copy_map
         DEFERRABLE FOR EACH ROW EXECUTE PROCEDURE evergreen.asset_copy_tag_copy_map_copy_inh_fkey();
 
+CREATE CONSTRAINT TRIGGER inherit_asset_last_copy_inventory_copy_fkey
+        AFTER UPDATE OR INSERT ON asset.last_copy_inventory
+        DEFERRABLE FOR EACH ROW EXECUTE PROCEDURE evergreen.asset_last_copy_inventory_copy_inh_fkey();
+
 ALTER TABLE asset.copy_note ADD CONSTRAINT asset_copy_note_creator_fkey FOREIGN KEY (creator) REFERENCES actor.usr (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED;
 
 ALTER TABLE asset.call_number ADD CONSTRAINT asset_call_number_owning_lib_fkey FOREIGN KEY (owning_lib) REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED;
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX-create-inventory-workstation-and-date-columns.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX-create-inventory-workstation-and-date-columns.sql
deleted file mode 100644 (file)
index 1d03b93..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-BEGIN;
-
-SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version);
-
-ALTER TABLE asset.copy
-       ADD COLUMN inventory_workstation INT    REFERENCES actor.workstation (id) DEFERRABLE INITIALLY DEFERRED,
-       ADD COLUMN inventory_date TIMESTAMP WITH TIME ZONE      DEFAULT NOW();
-
-COMMIT;
\ No newline at end of file
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX-create-inventory-workstation-and-date.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX-create-inventory-workstation-and-date.sql
new file mode 100644 (file)
index 0000000..b4dcdf8
--- /dev/null
@@ -0,0 +1,29 @@
+BEGIN;
+
+SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version);
+
+CREATE TABLE asset.last_copy_inventory (
+    id                          SERIAL                      PRIMARY KEY,
+    inventory_workstation       INTEGER                     REFERENCES actor.workstation (id) DEFERRABLE INITIALLY DEFERRED,
+    inventory_date              TIMESTAMP WITH TIME ZONE    DEFAULT NOW(),
+    copy                        BIGINT                      NOT NULL
+);
+CREATE INDEX last_copy_inventory_copy_idx ON asset.last_copy_inventory (copy);
+
+CREATE OR REPLACE FUNCTION evergreen.asset_last_copy_inventory_copy_inh_fkey() RETURNS TRIGGER AS $f$
+BEGIN
+        PERFORM 1 FROM asset.copy WHERE id = NEW.copy;
+        IF NOT FOUND THEN
+                RAISE foreign_key_violation USING MESSAGE = FORMAT(
+                        $$Referenced asset.copy id not found, copy:%s$$, NEW.copy
+                );
+        END IF;
+        RETURN NEW;
+END;
+$f$ LANGUAGE PLPGSQL VOLATILE COST 50;
+
+CREATE CONSTRAINT TRIGGER inherit_asset_last_copy_inventory_copy_fkey
+        AFTER UPDATE OR INSERT ON asset.last_copy_inventory
+        DEFERRABLE FOR EACH ROW EXECUTE PROCEDURE evergreen.asset_last_copy_inventory_copy_inh_fkey();
+
+COMMIT;
\ No newline at end of file
index 2396333..321139b 100644 (file)
@@ -58,5 +58,7 @@
       {{item['call_number.record.simple_record.title']}}
     </a>
   </eg-grid-field>
+  <eg-grid-field path="_last_inventory_date" datatype="timestamp" label="[% l('Inventory Date') %]"></eg-grid-field>
+  <eg-grid-field path="_last_inventory_workstation" label="[% l('Inventory Workstation') %]"></eg-grid-field>
 
 </eg-grid>
index fb7d3d1..b49bc22 100644 (file)
@@ -43,5 +43,7 @@
       {{item['call_number.record.simple_record.title']}}
     </a>
   </eg-grid-field>
+  <eg-grid-field path="_last_inventory_date" datatype="timestamp" label="[% l('Inventory Date') %]"></eg-grid-field>
+  <eg-grid-field path="_last_inventory_workstation" label="[% l('Inventory Workstation') %]"></eg-grid-field>
 
 </eg-grid>
index f6de9c6..b1a5b74 100644 (file)
       {{item['copy_alert_count']}}
       <button ng-disabled="item['copy_alert_count'] <= 0" class="btn btn-sm btn-default" ng-click="col.handlers.copyAlertsEdit(item['id'])">[% l('Manage') %]</button>
     </eg-grid-field>
-    <eg-grid-field label="[% l('Inventory Date') %]"          path="inventory_date"></eg-grid-field>
-    <eg-grid-field label="[% l('Inventory Workstation') %]"   path="inventory_workstation"></eg-grid-field>
+    <eg-grid-field label="[% l('Inventory Date') %]"          path="_last_inventory_date"></eg-grid-field>
+    <eg-grid-field label="[% l('Inventory Workstation') %]"   path="_last_inventory_workstation"></eg-grid-field>
   
   </eg-grid>
 </div>
index df5ed04..c661742 100644 (file)
       "[% l('One or more items could not be transferred. Override?') %]";
     s.OVERRIDE_TRANSFER_COPIES_TO_MARKED_VOLUME_BODY =
       "[% l('Reason(s) include: [_1]', '{{evt_desc}}') %]";
+    s.SUCCESS_UPDATE_INVENTORY =
+      "[% l('Updated most recent inventory data for selected items.') %]";
+    s.FAIL_UPDATE_INVENTORY =
+      "[% l('Failed to update recent inventory data for selected items.')%]"
   }])
 </script>
 [% END %]
@@ -84,6 +88,7 @@
         <li><a href ng-click="checkin()">[% l('Check In Items') %]</a></li>
         <li><a href ng-click="renew()">[% l('Renew Items') %]</a></li>
         <li><a href ng-click="cancel_transit()">[% l('Cancel Transit') %]</a></li>
+        <li><a href ng-click="update_inventory()">[% l('Update Inventory') %]</a></li>
 
         <p><b>[% l('Mark') %]</b></p>    
         <li><a href ng-click="selectedHoldingsDamaged()">[% l('Item as Damaged') %]</a></li>
index 7b59f21..ab3a4e1 100644 (file)
@@ -27,6 +27,8 @@
     label="[% l('Renew Items') %]"></eg-grid-action>
   <eg-grid-action handler="cancel_transit"
     label="[% l('Cancel Transit') %]"></eg-grid-action>
+  <eg-grid-action handler="update_inventory"
+    label="[% l('Update Inventory') %]"></eg-grid-action>
     
   <eg-grid-action handler="selectedHoldingsItemStatusTgrEvt" group="[% l('Show') %]"
     label="[% l('Triggered Events') %]"></eg-grid-action>
     {{item['copy_alert_count']}}
     <button ng-disabled="item['copy_alert_count'] <= 0" class="btn btn-sm btn-default" ng-click="col.handlers.copyAlertsEdit(item['id'])">[% l('Manage') %]</button>
   </eg-grid-field>
-  <eg-grid-field label="[% l('Inventory Date') %]"        path="inventory_date"></eg-grid-field>
-  <eg-grid-field label="[% l('Inventory Workstation') %]" path="inventory_workstation.name()"></eg-grid-field>
+  <eg-grid-field label="[% l('Inventory Date') %]"        path="_last_copy_inventory.inventory_date" datatype="timestamp"></eg-grid-field>
+  <eg-grid-field label="[% l('Inventory Workstation') %]" path="_last_copy_inventory._inventory_workstation_name"></eg-grid-field>
   
 </eg-grid>
 
index 85cb8aa..a372711 100644 (file)
 
   <div class="flex-row">
     <div class="flex-cell">[% l('Inventory Date') %]</div>
-    <div class="flex-cell well">{{copy.inventory_date() | date:egDateAndTimeFormat}}</div>
+    <div class="flex-cell well">{{last_copy_inventory.inventory_date() | date:egDateAndTimeFormat}}</div>
 
     <div class="flex-cell">[% l('Inventory Workstation') %]</div>
-    <div class="flex-cell well">{{copy.inventory_workstation().name()}}</div>
+    <div class="flex-cell well">{{last_copy_inventory.inventory_workstation().name()}}</div>
 
     <div class="flex-cell"></div>
     <div class="flex-cell"></div>
index ec7b1a7..30d996b 100644 (file)
@@ -41,6 +41,9 @@
     <div ng-if="modifiers.manual_float" class="alert-danger pad-all-min">
       [% l('Manual Floating Active') %]
     </div>
+    <div ng-if="modifiers.do_inventory_update" class="alert-danger pad-all-min">
+      [% l('Update Inventory') %]
+    </div>
   </div>
 </div>
 
     </div>
   </div>
 
-  <div class="col-md-2">
-    <input type="checkbox" ng-model="checkinArgs.do_inventory_update"/>
-    <label>
-      [% l('Update Inventory') %]
-    </label>
-  </div>
-
   <div class="col-md-3" ng-if="!is_capture">
     <div class="flex-row">
       <div class="flex-cell"></div>
             <span>[% l('Manual Floating Active') %]</span>
           </a>
         </li>
+        <li>
+          <a href
+            ng-click="toggle_mod('do_inventory_update')">
+            <span ng-if="modifiers.do_inventory_update"
+              class="label label-success">&#x2713;</span>
+            <span ng-if="!modifiers.do_inventory_update"
+              class="label label-warning">&#x2717;</span>
+            <span>[% l('Update Inventory') %]</span>
+          </a>
+        </li>
       </ul>
     </div><!-- btn grp -->
   </div><!-- col -->
index 8f98584..ff9b234 100644 (file)
   <eg-grid-field path="au.*" parent-idl-class="au" hidden></eg-grid-field>
   <eg-grid-field path="transit.*" parent-idl-class="atc" hidden></eg-grid-field>
   <eg-grid-field path="hold.*" parent-idl-class="ahr" hidden></eg-grid-field>
+  <eg-grid-field path="alci.inventory_date" label="[% l('Inventory Date') %]" datatype="timestamp" parent-idl-class="alci" hidden></eg-grid-field>
+  <eg-grid-field path="alci.inventory_workstation" label="[% l('Inventory Workstation')%]" parent-idl-class="alci" hidden>{{item.alci["inventory_workstation.name"]}}</eg-grid-field>
 </eg-grid>
 
index a51eef4..2ebb0ee 100644 (file)
@@ -215,6 +215,16 @@ angular.module('egCatCopyBuckets',
         return deferred.promise;
     }
 
+    // apply last inventory data to fetched bucket items
+    service.fetchRecentInventoryData = function(copy) {
+        return egCore.pcrud.search('alci',
+            {copy: copy.id},
+            {flesh: 2, flesh_fields: {alci: ['inventory_workstation']}}
+        ).then(function(alci) {
+            return alci;
+        });
+    }
+
     return service;
 }])
 
@@ -390,6 +400,14 @@ function($scope,  $routeParams,  bucketSvc , egGridDataProvider,   egCore) {
             return null;
         },
         allItemsRetrieved : function() {
+            angular.forEach($scope.gridControls.allItems(), function(copy) {
+                bucketSvc.fetchRecentInventoryData(copy).then(function(alci) {
+                    if (alci) {
+                        copy._last_inventory_date = alci.inventory_date();
+                        copy._last_inventory_workstation = alci.inventory_workstation().name();
+                    }
+                });
+            });
             $scope.context.selectPendingBC = true;
         }
     }
@@ -448,6 +466,16 @@ function($scope,  $q , $routeParams , $timeout , $window , $uibModal , bucketSvc
         setQuery : function(q) {
             if (q) query = q;
             return query;
+        },
+        allItemsRetrieved : function() {
+            angular.forEach($scope.gridControls.allItems(), function(copy) {
+                bucketSvc.fetchRecentInventoryData(copy).then(function(alci) {
+                    if (alci) {
+                        copy._last_inventory_date = alci.inventory_date();
+                        copy._last_inventory_workstation = alci.inventory_workstation().name();
+                    }
+                });
+            });
         }
     };
 
index cededcd..24178d7 100644 (file)
@@ -104,6 +104,13 @@ function($scope , $location , $timeout , egCore , egGridDataProvider , itemSvc)
         itemSvc.requestItems([$scope.args.copyId]);
     }
 
+    $scope.update_inventory = function() {
+        itemSvc.updateInventory([$scope.args.copyId], null)
+        .then(function(res) {
+            $timeout(function() { location.href = location.href; }, 1000);
+        });
+    }
+
     $scope.attach_to_peer_bib = function() {
         itemSvc.attach_to_peer_bib([{
             id : $scope.args.copyId,
@@ -236,10 +243,10 @@ function($scope , $location , $timeout , egCore , egGridDataProvider , itemSvc)
 .controller('ListCtrl', 
        ['$scope','$q','$routeParams','$location','$timeout','$window','egCore',
         'egGridDataProvider','egItem','egUser','$uibModal','egCirc','egConfirmDialog',
-        'egProgressDialog',
+        'egProgressDialog', 'ngToast',
 function($scope , $q , $routeParams , $location , $timeout , $window , egCore , 
          egGridDataProvider , itemSvc , egUser , $uibModal , egCirc , egConfirmDialog,
-         egProgressDialog) {
+         egProgressDialog, ngToast) {
 
     var copyId = [];
     var cp_list = $routeParams.idList;
@@ -385,6 +392,18 @@ function($scope , $q , $routeParams , $location , $timeout , $window , egCore ,
         itemSvc.add_copies_to_bucket(copy_list);
     }
 
+    $scope.update_inventory = function() {
+        var copy_list = gatherSelectedHoldingsIds();
+        itemSvc.updateInventory(copy_list, $scope.gridControls.allItems()).then(function(res) {
+            if (res) {
+                $scope.gridControls.allItems(res);
+                ngToast.create(egCore.strings.SUCCESS_UPDATE_INVENTORY);
+            } else {
+                ngToast.warning(egCore.strings.FAIL_UPDATE_INVENTORY);
+            }
+        });
+    }
+
     $scope.need_one_selected = function() {
         var items = $scope.gridControls.selectedItems();
         if (items.length == 1) return false;
@@ -591,6 +610,9 @@ function($scope , $q , $location , $routeParams , $timeout , $window , egCore ,
         // regardless of whether it matches the current item.
         if (!barcode && itemSvc.copy && itemSvc.copy.id() == copyId) {
             $scope.copy = itemSvc.copy;
+            if (itemSvc.last_copy_inventory && itemSvc.last_copy_inventory.copy() == copyId) {
+                $scope.last_copy_inventory = itemSvc.last_copy_inventory;
+            }
             $scope.copy_alert_count = itemSvc.copy.copy_alerts().filter(function(aca) {
                 return !aca.ack_time();
             }).length;
@@ -623,9 +645,11 @@ function($scope , $q , $location , $routeParams , $timeout , $window , egCore ,
 
             var copy = res.copy;
             itemSvc.copy = copy;
+            if (res.last_copy_inventory) itemSvc.last_copy_inventory = res.last_copy_inventory;
 
 
             $scope.copy = copy;
+            $scope.last_copy_inventory = res.last_copy_inventory;
             $scope.copy_alert_count = copy.copy_alerts().filter(function(aca) {
                 return !aca.ack_time();
             }).length;
index eb835f5..43b5a9f 100644 (file)
@@ -117,6 +117,19 @@ function(egCore , $q) {
                     }
                 });
 
+                //create a virtual field for displaying most recent inventory data
+                angular.forEach(svc.copies, function(cp) {
+                    egCore.pcrud.search('alci',
+                        {copy: cp.id},
+                        {flesh: 2, flesh_fields: {alci: ['inventory_workstation']}}
+                    ).then(function(alci) {
+                        if (alci) {
+                            cp._last_inventory_workstation = alci.inventory_workstation().name();
+                            cp._last_inventory_date = alci.inventory_date();
+                        }
+                    });
+                });
+
                 // create virtual field for copy alert count
                 angular.forEach(svc.copies, function (cp) {
                     if (cp.copy_alerts) cp.copy_alert_count = cp.copy_alerts.length;
index ef3944f..2dfb5e7 100644 (file)
@@ -85,6 +85,7 @@ function($scope , $q , $window , $location , $timeout , egCore , checkinSvc , eg
     } else {
         modifiers.push('noop'); // AKA suppress holds and transits
         modifiers.push('auto_print_holds_transits');
+        modifiers.push('do_inventory_update');
     }
 
     // set modifiers from stored preferences
@@ -153,14 +154,17 @@ function($scope , $q , $window , $location , $timeout , egCore , checkinSvc , eg
                 params.retarget_mode = 'retarget';
             }
         }
+        if ($scope.modifiers.do_inventory_update) params.do_inventory_update = true;
 
         egCore.hatch.setItem('circ.checkin.strict_barcode', $scope.strict_barcode);
+        egCore.hatch.setItem('circ.checkin.do_inventory_update', $scope.modifiers.do_inventory_update);
         var options = {
             check_barcode : $scope.strict_barcode,
             no_precat_alert : $scope.modifiers.no_precat_alert,
             auto_print_holds_transits : 
                 $scope.modifiers.auto_print_holds_transits,
-            suppress_popups : suppress_popups
+            suppress_popups : suppress_popups,
+            do_inventory_update : $scope.modifiers.do_inventory_update
         };
 
         return {params : params, options: options};
@@ -189,7 +193,6 @@ function($scope , $q , $window , $location , $timeout , egCore , checkinSvc , eg
 
         // track the item in the grid before sending the request
         checkinSvc.checkins.unshift(row_item);
-
         egCirc.checkin(params, options).then(
         function(final_resp) {
             
index 2807464..b6ebe84 100644 (file)
@@ -300,6 +300,7 @@ function($uibModal , $q , egCore , egAlertDialog , egConfirmDialog,  egAddCopyAl
         data.record = payload.record;
         data.acp = payload.copy;
         data.acn = payload.volume ?  payload.volume : payload.copy ? payload.copy.call_number() : null;
+        data.alci = egCore.idl.toHash(payload.last_copy_inventory, true);
         data.au = payload.patron;
         data.transit = payload.transit;
         data.status = payload.status;
@@ -309,8 +310,16 @@ function($uibModal , $q , egCore , egAlertDialog , egConfirmDialog,  egAddCopyAl
         data.isbn = final_resp.evt[0].isbn;
         data.route_to = final_resp.evt[0].route_to;
 
+
         if (payload.circ) data.duration = payload.circ.duration();
         if (payload.circ) data.circ_lib = payload.circ.circ_lib();
+        if (payload.do_inventory_update) {
+            if (payload.last_copy_inventory.id()) {
+                egCore.pcrud.update(payload.last_copy_inventory);
+            } else {
+                egCore.pcrud.create(payload.last_copy_inventory);
+            }
+        }
 
         // for checkin, the mbts lives on the main circ
         if (payload.circ && payload.circ.billable_transaction())
@@ -1463,11 +1472,12 @@ function($uibModal , $q , egCore , egAlertDialog , egConfirmDialog,  egAddCopyAl
 
         var final_resp = {evt : evt, params : params, options : options};
 
-        var copy, hold, transit;
+        var copy, hold, transit, last_copy_inventory;
         if (evt[0].payload) {
             copy = evt[0].payload.copy;
             hold = evt[0].payload.hold;
             transit = evt[0].payload.transit;
+            last_copy_inventory = evt[0].payload.last_copy_inventory;
         }
 
         // track the barcode regardless of whether it's valid
index 8e4c261..4496741 100644 (file)
@@ -13,10 +13,10 @@ function(egCore , egCirc , $uibModal , $q , $timeout , $window , egConfirmDialog
     };
 
     service.flesh = {   
-        flesh : 3, 
+        flesh : 4,
         flesh_fields : {
             acp : ['call_number','location','status','location','floating','circ_modifier',
-                'age_protect','circ_lib','copy_alerts', 'inventory_workstation'],
+                'age_protect','circ_lib','copy_alerts'],
             acn : ['record','prefix','suffix','label_class'],
             bre : ['simple_record','creator','editor']
         },
@@ -45,6 +45,13 @@ function(egCore , egCirc , $uibModal , $q , $timeout , $window , egConfirmDialog
         limit :  1
     }
 
+    service.inventoryFlesh = {
+        flesh : 2,
+        flesh_fields : {
+            alci : ['inventory_workstation']
+        }
+    }
+
     //Retrieve separate copy, aacs, and accs information
     service.getCopy = function(barcode, id) {
         if (barcode) {
@@ -66,6 +73,11 @@ function(egCore , egCirc , $uibModal , $q , $timeout , $window , egConfirmDialog
             service.circFlesh).then(function(circ) {return circ});
     }
 
+    service.getInventory = function(id) {
+        return egCore.pcrud.search('alci', {copy : id },
+            service.inventoryFlesh).then(function(alci) {return alci});
+    }
+
     service.getSummary = function(id) {
         return circ_summary = egCore.net.request(
             'open-ils.circ',
@@ -100,18 +112,29 @@ function(egCore , egCirc , $uibModal , $q , $timeout , $window , egConfirmDialog
                 });
         }
 
+        var fetchInventory = function(copy) {
+            return service.getInventory(copy.id())
+                .then(function(alci) {
+                    if (alci) {
+                        copyData.last_copy_inventory = alci;
+                        return copyData;
+                    }
+                });
+        }
+
         return fetchCopy(barcode, id).then(function(res) {
 
             if(!res.copy) { return $q.when(); }
-
-            return fetchCirc(copyData.copy).then(function(res) {
-                if (copyData.circ) {
-                    return fetchSummary(copyData.circ).then(function() {
+            return fetchInventory(copyData.copy).then(function(invRes) {
+                return fetchCirc(copyData.copy).then(function(res) {
+                    if (copyData.circ) {
+                        return fetchSummary(copyData.circ).then(function() {
+                            return copyData;
+                        });
+                    } else {
                         return copyData;
-                    });
-                } else {
-                    return copyData;
-                }
+                    }
+                });
             });
         });
 
@@ -122,6 +145,7 @@ function(egCore , egCirc , $uibModal , $q , $timeout , $window , egConfirmDialog
         var copy;
         var circ;
         var circ_summary;
+        var last_copy_inventory;
         var lastRes = {};
 
         return service.retrieveCopyData(barcode, id)
@@ -158,6 +182,10 @@ function(egCore , egCirc , $uibModal , $q , $timeout , $window , egConfirmDialog
                                         flatCopy._circ_summary.checkout_workstation :
                                         '';
                 }
+                if (copyData.last_copy_inventory) {
+                    flatCopy._last_copy_inventory = egCore.idl.toHash(copyData.last_copy_inventory, true);
+                    flatCopy._last_copy_inventory._inventory_workstation_name = copyData.last_copy_inventory.inventory_workstation().name();
+                }
                 flatCopy.index = service.index++;
                 flatCopy.copy_alert_count = copyData.copy.copy_alerts().filter(function(aca) {
                     return !aca.ack_time();
@@ -188,6 +216,7 @@ function(egCore , egCirc , $uibModal , $q , $timeout , $window , egConfirmDialog
 
             return lastRes = {
                 copy : copyData.copy,
+                last_copy_inventory : copyData.last_copy_inventory,
                 index : flatCopy.index
             }
         });
@@ -195,6 +224,33 @@ function(egCore , egCirc , $uibModal , $q , $timeout , $window , egConfirmDialog
 
     }
 
+    service.updateInventory = function(copy_list, all_items, refresh) {
+        if (copy_list.length == 0) return;
+        return egCore.net.request(
+            'open-ils.circ',
+            'open-ils.circ.circulation.update_last_copy_inventory',
+            egCore.auth.token(), {copy_list: copy_list}
+        ).then(function(res) {
+            if (res) {
+                if (all_items) angular.forEach(copy_list, function(copy) {
+                    angular.forEach(all_items, function(item) {
+                        if (copy == item.id) {
+                            egCore.pcrud.search('alci', {copy: copy},
+                              {flesh: 1, flesh_fields:
+                                {alci: ['inventory_workstation']}
+                            }).then(function(alci) {
+                                item._last_copy_inventory.inventory_date = alci.inventory_date();
+                                item._last_copy_inventory._inventory_workstation_name =
+                                    alci.inventory_workstation().name();
+                            });
+                        }
+                    });
+                });
+                return all_items || res;
+            }
+        });
+    }
+
     service.add_copies_to_bucket = function(copy_list) {
         if (copy_list.length == 0) return;