Improving holds.
authorkenstir <kenstir@gmail.com>
Sun, 27 Oct 2013 13:56:26 +0000 (09:56 -0400)
committerkenstir <kenstir@gmail.com>
Sun, 27 Oct 2013 13:56:26 +0000 (09:56 -0400)
* Hold status was all mixed up; it now properly says "Waiting for copy"
  instead of "Transit".  Hold status now indicates both #holds #copies and
  position in queue (user requested).
* Removed email checkbox and phone number checkbox from hold updates.  At
  least at my library web site you can't update phone/email preferences
  by editing holds.  hold_request.phone_notify is a text not boolean.

Open-ILS/src/Android/res/layout/hold_details.xml
Open-ILS/src/Android/res/values/strings.xml
Open-ILS/src/Android/src/org/evergreen/android/Constants.java [new file with mode: 0644]
Open-ILS/src/Android/src/org/evergreen/android/accountAccess/AccountAccess.java
Open-ILS/src/Android/src/org/evergreen/android/accountAccess/holds/HoldDetails.java
Open-ILS/src/Android/src/org/evergreen/android/accountAccess/holds/HoldRecord.java
Open-ILS/src/Android/src/org/evergreen/android/accountAccess/holds/HoldsListView.java
Open-ILS/src/Android/src/org/evergreen/android/globals/GlobalConfigs.java

index b480139..8432807 100644 (file)
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal"
-               >
-                       <TextView 
-                           android:layout_width="0dip"
-                           android:layout_height="wrap_content"
-                           android:layout_weight="2"
-                           android:text="@string/contact_telephone"
-                           />
-                       
-                       <EditText
-                           android:id="@+id/hold_contact_telephone"
-                           android:layout_width="0dip"
-                           android:layout_height="wrap_content"
-                           android:layout_weight="4"
-                           android:gravity="center"
-                           />    
+               >    
            </LinearLayout>
                        <LinearLayout 
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal"
-               >
-                       <TextView 
-                           android:layout_width="0dip"
-                           android:layout_height="wrap_content"
-                           android:layout_weight="2"
-                           android:text="@string/enable_phone_notification"
-                           />
-                       
-                       <CheckBox
-                           android:id="@+id/hold_enable_phone_notification"
-                           android:layout_width="0dip"
-                           android:layout_height="wrap_content"
-                           android:layout_weight="4"
-                           android:gravity="center"
-                           />    
+               >    
            </LinearLayout>
                        <LinearLayout 
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal"
-               >
-                       <TextView 
-                           android:layout_width="0dip"
-                           android:layout_height="wrap_content"
-                           android:layout_weight="2"
-                           android:text="@string/enable_email_notification"
-                           />
-                       
-                       <CheckBox
-                           android:id="@+id/hold_enable_email_notification"
-                           android:layout_width="0dip"
-                           android:layout_height="wrap_content"
-                           android:layout_weight="4"
-                           android:gravity="center"
-                           />    
+               >    
            </LinearLayout>
                        <LinearLayout 
                android:layout_width="fill_parent"
index 725f02f..e1e2830 100644 (file)
      
      <string name="preference_notifications_title">Notification settings</string>
      
+     <plurals name="number_of_copies">
+        <item quantity="one">one copy</item>
+        <item quantity="other">%d copies</item>
+     </plurals>
+     <plurals name="number_of_holds">
+        <item quantity="one">One hold</item>
+        <item quantity="other">%d holds</item>
+     </plurals>
+          <plurals name="number_of_items">
+        <item quantity="one">one item</item>
+        <item quantity="other">%d items</item>
+     </plurals>
+     
 </resources>
diff --git a/Open-ILS/src/Android/src/org/evergreen/android/Constants.java b/Open-ILS/src/Android/src/org/evergreen/android/Constants.java
new file mode 100644 (file)
index 0000000..0d3108f
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2013 Evergreen Open-ILS
+ * @author kenstir
+ */
+package org.evergreen.android;
+
+/**
+ * Conversion of constants from Const.pm
+ */
+class Constants {
+
+    // Copy Statuses
+    public static final int COPY_STATUS_AVAILABLE     = 0;
+    public static final int COPY_STATUS_CHECKED_OUT   = 1;
+    public static final int COPY_STATUS_BINDERY       = 2;
+    public static final int COPY_STATUS_LOST          = 3;
+    public static final int COPY_STATUS_MISSING       = 4;
+    public static final int COPY_STATUS_IN_PROCESS    = 5;
+    public static final int COPY_STATUS_IN_TRANSIT    = 6;
+    public static final int COPY_STATUS_RESHELVING    = 7;
+    public static final int COPY_STATUS_ON_HOLDS_SHELF= 8;
+    public static final int COPY_STATUS_ON_ORDER      = 9;
+    public static final int COPY_STATUS_ILL           = 10;
+    public static final int COPY_STATUS_CATALOGING    = 11;
+    public static final int COPY_STATUS_RESERVES      = 12;
+    public static final int COPY_STATUS_DISCARD       = 13;
+    public static final int COPY_STATUS_DAMAGED       = 14;
+    public static final int COPY_STATUS_ON_RESV_SHELF = 15;
+
+    // Circ defaults for pre-cataloged copies
+    public static final int PRECAT_COPY_FINE_LEVEL    = 2;
+    public static final int PRECAT_COPY_LOAN_DURATION = 2;
+    public static final int PRECAT_CALL_NUMBER        = -1;
+    public static final int PRECAT_RECORD             = -1;
+
+    // Circ constants
+    public static final int CIRC_DURATION_SHORT       = 1;
+    public static final int CIRC_DURATION_NORMAL      = 2;
+    public static final int CIRC_DURATION_EXTENDED    = 3;
+    public static final int REC_FINE_LEVEL_LOW        = 1;
+    public static final int REC_FINE_LEVEL_NORMAL     = 2;
+    public static final int REC_FINE_LEVEL_HIGH       = 3;
+    public static final String STOP_FINES_CHECKIN        = "CHECKIN";
+    public static final String STOP_FINES_RENEW          = "RENEW";
+    public static final String STOP_FINES_LOST           = "LOST";
+    public static final String STOP_FINES_CLAIMSRETURNED = "CLAIMSRETURNED";
+    public static final String STOP_FINES_LONGOVERDUE    = "LONGOVERDUE";
+    public static final String STOP_FINES_MAX_FINES      = "MAXFINES";
+    public static final String STOP_FINES_CLAIMS_NEVERCHECKEDOUT = "CLAIMSNEVERCHECKEDOUT";
+    public static final String UNLIMITED_CIRC_DURATION   = "unlimited";
+
+    // Settings
+    public static final String SETTING_LOST_PROCESSING_FEE = "circ.lost_materials_processing_fee";
+    public static final String SETTING_DEF_ITEM_PRICE = "cat.default_item_price";
+    public static final String SETTING_ORG_BOUNCED_EMAIL = "org.bounced_emails";
+    public static final String SETTING_CHARGE_LOST_ON_ZERO = "circ.charge_lost_on_zero";
+    public static final String SETTING_VOID_OVERDUE_ON_LOST = "circ.void_overdue_on_lost";
+    public static final String SETTING_HOLD_SOFT_STALL = "circ.hold_stalling.soft";
+    public static final String SETTING_HOLD_HARD_STALL = "circ.hold_stalling.hard";
+    public static final String SETTING_HOLD_SOFT_BOUNDARY = "circ.hold_boundary.soft";
+    public static final String SETTING_HOLD_HARD_BOUNDARY = "circ.hold_boundary.hard";
+    public static final String SETTING_HOLD_EXPIRE = "circ.hold_expire_interval";
+    public static final String SETTING_HOLD_ESIMATE_WAIT_INTERVAL = "circ.holds.default_estimated_wait_interval";
+    public static final String SETTING_VOID_LOST_ON_CHECKIN                = "circ.void_lost_on_checkin";
+    public static final String SETTING_MAX_ACCEPT_RETURN_OF_LOST           = "circ.max_accept_return_of_lost";
+    public static final String SETTING_VOID_LOST_PROCESS_FEE_ON_CHECKIN    = "circ.void_lost_proc_fee_on_checkin";
+    public static final String SETTING_RESTORE_OVERDUE_ON_LOST_RETURN      = "circ.restore_overdue_on_lost_return";
+    public static final String SETTING_LOST_IMMEDIATELY_AVAILABLE          = "circ.lost_immediately_available";
+    public static final String SETTING_BLOCK_HOLD_FOR_EXPIRED_PATRON       = "circ.holds.expired_patron_block";
+    public static final String SETTING_GENERATE_OVERDUE_ON_LOST_RETURN     = "circ.lost.generate_overdue_on_checkin";
+
+    public static final String HOLD_TYPE_COPY        = "C";
+    public static final String HOLD_TYPE_FORCE       = "F";
+    public static final String HOLD_TYPE_RECALL      = "R";
+    public static final String HOLD_TYPE_ISSUANCE    = "I";
+    public static final String HOLD_TYPE_VOLUME      = "V";
+    public static final String HOLD_TYPE_TITLE       = "T";
+    public static final String HOLD_TYPE_METARECORD  = "M";
+    public static final String HOLD_TYPE_MONOPART    = "P";
+
+    public static final String BILLING_TYPE_OVERDUE_MATERIALS = "Overdue materials";
+    public static final String BILLING_TYPE_COLLECTION_FEE = "Long Overdue Collection Fee";
+    public static final String BILLING_TYPE_DEPOSIT = "System: Deposit";
+    public static final String BILLING_TYPE_RENTAL = "System: Rental";
+    public static final String BILLING_NOTE_SYSTEM = "SYSTEM GENERATED";
+
+    public static final String ACQ_DEBIT_TYPE_PURCHASE = "purchase";
+    public static final String ACQ_DEBIT_TYPE_TRANSFER = "xfer";
+
+    // all penalties with ID < 100 are managed automatically
+    public static final int PENALTY_AUTO_ID = 100;
+    public static final int PENALTY_PATRON_EXCEEDS_FINES = 1;
+    public static final int PENALTY_PATRON_EXCEEDS_OVERDUE_COUNT = 2;
+    public static final int PENALTY_INVALID_PATRON_ADDRESS = 29;
+
+    public static final int BILLING_TYPE_NOTIFICATION_FEE = 9;
+}
index 32eaff6..e653a07 100644 (file)
@@ -957,25 +957,27 @@ public class AccountAccess {
      *
      * @param hold the hold
      * @param holdObj the hold obj
-     * @return the object
      * @throws SessionNotFoundException the session not found exception
      * @throws NoNetworkAccessException the no network access exception
      * @throws NoAccessToServer the no access to server
      */
-    public Object fetchHoldStatus(OSRFObject hold, HoldRecord holdObj)
+    public void fetchHoldStatus(OSRFObject hold, HoldRecord holdObj)
             throws SessionNotFoundException, NoNetworkAccessException,
             NoAccessToServer {
 
         Integer hold_id = hold.getInt("id");
         // MAP : potential_copies, status, total_holds, queue_position,
         // estimated_wait
-        Object hold_status = Utils.doRequest(conn, SERVICE_CIRC,
+        Object resp = Utils.doRequest(conn, SERVICE_CIRC,
                 METHOD_FETCH_HOLD_STATUS, authToken, cm, new Object[] {
                         authToken, hold_id });
 
-        // get status
-        holdObj.status = ((Map<String, Integer>) hold_status).get("status");
-        return hold_status;
+        Map<String, Integer> map = (Map<String, Integer>)resp;
+        holdObj.status = map.get("status");
+        holdObj.potentialCopies = map.get("potential_copies");
+        holdObj.estimatedWaitInSeconds = map.get("estimated_wait");
+        holdObj.queuePosition = map.get("queue_position");
+        holdObj.totalHolds = map.get("total_holds");
     }
 
     /**
@@ -1009,9 +1011,6 @@ public class AccountAccess {
      *
      * @param ahr the ahr
      * @param pickup_lib the pickup_lib
-     * @param email_notify the email_notify
-     * @param phone_notify the phone_notify
-     * @param phone the phone
      * @param suspendHold the suspend hold
      * @param expire_time the expire_time
      * @param thaw_date the thaw_date
@@ -1021,15 +1020,12 @@ public class AccountAccess {
      * @throws NoAccessToServer the no access to server
      */
     public Object updateHold(OSRFObject ahr, Integer pickup_lib,
-            boolean email_notify, boolean phone_notify, String phone,
             boolean suspendHold, String expire_time, String thaw_date)
             throws SessionNotFoundException, NoNetworkAccessException,
             NoAccessToServer {
         // TODO verify that object is correct passed to the server
 
-        ahr.put("pickup_lib", pickup_lib); // pick-up lib
-        ahr.put("phone_notify", phone);
-        ahr.put("email_notify", email_notify);
+        ahr.put("pickup_lib", pickup_lib);
         ahr.put("expire_time", expire_time);
         // frozen set, what this means ?
         ahr.put("frozen", suspendHold);
index ce42319..6eb2e2a 100644 (file)
@@ -88,12 +88,6 @@ public class HoldDetails extends Activity {
 
     private Button back;
 
-    private EditText phone_number;
-
-    private CheckBox phone_notification;
-
-    private CheckBox email_notification;
-
     private DatePickerDialog datePicker = null;
 
     private CheckBox suspendHold;
@@ -167,12 +161,7 @@ public class HoldDetails extends Activity {
         cancelHold = (Button) findViewById(R.id.cancel_hold_button);
         updateHold = (Button) findViewById(R.id.update_hold_button);
         back = (Button) findViewById(R.id.back_button);
-
-        phone_number = (EditText) findViewById(R.id.hold_contact_telephone);
-        phone_notification = (CheckBox) findViewById(R.id.hold_enable_phone_notification);
-        email_notification = (CheckBox) findViewById(R.id.hold_enable_email_notification);
         suspendHold = (CheckBox) findViewById(R.id.hold_suspend_hold);
-
         orgSelector = (Spinner) findViewById(R.id.hold_pickup_location);
         expiration_date = (EditText) findViewById(R.id.hold_expiration_date);
         thaw_date_edittext = (EditText) findViewById(R.id.hold_thaw_date);
@@ -185,8 +174,6 @@ public class HoldDetails extends Activity {
                     .setText(record.recordInfo.physical_description);
 
         // set record info
-        phone_notification.setChecked(record.phone_notification);
-        email_notification.setChecked(record.email_notification);
         suspendHold.setChecked(record.suspended);
 
         if (record.thaw_date != null) {
@@ -204,9 +191,6 @@ public class HoldDetails extends Activity {
         if (record.thaw_date == null)
             disableView(thaw_date_edittext);
 
-        if (!record.phone_notification)
-            disableView(phone_number);
-
         System.out.println(record.title + " " + record.author);
 
         back.setOnClickListener(new OnClickListener() {
@@ -301,9 +285,7 @@ public class HoldDetails extends Activity {
 
                 try {
                     accountAccess.updateHold(record.ahr, selectedOrgPos,
-                            email_notification.isChecked(), phone_notification
-                                    .isChecked(), phone_number.getText()
-                                    .toString(), suspendHold.isChecked(),
+                            suspendHold.isChecked(),
                             expire_date_s, thaw_date_s);
                 } catch (NoNetworkAccessException e) {
                     Utils.showNetworkNotAvailableDialog(context);
@@ -316,9 +298,6 @@ public class HoldDetails extends Activity {
                         if (accountAccess.authenticate())
                             accountAccess.updateHold(record.ahr,
                                     selectedOrgPos,
-                                    email_notification.isChecked(),
-                                    phone_notification.isChecked(),
-                                    phone_number.getText().toString(),
                                     suspendHold.isChecked(), expire_date_s,
                                     thaw_date_s);
                     } catch (Exception eauth) {
@@ -349,20 +328,6 @@ public class HoldDetails extends Activity {
             }
         });
 
-        phone_notification
-                .setOnCheckedChangeListener(new OnCheckedChangeListener() {
-
-                    @Override
-                    public void onCheckedChanged(CompoundButton buttonView,
-                            boolean isChecked) {
-
-                        if (isChecked) {
-                            enableView(phone_number);
-                        } else
-                            disableView(phone_number);
-                    }
-                });
-
         suspendHold.setOnCheckedChangeListener(new OnCheckedChangeListener() {
 
             @Override
index 82e5a28..be07f1c 100644 (file)
@@ -21,32 +21,22 @@ package org.evergreen.android.accountAccess.holds;
 
 import java.io.Serializable;
 import java.util.Date;
-import java.util.List;
 
+import org.evergreen.android.R;
 import org.evergreen.android.globals.GlobalConfigs;
 import org.evergreen.android.searchCatalog.RecordInfo;
 import org.opensrf.util.OSRFObject;
+import org.simpleframework.xml.stream.Position;
 
-public class HoldRecord implements Serializable {
+import android.content.res.Resources;
 
-    // metarecord
-    public static final int M = 0;
-    // record
-    public static final int T = 1;
-    // volume
-    public static final int V = 2;
-    // issuance
-    public static final int I = 3;
-    // copy
-    public static final int C = 4;
-    // part
-    public static final int P = 5;
+public class HoldRecord implements Serializable {
 
     private Integer requestLibID = null;
 
     private Integer pickupLibID = null;
 
-    public Integer holdType = null;
+    public String holdType = null;
     // id for target object
     public Integer target = null;
     public Date expire_time = null;
@@ -57,11 +47,6 @@ public class HoldRecord implements Serializable {
 
     public String types_of_resource;
 
-    /*
-     * Hold status holdStatus == 4 => AVAILABLE holdStatus == 3 => WAITING
-     * holdStatus <= 3 => TRANSIT
-     */
-
     // only for P types
     public String part_label = null;
 
@@ -76,7 +61,7 @@ public class HoldRecord implements Serializable {
 
     public boolean email_notification = false;
 
-    public boolean phone_notification = false;
+    public String phone_notification = null;
 
     public boolean suspended = false;
 
@@ -84,63 +69,58 @@ public class HoldRecord implements Serializable {
 
     public int pickup_lib;
 
-    public HoldRecord(OSRFObject ahr) {
+    public Integer potentialCopies;
 
-        this.target = ahr.getInt("target");
-        String type = ahr.getString("hold_type");
+    public Integer estimatedWaitInSeconds;
 
-        this.ahr = ahr;
+    public Integer queuePosition;
 
-        if (type.equals("M")) {
-            holdType = M;
-        } else if (type.equals("T")) {
-            holdType = T;
-        } else if (type.equals("V")) {
-            holdType = V;
-        } else if (type.equals("I")) {
-            holdType = I;
-        } else if (type.equals("C")) {
-            holdType = C;
-        } else if (type.equals("P"))
-            holdType = P;
-
-        this.expire_time = GlobalConfigs
-                .parseDate(ahr.getString("expire_time"));
+    public Integer totalHolds;
 
-        this.thaw_date = GlobalConfigs.parseDate(ahr.getString("thaw_date"));
-        String res = ahr.getString("email_notify");
+    public HoldRecord(OSRFObject ahr) {
 
-        if (res.equals("t"))
-            this.email_notification = true;
-        res = ahr.getString("phone_notify");
+        this.target = ahr.getInt("target");
+        this.holdType = ahr.getString("hold_type");
 
-        if (res != null)
-            if (res.equals("t"))
-                this.phone_notification = true;
+        this.ahr = ahr;
 
-        res = ahr.getString("frozen");
-        if (res != null)
-            if (res.equals("t"))
-                this.suspended = true;
+        this.expire_time = GlobalConfigs.parseDate(ahr.getString("expire_time"));
+        this.thaw_date = GlobalConfigs.parseDate(ahr.getString("thaw_date"));
+        this.email_notification = GlobalConfigs.parseBoolean(ahr.getString("email_notify"));
+        this.phone_notification = ahr.getString("phone_notify");
+        this.suspended = GlobalConfigs.parseBoolean(ahr.getString("frozen"));
         pickup_lib = ahr.getInt("pickup_lib");
 
     }
 
-    // based on status integer field retreive hold status in text
-    public String getHoldStatus() {
-
-        String holdStatus = "";
-
-        if (holdType == 7)
-            return "Suspended";
-        if (holdType == 4)
+    // Retreive hold status in text
+    public String getHoldStatus(Resources res) {
+        // Constants from Holds.pm and logic from hold_status.tt2
+        // -1 on error (for now),
+        //  1 for 'waiting for copy to become available',
+        //  2 for 'waiting for copy capture',
+        //  3 for 'in transit',
+        //  4 for 'arrived',
+        //  5 for 'hold-shelf-delay'
+        //  6 for 'canceled'
+        //  7 for 'suspended'
+        //  8 for 'captured, on wrong hold shelf'
+        if (status == 4) {
             return "Available";
-        if (holdType == 3)
-            return "Waiting";
-        if (holdType < 3)
-            return "Transit";
-
-        return holdStatus;
+        } else if (estimatedWaitInSeconds > 0) {
+            int days = (int)Math.ceil((double)estimatedWaitInSeconds / 86400.0);
+            return "Estimated wait "+days+" day wait";
+        } else if (status == 3 || status == 8) {
+            return "In Transit";
+        } else if (status < 3) {
+            return "Waiting for copy\n"
+                    + res.getQuantityString(R.plurals.number_of_holds, totalHolds, totalHolds) + " on "
+                    + res.getQuantityString(R.plurals.number_of_copies, potentialCopies, potentialCopies)
+                    + "\n"
+                    + "Queue position: " + queuePosition;
+        } else {
+            return "";
+        }
     }
 
 }
index 1ad7e7e..ce75475 100644 (file)
@@ -133,6 +133,7 @@ public class HoldsListView extends Activity {
                 try {
                     holdRecords = accountAccess.getHolds();
                 } catch (SessionNotFoundException e) {
+                    System.out.println("no session!");
                     // TODO other way?
                     try {
                         if (accountAccess.authenticate())
@@ -297,13 +298,10 @@ public class HoldsListView extends Activity {
 
             imageDownloader.download(imageResourceHref, hold_icon);
 
-            System.out.println("Image " + imageResourceHref + " Row "
-                    + record.title + " " + record.author + " "
-                    + record.getHoldStatus());
             // set raw information
             holdTitle.setText(record.title);
             holdAuthor.setText(record.author);
-            status.setText(record.getHoldStatus());
+            status.setText(record.getHoldStatus(getResources()));
 
             return row;
         }
index f0f2bc5..06cebce 100644 (file)
@@ -376,6 +376,11 @@ public class GlobalConfigs {
 
         return date;
     }
+    
+    // parse from opac methods query result to boolean
+    public static boolean parseBoolean(String boolString) {
+        return (boolString != null && boolString.equals("t"));
+    }
 
     public String getOrganizationName(int id) {