fix conflicts
authorLinda Nichols <lynnaloo@gmail.com>
Thu, 5 Jun 2014 15:54:11 +0000 (15:54 +0000)
committerLinda Nichols <lynnaloo@gmail.com>
Thu, 5 Jun 2014 15:54:11 +0000 (15:54 +0000)
34 files changed:
README.md
enyo-client/application/source/models/sales_order_base.js
enyo-client/application/source/views/list.js
enyo-client/database/source/xm/javascript/item_site.sql
foundation-database/public/functions/calcsalesorderamt.sql
foundation-database/public/functions/convertquote.sql
foundation-database/public/functions/createpurchasetosale.sql
foundation-database/public/functions/itemcost.sql
foundation-database/public/functions/postcounttaglocation.sql
foundation-database/public/functions/thawitemsite.sql
foundation-database/public/tables/metasql/projects-detail.mql
foundation-database/public/tables/metasql/quoteItems-list.mql
foundation-database/public/tables/metasql/salesOrderItems-list.mql
foundation-database/public/tables/taxpay.sql
lib/enyo-x/source/less/dashboard.less
lib/enyo-x/source/less/grid.less
lib/enyo-x/source/less/relations.less
lib/enyo-x/source/stylesheets/screen.css
lib/enyo-x/source/widgets/relation.js
package.json
scripts/export_dictionary.js
scripts/lib/build_database.js
scripts/lib/build_database_util.js
scripts/lib/build_dictionary.js
scripts/release_build.sh
scripts/xml/distribution_install.xml
scripts/xml/distribution_package.xml
scripts/xml/postbooks_package.xml
scripts/xml/xtmfg_install.xml
scripts/xml/xtmfg_package.xml
test/database/joins.js
test/database/setmetric.js [new file with mode: 0644]
test/extensions/sales/sales_order_workspace.js
test/lib/smoke.js

index 4e348e1..879f68f 100644 (file)
--- a/README.md
+++ b/README.md
@@ -45,7 +45,7 @@ a convenient, well-documented starting point into our project.
 ### Installing this project
 
 The best way to start coding on our stack is to use our
-[Vagrant setup](https://github.com/xtuple/xtuple-vagrant/blob/master/README.md).
+[Vagrant setup](https://github.com/xtuple/xtuple/wiki/Become-an-xTuple-Developer!).
 
 ### Release Notes
 
index 2e441ba..efee949 100644 (file)
@@ -1,7 +1,7 @@
 /*jshint indent:2, curly:true, eqeqeq:true, immed:true, latedef:true,
 newcap:true, noarg:true, regexp:true, undef:true, strict:true, trailing:true,
 white:true*/
-/*global XT:true, XM:true, Backbone:true, _:true */
+/*global XT:true, XM:true, Backbone:true, _:true, async:true */
 
 (function () {
   "use strict";
@@ -901,60 +901,58 @@ white:true*/
       // Confirm the user really wants to reschedule, then check whether all lines
       // can be updated to the requested schedule date
       options.callback = function (response) {
-        var counter = lineItems.length,
-          custOptions = {},
-          results = [],
-          id,
-          reschedule = function (ids) {
-            _.each(ids, function (id) {
+        var processLine,
+          finish,
+          reschedule = function (results) {
+            _.each(results, function (id) {
               lineItems.get(id).set("scheduleDate", scheduleDate);
             });
           };
 
         if (response.answer) {
-          // Callback for each check
-          custOptions.success = function (canPurchase) {
-            counter--;
-            if (canPurchase) { results.push(id); }
-
-            // If all items have been checked, proceed
-            if (!counter) {
-
-              // First check for mix of items that can be rescheduled and not
-              // If partial, then ask if they only want to reschedule partial
-              if (results.length && results.length !== lineItems.length) {
-                message = "_partialReschedule".loc() + "_continue?".loc();
-                options.callback = function (response) {
-                  if (response.answer) { reschedule(results); }
-
-                  // Recalculate the date because some lines may not have changed
-                  that.calculateScheduleDate();
-                };
-                that.notify(message, options);
-
-              // If we have results, then reschedule all of them
-              } else if (results.length) {
-                reschedule(results);
-
-              // No lines can be rescheduled, just tell user "no can do"
-              } else {
-                that.notify("_noReschedule".loc());
-                that.calculateScheduleDate(); // Recalculate the date
-              }
+          processLine = function (line, done) {
+            var custOptions = {},
+              item = line.getValue("item");
+            custOptions.success = function (canPurchase) {
+              if (canPurchase) {
+                done(null, {
+                  id: line.id
+                });
+              } else { done(); }
+              return;
+            };
+
+            customer.canPurchase(item, scheduleDate, custOptions);
+          };
+
+          finish = function (err, results) {
+            // First check for mix of items that can be rescheduled and not
+            // If partial, then ask if they only want to reschedule partial
+            if (results.length && results.length !== lineItems.length) {
+              message = "_partialReschedule".loc() + "_continue?".loc();
+              options.callback = function (response) {
+                if (response.answer) { reschedule(results); }
+
+                // Recalculate the date because some lines may not have changed
+                that.calculateScheduleDate();
+              };
+              that.notify(message, options);
+
+            // If we have results, then reschedule all of them
+            } else if (results.length) {
+              reschedule(results);
+
+            // No lines can be rescheduled, just tell user "no can do"
+            } else {
+              that.notify("_noReschedule".loc());
+              that.calculateScheduleDate(); // Recalculate the date
             }
           };
 
-          // Loop through each item and see if we can sell on
-          // requested date
-          _.each(lineItems.models, function (line) {
-            var item = line.getValue("item");
-            id = line.id;
-            customer.canPurchase(item, scheduleDate, custOptions);
-          });
+          async.mapSeries(lineItems.models, processLine, finish);
         }
       };
       this.notify(message, options);
-
     },
 
     /**
@@ -1902,7 +1900,9 @@ white:true*/
         }
         parent.calculateScheduleDate();
 
-        this.set("site", parent.get("site") || XT.defaultSite());
+        if (!this.get("site")) {
+          this.set("site", parent.get("site") || XT.defaultSite());
+        }
       }
     },
 
index 161d7f9..90c363a 100644 (file)
@@ -2528,6 +2528,8 @@ trailing:true, white:true, strict: false*/
       ]}
     ]
   });
+  
+  XV.registerModelList("XM.UserAccountRelation", "XV.UserAccountList");
 
   // ..........................................................
   // STATES AND COUNTRIES
index 0014b8d..5ddbacd 100644 (file)
@@ -64,6 +64,7 @@ select xt.install_js('XM','ItemSite','xtuple', $$
       },
       itemJoinMatches,
       itemJoinTable,
+      joinTables = [],
       keySearch = false,
       extra = "",
       qry,
@@ -72,9 +73,9 @@ select xt.install_js('XM','ItemSite','xtuple', $$
       idParams = [],
       etags,
       sqlCount,
-      sql1 = 'select pt1.%3$I as id ' +
+      sql1 = 'select pt1.id ' +
              'from ( ' +
-             'select t1.* as id ' +
+             'select t1.%3$I as id {groupColumns} ' +
              'from %1$I.%2$I t1 {joins} ' +
              'where {conditions} {extra}',
       sql2 = 'select * from %1$I.%2$I where id in ({ids}) {orderBy}';
@@ -89,14 +90,14 @@ select xt.install_js('XM','ItemSite','xtuple', $$
           keySearch = param.value;
           sql1 += ' and t1.%4$I in (select item_id from item where item_number ~^ ${p1} or item_upccode ~^ ${p1}) ' +
             'union ' +
-            'select t1.* ' +
+            'select t1.%3$I as id {groupColumns} ' +
             'from %1$I.%2$I t1 {joins} ' +
             ' join itemalias on t1.%4$I=itemalias_item_id ' +
             '   and itemalias_crmacct_id is null ' +
             'where {conditions} {extra} ' +
             ' and (itemalias_number ~^ ${p1}) ' +
             'union ' +
-            'select t1.* ' +
+            'select t1.%3$I as id {groupColumns}  ' +
             'from %1$I.%2$I t1 {joins} ' +
             ' join itemalias on t1.%4$I=itemalias_item_id ' +
             '   and itemalias_crmacct_id={accountId} ' +
@@ -137,6 +138,9 @@ select xt.install_js('XM','ItemSite','xtuple', $$
       if (itemJoinMatches && itemJoinMatches.length) {
         itemJoinTable = itemJoinMatches[0].match(/(jt\d+)/g);
       }
+
+      /* Get all join table names. */
+      joinTables = clause.joins.match(/(jt\d+)/g).unique();
     }
 
     if (!itemJoinTable) {
@@ -214,7 +218,7 @@ select xt.install_js('XM','ItemSite','xtuple', $$
     }
 
     sql1 = XT.format(
-      sql1 += ') pt1 group by pt1.%3$I{groupBy} {orderBy} %5$s %6$s;',
+      sql1 += ') pt1 group by pt1.id {groupBy} {orderBy} %5$s %6$s;',
       [tableNamespace, table, idColumn, backingTypeJoinColumn, limit, offset]
     );
 
@@ -225,6 +229,9 @@ select xt.install_js('XM','ItemSite','xtuple', $$
       clause.orderByColumns = XT.format('order by t1.%1$I', [idColumn]);
     }
 
+    /* Set columns to include in sub query unions before replacing table alias. */
+    clause.joinGroupColumns = clause.groupByColumns || '';
+
     /* Change table reference in group by and order by to pt1. */
     if (clause.groupByColumns && clause.groupByColumns.length) {
       clause.groupByColumns = clause.groupByColumns.replace(/t1./g, 'pt1.');
@@ -232,12 +239,20 @@ select xt.install_js('XM','ItemSite','xtuple', $$
     if (clause.orderByColumns && clause.orderByColumns.length) {
       clause.orderByColumns = clause.orderByColumns.replace(/t1./g, 'pt1.');
     }
+    if (joinTables.length) {
+      for (var j=0; j < joinTables.length; j++) {
+        var regex = new RegExp(joinTables + '.', 'g');
+        clause.groupByColumns = clause.groupByColumns.replace(regex, 'pt1.');
+        clause.orderByColumns = clause.orderByColumns.replace(regex, 'pt1.');
+      }
+    }
 
     /* Query the model */
     sql1 = sql1.replace(/{conditions}/g, clause.conditions)
              .replace(/{extra}/g, extra)
              .replace(/{joins}/g, clause.joins)
              .replace(/{groupBy}/g, clause.groupByColumns)
+             .replace(/{groupColumns}/g, clause.joinGroupColumns)
              .replace('{orderBy}', clause.orderByColumns)
              .replace('{accountId}', accountId)
              .replace(/{p1}/g, clause.parameters.length + 1)
@@ -257,6 +272,10 @@ select xt.install_js('XM','ItemSite','xtuple', $$
       XT.debug(sql1.slice(1000, 1500));
       XT.debug(sql1.slice(1500, 2000));
       XT.debug(sql1.slice(2000, 2500));
+      XT.debug(sql1.slice(2500, 3000));
+      XT.debug(sql1.slice(3000, 3500));
+      XT.debug(sql1.slice(3500, 4000));
+      XT.debug(sql1.slice(4000, 4500));
       XT.debug('ItemSiteListItem parameters = ', clause.parameters);
     }
     qry = plv8.execute(sql1, clause.parameters);
index 3c6d188..0f4def9 100644 (file)
@@ -33,7 +33,8 @@ BEGIN
   SELECT COALESCE(SUM(ROUND((coitem_qtyord * coitem_qty_invuomratio) *
                             (coitem_price / coitem_price_invuomratio), 2)), 0.0),
          COALESCE(SUM(ROUND((coitem_qtyord * coitem_qty_invuomratio) *
-                            (coitem_unitcost / coitem_price_invuomratio), 2)), 0.0)
+                            (CASE WHEN (coitem_subnumber > 0) THEN 0.0 ELSE coitem_unitcost END
+                             / coitem_price_invuomratio), 2)), 0.0)
          INTO _subtotal, _cost
   FROM coitem
   WHERE (coitem_cohead_id=pCoheadid)
index 095a778..bf15b9b 100644 (file)
@@ -143,6 +143,7 @@ BEGIN
   FROM quhead JOIN custinfo ON (cust_id=quhead_cust_id)
   WHERE (quhead_id=pQuheadid);
 
+  -- Move Documents
   UPDATE url SET url_source_id = _soheadid,
                  url_source = 'S'
   WHERE ((url_source='Q') AND (url_source_id = pQuheadid));
@@ -155,6 +156,21 @@ BEGIN
                     docass_source_type = 'S'
   WHERE ((docass_source_type='Q') AND (docass_source_id = pQuheadid));
 
+  -- Move Email
+  IF (fetchMetricBool('EnableBatchManager')) THEN
+    UPDATE xtbatch.emlassc SET emlassc_type='S',
+                               emlassc_assc_id=_soheadid
+    WHERE ((emlassc_type='Q') AND (emlassc_assc_id=pQuheadid));
+  END IF;
+
+  -- Copy Characteristics
+  INSERT INTO charass
+        (charass_target_type, charass_target_id, charass_char_id, charass_value, charass_default, charass_price)
+  SELECT 'SO', _soheadid, charass_char_id, charass_value, charass_default, charass_price
+    FROM charass
+   WHERE ((charass_target_type='QU')
+     AND  (charass_target_id=pQuheadid));
+
   -- Copy Comments
   INSERT INTO comment
   ( comment_cmnttype_id, comment_source, comment_source_id, comment_date, comment_user, comment_text, comment_public )
@@ -205,6 +221,7 @@ BEGIN
       WHERE (quhead_id=pQuheadid);
     END IF;
 
+    -- Copy Characteristics
     INSERT INTO charass
           (charass_target_type, charass_target_id, charass_char_id, charass_value, charass_default, charass_price)
     SELECT 'SI', _soitemid, charass_char_id, charass_value, charass_default, charass_price
index db45cc0..d52a6ea 100644 (file)
@@ -46,6 +46,26 @@ DECLARE
   pDueDate ALIAS FOR $5;
   pPrice ALIAS FOR $6;
 
+BEGIN
+
+  RETURN createPurchaseToSale(pCoitemId, pItemSourceId, pDropShip, pQty, pDueDate, pPrice, NULL);
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION createPurchaseToSale(INTEGER, INTEGER, BOOLEAN, NUMERIC, DATE, NUMERIC, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2014 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCoitemId ALIAS FOR $1;
+  pItemSourceId ALIAS FOR $2;
+  pDropShip ALIAS FOR $3;
+  pQty ALIAS FOR $4;
+  pDueDate ALIAS FOR $5;
+  pPrice ALIAS FOR $6;
+  pPoheadId ALIAS FOR $7;
+
   _s RECORD;
   _w RECORD;
   _i RECORD;
@@ -98,6 +118,9 @@ BEGIN
     RETURN -2;
   END IF;
 
+  -- pPoheadId - NULL=add to existing PO if one exists
+  --               -1=must create new PO
+  --               >0=must add to existing specified PO
   IF (pDropShip) THEN
     SELECT COALESCE(pohead_id, -1) INTO _temp
     FROM pohead
@@ -110,7 +133,8 @@ BEGIN
       AND (pohead_shiptocity = COALESCE(_s.cohead_shiptocity, _s.addr_city, ''))
       AND (pohead_shiptostate = COALESCE(_s.cohead_shiptostate, _s.addr_state, ''))
       AND (pohead_shiptozipcode = COALESCE(_s.cohead_shiptozipcode, _s.addr_postalcode, ''))
-      AND (pohead_shiptocountry = COALESCE(_s.cohead_shiptocountry, _s.addr_country, '')) );
+      AND (pohead_shiptocountry = COALESCE(_s.cohead_shiptocountry, _s.addr_country, ''))
+      AND ((pohead_id=pPoheadId) OR (pPoheadid IS NULL)) );
   ELSE
     SELECT COALESCE(pohead_id, -1) INTO _temp
     FROM pohead
@@ -122,15 +146,22 @@ BEGIN
       AND (pohead_shiptocity = COALESCE(_w.addr_city, ''))
       AND (pohead_shiptostate = COALESCE(_w.addr_state, ''))
       AND (pohead_shiptozipcode = COALESCE(_w.addr_postalcode, ''))
-      AND (pohead_shiptocountry = COALESCE(_w.addr_country, '')) );
+      AND (pohead_shiptocountry = COALESCE(_w.addr_country, ''))
+      AND ((pohead_id=pPoheadId) OR (pPoheadid IS NULL)) );
   END IF;
 
   IF (FOUND) THEN
+    IF (pPoheadId = -1) THEN
+      RAISE EXCEPTION 'Problem creating new PO';
+    END IF;
     _poheadid := _temp;
     UPDATE pohead
     SET pohead_dropship = pDropShip
     WHERE (pohead_id = _poheadid);
   ELSE
+    IF (pPoheadId > 0) THEN
+      RAISE EXCEPTION 'Problem adding to existing PO';
+    END IF;
     SELECT NEXTVAL('pohead_pohead_id_seq') INTO _poheadid;
     SELECT fetchPoNumber() INTO _ponumber;
 
index 0d75305..03cef38 100644 (file)
@@ -14,17 +14,28 @@ CREATE OR REPLACE FUNCTION itemCost(pItemid INTEGER,
 -- Overload for future costing enhancements
 --
 DECLARE
+  _r RECORD;
   _cost NUMERIC := 0.0;
 BEGIN
-  IF (fetchMetricBool('WholesalePriceCosting')) THEN
-    SELECT item_listcost INTO _cost
-    FROM item
-    WHERE (item_id=pItemid);
+  -- cache item info
+  SELECT * INTO _r
+  FROM itemsite, item
+  WHERE (itemsite_item_id=pItemid)
+    AND (itemsite_warehous_id=pSiteid)
+    AND (item_id=pItemid);
+
+  IF (_r.item_type = 'K') THEN
+    SELECT SUM(roundQty(itemuomfractionalbyuom(bomitem_item_id, bomitem_uom_id),
+                                               (bomitem_qtyfxd + bomitem_qtyper) * (1 + bomitem_scrap))
+               * stdCost(bomitem_item_id)) INTO _cost
+    FROM bomitem
+    WHERE (bomitem_parent_item_id=_r.item_id)
+      AND (bomitem_rev_id=getActiveRevid('BOM', _r.item_id))
+      AND (pEffective BETWEEN bomitem_effective AND (bomitem_expires - 1));
+  ELSEIF (fetchMetricBool('WholesalePriceCosting')) THEN
+    _cost := _r.item_listcost;
   ELSE
-    SELECT itemcost(itemsite_id) INTO _cost
-    FROM itemsite
-    WHERE (itemsite_item_id=pItemid)
-      AND (itemsite_warehous_id=pSiteid);
+    SELECT itemcost(_r.itemsite_id) INTO _cost;
   END IF;
 
   RETURN _cost;
index 229fe0e..94514b8 100644 (file)
@@ -29,7 +29,7 @@ BEGIN
          itemsite_loccntrl, COALESCE(invcnt_location_id, -1) AS itemsite_location_id,
          CASE WHEN (itemsite_costmethod = 'N') THEN 0
               WHEN ( (itemsite_costmethod = 'A') AND
-                     (itemsite_qtyonhand = 0) AND
+                     ((itemsite_qtyonhand + itemsite_nnqoh) = 0) AND
                      (_avgCostingMethod = 'ACT') ) THEN actcost(itemsite_item_id)
               WHEN ( (itemsite_costmethod = 'A') AND
                      (_avgCostingMethod IN ('ACT', 'AVG')) ) THEN avgcost(itemsite_id)
@@ -241,12 +241,17 @@ BEGIN
     SET itemsite_qtyonhand= itemsite_qtyonhand + (_p.invcnt_qoh_after - _origLocQty),
         itemsite_datelastcount=_postDate
     WHERE (itemsite_id=_p.itemsite_id);
+    UPDATE itemsite
+    SET itemsite_value =  (itemsite_qtyonhand + itemsite_nnqoh) * _p.cost
+    WHERE (itemsite_id=_p.itemsite_id);
   ELSE
     UPDATE itemsite
-    SET itemsite_nnqoh =  itemsite_nnqoh - _origLocQty,
-       itemsite_qtyonhand = itemsite_qtyonhand + _p.invcnt_qoh_after,
+    SET itemsite_nnqoh =  itemsite_nnqoh + (_p.invcnt_qoh_after - _origLocQty),
         itemsite_datelastcount=_postDate
     WHERE (itemsite_id=_p.itemsite_id);
+    UPDATE itemsite
+    SET itemsite_value =  (itemsite_qtyonhand + itemsite_nnqoh) * _p.cost
+    WHERE (itemsite_id=_p.itemsite_id);
   END IF;
  
 --  Post the detail, if any
index ad24fb9..54d85bc 100644 (file)
@@ -167,8 +167,9 @@ BEGIN
     END LOOP;
 
 -- _qoh can be used for the netable qoh because of the negative NN transactions
+-- change to update qtyonhand with _netable_qoh
     UPDATE itemsite
-       SET itemsite_qtyonhand = _qoh,
+       SET itemsite_qtyonhand = _netable_qoh,
            itemsite_nnqoh = _nonnetable_qoh,
            itemsite_value = CASE WHEN ((itemsite_costmethod='A') AND (_value < 0.0)) THEN 0.0
                                  ELSE _value END
index 9e17bb1..2954e7b 100644 (file)
@@ -1458,6 +1458,9 @@ WHERE (true)
   AND project IN (SELECT poitem_prj_id FROM pohead JOIN poitem ON (pohead_id=poitem_pohead_id) 
                        WHERE pohead_id=<? value("pohead_id") ?>)
 <? endif ?>
+<? if exists("prjtype_id") ?>
+  AND project IN (SELECT prj_id FROM prj WHERE prj_prjtype_id = <? value("prjtype_id") ?>)
+<? endif ?>
 
 
 ORDER BY project, section, subtype, type, id;
index 2c31a83..584fb21 100644 (file)
@@ -22,9 +22,11 @@ SELECT quitem_id,
             ELSE ((1.0 - (quitem_price / quitem_custprice)) * 100.0)
        END AS discountfromcust,
        quitem_unitcost AS coitem_unitcost,
-       ROUND((quitem_qtyord * quitem_qty_invuomratio) *
-             ((quitem_price / quitem_price_invuomratio) - (quitem_unitcost / quitem_price_invuomratio)),2) AS margin,
-       CASE WHEN (quitem_price = 0.0) THEN 100.0
+       CASE WHEN (quitem_price = 0.0) THEN 0.0
+            ELSE ROUND((quitem_qtyord * quitem_qty_invuomratio) *
+                 ((quitem_price / quitem_price_invuomratio) - (quitem_unitcost / quitem_price_invuomratio)),2)
+       END AS margin,
+       CASE WHEN (quitem_price = 0.0) THEN 0.0
             ELSE ((quitem_price - quitem_unitcost) / quitem_price)
        END AS marginpercent,
        CASE WHEN (quitem_custpn != '') THEN quitem_custpn
index d73ca25..3f06697 100644 (file)
@@ -29,6 +29,8 @@ SELECT coitem_id, coitem_altid, groupby,
        coitem_qtyord, qtyshipped, balance, qtyatshipping,
        extprice, extprice_shipped,
        CASE WHEN (discountfromcust=100.0) THEN 'N/A' END AS discountfromcust_qtdisplayrole,
+       CASE WHEN (margin=0.0) THEN 'N/A' END AS margin_qtdisplayrole,
+       CASE WHEN (marginpercent=0.0) THEN 'N/A' END AS marginpercent_qtdisplayrole,
        'qty' AS coitem_qtyord_xtnumericrole,
        'qty' AS qtyshipped_xtnumericrole,
        'qty' AS balance_xtnumericrole,
@@ -98,9 +100,11 @@ SELECT coitem_id,
             ELSE ((1.0 - (coitem_price / coitem_custprice)) * 100.0)
        END AS discountfromcust,
        coitem_unitcost,
-       ROUND((coitem_qtyord * coitem_qty_invuomratio) *
-             ((coitem_price / coitem_price_invuomratio) - (coitem_unitcost / coitem_price_invuomratio)),2) AS margin,
-       CASE WHEN (coitem_price = 0.0) THEN 100.0
+       CASE WHEN (coitem_price = 0.0) THEN 0.0
+            ELSE ROUND((coitem_qtyord * coitem_qty_invuomratio) *
+                 ((coitem_price / coitem_price_invuomratio) - (coitem_unitcost / coitem_price_invuomratio)),2)
+       END AS margin,
+       CASE WHEN (coitem_price = 0.0) THEN 0.0
             ELSE ((coitem_price - coitem_unitcost) / coitem_price)
        END AS marginpercent,
        noNeg(coitem_qtyshipped - coitem_qtyreturned) AS qtyshipped,
index c9737b5..e0814ad 100644 (file)
@@ -1,7 +1,6 @@
 select xt.create_table('taxpay', 'public');
-select xt.add_column('taxpay','taxpay_id', 'INTEGER', 'NOT NULL', 'public');
+select xt.add_column('taxpay','taxpay_id', 'SERIAL', 'PRIMARY KEY', 'public');
 select xt.add_column('taxpay','taxpay_taxhist_id', 'INTEGER', 'NOT NULL', 'public');
 select xt.add_column('taxpay','taxpay_apply_id', 'INTEGER', 'NOT NULL', 'public');
 select xt.add_column('taxpay','taxpay_distdate', 'DATE', 'NOT NULL', 'public');
 select xt.add_column('taxpay','taxpay_tax', 'NUMERIC', 'NOT NULL', 'public');
-select xt.add_primary_key('taxpay','taxpay_id', 'public');
\ No newline at end of file
index 23b5a07..b33178e 100644 (file)
@@ -11,6 +11,7 @@
 @picker-label: 100px;
 @bottom-border: #444;
 @icon-height: 32px;
+@title-height: 48px;
 
 .dashboard {
   background-color: @gray;
@@ -57,8 +58,9 @@
   }
 
   .chart-title-bar {
-    height: @icon-height;
+    height: @title-height;
     background-color: @lightest-gray;
+    text-transform: none;
     .border-top-radius(10px);
 
     .chart-title {
       color: @black;
       text-align: center;
       /*font-size: 2em;*/
-      font-weight: normal
+      font-weight: normal;
+      font-family: Helvetica;
+    }
+    
+    .chart-sub-title {
+      /*padding-top: 8px;*/
+      color: @blue;
+      text-align: center;
+      font-size: small;
+      font-weight: normal;
+      font-family: Helvetica;
     }
 
     .remove-icon {
       padding: 10px 10px;
     }
   }
+  
+  .chart-filterDrawer {
+    top: 0px;
+  }
+
+  /*  The chart-filters may not be need as we are using the class xv-pullout
+      for styles.  Delete when finished hacking the filter styles.
 
   .chart-filters {
          background-color: @white;
          border-bottom: 1px solid @smoke;
          background: @lightest-gray;
          color: @near-black;
-}
+  }
 
   .chart-filters.xv-parameter-panel .enyo-fittable-columns-layout {
-         padding: 6px 10px;
          border-bottom: 1px solid @smoke;
          background: @white;
     color: @near-black;
-}
+    position: relative;
+    text-align: left;
+    white-space: nowrap;
+  }
+
+  .chart-filters.xv-parameter-panel .enyo-fittable-columns-layout > * {
+    vertical-align: middle;
+  }
+
+  .chart-filters.xv-parameter-panel .xv-label {
+    text-align: right;
+  }
+  
+  */
 
 }
index c47eee9..4453ce6 100644 (file)
   }
 
   .xv-grid-attr {
+    // This limits the text to three lines
+    overflow: hidden;
+    display: -webkit-box;
+    -webkit-line-clamp: 3;
+    -webkit-box-orient: vertical;
+
     &.bold {
     font-weight: bold;
     }
           display: none;
         }
         .xv-relationwidget-description {
-          text-indent: 0;
-          padding-bottom: 0;
-          padding-top: 8px;
+          margin: 0;
+          margin-top: 5px;
         }
         .xv-private-item-site-widget {
           border-bottom: 0;
index f6b3afa..d6a480f 100644 (file)
  }
 
  .xv-relationwidget-description {
-   white-space: nowrap;
    overflow: hidden;
-   text-overflow: ellipsis;
-   text-indent: 140px;
-   font-size: small;
-   padding-bottom: 4px;
-   max-width: 325px;
-   color: black;
+   // This gives them 3 lines of description
+   display: -webkit-box;
+   -webkit-line-clamp: 3;
+   -webkit-box-orient: vertical;
+   max-width: 250px;
+   margin: 5px 5px 5px 80px;
    &.disabled {
      color: @dim-gray;
    }
index 60696d7..992a775 100755 (executable)
@@ -2108,6 +2108,12 @@ body {
 .xv-grid-box .xv-scroller {
   background: #f8f8f8;
 }
+.xv-grid-box .xv-grid-attr {
+  overflow: hidden;
+  display: -webkit-box;
+  -webkit-line-clamp: 3;
+  -webkit-box-orient: vertical;
+}
 .xv-grid-box .xv-grid-attr.bold {
   font-weight: bold;
 }
@@ -2310,9 +2316,8 @@ body {
   display: none;
 }
 .xv-grid-box .xv-grid-row.selected .xv-grid-column .xv-relationwidget-description {
-  text-indent: 0;
-  padding-bottom: 0;
-  padding-top: 8px;
+  margin: 0;
+  margin-top: 5px;
 }
 .xv-grid-box .xv-grid-row.selected .xv-grid-column .xv-private-item-site-widget {
   border-bottom: 0;
@@ -2377,6 +2382,49 @@ body {
   margin: 10px;
   color: #357ec7;
   overflow: hidden;
+  /*  The chart-filters may not be need as we are using the class xv-pullout
+      for styles.  Delete when finished hacking the filter styles.
+
+  .chart-filters {
+         background-color: @white;
+         
+         -webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.2);
+         -moz-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.2);
+         box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.2);
+  }
+  
+  .chart-filters .xv-parameter-panel {
+         background-color: @ghost;
+         border: 1px solid @smoke;
+         margin: 0 4px 6px 0;
+         width: 100%;
+  }
+  
+  .chart-filters.xv-parameter-panel .onyx-groupbox-header {
+         padding: 6px 10px;
+         border-bottom: 1px solid @smoke;
+         background: @lightest-gray;
+         color: @near-black;
+  }
+
+  .chart-filters.xv-parameter-panel .enyo-fittable-columns-layout {
+         border-bottom: 1px solid @smoke;
+         background: @white;
+    color: @near-black;
+    position: relative;
+    text-align: left;
+    white-space: nowrap;
+  }
+
+  .chart-filters.xv-parameter-panel .enyo-fittable-columns-layout > * {
+    vertical-align: middle;
+  }
+
+  .chart-filters.xv-parameter-panel .xv-label {
+    text-align: right;
+  }
+  
+  */
 }
 .selectable-chart .xv-list-item {
   border-bottom: 1px solid grey;
@@ -2391,8 +2439,9 @@ body {
   margin-left: 200px;
 }
 .selectable-chart .chart-title-bar {
-  height: 32px;
+  height: 48px;
   background-color: #efefef;
+  text-transform: none;
   -webkit-border-top-right-radius: 10px;
   -moz-border-radius-topright: 10px;
   border-top-right-radius: 10px;
@@ -2406,6 +2455,15 @@ body {
   text-align: center;
   /*font-size: 2em;*/
   font-weight: normal;
+  font-family: Helvetica;
+}
+.selectable-chart .chart-title-bar .chart-sub-title {
+  /*padding-top: 8px;*/
+  color: #0000ff;
+  text-align: center;
+  font-size: small;
+  font-weight: normal;
+  font-family: Helvetica;
 }
 .selectable-chart .chart-title-bar .remove-icon {
   position: absolute;
@@ -2436,29 +2494,8 @@ body {
   width: 130px;
   padding: 10px 10px;
 }
-.selectable-chart .chart-filters {
-  background-color: #fdfdfd;
-  -webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.2);
-  -moz-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.2);
-  box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.2);
-}
-.selectable-chart .chart-filters .xv-parameter-panel {
-  background-color: #f8f8f8;
-  border: 1px solid #d7d7d7;
-  margin: 0 4px 6px 0;
-  width: 100%;
-}
-.selectable-chart .chart-filters.xv-parameter-panel .onyx-groupbox-header {
-  padding: 6px 10px;
-  border-bottom: 1px solid #d7d7d7;
-  background: #efefef;
-  color: #0e0e0e;
-}
-.selectable-chart .chart-filters.xv-parameter-panel .enyo-fittable-columns-layout {
-  padding: 6px 10px;
-  border-bottom: 1px solid #d7d7d7;
-  background: #fdfdfd;
-  color: #0e0e0e;
+.selectable-chart .chart-filterDrawer {
+  top: 0px;
 }
 /**
   Styles relating to Lists
@@ -2970,14 +3007,12 @@ body {
   position: relative;
 }
 .xv-relationwidget-description {
-  white-space: nowrap;
   overflow: hidden;
-  text-overflow: ellipsis;
-  text-indent: 140px;
-  font-size: small;
-  padding-bottom: 4px;
-  max-width: 325px;
-  color: black;
+  display: -webkit-box;
+  -webkit-line-clamp: 3;
+  -webkit-box-orient: vertical;
+  max-width: 250px;
+  margin: 5px 5px 5px 80px;
 }
 .xv-relationwidget-description.disabled {
   color: #777777;
index 81a6027..455bd44 100644 (file)
@@ -117,12 +117,13 @@ regexp:true, undef:true, trailing:true, white:true, strict:false */
         attrs = value ? value.get(key) : "";
         changed = inputValue !== attrs;
       } else {
-        // Must be array. Handle it.
+        // key must be array. Handle it.
         _.each(key, function (str) {
-          var attr = value ? value.get(key) : "";
+          var attr = value ? value.get(str) : "";
           attrs.push(attr);
-          changed = changed || inputValue !== attrs;
         });
+        // if changed or inputValue doesn't match an attr
+        changed = changed || !(_.find(attrs, function (attr) {return inputValue === attr; }));
       }
 
       if (inputValue && changed) {
index aa328dd..b5ecb55 100644 (file)
@@ -62,7 +62,7 @@
   },
   "main": "node-datasource/main.js",
   "scripts": {
-    "build-basic-postbooks-package-sql": "./scripts/explode_manifest.js -m foundation-database/manifest.js -n updates.sql",
+    "build-basic-postbooks-package-sql": "./scripts/explode_manifest.js -m foundation-database/manifest.js -n postbooks_upgrade.sql",
     "build-basic-empty": "./scripts/build_app.js -d empty --databaseonly -e foundation-database -i -s foundation-database/empty_data.sql",
     "build-basic-postbooks-demo": "./scripts/build_app.js -d postbooks_demo --databaseonly -e foundation-database -i -s foundation-database/postbooks_demo_data.sql",
     "build-basic-quickstart": "./scripts/build_app.js -d quickstart --databaseonly -e foundation-database -i -s foundation-database/quickstart_data.sql",
index 7139b10..933357c 100755 (executable)
@@ -10,21 +10,114 @@ regexp:true, undef:true, strict:true, trailing:true, white:true */
 (function () {
   "use strict";
 
-  var program = require('commander'),
-    exportEnglish = require("./lib/build_dictionary").exportEnglish;
+  var _ = require("underscore"),
+    async = require("async"),
+    fs = require('fs'),
+    path = require('path'),
+    program = require('commander'),
+    dictionaryToolkit = require("./lib/build_dictionary");
 
   program
     .option('-a, --apikey [api key]', 'Google Translate API key.')
     .option('-d, --database [database name]', 'Database name to export from.')
     .option('-l, --language [language]', 'Language name in form es_MX.')
+    .option('-t, --total', 'Export the totality of the language files en batch.')
     .parse(process.argv);
 
-  exportEnglish(program.database, program.apikey, program.language, function (err, res) {
+
+  if (!program.total) {
+    dictionaryToolkit.exportEnglish(
+    {
+      database: program.database,
+      apiKey: program.apikey,
+      language: program.language
+    },
+    function (err, res) {
+      if (err) {
+        console.log("Export failed", err);
+        return;
+      }
+      console.log("Success!");
+    });
+    return;
+  }
+
+
+  //
+  // Do a total batch export
+  //
+  if (program.database || program.language) {
+    console.log("Don't enter a database name or a language. I'll take care of that.");
+    return;
+  }
+  var buildAll = require('./lib/build_all');
+  program.database = "linguist";
+
+  //
+  // Step 1: Create a database with the appropriate extensions
+  //
+  var buildPostbooks = function (done) {
+    buildAll.build({
+      database: program.database,
+      initialize: true,
+      source: path.join(__dirname, "../foundation-database/postbooks_demo_data.sql")
+    }, done);
+  };
+  var buildExtensions = function (done) {
+    var extensions = ["inventory", "manufacturing", "distribution"/*, "bi"*/];
+    async.mapSeries(extensions, function (extension, next) {
+      buildAll.build({
+        database: program.database,
+        frozen: (extension !== "bi"),
+        extension: path.join(__dirname, "../../private-extensions/source", extension)
+      }, next)}, done);
+  };
+
+  //
+  // Step 2: Load all the language files that we have
+  //
+  var importExistingDictionaries = function (done) {
+    fs.readdir(path.join(__dirname, "../../xtuple-linguist/translations"), function (err, files) {
+      async.mapSeries(files, function (file, next) {
+        var fullFilename = path.join(__dirname, "../../xtuple-linguist/translations", file);
+        dictionaryToolkit.importDictionary(program.database, fullFilename, next);
+      }, done);
+    });
+  };
+
+  //
+  // Step 3: Export in every language
+  //
+  var supportedLanguages = [
+    {locale: "de_DE", label: "Deutch", english: "German"},
+    {locale: "es_MX", label: "Español", english: "Spanish"},
+    {locale: "fr_FR", label: "Français", english: "French"},
+    {locale: "te_IN", label: "తెలుగు", english: "Telugu"},
+    {locale: "zh_CN", label: "中国的", english: "Simplified Chinese"}
+  ];
+  var exportAllDictionaries = function (done) {
+    async.mapSeries(supportedLanguages, function (language, next) {
+      dictionaryToolkit.exportEnglish(
+      {
+        database: program.database,
+        apiKey: program.apikey,
+        language: language.locale,
+        directory: path.join(__dirname, "../../xtuple-linguist/translations")
+      }, next);
+    }, done);
+  };
+
+  async.series([
+    buildPostbooks,
+    buildExtensions,
+    importExistingDictionaries,
+    exportAllDictionaries
+  ], function (err, results) {
     if (err) {
-      console.log("Export failed", err);
+      console.log("Export failed", err);
       return;
     }
-    console.log("Success!");
+    console.log("Success! Don't forget to commit and push xtuple-linguist!");
   });
 
 }());
index 784f95b..f7f8934 100644 (file)
@@ -100,7 +100,6 @@ var  async = require('async'),
             isLibOrm ? path.join(extension, "source") :
             path.join(extension, "database/source"),
           manifestOptions = {
-            useSafeFoundationToolkit: isFoundation && !isFoundationExtension && extensions.length === 1,
             useFrozenScripts: spec.frozen,
             useFoundationScripts: baseName.indexOf('inventory') >= 0 ||
               baseName.indexOf('manufacturing') >= 0 ||
index c49404a..e2f85ec 100644 (file)
@@ -14,11 +14,12 @@ regexp:true, undef:true, strict:true, trailing:true, white:true */
     dataSource = require('../../node-datasource/lib/ext/datasource').dataSource,
     winston = require('winston');
 
-  var convertFromMetasql = function (content, filename) {
+
+
+
+  var convertFromMetasql = function (content, filename, defaultSchema) {
     var lines = content.split("\n"),
-      schema = filename.indexOf('manufacturing') >= 0 ?
-        "'xtmfg'" :
-        "NULL",
+      schema = defaultSchema ? "'" + defaultSchema + "'" : "NULL",
       group,
       i = 2,
       name,
@@ -53,12 +54,11 @@ regexp:true, undef:true, strict:true, trailing:true, white:true */
     return insertSql;
   };
 
-  var convertFromReport = function (content, filename) {
+  var convertFromReport = function (content, filename, defaultSchema) {
     var lines = content.split("\n"),
       name,
-      tableName = filename.indexOf('manufacturing') >= 0 ?
-        "xtmfg.pkgreport" :
-        "report",
+      grade = "0",
+      tableName = defaultSchema ? defaultSchema + ".pkgreport" : "report",
       description,
       disableSql,
       deleteSql,
@@ -72,32 +72,34 @@ regexp:true, undef:true, strict:true, trailing:true, white:true */
     name = lines[3].substring(" <name>".length).trim();
     name = name.substring(0, name.indexOf("<"));
     description = lines[4].substring(" <description>".length).trim();
-    description = description.substring(0, name.indexOf("<"));
+    description = description.substring(0, description.indexOf("<"));
+    if (lines[5].indexOf("grade") >= 0) {
+      grade = lines[5].substring(" <grade>".length).trim();
+      grade = grade.substring(0, grade.indexOf("<"));
+    }
 
     disableSql = "ALTER TABLE " + tableName + " DISABLE TRIGGER ALL;";
 
     deleteSql = "delete from " + tableName + " " +
       "where report_name = '" + name +
-      "' and report_grade = 0;";
+      "' and report_grade = " + grade + ";";
 
     insertSql = "insert into " + tableName + " (report_name, report_descrip, " +
       "report_source, report_loaddate, report_grade) VALUES (" +
       "'" + name + "'," +
-      "'" + description + "'," +
+      "$$" + description + "$$," +
       "$$" + content + "$$," +
-      "now(), 0);";
+      "now(), " + grade + ");";
 
     enableSql = "ALTER TABLE " + tableName + " ENABLE TRIGGER ALL;";
 
     return disableSql + deleteSql + insertSql + enableSql;
   };
 
-  var convertFromScript = function (content, filename) {
+  var convertFromScript = function (content, filename, defaultSchema) {
     var name = path.basename(filename, '.js'),
-      tableName = filename.indexOf('manufacturing') >= 0 ?
-        "xtmfg.pkgscript" :
-        "unknown",
-      notes = "xtMfg package",
+      tableName = defaultSchema ? defaultSchema + ".pkgscript" : "unknown",
+      notes = "", //"xtMfg package",
       disableSql,
       deleteSql,
       insertSql,
@@ -120,12 +122,10 @@ regexp:true, undef:true, strict:true, trailing:true, white:true */
     return disableSql + deleteSql + insertSql + enableSql;
   };
 
-  var convertFromUiform = function (content, filename) {
+  var convertFromUiform = function (content, filename, defaultSchema) {
     var name = path.basename(filename, '.ui'),
-      tableName = filename.indexOf('manufacturing') >= 0 ?
-        "xtmfg.pkguiform" :
-        "unknown",
-      notes = "xtMfg package",
+      tableName = defaultSchema ? defaultSchema + ".pkguiform" : "unknown",
+      notes = "", //"xtMfg package",
       disableSql,
       deleteSql,
       insertSql,
@@ -174,6 +174,7 @@ regexp:true, undef:true, strict:true, trailing:true, white:true */
       var manifest,
         databaseScripts,
         extraManifestPath,
+        defaultSchema,
         extraManifest,
         extraManifestScripts,
         alterPaths = dbSourceRoot.indexOf("foundation-database") < 0,
@@ -186,6 +187,7 @@ regexp:true, undef:true, strict:true, trailing:true, white:true */
         extensionName = manifest.name;
         extensionComment = manifest.comment;
         databaseScripts = manifest.databaseScripts;
+        defaultSchema = manifest.defaultSchema;
         loadOrder = manifest.loadOrder || 999;
 
       } catch (error) {
@@ -220,8 +222,9 @@ regexp:true, undef:true, strict:true, trailing:true, white:true */
       // -e ../private-extensions/source/inventory/foundation-database
 
       if (options.useFoundationScripts) {
-        extraManifest = fs.readFileSync(path.join(dbSourceRoot, "../../foundation-database/manifest.js"));
-        extraManifestScripts = JSON.parse(extraManifest).databaseScripts;
+        extraManifest = JSON.parse(fs.readFileSync(path.join(dbSourceRoot, "../../foundation-database/manifest.js")));
+        defaultSchema = defaultSchema || extraManifest.defaultSchema;
+        extraManifestScripts = extraManifest.databaseScripts;
         extraManifestScripts = _.map(extraManifestScripts, function (path) {
           return "../../foundation-database/" + path;
         });
@@ -234,8 +237,9 @@ regexp:true, undef:true, strict:true, trailing:true, white:true */
          path.join(dbSourceRoot, "../../foundation-database/frozen_manifest.js") :
          path.join(dbSourceRoot, "frozen_manifest.js");
 
-        extraManifest = fs.readFileSync(extraManifestPath);
-        extraManifestScripts = JSON.parse(extraManifest).databaseScripts;
+        extraManifest = JSON.parse(fs.readFileSync(extraManifestPath));
+        defaultSchema = defaultSchema || extraManifest.defaultSchema;
+        extraManifestScripts = extraManifest.databaseScripts;
         if (alterPaths) {
           extraManifestScripts = _.map(extraManifestScripts, function (path) {
             return "../../foundation-database/" + path;
@@ -267,7 +271,7 @@ regexp:true, undef:true, strict:true, trailing:true, white:true */
             extname = path.extname(fullFilename).substring(1);
 
           // convert special files: metasql, uiforms, reports, uijs
-          scriptContents = conversionMap[extname](scriptContents, fullFilename);
+          scriptContents = conversionMap[extname](scriptContents, fullFilename, defaultSchema);
           //
           // Allow inclusion of js files in manifest. If it is a js file,
           // use plv8 to execute it.
index b261be8..cb86ad4 100644 (file)
@@ -91,7 +91,11 @@ if (typeof XT === 'undefined') {
       return;
     }
 
-    if (destinationLang.indexOf("_") >= 0) {
+    if (destinationLang.indexOf("zh") === 0) {
+      // Google uses the country code for chinese, but uses dashes instead of our underscores
+      destinationLang = destinationLang.replace("_", "-");
+
+    } else if (destinationLang.indexOf("_") >= 0) {
       // strip off the locale for google
       destinationLang = destinationLang.substring(0, destinationLang.indexOf("_"));
     }
@@ -157,12 +161,16 @@ if (typeof XT === 'undefined') {
     @param {String} destinationLang. In form "es_MX".
     @param {Function} masterCallback
    */
-  exports.exportEnglish = function (database, apiKey, destinationLang, masterCallback) {
+  exports.exportEnglish = function (options, masterCallback) {
     var creds = require("../../node-datasource/config").databaseServer,
       sql = "select dict_strings, dict_is_database, dict_is_framework, " +
         "dict_language_name, ext_name from xt.dict " +
         "left join xt.ext on dict_ext_id = ext_id " +
-        "where dict_language_name = 'en_US'";
+        "where dict_language_name = 'en_US'",
+      database = options.database,
+      apiKey = options.apiKey,
+      destinationDir = options.directory,
+      destinationLang = options.language;
 
     if (destinationLang) {
       sql = sql + " or dict_language_name = $1";
@@ -217,12 +225,24 @@ if (typeof XT === 'undefined') {
       // group together english and foreign strings of the same extension
       var marriedRows = marryLists(res.rows);
       async.map(marriedRows, processExtension, function (err, extensions) {
+        // sort alpha so as to keep diffs under control
+        _.each(extensions, function (extension) {
+          extension.strings = _.sortBy(extension.strings, function (stringObj) {
+            return stringObj.key.toLowerCase();
+          });
+        });
+        extensions = _.sortBy(extensions, function (extObj) {
+          return extObj.extension;
+        });
+
         var output = {
           language: destinationLang || "",
           extensions: extensions
         };
-        // filename convention is ./scripts/private/es_MX_dictionary.js
-        var exportFilename = path.join(__dirname, "../private",
+        // filename convention is ./scripts/output/es_MX_dictionary.js
+        destinationDir = destinationDir || path.join(__dirname, "../output");
+
+        var exportFilename = path.join(destinationDir,
           (destinationLang || "blank") + "_dictionary.js");
         console.log("Exporting to", exportFilename);
         fs.writeFile(exportFilename, JSON.stringify(output, undefined, 2), function (err, result) {
@@ -243,7 +263,11 @@ if (typeof XT === 'undefined') {
     if (filename.substring(0, 1) !== '/') {
       filename = path.join(process.cwd(), filename);
     }
-
+    if (path.extname(filename) !== '.js') {
+      console.log("Skipping non-dictionary file", filename);
+      masterCallback();
+      return;
+    }
     fs.readFile(filename, "utf8", function (err, contents) {
       if (err) {
         masterCallback(err);
index 1e8619d..7594812 100755 (executable)
@@ -3,6 +3,7 @@ MAJ=$1
 MIN=$2
 PAT=$3
 
+# Usage: ./scripts/release_build.sh 4 5 0-beta
 echo "BUILDING RELEASE "$MAJ"."$MIN"."$PAT""
 
 git fetch XTUPLE
@@ -25,47 +26,58 @@ npm run-script build-basic-manufacturing-demo
 npm run-script build-basic-distribution-package-sql
 npm run-script build-basic-distribution-empty
 npm run-script build-basic-distribution-quickstart
-#postbooks package
+
+#postbooks upgrade
 cd ../xtuple
-mkdir scripts/output/pb$MAJ$MIN$PAT
-cp scripts/xml/postbooks_package.xml scripts/output/pb$MAJ$MIN$PAT/package.xml
-cp scripts/output/toolkit.sql scripts/output/pb$MAJ$MIN$PAT
-cp scripts/output/updates.sql scripts/output/pb$MAJ$MIN$PAT
+mkdir scripts/output/postbooks-upgrade-$MAJ$MIN$PAT
+cp scripts/xml/postbooks_package.xml scripts/output/postbooks-upgrade-$MAJ$MIN$PAT/package.xml
+cp scripts/output/postbooks_upgrade.sql scripts/output/postbooks-upgrade-$MAJ$MIN$PAT
 cd scripts/output
-tar -zcvf pb$MAJ$MIN$PAT.gz pb$MAJ$MIN$PAT/
-#distribution package
+tar -zcvf postbooks-upgrade-$MAJ$MIN$PAT.gz postbooks-upgrade-$MAJ$MIN$PAT/
+
+#distribution upgrade
 cd ../../
-mkdir scripts/output/dist$MAJ$MIN$PAT
-cp scripts/xml/distribution_package.xml scripts/output/dist$MAJ$MIN$PAT/package.xml
-cp scripts/output/updates.sql scripts/output/dist$MAJ$MIN$PAT
-cp scripts/output/inventory_upgrade.sql scripts/output/dist$MAJ$MIN$PAT
+mkdir scripts/output/distribution-upgrade-$MAJ$MIN$PAT
+cp scripts/xml/distribution_package.xml scripts/output/distribution-upgrade-$MAJ$MIN$PAT/package.xml
+cp scripts/output/postbooks_upgrade.sql scripts/output/distribution-upgrade-$MAJ$MIN$PAT
+cp scripts/output/inventory_upgrade.sql scripts/output/distribution-upgrade-$MAJ$MIN$PAT
+cp scripts/output/distribution_upgrade.sql scripts/output/distribution-upgrade-$MAJ$MIN$PAT
 cd scripts/output
-tar -zcvf dist$MAJ$MIN$PAT.gz dist$MAJ$MIN$PAT/
-#postbooks to distribution
+tar -zcvf distribution-upgrade-$MAJ$MIN$PAT.gz distribution-upgrade-$MAJ$MIN$PAT/
+
+#distribution install
 cd ../../
-mkdir scripts/output/pbtodist$MAJ$MIN$PAT
-cp scripts/xml/distribution_install.xml scripts/output/pbtodist$MAJ$MIN$PAT/package.xml
-cp scripts/output/inventory_basic_install.sql scripts/output/pbtodist$MAJ$MIN$PAT
-cp scripts/output/inventory_upgrade.sql scripts/output/pbtodist$MAJ$MIN$PAT
+mkdir scripts/output/distribution-install-$MAJ$MIN$PAT
+cp scripts/xml/distribution_install.xml scripts/output/distribution-install-$MAJ$MIN$PAT/package.xml
+cp scripts/output/postbooks_upgrade.sql scripts/output/distribution-install-$MAJ$MIN$PAT
+cp scripts/output/inventory_basic_install.sql scripts/output/distribution-install-$MAJ$MIN$PAT
+cp scripts/output/inventory_upgrade.sql scripts/output/distribution-install-$MAJ$MIN$PAT
+cp scripts/output/distribution_basic_install.sql scripts/output/distribution-install-$MAJ$MIN$PAT
+cp scripts/output/distribution_upgrade.sql scripts/output/distribution-install-$MAJ$MIN$PAT
 cd scripts/output
-tar -zcvf pbtodist$MAJ$MIN$PAT.gz pbtodist$MAJ$MIN$PAT/
-#xtmfg packages
+tar -zcvf distribution-install-$MAJ$MIN$PAT.gz distribution-install-$MAJ$MIN$PAT/
+
+#manufacturing upgrade
 cd ../../
-mkdir scripts/output/xtmfg$MAJ$MIN$PAT
-cp scripts/xml/xtmfg_package.xml scripts/output/xtmfg$MAJ$MIN$PAT/package.xml
-cp scripts/output/updates.sql scripts/output/xtmfg$MAJ$MIN$PAT
-cp scripts/output/inventory_upgrade.sql scripts/output/xtmfg$MAJ$MIN$PAT
-cp scripts/output/manufacturing_upgrade.sql scripts/output/xtmfg$MAJ$MIN$PAT
+mkdir scripts/output/manufacturing-upgrade-$MAJ$MIN$PAT
+cp scripts/xml/xtmfg_package.xml scripts/output/manufacturing-upgrade-$MAJ$MIN$PAT/package.xml
+cp scripts/output/postbooks_upgrade.sql scripts/output/manufacturing-upgrade-$MAJ$MIN$PAT
+cp scripts/output/inventory_upgrade.sql scripts/output/manufacturing-upgrade-$MAJ$MIN$PAT
+cp scripts/output/manufacturing_upgrade.sql scripts/output/manufacturing-upgrade-$MAJ$MIN$PAT
 cd scripts/output
-tar -zcvf xtmfg_upgrade-$MAJ$MIN$PAT.gz xtmfg$MAJ$MIN$PAT/
+tar -zcvf manufacturing-upgrade-$MAJ$MIN$PAT.gz manufacturing-upgrade-$MAJ$MIN$PAT/
 
+#manufacturing install
 cd ../../
-mkdir scripts/output/xtmfg_install$MAJ$MIN$PAT
-cp scripts/xml/xtmfg_install.xml scripts/output/xtmfg_install$MAJ$MIN$PAT/package.xml
-cp scripts/output/manufacturing_basic_install.sql scripts/output/xtmfg_install$MAJ$MIN$PAT
-cp scripts/output/manufacturing_upgrade.sql scripts/output/xtmfg_install$MAJ$MIN$PAT
+mkdir scripts/output/manufacturing-install-$MAJ$MIN$PAT
+cp scripts/xml/xtmfg_package.xml scripts/output/manufacturing-install-$MAJ$MIN$PAT/package.xml
+cp scripts/output/postbooks_upgrade.sql scripts/output/manufacturing-install-$MAJ$MIN$PAT
+cp scripts/output/inventory_basic_install.sql scripts/output/manufacturing-install-$MAJ$MIN$PAT
+cp scripts/output/inventory_upgrade.sql scripts/output/manufacturing-install-$MAJ$MIN$PAT
+cp scripts/output/manufacturing_basic_install.sql scripts/output/manufacturing-install-$MAJ$MIN$PAT
+cp scripts/output/manufacturing_upgrade.sql scripts/output/manufacturing-install-$MAJ$MIN$PAT
 cd scripts/output
-tar -zcvf xtmfg_install-$MAJ$MIN$PAT.gz xtmfg_install$MAJ$MIN$PAT/
+tar -zcvf manufacturing-install-$MAJ$MIN$PAT.gz manufacturing-install-$MAJ$MIN$PAT/
 
 ADMIN=admin
 PORT=5432
@@ -78,16 +90,15 @@ done
 
 #cleanup
 cd ../..
-rm -rf scripts/output/pb$MAJ$MIN$PAT/
-rm -rf scripts/output/updates.sql
-rm -rf scripts/output/toolkit.sql
-rm -rf scripts/output/pbtodist$MAJ$MIN$PAT/
-rm -rf scripts/output/dist$MAJ$MIN$PAT/
+rm -rf scripts/output/postbooks-upgrade-$MAJ$MIN$PAT/
+rm -rf scripts/output/postbooks_upgrade.sql
+rm -rf scripts/output/distribution-install-$MAJ$MIN$PAT/
+rm -rf scripts/output/distribution-upgrade-$MAJ$MIN$PAT/
 rm -rf scripts/output/distribution_upgrade.sql
 rm -rf scripts/output/distribution_basic_install.sql
 rm -rf scripts/output/inventory_basic_install.sql
 rm -rf scripts/output/inventory_upgrade.sql
-rm -rf scripts/output/xtmfg$MAJ$MIN$PAT/
-rm -rf scripts/output/xtmfg_install$MAJ$MIN$PAT/
+rm -rf scripts/output/manufacturing-install-$MAJ$MIN$PAT/
+rm -rf scripts/output/manufacturing-upgrade-$MAJ$MIN$PAT/
 rm -rf scripts/output/manufacturing_basic_install.sql
 rm -rf scripts/output/manufacturing_upgrade.sql
index b050f76..34127bc 100644 (file)
@@ -1,5 +1,5 @@
-<package id        = "pbtodist450Beta"
-         version   = "4.5.0Beta"
+<package id        = "distribution-install-450"
+         version   = "4.5.0"
          developer = "xTuple"
          descrip   = "load PostBooks resources"
          updater   = "2.2.4" >
 
   <prerequisite type = "query"
                 name = "Checking xTuple ERP database version" >
-    <query>SELECT fetchMetricText('ServerVersion') ~ '^4.4.';</query>
-    <message>This package must be applied to a 4.4+ Postbooks database.
+    <query>SELECT fetchMetricText('ServerVersion') > '4.4.';</query>
+    <message>This package must be applied to a 4.4+ Distribution database.
     </message>
   </prerequisite>
 
+ <prerequisite type = "query"
+               name = "Checking for bad xTuple ERP database version" >
+    <query>SELECT fetchMetricText('ServerVersion') &lt; '4.5.0';</query>
+    <message>This package may not be applied to a 4.5+ Distribution database.
+    </message>
+  </prerequisite>
+
+ <prerequisite type = "query"
+               name = "Checking for mobile-enabled schemas" >
+    <query>SELECT TRUE FROM pg_namespace WHERE nspname = 'xm';</query>
+    <message>This package may not be applied to a mobile-enabled database. Please see your system administrator or contact xTuple.
+    </message>
+  </prerequisite>
+
+  <script file="postbooks_upgrade.sql" />
   <script file="inventory_basic_install.sql" />
   <script file="inventory_upgrade.sql" />
+  <script file="distribution_basic_install.sql" />
+  <script file="distribution_upgrade.sql" />
 
 </package>
index 3f2a490..88c6a99 100644 (file)
@@ -1,5 +1,5 @@
-<package id        = "dist450Beta"
-         version   = "4.5.0Beta"
+<package id        = "distribution-upgrade-450"
+         version   = "4.5.0"
          developer = "xTuple"
          descrip   = "load PostBooks resources"
          updater   = "2.2.4" >
 
   <prerequisite type = "query"
                 name = "Checking xTuple ERP database version" >
-    <query>SELECT fetchMetricText('ServerVersion') ~ '^4.4.';</query>
+    <query>SELECT fetchMetricText('ServerVersion') > '4.4.';</query>
     <message>This package must be applied to a 4.4+ Distribution database.
     </message>
   </prerequisite>
 
-  <script file="updates.sql" />
+ <prerequisite type = "query"
+               name = "Checking for bad xTuple ERP database version" >
+    <query>SELECT fetchMetricText('ServerVersion') &lt; '4.5.0';</query>
+    <message>This package may not be applied to a 4.5+ Distribution database.
+    </message>
+  </prerequisite>
+
+ <prerequisite type = "query"
+               name = "Checking for mobile-enabled schemas" >
+    <query>SELECT TRUE FROM pg_namespace WHERE nspname = 'xm';</query>
+    <message>This package may not be applied to a mobile-enabled database. Please see your system administrator or contact xTuple.
+    </message>
+  </prerequisite>
+
+  <script file="postbooks_upgrade.sql" />
   <script file="inventory_upgrade.sql" />
+  <script file="distribution_upgrade.sql" />
 
 </package>
index db3e6cc..5f93bc2 100644 (file)
@@ -1,5 +1,5 @@
-<package id        = "pb450Beta"
-         version   = "4.5.0Beta"
+<package id        = "postbooks-upgrade-450"
+         version   = "4.5.0"
          developer = "xTuple"
          descrip   = "load PostBooks resources"
          updater   = "2.2.4" >
 
   <prerequisite type = "query"
                 name = "Checking xTuple ERP database version" >
-    <query>SELECT fetchMetricText('ServerVersion') ~ '^4.4.';</query>
+    <query>SELECT fetchMetricText('ServerVersion') > '4.4.';</query>
     <message>This package must be applied to a 4.4+ PostBooks database.
     </message>
   </prerequisite>
 
-  <script file="updates.sql" />
+ <prerequisite type = "query"
+               name = "Checking for bad xTuple ERP database version" >
+    <query>SELECT fetchMetricText('ServerVersion') &lt; '4.5.0';</query>
+    <message>This package may not be applied to a 4.5+ Postbooks database.
+    </message>
+  </prerequisite>
+
+ <prerequisite type = "query"
+               name = "Checking for mobile-enabled schemas" >
+    <query>SELECT TRUE FROM pg_namespace WHERE nspname = 'xm';</query>
+    <message>This package may not be applied to a mobile-enabled database. Please see your system administrator or contact xTuple.
+    </message>
+  </prerequisite>
+
+  <script file="postbooks_upgrade.sql" />
 
 </package>
index 549c254..fb36d4a 100644 (file)
@@ -1,5 +1,5 @@
-<package id        = "xtmfg_install450Beta"
-         version   = "4.5.0Beta"
+<package id        = "manufacturing-install-450"
+         version   = "4.5.0"
          developer = "xTuple"
          descrip   = "load PostBooks resources"
          updater   = "2.2.4" >
 
   <prerequisite type = "query"
                 name = "Checking xTuple ERP database version" >
-    <query>SELECT fetchMetricText('ServerVersion') ~ '^4.4.';</query>
-    <message>This package must be applied to a 4.4+ Manufacturing database.
+    <query>SELECT fetchMetricText('ServerVersion') > '4.4.';</query>
+    <message>This package must be applied to a 4.4+ Distribution database.
     </message>
   </prerequisite>
 
+ <prerequisite type = "query"
+               name = "Checking for bad xTuple ERP database version" >
+    <query>SELECT fetchMetricText('ServerVersion')='4.5.0Beta';</query>
+    <message>This package may not be applied to a 4.5.0Beta Distribution database.
+    </message>
+  </prerequisite>
+
+ <prerequisite type = "query"
+               name = "Checking for mobile-enabled schemas" >
+    <query>SELECT TRUE FROM pg_namespace WHERE nspname = 'xm';</query>
+    <message>This package may not be applied to a mobile-enabled database. Please see your system administrator or contact xTuple.
+    </message>
+  </prerequisite>
+
+  <script file="postbooks_upgrade.sql" />
+  <script file="inventory_basic_install.sql" />
+  <script file="inventory_upgrade.sql" />
   <script file="manufacturing_basic_install.sql" />
   <script file="manufacturing_upgrade.sql" />
 
index 64940fa..3078ba1 100644 (file)
@@ -1,5 +1,5 @@
-<package id        = "xtmfg450Beta"
-         version   = "4.5.0Beta"
+<package id        = "manufacturing-upgrade-450"
+         version   = "4.5.0"
          developer = "xTuple"
          descrip   = "load PostBooks resources"
          updater   = "2.2.4" >
 
   <prerequisite type = "query"
                 name = "Checking xTuple ERP database version" >
-    <query>SELECT fetchMetricText('ServerVersion') ~ '^4.4.';</query>
+    <query>SELECT fetchMetricText('ServerVersion') > '4.4.';</query>
     <message>This package must be applied to a 4.4+ Manufacturing database.
     </message>
   </prerequisite>
 
+ <prerequisite type = "query"
+                name = "Checking for bad xTuple ERP database version" >
+    <query>SELECT fetchMetricText('ServerVersion')='4.5.0Beta';</query>
+    <message>This package may not be applied to a 4.5.0Beta Manufacturing database.
+    </message>
+  </prerequisite>
+
+ <prerequisite type = "query"
+               name = "Checking for mobile-enabled schemas" >
+    <query>SELECT TRUE FROM pg_namespace WHERE nspname = 'xm';</query>
+    <message>This package may not be applied to a mobile-enabled database. Please see your system administrator or contact xTuple.
+    </message>
+  </prerequisite>
+
+  <script file="postbooks_upgrade.sql" />
+  <script file="inventory_upgrade.sql" />
   <script file="manufacturing_upgrade.sql" />
 
 </package>
index f0b3013..c1fd25a 100644 (file)
@@ -162,6 +162,19 @@ var _ = require("underscore"),
       });
     });
 
+    it('should execute a simple item-site fetch', function (done) {
+      var sql = 'select xt.js_init(true);select xt.post($${"nameSpace":"XM","type":"ItemSiteListItem","dispatch":{"functionName":"fetch","parameters":{"orderBy":[{"attribute":"item.number"}],"parameters":[{"attribute":"isActive","operator":"=","value":true}],"rowOffset":0,"rowLimit":50}},"username":"admin"}$$);';
+
+      datasource.query(sql, creds, function (err, res) {
+        var results;
+        assert.isNull(err);
+        assert.equal(1, res.rowCount, JSON.stringify(res.rows));
+        results = JSON.parse(res.rows[1].post);
+        assert.isNumber(results.length);
+        done();
+      });
+    });
+
     it('should execute an item-site fetch', function (done) {
       var sql = 'select xt.js_init(true);select xt.post($${"nameSpace":"XM","type":"ItemSiteRelation","dispatch":{"functionName":"fetch","parameters":{"parameters":[{"attribute":"item.number","value":"BTRUCK1"},{"attribute":"site.code","value":"WH1"}]}},"username":"admin","encryptionKey":"this is any content"}$$);';
 
@@ -201,6 +214,18 @@ var _ = require("underscore"),
       });
     });
 
+    it('should be able to do a complex item-site search with a keysearch and join table parameters', function (done) {
+      var sql = 'select xt.js_init(true);select xt.post($${"nameSpace":"XM","type":"ItemSiteRelation","dispatch":{"functionName":"fetch","parameters":{"parameters":[{"attribute":"item.isSold","value":true},{"attribute":"item.isActive","value":true},{"attribute":"isSold","value":true},{"attribute":"isActive","value":true},{"attribute":"site.code","value":"WH1"},{"attribute":"customer","value":"TTOYS"},{"attribute":["number","barcode"],"operator":"BEGINS_WITH","value":"btr","keySearch":true}],"orderBy":[{"attribute":"number"},{"attribute":"barcode"}],"rowLimit":10}},"username":"admin"}$$);';
+
+      datasource.query(sql, creds, function (err, res) {
+        var results;
+        assert.isNull(err);
+        assert.equal(1, res.rowCount, JSON.stringify(res.rows));
+        results = JSON.parse(res.rows[1].post);
+        assert.isNumber(results.length);
+        done();
+      });
+    });
 
     it('should support a nested order-by', function (done) {
       var sql = 'select xt.js_init(true);select xt.get($${"nameSpace":"XM","type":"ItemSource","query":{"orderBy":[{"attribute":"vendorItemNumber"},{"attribute":"vendor.name"}],"parameters":[{"attribute":"isActive","value":true},{"attribute":"effective","operator":"<=","value":"2014-03-20T04:00:00.000Z"},{"attribute":"expires","operator":">=","value":"2014-03-22T01:18:09.202Z"}],"rowOffset":0,"rowLimit":50},"username":"admin","encryptionKey":"this is any content"}$$);';
diff --git a/test/database/setmetric.js b/test/database/setmetric.js
new file mode 100644 (file)
index 0000000..2f6a87d
--- /dev/null
@@ -0,0 +1,65 @@
+/*jshint trailing:true, white:true, indent:2, strict:true, curly:true,
+  immed:true, eqeqeq:true, forin:true, latedef:true,
+  newcap:true, noarg:true, undef:true */
+/*global XT:true, describe:true, it:true, require:true, __dirname:true, before:true */
+
+var _ = require("underscore"),
+  assert = require('chai').assert,
+  path = require('path');
+
+(function () {
+  "use strict";
+  describe('The setMetric function', function () {
+
+    var loginData = require(path.join(__dirname, "../lib/login_data.js")).data,
+      datasource = require('../../../xtuple/node-datasource/lib/ext/datasource').dataSource,
+      config = require(path.join(__dirname, "../../node-datasource/config.js")),
+      creds = _.extend({}, config.databaseServer, {database: loginData.org});
+
+    it("should verify that there is no staged data", function (done) {
+      var sql = "select metric_value from public.metric where metric_name = 'Test999';";
+      datasource.query(sql, creds, function (err, res) {
+        assert.equal(res.rowCount, 0);
+        done();
+      });
+    });
+
+    it("should add a new metric", function (done) {
+      var sql = "select setmetric('Test999', 'Value999');";
+      datasource.query(sql, creds, done);
+    });
+
+    it("should verify that the metric was set", function (done) {
+      var sql = "select metric_value from public.metric where metric_name = 'Test999';";
+      datasource.query(sql, creds, function (err, res) {
+        assert.equal(res.rowCount, 1);
+        assert.equal(res.rows[0].metric_value, "Value999");
+        done();
+      });
+    });
+
+    it("should update the metric", function (done) {
+      var sql = "select setmetric('Test999', 'Value888');";
+      datasource.query(sql, creds, done);
+    });
+
+    it("should verify that the metric was set", function (done) {
+      var sql = "select metric_value from public.metric where metric_name = 'Test999';";
+      datasource.query(sql, creds, function (err, res) {
+        assert.equal(res.rowCount, 1);
+        assert.equal(res.rows[0].metric_value, "Value888");
+        done();
+      });
+    });
+
+    after(function (done) {
+      // cleanup
+      var sql = "delete from public.metric where metric_name = 'Test999';";
+      datasource.query(sql, creds, done);
+    });
+
+  });
+}());
+
+
+
index aacf04f..5366e94 100644 (file)
     submodels,
     smoke = require("../../lib/smoke"),
     assert = require("chai").assert,
+    gridRow,
+    gridBox,
+    workspace,
+    skipIfSiteCal,
     primeSubmodels = function (done) {
       var submodels = {};
       async.series([
           submodels = submods;
           done();
         });
+        if (XT.extensions.manufacturing && XT.session.settings.get("UseSiteCalendar")) {skipIfSiteCal = true; }
       });
     });
 
     describe('User selects to create a sales order', function () {
       it('User navigates to Sales Order-New and selects to create a new Sales order', function (done) {
         smoke.navigateToNewWorkspace(XT.app, "XV.SalesOrderList", function (workspaceContainer) {
-          var workspace = workspaceContainer.$.workspace,
-            gridRow, gridBox, collect;
+          workspace = workspaceContainer.$.workspace;
 
           assert.equal(workspace.value.recordType, "XM.SalesOrder");
-
           //
-          // Set the customer from the appropriate workspace widget
+          // Set the customer from the appropriate workspace quantityWidget
           //
           var createHash = {
             customer: submodels.customerModel
           // know that the workspace is ready to save.
           // It's good practice to set this trigger *before* we change the line
           // item fields, so that we're 100% sure we're ready for the responses.
-          workspace.value.on("change:total", function () {
+          workspace.value.once("change:total", function () {
+            done();
+            /* The following save was moved to the second test
             smoke.saveWorkspace(workspace, function (err, model) {
               assert.isNull(err);
               // TODO: sloppy
               setTimeout(function () {
                 smoke.deleteFromList(XT.app, model, done);
               }, 2000);
-            });
+            });*/
           });
 
           //
           assert.equal(gridBox.liveModels().length, 1);
         });
       });
+      it('adding a second line item should not copy the item', function (done) {
+        workspace.value.once("change:total", done());
+
+        gridRow.$.itemSiteWidget.$.privateItemSiteWidget.$.input.focus();
+        // Add a new item, check that row exists, and make sure the itemSiteWidget doesn't copy irrelevantly
+        gridBox.newItem();
+        assert.equal(gridBox.liveModels().length, 2);
+        assert.notEqual(submodels.itemModel.id, gridRow.$.itemSiteWidget.$.privateItemSiteWidget.$.input.value);
+
+        // The intention was to delete the above line after verifying that the item doesn't copy but ran into 
+        // many issues so just populating with same data and saving it with 2 line items.
+        gridRow.$.itemSiteWidget.doValueChange({value: {item: submodels.itemModel, site: submodels.siteModel}});
+        gridRow.$.quantityWidget.doValueChange({value: 5});
+
+        /* Delete the line item
+        workspace.value.get("lineItems").models[1].destroy({
+              success: function () {
+                gridBox.setEditableIndex(null);
+                gridBox.$.editableGridRow.hide();
+                gridBox.valueChanged();
+              }
+            });
+        */
+      });
+      // XXX - skip test if site calendar is enabled -
+      // temporary until second notifyPopup (_nextWorkingDate) is handled in test (TODO).
+
+      //it('changing the Schedule Date updates the line item\'s schedule date', function (done) {
+      (skipIfSiteCal ? it.skip : it)(
+        'changing the Schedule Date updates the line item\'s schedule date', function (done) {
+        var getDowDate = function (dow) {
+            var date = new Date(),
+              currentDow = date.getDay(),
+              distance = dow - currentDow;
+            date.setDate(date.getDate() + distance);
+            return date;
+          },
+          newScheduleDate = getDowDate(0); // Sunday from current week
+
+        var handlePopup = function () {
+          assert.equal(workspace.value.get("scheduleDate"), newScheduleDate);
+          // Confirm to update all line items
+          XT.app.$.postbooks.notifyTap(null, {originator: {name: "notifyYes"}});
+          // And verify that they were all updated with the new date
+          setTimeout(function () {
+            _.each(workspace.value.get("lineItems").models, function (model) {
+              assert.equal(newScheduleDate, model.get("scheduleDate"));
+            });
+            done();
+          }, 3000);
+        };
+
+        workspace.value.once("change:scheduleDate", handlePopup);
+        workspace.value.set("scheduleDate", newScheduleDate);
+      });
+      it('save, then delete order', function (done) {
+        assert.equal(workspace.value.status, XM.Model.READY_NEW);
+        smoke.saveWorkspace(workspace, function (err, model) {
+          assert.isNull(err);
+          // TODO: sloppy
+          setTimeout(function () {
+            smoke.deleteFromList(XT.app, model, done);
+          }, 4000);
+        }, true);
+      });
     });
   });
 }());
index 7bbd037..df0c588 100644 (file)
     autoRegex = XM.Document.AUTO_NUMBER + "|" + XM.Document.AUTO_OVERRIDE_NUMBER;
     if (model instanceof XM.Document && model.numberPolicy.match(autoRegex)) {
       // wait for the model to fetch its id if appropriate
+      if (model.id) {
+        // the id is already defined? No need to wait for it from the server, then.
+        done(workspaceContainer);
+        return;
+      }
       eventName = "change:" + model.idAttribute;
       idChanged = function () {
         if (model.id) {