documentDateKey: 'invoiceDate',
couldDestroy: function (callback) {
- callback(XT.session.privileges.get("MaintainMiscInvoices") && !this.get("isPosted"));
+ callback(!this.get("isPosted"));
},
canPost: function (callback) {
- callback(XT.session.privileges.get("PostMiscInvoices") && !this.get("isPosted"));
+ callback(!this.get("isPosted"));
},
canVoid: function (callback) {
- var response = XT.session.privileges.get("VoidPostedInvoices") && this.get("isPosted");
+ var response = this.get("isPosted");
callback(response || false);
},
- canPrint: function (callback) {
- callback(XT.session.privileges.get("PrintInvoices") || false);
- },
-
doPost: function (options) {
this.dispatch("XM.Invoice", "post", [this.id], {
success: options && options.success,
enyo.depends(
"email.js",
- "workflow.js",
"account.js",
"activity.js",
"address.js",
documentDateKey: 'returnDate',
couldDestroy: function (callback) {
- callback(XT.session.privileges.get("MaintainCreditMemos") && !this.get("isPosted"));
+ callback(!this.get("isPosted"));
},
canPost: function (callback) {
- callback(XT.session.privileges.get("PostARDocuments") && !this.get("isPosted"));
+ callback(!this.get("isPosted"));
},
canVoid: function (callback) {
- var response = XT.session.privileges.get("VoidPostedARCreditMemos") && this.get("isPosted");
+ var response = this.get("isPosted");
callback(response || false);
},
- canPrint: function (callback) {
- callback(XT.session.privileges.get("PrintCreditMemos") || false);
- },
-
doPost: function (options) {
this.dispatch("XM.Return", "post", [this.id], {
success: options && options.success,
{attribute: 'number'}
]},
actions: [
- {name: "void", prerequisite: "canVoid", method: "doVoid" },
- {name: "post", prerequisite: "canPost", method: "doPost" },
- {name: "print", prerequisite: "canPrint", method: "doPrint", isViewMethod: true },
- {name: "download", prerequisite: "canPrint", method: "doDownload", isViewMethod: true }
+ {name: "void", privilege: "VoidPostedInvoices", prerequisite: "canVoid",
+ method: "doVoid" },
+ {name: "post", privilege: "PostMiscInvoices", prerequisite: "canPost",
+ method: "doPost" },
+ {name: "print", privilege: "PrintInvoices", method: "doPrint" },
+ {name: "download", privilege: "PrintInvoices", method: "doDownload",
+ isViewMethod: true }
],
components: [
{kind: "XV.ListItem", components: [
actions: [{
name: "convert",
method: "convertProspect",
+ privilege: "MaintainCustomerMasters",
isViewMethod: true
}],
query: {orderBy: [
actions: [{
name: "convert",
method: "convertQuote",
+ privilege: "ConvertQuotes",
isViewMethod: true,
notify: false
}],
parameterWidget: "XV.ReturnListParameters",
collection: "XM.ReturnListItemCollection",
actions: [
- {name: "void", prerequisite: "canVoid", method: "doVoid" },
- {name: "post", prerequisite: "canPost", method: "doPost" },
- {name: "print", prerequisite: "canPrint", method: "doPrint" }
+ {name: "void", privilege: "VoidPostedARCreditMemos",
+ prerequisite: "canVoid", method: "doVoid" },
+ {name: "post", privilege: "PostARDocuments",
+ prerequisite: "canPost", method: "doPost" },
+ {name: "print", privileg: "PrintCreditMemos",
+ method: "doPrint" }
],
create: function () {
this.inherited(arguments);
or collection attribute.
*/
create: function () {
- this.inherited(arguments);
var kindName = this.kind.substring(0, this.kind.length - 4).substring(3);
if (!this.getLabel()) {
this.setLabel(this.determineLabel(kindName));
if (!this.getCollection()) {
this.setCollection("XM." + kindName + "Collection");
}
+ this.inherited(arguments);
},
determineLabel: function (kindName) {
components: [
{kind: "Panels", arrangerKind: "CarouselArranger",
fit: true, components: [
- {kind: "XV.Groupbox", name: "mainPanel", components: [
+ {kind: "XV.Groupbox", name: "mainPanel",
+ components: [
{kind: "onyx.GroupboxHeader", content: "_overview".loc()},
{kind: "XV.ScrollableGroupbox", name: "mainGroup",
- classes: "in-panel", components: [
+ classes: "in-panel", fit: true, components: [
{kind: "XV.InputWidget", attr: "code"},
{kind: "XV.InputWidget", attr: "name"}
]}
"_applications": "Applications",
"_applied": "Applied",
"_appliedAmount": "Applied Amount",
- "_applyARMemos": "Apply A/R Memos",
+ "_applyARMemos": "Apply Receivable Memos",
"_applyBalanceAs": "Apply Balance As",
"_applyBalanceToCreditMemo": "Credit Memo",
"_applyBalanceToCustomerDeposit": "Customer Deposit",
"_enableCustomerDeposits": "Enable Customer Deposits",
"_fundsType": "Type of Funds",
"_hideApplyToBalance": "Hide 'Apply to Balance' Action",
- "_isPosted": "POSTED",
- "_isNotPosted": "NOT POSTED",
+ "_isPosted": "Posted",
+ "_isNotPosted": "Unposted",
"_maintainCashReceipts": "Maintain Cash Receipts",
"_maintainCreditMemos": "Maintain Credit Memos",
"_maintainCurrencies": "Maintain Currencies",
"_otherCreditCard": 'Other Credit Card',
"_orderNumber": "Order #",
"_other": 'Other',
- "_postARDocuments": "Post A/R Documents",
+ "_postARDocuments": "Post Receivable Documents",
"_postCashReceipts": "Post Cash Receipts",
"_postMiscInvoices": "Post Invoices",
"_printInvoices": "Print Invoices",
editableModel: 'XM.Receivable',
canOpen: function (callback) {
- var canView = XT.session.privileges.get("ViewAROpenItems") && this.get("isPosted");
- if (callback) {
- callback(canView);
- }
- return canView;
+ if (callback) { callback(this.get("isPosted")); }
+
+ return this;
},
});
]}
],
actions: [
- {name: 'post', prerequisite: 'canPost', method: 'post'},
- {name: 'void', prerequisite: 'canVoid', method: 'void'},
- {name: 'delete', prerequisite: 'canDelete', method: 'delete'}
+ {name: 'post', privilege: "PostCashReceipts",prerequisite: 'canPost',
+ method: 'post'},
+ {name: 'void', privilege: "VoidPostedCashReceipts",
+ prerequisite: 'canVoid', method: 'void'}
]
},
}}
],
actions: [
- {name: "open", prerequisite: "canOpen",
+ {name: "open", privilege: "ViewAROpenItems", prerequisite: "canOpen",
method: "openReceivable", notify: false, isViewMethod: true}
],
components: [
actions: [
{
name: 'deactivate',
+ privilege: "MaintainSalesCategories",
prerequisite: 'canDeactivate',
method: 'deactivate'
}
kind: "XV.GridBox",
classes: "large-panel",
workspace: "XV.ProjectTaskWorkspace",
+ parentKey: "project",
orderBy: [{attribute: 'number'}],
title: "_tasks".loc(),
columns: [
editableModel: "XM.PurchaseOrder",
canRelease: function (callback) {
- var status = this.get("status"),
- ret = XT.session.privileges.get("ReleasePurchaseOrders") &&
- status === XM.PurchaseOrder.UNRELEASED_STATUS;
+ var status = this.get("status");
+
if (callback) {
- callback(ret);
+ callback(status === XM.PurchaseOrder.UNRELEASED_STATUS);
}
- return ret;
+
+ return this;
},
canUnrelease: function (callback) {
var status = this.get("status"),
- ret = XT.session.privileges.get("ReleasePurchaseOrders") &&
- status === XM.PurchaseOrder.OPEN_STATUS,
- options = {},
- params;
+ ret = status === XM.PurchaseOrder.OPEN_STATUS,
+ options = {},
+ params;
+
if (ret) {
params = [this.id, true];
options.success = function (resp) {
parameterWidget: "XV.PurchaseOrderListParameters",
multiSelect: true,
actions: [
- {name: "release", prerequisite: "canRelease", method: "doRelease",
+ {name: "release", privilege: "ReleasePurchaseOrders",
+ prerequisite: "canRelease", method: "doRelease",
notify: false},
- {name: "unrelease", prerequisite: "canUnrelease",
+ {name: "unrelease", privilege: "ReleasePurchaseOrders",
+ prerequisite: "canUnrelease",
method: "doUnrelease", notify: false}
],
query: {orderBy: [
enforceUpperKey: false,
- defaults: {
- characteristicType: 0,
- isAddresses: false,
- isContacts: false,
- isItems: false,
- isEmployees: false,
- isInvoices: false,
- order: 0,
- isSearchable: true
+ defaults: function () {
+ return {
+ characteristicType: 0,
+ isAddresses: false,
+ isContacts: false,
+ isItems: false,
+ isEmployees: false,
+ isInvoices: false,
+ order: 0,
+ isSearchable: true
+ };
},
contextAttributes: [
});
+ /**
+ This mixin is to be used on objects that have workflow model arrays.
+ */
+ XM.WorkflowMixin = {
+ /**
+ A helper function to copy workflow templates from a source to the destination object.
+ */
+ inheritWorkflowSource: function (sourceModel, characteristicClass, workflowClass) {
+ var charProfile = sourceModel && characteristicClass ? sourceModel.get("characteristics") : false,
+ wfProfile = sourceModel && workflowClass ? sourceModel.get("workflow") : false,
+ chars = this.get("characteristics"),
+ workflow = this.get("workflow"),
+ that = this,
+ CharacteristicClass = XT.getObjectByName(characteristicClass),
+ WorkflowClass = XT.getObjectByName(workflowClass),
+
+ // Copies characteristics from source to destination
+ copyCharProfile = function () {
+ chars.reset();
+ _.each(charProfile.models, function (model) {
+ var assignment = new CharacteristicClass(null, {isNew: true});
+ assignment.set("characteristic", model.get("characteristic"));
+ assignment.set("value", model.get("value"));
+ chars.add(assignment);
+ });
+ },
+
+ // Copies workflow from source to destination
+ copyWfProfile = function () {
+ var map = {};
+ workflow.reset();
+ _.each(wfProfile.models, function (model) {
+ var item = new WorkflowClass(null, {isNew: true}),
+ id = XT.generateUUID(),
+ dueOffset = model.get("dueOffset"),
+ startOffset = model.get("startOffset"),
+ dueDate,
+ startDate;
+
+ map[model.id] = id;
+
+ if (model.get("dueSet")) {
+ dueDate = XT.date.today();
+ dueDate.setDate(dueDate.getDate() + model.get("dueOffset"));
+ }
+
+ if (model.get("startSet")) {
+ startDate = XT.date.today();
+ startDate.setDate(startDate.getDate() + model.get("startOffset"));
+ }
+
+ item.set({
+ uuid: id,
+ name: model.get("name"),
+ description: model.get("description"),
+ status: model.get("status"),
+ workflowType: model.get("workflowType"),
+ priority: model.get("priority"),
+ startDate: startDate,
+ dueDate: dueDate,
+ owner: model.get("owner") || item.get("owner"),
+ assignedTo: model.get("assignedTo") || item.get("assignedTo"),
+ sequence: model.get("sequence"),
+ notes: model.get("notes"),
+ completedParentStatus : model.get("completedParentStatus"),
+ deferredParentStatus : model.get("deferredParentStatus"),
+ completedSuccessors: model.get("completedSuccessors"),
+ deferredSuccessors: model.get("deferredSuccessors")
+ });
+ workflow.add(item);
+ });
+
+ // Reiterate through new collection and fix successor mappings
+ _.each(_.keys(map), function (uuid) {
+ _.each(workflow.models, function (model) {
+ var successors = model.get("completedSuccessors");
+ if (_.isString(successors)) {
+ model.set("completedSuccessors", successors.replace(uuid, map[uuid]));
+ }
+ successors = model.get("deferredSuccessors");
+ if (_.isString(successors)) {
+ model.set("deferredSuccessors", successors.replace(uuid, map[uuid]));
+ }
+ });
+ });
+ },
+ handleWfProfile = function () {
+ // Handle copying workflow
+ if (wfProfile && wfProfile.length) {
+ if (!workflow.length) {
+ copyWfProfile();
+ } else {
+ that.notify("_copyWorkflow?".loc(), {
+ type: XM.Model.QUESTION,
+ callback: function (response) {
+ if (response.answer) {
+ copyWfProfile();
+ }
+ }
+ });
+ }
+ }
+ };
+
+ // Handle copying characteristics
+ if (charProfile && charProfile.length) {
+ if (!chars.length) {
+ copyCharProfile();
+ } else {
+ this.notify("_copyCharacteristics?".loc(), {
+ type: XM.Model.QUESTION,
+ callback: function (response) {
+ if (response.answer) {
+ copyCharProfile();
+ }
+ handleWfProfile();
+ }
+ });
+ return;
+ }
+ }
+ handleWfProfile();
+ }
+ };
+
}());
@param {Number} Value
@returns {String}
*/
- formatMoney: function (value) {
+ formatMoney: function (value, view) {
+ view.addRemoveClass("error", value < 0);
return Globalize.format(value, "c" + XT.locale.currencyScale);
},
model = new Klass(null, {isNew: true}),
editor = this.$.editableGridRow;
- this.getValue().add(model);
+ this.getValue().add(model, {status: XM.Model.READY_NEW});
this.setEditableIndex(editableIndex);
this.valueChanged();
// XXX hack. not sure why the port doesn't know to resize down
onPrintSelectList: "",
onReportList: "",
onSelectionChanged: "",
+ onTransactionList: "",
onWorkspace: "",
onNotify: ""
},
deleteAction = _.findWhere(actions, {"name": "delete"}),
collection,
Klass,
- method;
+ method,
+ privilege,
+ privs;
+
this._actionPermissions = [];
this._haveAllAnswers = [];
collection = this.getValue();
Klass = collection ? collection.model : false;
+
+ // Find the function that determines if the select object could be deleted.
method = Klass && Klass.prototype.couldDestroy ? "couldDestroy" : "canDestroy";
+ // Find the privilege associated with deletion for this object.
+ if (Klass.prototype.editableModel) {
+ Klass = XT.getObjectByName(Klass.prototype.editableModel);
+ }
+ privs = Klass.prototype.privileges;
+ privilege = privs && privs.all && privs.all.delete ? privs.all.delete : undefined;
+
// add the delete action if it's not already there
if (!deleteAction && this.getShowDeleteAction()) {
actions.push({
name: "delete",
+ privilege: privilege,
prerequisite: method,
notifyMessage: "_confirmDelete".loc() + " " + "_confirmAction".loc(),
method: "deleteItem",
previous: function () {
// Stock implementation is screwy, do our own
var last = this.getActive(),
- previous = this.index - 1;
+ previous = this.index - 1,
+ active;
this.setIndex(previous);
+
+ // Provide a way to let panels or their children know they have been activated
+ active = this.getActive();
+ active.waterfallDown("onActivatePanel", {activated: active});
+
last.destroy();
},
popupWorkspaceNotify: function (inSender, inEvent) {
close: function (options) {
var that = this,
workspace = this.$.workspace,
- model = this.$.workspace.getValue(),
- callback = function () {
- if (model && model.hasLockKey && model.hasLockKey()) {
- model.releaseLock({
- success: function () {
- that.doPrevious();
- },
- error: function () {
- XT.log("Error releasing lock");
- that.doPrevious();
- }
- });
- } else {
- that.doPrevious();
- }
- };
+ model = this.$.workspace.getValue();
options = options || {};
if (!options.force) {
if (action.isViewMethod) {
list.$.listItem.doActionSelected({
model: model,
- action: action
+ action: action,
+ callback: callback
});
// If the list item model doesn't have the function being asked for, try the editable version
// then add whatever actions are applicable
_.each(inEvent.actions, function (action) {
var name = action.name,
- isDisabled = !inEvent.actionPermissions[name],
+ isDisabled = action.privilege ?
+ !XT.session.privileges.get(action.privilege) : false,
component = _.find(menu.$, function (value, key) {
// see if the component has already been created
return key === name;
component.setDisabled(isDisabled);
} else {
- // otherwise if we have permissions, make it
- menu.createComponent({
- name: name,
- kind: 'XV.MenuItem',
- content: action.label || ("_" + name).loc(),
- action: action,
- disabled: isDisabled
- });
+ // otherwise if we are allowed, make it
+ if (inEvent.actionPermissions[name]) {
+ menu.createComponent({
+ name: name,
+ kind: 'XV.MenuItem',
+ content: action.label || ("_" + name).loc(),
+ action: action,
+ disabled: isDisabled
+ });
+ }
} // else if it doesn't exist and we don't have permissions, do nothing
-
+
});
+
+ // If no menu items, let the user know something about the situation
+ if (!menu.getClientControls().length) {
+ menu.createComponent({
+ name: "noActions",
+ kind: 'XV.MenuItem',
+ content: "_noEligibleActions".loc(),
+ disabled: true
+ });
+ }
+
menu.render();
// convoluted code to help us deceive the Menu into thinking
for (var i = 1; i < privArray.length; i++) {
sql = sql + ' or priv_name = $' + (i + 2);
}
- sql = sql + ";";
+ sql = sql + "order by granted desc limit 1;";
/* Cleverness: the query parameters are just the priv array with the username tacked on front. */
privArray.unshift(XT.username);
}, {
code: "xt2021",
messageKey: "_itemSiteActiveItemInactive"
- }, {
- code: "xt2021",
- messageKey: "_itemSiteActiveQtyOnHand"
}, {
code: "xt2022",
messageKey: "_canNotCreateOrderOnCreditWarn"
this.timeout(100 * 60 * 1000);
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 = config.databaseServer,
databaseName = loginData.org;
it('should build without error on a brand-new database', function (done) {
done();
});
});
+
+ it('should grant all privileges to the user', function (done) {
+ var sql = "insert into usrpriv (usrpriv_username, usrpriv_priv_id) " +
+ "select $1, priv_id " +
+ "from priv " +
+ "left join usrpriv on priv_id = usrpriv_priv_id and usrpriv_username = $1 " +
+ "where usrpriv_id is null";
+
+ creds.database = databaseName;
+ creds.parameters = [loginData.username];
+ datasource.query(sql, creds, function (err, res) {
+ assert.isNull(err);
+ done();
+ });
+ });
});
}());
});
it('should all be accessible', function (done) {
- this.timeout(30 * 1000);
+ this.timeout(80 * 1000);
var navigator, workspace,
list,
i = -1;
workspaceContainer = XT.app.$.postbooks.getActive();
assert.equal(workspaceContainer.kind, "XV.WorkspaceContainer");
workspace = workspaceContainer.$.workspace;
-
//workspace.value.set("test", "test");
//workspaceContainer.saveAndClose({force: true});
//{kind: "XV.SalesOrderList", model: "XM.SalesOrder", update: "fob"},
//{kind: "XV.CustomerList", model: "XM.Customer", update: "notes"}, // zombie can't handle TTOYS
{kind: "XV.ProspectList", model: "XM.Prospect", update: "notes"},
- {kind: "XV.QuoteList", model: "XM.Quote", update: "fob"},
+ //{kind: "XV.QuoteList", model: "XM.Quote", update: "fob"},
{kind: "XV.ItemList", model: "XM.Item", update: "notes"}
];
done();
});
});
- it("Cannot delete unposted invoices where the user has no MaintainMiscInvoices privilege",
- function (done) {
- var model = new XM.InvoiceListItem();
- XT.session.privileges.attributes.MaintainMiscInvoices = false;
- model.couldDestroy(function (response) {
- assert.isFalse(response);
- done();
- });
- });
it("Cannot delete invoices that are already posted", function (done) {
var model = new XM.InvoiceListItem();
model.set({isPosted: true});
done();
});
});
- it("Cannot post invoices where the user has no PostMiscInvoices privilege", function (done) {
- var model = new XM.InvoiceListItem();
- XT.session.privileges.attributes.PostMiscInvoices = false;
- model.canPost(function (response) {
- assert.isFalse(response);
- done();
- });
- });
it("Cannot post invoices that are already posted", function (done) {
var model = new XM.InvoiceListItem();
model.set({isPosted: true});
done();
});
});
- it("Cannot void invoices where the user has no VoidPostedInvoices privilege",
- function (done) {
- var model = new XM.InvoiceListItem();
- model.set({isPosted: true});
- XT.session.privileges.attributes.VoidPostedInvoices = false;
- model.canVoid(function (response) {
- assert.isFalse(response);
- done();
- });
- });
it("Cannot void invoices that are not already posted", function (done) {
var model = new XM.InvoiceListItem();
model.set({isPosted: false});
done();
});
});
- it("Print invoices where the user has the PrintInvoices privilege", function (done) {
- var model = new XM.InvoiceListItem();
- model.canPrint(function (response) {
- assert.isTrue(response);
- done();
- });
- });
- it("Cannot print invoices where the user has no PrintInvoices privilege", function (done) {
- var model = new XM.InvoiceListItem();
- XT.session.privileges.attributes.PrintInvoices = false;
- model.canPrint(function (response) {
- assert.isFalse(response);
- done();
- });
- });
/**
@member -
@memberof Invoice.prototype
read: "ViewCreditMemos"
},
createHash: {
- customer: {number: "XRETAIL"}
+ //customer: {number: "XRETAIL"}
+ customer: {number: "TTOYS"}
},
updatableField: "notes",
beforeSaveActions: [{it: 'sets up a valid line item',
done();
});
});
- it("Cannot delete unposted Returns where the user has no MaintainCreditMemos privilege",
- function (done) {
- var model = new XM.ReturnListItem();
- XT.session.privileges.attributes.MaintainCreditMemos = false;
- model.couldDestroy(function (response) {
- assert.isFalse(response);
- done();
- });
- });
it("Cannot delete Returns that are already posted", function (done) {
var model = new XM.ReturnListItem();
model.set({isPosted: true});
done();
});
});
- it("Cannot post Returns where the user has no PostARDocuments privilege", function (done) {
- var model = new XM.ReturnListItem();
- XT.session.privileges.attributes.PostARDocuments = false;
- model.canPost(function (response) {
- assert.isFalse(response);
- done();
- });
- });
it("Cannot post Returns that are already posted", function (done) {
var model = new XM.ReturnListItem();
model.set({isPosted: true});
done();
});
});
- it("Cannot void Returns where the user has no VoidPostedARCreditMemos privilege",
- function (done) {
- var model = new XM.ReturnListItem();
- model.set({isPosted: true});
- XT.session.privileges.attributes.VoidPostedARCreditMemos = false;
- model.canVoid(function (response) {
- assert.isFalse(response);
- done();
- });
- });
it("Cannot void Returns that are not already posted", function (done) {
var model = new XM.ReturnListItem();
model.set({isPosted: false});
done();
});
});
- it("Print Returns where the user has the PrintCreditMemos privilege", function (done) {
- var model = new XM.ReturnListItem();
- model.canPrint(function (response) {
- assert.isTrue(response);
- done();
- });
- });
- it("Cannot print Returns where the user has no PrintCreditMemos privilege", function (done) {
- var model = new XM.ReturnListItem();
- XT.session.privileges.attributes.PrintCreditMemos = false;
- model.canPrint(function (response) {
- assert.isFalse(response);
- done();
- });
- });
/**
@member -
@memberof Return.prototype