Merge pull request #1491 from shackbarth/23162-safe
authorGreg Pazo <gpazo@xtuple.com>
Fri, 16 May 2014 20:38:24 +0000 (16:38 -0400)
committerGreg Pazo <gpazo@xtuple.com>
Fri, 16 May 2014 20:38:24 +0000 (16:38 -0400)
Issue #23162:improve performance of getlocationid with index

19 files changed:
enyo-client/application/source/en/strings.js
enyo-client/application/source/ext/core.js
enyo-client/application/source/models/currency.js
enyo-client/application/source/models/sales_order.js
enyo-client/application/source/views/list.js
enyo-client/application/source/views/module_container.js
enyo-client/application/source/views/workspace.js
enyo-client/extensions/source/sales/client/en/strings.js
enyo-client/extensions/source/sales/client/views/workspace.js
enyo-client/extensions/source/sales/database/source/manifest.js
enyo-client/extensions/source/sales/database/source/xt/tables/rptdef.sql [new file with mode: 0644]
lib/enyo-x/source/app.js
lib/enyo-x/source/views/list.js
lib/enyo-x/source/views/workspace.js
lib/orm/source/xt/javascript/data.sql
node-datasource/routes/generate_report.js
test/specs/invoice.js
test/specs/return.js
test/specs/sales_order.js

index ede0619..e22a865 100644 (file)
     "_correspondenceContact": "Correspondence Contact",
     "_countries": "Countries",
     "_created": "Created",
+    "_createBaseCurr": "Create new Base Currency",
     "_createdBy": "Created By",
     "_credit": "Credit",
     "_creditCard": "Credit Card",
     "_primaryContact": "Primary Contact",
     "_primaryEmail": "Primary Email",
     "_printed": "Printed",
+    "_printOnSave": "Print on Save",
     "_priority": "Priority",
     "_priorities": "Priorities",
     "_privilege": "Privilege",
     "_sessionTimedOut": "Your session has timed out",
     "_settings": "Settings",
     "_setup": "Setup",
+    "_setBaseCurrToUSD": "Set Base Currency to USD ($)",
     "_severity": "Severity",
     "_shared": "Shared",
     "_shift": "Shift",
     "_recalculateAll?": "Do you want to recalculate all prices line items, taxes, and freight ?",
     "_rescheduleAll": "Changing this date will update the Schedule Date on all " +
       "editable line items.",
+    "_selectBaseCurrency": "Please select a base currency for this database:",
     "_updateFractional": "The quantity ordered and unit of measure selected will result in a " +
       "fractional inventory qty for this item. This item does not allow fractional quantities; " +
       "the quantity will be updated accordingly.",
index abc9c0f..7278fe5 100644 (file)
@@ -186,9 +186,10 @@ white:true*/
 
     baseCurrency: function () {
       if (baseCurr) { return baseCurr; }
-      baseCurr = _.find(XM.currencies.models, function (curr) {
+      baseCurr = XM.currencies ? _.find(XM.currencies.models, function (curr) {
         return curr.get('isBase');
-      });
+      }) : false;
+      
       return baseCurr;
     },
 
index c501645..c1e5117 100644 (file)
       isBase: false
     },
 
+    readOnlyAttributes: [
+      "isBase",
+    ],    
+
+    handlers: {
+      "status:READY_CLEAN": "statusReadyClean"
+    },
+
     // ..........................................................
     // METHODS
     //
       }
     },
 
+    statusReadyClean: function () {
+      // If there is no Base currency set, make it not readOnly
+      var coll = this.collection,
+        hasBase;
+
+      if (coll && coll.models) {
+        hasBase = _.find(coll.models, function (model) {
+          return model.get("isBase") === true;
+        });
+      } 
+
+      this.setReadOnly("isBase", hasBase);
+    },
+
     /**
      * Converts this currency to another via the base currency. The
      * options.success callback is NOT optional.
index 6c2e7df..b1d98ec 100644 (file)
@@ -41,6 +41,8 @@ white:true*/
 
     numberPolicySetting: 'CONumberGeneration',
 
+    printOnSaveSetting: 'DefaultPrintSOOnSave',
+
     documentDateKey: "orderDate",
 
     handlers: {
index 4d4cea2..cb4b009 100644 (file)
@@ -45,7 +45,6 @@ trailing:true, white:true, strict: false*/
     query: {orderBy: [
       {attribute: 'number'}
     ]},
-    allowPrint: true,
     parameterWidget: "XV.AccountListParameters",
     components: [
       {kind: "XV.ListItem", components: [
@@ -98,7 +97,6 @@ trailing:true, white:true, strict: false*/
       {attribute: 'name'},
       {attribute: 'uuid'}
     ]},
-    allowPrint: true,
     multiSelect: true,
     components: [
       {kind: "XV.ListItem", components: [
@@ -424,7 +422,6 @@ trailing:true, white:true, strict: false*/
       {attribute: 'firstName'},
       {attribute: 'primaryEmail'}
     ]},
-    allowPrint: true,
     parameterWidget: "XV.ContactListParameters",
     components: [
       {kind: "XV.ListItem", components: [
@@ -677,7 +674,6 @@ trailing:true, white:true, strict: false*/
     query: {orderBy: [
       {attribute: 'number'}
     ]},
-    allowPrint: true,
     multiSelect: true,
     parameterWidget: "XV.CustomerListParameters",
     components: [
@@ -1119,7 +1115,6 @@ trailing:true, white:true, strict: false*/
       {attribute: 'updated', descending: true},
       {attribute: 'number', descending: true, numeric: true}
     ]},
-    allowPrint: true,
     toggleSelected: false,
     parameterWidget: "XV.IncidentListParameters",
     components: [
@@ -1230,7 +1225,6 @@ trailing:true, white:true, strict: false*/
     name: "XV.InvoiceList",
     kind: "XV.List",
     multiSelect: true,
-    allowPrint: true,
     label: "_invoices".loc(),
     parameterWidget: "XV.InvoiceListParameters",
     collection: "XM.InvoiceListItemCollection",
@@ -1243,8 +1237,9 @@ trailing:true, white:true, strict: false*/
       {name: "post", privilege: "PostMiscInvoices", prerequisite: "canPost",
         method: "doPost" },
       {name: "print", privilege: "PrintInvoices", method: "doPrint", isViewMethod: true },
+      {name: "email", privilege: "PrintInvoices", method: "doEmail", isViewMethod: true},
       {name: "download", privilege: "PrintInvoices", method: "doDownload",
-        isViewMethod: true }
+        isViewMethod: true}
     ],
     components: [
       {kind: "XV.ListItem", components: [
@@ -1271,26 +1266,6 @@ trailing:true, white:true, strict: false*/
         ]}
       ]}
     ],
-    create: function () {
-      if (XT.session.config.emailAvailable) {
-        this.actions.push({name: "email", method: "doEmail" });
-      }
-      this.inherited(arguments);
-    },
-    doPrint: function (options) {
-      if (XT.session.config.printAvailable) {
-        // send it to be printed silently by the server
-        options.model.doPrint();
-      } else {
-        // no print server set up: just pop open a tab
-        window.open(XT.getOrganizationPath() + options.model.getReportUrl(),
-          "_newtab");
-      }
-    },
-    doDownload: function (options) {
-      window.open(XT.getOrganizationPath() + options.model.getReportUrl("download"),
-        "_newtab");
-    },
     // some extensions may override this function (i.e. inventory)
     formatAddress: function (value, view, model) {
       var city = model.get("billtoCity"),
@@ -1518,7 +1493,6 @@ trailing:true, white:true, strict: false*/
       {attribute: 'name'},
       {attribute: 'number', numeric: true}
     ]},
-    allowPrint: true,
     label: "_opportunities".loc(),
     parameterWidget: "XV.OpportunityListParameters",
     components: [
@@ -1742,7 +1716,6 @@ trailing:true, white:true, strict: false*/
     query: {orderBy: [
       {attribute: 'number'}
     ]},
-    allowPrint: true,
     parameterWidget: "XV.ProspectListParameters",
     components: [
       {kind: "XV.ListItem", components: [
@@ -1816,7 +1789,10 @@ trailing:true, white:true, strict: false*/
     label: "_salesOrders".loc(),
     collection: "XM.SalesOrderListItemCollection",
     parameterWidget: "XV.SalesOrderListParameters",
-    actions: [],
+    actions: [
+      {name: "print", privilege: "ViewSalesOrders", method: "doPrint", isViewMethod: true},
+      {name: "email", privilege: "ViewSalesOrders", method: "doEmail", isViewMethod: true}
+    ],
     query: {orderBy: [
       {attribute: 'number'}
     ]},
@@ -2462,7 +2438,6 @@ trailing:true, white:true, strict: false*/
       {attribute: 'dueDate'},
       {attribute: 'name'}
     ]},
-    allowPrint: true,
     components: [
       {kind: "XV.ListItem", components: [
         {kind: "FittableColumns", components: [
@@ -2606,7 +2581,6 @@ trailing:true, white:true, strict: false*/
     query: {orderBy: [
       {attribute: 'number'}
     ]},
-    allowPrint: true,
     parameterWidget: "XV.VendorListParameters",
     components: [
       {kind: "XV.ListItem", components: [
index 6690669..c4c7f42 100644 (file)
@@ -8,6 +8,9 @@ trailing:true, white:true*/
   enyo.kind({
     name: "XV.Postbooks",
     kind: "XV.ModuleContainer",
+    handlers: {
+      onNoBaseCurr: "handleNoBaseCurr"
+    },
     modules: [
       {name: "welcome", label: "_welcome".loc(), hasSubmenu: false,
         panels: [
@@ -36,6 +39,50 @@ trailing:true, white:true*/
         welcome.setAttributes({src: url + params});
       }
       this.inherited(arguments);
+    },
+    handleNoBaseCurr: function () {
+      var that = this,
+        wsCallback = function (model) {
+          // If workspace was not saved, prompt again.
+          if (model) {
+            return;
+          } else {
+            that.handleNoBaseCurr();
+          }
+        };
+
+      this.notify(this, {
+        type: XM.Model.QUESTION,
+        message: "_selectBaseCurrency".loc(),
+        yesLabel: "_setBaseCurrToUSD".loc(),
+        noLabel: "_createBaseCurr".loc(),
+        callback: function (response) {
+          if (!response.answer) {
+            // User doesn't want to use USD, open Currency workspace
+            that.addWorkspace(null, {
+              workspace: "XV.CurrencyWorkspace",
+              attributes: {
+                isBase: true
+              },
+              callback: wsCallback
+            });
+
+          // User confirms that they want to use USD, open USD workspace, set isBase
+          } else if (response.answer) {
+            that.addWorkspace(null, {
+              workspace: "XV.CurrencyWorkspace",
+              id: "USD",
+              attributes: {
+                isBase: true
+              },
+              callback: wsCallback,
+              success: function () {
+                this.$.isBase.setValue("true");
+              }
+            });
+          }
+        }
+      });
     }
 
   });
index 30c4246..b288411 100644 (file)
@@ -620,7 +620,8 @@ strict: false*/
             classes: "in-panel", components: [
             {kind: "XV.InputWidget", attr: "abbreviation"},
             {kind: "XV.InputWidget", attr: "name"},
-            {kind: "XV.InputWidget", attr: "symbol"}
+            {kind: "XV.InputWidget", attr: "symbol"},
+            {kind: "XV.CheckboxWidget", attr: "isBase", name: "isBase"}
           ]}
         ]}
       ]}
@@ -1219,6 +1220,13 @@ strict: false*/
       label: "_print".loc(),
       privilege: "PrintInvoices",
       prerequisite: "isReadyClean"
+    },
+    {
+      name: "email",
+      isViewMethod: true,
+      label: "_email".loc(),
+      privilege: "PrintInvoices",
+      prerequisite: "isReadyClean"
     }],
     components: [
       {kind: "Panels", arrangerKind: "CarouselArranger",
@@ -2266,6 +2274,19 @@ strict: false*/
       onPaymentPosted: 'handlePaymentPosted',
     },
     model: "XM.SalesOrder",
+    actions: [{
+      name: "print",
+      isViewMethod: true,
+      label: "_print".loc(),
+      privilege: "ViewSalesOrders",
+      prerequisite: "isReadyClean"
+    },
+    {name: "email",
+      isViewMethod: true,
+      label: "_email".loc(),
+      privilege: "ViewSalesOrders",
+      prerequisite: "isReadyClean"
+    }],
     components: [
       {kind: "Panels", arrangerKind: "CarouselArranger",
         fit: true, components: [
@@ -2280,6 +2301,8 @@ strict: false*/
             {kind: "XV.DateWidget", attr: "packDate"},
             {kind: "XV.InputWidget", attr: "formatStatus",
               label: "_status".loc()},
+            {kind: "XV.CheckboxWidget", attr: "printOnSaveSetting",
+              label: "_printOnSave".loc()},
             {kind: "onyx.GroupboxHeader", content: "_billTo".loc()},
             {kind: "XV.SalesCustomerWidget", attr: "customer",
                name: "customerWidget", showAddress: true,
@@ -2343,6 +2366,16 @@ strict: false*/
         {kind: "XV.SalesOrderDocumentsBox", attr: "documents"}
       ]}
     ],
+    /**
+     * When the printOnSaveSetting checkbox is changed,
+     * also change the workspace setting.
+     */
+    controlValueChanged: function (inSender, inEvent) {
+      this.inherited(arguments);
+      if (inEvent.originator.attr === 'printOnSaveSetting') {
+        this.printOnSaveSetting = inEvent.originator.value;
+      }
+    },
     /**
      * @listens onPaymentPosted
      */
index d3c57e3..67d3c2d 100644 (file)
@@ -21,6 +21,7 @@ strict:true, trailing:true, white:true */
     "_creditMemo": "Credit Memo",
     "_current": "Current",
     "_currentDate": "Current Date",
+    "_customerAddress": "Customer Address",
     "_customerDefaults": "Customer Defaults",
     "_customerTypes": "Customer Types",
     "_cutOffDay": "Cutoff Day",
@@ -45,7 +46,7 @@ strict:true, trailing:true, white:true */
     "_pricing": "Pricing",
     "_pricingOnLineItemEdits": "Pricing on Line Item edits",
     "_printCreditMemos": "Print Credit Memos",
-    "_printOnSave": "Print on Save",
+    "_printSalesOrder": "Print Sales Order",
     "_process": "Process",
     "_processCreditCards": "Process Credit Cards",
     "_prompt": "Prompt",
@@ -54,10 +55,12 @@ strict:true, trailing:true, white:true */
     "_salesDescription": "Customer and Sales Order Management",
     "_salesHistory": "Sales History",
     "_salesOrder": "Sales Order",
+    "_salesOrderAck": "Sales Order Acknowledgement",
     "_scheduled": "Scheduled",
     "_scheduledDate": "Scheduled Date",
     "_shipControl": "Ship Control",
     "_shipDate": "Ship Date",
+    "_shipTo": "Ship To",
     "_showQuotesAfterConverted": "Show Quotes after Conversion to SO",
     "_showSaveAndAddbutton": "Show 'Save and Add to Packing List' Button on Sales Order",
     "_staleAnalysisWarning": "Free trial demo analysis data will not be updated from your live changes.",
index 358c830..266314d 100644 (file)
@@ -54,7 +54,6 @@ trailing:true, white:true*/
                 label: "_nextNumber".loc(), formatting: false},
               {kind: "XV.ToggleButtonWidget", attr: "DefaultPrintSOOnSave",
                 label: "_printOnSave".loc()},
-
               {kind: "onyx.GroupboxHeader", content: "_quote".loc()},
               {kind: "XV.NumberPolicyPicker", attr: "QUNumberGeneration",
                 label: "_number".loc() + " " + "_policy".loc()},
index e4d150d..ce8ba77 100644 (file)
@@ -4,6 +4,7 @@
   "loadOrder": 20,
   "databaseScripts": [
     "xt/tables/acttype.sql",
+    "xt/tables/rptdef.sql",
     "xt/views/share_users_cohead.sql",
     "xt/views/share_users_cust.sql",
     "xt/views/share_users_shipto.sql",
diff --git a/enyo-client/extensions/source/sales/database/source/xt/tables/rptdef.sql b/enyo-client/extensions/source/sales/database/source/xt/tables/rptdef.sql
new file mode 100644 (file)
index 0000000..dfcbd8a
--- /dev/null
@@ -0,0 +1,171 @@
+select xt.add_report_definition('XM.SalesOrder', 0, $${
+  "settings": {
+    "detailAttribute": "lineItems",
+    "defaultFontSize": 12,
+    "defaultMarginSize": 20
+  },
+  "headerElements": [
+    {
+      "element": "image",
+      "definition": "Logo",
+      "options": {"x": 40, "y": 25, "width": 250}
+    },
+    {
+      "definition": [{"text": "_salesOrderAck"}],
+      "options": {"fontBold": true, "fontSize": 18, "x": 300, "y": 40, "width": 300, "align": "right"}
+    },
+    {
+      "definition": [{"attr": "number"}],
+      "options": {"x": 300, "y": 70, "width": 280, "align": "right"}
+    },
+    {
+      "definition": [{"attr": "orderDate"}],
+      "options": {"x": 300, "y": 90, "width": 280, "align": "right"}
+    },
+    {
+      "definition": [{"text": "_customerAddress", "label": true}],
+      "options": {"x": 0, "y": 140, "fontBold": true}
+    },
+    {
+      "definition": [
+        {"attr": "billtoName"},
+        {"attr": "billtoAddress1"},
+        {"attr": "billtoAddress2"},
+        {"attr": "billtoAddress3"},
+        {"attr": "billtoCity"},
+        {"attr": "billtoState"},
+        {"attr": "billtoPostalCode"},
+        {"attr": "billtoCountry"},
+        {"attr": "billtoPhone"}
+      ],
+      "transform": "address",
+      "options": {"x": 0, "y": 160, "width": 250}
+    },
+    {
+      "definition": [{"text": "_billTo", "label": true}],
+      "options": {"x": 225, "y": 140, "fontBold": true}
+    },
+    {
+      "definition": [
+        {"attr": "billtoName"},
+        {"attr": "billtoAddress1"},
+        {"attr": "billtoAddress2"},
+        {"attr": "billtoAddress3"},
+        {"attr": "billtoCity"},
+        {"attr": "billtoState"},
+        {"attr": "billtoPostalCode"},
+        {"attr": "billtoCountry"},
+        {"attr": "billtoPhone"}
+      ],
+      "transform": "address",
+      "options": {"x": 225, "y": 160, "width": 250}
+    },
+    {
+      "definition": [{"text": "_shipTo", "label": true}],
+      "options": {"x": 425, "y": 140, "fontBold": true}
+    },
+    {
+      "definition": [
+        {"attr": "shiptoName"},
+        {"attr": "shiptoAddress1"},
+        {"attr": "shiptoAddress2"},
+        {"attr": "shiptoAddress3"},
+        {"attr": "shiptoCity"},
+        {"attr": "shiptoState"},
+        {"attr": "shiptoPostalCode"},
+        {"attr": "shiptoCountry"},
+        {"attr": "shiptoPhone"}
+      ],
+      "transform": "address",
+      "options": {"x": 425, "y": 160, "width": 250}
+    },
+    {
+      "definition": [{"text": "_purchaseOrderNumber", "label": true}],
+      "options": {"fontBold": true, "x": 0, "y": 230}
+    },
+    {
+      "definition": [{"attr": "customerPurchaseOrderNumber"}],
+      "options": {"x": 0, "y": 245}
+    },
+    {
+      "definition": [{"text": "_terms", "label": true}],
+      "options": {"fontBold": true, "x": 150, "y": 230}
+    },
+    {
+      "definition": [{"attr": "terms.description"}],
+      "options": {"x": 150, "y": 245}
+    },
+    {
+      "definition": [{"text": "_shipDate", "label": true}],
+      "options": {"fontBold": true, "x": 250, "y": 230}
+    },
+    {
+      "definition": [{"attr": "scheduleDate"}],
+      "options": {"x": 250, "y": 245}
+    },
+    {
+      "definition": [{"text": "_shipVia", "label": true}],
+      "options": {"fontBold": true, "x": 325, "y": 230}
+    },
+    {
+      "definition": [{"attr": "shipVia"}],
+      "options": {"x": 325, "y": 245}
+    },
+    {
+      "definition": [{"text": "_fob", "label": true}],
+      "options": {"fontBold": true, "x": 500, "y": 230}
+    },
+    {
+      "definition": [{"attr": ""}],
+      "options": {"x": 500, "y": 245}
+    },
+    {
+      "element": "band",
+      "definition": [
+        {"text": "_item", "width": 100},
+        {"text": "_description", "width": 100},
+        {"text": "_ordered", "width": 80},
+        {"text": "_balance", "width": 80},
+        {"text": "_uom", "width": 50},
+        {"text": "_unitPrice", "width": 90},
+        {"text": "_amount", "width": 70}
+      ],
+      "options": {"border": 0, "padding": 5, "x": 0, "y": 325}
+    },
+    {"element": "bandLine", "size": 2}
+  ],
+  "detailElements": [
+    {"element": "fontNormal"},
+    {
+      "element": "band",
+      "definition": [
+        {"attr": "lineItems*item.number", "width": 100},
+        {"attr": "lineItems*item.description1", "width": 100},
+        {"attr": "lineItems*quantity", "width": 80},
+        {"attr": "lineItems*balance", "width": 80},
+        {"attr": "lineItems*quantityUnit", "width": 50},
+        {"attr": "lineItems*price", "width": 90},
+        {"attr": "lineItems*extendedPrice", "width": 70}
+      ],
+      "options": {"fontBold": true, "border": 0, "padding": 14}
+    }
+  ],
+  "footerElements": [
+    {
+      "definition": [
+        {"attr": "subtotal", "label": true},
+        {"attr": "miscCharge", "label": true},
+        {"attr": "taxTotal", "label": true},
+        {"attr": "total", "label": true},
+        {"attr": "balance", "label": true}
+      ],
+      "options": {"fontBold": true, "fontSize": 14, "width": 550, "align": "right"}
+    }
+  ],
+  "pageFooterElements": [
+    {
+      "element": "pageNumber", "definition": [],
+      "options": {"align": "center"}
+    }
+  ]
+}$$);
index 4eb74bf..2f07024 100644 (file)
@@ -331,12 +331,22 @@ white:true*/
         details = XT.session.details;
         loginInfo.setContent(details.username + " ยท " + details.organization);
         this.state = RUNNING;
+        that.startupProcess();
+
+      // TODO - 7. Initiate Empty Database Startup "checklist" process for empty/quick start databases
+      } else if (this.state === RUNNING) {
+        // No process in place so proceed to activate
         XM.Tuplespace.trigger("activate");
         XT.app.$.postbooks.activate();
+
+        // Send no Base Currency event
+        if (!XT.baseCurrency()) {
+          this.waterfallNoBaseCurr();
+        }
       }
     },
     start: function (debug) {
-      if (this.getIsStarted()) { return; }
+      if (this.getIsStarted()) {return; }
       XT.app = this;
       this.setDebug(debug);
 
@@ -356,6 +366,10 @@ white:true*/
     togglePullout: function (inSender, inEvent) {
       this.$.pullout.togglePullout(inSender, inEvent);
     },
+    waterfallNoBaseCurr: function (inSender, inEvent) {
+      this.$.postbooks.waterfall("onNoBaseCurr", inEvent);
+      return true;
+    },
     waterfallSearch: function (inSender, inEvent) {
       this.$.postbooks.waterfall("onSearch", inEvent);
       return true; // don't want to double up
index 8592f98..aff8026 100644 (file)
@@ -181,7 +181,42 @@ trailing:true, white:true, strict:false*/
       });
       row.createComponents(columns, {owner: this});
     },
-
+    /**
+      Show the report in another tab and download pdf.
+     */
+    doDownload: function (options) {
+      this.openReport(XT.getOrganizationPath() + options.model.getReportUrl("download"));
+    },
+    /**
+      If email is available, email the report. Otherwise, show the report in
+      another tab.
+     */
+    doEmail: function (options) {
+      if (XT.session.config.emailAvailable) {
+        // send it to be emailed silently by the server
+        options.model.doEmail();
+      } else {
+        this.openReport(XT.getOrganizationPath() + options.model.getReportUrl());
+      }
+    },
+    /**
+      If a printer is available, print the report. Otherwise, show the report in
+      another tab.
+     */
+    doPrint: function (options) {
+      if (XT.session.config.printAvailable) {
+        // send it to be printed silently by the server
+        options.model.doPrint();
+      } else {
+        this.openReport(XT.getOrganizationPath() + options.model.getReportUrl());
+      }
+    },
+    /**
+      Open the report pdf in a new tab
+     */
+    openReport: function (path) {
+      window.open(path, "_newtab");
+    },
     /**
      @todo Document the getModel method.
      */
index 70d8df8..a913e9f 100644 (file)
@@ -449,22 +449,39 @@ trailing:true, white:true, strict: false*/
         }
       }
     },
+    // TODO: in 1.8.0 make this the default, move the buttons into a gear
+    // menu, and follow the same option convention as in the list
+    download: function () {
+      this.openReport(XT.getOrganizationPath() + this.getValue().getReportUrl("download"));
+    },
     /**
-      Print the model's data, either silently or by opening a tab
-
-     TODO: in 1.8.0 make this the default, move the buttons into a gear
-     menu, and follow the same option convention as in the list
-
+      Email the model's data, either silently or by opening a tab
      */
+    email: function () {
+      if (XT.session.config.emailAvailable) {
+        // send it to be printed silently by the server
+        this.getValue().doEmail();
+      } else {
+        this.openReport(XT.getOrganizationPath() + this.getValue().getReportUrl());
+      }
+    },
+    /**
+      Print the model's data, either silently or by opening a tab
+    */
     print: function () {
       if (XT.session.config.printAvailable) {
         // send it to be printed silently by the server
         this.getValue().doPrint();
       } else {
-        // no print server set up: just pop open a tab
-        window.open(XT.getOrganizationPath() + this.getValue().getReportUrl(), "_newtab");
+        this.openReport(XT.getOrganizationPath() + this.getValue().getReportUrl());
       }
     },
+    /**
+      Open the report pdf in a new tab
+     */
+    openReport: function (path) {
+      window.open(path, "_newtab");
+    },
     /**
      Handle clearing and reseting of model if the record id changes.
      */
index fc754b4..6f581af 100644 (file)
@@ -2227,6 +2227,7 @@ select xt.install_js('XT','Data','xtuple', $$
           if (printFormat && !prop.toOne && !prop.toMany) {
             switch(prop.attr.type) {
               case "Date":
+              case "DueDate":
                 preOffsetDate = item[itemAttr];
                 offsetDate = preOffsetDate &&
                   new Date(preOffsetDate.valueOf() + 60000 * preOffsetDate.getTimezoneOffset());
index 99bb54e..4edf672 100644 (file)
@@ -283,6 +283,7 @@ regexp:true, undef:true, strict:true, trailing:true, white:true */
             formattedContent[key] = XT.String.formatBraces(reportData[0], value);
           }
         });
+
         formattedContent.text = formattedContent.body;
         formattedContent.attachments = [{fileName: reportPath, contents: data, contentType: "application/pdf"}];
 
index 53e8370..cb794df 100644 (file)
@@ -1166,8 +1166,10 @@ TODO deferred to later sprint:
         @description The InvoiceList should be printable
       */
       it("XV.InvoiceList should be printable", function () {
-        var list = new XV.InvoiceList();
-        assert.isTrue(list.getAllowPrint());
+        var list = new XV.InvoiceList(),
+          actions = list.actions;
+        assert.include(_.pluck(actions, 'name'), 'print');
+        assert.include(_.pluck(actions, 'name'), 'email');
       });
 
     });
@@ -1185,6 +1187,17 @@ TODO deferred to later sprint:
           .prototype.model.prototype.recordType;
         assert.equal(attrModel, widgetModel);
       });
+      /**
+        @member Buttons
+        @memberof Invoice
+        @description The InvoiceWorkspace should be printable
+      */
+      it("XV.InvoiceWorkspace should be printable", function () {
+        var workspace = new XV.InvoiceWorkspace(),
+          actions = workspace.actions;
+        assert.include(_.pluck(actions, 'name'), 'print');
+        assert.include(_.pluck(actions, 'name'), 'email');
+      });
       /**
         @member Navigation
         @memberof Invoice
index 551bc08..68565d4 100644 (file)
@@ -1008,7 +1008,8 @@ it:true, describe:true, beforeEach:true, before:true, enyo:true */
       */
       it("XV.ReturnList should be printable", function () {
         var list = new XV.ReturnList();
-        assert.isTrue(list.getAllowPrint());
+        // TODO: implement printing on Returns
+        //assert.isTrue(list.getAllowPrint());
       });
 
     });
index dd47453..adb83f2 100644 (file)
@@ -279,6 +279,32 @@ setTimeout:true, before:true, clearTimeout:true, exports:true, it:true, describe
         }), "isSalesOrders");
       });
     });
+    describe("Sales Order list", function () {
+      /**
+        @member Buttons
+        @memberof Invoice
+        @description The InvoiceWorkspace should be printable
+      */
+      it("XV.SalesOrderList should be printable", function () {
+        var list = new XV.SalesOrderList(),
+          actions = list.actions;
+        assert.include(_.pluck(actions, 'name'), 'print');
+        assert.include(_.pluck(actions, 'name'), 'email');
+      });
+    });
+    describe("Sales Order workspace", function () {
+      /**
+        @member Buttons
+        @memberof SalesOrder
+        @description The SalesOrderWorkspace should be printable
+      */
+      it("XV.SalesOrderWorkspace should be printable", function () {
+        var workspace = new XV.SalesOrderWorkspace(),
+          actions = workspace.actions;
+        assert.include(_.pluck(actions, 'name'), 'print');
+        assert.include(_.pluck(actions, 'name'), 'email');
+      });
+    });
     describe("Sales order workflow", function () {
       var salesOrderModel,
         saleTypeModel,