issue #20438: Implement convert quote to work order.
+1.x.x (???/xx/xx)
+
+This version requires version 4.2.0 or higher of xTuple PostBooks or commercial edition database.
+
+1.4.5 (2013/10/11)
+==================
+- Fixed
+ issue #[19869](http://www.xtuple.org/xtincident/view/bugs/19869)
+ _*Omnibus: Locked record is displayed on selecting to open a contact after discarding new contact screen opened from it _
+- Fixed
+ issue #[19957](http://www.xtuple.org/xtincident/view/bugs/19957)
+ _Selecting to create a new Customer/Prospect from the customer field of Quote screen doesn't populates the customer automatically_
+- Fixed
+ issue #[20000](http://www.xtuple.org/xtincident/view/bugs/20000)
+ _* It is not possible to delete the customer SHIP-TO on reopening the customer_
+- Fixed
+ issue #[20012](http://www.xtuple.org/xtincident/view/bugs/20012)
+ _*Selected sales representative commission rate is not displayed automatically on the Customer screen_
+- Fixed
+ issue #[20064](http://www.xtuple.org/xtincident/view/bugs/20064)
+ _*Ship-To Number search screen is not labeled_
+- Fixed
+ issue #[20071](http://www.xtuple.org/xtincident/view/bugs/20071)
+ _Saving while Customer Ship-To is open gives error then causes other issues_
+- Fixed
+ issue #[20173](http://www.xtuple.org/xtincident/view/bugs/20173)
+ _*It is not possible to assign a Tax Authority to a Tax Code_
+- Fixed
+ issue #[20196](http://www.xtuple.org/xtincident/view/bugs/20196)
+ _*Data Source error is displayed on selecting to search the item sites screen with 'Class Code' filter_
+- Duplicate
+ issue #[20198](http://www.xtuple.org/xtincident/view/bugs/20198)
+ _*'Mask' and 'Validator' fields present under characteristic of type 'Text' are not functional_
+- Implemented
+ issue #[20311](http://www.xtuple.org/xtincident/view/bugs/20311)
+ _Welcome screen iframe does not scroll on iOS devices. CSS fix attached. ASM#5469_
+- Reopened
+ issue #[20332](http://www.xtuple.org/xtincident/view/bugs/20332)
+ _Error Adding a Sales Order to an Opportunity_
+- Implemented
+ issue #[20682](http://www.xtuple.org/xtincident/view/bugs/20682)
+ _Inventory History Report_
+- No Change Required
+ issue #[20885](http://www.xtuple.org/xtincident/view/bugs/20885)
+ _Time Expense Version has Incorrect title_
+- Fixed
+ issue #[20888](http://www.xtuple.org/xtincident/view/bugs/20888)
+ _Class Code List updates after a Discard_
+- Fixed
+ issue #[20981](http://www.xtuple.org/xtincident/view/bugs/20981)
+ _BI Readme steps updated_
+- Fixed
+ issue #[20994](http://www.xtuple.org/xtincident/view/bugs/20994)
+ _Clicking on Advanced Search displays History_
+- Fixed
+ issue #[21008](http://www.xtuple.org/xtincident/view/bugs/21008)
+ _*CRM Dashboard charts are duplicated on selecting to refresh_
+- Fixed
+ issue #[21062](http://www.xtuple.org/xtincident/view/bugs/21062)
+ _Using Help Pullout Tab gives Java Console Error_
+- Fixed
+ issue #[21110](http://www.xtuple.org/xtincident/view/bugs/21110)
+ _*Omnibus: Record is locked for some time on selecting 'Save and New' or 'New' button from the record screen_
+- Fixed
+ issue #[21114](http://www.xtuple.org/xtincident/view/bugs/21114)
+ _*observation: Unable to delete a Sales Representative_
+- Fixed
+ issue #[21181](http://www.xtuple.org/xtincident/view/bugs/21181)
+ _*New Quotes/Sales Orders created from the Opportunity screen are not displayed as attached to the Opportunity_
+- Fixed
+ issue #[21182](http://www.xtuple.org/xtincident/view/bugs/21182)
+ _*Omnibus: Selecting to delete a characteristic and save the record displays irrelevant dialog_
+- Fixed
+ issue #[21230](http://www.xtuple.org/xtincident/view/bugs/21230)
+ _*Employee screen doesn't save the Group attached to it_
+- Fixed
+ issue #[21232](http://www.xtuple.org/xtincident/view/bugs/21232)
+ _*Translation is required for the description label in the 'Advanced Search' panel of Employee Group list_
+- Duplicate
+ issue #[21248](http://www.xtuple.org/xtincident/view/bugs/21248)
+ _* Translation is required for the timeExpense label in About screen_
+- Fixed
+ issue #[21270](http://www.xtuple.org/xtincident/view/bugs/21270)
+ _*Translation is required for the label in About screen_
+- Fixed
+ issue #[21383](http://www.xtuple.org/xtincident/view/bugs/21383)
+ _Found/Fixed in lists are not populated on Advanced Search_
+- Fixed
+ issue #[21396](http://www.xtuple.org/xtincident/view/bugs/21396)
+ _Misbehavior in Time/Expense editor panels_
+- Fixed
+ issue #[21401](http://www.xtuple.org/xtincident/view/bugs/21401)
+ _Gear on worksheet list inconsistent_
+- Fixed
+ issue #[21402](http://www.xtuple.org/xtincident/view/bugs/21402)
+ _All new records prompt to discard on iPad_
+- No Change Required
+ issue #[21415](http://www.xtuple.org/xtincident/view/bugs/21415)
+ _Attaching an Incident or Contact to an Account will not save_
+- Fixed
+ issue #[21419](http://www.xtuple.org/xtincident/view/bugs/21419)
+ _Worksheet owned by person who created it_
+- Fixed
+ issue #[21425](http://www.xtuple.org/xtincident/view/bugs/21425)
+ _List box editor doesn't validate_
+- Fixed
+ issue #[21437](http://www.xtuple.org/xtincident/view/bugs/21437)
+ _Pickers not populating on configuration_
+- Fixed
+ issue #[21440](http://www.xtuple.org/xtincident/view/bugs/21440)
+ _*Delete option is inactive for Tax Authorities_
+- Fixed
+ issue #[21451](http://www.xtuple.org/xtincident/view/bugs/21451)
+ _Timesheet remembers my location every time_
+- Fixed
+ issue #[21454](http://www.xtuple.org/xtincident/view/bugs/21454)
+ _Search box pushed off the screen_
+- Fixed
+ issue #[21473](http://www.xtuple.org/xtincident/view/bugs/21473)
+ _Unable to login Standard if Inventory or Sales is not enabled_
+- Fixed
+ issue #[21479](http://www.xtuple.org/xtincident/view/bugs/21479)
+ _*'View Inventory History' privilege under 'Inventory' module requires translation_
+- Fixed
+ issue #[21480](http://www.xtuple.org/xtincident/view/bugs/21480)
+ _*Transaction Date, Transaction Type and Order Type options in the Sort By list of 'Inventory History' require translation_
+- Implemented
+ issue #[21487](http://www.xtuple.org/xtincident/view/bugs/21487)
+ _grid entry in quote_
+- Fixed
+ issue #[21496](http://www.xtuple.org/xtincident/view/bugs/21496)
+ _*It is possible to select a project in 'Complete' status to create a time/expense sheet_
+- Fixed
+ issue #[21500](http://www.xtuple.org/xtincident/view/bugs/21500)
+ _Icons on pickers overlap text when scrolling_
+- Fixed
+ issue #[21505](http://www.xtuple.org/xtincident/view/bugs/21505)
+ _Mobile object names inconsistent with precedent_
+- Fixed
+ issue #[21515](http://www.xtuple.org/xtincident/view/bugs/21515)
+ _*Incorrect Project Task is displayed in the Time/Expense sheet of a worksheet_
+- Fixed
+ issue #[21540](http://www.xtuple.org/xtincident/view/bugs/21540)
+ _Characteristics don't get disabled_
+- Fixed
+ issue #[21541](http://www.xtuple.org/xtincident/view/bugs/21541)
+ _*Unable to update the Schedule date of a Quote_
+- Fixed
+ issue #[21565](http://www.xtuple.org/xtincident/view/bugs/21565)
+ _Selecting new time entry brings up prior entry_
+- Implemented
+ issue #[21581](http://www.xtuple.org/xtincident/view/bugs/21581)
+ _Add freight class picker to item workspace_
+- Fixed
+ issue #[21605](http://www.xtuple.org/xtincident/view/bugs/21605)
+ _Document linkages are only showing up on one side_
+- Fixed
+ issue #[21627](http://www.xtuple.org/xtincident/view/bugs/21627)
+ _Sales analysis (and other) object name problem_
+
+
1.4.4 (2013/09/27)
==================
"_isPrinted": "Printed",
"_isPublic": "Public",
"_isSold": "Sold",
+ "_issueItem": "Issue Item",
"_isSearchable": "Searchable",
"_isSystem": "System",
"_item": "Item",
"_manager": "Manager",
"_mainAddress": "Main Address",
"_manufactured": "Manufactured",
+ "_manufacturing": "Manufacturing",
"_map": "Map",
"_margin": "Margin",
"_markup": "Markup",
"_showCompletedOnly": "Show Complete Only",
"_showExpired": "Show Expired",
"_showUnReleased": "Show Unreleased",
+ "_showOnlyTopLevel": "Show Only Top Level",
"_showInactive": "Show Inactive",
"_sold": "Sold",
"_soldRanking": "Sold Ranking",
"_welcome": "Welcome",
"_wholesalePrice": "Wholesale Price",
"_workOrder": "Work Order",
+ "_workOrders": "Work Orders",
"_xtuplePostbooks": "PostBooks",
"_yearExpired": "Expiration Year",
"user_account.js",
"user_chart.js",
"static.js",
- "vendor.js"
+ "vendor.js",
+ "work_order.js"
);
--- /dev/null
+/*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 */
+
+(function () {
+
+ "use strict";
+
+ /**
+ @class
+
+ @extends XM.Model
+ */
+ XM.WorkOrder = XM.Model.extend({
+ /** @lends XM.WorkOrder.prototype */
+
+ recordType: 'XM.WorkOrder',
+
+ /**
+ Returns incident status as a localized string.
+
+ @returns {String}
+ */
+ getWorkOrderStatusString: function () {
+ var K = XM.WorkOrder,
+ status = this.get('status');
+ if (status === K.RELEASED) {
+ return '_released'.loc();
+ }
+ if (status === K.EXPLODED) {
+ return '_exploded'.loc();
+ }
+ if (status === K.INPROCESS) {
+ return '_in-process'.loc();
+ }
+ if (status === K.OPEN) {
+ return '_open'.loc();
+ }
+ if (status === K.CLOSED) {
+ return '_closed'.loc();
+ }
+ },
+
+ /**
+ Calculate the balance remaining to issue.
+
+ @returns {Number}
+ */
+ postBalance: function () {
+ var qtyOrdered = this.get("qtyOrdered"),
+ qtyReceived = this.get("qtyReceived"),
+ toPost = XT.math.subtract(qtyReceived, qtyOrdered, XT.QUANTITY_SCALE);
+ return toPost >= 0 ? toPost : 0;
+ }
+
+ });
+
+ /**
+ @class
+
+ @extends XM.Info
+ */
+ XM.WorkOrderRelation = XM.Info.extend({
+ /** @lends XM.WorkOrderRelation.prototype */
+
+ recordType: 'XM.WorkOrderRelation',
+
+ editableModel: 'XM.WorkOrder'
+
+ });
+
+ /**
+ @class
+
+ @extends XM.Info
+ */
+ XM.WorkOrderListItem = XM.Info.extend({
+ /** @lends XM.WorkOrderListItem.prototype */
+
+ recordType: 'XM.WorkOrderListItem',
+
+ editableModel: 'XM.WorkOrder'
+
+ });
+
+ _.extend(XM.WorkOrder, {
+ /** @scope XM.WorkOrderListItem */
+
+ /**
+ Released Status.
+
+ @static
+ @constant
+ @type String
+ @default R
+ */
+ RELEASED: 'R',
+
+ /**
+ Expoloded status.
+
+ @static
+ @constant
+ @type String
+ @default E
+ */
+ EXPLODED: 'E',
+
+ /**
+ In-Process status.
+
+ @static
+ @constant
+ @type String
+ @default I
+ */
+ INPROCESS: 'I',
+
+ /**
+ Open Status.
+
+ @static
+ @constant
+ @type String
+ @default S
+ */
+ OPEN: 'O',
+
+ /**
+ Start Date.
+
+ @static
+ @constant
+ @type String
+ @default S
+ */
+ START_DATE: 'S',
+
+ /**
+ Explosion Date.
+
+ @static
+ @constant
+ @type String
+ @default E
+ */
+ EXPLOSION_DATE: 'E',
+
+ /**
+ Single Level.
+
+ @static
+ @constant
+ @type String
+ @default S
+ */
+ SINGLE_LEVEL: 'S',
+
+ /**
+ Multiple Level.
+
+ @static
+ @constant
+ @type String
+ @default M
+ */
+ MULTIPLE_LEVEL: 'M',
+
+ /**
+ To Date.
+
+ @static
+ @constant
+ @type String
+ @default D
+ */
+ TO_DATE: 'D',
+
+ /**
+ Proportional.
+
+ @static
+ @constant
+ @type String
+ @default P
+ */
+ PROPORTIONAL: 'P'
+
+ });
+
+ // ..........................................................
+ // COLLECTIONS
+ //
+
+ /**
+ @class
+
+ @extends XM.Collection
+ */
+ XM.WorkOrderListItemCollection = XM.Collection.extend(/** @lends XM.WorkOrderListItemCollection.prototype */{
+
+ model: XM.WorkOrderListItem
+
+ });
+
+ /**
+ @class
+
+ @extends XM.Collection
+ */
+ XM.WorkOrderRelationCollection = XM.Collection.extend(/** @lends XM.WorkOrderRelationCollection.prototype */{
+
+ model: XM.WorkOrderRelation
+
+ });
+
+}());
XV.registerModelList("XM.VendorRelation", "XV.VendorList");
+ // ..........................................................
+ // WORK ORDER
+ //
+
+ enyo.kind({
+ name: "XV.WorkOrderList",
+ kind: "XV.List",
+ label: "_workOrders".loc(),
+ collection: "XM.WorkOrderListItemCollection",
+ parameterWidget: "XV.WorkOrderListParameters",
+ query: {orderBy: [
+ {attribute: 'number'}
+ ]},
+ components: [
+ {kind: "XV.ListItem", components: [
+ {kind: "FittableColumns", components: [
+ {kind: "XV.ListColumn", components: [
+ {kind: "XV.ListAttr", attr: "number", isKey: true, fit: true}
+ ]},
+ {kind: "XV.ListColumn", classes: "first", components: [
+ {kind: "FittableColumns", components: [
+ {kind: "XV.ListAttr", attr: "status",
+ style: "padding-left: 24px"},
+ {kind: "XV.ListAttr", attr: "itemSite.item.number", classes: "bold", style: "padding-left: 12px"}
+ ]},
+ {kind: "FittableColumns", components: [
+ {kind: "XV.ListAttr", attr: "itemSite.site.code", style: "padding-left: 12px"},
+ {kind: "XV.ListAttr", attr: "itemSite.item.description1", classes: "italic"}
+ ]}
+ ]},
+ {kind: "XV.ListColumn", classes: "second", components: [
+ {kind: "FittableColumns", components: [
+ {kind: "XV.ListAttr", attr: "dueDate", classes: "right"}
+ ]}
+ ]},
+ {kind: "XV.ListColumn", classes: "last", components: [
+ {kind: "FittableColumns", components: [
+ {kind: "XV.ListAttr", attr: "itemSite.item.inventoryUnit.name"},
+ {kind: "XV.ListAttr", attr: "qtyOrdered"},
+ {kind: "XV.ListAttr", attr: "qtyReceived"}
+ ]}
+ ]}
+ ]}
+ ]}
+ ]
+ });
+
+ XV.registerModelList("XM.WorkOrderListItem", "XV.WorkOrderList");
+
enyo.kind({
name: "XV.NameList",
kind: "XV.List",
{kind: "onyx.GroupboxHeader", content: "_overview".loc()},
{kind: "XV.ScrollableGroupbox", name: "mainGroup", fit: true,
classes: "in-panel", components: [
- {kind: "XV.InputWidget", attr: "number"},
- {kind: "XV.InputWidget", attr: "name"},
- {kind: "XV.ProjectStatusPicker", attr: "status"},
- {kind: "onyx.GroupboxHeader", content: "_schedule".loc()},
- {kind: "XV.DateWidget", attr: "dueDate"},
- {kind: "XV.DateWidget", attr: "startDate"},
- {kind: "XV.DateWidget", attr: "assignDate"},
- {kind: "XV.DateWidget", attr: "completeDate"},
- {kind: "onyx.GroupboxHeader", content: "_userAccounts".loc()},
- {kind: "XV.UserAccountWidget", attr: "owner"},
- {kind: "XV.UserAccountWidget", attr: "assignedTo"},
- {kind: "onyx.GroupboxHeader", content: "_notes".loc()},
- {kind: "XV.TextArea", attr: "notes", fit: true},
- {kind: "onyx.GroupboxHeader", content: "_relationships".loc()},
- {kind: "XV.AccountWidget", attr: "account"},
- {kind: "XV.ContactWidget", attr: "contact"}
+ {name: "overviewControl", components:[
+ {kind: "XV.InputWidget", attr: "number"},
+ {kind: "XV.InputWidget", attr: "name"},
+ {kind: "XV.ProjectStatusPicker", attr: "status"},
+ {kind: "onyx.GroupboxHeader", content: "_schedule".loc()},
+ {kind: "XV.DateWidget", attr: "dueDate"},
+ {kind: "XV.DateWidget", attr: "startDate"},
+ {kind: "XV.DateWidget", attr: "assignDate"},
+ {kind: "XV.DateWidget", attr: "completeDate"},
+ {kind: "onyx.GroupboxHeader", content: "_userAccounts".loc()},
+ {kind: "XV.UserAccountWidget", attr: "owner"},
+ {kind: "XV.UserAccountWidget", attr: "assignedTo"},
+ {kind: "onyx.GroupboxHeader", content: "_notes".loc()},
+ {kind: "XV.TextArea", attr: "notes", fit: true},
+ {kind: "onyx.GroupboxHeader", content: "_relationships".loc()},
+ {kind: "XV.AccountWidget", attr: "account"},
+ {kind: "XV.ContactWidget", attr: "contact"}
+ ]}
]}
]},
{kind: "XV.ProjectTasksBox", attr: "tasks"},
{kind: "XV.InputWidget", attr: "name"},
{kind: "XV.CharacteristicTypePicker", name: "typePicker", attr: "characteristicType"},
{kind: "XV.CheckboxWidget", attr: "isSearchable"},
- {kind: "onyx.GroupboxHeader", content: "_roles".loc()},
- {kind: "XV.ToggleButtonWidget", attr: "isAddresses", label: "_address".loc()},
- {kind: "XV.ToggleButtonWidget", attr: "isContacts", label: "_contact".loc()},
- {kind: "XV.ToggleButtonWidget", attr: "isAccounts", label: "_account".loc()},
- {kind: "XV.ToggleButtonWidget", attr: "isIncidents", label: "_incident".loc()},
- {kind: "XV.ToggleButtonWidget", attr: "isItems", label: "_item".loc()},
- {kind: "XV.ToggleButtonWidget", attr: "isOpportunities", label: "_opportunity".loc()},
- {kind: "XV.ToggleButtonWidget", attr: "isEmployees", label: "_employees".loc()},
{kind: "onyx.GroupboxHeader", content: "_notes".loc()},
- {kind: "XV.TextArea", attr: "notes", fit: true},
+ {kind: "XV.TextArea", attr: "notes", fit: true, name: "notesHeader"},
{name: "advancedPanel", showing: false, components: [
{kind: "onyx.GroupboxHeader", content: "_advanced".loc()},
{kind: "XV.InputWidget", attr: "mask"},
]}
]}
]},
- {kind: "XV.CharacteristicOptionBox", name: "optionsPanel", attr: "options", showing: false}
+ {kind: "XV.Groupbox", name: "rolesPanel", title: "_roles".loc(),
+ components: [
+ {kind: "onyx.GroupboxHeader", content: "_roles".loc()},
+ {kind: "XV.ScrollableGroupbox", name: "rolesGroup", fit: true,
+ classes: "in-panel", components: [
+ {kind: "XV.ToggleButtonWidget", attr: "isAddresses", label: "_addresses".loc()},
+ {kind: "XV.ToggleButtonWidget", attr: "isContacts", label: "_contacts".loc()},
+ {kind: "XV.ToggleButtonWidget", attr: "isAccounts", label: "_accounts".loc()},
+ {kind: "XV.ToggleButtonWidget", attr: "isIncidents", label: "_incidents".loc()},
+ {kind: "XV.ToggleButtonWidget", attr: "isItems", label: "_items".loc()},
+ {kind: "XV.ToggleButtonWidget", attr: "isOpportunities", label: "_opportunities".loc()},
+ {kind: "XV.ToggleButtonWidget", attr: "isEmployees", label: "_employees".loc()},
+ ]}
+ ]},
+ {kind: "XV.CharacteristicOptionBox", name: "optionsPanel",
+ attr: "options", showing: false}
]}
],
/**
-/*jshint bitwise:true, indent:2, curly:true, eqeqeq:true, immed:true,
+ /*jshint bitwise:true, indent:2, curly:true, eqeqeq:true, immed:true,
latedef:true, newcap:true, noarg:true, regexp:true, undef:true,
trailing:true, white:true*/
/*global XT:true, XM:true, _:true, enyo:true, Globalize:true*/
]
});
+ // ..........................................................
+ // WORK ORDER
+ //
+
+ enyo.kind({
+ name: "XV.WorkOrderListParameters",
+ kind: "XV.ParameterWidget",
+ components: [
+ {kind: "onyx.GroupboxHeader", content: "_workOrders".loc()},
+ {name: "orderNumberPattern", label: "_orderNumber".loc(), attr: "number"},
+ {name: "site", label: "_site".loc(), attr: "itemSite.site.code", defaultKind: "XV.SitePicker"},
+ {name: "showClosed", attr: "status", label: "_showClosed".loc(), defaultKind: "XV.CheckboxWidget",
+ getParameter: function () {
+ var param;
+ if (!this.getValue()) {
+ param = {
+ attribute: this.getAttr(),
+ operator: '!=',
+ value: "C"
+ };
+ }
+ return param;
+ }
+ },
+ /*TODO
+ Has Parent Sales Order
+ Has Closed Parent Sales Orders
+ Item Group
+ */
+ {kind: "onyx.GroupboxHeader", content: "_item".loc()},
+ {name: "itemWidget", label: "_item".loc(), attr: "itemSite.item",
+ defaultKind: "XV.ItemWidget"},
+ {name: "description", label: "_description".loc(), attr: "itemSite.item.description1"},
+ {kind: "onyx.GroupboxHeader", content: "_plannerCode".loc()},
+ {name: "plannerCode", label: "_plannerCode".loc(), attr: "itemSite.plannerCode",
+ defaultKind: "XV.PlannerCodePicker"},
+ {name: "plannerCodePattern", label: "_plannerCode".loc() + " " + "_pattern".loc(), attr: "itemSite.plannerCode"},
+ {kind: "onyx.GroupboxHeader", content: "_classCode".loc()},
+ {name: "classCode", label: "_classCode".loc(), attr: "itemSite.item.classCode",
+ defaultKind: "XV.ClassCodePicker"},
+ {name: "classCodePattern", label: "_classCode".loc() + " " + "_pattern".loc(), attr: "itemSite.item.classCode"}
+ ]
+ });
+
}());
list: "XV.VendorList"
});
+ // ..........................................................
+ // WORK ORDER
+ //
+
+ enyo.kind({
+ name: "XV.WorkOrderWidget",
+ kind: "XV.RelationWidget",
+ collection: "XM.WorkOrderRelationCollection",
+ keyAttribute: "number",
+ list: "XV.WorkOrderList"
+ });
+
}());
{
"context": "xtuple",
"nameSpace": "XM",
- "type": "WorkOrderListItem",
- "table": "wo",
- "comment": "Work List Item Map",
+ "type": "WorkOrder",
+ "table": "xt.woinfo",
+ "isRest": true,
+ "lockable": true,
+ "idSequenceName": "wo_wo_id_seq",
+ "comment": "Work Order Map",
"privileges": {
"all": {
- "create": false,
+ "create": "MaintainWorkOrders",
"read": "ViewWorkOrders MaintainWorkOrders",
- "update": false,
- "delete": false
+ "update": "MaintainWorkOrders",
+ "delete": "MaintainWorkOrders"
}
},
"properties": [
{
"name": "number",
"attr": {
- "type": "Number",
+ "type": "String",
"column": "wo_number",
"isNaturalKey": true
}
},
- {
- "name": "subnumber",
- "attr": {
- "type": "Number",
- "column": "wo_subnumber"
- }
- },
{
"name": "status",
"attr": {
"toOne": {
"isNested": true,
"type": "ItemSiteRelation",
- "column": "wo_itemsite_id"
+ "column": "wo_itemsite_id",
+ "required": true
+ }
+ },
+ {
+ "name": "item",
+ "toOne": {
+ "isNested": true,
+ "type": "ItemRelation",
+ "column": "wo_item_id",
+ "required": true
+ }
+ },
+ {
+ "name": "site",
+ "toOne": {
+ "isNested": true,
+ "type": "SiteRelation",
+ "column": "wo_warehous_id",
+ "required": true
}
},
{
}
},
{
- "name": "orderType",
+ "name": "ordered",
"attr": {
- "type": "String",
- "column": "wo_ordtype"
+ "type": "Number",
+ "column": "wo_qtyord"
}
},
{
- "name": "orderId",
+ "name": "quantityReceived",
"attr": {
"type": "Number",
- "column": "wo_ordid"
- }
+ "column": "wo_qtyrcv"
+ }
},
{
- "name": "qtyOrdered",
+ "name": "isAdhoc",
+ "attr": {
+ "type": "Boolean",
+ "column": "wo_adhoc"
+ }
+ },
+ {
+ "name": "wipValue",
+ "attr": {
+ "type": "Cost",
+ "column": "wo_wipvalue"
+ }
+ },
+ {
+ "name": "postedValue",
+ "attr": {
+ "type": "Cost",
+ "column": "wo_postedvalue"
+ }
+ },
+ {
+ "name": "notes",
+ "attr": {
+ "type": "String",
+ "column": "wo_prodnotes"
+ }
+ },
+ {
+ "name": "priority",
"attr": {
"type": "Number",
- "column": "wo_qtyord"
- }
+ "column": "wo_priority"
+ }
},
{
- "name": "qtyReceived",
+ "name": "username",
+ "attr": {
+ "type": "String",
+ "column": "wo_username"
+ }
+ }
+ ],
+ "isSystem": true
+ },
+ {
+ "context": "xtuple",
+ "nameSpace": "XM",
+ "type": "WorkOrderListItem",
+ "table": "xt.woinfo",
+ "comment": "Work Order List Item Map",
+ "privileges": {
+ "all": {
+ "create": false,
+ "read": "ViewWorkOrders MaintainWorkOrders",
+ "update": false,
+ "delete": false
+ }
+ },
+ "properties": [
+ {
+ "name": "id",
"attr": {
"type": "Number",
- "column": "wo_qtyrcv"
+ "column": "wo_id",
+ "isPrimaryKey": true
}
},
{
- "name": "isAdhoc",
+ "name": "number",
"attr": {
- "type": "Boolean",
- "column": "wo_adhoc"
+ "type": "String",
+ "column": "wo_number",
+ "isNaturalKey": true
+ }
+ },
+ {
+ "name": "subnumber",
+ "attr": {
+ "type": "Number",
+ "column": "wo_subnumber"
+ }
+ },
+ {
+ "name": "status",
+ "attr": {
+ "type": "String",
+ "column": "wo_status"
+ }
+ },
+ {
+ "name": "itemSite",
+ "toOne": {
+ "isNested": true,
+ "type": "ItemSiteRelation",
+ "column": "wo_itemsite_id",
+ "required": true
}
},
{
- "name": "itemCfgSeries",
+ "name": "item",
+ "toOne": {
+ "isNested": true,
+ "type": "ItemRelation",
+ "column": "wo_item_id",
+ "required": true
+ }
+ },
+ {
+ "name": "site",
+ "toOne": {
+ "isNested": true,
+ "type": "SiteRelation",
+ "column": "wo_warehous_id",
+ "required": true
+ }
+ },
+ {
+ "name": "startDate",
+ "attr": {
+ "type": "Date",
+ "column": "wo_startdate"
+ }
+ },
+ {
+ "name": "dueDate",
+ "attr": {
+ "type": "Date",
+ "column": "wo_duedate"
+ }
+ },
+ {
+ "name": "ordered",
"attr": {
"type": "Number",
- "column": "wo_itemcfg_series"
+ "column": "wo_qtyord"
+ }
+ },
+ {
+ "name": "quantityReceived",
+ "attr": {
+ "type": "Number",
+ "column": "wo_qtyrcv"
}
},
{
- "name": "isImported",
+ "name": "isAdhoc",
"attr": {
"type": "Boolean",
- "column": "wo_imported"
+ "column": "wo_adhoc"
}
},
{
"column": "wo_postedvalue"
}
},
- {
- "name": "productionNotes",
+ {
+ "name": "notes",
"attr": {
"type": "String",
"column": "wo_prodnotes"
}
},
- {
+ {
"name": "priority",
"attr": {
"type": "Number",
}
},
{
- "name": "brdValue",
+ "name": "username",
"attr": {
- "type": "Cost",
- "column": "wo_brdvalue"
+ "type": "String",
+ "column": "wo_username"
+ }
+ }
+ ],
+ "isSystem": true
+ },
+ {
+ "context": "xtuple",
+ "nameSpace": "XM",
+ "type": "WorkOrderRelation",
+ "table": "xt.woinfo",
+ "comment": "Work Relation Map",
+ "privileges": {
+ "all": {
+ "create": "MaintainWorkOrders",
+ "read": "ViewWorkOrders MaintainWorkOrders",
+ "update": "MaintainWorkOrders",
+ "delete": "MaintainWorkOrders"
+ }
+ },
+ "properties": [
+ {
+ "name": "id",
+ "attr": {
+ "type": "Number",
+ "column": "wo_id",
+ "isPrimaryKey": true
}
},
{
- "name": "cosMethod",
+ "name": "number",
"attr": {
"type": "String",
- "column": "wo_cosmethod"
+ "column": "wo_number",
+ "isNaturalKey": true
}
},
{
- "name": "username",
+ "name": "subnumber",
+ "attr": {
+ "type": "Number",
+ "column": "wo_subnumber"
+ }
+ },
+ {
+ "name": "status",
"attr": {
"type": "String",
- "column": "wo_username"
+ "column": "wo_status"
+ }
+ },
+ {
+ "name": "itemSite",
+ "toOne": {
+ "isNested": true,
+ "type": "ItemSiteRelation",
+ "column": "wo_itemsite_id",
+ "required": true
+ }
+ },
+ {
+ "name": "item",
+ "toOne": {
+ "isNested": true,
+ "type": "ItemRelation",
+ "column": "wo_item_id"
+ }
+ },
+ {
+ "name": "site",
+ "toOne": {
+ "isNested": true,
+ "type": "SiteRelation",
+ "column": "wo_warehous_id"
}
}
- ],
+ ],
"isSystem": true
}
]
{
"name": "_application_",
"databaseScripts": [
+ "version_check.sql",
"drop_deprecated.sql",
"xt/trigger_functions/comment_did_change.sql",
"xt/trigger_functions/customer_did_change.sql",
"public/tables/todoitem.sql",
"public/tables/usrpref.sql",
"public/tables/usrpriv.sql",
+ "public/tables/wo.sql",
+ "public/tables/womatl.sql",
"xt/functions/add_priv.sql",
"xt/functions/average_cost.sql",
"xt/functions/change_password.sql",
"xt/views/site.sql",
"xt/views/todoiteminfo.sql",
"xt/views/usrinfo.sql",
+ "xt/views/woinfo.sql",
"xt/tables/usrlite.sql",
"xm/javascript/account.sql",
"xm/javascript/address.sql",
--- /dev/null
+-- add uuid column here because there are views that need this
+select xt.add_column('wo','obj_uuid', 'text', 'default xt.generate_uuid()', 'public');
+select xt.add_inheritance('wo', 'xt.obj');
+select xt.add_constraint('wo', 'wo_obj_uuid','unique(obj_uuid)', 'public');
\ No newline at end of file
--- /dev/null
+-- add uuid column here because there are views that need this
+select xt.add_column('womatl','obj_uuid', 'text', 'default xt.generate_uuid()', 'public');
+select xt.add_inheritance('womatl', 'xt.obj');
+select xt.add_constraint('womatl', 'womatl_obj_uuid','unique(obj_uuid)', 'public');
\ No newline at end of file
-UPDATE pkghead SET pkghead_version = '1.4.5' WHERE pkghead_name = 'xt' ;
+UPDATE pkghead SET pkghead_version = '1.4.6' WHERE pkghead_name = 'xt' ;
--- /dev/null
+do $$
+
+ var qry = plv8.execute("select * from metric where metric_name = 'ServerVersion'"),
+ major = qry[0].metric_value.slice(0,1),
+ minor = qry[0].metric_value.slice(2,3);
+
+ if (major < 4 || minor < 2) {
+ plv8.elog(ERORR, "Database version must be 4.2.0 or higher");
+ }
+
+$$ language plv8;
--- /dev/null
+select xt.create_view('xt.woinfo', $$
+
+select
+ wo_id,
+ wo_number::text AS wo_number,
+ wo_subnumber,
+ wo_status,
+ wo_itemsite_id,
+ itemsite_item_id as wo_item_id,
+ itemsite_warehous_id as wo_warehous_id,
+ wo_startdate,
+ wo_duedate,
+ wo_ordtype,
+ wo_ordid,
+ wo_qtyord,
+ wo_qtyrcv,
+ wo_adhoc,
+ wo_itemcfg_series,
+ wo_imported,
+ wo_wipvalue,
+ wo_postedvalue,
+ wo_prodnotes,
+ wo_prj_id,
+ wo_priority,
+ wo_brdvalue,
+ wo_bom_rev_id,
+ wo_boo_rev_id,
+ wo_cosmethod,
+ wo_womatl_id,
+ wo_username,
+ case when (wo_qtyrcv > wo_qtyord) then 0 else (wo_qtyord - wo_qtyrcv) end AS balance,
+ null::numeric AS qty_to_post
+from wo
+ join itemsite on wo_itemsite_id = itemsite_id;
+
+$$, false);
+
+create or replace rule "_INSERT" as on insert to xt.woinfo do instead
+
+insert into wo (
+ wo_id,
+ wo_status,
+ wo_itemsite_id,
+ wo_startdate,
+ wo_duedate,
+ wo_ordtype,
+ wo_ordid,
+ wo_qtyord,
+ wo_qtyrcv,
+ wo_adhoc,
+ wo_itemcfg_series,
+ wo_imported,
+ wo_wipvalue,
+ wo_postedvalue,
+ wo_prodnotes,
+ wo_prj_id,
+ wo_priority,
+ wo_brdvalue,
+ wo_bom_rev_id,
+ wo_boo_rev_id,
+ wo_cosmethod,
+ wo_womatl_id,
+ wo_username
+) select
+ new.wo_id ,
+ new.wo_status,
+ itemsite_id,
+ new.wo_startdate,
+ new.wo_duedate,
+ new.wo_ordtype,
+ new.wo_ordid,
+ new.wo_qtyord,
+ 0,
+ new.wo_adhoc,
+ new.wo_itemcfg_series,
+ new.wo_imported,
+ 0,
+ 0,
+ new.wo_prodnotes,
+ new.wo_prj_id,
+ new.wo_priority,
+ new.wo_brdvalue,
+ new.wo_bom_rev_id,
+ new.wo_boo_rev_id,
+ new.wo_cosmethod,
+ new.wo_womatl_id,
+ new.wo_username
+from itemsite
+where itemsite_item_id=new.wo_item_id
+ and itemsite_warehous_id=new.wo_warehous_id;
+
+create or replace rule "_UPDATE" as on update to xt.woinfo do instead
+
+update wo set
+ wo_id = new.wo_id,
+ wo_status = new.wo_status,
+ wo_startdate = new.wo_startdate,
+ wo_duedate = new.wo_duedate,
+ wo_ordtype = new.wo_ordtype,
+ wo_ordid = new.wo_ordid,
+ wo_qtyord = new.wo_qtyord,
+ wo_adhoc = new.wo_adhoc,
+ wo_itemcfg_series = new.wo_itemcfg_series,
+ wo_imported = new.wo_imported,
+ wo_prodnotes = new.wo_prodnotes,
+ wo_prj_id = new.wo_prj_id,
+ wo_priority = new.wo_priority,
+ wo_bom_rev_id = new.wo_bom_rev_id,
+ wo_boo_rev_id = new.wo_boo_rev_id,
+ wo_cosmethod = new.wo_cosmethod,
+ wo_womatl_id = new.wo_womatl_id,
+ wo_username = new.wo_username
+where wo_id = old.wo_id;
+
+create or replace rule "_DELETE" as on delete to xt.woinfo do instead
+
+select deletewo(old.wo_id, true);
quantityAttribute: "toIssue",
- issueMethod: "issueToShipping",
+ issueMethod: "issueItem",
readOnlyAttributes: [
"atShipping",
"returned",
"site",
"shipment",
- "shipped"
+ "shipped",
+ "unit"
],
transactionDate: null,
this.on("change:toIssue", this.toIssueDidChange);
},
- canIssueStock: function (callback) {
+ canIssueItem: function (callback) {
var isShipped = this.getValue("shipment.isShipped") || false,
hasPrivilege = XT.session.privileges.get("IssueStockToShipping");
if (callback) {
return this;
},
- canReturnStock: function (callback) {
+ canReturnItem: function (callback) {
var isShipped = this.getValue("shipment.isShipped") || false,
hasPrivilege = XT.session.privileges.get("ReturnStockFromShipping"),
atShipping = this.get("atShipping");
@params {Array} Data
@params {Object} Options
*/
- XM.Inventory.issueToShipping = function (params, options) {
+ XM.Inventory.issueItem = function (params, options) {
var obj = XM.Model.prototype;
obj.dispatch("XM.Inventory", "issueToShipping", params, options);
};
@params {Array} Array of model ids
@params {Object} Options
*/
- XM.Inventory.returnFromShipping = function (params, options) {
+ XM.Inventory.returnItem = function (params, options) {
var obj = XM.Model.prototype;
obj.dispatch("XM.Inventory", "returnFromShipping", params, options);
};
XV.registerModelList("XM.InventoryHistory", "XV.InventoryHistoryList");
- // ..........................................................
- // ISSUE TO SHIPPING
- //
-
- enyo.kind({
- name: "XV.IssueToShippingList",
- kind: "XV.List",
- label: "_issueToShipping".loc(),
- collection: "XM.IssueToShippingCollection",
- parameterWidget: "XV.IssueToShippingParameters",
- multiSelect: true,
- query: {orderBy: [
- {attribute: "lineNumber"},
- {attribute: "subNumber"}
- ]},
- showDeleteAction: false,
- actions: [
- {name: "issueStock", prerequisite: "canIssueStock",
- method: "issueStock", notify: false, isViewMethod: true},
- {name: "issueLine", prerequisite: "canIssueStock",
- method: "issueLine", notify: false, isViewMethod: true},
- {name: "returnLine", prerequisite: "canReturnStock",
- method: "returnStock", notify: false, isViewMethod: true}
- ],
- toggleSelected: true,
- published: {
- shipment: null
- },
- events: {
- onProcessingChanged: "",
- onShipmentChanged: ""
- },
- components: [
- {kind: "XV.ListItem", components: [
- {kind: "FittableColumns", components: [
- {kind: "XV.ListColumn", classes: "first", components: [
- {kind: "FittableColumns", components: [
- {kind: "XV.ListAttr", attr: "lineNumber"},
- {kind: "XV.ListAttr", attr: "itemSite.site.code",
- classes: "right"},
- {kind: "XV.ListAttr", attr: "itemSite.item.number", fit: true}
- ]},
- {kind: "XV.ListAttr", attr: "itemSite.item.description1",
- fit: true, style: "text-indent: 18px;"}
- ]},
- {kind: "XV.ListColumn", classes: "money", components: [
- {kind: "XV.ListAttr", attr: "ordered",
- formatter: "formatQuantity", style: "text-align: right"}
- ]},
- {kind: "XV.ListColumn", classes: "money", components: [
- {kind: "XV.ListAttr", attr: "balance",
- formatter: "formatQuantity", style: "text-align: right"}
- ]},
- {kind: "XV.ListColumn", classes: "money", components: [
- {kind: "XV.ListAttr", attr: "atShipping",
- formatter: "formatQuantity", style: "text-align: right"}
- ]},
- {kind: "XV.ListColumn", classes: "money", components: [
- {kind: "XV.ListAttr", attr: "scheduleDate",
- formatter: "formatScheduleDate", style: "text-align: right"}
- ]}
- ]}
- ]}
- ],
- fetch: function () {
- this.setShipment(null);
- this.inherited(arguments);
- },
- formatScheduleDate: function (value, view, model) {
- var today = new Date(),
- isLate = XT.date.compareDate(value, today) < 1 &&
- model.get("balance") > 0;
- view.addRemoveClass("error", isLate);
- return value;
- },
- formatLineNumber: function (value, view, model) {
- var lineNumber = model.get("lineNumber"),
- subnumber = model.get("subNumber");
- if (subnumber === 0) {
- value = lineNumber;
- } else {
- value = lineNumber + "." + subnumber;
- }
- return value;
- },
- formatQuantity: function (value) {
- var scale = XT.locale.quantityScale;
- return Globalize.format(value, "n" + scale);
- },
- /**
- Helper function for transacting `issue` on an array of models
-
- @param {Array} Models
- @param {Boolean} Prompt user for confirmation on every model
- */
- issue: function (models, prompt) {
- var that = this,
- i = -1,
- callback,
- data = [];
-
- // Recursively issue everything we can
- callback = function (workspace) {
- var model,
- options = {},
- toIssue,
- transDate,
- params,
- dispOptions = {},
- wsOptions = {},
- wsParams;
-
- // If argument is false, this whole process was cancelled
- if (workspace === false) {
- return;
-
- // If a workspace brought us here, process the information it obtained
- } else if (workspace) {
- model = workspace.getValue();
- toIssue = model.get("toIssue");
- transDate = model.transactionDate;
-
- if (toIssue) {
- wsOptions.detail = model.formatDetail();
- wsOptions.asOf = transDate;
- wsParams = {
- orderLine: model.id,
- quantity: toIssue,
- options: wsOptions
- };
- data.push(wsParams);
- }
- workspace.doPrevious();
- }
-
- i++;
- // If we've worked through all the models then forward to the server
- if (i === models.length) {
- that.doProcessingChanged({isProcessing: true});
- dispOptions.success = function () {
- that.doProcessingChanged({isProcessing: false});
- };
- XM.Inventory.issueToShipping(data, dispOptions);
-
- // Else if there's something here we can issue, handle it
- } else {
- model = models[i];
- toIssue = model.get("toIssue");
- transDate = model.transactionDate;
-
- // See if there's anything to issue here
- if (toIssue) {
-
- // If prompt or distribution detail required,
- // open a workspace to handle it
- if (prompt || model.undistributed()) {
- that.doWorkspace({
- workspace: "XV.IssueStockWorkspace",
- id: model.id,
- callback: callback,
- allowNew: false,
- success: function (model) {
- model.transactionDate = transDate;
- }
- });
-
- // Otherwise just use the data we have
- } else {
- options.asOf = transDate;
- options.detail = model.formatDetail();
- params = {
- orderLine: model.id,
- quantity: toIssue,
- options: options
- };
- data.push(params);
- callback();
- }
-
- // Nothing to issue, move on
- } else {
- callback();
- }
- }
- };
- callback();
- },
- issueAll: function () {
- var models = this.getValue().models;
- this.issue(models);
- },
- issueLine: function () {
- var models = this.selectedModels();
- this.issue(models);
- },
- issueStock: function () {
- var models = this.selectedModels();
- this.issue(models, true);
- },
- returnStock: function () {
- var models = this.selectedModels(),
- that = this,
- data = [],
- options = {},
- atShipping,
- model,
- i;
-
- for (i = 0; i < models.length; i++) {
- model = models[i];
- atShipping = model.get("atShipping");
-
- // See if there's anything to issue here
- if (atShipping) {
- data.push(model.id);
- }
- }
-
- if (data.length) {
- that.doProcessingChanged({isProcessing: true});
- options.success = function () {
- that.doProcessingChanged({isProcessing: false});
- };
- XM.Inventory.returnFromShipping(data, options);
- }
- },
- selectedModels: function () {
- var collection = this.getValue(),
- models = [],
- selected,
- prop;
- if (collection.length) {
- selected = this.getSelection().selected;
- for (prop in selected) {
- if (selected.hasOwnProperty(prop)) {
- models.push(this.getModel(prop - 0));
- }
- }
- }
- return models;
- },
- /**
- Overload: used to keep track of shipment.
- */
- setupItem: function (inSender, inEvent) {
- this.inherited(arguments);
- var collection = this.getValue(),
- listShipment = collection.at(inEvent.index).get("shipment"),
- listShipmentId = listShipment ? listShipment.id : false,
- shipment = this.getShipment(),
- shipmentId = shipment ? shipment.id : false;
- if (listShipmentId !== shipmentId) {
- this.setShipment(listShipment);
- // Update all rows to match
- _.each(collection.models, function (model) {
- model.set("shipment", listShipment);
- });
- }
- },
- shipmentChanged: function () {
- this.doShipmentChanged({shipment: this.getShipment()});
- }
- });
-
- XV.registerModelList("XM.SalesOrderRelation", "XV.SalesOrderLineListItem");
-
// ..........................................................
// LOCATION
//
enyo.depends(
"assignment_box.js",
- "transaction_list.js",
"list.js",
"list_relations.js",
"list_relations_box.js",
+ "transaction_list.js",
+ "transaction_list_container.js",
"workspace.js"
);
/*jshint bitwise:true, indent:2, curly:true, eqeqeq:true, immed:true,
latedef:true, newcap:true, noarg:true, regexp:true, undef:true,
trailing:true, white:true, strict:false*/
-/*global XT:true, XM:true, _:true, enyo:true */
+/*global XM:true, _:true, XT:true, XV:true, enyo:true, Globalize:true*/
(function () {
XT.extensions.inventory.initTransactionList = function () {
+ // ..........................................................
+ // ISSUE TO SHIPPING
+ //
+
enyo.kind({
- name: "XV.IssueToShipping",
+ name: "XV.IssueToShippingList",
kind: "XV.TransactionList",
- prerequisite: "canIssueStock",
- notifyMessage: "_issueAll?".loc(),
- list: "XV.IssueToShippingList",
- actions: [
- {name: "issueAll", label: "_issueAll".loc(),
- prerequisite: "canIssueStock" }
+ label: "_issueToShipping".loc(),
+ collection: "XM.IssueToShippingCollection",
+ parameterWidget: "XV.IssueToShippingParameters",
+ query: {orderBy: [
+ {attribute: "lineNumber"},
+ {attribute: "subNumber"}
+ ]},
+ published: {
+ shipment: null,
+ transModule: XM.Inventory,
+ transWorkspace: "XV.IssueStockWorkspace"
+ },
+ components: [
+ {kind: "XV.ListItem", components: [
+ {kind: "FittableColumns", components: [
+ {kind: "XV.ListColumn", classes: "first", components: [
+ {kind: "FittableColumns", components: [
+ {kind: "XV.ListAttr", attr: "lineNumber"},
+ {kind: "XV.ListAttr", attr: "itemSite.site.code",
+ classes: "right"},
+ {kind: "XV.ListAttr", attr: "itemSite.item.number", fit: true}
+ ]},
+ {kind: "XV.ListAttr", attr: "itemSite.item.description1",
+ fit: true, style: "text-indent: 18px;"}
+ ]},
+ {kind: "XV.ListColumn", components: [
+ {kind: "XV.ListAttr", attr: "unit.name", style: "text-align: right"}
+ ]},
+ {kind: "XV.ListColumn", classes: "money", components: [
+ {kind: "XV.ListAttr", attr: "ordered",
+ formatter: "formatQuantity", style: "text-align: right"}
+ ]},
+ {kind: "XV.ListColumn", classes: "money", components: [
+ {kind: "XV.ListAttr", attr: "balance",
+ formatter: "formatQuantity", style: "text-align: right"}
+ ]},
+ {kind: "XV.ListColumn", classes: "money", components: [
+ {kind: "XV.ListAttr", attr: "atShipping",
+ formatter: "formatQuantity", style: "text-align: right"}
+ ]},
+ {kind: "XV.ListColumn", classes: "money", components: [
+ {kind: "XV.ListAttr", attr: "scheduleDate",
+ formatter: "formatScheduleDate", style: "text-align: right"}
+ ]}
+ ]}
+ ]}
],
- handlers: {
- onShipmentChanged: "shipmentChanged"
+ fetch: function () {
+ this.setShipment(null);
+ this.inherited(arguments);
},
- canIssueStock: function () {
- var hasPrivilege = XT.session.privileges.get("IssueStockToShipping"),
- model = this.getModel(),
- validModel = _.isObject(model) ? !model.get("isShipped") : false,
- hasOpenLines = this.$.list.value.length;
- return hasPrivilege && validModel && hasOpenLines;
+ formatScheduleDate: function (value, view, model) {
+ var today = new Date(),
+ isLate = XT.date.compareDate(value, today) < 1 &&
+ model.get("balance") > 0;
+ view.addRemoveClass("error", isLate);
+ return value;
},
- create: function () {
- this.inherited(arguments);
- var button = this.$.postButton;
- button.setContent("_ship".loc());
- button.setShowing(true);
+ formatLineNumber: function (value, view, model) {
+ var lineNumber = model.get("lineNumber"),
+ subnumber = model.get("subNumber");
+ if (subnumber === 0) {
+ value = lineNumber;
+ } else {
+ value = lineNumber + "." + subnumber;
+ }
+ return value;
},
- issueAll: function () {
- this.$.list.issueAll();
+ formatQuantity: function (value) {
+ var scale = XT.locale.quantityScale;
+ return Globalize.format(value, "n" + scale);
},
- post: function () {
- var that = this,
- shipment = this.$.parameterWidget.$.shipment.getValue(),
- callback = function (resp) {
- if (resp) { that.$.parameterWidget.$.order.setValue(null); }
- };
- this.doWorkspace({
- workspace: "XV.ShipShipmentWorkspace",
- id: shipment.id,
- callback: callback
- });
+ /**
+ Overload: used to keep track of shipment.
+ */
+ setupItem: function (inSender, inEvent) {
+ this.inherited(arguments);
+ var collection = this.getValue(),
+ listShipment = collection.at(inEvent.index).get("shipment"),
+ listShipmentId = listShipment ? listShipment.id : false,
+ shipment = this.getShipment(),
+ shipmentId = shipment ? shipment.id : false;
+ if (listShipmentId !== shipmentId) {
+ this.setShipment(listShipment);
+ // Update all rows to match
+ _.each(collection.models, function (model) {
+ model.set("shipment", listShipment);
+ });
+ }
},
- shipmentChanged: function (inSender, inEvent) {
- var disabled = _.isEmpty(inEvent.shipment) ||
- !XT.session.privileges.get("ShipOrders");
- this.$.parameterWidget.$.shipment.setValue(inEvent.shipment);
- this.$.postButton.setDisabled(disabled);
+ shipmentChanged: function () {
+ this.doShipmentChanged({shipment: this.getShipment()});
}
});
+
+ XV.registerModelList("XM.SalesOrderRelation", "XV.SalesOrderLineListItem");
};
}());
--- /dev/null
+/*jshint bitwise:true, indent:2, curly:true, eqeqeq:true, immed:true,
+latedef:true, newcap:true, noarg:true, regexp:true, undef:true,
+trailing:true, white:true, strict:false*/
+/*global XT:true, XM:true, _:true, enyo:true */
+
+(function () {
+
+ XT.extensions.inventory.initTransactionListContainer = function () {
+
+ enyo.kind({
+ name: "XV.IssueToShipping",
+ kind: "XV.TransactionListContainer",
+ prerequisite: "canIssueItem",
+ notifyMessage: "_issueAll?".loc(),
+ list: "XV.IssueToShippingList",
+ actions: [
+ {name: "issueAll", label: "_issueAll".loc(),
+ prerequisite: "canIssueItem" }
+ ],
+ handlers: {
+ onShipmentChanged: "shipmentChanged"
+ },
+ canIssueItem: function () {
+ var hasPrivilege = XT.session.privileges.get("IssueStockToShipping"),
+ model = this.getModel(),
+ validModel = _.isObject(model) ? !model.get("isShipped") : false,
+ hasOpenLines = this.$.list.value.length;
+ return hasPrivilege && validModel && hasOpenLines;
+ },
+ create: function () {
+ this.inherited(arguments);
+ var button = this.$.postButton;
+ button.setContent("_ship".loc());
+ button.setShowing(true);
+ },
+ issueAll: function () {
+ this.$.list.issueAll();
+ },
+ post: function () {
+ var that = this,
+ shipment = this.$.parameterWidget.$.shipment.getValue(),
+ callback = function (resp) {
+ if (resp) { that.$.parameterWidget.$.order.setValue(null); }
+ };
+ this.doWorkspace({
+ workspace: "XV.ShipShipmentWorkspace",
+ id: shipment.id,
+ callback: callback
+ });
+ },
+ shipmentChanged: function (inSender, inEvent) {
+ var disabled = _.isEmpty(inEvent.shipment) ||
+ !XT.session.privileges.get("ShipOrders");
+ this.$.parameterWidget.$.shipment.setValue(inEvent.shipment);
+ this.$.postButton.setDisabled(disabled);
+ }
+ });
+ };
+
+}());
{kind: "XV.ItemSiteWidget", attr:
{item: "itemSite.item", site: "itemSite.site"}
},
+ {kind: "XV.InputWidget", attr: "unit.name"},
{kind: "XV.QuantityWidget", attr: "ordered"},
{kind: "XV.QuantityWidget", attr: "shipped"},
{kind: "XV.QuantityWidget", attr: "returned"},
"name": "unit",
"toOne": {
"type": "Unit",
- "column": "coitem_qty_uom_id"
+ "column": "coitem_qty_uom_id",
+ "isNested": true
}
},
{
" where locitemsite_itemsite_id = $2);",
qry = plv8.execute(locSql, [uuid, info.itemsite_id]);
if (!qry.length) {
- throw new handleError("Location " + uuid + " is not valid.");
+ throw new handleError("Location " + uuid + " is not valid.");
}
return qry[0].location_id;
};
sql3,
ary,
item,
- id,
i;
/* Make into an array if an array not passed */
" join xt.ordtype on c.relname=ordtype_tblname " +
"where obj_uuid= $1;",
- sql2 = "select {table}_id as id " +
- "from {table} where obj_uuid = $1;";
+ sql2 = "select issuetoshipping($1, {table}_id, $3, $4, $5::timestamptz) as series " +
+ "from {table} where obj_uuid = $2;";
sql3 = "select current_date != $1 as invalid";
item = ary[i];
asOf = item.options ? item.options.asOf : null;
orderType = plv8.execute(sql1, [item.orderLine])[0];
- id = plv8.execute(sql2.replace(/{table}/g, orderType.ordtype_tblname),
- [item.orderLine])[0].id;
- series = XT.executeFunction("issuetoshipping",
- [orderType.ordtype_code, id, item.quantity, 0, asOf],
- [null, null, null, null, "timestamptz"]);
+ series = plv8.execute(sql2.replace(/{table}/g, orderType.ordtype_tblname),
+ [orderType.ordtype_code, item.orderLine, item.quantity, 0, asOf])[0].series;
if (asOf && plv8.execute(sql3, [asOf])[0].invalid &&
!XT.Data.checkPrivilege("AlterTransactionDates")) {
@param {Date} Ship date, default = current date
*/
XM.Inventory.shipShipment = function (shipment, shipDate) {
- var sql = "select shiphead_id " +
+ var sql = "select shipshipment(shiphead_id, $2) as series " +
"from shiphead where shiphead_number = $1;";
/* Make sure user can do this */
if (!XT.Data.checkPrivilege("ShipOrders")) { throw new handleError("Access Denied", 401); }
/* Post the transaction */
- var shipmentId = plv8.execute(sql, [shipment])[0].shiphead_id;
- return XT.executeFunction("shipshipment", [shipmentId, shipDate]);
+ var ret = plv8.execute(sql, [shipment, shipDate])[0].series;
+
+ return ret;
};
XM.Inventory.shipShipment.description = "Ship shipment";
XM.Inventory.shipShipment.params = {
- shipment: { type: "String", description: "Shipment natural key" },
- shipDate: { type: "Date", description: "Ship Date" }
+ shipment: { shipment: "Number", shipDate: "Ship Date" }
};
/**
--- /dev/null
+/*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, console:true */
+
+(function () {
+ "use strict";
+
+ XT.extensions.manufacturing = {
+ setVersion: function () {
+ XT.setVersion("", "manufacturing");
+ }
+ };
+
+}());
--- /dev/null
+enyo.depends(
+ "strings.js"
+);
--- /dev/null
+// ==========================================================================
+// Project: XT` Strings
+// Copyright: ©2011 OpenMFG LLC, d/b/a xTuple
+// ==========================================================================
+/*globals XT */
+
+// Place strings you want to localize here. In your app, use the key and
+// localize it using "key string".loc(). HINT: For your key names, use the
+// english string with an underscore in front. This way you can still see
+// how your UI will look and you'll notice right away when something needs a
+// localized string added to this file!
+//
+
+(function () {
+ "use strict";
+
+ var lang = XT.stringsFor("en_US", {
+ "_backflushMaterials": "Backflush Materials",
+ "_closeWorkOrderAfterPosting": "Close Work Order After Posting",
+ "_postProduction": "Post Production",
+ "_qtyIssued": "Qty Issued",
+ "_quantityReceived": "Qty Received",
+ "_qtyRequired": "Qty Required",
+ "_qtyToPost": "Qty to Post",
+ "_scrapOnPost": "Scrap on Post"
+ });
+
+ if (typeof exports !== 'undefined') {
+ exports.language = lang;
+ }
+}());
\ No newline at end of file
--- /dev/null
+/*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, console:true */
+
+(function () {
+ "use strict";
+
+ XT.extensions.manufacturing.initSettings = function () {
+ /**
+ @class
+
+ @extends XM.Settings
+ */
+ XM.Manufacturing = XM.Settings.extend(/** @lends XM.Manufacturing.Settings.prototype */ {
+
+ recordType: 'XM.Manufacturing',
+
+ privileges: 'ConfigureWO',
+
+ validate: function (attributes, options) {
+ // XXX not sure if number widgets can fail in this way.
+ var params = { type: "_number".loc() };
+ if (attributes.NextWorkOrderNumber !== undefined &&
+ isNaN(attributes.NextWorkOrderNumber)) {
+ params.attr = "_workOrder".loc() + " " + "_number".loc();
+ return XT.Error.clone('xt1003', { params: params });
+ }
+ }
+ });
+
+ XM.manufacturing = new XM.Manufacturing();
+
+ };
+
+}());
--- /dev/null
+/*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, _:true */
+
+(function () {
+ "use strict";
+
+ XT.extensions.manufacturing.initManufacturingModels = function () {
+
+ /**
+ @class
+
+ @extends XM.Document
+ */
+ XM.PostProduction = XM.Model.extend({
+
+ recordType: "XM.PostProduction",
+
+ readOnlyAttributes: [
+ "number",
+ "dueDate",
+ "itemSite",
+ "status",
+ "ordered",
+ "quantityReceived",
+ "qtyRequired",
+ "balance"
+ ],
+
+ transactionDate: null,
+
+ bindEvents: function () {
+ XM.Model.prototype.bindEvents.apply(this, arguments);
+ this.on('statusChange', this.statusDidChange);
+ },
+
+ statusDidChange: function () {
+ var K = XM.Model;
+ // We want to be able to save and post immeditately.
+ if (this.getStatus() === K.READY_CLEAN) {
+ this.setStatus(K.READY_DIRTY);
+ }
+ }
+
+ });
+
+ /**
+ @class
+
+ @extends XM.Transaction
+ */
+ XM.IssueMaterial = XM.Transaction.extend({
+
+ recordType: "XM.IssueMaterial",
+
+ quantityAttribute: "toIssue",
+
+ issueMethod: "issueItem",
+
+ readOnlyAttributes: [
+ "qohBefore",
+ "qtyPer",
+ "qtyRequired",
+ "qtyIssued",
+ "unit.name"
+ ],
+
+ transactionDate: null,
+
+ qohAfter: function () {
+ var qohBefore = this.get("qohBefore"),
+ toIssue = this.get("toIssue"),
+ qohAfter = XT.math.subtract(qohBefore, toIssue, XT.QUANTITY_SCALE);
+ return qohAfter;
+ },
+
+ bindEvents: function () {
+ XM.Model.prototype.bindEvents.apply(this, arguments);
+
+ // Bind events
+ this.on("statusChange", this.statusDidChange);
+ this.on("change:toIssue", this.toIssueDidChange);
+ },
+
+ canIssueItem: function (callback) {
+ var hasPrivilege = XT.session.privileges.get("IssueWoMaterials");
+ if (callback) {
+ callback(hasPrivilege);
+ }
+ return this;
+ },
+
+ canReturnItem: function (callback) {
+ var hasPrivilege = XT.session.privileges.get("ReturnWoMaterials");
+ if (callback) {
+ callback(hasPrivilege);
+ }
+ return this;
+ },
+
+ /**
+ Calculate the balance remaining to issue.
+
+ @returns {Number}
+ */
+ issueBalance: function () {
+ var qtyRequired = this.get("qtyRequired"),
+ qtyIssued = this.get("qtyIssued"),
+ toIssue = XT.math.subtract(qtyRequired, qtyIssued, XT.QUANTITY_SCALE);
+ return toIssue >= 0 ? toIssue : 0;
+ },
+
+ /**
+ Unlike most validations on models, this one accepts a callback
+ into which will be forwarded a boolean response. Errors will
+ trigger `invalid`.
+
+ @param {Function} Callback
+ @returns {Object} Receiver
+ */
+ validate: function (callback) {
+ var toIssue = this.get("toIssue"),
+ err;
+
+ // Validate
+ if (this.undistributed()) {
+ err = XT.Error.clone("xt2017");
+ } else if (toIssue <= 0) {
+ err = XT.Error.clone("xt2013");
+ } else if (toIssue > this.issueBalance()) {
+ this.notify("_issueExcess".loc(), {
+ type: XM.Model.QUESTION,
+ callback: function (resp) {
+ callback(resp.answer);
+ }
+ });
+ return this;
+ }
+
+ if (err) {
+ this.trigger("invalid", this, err, {});
+ callback(false);
+ } else {
+ callback(true);
+ }
+
+ return this;
+ },
+
+ statusDidChange: function () {
+ if (this.getStatus() === XM.Model.READY_CLEAN) {
+ this.set("toIssue", this.issueBalance());
+ }
+ },
+
+ toIssueDidChange: function () {
+ this.distributeToDefault();
+ this.qohAfter();
+ }
+
+ });
+
+ /**
+ Static function to call issue material on a set of multiple items.
+
+ @params {Array} Data
+ @params {Object} Options
+ */
+ XM.Manufacturing.issueItem = function (params, options) {
+ var obj = XM.Model.prototype;
+ obj.dispatch("XM.Manufacturing", "issueMaterial", params, options);
+ };
+
+ /**
+ Static function to call return material on a set of multiple items.
+
+ @params {Array} Array of model ids
+ @params {Object} Options
+ */
+ XM.Manufacturing.returnItem = function (params, options) {
+ var obj = XM.Model.prototype;
+ obj.dispatch("XM.Manufacturing", "returnMaterial", params, options);
+ };
+
+ // ..........................................................
+ // COLLECTIONS
+ //
+
+ /**
+ @class
+
+ @extends XM.Collection
+ */
+ XM.IssueMaterialCollection = XM.Collection.extend({
+
+ model: XM.IssueMaterial
+
+ });
+
+ };
+
+}());
+
--- /dev/null
+enyo.depends(
+ "configure.js",
+ "manufacturing.js",
+ "static.js"
+);
\ No newline at end of file
--- /dev/null
+/*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, console:true */
+
+(function () {
+ "use strict";
+
+ XT.extensions.manufacturing.initStaticModels = function () {
+
+ // These are hard coded collections that may be turned into tables at a later date
+ var i;
+
+ // Explode Work Order's Effective as of
+ var explodeWOEffectiveJson = [
+ { id: XM.WorkOrder.START_DATE, name: "_workOrderStartDate".loc() },
+ { id: XM.WorkOrder.EXPLOSION_DATE, name: "_dateOfExplosion".loc() }
+ ];
+ XM.ExplodeWOEffectiveModel = Backbone.Model.extend({
+ });
+ XM.ExplodeWOEffectiveCollection = Backbone.Collection.extend({
+ model: XM.ExplodeWOEffectiveModel
+ });
+ XM.explodeWOEffectives = new XM.ExplodeWOEffectiveCollection();
+ for (i = 0; i < explodeWOEffectiveJson.length; i++) {
+ var explodeWOEffective = new XM.ExplodeWOEffectiveModel(explodeWOEffectiveJson[i]);
+ XM.explodeWOEffectives.add(explodeWOEffective);
+ }
+
+ // Default Work Order Explosion Level
+ var wOExplosionLevelJson = [
+ { id: XM.WorkOrder.SINGLE_LEVEL, name: "_singleLevel".loc() },
+ { id: XM.WorkOrder.MULTIPLE_LEVEL, name: "_multipleLevel".loc() }
+ ];
+ XM.WOExplosionLevelModel = Backbone.Model.extend({
+ });
+ XM.WOExplosionLevelCollection = Backbone.Collection.extend({
+ model: XM.WOExplosionLevelModel
+ });
+ XM.wOExplosionLevels = new XM.WOExplosionLevelCollection();
+ for (i = 0; i < wOExplosionLevelJson.length; i++) {
+ var wOExplosionLevel = new XM.WOExplosionLevelModel(wOExplosionLevelJson[i]);
+ XM.wOExplosionLevels.add(wOExplosionLevel);
+ }
+
+ // Job Items Work Order Cost Recognition Defaults
+ var jobItemCosDefaultJson = [
+ { id: XM.WorkOrder.TO_DATE, name: "_toDate".loc() },
+ { id: XM.WorkOrder.PROPORTIONAL, name: "_proportional".loc() }
+ ];
+ XM.JobItemCosDefaultModel = Backbone.Model.extend({
+ });
+ XM.JobItemCosDefaultCollection = Backbone.Collection.extend({
+ model: XM.JobItemCosDefaultModel
+ });
+ XM.jobItemCosDefaults = new XM.JobItemCosDefaultCollection();
+ for (i = 0; i < jobItemCosDefaultJson.length; i++) {
+ var jobItemCosDefault = new XM.JobItemCosDefaultModel(jobItemCosDefaultJson[i]);
+ XM.jobItemCosDefaults.add(jobItemCosDefault);
+ }
+
+ };
+
+}());
--- /dev/null
+enyo.depends(
+ "core.js",
+ "en",
+ "models",
+ "postbooks.js",
+ "views",
+ "widgets"
+);
\ No newline at end of file
--- /dev/null
+/*jshint bitwise:true, indent:2, curly:true, eqeqeq:true, immed:true,
+latedef:true, newcap:true, noarg:true, regexp:true, undef:true,
+trailing:true, white:true*/
+/*global XT:true, XV:true, XM:true, enyo:true*/
+
+(function () {
+
+ XT.extensions.manufacturing.initPostbooks = function () {
+ var module,
+ panels,
+ relevantPrivileges,
+ configurationJson,
+ configuration;
+
+ // ..........................................................
+ // APPLICATION
+ //
+ /*
+ panels = [
+ //Bill of Materials
+ ];
+ XT.app.$.postbooks.appendPanels("setup", panels);
+ */
+
+ configurationJson = {
+ model: "XM.manufacturing",
+ name: "_manufacturing".loc(),
+ description: "_manufacturingDescription".loc(),
+ workspace: "XV.ManufacturingWorkspace"
+ };
+ configuration = new XM.ConfigurationModel(configurationJson);
+ XM.configurations.add(configuration);
+
+ module = {
+ name: "manufacturing",
+ label: "_manufacturing".loc(),
+ panels: [
+ {name: "workOrderList", kind: "XV.WorkOrderList"}
+ ],
+ actions: [
+ {name: "issueMaterial", privilege: "issueWoMaterials", method: "issueMaterial", notify: false}
+ ],
+ issueMaterial: function (inSender, inEvent) {
+ inSender.bubbleUp("onIssueMaterial", inEvent, inSender);
+ }
+
+ };
+ XT.app.$.postbooks.insertModule(module, 90);
+
+ relevantPrivileges = [
+ "IssueWoMaterials",
+ "PostProduction",
+ "ReturnWoMaterials"
+ ];
+ XT.session.addRelevantPrivileges(module.name, relevantPrivileges);
+
+ // Postbooks level handler for the thing that is neither fish nor fowl
+ XT.app.$.postbooks.handlers.onIssueMaterial = "issueMaterial";
+ XT.app.$.postbooks.issueMaterial = function (inSender, inEvent) {
+ var panel = this.createComponent({kind: "XV.IssueMaterial"});
+
+ panel.render();
+ this.reflow();
+ this.setIndex(this.getPanels().length - 1);
+
+ return true;
+ };
+
+ };
+}());
--- /dev/null
+/*jshint bitwise:true, indent:2, curly:true, eqeqeq:true, immed:true,
+latedef:true, newcap:true, noarg:true, regexp:true, undef:true, strict: false,
+trailing:true, white:true*/
+/*global XT:true, enyo:true, Globalize:true, _:true*/
+
+(function () {
+
+
+ XT.extensions.manufacturing.initListRelations = function () {
+
+ // ..........................................................
+ // ISSUE MATERIAL DETAIL
+ //
+
+ enyo.kind({
+ name: "XV.IssueMaterialDetailListRelations",
+ kind: "XV.ListRelations",
+ orderBy: [
+ {attribute: "aisle"},
+ {attribute: "rack"},
+ {attribute: "bin"},
+ {attribute: "location"}
+ ],
+ multiSelect: true,
+ parentKey: "itemSite",
+ events: {
+ onDistributedTapped: ""
+ },
+ components: [
+ {kind: "XV.ListItem", components: [
+ {kind: "FittableColumns", components: [
+ {kind: "XV.ListColumn", classes: "first", components: [
+ {kind: "FittableColumns", components: [
+ {kind: "FittableColumns", components: [
+ {kind: "XV.ListAttr", attr: "location",
+ formatter: "formatLocation"},
+ ]},
+ {kind: "XV.ListAttr", attr: "quantity",
+ formatter: "formatQuantity",
+ classes: "right"},
+ ]},
+ {kind: "FittableColumns", components: [
+ {content: ""},
+ {kind: "XV.ListAttr", attr: "distributed",
+ formatter: "formatQuantity",
+ classes: "right hyperlink", ontap: "distributedTapped"}
+ ]}
+ ]}
+ ]}
+ ]}
+ ],
+ destroy: function () {
+ var collection = this.getValue(),
+ that = this;
+ _.each(collection.models, function (model) {
+ model.off("change:distributed", that.rowChanged, that);
+ });
+ this.inherited(arguments);
+ },
+ distributedTapped: function (inSender, inEvent) {
+ inEvent.model = this.readyModels()[inEvent.index];
+ this.doDistributedTapped(inEvent);
+ return true;
+ },
+ isDefault: function (model) {
+ var location = model.get("location"),
+ itemSite = model.get("itemSite"),
+ stockLoc = itemSite.get("stockLocation");
+ return location && stockLoc.id === location.id;
+ },
+ formatLocation: function (value, view, model) {
+ view.addRemoveClass("emphasis", this.isDefault(model));
+ if (value) { return value.format(); }
+ },
+ formatQuantity: function (value) {
+ var scale = XT.locale.quantityScale;
+ return Globalize.format(value, "n" + scale);
+ },
+ rowChanged: function (model) {
+ this.renderRow(this.getValue().indexOf(model));
+ },
+ /**
+ Overload: Don't highlight as selected if no quantity was distributed.
+ */
+ setupItem: function (inSender, inEvent) {
+ var view = this.$.listItem,
+ model = this.readyModels()[inEvent.index],
+ isDistributed;
+ if (!model) { return; } // Hack
+ this.inherited(arguments);
+ isDistributed = model.get("distributed");
+ view.addRemoveClass("item-selected", isDistributed);
+ },
+ /**
+ Overload: Add observers to all detail models to re-render if
+ distribute values change.
+ */
+ valueChanged: function () {
+ this.inherited(arguments);
+ var that = this,
+ collection = this.getValue();
+ _.each(collection.models, function (model) {
+ model.on("change:distributed", that.rowChanged, that);
+ });
+ }
+ });
+
+ // ..........................................................
+ // WORK ORDER MATERIAL LINE
+ //
+
+ enyo.kind({
+ name: "XV.WorkOrderMaterialLineListRelations",
+ kind: "XV.ListRelations",
+ orderBy: [
+ {attribute: "lineNumber"}
+ ],
+ parentKey: "shipment",
+ components: [
+ {kind: "XV.ListItem", components: [
+ {kind: "FittableColumns", components: [
+ {kind: "XV.ListColumn", classes: "first", components: [
+ {kind: "FittableColumns", components: [
+ {kind: "XV.ListAttr", attr: "orderLine.lineNumber", classes: "bold"},
+ {kind: "XV.ListAttr", attr: "orderLine.item.number", fit: true},
+ {kind: "XV.ListAttr", attr: "orderLine.quantity",
+ formatter: "formatQuantity", classes: "right"},
+ ]},
+ {kind: "FittableColumns", components: [
+ {kind: "XV.ListAttr", attr: "orderLine.item.description1",
+ fit: true, style: "text-indent: 18px;"},
+ {kind: "XV.ListAttr", attr: "orderLine.quantityUnit.name",
+ classes: "right"}
+ ]}
+ ]}
+ ]}
+ ]}
+ ],
+ formatQuantity: function (value) {
+ var scale = XT.session.locale.attributes.quantityScale;
+ return Globalize.format(value, "n" + scale);
+ }
+ });
+
+ };
+
+}());
--- /dev/null
+/*jshint bitwise:false, indent:2, curly:true, eqeqeq:true, immed:true,
+latedef:true, newcap:true, noarg:true, regexp:true, undef:true, strict: false,
+trailing:true, white:true*/
+/*global enyo:true, XT: true */
+
+(function () {
+
+ XT.extensions.manufacturing.initListRelationsBox = function () {
+
+ // ..........................................................
+ // ISSUE MATERIAL LOCATIONS
+ //
+
+ enyo.kind({
+ name: "XV.IssueMaterialDetailRelationsBox",
+ kind: "XV.ListRelationsBox",
+ title: "_detail".loc(),
+ parentKey: "itemSite",
+ listRelations: "XV.IssueMaterialDetailListRelations",
+ canOpen: false,
+ events: {
+ onDetailSelectionChanged: ""
+ },
+ selectionChanged: function (inSender, inEvent) {
+ var index = inEvent.index;
+ this.doDetailSelectionChanged({
+ index: index,
+ model: this.$.list.readyModels()[index],
+ isSelected: inEvent.originator.isSelected(index)
+ });
+ }
+ });
+
+ // ..........................................................
+ // POST LINE
+ //
+
+ enyo.kind({
+ name: "XV.WorkOrderMaterialRelationsBox",
+ kind: "XV.ListRelationsBox",
+ title: "_lineItems".loc(),
+ parentKey: "workOrder",
+ listRelations: "XV.WorkOrderMaterialLineListRelations",
+ canOpen: false
+ });
+
+ };
+
+}());
--- /dev/null
+enyo.depends(
+ "list_relations.js",
+ "list_relations_box.js",
+ "transaction_list.js",
+ "transaction_list_container.js",
+ "workspace.js"
+);
\ No newline at end of file
--- /dev/null
+/*jshint bitwise:true, indent:2, curly:true, eqeqeq:true, immed:true,
+latedef:true, newcap:true, noarg:true, regexp:true, undef:true,
+trailing:true, white:true, strict:false*/
+/*global XM:true, _:true, XT:true, XV:true, enyo:true, Globalize:true*/
+
+(function () {
+
+ XT.extensions.manufacturing.initTransactionLists = function () {
+
+ // ..........................................................
+ // ISSUE WORK ORDER MATERIALS
+ //
+
+ enyo.kind({
+ name: "XV.IssueMaterialList",
+ kind: "XV.TransactionList",
+ label: "_issueMaterial".loc(),
+ collection: "XM.IssueMaterialCollection",
+ parameterWidget: "XV.IssueMaterialParameters",
+ query: {orderBy: [
+ {attribute: "order.number"}
+ ]},
+ published: {
+ status: null,
+ transModule: XM.Manufacturing,
+ transWorkspace: "XV.IssueMaterialWorkspace"
+ },
+ components: [
+ {kind: "XV.ListItem", components: [
+ {kind: "FittableColumns", components: [
+ {kind: "XV.ListColumn", classes: "first", components: [
+ {kind: "FittableColumns", components: [
+ {kind: "XV.ListAttr", attr: "itemSite.site.code",
+ classes: "right"},
+ {kind: "XV.ListAttr", attr: "itemSite.item.number", fit: true}
+ ]},
+ {kind: "XV.ListAttr", attr: "itemSite.item.description1",
+ fit: true, style: "text-indent: 18px;"}
+ ]},
+ {kind: "XV.ListColumn", components: [
+ {kind: "XV.ListAttr", attr: "unit.name", style: "text-align: right"}
+ ]},
+ {kind: "XV.ListColumn", classes: "money", components: [
+ {kind: "XV.ListAttr", attr: "qtyRequired",
+ formatter: "formatQuantity", style: "text-align: right"}
+ ]},
+ {kind: "XV.ListColumn", classes: "money bold", components: [
+ {kind: "XV.ListAttr", attr: "balance",
+ formatter: "formatQuantity", style: "text-align: right"}
+ ]},
+ {kind: "XV.ListColumn", classes: "money", components: [
+ {kind: "XV.ListAttr", attr: "qtyIssued",
+ formatter: "formatQuantity", style: "text-align: right"}
+ ]},
+ {kind: "XV.ListColumn", classes: "money", components: [
+ {kind: "XV.ListAttr", attr: "scheduleDate",
+ formatter: "formatScheduleDate", style: "text-align: right"}
+ ]}
+ ]}
+ ]}
+ ],
+ fetch: function () {
+ this.inherited(arguments);
+ },
+
+ formatScheduleDate: function (value, view, model) {
+ var today = new Date(),
+ isLate = XT.date.compareDate(value, today) < 1 &&
+ model.get("balance") > 0;
+ view.addRemoveClass("error", isLate);
+ return value;
+ },
+
+ formatQuantity: function (value) {
+ var scale = XT.locale.quantityScale;
+ return Globalize.format(value, "n" + scale);
+ },
+
+ orderChanged: function () {
+ this.doOrderChanged({order: this.getOrder()});
+ }
+ });
+
+ XV.registerModelList("XM.WorkOrderRelation", "XV.WorkOrderList");
+
+ };
+}());
--- /dev/null
+/*jshint bitwise:true, indent:2, curly:true, eqeqeq:true, immed:true,
+latedef:true, newcap:true, noarg:true, regexp:true, undef:true,
+trailing:true, white:true, strict:false*/
+/*global XT:true, XM:true, _:true, enyo:true */
+
+(function () {
+
+ XT.extensions.manufacturing.initTransactionListContainer = function () {
+
+ /** @private */
+ var _canDo = function (priv) {
+ var hasPrivilege = XT.session.privileges.get(priv),
+ model = this.getModel(),
+ //validModel = _.isObject(model) ? !model.get("isShipped") : false;
+ validModel = _.isObject(model);
+ return hasPrivilege && validModel;
+ };
+
+ enyo.kind({
+ name: "XV.IssueMaterial",
+ kind: "XV.TransactionListContainer",
+ prerequisite: "canIssueItem",
+ notifyMessage: "_issueAll?".loc(),
+ list: "XV.IssueMaterialList",
+ actions: [
+ {name: "issueAll", label: "_issueAll".loc(),
+ prerequisite: "canIssueItem" }
+ ],
+ handlers: {
+ onShipmentChanged: "shipmentChanged"
+ },
+ canIssueItem: function () {
+ var hasPrivilege = XT.session.privileges.get("IssueStockToShipping"),
+ model = this.getModel(),
+ validModel = _.isObject(model) ? true : false,
+ hasOpenLines = this.$.list.value.length;
+ return hasPrivilege && validModel && hasOpenLines;
+ },
+ create: function () {
+ this.inherited(arguments);
+ var button = this.$.postButton;
+ button.setContent("_post".loc());
+ button.setShowing(true);
+ },
+ issueAll: function () {
+ this.$.list.issueAll();
+ },
+ post: function () {
+ var that = this,
+ model = that.model,
+ callback = function (resp) {
+ if (resp) { that.$.parameterWidget.$.order.setValue(null); }
+ };
+ this.doWorkspace({
+ workspace: "XV.PostProductionWorkspace",
+ id: model.id,
+ callback: callback
+ });
+ },
+ parameterChanged: function (inSender, inEvent) {
+ this.inherited(arguments);
+ var originator = inEvent ? inEvent.originator : false,
+ name = originator ? originator.name : false,
+ that = this;
+ if (name === "order" && this.model !== -1) {
+ if (inEvent.originator.$.input.getValue().id === that.model.id) {
+ this.$.postButton.setDisabled(false);
+ }
+ } else {
+ this.$.postButton.setDisabled(true);
+ }
+ //this.$.postButton.setDisabled(false);
+ }
+ });
+ };
+
+}());
--- /dev/null
+/*jshint bitwise:true, indent:2, curly:true, eqeqeq:true, immed:true,
+latedef:true, newcap:true, noarg:true, regexp:true, undef:true,
+trailing:true, white:true, strict: false*/
+/*global XT:true, XM:true, XV:true, enyo:true, Globalize: true*/
+
+(function () {
+
+ XT.extensions.manufacturing.initWorkspaces = function () {
+
+ // ..........................................................
+ // CONFIGURE
+ //
+
+ enyo.kind({
+ name: "XV.ManufacturingWorkspace",
+ kind: "XV.Workspace",
+ title: "_configure".loc() + " " + "_manufacturing".loc(),
+ model: "XM.Manufacturing",
+ components: [
+ {kind: "Panels", arrangerKind: "CarouselArranger",
+ fit: true, components: [
+ {kind: "XV.Groupbox", name: "mainPanel", components: [
+ {kind: "XV.ScrollableGroupbox", name: "mainGroup", fit: true,
+ classes: "in-panel", components: [
+ {kind: "onyx.GroupboxHeader", content: "_workOrder".loc()},
+ {kind: "XV.NumberPolicyPicker", attr: "WONumberGeneration",
+ label: "_number".loc() + " " + "_policy".loc()},
+ {kind: "XV.NumberWidget", attr: "NextWorkOrderNumber",
+ label: "_nextNumber".loc(), formatting: false},
+ {kind: "XV.ToggleButtonWidget", attr: "AutoExplodeWO",
+ label: "_autoExplodeWO".loc()},
+ {kind: "XV.ToggleButtonWidget", attr: "WorkOrderChangeLog",
+ label: "_workOrderChangeLog".loc()},
+ {kind: "XV.ToggleButtonWidget", attr: "PostMaterialVariances",
+ label: "_postMaterialVariances".loc()},
+ {kind: "XV.PickerWidget", attr: "explodeWOEffective",
+ label: "_explodeWorkOrderEffective".loc(), collection: "XM.explodeWOEffective"},
+ {kind: "XV.PickerWidget", attr: "woExplosionLevel",
+ label: "_woExplosionLevel".loc(), collection: "XM.woExplosionLevel"},
+ {kind: "XV.PickerWidget", attr: "jobItemCosDefault",
+ label: "_jobItemCosDefault".loc(), collection: "XM.jobItemCosDefault"}
+ ]}
+ ]}
+ ]}
+ ]
+ });
+
+ // ..........................................................
+ // ISSUE MATERIAL
+ //
+
+ enyo.kind({
+ name: "XV.IssueMaterialWorkspace",
+ kind: "XV.IssueStockWorkspace",
+ title: "_issueMaterial".loc(),
+ model: "XM.IssueMaterial",
+ saveText: "_issue".loc(),
+ components: [
+ {kind: "Panels", arrangerKind: "CarouselArranger",
+ fit: true, components: [
+ {kind: "XV.Groupbox", name: "mainPanel", components: [
+ {kind: "onyx.GroupboxHeader", content: "_order".loc()},
+ {kind: "XV.ScrollableGroupbox", name: "mainGroup",
+ classes: "in-panel", fit: true, components: [
+ {kind: "XV.WorkOrderWidget", attr: "order"},
+ {kind: "onyx.GroupboxHeader", content: "_item".loc()},
+ {kind: "XV.ItemSiteWidget", attr:
+ {item: "itemSite.item", site: "itemSite.site"}
+ },
+ {kind: "XV.InputWidget", attr: "unit.name"},
+ {kind: "XV.QuantityWidget", attr: "qtyRequired"},
+ {kind: "XV.QuantityWidget", attr: "qtyIssued"},
+ {kind: "onyx.GroupboxHeader", content: "_issue".loc()},
+ {kind: "XV.QuantityWidget", attr: "toIssue", name: "toIssue", classes: "bold"},
+ ]}
+ ]},
+ {kind: "XV.IssueMaterialDetailRelationsBox",
+ attr: "itemSite.detail", name: "detail"}
+ ]},
+ {kind: "onyx.Popup", name: "distributePopup", centered: true,
+ onHide: "popupHidden",
+ modal: true, floating: true, components: [
+ {content: "_quantity".loc()},
+ {kind: "onyx.InputDecorator", components: [
+ {kind: "onyx.Input", name: "quantityInput"}
+ ]},
+ {tag: "br"},
+ {kind: "onyx.Button", content: "_ok".loc(), ontap: "distributeOk",
+ classes: "onyx-blue xv-popup-button"},
+ {kind: "onyx.Button", content: "_cancel".loc(), ontap: "distributeDone",
+ classes: "xv-popup-button"},
+ ]}
+ ]
+ });
+
+ // ..........................................................
+ // POST PRODUCTION
+ //
+
+ enyo.kind({
+ name: "XV.PostProductionWorkspace",
+ kind: "XV.Workspace",
+ title: "_postProduction".loc(),
+ model: "XM.PostProduction",
+ reportModel: "XM.WorkOrder",
+ saveText: "_post".loc(),
+ allowNew: false,
+ hideApply: true,
+ dirtyWarn: false,
+ events: {
+ onPrint: ""
+ },
+ components: [
+ {kind: "Panels", arrangerKind: "CarouselArranger",
+ fit: true, components: [
+ {kind: "XV.Groupbox", name: "mainPanel", components: [
+ {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
+ {kind: "XV.ScrollableGroupbox", name: "mainGroup",
+ classes: "in-panel", fit: true, components: [
+ {kind: "XV.InputWidget", attr: "number"},
+ {kind: "XV.DateWidget", attr: "dueDate"},
+ {kind: "XV.ItemSiteWidget", attr:
+ {item: "itemSite.item", site: "itemSite.site"}
+ },
+ {kind: "XV.InputWidget", attr: "status"},
+ {kind: "onyx.GroupboxHeader", content: "_notes".loc()},
+ {kind: "XV.TextArea", attr: "productionNotes", fit: true},
+ {kind: "onyx.GroupboxHeader", content: "_options".loc()},
+ {kind: "XV.StickyCheckboxWidget", label: "_backflushMaterials".loc(),
+ name: "backflushMaterials"},
+ {kind: "XV.StickyCheckboxWidget", label: "_closeWorkOrderAfterPosting".loc(),
+ name: "closeWorkOrderAfterPosting"},
+ {kind: "XV.StickyCheckboxWidget", label: "_scrapOnPost".loc(),
+ name: "scrapOnPost"},
+ {kind: "XV.QuantityWidget", attr: "ordered"},
+ {kind: "XV.QuantityWidget", attr: "quantityReceived"},
+ {kind: "XV.QuantityWidget", attr: "balance"},
+ {kind: "onyx.GroupboxHeader", content: "_post".loc()},
+ {kind: "XV.QuantityWidget", attr: "qtyToPost", name: "qtyToPost"}
+ ]}
+ ]}
+ //{kind: "XV.ShipmentLineRelationsBox", attr: "lineItems"}
+ ]}
+ ],
+ /**
+ Overload: Some special handling for start up.
+ */
+ attributesChanged: function () {
+ this.inherited(arguments);
+ var model = this.getValue();
+
+ // Focus and select qty on start up.
+ if (!this._started && model &&
+ model.getStatus() === XM.Model.READY_DIRTY) {
+ this.$.qtyToPost.focus();
+ this.$.qtyToPost.$.input.selectContents();
+ this._started = true;
+ }
+ }/*,
+ create: function (options) {
+ this.inherited(arguments);
+ if (!this.getBiAvailable()) {
+ this.$.printPacklist.setChecked(false);
+ this.$.printPacklist.setDisabled(true);
+ }
+ },
+ save: function (options) {
+ if (this.$.printPacklist.isChecked()) {
+ this.doPrint();
+ }
+ this.inherited(arguments);
+ }*/
+ });
+
+ };
+}());
--- /dev/null
+enyo.depends(
+ "parameter.js",
+ "relation.js"
+);
--- /dev/null
+/*jshint bitwise:true, indent:2, curly:true, eqeqeq:true, immed:true,
+latedef:true, newcap:true, noarg:true, regexp:true, undef:true,
+trailing:true, white:true, strict: false*/
+/*global XT:true, XM:true, enyo:true*/
+
+(function () {
+
+ XT.extensions.manufacturing.initParameters = function () {
+
+ // ..........................................................
+ // ISSUE MATERIAL
+ //
+
+ enyo.kind({
+ name: "XV.IssueMaterialParameters",
+ kind: "XV.ParameterWidget",
+ components: [
+ {kind: "onyx.GroupboxHeader", content: "_parameters".loc()},
+ {name: "transactionDate", label: "_issueDate".loc(),
+ defaultKind: "XV.DateWidget"},
+ {name: "order", attr: "order", label: "_workOrder".loc(),
+ defaultKind: "XV.OpenWorkOrderWidget",
+ getParameter: function () {
+ var param,
+ value = this.getValue();
+
+ // If no order build a query that returns nothing
+ if (value) {
+ param = {
+ attribute: "order.number",
+ operator: "=",
+ value: value
+ };
+ } else {
+ param = {
+ attribute: "order.number",
+ operator: "=",
+ value: -1
+ };
+ }
+
+ return param;
+ }}
+ ],
+ create: function () {
+ this.inherited(arguments);
+ this.$.transactionDate.setValue(new Date());
+ //this.$.shipment.$.input.setDisabled(true);
+ }
+ });
+
+ };
+
+}());
--- /dev/null
+/*jshint node:true, indent:2, curly:true, eqeqeq:true, immed:true, latedef:true, newcap:true, noarg:true,
+regexp:true, undef:true, trailing:true, white:true, strict:false */
+/*global XM:true, enyo:true */
+
+(function () {
+
+ // ..........................................................
+ // SALES ORDER
+ //
+
+ enyo.kind({
+ name: "XV.OpenWorkOrderWidget",
+ kind: "XV.WorkOrderWidget"
+ });
+
+}());
\ No newline at end of file
--- /dev/null
+[
+ {
+ "context": "manufacturing",
+ "nameSpace": "XM",
+ "type": "IssueMaterial",
+ "table": "xt.womatlissue",
+ "comment": "Issue Work Order Material",
+ "privileges": {
+ "all": {
+ "create": false,
+ "read": "IssueWoMaterials ReturnWoMaterials",
+ "update": "IssueWoMaterials ReturnWoMaterials",
+ "delete": false
+ }
+ },
+ "properties": [
+ {
+ "name": "uuid",
+ "attr": {
+ "type": "String",
+ "column": "obj_uuid",
+ "isPrimaryKey": true
+ }
+ },
+ {
+ "name": "order",
+ "toOne": {
+ "type": "WorkOrderRelation",
+ "column": "womatl_wo_id",
+ "isNested": true
+ }
+ },
+ {
+ "name": "itemSite",
+ "toOne": {
+ "isNested": true,
+ "type": "ItemSiteInventory",
+ "column": "womatl_itemsite_id"
+ }
+ },
+ {
+ "name": "item",
+ "toOne": {
+ "isNested": true,
+ "type": "ItemRelation",
+ "column": "womatl_item_id"
+ }
+ },
+ {
+ "name": "site",
+ "toOne": {
+ "isNested": true,
+ "type": "SiteRelation",
+ "column": "womatl_warehous_id"
+ }
+ },
+ {
+ "name": "status",
+ "attr": {
+ "type": "String",
+ "column": "womatl_status"
+ }
+ },
+ {
+ "name": "method",
+ "attr": {
+ "type": "String",
+ "column": "womatl_issuemethod"
+ }
+ },
+ {
+ "name": "unit",
+ "toOne": {
+ "type": "Unit",
+ "column": "womatl_uom_id",
+ "isNested": true
+ }
+ },
+ {
+ "name": "qtyPer",
+ "attr": {
+ "type": "Quantity",
+ "column": "womatl_qtyper"
+ }
+ },
+ {
+ "name": "qtyFixed",
+ "attr": {
+ "type": "Quantity",
+ "column": "womatl_qtyfxd"
+ }
+ },
+ {
+ "name": "scrap",
+ "attr": {
+ "type": "Quantity",
+ "column": "womatl_scrap"
+ }
+ },
+ {
+ "name": "qtyRequired",
+ "attr": {
+ "type": "Quantity",
+ "column": "womatl_qtyreq"
+ }
+ },
+ {
+ "name": "qtyIssued",
+ "attr": {
+ "type": "Quantity",
+ "column": "womatl_qtyiss"
+ }
+ },
+ {
+ "name": "qtyWipScrapped",
+ "attr": {
+ "type": "Number",
+ "column": "womatl_qtywipscrap"
+ }
+ },
+ {
+ "name": "balance",
+ "attr": {
+ "type": "Number",
+ "column": "balance"
+ }
+ },
+ {
+ "name": "toIssue",
+ "attr": {
+ "type": "Number",
+ "column": "to_issue"
+ }
+ },
+ {
+ "name": "qohBefore",
+ "attr": {
+ "type": "Number",
+ "column": "qoh_before"
+ }
+ }
+ ],
+ "isSystem": true
+ },
+ {
+ "context": "manufacturing",
+ "nameSpace": "XM",
+ "type": "PostProduction",
+ "table": "xt.woinfo",
+ "comment": "Work Order Map",
+ "isRest": true,
+ "lockable": true,
+ "lockTable": "wo",
+ "idSequenceName": "wo_wo_id_seq",
+ "privileges": {
+ "all": {
+ "create": false,
+ "read": "ViewWorkOrders MaintainWorkOrders",
+ "update": "MaintainWorkOrders",
+ "delete": false
+ }
+ },
+ "properties": [
+ {
+ "name": "id",
+ "attr": {
+ "type": "Number",
+ "column": "wo_id",
+ "isPrimaryKey": true
+ }
+ },
+ {
+ "name": "number",
+ "attr": {
+ "type": "String",
+ "column": "wo_number",
+ "isNaturalKey": true
+ }
+ },
+ {
+ "name": "status",
+ "attr": {
+ "type": "String",
+ "column": "wo_status"
+ }
+ },
+ {
+ "name": "itemSite",
+ "toOne": {
+ "isNested": true,
+ "type": "ItemSiteRelation",
+ "column": "wo_itemsite_id"
+ }
+ },
+ {
+ "name": "item",
+ "toOne": {
+ "isNested": true,
+ "type": "ItemRelation",
+ "column": "wo_item_id"
+ }
+ },
+ {
+ "name": "site",
+ "toOne": {
+ "isNested": true,
+ "type": "SiteRelation",
+ "column": "wo_warehous_id"
+ }
+ },
+ {
+ "name": "startDate",
+ "attr": {
+ "type": "Date",
+ "column": "wo_startdate"
+ }
+ },
+ {
+ "name": "dueDate",
+ "attr": {
+ "type": "Date",
+ "column": "wo_duedate"
+ }
+ },
+ {
+ "name": "ordered",
+ "attr": {
+ "type": "Number",
+ "column": "wo_qtyord"
+ }
+ },
+ {
+ "name": "quantityReceived",
+ "attr": {
+ "type": "Number",
+ "column": "wo_qtyrcv"
+ }
+ },
+ {
+ "name": "isAdhoc",
+ "attr": {
+ "type": "Boolean",
+ "column": "wo_adhoc"
+ }
+ },
+ {
+ "name": "wipValue",
+ "attr": {
+ "type": "Cost",
+ "column": "wo_wipvalue"
+ }
+ },
+ {
+ "name": "postedValue",
+ "attr": {
+ "type": "Cost",
+ "column": "wo_postedvalue"
+ }
+ },
+ {
+ "name": "notes",
+ "attr": {
+ "type": "String",
+ "column": "wo_prodnotes"
+ }
+ },
+ {
+ "name": "priority",
+ "attr": {
+ "type": "Number",
+ "column": "wo_priority"
+ }
+ },
+ {
+ "name": "username",
+ "attr": {
+ "type": "String",
+ "column": "wo_username"
+ }
+ },
+ {
+ "name": "balance",
+ "attr": {
+ "type": "Number",
+ "column": "balance"
+ }
+ },
+ {
+ "name": "qtyToPost",
+ "attr": {
+ "type": "Number",
+ "column": "qty_to_post"
+ }
+ }
+ ],
+ "isSystem": true
+ }
+]
--- /dev/null
+{
+ "name": "manufacturing",
+ "comment": "Manufacturing extension",
+ "loadOrder": 80,
+ "databaseScripts": [
+ "xm/javascript/manufacturing.sql",
+ "xt/views/womatlissue.sql",
+ "xt/views/womatlissuedetail.sql"
+ ]
+}
\ No newline at end of file
--- /dev/null
+select xt.install_js('XM','Manufacturing','xtuple', $$
+/* Copyright (c) 1999-2011 by OpenMFG LLC, d/b/a xTuple.
+ See www.xtuple.com/CPAL for the full text of the software license. */
+
+(function () {
+
+ if (!XM.PrivateInventory) { XM.PrivateInventory = {}; }
+
+ if (!XM.Manufacturing) { XM.Manufacturing = {options: []}; }
+
+ XM.Manufacturing.isDispatchable = true;
+
+ XM.Manufacturing.options = [
+ "WorkOrderChangeLog",
+ "AutoExplodeWO",
+ "ExplodeWOEffective",
+ "PostMaterialVariances",
+ "WOExplosionLevel",
+ "DefaultWomatlIssueMethod",
+ "NextWorkOrderNumber",
+ "WONumberGeneration",
+ "JobItemCosDefault"
+ ];
+
+ /*
+ Return Manufacturing configuration settings.
+
+ @returns {Object}
+ */
+ XM.Manufacturing.settings = function() {
+ var keys = XM.Manufacturing.options.slice(0),
+ data = Object.create(XT.Data),
+ sql = "select fetchwonumber();",
+ ret = {},
+ qry;
+
+ ret.NextWorkOrderNumber = plv8.execute(sql)[0].value;
+
+ ret = XT.extend(ret, data.retrieveMetrics(keys));
+
+ return JSON.stringify(ret);
+ };
+
+ /*
+ Update Manufacturing configuration settings. Only valid options as defined in the array
+ XM.Manufacturing.options will be processed.
+
+ @param {Object} settings
+ @returns {Boolean}
+ */
+ XM.Manufacturing.commitSettings = function(patches) {
+ var sql, settings,
+ options = XM.Manufacturing.options.slice(0),
+ data = Object.create(XT.Data),
+ metrics = {};
+
+ /* check privileges */
+ if(!data.checkPrivilege('ConfigureWO')) throw new Error('Access Denied');
+
+ /* Compose our commit settings by applying the patch to what we already have */
+ settings = JSON.parse(XM.Manufacturing.settings());
+ if (!XT.jsonpatch.apply(settings, patches)) {
+ plv8.elog(NOTICE, 'Malformed patch document');
+ }
+
+ /* update numbers */
+ if(settings['NextWorkOrderNumber']) {
+ plv8.execute("select setnextwonumber($1)", [settings['NextWorkOrderNumber'] - 0]);
+ }
+ options.remove('NextWorkOrderNumber');
+
+ /* update remaining options as metrics
+ first make sure we pass an object that only has valid metric options for this type */
+ for(var i = 0; i < options.length; i++) {
+ var prop = options[i];
+ if(settings[prop] !== undefined) metrics[prop] = settings[prop];
+ }
+
+ return data.commitMetrics(metrics);
+ };
+
+ /**
+ Issue Material.
+
+ select xt.post('{
+ "username": "admin",
+ "nameSpace":"XM",
+ "type":"Inventory",
+ "dispatch":{
+ "functionName":"issueMaterial",
+ "parameters":[
+ "95c30aba-883a-41da-e780-1d844a1dc112",
+ 1,
+ {
+ "asOf": "2013-07-03T13:52:55.964Z",
+ "detail": [
+ {
+ "location": "84cf43d5-8a44-4a2b-f709-4f415ca51a52",
+ "quantity": 8
+ },
+ {
+ "location": "d756682c-eda3-445d-eaef-4dce793b0dcf",
+ "quantity": 2
+ }
+ ]
+ }
+ ]
+ }
+ }');
+
+ @param {String|Array} Order line uuid or array of objects
+ @param {Number|Object} Quantity or options
+ @param {Date} [options.asOf=now()] Transaction Timestamp
+ @param {Array} [options.detail] Distribution detail
+ */
+ XM.Manufacturing.issueMaterial = function (orderLine, quantity, options) {
+ var asOf,
+ series,
+ sql,
+ sql2,
+ ary,
+ item,
+ i;
+
+ /* Make into an array if an array not passed */
+ if (typeof arguments[0] !== "object") {
+ ary = [{orderLine: orderLine, quantity: quantity, options: options || {}}];
+ } else {
+ ary = arguments;
+ }
+
+ /* Make sure user can do this */
+ if (!XT.Data.checkPrivilege("IssueWoMaterials")) { throw new handleError("Access Denied", 401); }
+
+ sql = "select issuewomaterial(womatl_id, $2::numeric, $3::integer, $4::timestamptz) as series " +
+ "from womatl where obj_uuid = $1;";
+
+ sql2 = "select current_date != $1 as invalid";
+
+ /* Post the transaction */
+ for (i = 0; i < ary.length; i++) {
+ item = ary[i];
+ asOf = item.options ? item.options.asOf : null;
+ series = plv8.execute(sql, [item.orderLine, item.quantity, 0, asOf])[0].series;
+
+ if (asOf && plv8.execute(sql2, [asOf])[0].invalid &&
+ !XT.Data.checkPrivilege("AlterTransactionDates")) {
+ throw new handleError("Insufficient privileges to alter transaction date", 401);
+ }
+
+ /* Distribute detail */
+ XM.PrivateInventory.distribute(series, item.options.detail);
+ }
+
+ return;
+ };
+ XM.Manufacturing.issueMaterial.description = "Issue Materials.";
+ XM.Manufacturing.issueMaterial.params = {
+ orderLine: { type: "String", description: "Order line UUID" },
+ quantity: {type: "Number", description: "Quantity" },
+ options: {type: "Object", description: "Other attributes", attributes: {
+ asOf: {type: "Date", description: "Transaction Timestamp. Default to now()."},
+ detail: {type: "Array", description: "Distribution detail" }
+ }}
+ };
+
+ /**
+ Return material transactions.
+
+ select xt.post('{
+ "username": "admin",
+ "nameSpace":"XM",
+ "type":"Inventory",
+ "dispatch":{
+ "functionName":"returnFromShipping",
+ "parameters":["95c30aba-883a-41da-e780-1d844a1dc112"]
+ }
+ }');
+
+ @param {String|Array} Order line uuid, or array of uuids
+ */
+ XM.Manufacturing.returnMaterial = function (orderLine) {
+ var sql = "select returnwomaterial(womatl_id, womatl_qtyiss, current_timestamp) " +
+ "from womatl where obj_uuid = $1;",
+ ret,
+ i;
+
+ /* Make sure user can do this */
+ if (!XT.Data.checkPrivilege("ReturnWoMaterials")) { throw new handleError("Access Denied", 401); }
+
+ /* Post the transaction */
+ for (i = 0; i < arguments.length; i++) {
+ ret = plv8.execute(sql, [arguments[i]])[0];
+ }
+
+ return ret;
+ };
+
+ XM.Manufacturing.returnMaterial.description = "Return shipment transactions.";
+ XM.Manufacturing.returnMaterial.params = {
+ orderLine: { type: "String", description: "Order line UUID" }
+ };
+
+ /**
+ Post production.
+
+ select xt.post('{
+ "username": "admin",
+ "nameSpace":"XM",
+ "type":"Manufacturing",
+ "dispatch":{
+ "functionName":"shipShipment",
+ "parameters":["203"]
+ }
+ }');
+
+ @param {Number} Shipment number
+ @param {Date} Ship date, default = current date
+ */
+ XM.Manufacturing.postProduction = function (workOrder, quantity) {
+ var sql = "select postproduction(wo_id, $2, true, 0, current_timestamp) as series " +
+ "from wo where obj_uuid = $1;";
+
+ /* Make sure user can do this */
+ if (!XT.Data.checkPrivilege("PostProduction")) { throw new handleError("Access Denied", 401); }
+
+ /* Post the transaction */
+ var ret = plv8.execute(sql, [workOrder, quantity])[0].series;
+
+ return ret;
+ };
+ XM.Manufacturing.postProduction.description = "Post production";
+ XM.Manufacturing.postProduction.params = {
+ workOrder: { type: "String", description: "Order line UUID" },
+ quantity: {type: "Number", description: "Quantity" }
+ };
+
+}());
+
+$$ );
--- /dev/null
+select xt.create_view('xt.womatlissue', $$
+
+ select
+ womatl.*,
+ item_id AS womatl_item_id,
+ itemsite_warehous_id AS womatl_warehous_id,
+ itemsite_qtyonhand AS qoh_before,
+ case when (womatl_qtyiss > womatl_qtyreq) then 0 else (womatl_qtyreq - womatl_qtyiss) end AS balance,
+ null::numeric AS to_issue
+ from womatl
+ join itemsite on itemsite_id=womatl_itemsite_id
+ join item on itemsite_item_id=item_id
+ join wo on wo_id=womatl_wo_id
+ where coalesce(womatl_status, '') != 'C'
+ AND wo_status != 'C'
+ AND item_type != 'K'
+ order by item_number
+
+$$);
\ No newline at end of file
--- /dev/null
+select xt.create_view('xt.womatlissuedetail', $$
+
+ select
+ womatlpost_id as issued_materials,
+ womatl_id as order_line,
+ case when invdetail_location_id = -1 then null else invdetail_location_id end as location,
+ invdetail_ls_id as trace,
+ invdetail_qty * -1 as quantity,
+ invhist_invuom as unit
+ from invdetail
+ join invhist on invdetail_invhist_id=invhist_id
+ join womatlpost on invhist_id=womatlpost_invhist_id
+ join womatl on womatlpost_womatl_id = womatl_id
+ join wo on womatl_wo_id=wo_id
+
+$$, true);
--- /dev/null
+/*jshint node:true, indent:2, curly:false, eqeqeq:true, immed:true,
+latedef:true, newcap:true, noarg:true, regexp:true, undef:true,
+strict:true, trailing:true, white:true */
+/*global XT:true */
+
+(function () {
+ "use strict";
+
+ var lang = XT.stringsFor("en_US", {
+ "_projectType": "Project Type",
+ "_projectTypes": "Project Types"
+ });
+
+ if (typeof exports !== 'undefined') {
+ exports.language = lang;
+ }
+}());
--- /dev/null
+/*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, console:true */
+
+(function () {
+ "use strict";
+
+ XT.extensions.project.initCharacteristicModels = function () {
+
+ // Add to context attributes
+ var ary = XM.Characteristic.prototype.contextAttributes;
+ ary.push("isProjects");
+ ary.push("isTasks");
+
+ };
+
+}());
enyo.depends(
"account.js",
+ "characteristic.js",
"contact.js",
"customer.js",
"incident.js",
"project.js",
"quote.js",
"sales_order.js",
- "to_do.js"
+ "to_do.js",
+ "startup.js"
);
"use strict";
XT.extensions.project.initProjectModels = function () {
+
+ var _proto = XM.Project.prototype,
+ _defaults = _proto.defaults;
+
+ _proto.defaults = function () {
+ var defaults = _.isFunction(_defaults) ? _defaults() : defaults;
+ // Add first active project type
+ defaults.projectType = _.find(XM.projectTypes.models, function (model) {
+ return model.get("isActive");
+ });
+ return defaults;
+ };
+ /**
+ @class
+
+ @extends XM.Document
+ */
+ XM.ProjectType = XM.Document.extend(
+ /** @scope XM.ProjectType.prototype */ {
+
+ recordType: 'XM.ProjectType',
+
+ documentKey: 'code',
+
+ enforceUpperKey: false,
+
+ defaults: {
+ isActive: true
+ }
+
+ });
/**
@class
isDocumentAssignment: true
});
+
+ /**
+ @class
+
+ @extends XM.CharacteristicAssignment
+ */
+ XM.ProjectCharacteristic = XM.CharacteristicAssignment.extend(
+ /** @scope XM.ProjectCharacteristic.prototype */ {
+
+ recordType: 'XM.ProjectCharacteristic'
+
+ });
+
+ /**
+ @class
+
+ @extends XM.CharacteristicAssignment
+ */
+ XM.ProjectListItemCharacteristic = XM.CharacteristicAssignment.extend(
+ /** @scope XM.ProjectListItemCharacteristic.prototype */ {
+
+ recordType: 'XM.ProjectListItemCharacteristic'
+
+ });
+
+ /**
+ @class
+
+ @extends XM.CharacteristicAssignment
+ */
+ XM.ProjectTaskCharacteristic = XM.CharacteristicAssignment.extend(
+ /** @scope XM.ProjectTaskCharacteristic.prototype */ {
+
+ recordType: 'XM.ProjectTaskCharacteristic'
+
+ });
+
+ /**
+ @class
+
+ @extends XM.CharacteristicAssignment
+ */
+ XM.TaskCharacteristic = XM.CharacteristicAssignment.extend(
+ /** @scope XM.TaskCharacteristic.prototype */ {
+
+ recordType: 'XM.TaskCharacteristic'
+
+ });
+
+ /**
+ @class
+
+ @extends XM.CharacteristicAssignment
+ */
+ XM.TaskListItemCharacteristic = XM.CharacteristicAssignment.extend(
+ /** @scope XM.TaskListItemCharacteristic.prototype */ {
+
+ recordType: 'XM.TaskListItemCharacteristic'
+
+ });
+
+ // ..........................................................
+ // COLLECTIONS
+ //
+
+ /**
+ @class
+
+ @extends XM.Collection
+ */
+ XM.ProjectTypeCollection = XM.Collection.extend({
+ /** @scope XM.ProjectTypeCollection.prototype */
+
+ model: XM.ProjectType
+
+ });
+
};
}());
--- /dev/null
+/*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, console:true */
+
+(function () {
+ "use strict";
+
+ XT.extensions.project.initStartup = function () {
+ XT.cacheCollection("XM.projectTypes", "XM.ProjectTypeCollection", "code");
+ };
+
+}());
{name: "classCodeList", kind: "XV.ClassCodeList"},
{name: "unitList", kind: "XV.UnitList"},
{name: "stateList", kind: "XV.StateList"},
- {name: "countryList", kind: "XV.CountryList"}
+ {name: "countryList", kind: "XV.CountryList"},
+ {name: "projectTypeList", kind: "XV.ProjectTypeList"}
];
XT.app.$.postbooks.appendPanels("setup", panels);
--- /dev/null
+/*jshint bitwise:true, indent:2, curly:true, eqeqeq:true, immed:true,
+latedef:true, newcap:true, noarg:true, regexp:true, undef:true,
+trailing:true, white:true*/
+/*global XT:true, XV:true, enyo:true*/
+
+(function () {
+
+ XT.extensions.project.initLists = function () {
+
+ // ..........................................................
+ // PROJECT TYPE
+ //
+
+ enyo.kind({
+ name: "XV.ProjectTypeList",
+ kind: "XV.List",
+ label: "_projectTypes".loc(),
+ collection: "XM.ProjectTypeCollection",
+ query: {orderBy: [
+ {attribute: 'code'}
+ ]},
+ components: [
+ {kind: "XV.ListItem", components: [
+ {kind: "FittableColumns", components: [
+ {kind: "XV.ListColumn", classes: "short",
+ components: [
+ {kind: "XV.ListAttr", attr: "code", isKey: true}
+ ]},
+ {kind: "XV.ListColumn", classes: "last", fit: true, components: [
+ {kind: "XV.ListAttr", attr: "description"}
+ ]}
+ ]}
+ ]}
+ ]
+ });
+
+ };
+
+}());
--- /dev/null
+/*jshint bitwise:true, indent:2, curly:true, eqeqeq:true, immed:true,
+latedef:true, newcap:true, noarg:true, regexp:true, undef:true,
+trailing:true, white:true*/
+/*global XT:true, XM:true, XV:true, enyo:true*/
+
+(function () {
+
+ XT.extensions.project.initListRelationsEditorBoxes = function () {
+ var extensions;
+
+ // ..........................................................
+ // TASK
+ //
+
+ extensions = [
+ {kind: "XV.TaskCharacteristicsWidget", container: "mainGroup",
+ attr: "characteristics"}
+ ];
+
+ XV.appendExtension("XV.ProjectTaskEditor", extensions);
+
+ };
+
+}());
enyo.depends(
+ "list.js",
"list_relations.js",
"list_relations_box.js",
+ "list_relations_editor_box.js",
"workspace.js"
);
\ No newline at end of file
];
XV.appendExtension("XV.AccountWorkspace", extensions);
+
+ // ..........................................................
+ // CHARACTERISTIC
+ //
+
+ extensions = [
+ {kind: "XV.ToggleButtonWidget", attr: "isProjects",
+ label: "_projects".loc(), container: "rolesGroup"},
+ ];
+
+ XV.appendExtension("XV.CharacteristicWorkspace", extensions);
// ..........................................................
// CONTACT
XV.appendExtension("XV.IncidentWorkspace", extensions);
+ // ..........................................................
+ // PROJECT
+ //
+
+ extensions = [
+ {kind: "XV.ProjectTypePicker", container: "overviewControl",
+ attr: "projectType", addBefore: "projectStatusPicker"},
+ {kind: "XV.ProjectCharacteristicsWidget", container: "mainGroup",
+ attr: "characteristics"}
+ ];
+
+ XV.appendExtension("XV.ProjectWorkspace", extensions);
+
+ // ..........................................................
+ // PROJECT TYPE
+ //
+
+ enyo.kind({
+ name: "XV.ProjectTypeWorkspace",
+ kind: "XV.Workspace",
+ title: "_projectType".loc(),
+ model: "XM.ProjectType",
+ components: [
+ {kind: "Panels", arrangerKind: "CarouselArranger",
+ fit: true, components: [
+ {kind: "XV.Groupbox", name: "mainPanel", components: [
+ {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
+ {kind: "XV.ScrollableGroupbox", name: "mainGroup",
+ classes: "in-panel", components: [
+ {kind: "XV.InputWidget", attr: "code"},
+ {kind: "XV.CheckboxWidget", attr: "isActive"},
+ {kind: "XV.InputWidget", attr: "description"}
+ ]}
+ ]}
+ ]}
+ ]
+ });
+
+ XV.registerModelWorkspace("XM.ProjectType", "XV.ProjectTypeWorkspace");
+
+
// ..........................................................
// QUOTE
//
XV.appendExtension("XV.SalesOrderWorkspace", extensions);
+ // ..........................................................
+ // TASK
+ //
+
+ extensions = [
+ {kind: "XV.TaskCharacteristicsWidget", container: "mainGroup",
+ attr: "characteristics"}
+ ];
+
+ XV.appendExtension("XV.TaskWorkspace", extensions);
+
};
}());
--- /dev/null
+/*jshint indent:2, curly:true, eqeqeq:true, immed:true, latedef:true,
+newcap:true, noarg:true, regexp:true, undef:true, trailing:true,
+white:true*/
+/*global enyo:true, XT:true, XV:true, Globalize:true, XM:true */
+
+(function () {
+
+ XT.extensions.project.initCharacteristicWidgets = function () {
+ // ..........................................................
+ // PROJECT
+ //
+
+ enyo.kind({
+ name: "XV.ProjectCharacteristicsWidget",
+ kind: "XV.CharacteristicsWidget",
+ model: "XM.AccountCharacteristic",
+ which: "isProjects"
+ });
+
+ // ..........................................................
+ // TASK
+ //
+
+ enyo.kind({
+ name: "XV.TaskCharacteristicsWidget",
+ kind: "XV.CharacteristicsWidget",
+ model: "XM.ContactCharacteristic",
+ which: "isTasks"
+ });
+ }
+
+}());
enyo.depends(
- "parameter.js"
+ "characteristics.js",
+ "parameter.js",
+ "picker.js"
);
];
XV.appendExtension("XV.IncidentListParameters", extensions);
+
+ // ..........................................................
+ // PROJECT
+ //
+
+ XV.ProjectListParameters.prototype.characteristicsRole = 'isProjects';
+
+ // ..........................................................
+ // TASK
+ //
+
+ XV.ProjectTaskListParameters.prototype.characteristicsRole = 'isTasks';
};
}());
--- /dev/null
+/*jshint indent:2, curly:true, eqeqeq:true, immed:true, latedef:true,
+newcap:true, noarg:true, regexp:true, undef:true, trailing:true,
+white:true*/
+/*global enyo:true, XT:true, XV:true, Globalize:true, XM:true */
+
+(function () {
+
+ XT.extensions.project.initPickers = function () {
+
+ // ..........................................................
+ // PROJECT TYPE
+ //
+
+ enyo.kind({
+ name: "XV.ProjectTypePicker",
+ kind: "XV.PickerWidget",
+ collection: "XM.projectTypes",
+ showNone: false,
+ nameAttribute: "code"
+ });
+
+ }
+
+}());
--- /dev/null
+[
+ {
+ "context": "project",
+ "nameSpace": "XM",
+ "type": "Characteristic",
+ "table": "char",
+ "isExtension": true,
+ "comment": "Extended by Project",
+ "relations": [
+ {
+ "column": "char_id",
+ "inverse": "id"
+ }
+ ],
+ "properties": [
+ {
+ "name": "isProjects",
+ "attr": {
+ "type": "Boolean",
+ "column": "char_projects"
+ }
+ },
+ {
+ "name": "isTasks",
+ "attr": {
+ "type": "Boolean",
+ "column": "char_tasks"
+ }
+ }
+ ],
+ "isSystem": true
+ }
+]
}
],
"properties": [
+ {
+ "name": "projectType",
+ "toOne": {
+ "type": "ProjectType",
+ "column": "prj_prjtype_id"
+ }
+ },
{
"name": "opportunities",
"toMany": {
"column": "prj_id",
"inverse": "source"
}
+ },
+ {
+ "name": "characteristics",
+ "toMany": {
+ "isNested": true,
+ "type": "ProjectCharacteristic",
+ "column": "prj_id",
+ "inverse": "project"
+ }
+ }
+ ],
+ "isSystem": true
+ },
+ {
+ "context": "project",
+ "nameSpace": "XM",
+ "type": "ProjectListItem",
+ "table": "prj",
+ "isExtension": true,
+ "comment": "Extended by Project",
+ "relations": [
+ {
+ "column": "prj_id",
+ "inverse": "id"
}
],
- "sequence": 0,
+ "properties": [
+ {
+ "name": "projectType",
+ "toOne": {
+ "type": "ProjectType",
+ "column": "prj_id"
+ }
+ },
+ {
+ "name": "characteristics",
+ "toMany": {
+ "isNested": true,
+ "type": "ProjectListItemCharacteristic",
+ "column": "prj_id",
+ "inverse": "project"
+ }
+ }
+ ],
+ "isSystem": true
+ },
+ {
+ "context": "project",
+ "nameSpace": "XM",
+ "type": "ProjectTask",
+ "table": "prjtask",
+ "isExtension": true,
+ "comment": "Extended by Project",
+ "relations": [
+ {
+ "column": "prjtask_id",
+ "inverse": "id"
+ }
+ ],
+ "properties": [
+ {
+ "name": "characteristics",
+ "toMany": {
+ "isNested": true,
+ "type": "ProjectTaskCharacteristic",
+ "column": "prjtask_id",
+ "inverse": "projectTask"
+ }
+ }
+ ],
+ "isSystem": true
+ },
+ {
+ "context": "project",
+ "nameSpace": "XM",
+ "type": "Task",
+ "table": "prjtask",
+ "isExtension": true,
+ "comment": "Extended by Project",
+ "relations": [
+ {
+ "column": "prjtask_id",
+ "inverse": "id"
+ }
+ ],
+ "properties": [
+ {
+ "name": "characteristics",
+ "toMany": {
+ "isNested": true,
+ "type": "TaskCharacteristic",
+ "column": "prjtask_id",
+ "inverse": "task"
+ }
+ }
+ ],
+ "isSystem": true
+ },
+ {
+ "context": "project",
+ "nameSpace": "XM",
+ "type": "TaskListItem",
+ "table": "prjtask",
+ "isExtension": true,
+ "comment": "Extended by Project",
+ "relations": [
+ {
+ "column": "prjtask_id",
+ "inverse": "id"
+ }
+ ],
+ "properties": [
+ {
+ "name": "characteristics",
+ "toMany": {
+ "isNested": true,
+ "type": "TaskListItemCharacteristic",
+ "column": "prjtask_id",
+ "inverse": "task"
+ }
+ }
+ ],
"isSystem": true
},
{
[
+ {
+ "context": "project",
+ "nameSpace": "XM",
+ "type": "ProjectType",
+ "table": "prjtype",
+ "idSequenceName": "prjtype_prjtype_id_seq",
+ "comment": "Project Type Map",
+ "privileges": {
+ "all": {
+ "create": "MaintainProjectTypes",
+ "read": true,
+ "update": "MaintainProjectTypes",
+ "delete": "MaintainProjectTypes"
+ }
+ },
+ "properties": [
+ {
+ "name": "id",
+ "attr": {
+ "type": "Number",
+ "column": "prjtype_id",
+ "isPrimaryKey": true
+ }
+ },
+ {
+ "name": "code",
+ "attr": {
+ "type": "String",
+ "column": "prjtype_code",
+ "isNaturalKey": true
+ }
+ },
+ {
+ "name": "isActive",
+ "attr": {
+ "type": "Boolean",
+ "column": "prjtype_active"
+ }
+ },
+ {
+ "name": "description",
+ "attr": {
+ "type": "String",
+ "column": "prjtype_descr"
+ }
+ }
+ ],
+ "isSystem": true
+ },
{
"context": "project",
"nameSpace": "XM",
],
"isNestedOnly": true,
"isSystem": true
+ },
+ {
+ "context": "project",
+ "nameSpace": "XM",
+ "type": "ProjectCharacteristic",
+ "table": "charass",
+ "idSequenceName": "charass_charass_id_seq",
+ "comment": "Project Characteristic Map",
+ "properties": [
+ {
+ "name": "id",
+ "attr": {
+ "type": "Number",
+ "column": "charass_id",
+ "isPrimaryKey": true
+ }
+ },
+ {
+ "name": "uuid",
+ "attr": {
+ "type": "String",
+ "column": "obj_uuid",
+ "isNaturalKey": true
+ }
+ },
+ {
+ "name": "targetType",
+ "attr": {
+ "type": "String",
+ "column": "charass_target_type",
+ "value": "PROJ"
+ }
+ },
+ {
+ "name": "project",
+ "attr": {
+ "type": "Number",
+ "column": "charass_target_id"
+ }
+ },
+ {
+ "name": "characteristic",
+ "toOne": {
+ "type": "Characteristic",
+ "column": "charass_char_id",
+ "required": true
+ }
+ },
+ {
+ "name": "value",
+ "attr": {
+ "type": "String",
+ "column": "charass_value"
+ }
+ }
+ ],
+ "isNestedOnly": true,
+ "isSystem": true
+ },
+ {
+ "context": "project",
+ "nameSpace": "XM",
+ "type": "ProjectListItemCharacteristic",
+ "table": "charass",
+ "comment": "Project List Item Characteristic Map",
+ "properties": [
+ {
+ "name": "id",
+ "attr": {
+ "type": "Number",
+ "column": "charass_id",
+ "isPrimaryKey": true
+ }
+ },
+ {
+ "name": "uuid",
+ "attr": {
+ "type": "String",
+ "column": "obj_uuid",
+ "isNaturalKey": true
+ }
+ },
+ {
+ "name": "targetType",
+ "attr": {
+ "type": "String",
+ "column": "charass_target_type",
+ "value": "PROJ"
+ }
+ },
+ {
+ "name": "project",
+ "attr": {
+ "type": "Number",
+ "column": "charass_target_id"
+ }
+ },
+ {
+ "name": "characteristic",
+ "toOne": {
+ "type": "Characteristic",
+ "column": "charass_char_id",
+ "required": true
+ }
+ },
+ {
+ "name": "value",
+ "attr": {
+ "type": "String",
+ "column": "charass_value"
+ }
+ }
+ ],
+ "isNestedOnly": true,
+ "isSystem": true
+ },
+ {
+ "context": "project",
+ "nameSpace": "XM",
+ "type": "ProjectTaskCharacteristic",
+ "table": "charass",
+ "idSequenceName": "charass_charass_id_seq",
+ "comment": "Project Task Characteristic Map",
+ "properties": [
+ {
+ "name": "id",
+ "attr": {
+ "type": "Number",
+ "column": "charass_id",
+ "isPrimaryKey": true
+ }
+ },
+ {
+ "name": "uuid",
+ "attr": {
+ "type": "String",
+ "column": "obj_uuid",
+ "isNaturalKey": true
+ }
+ },
+ {
+ "name": "targetType",
+ "attr": {
+ "type": "String",
+ "column": "charass_target_type",
+ "value": "TASK"
+ }
+ },
+ {
+ "name": "projectTask",
+ "attr": {
+ "type": "Number",
+ "column": "charass_target_id"
+ }
+ },
+ {
+ "name": "characteristic",
+ "toOne": {
+ "type": "Characteristic",
+ "column": "charass_char_id",
+ "required": true
+ }
+ },
+ {
+ "name": "value",
+ "attr": {
+ "type": "String",
+ "column": "charass_value"
+ }
+ }
+ ],
+ "isNestedOnly": true,
+ "isSystem": true
+ },
+ {
+ "context": "project",
+ "nameSpace": "XM",
+ "type": "TaskCharacteristic",
+ "table": "charass",
+ "idSequenceName": "charass_charass_id_seq",
+ "comment": "Task Characteristic Map",
+ "properties": [
+ {
+ "name": "id",
+ "attr": {
+ "type": "Number",
+ "column": "charass_id",
+ "isPrimaryKey": true
+ }
+ },
+ {
+ "name": "uuid",
+ "attr": {
+ "type": "String",
+ "column": "obj_uuid",
+ "isNaturalKey": true
+ }
+ },
+ {
+ "name": "targetType",
+ "attr": {
+ "type": "String",
+ "column": "charass_target_type",
+ "value": "TASK"
+ }
+ },
+ {
+ "name": "task",
+ "attr": {
+ "type": "Number",
+ "column": "charass_target_id"
+ }
+ },
+ {
+ "name": "characteristic",
+ "toOne": {
+ "type": "Characteristic",
+ "column": "charass_char_id",
+ "required": true
+ }
+ },
+ {
+ "name": "value",
+ "attr": {
+ "type": "String",
+ "column": "charass_value"
+ }
+ }
+ ],
+ "isNestedOnly": true,
+ "isSystem": true
+ },
+ {
+ "context": "project",
+ "nameSpace": "XM",
+ "type": "TaskListItemCharacteristic",
+ "table": "charass",
+ "idSequenceName": "charass_charass_id_seq",
+ "comment": "Task List Item Characteristic Map",
+ "properties": [
+ {
+ "name": "id",
+ "attr": {
+ "type": "Number",
+ "column": "charass_id",
+ "isPrimaryKey": true
+ }
+ },
+ {
+ "name": "uuid",
+ "attr": {
+ "type": "String",
+ "column": "obj_uuid",
+ "isNaturalKey": true
+ }
+ },
+ {
+ "name": "targetType",
+ "attr": {
+ "type": "String",
+ "column": "charass_target_type",
+ "value": "TASK"
+ }
+ },
+ {
+ "name": "task",
+ "attr": {
+ "type": "Number",
+ "column": "charass_target_id"
+ }
+ },
+ {
+ "name": "characteristic",
+ "toOne": {
+ "type": "Characteristic",
+ "column": "charass_char_id",
+ "required": true
+ }
+ },
+ {
+ "name": "value",
+ "attr": {
+ "type": "String",
+ "column": "charass_value"
+ }
+ }
+ ],
+ "isNestedOnly": true,
+ "isSystem": true
}
]
for (i = 0; i < extensions.length; i++) {
ext = _.clone(this.extensions[i]);
// Resolve name of container to the instance
- if (ext.container && typeof ext.container === 'string') {
+ if (_.isString(ext.container)) {
ext.container = this.$[ext.container];
}
+ // Resolve `addBefore`
+ if (_.isString(ext.addBefore)) {
+ ext.addBefore = this.$[ext.addBefore];
+ }
this.createComponent(ext);
}
this._extLength = extensions.length;
max-width: 100px;
overflow: hidden;
}
+ &.disabled {
+ color: @dim-gray;
+ }
}
.xv-picker-label {
max-width: 100px;
overflow: hidden;
}
+.xv-picker-button.disabled {
+ color: #696969;
+}
.xv-picker-label {
color: #000000;
padding: 18px 8px 8px 8px;
"my_account_popup.js",
"sort_popup.js",
"module_container.js",
- "transaction_list.js"
+ "transaction_list.js",
+ "transaction_list_container.js"
);
a transaction date.
@name XV.TransactionList
- @extends XV.SearchContainer
+ @extends XV.List
*/
- var transactionList = /** @lends XV.TransactionList# */ {
+ enyo.kind({
name: "XV.TransactionList",
- kind: "Panels",
- classes: "app enyo-unselectable",
- arrangerKind: "CollapsingArranger",
+ kind: "XV.List",
published: {
- prerequisite: "",
- notifyMessage: "",
- list: null,
- actions: null,
- transactionDate: null,
- model: null
+ transModule: "",
+ transWorkspace: ""
},
events: {
- onPrevious: "",
- onWorkspace: ""
- },
- handlers: {
- onListItemMenuTap: "showListItemMenu",
- onParameterChange: "requery",
- onProcessingChanged: "processingChanged",
- onSelectionChanged: "selectionChanged"
- },
- init: false,
- components: [
- {name: "parameterPanel", kind: "FittableRows", classes: "left",
- components: [
- {kind: "onyx.Toolbar", classes: "onyx-menu-toolbar", components: [
- {kind: "onyx.Button", name: "backButton", content: "_back".loc(), ontap: "close"},
- {kind: "onyx.MenuDecorator", style: "margin: 0;",
- onSelect: "actionSelected", components: [
- {kind: "XV.IconButton", src: "/assets/menu-icon-gear.png",
- content: "_actions".loc(), name: "actionButton"},
- {kind: "onyx.Menu", name: "actionMenu"}
- ]}
- ]},
- {kind: "Scroller", name: "parameterScroller"}
- ]},
- {name: "listPanel", kind: "FittableRows", components: [
- // the onyx-menu-toolbar class keeps the popups from being hidden
- {kind: "onyx.MoreToolbar", name: "contentToolbar",
- classes: "onyx-menu-toolbar", movedClass: "xv-toolbar-moved", components: [
- {kind: "onyx.Grabber", classes: "left-float"},
- {name: "rightLabel", content: "_search".loc(), classes: "left-float"},
- {name: "space", fit: true},
- {kind: "onyx.Button", name: "printButton", showing: false,
- content: "_print".loc(), onclick: "print"},
- {kind: "onyx.Button", name: "refreshButton", disabled: false,
- content: "_refresh".loc(), onclick: "requery"},
- {kind: "onyx.Button", name: "postButton",
- disabled: true, classes: "save", showing: false,
- content: "_post".loc(), onclick: "post"},
- {name: "listItemMenu", kind: "onyx.Menu", floating: true,
- onSelect: "listActionSelected", maxHeight: 500}
- ]},
- {name: "contentPanels", kind: "Panels", margin: 0, fit: true, draggable: false,
- panelCount: 0},
- {kind: "onyx.Popup", name: "spinnerPopup", centered: true,
- modal: true, floating: true, scrim: true,
- onHide: "popupHidden", components: [
- {kind: "onyx.Spinner"},
- {name: "spinnerMessage", content: "_processing".loc() + "..."}
- ]}
- ]}
+ onProcessingChanged: "",
+ onOrderChanged: "",
+ onShipmentChanged: ""
+ },
+ multiSelect: true,
+ showDeleteAction: false,
+ toggleSelected: true,
+ actions: [
+ {name: "issueItem", prerequisite: "canIssueItem",
+ method: "issueItem", notify: false, isViewMethod: true},
+ {name: "issueLine", prerequisite: "canIssueItem",
+ method: "issueLine", notify: false, isViewMethod: true},
+ {name: "returnLine", prerequisite: "canReturnItem",
+ method: "returnItem", notify: false, isViewMethod: true}
],
- actionSelected: function (inSender, inEvent) {
- var action = inEvent.originator.action,
- method = action.method || action.name;
+ /**
+ Helper function for transacting `issue` on an array of models
- this[method](inSender, inEvent);
- },
- close: function () {
- this.doPrevious();
- },
- buildMenu: function () {
- var actionMenu = this.$.actionMenu,
- actions = this.getActions().slice(0),
- that = this;
+ @param {Array} Models
+ @param {Boolean} Prompt user for confirmation on every model
+ */
+ issue: function (models, prompt, issueStock) {
+ var that = this,
+ i = -1,
+ callback,
+ data = [];
- // reset the menu
- actionMenu.destroyClientControls();
+ // Recursively issue everything we can
+ callback = function (workspace) {
+ var model,
+ options = {},
+ toIssue,
+ transDate,
+ params,
+ dispOptions = {},
+ wsOptions = {},
+ wsParams,
+ transModule = that.getTransModule(),
+ transWorkspace = that.getTransWorkspace();
- // then add whatever actions are applicable
- _.each(actions, function (action) {
- var name = action.name,
- prerequisite = action.prerequisite,
- isDisabled = prerequisite ? !that[prerequisite]() : false;
- actionMenu.createComponent({
- name: name,
- kind: XV.MenuItem,
- content: action.label || ("_" + name).loc(),
- action: action,
- disabled: isDisabled
- });
+ // If argument is false, this whole process was cancelled
+ if (workspace === false) {
+ return;
- });
- actionMenu.render();
- this.$.actionButton.setShowing(actions.length);
- },
- create: function () {
- this.inherited(arguments);
- var disabled = !XT.session.privileges.get("AlterTransactionDates"),
- parameterWidget;
- this.setList({list: this.getList()});
- parameterWidget = this.$.parameterWidget;
- parameterWidget.$.transactionDate.$.input.setDisabled(disabled);
- if (!this.getActions()) {
- this.setActions([]);
- }
- this.buildMenu();
- },
- fetch: function (options) {
- if (!this.init) { return; }
- options = options ? _.clone(options) : {};
- var list = this.$.list,
- query,
- parameterWidget,
- parameters;
- if (!list) { return; }
- query = list.getQuery() || {};
- parameterWidget = this.$.parameterWidget;
- parameters = parameterWidget && parameterWidget.getParameters ?
- parameterWidget.getParameters() : [];
- options.showMore = _.isBoolean(options.showMore) ?
- options.showMore : false;
+ // If a workspace brought us here, process the information it obtained
+ } else if (workspace) {
+ model = workspace.getValue();
+ toIssue = model.get("toIssue");
+ transDate = model.transactionDate;
- // Build conditions
- if (parameters.length) {
- query.parameters = parameters;
- } else {
- delete query.parameters;
- }
- list.setQuery(query);
- list.fetch(options);
- },
- /**
- Capture order changed and transaction date changed events.
- Depends on a very specific implementation of parameter widget
- that includes `order` and `transactionDate` parameters.
- */
- parameterChanged: function (inSender, inEvent) {
- var originator = inEvent ? inEvent.originator : false,
- name = originator ? originator.name : false,
- that = this,
- options,
- value;
+ if (toIssue) {
+ wsOptions.detail = model.formatDetail();
+ wsOptions.asOf = transDate;
+ wsParams = {
+ orderLine: model.id,
+ quantity: toIssue,
+ options: wsOptions
+ };
+ data.push(wsParams);
+ }
+ workspace.doPrevious();
+ }
- if (name === "transactionDate") {
- value = originator.$.input.getValue();
- value = XT.date.applyTimezoneOffset(value, true);
- value = XT.date.toMidnight(value);
- this.setTransactionDate(value);
- this.buildMenu();
- return;
- } else if (name === "order") {
- value = originator.getParameter().value;
- this.setModel(value);
- this.buildMenu();
- } else if (name === "shipment") {
- return;
- }
+ i++;
+ // If we've worked through all the models then forward to the server
+ if (i === models.length) {
+ if (data[0]) {
+ that.doProcessingChanged({isProcessing: true});
+ dispOptions.success = function () {
+ that.doProcessingChanged({isProcessing: false});
+ };
+ transModule.issueItem(data, dispOptions);
+ } else {
+ return;
+ }
+
+ // Else if there's something here we can issue, handle it
+ } else {
+ model = models[i];
+ toIssue = model.get("toIssue");
+ transDate = model.transactionDate;
+
+ // See if there's anything to issue here
+ if (toIssue || issueStock) {
+
+ // If prompt or distribution detail required,
+ // open a workspace to handle it
+ if (prompt || model.undistributed()) {
+ that.doWorkspace({
+ workspace: transWorkspace,
+ id: model.id,
+ callback: callback,
+ allowNew: false,
+ success: function (model) {
+ model.transactionDate = transDate;
+ }
+ });
+
+ // Otherwise just use the data we have
+ } else {
+ options.asOf = transDate;
+ options.detail = model.formatDetail();
+ params = {
+ orderLine: model.id,
+ quantity: toIssue,
+ options: options
+ };
+ data.push(params);
+ callback();
+ }
- options = {
- success: function () {
- that.selectionChanged();
+ // Nothing to issue, move on
+ } else {
+ callback();
+ }
}
};
- this.fetch(options);
+ callback();
},
- popupHidden: function (inSender, inEvent) {
- if (!this._popupDone) {
- inEvent.originator.show();
- }
- },
- processingChanged: function (inSender, inEvent) {
- if (inEvent.isProcessing) {
- this.spinnerShow();
- } else {
- this.requery();
- this.spinnerHide();
- }
+ issueAll: function () {
+ var models = this.getValue().models;
+ this.issue(models);
},
- /**
- Overload: Piggy back on existing handler for `onParameterChanged event`
- by forwarding this requery to `parameterChanged`.
- */
- requery: function (inSender, inEvent) {
- this.parameterChanged(inSender, inEvent);
- return true;
+ issueLine: function () {
+ var models = this.selectedModels();
+ this.issue(models);
},
- /**
- Whenever a user makes a selection, rebuild the menu
- and set the transaction date on the selected models
- to match what has been selected here.
- */
- selectionChanged: function () {
- this.transactionDateChanged();
- this.buildMenu();
+ issueItem: function () {
+ var models = this.selectedModels();
+ this.issue(models, true, true);
},
- /**
- @param {Object} Options
- @param {String} [options.list] Class name
- */
- setList: function (options) {
- var component,
- list = options.list;
+ returnItem: function () {
+ var models = this.selectedModels(),
+ that = this,
+ data = [],
+ options = {},
+ qtyIssued,
+ model,
+ i,
+ transModule = that.getTransModule();
- component = this.createComponent({
- name: "list",
- container: this.$.contentPanels,
- kind: list,
- fit: true
- });
- this.$.rightLabel.setContent(component.label);
- if (component) {
- this.createComponent({
- name: "parameterWidget",
- classes: "xv-groupbox xv-parameter",
- showSaveFilter: false,
- showLayout: false,
- defaultParameters: null,
- container: this.$.parameterScroller,
- kind: component.getParameterWidget(),
- memoizeEnabled: false,
- fit: true
- });
+ for (i = 0; i < models.length; i++) {
+ model = models[i];
+ qtyIssued = model.get("qtyIssued");
+
+ // See if there's anything to issue here
+ if (qtyIssued) {
+ data.push(model.id);
+ }
}
- this.init = true;
- this.render();
- },
- spinnerHide: function () {
- this._popupDone = true;
- this.$.spinnerPopup.hide();
- },
- spinnerShow: function () {
- this._popupDone = false;
- this.$.spinnerPopup.show();
+ if (data.length) {
+ that.doProcessingChanged({isProcessing: true});
+ options.success = function () {
+ that.doProcessingChanged({isProcessing: false});
+ };
+ transModule.returnItem(data, options);
+ }
},
- transactionDateChanged: function () {
- var transDate = this.getTransactionDate(),
- collection = this.$.list.getValue(),
- i;
-
- // Update the transaction dates on all models to match
- // What has been selected
- for (i = 0; i < collection.length; i++) {
- collection.at(i).transactionDate = transDate;
+ selectedModels: function () {
+ var collection = this.getValue(),
+ models = [],
+ selected,
+ prop;
+ if (collection.length) {
+ selected = this.getSelection().selected;
+ for (prop in selected) {
+ if (selected.hasOwnProperty(prop)) {
+ models.push(this.getModel(prop - 0));
+ }
+ }
}
+ return models;
}
- };
-
- enyo.mixin(transactionList, XV.ListMenuManagerMixin);
- enyo.kind(transactionList);
+ });
}());
+
--- /dev/null
+/*jshint bitwise:true, indent:2, curly:true, eqeqeq:true, immed:true,
+latedef:true, newcap:true, noarg:true, regexp:true, undef:true,
+trailing:true, white:true, strict:false*/
+/*global XT:true, XM:true, XV:true, _:true, enyo:true */
+
+(function () {
+
+ /**
+ Expected to a have a parameter widget that contains an order and
+ a transaction date.
+
+ @name XV.TransactionListContainer
+ @extends XV.SearchContainer
+ */
+ var transactionListContainer = /** @lends XV.TransactionListContainer# */ {
+ name: "XV.TransactionListContainer",
+ kind: "Panels",
+ classes: "app enyo-unselectable",
+ arrangerKind: "CollapsingArranger",
+ published: {
+ prerequisite: "",
+ notifyMessage: "",
+ list: null,
+ actions: null,
+ transactionDate: null,
+ model: null
+ },
+ events: {
+ onPrevious: "",
+ onWorkspace: ""
+ },
+ handlers: {
+ onListItemMenuTap: "showListItemMenu",
+ onParameterChange: "requery",
+ onProcessingChanged: "processingChanged",
+ onSelectionChanged: "selectionChanged"
+ },
+ init: false,
+ components: [
+ {name: "parameterPanel", kind: "FittableRows", classes: "left",
+ components: [
+ {kind: "onyx.Toolbar", classes: "onyx-menu-toolbar", components: [
+ {kind: "onyx.Button", name: "backButton", content: "_back".loc(), ontap: "close"},
+ {kind: "onyx.MenuDecorator", style: "margin: 0;",
+ onSelect: "actionSelected", components: [
+ {kind: "XV.IconButton", src: "/assets/menu-icon-gear.png",
+ content: "_actions".loc(), name: "actionButton"},
+ {kind: "onyx.Menu", name: "actionMenu"}
+ ]}
+ ]},
+ {kind: "Scroller", name: "parameterScroller"}
+ ]},
+ {name: "listPanel", kind: "FittableRows", components: [
+ // the onyx-menu-toolbar class keeps the popups from being hidden
+ {kind: "onyx.MoreToolbar", name: "contentToolbar",
+ classes: "onyx-menu-toolbar", movedClass: "xv-toolbar-moved", components: [
+ {kind: "onyx.Grabber", classes: "left-float"},
+ {name: "rightLabel", content: "_search".loc(), classes: "left-float"},
+ {name: "space", fit: true},
+ {kind: "onyx.Button", name: "printButton", showing: false,
+ content: "_print".loc(), onclick: "print"},
+ {kind: "onyx.Button", name: "refreshButton", disabled: false,
+ content: "_refresh".loc(), onclick: "requery"},
+ {kind: "onyx.Button", name: "postButton",
+ disabled: true, classes: "save", showing: false,
+ content: "_post".loc(), onclick: "post"},
+ {name: "listItemMenu", kind: "onyx.Menu", floating: true,
+ onSelect: "listActionSelected", maxHeight: 500}
+ ]},
+ {name: "contentPanels", kind: "Panels", margin: 0, fit: true, draggable: false,
+ panelCount: 0},
+ {kind: "onyx.Popup", name: "spinnerPopup", centered: true,
+ modal: true, floating: true, scrim: true,
+ onHide: "popupHidden", components: [
+ {kind: "onyx.Spinner"},
+ {name: "spinnerMessage", content: "_processing".loc() + "..."}
+ ]}
+ ]}
+ ],
+ actionSelected: function (inSender, inEvent) {
+ var action = inEvent.originator.action,
+ method = action.method || action.name;
+
+ this[method](inSender, inEvent);
+ },
+ close: function () {
+ this.doPrevious();
+ },
+ buildMenu: function () {
+ var actionMenu = this.$.actionMenu,
+ actions = this.getActions().slice(0),
+ that = this;
+
+ // reset the menu
+ actionMenu.destroyClientControls();
+
+ // then add whatever actions are applicable
+ _.each(actions, function (action) {
+ var name = action.name,
+ prerequisite = action.prerequisite,
+ isDisabled = prerequisite ? !that[prerequisite]() : false;
+ actionMenu.createComponent({
+ name: name,
+ kind: XV.MenuItem,
+ content: action.label || ("_" + name).loc(),
+ action: action,
+ disabled: isDisabled
+ });
+
+ });
+ actionMenu.render();
+ this.$.actionButton.setShowing(actions.length);
+ },
+ create: function () {
+ this.inherited(arguments);
+ var disabled = !XT.session.privileges.get("AlterTransactionDates"),
+ parameterWidget;
+ this.setList({list: this.getList()});
+ parameterWidget = this.$.parameterWidget;
+ parameterWidget.$.transactionDate.$.input.setDisabled(disabled);
+ if (!this.getActions()) {
+ this.setActions([]);
+ }
+ this.buildMenu();
+ },
+ fetch: function (options) {
+ if (!this.init) { return; }
+ options = options ? _.clone(options) : {};
+ var list = this.$.list,
+ query,
+ parameterWidget,
+ parameters;
+ if (!list) { return; }
+ query = list.getQuery() || {};
+ parameterWidget = this.$.parameterWidget;
+ parameters = parameterWidget && parameterWidget.getParameters ?
+ parameterWidget.getParameters() : [];
+ options.showMore = _.isBoolean(options.showMore) ?
+ options.showMore : false;
+
+ // Build conditions
+ if (parameters.length) {
+ query.parameters = parameters;
+ } else {
+ delete query.parameters;
+ }
+ list.setQuery(query);
+ list.fetch(options);
+ },
+ /**
+ Capture order changed and transaction date changed events.
+ Depends on a very specific implementation of parameter widget
+ that includes `order` and `transactionDate` parameters.
+ */
+ parameterChanged: function (inSender, inEvent) {
+ var originator = inEvent ? inEvent.originator : false,
+ name = originator ? originator.name : false,
+ that = this,
+ options,
+ value;
+
+ if (name === "transactionDate") {
+ value = originator.$.input.getValue();
+ value = XT.date.applyTimezoneOffset(value, true);
+ value = XT.date.toMidnight(value);
+ this.setTransactionDate(value);
+ this.buildMenu();
+ return;
+ } else if (name === "order") {
+ value = originator.getParameter().value;
+ this.setModel(value);
+ this.buildMenu();
+ } else if (name === "shipment") {
+ return;
+ }
+
+ options = {
+ success: function () {
+ that.selectionChanged();
+ }
+ };
+ this.fetch(options);
+ },
+ popupHidden: function (inSender, inEvent) {
+ if (!this._popupDone) {
+ inEvent.originator.show();
+ }
+ },
+ processingChanged: function (inSender, inEvent) {
+ if (inEvent.isProcessing) {
+ this.spinnerShow();
+ } else {
+ this.requery();
+ this.spinnerHide();
+ }
+ },
+ /**
+ Overload: Piggy back on existing handler for `onParameterChanged event`
+ by forwarding this requery to `parameterChanged`.
+ */
+ requery: function (inSender, inEvent) {
+ this.parameterChanged(inSender, inEvent);
+ return true;
+ },
+ /**
+ Whenever a user makes a selection, rebuild the menu
+ and set the transaction date on the selected models
+ to match what has been selected here.
+ */
+ selectionChanged: function () {
+ this.transactionDateChanged();
+ this.buildMenu();
+ },
+ /**
+ @param {Object} Options
+ @param {String} [options.list] Class name
+ */
+ setList: function (options) {
+ var component,
+ list = options.list;
+
+ component = this.createComponent({
+ name: "list",
+ container: this.$.contentPanels,
+ kind: list,
+ fit: true
+ });
+ this.$.rightLabel.setContent(component.label);
+ if (component) {
+ this.createComponent({
+ name: "parameterWidget",
+ classes: "xv-groupbox xv-parameter",
+ showSaveFilter: false,
+ showLayout: false,
+ defaultParameters: null,
+ container: this.$.parameterScroller,
+ kind: component.getParameterWidget(),
+ memoizeEnabled: false,
+ fit: true
+ });
+ }
+
+ this.init = true;
+ this.render();
+ },
+ spinnerHide: function () {
+ this._popupDone = true;
+ this.$.spinnerPopup.hide();
+ },
+ spinnerShow: function () {
+ this._popupDone = false;
+ this.$.spinnerPopup.show();
+ },
+ transactionDateChanged: function () {
+ var transDate = this.getTransactionDate(),
+ collection = this.$.list.getValue(),
+ i;
+
+ // Update the transaction dates on all models to match
+ // What has been selected
+ for (i = 0; i < collection.length; i++) {
+ collection.at(i).transactionDate = transDate;
+ }
+ }
+ };
+
+ enyo.mixin(transactionListContainer, XV.ListMenuManagerMixin);
+ enyo.kind(transactionListContainer);
+
+}());
/*jshint indent:2, curly:true, eqeqeq:true, immed:true, latedef:true, newcap:true, noarg:true,
-regexp:true, undef:true, trailing:true, white:true */
-/*global XT:true, XM:true, enyo:true, _:true */
+regexp:true, undef:true, trailing:true, white:true, strict:false */
+/*global XT:true, enyo:true, _:true */
(function () {
models = this.filteredList(options),
none = this.getNoneText(),
classes = this.getNoneClasses(),
- component,
- name,
- model,
- i;
+ picker = this.$.picker,
+ iconClass = this.iconClass,
+ iconVisible = this.iconVisible,
+ component;
+
this.$.picker.destroyClientControls();
if (this.showNone) {
this.$.picker.createComponent({
classes: classes
});
}
- for (i = 0; i < models.length; i++) {
- model = models[i];
- name = model.getValue ? model.getValue(nameAttribute) : model.get(nameAttribute);
+
+ _.each(models, function (model) {
+ var name = model.getValue ? model.getValue(nameAttribute) : model.get(nameAttribute),
+ isActive = model && model.getValue ? model.getValue("isActive") !== false : true;
+
component = {
kind: "XV.PickerMenuItem",
value: model,
+ disabled: !isActive,
content: name,
- iconClass: this.iconClass,
- iconVisible: this.iconVisible
+ iconClass: iconClass,
+ iconVisible: iconVisible
};
- this.$.picker.createComponent(component);
- }
+
+ picker.createComponent(component);
+ });
+
// this is for an Enyo Bug relating
// to pickers inside of a popup
if (this.prerender) {
value: null,
published: {
iconClass: "",
- iconVisible: null
+ iconVisible: null,
+ disabled: false
},
components: [
{name: "text", content: ""},
this.inherited(arguments);
this.contentChanged();
this.iconVisibleChanged();
+ this.disabledChanged();
},
/**
When the content is changed on the parent MenuItem,
contentChanged: function () {
this.$.text.setContent(this.getContent());
},
+ disabledChanged: function () {
+ this.addRemoveClass("disabled", this.disabled);
+ },
/**
If there is an icon class, we determine if it is
showing based on the iconVisible logic.
this.$.icon.removeClass(this.getIconClass());
this.$.text.removeClass("picker-content");
}
+ },
+ tap: function (inSender) {
+ if (!this.disabled) { return this.inherited(arguments); }
}
});
"author": "xTuple <dev@xtuple.com>",
"name": "xtuple",
"description": "xTuple Enterprise Resource Planning Mobile-Web client",
- "version": "1.4.5",
+ "version": "1.4.6",
"repository": {
"type": "git",
"url": "https://github.com/xtuple/xtuple.git"
Pentaho Report Designer. The business intelligence server provides the reports engine to\r
serve reports.\r
\r
-Get the Reports \r
+Get the Reports\r
--------------- \r
Reports are located in the xtuple/xtuple repo: \r
\r
export BISERVER_HOME= ~/ErpBI-x.x.x\r
cd xtuple/pentaho/datasource\r
./build.sh\r
+ \r
+Create Server SSL Keys\r
+----------------------\r
+A self-signed SSL certificate is provided with a common name of "localhost". This\r
+is useful for private test systems, but public systems should use a signed certificate. If you use\r
+a self-signed certificate note that you may need to click on the shield in the top right corner of\r
+Google Chrome when using BI facilities.\r
+\r
+To use a signed certificate copy the certificate and key to the following folder:\r
+\r
+ ErpBI-x.x.x/biserver-ce/ssl-keys\r
+ \r
+and run the following where server.crt and server.key are your signed certificate\r
+and key:\r
+\r
+ openssl pkcs12 -export -out server.pkcs12 -in server.crt -inkey server.key\r
+ keytool -importkeystore -srckeystore server.pkcs12 -srcstoretype PKCS12 -destkeystore server.jks -deststoretype JKS\r
+ keytool -export -alias 1 -file server.cer -storepass changeit -keystore server.jks\r
+ keytool -import -alias 1 -v -trustcacerts -file server.cer -keypass changeit -storepass changeit -keystore cacerts.jks\r
\r
Start Server\r
------------\r
----------------------------\r
Edit xtuple/node-datasource/config.js defining the server URL. For example:\r
\r
- biUrl: "http://192.168.56.101:8080/pentaho/content/reporting/reportviewer/report.html?solution=xtuple&path=%2Fprpt&locale=en_US&userid=reports&password=password&output-target=pageable/pdf"\r
+ biUrl: "https://localhost:8443/pentaho/content/reporting/reportviewer/report.html?solution=xtuple&path=%2Fprpt&locale=en_US&userid=reports&password=password&output-target=pageable/pdf"\r
\r
Also, Mobile App SSL keys must have a Common Name. Make sure the common name is your URL \r
or IP address when you run openssl req. \r
log "Created salt"
openssl genrsa -des3 -out server.key -passout pass:xtuple 1024 2>1 | tee -a $LOG_FILE
openssl rsa -in server.key -passin pass:xtuple -out key.pem -passout pass:xtuple 2>1 | tee -a $LOG_FILE
- openssl req -batch -new -key key.pem -out server.csr 2>1 | tee -a $LOG_FILE
+ openssl req -batch -new -key key.pem -out server.csr -subj '/CN=localhost' 2>1 | tee -a $LOG_FILE
openssl x509 -req -days 365 -in server.csr -signkey key.pem -out server.crt 2>1 | tee -a $LOG_FILE
if [ $? -ne 0 ]
then
--- /dev/null
+/*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, XM:true, XV:true, describe:true, it:true,
+ before:true, module:true, require:true */
+
+(function () {
+ "use strict";
+
+ var _ = require("underscore"),
+ zombieAuth = require("../lib/zombie_auth"),
+ smoke = require("../lib/smoke"),
+ crud = require("../lib/crud"),
+ modelData = require("../lib/model_data"),
+ assert = require("chai").assert;
+
+ describe('Issue to Shipping Transaction', function () {
+ this.timeout(50 * 1000);
+ before(function (done) {
+ this.timeout(30 * 1000);
+ zombieAuth.loadApp(done);
+ });
+
+ var salesOrderData = modelData.salesOrderData;
+ salesOrderData.skipDelete = true;
+ crud.runAllCrud(modelData.salesOrderData);
+
+ it('User navigates to Issue to Shipping, selects the first line item, issue stock', function (done) {
+ smoke.navigateToList(XT.app, "XV.ShipmentList");
+ XT.app.$.postbooks.issueToShipping();
+ var transactionList = XT.app.$.postbooks.getActive();
+ assert.equal(transactionList.kind, "XV.IssueToShipping");
+
+
+ //Enter the order number in the order input widget
+ var orderNumber = salesOrderData.model.id;
+ transactionList.$.parameterWidget.$.order.setValue(orderNumber);
+
+ var list = XT.app.$.postbooks.getActive().$.list;
+ assert.equal(list, "XV.IssueToShippingList");
+
+ setTimeout(function () {
+ assert.equal(transactionList.model.id, orderNumber);
+ assert.isDefined(list.value.models);
+ //Select the first line item from list
+ list.select(0);
+ assert.equal(list.selectedIndexes(), "0");
+ setTimeout(function () {
+ var myOriginantor = list.$.listItem;
+ var myModel = list.value.models[0];
+ var myEvent = {originantor: myOriginantor, model: myModel};
+ //Click the gear, Issue Stock
+ list.issueStock(myEvent);
+ setTimeout(function () {
+ var workspace = XT.app.$.postbooks.getActive().$.workspace;
+ assert.equal(workspace.kind, "XV.IssueStockWorkspace");
+ assert.equal(workspace.value.getStatusString(), "READY_DIRTY");
+ assert.equal(workspace.value.get("lineNumber"), "1");
+ //Enter Qty 2
+ smoke.setWorkspaceAttributes(workspace, {toIssue: "7"}); //workspace.$.toIssue.doValueChange({value: 7})
+ setTimeout(function () {
+ assert.equal(workspace.value.get("toIssue"), "7");
+ //Save
+ XT.app.$.postbooks.getActive().save({requery: false});
+ XT.app.$.postbooks.getActive().close();
+ setTimeout(function () {
+ //assert.equal(workspace.value.getStatusString(), "READY_DIRTY");
+ assert.equal(XT.app.$.postbooks.getActive().kind, "XV.IssueToShipping");
+ /*
+ //Ship
+ XT.app.$.postbooks.getActive().post();
+ setTimeout(function () {
+ var workspace = XT.app.$.postbooks.getActive().$.workspace;
+ assert.equal(workspace.kind, "XV.ShipShipmentWorkspace");
+ //Ship
+ workspace.save({requery: false});
+ assert.equal(XT.app.$.postbooks.getActive().kind, "XV.IssueToShipping");*/
+ done();
+ //}, 3000);
+ }, 5000);
+ }, 5000);
+ }, 3000);
+ }, 3000);
+ }, 3000);
+
+ });
+ });
+}());