Merge pull request #1770 from xtuple/4_6_x
[xtuple] / enyo-client / application / source / views / workspace.js
1 /*jshint bitwise:false, indent:2, curly:true, eqeqeq:true, immed:true, latedef:true,
2 newcap:true, noarg:true, regexp:true, undef:true, trailing:true, white:true,
3 strict: false*/
4 /*global XV:true, XM:true, _:true, enyo:true, XT:true, onyx:true, window:true */
5
6 (function () {
7
8   var hash;
9
10   /**
11     Used to notify change of account to contact widget if both exist on
12     the same workspace.
13   */
14   XV.accountNotifyContactMixin = {
15     accountChanged: function () {
16       var account = this.$.accountWidget.getValue();
17       if (account) {
18         this.$.contactWidget.addParameter({
19           attribute: ["account", "accountParent"],
20           value: account.id
21         }, true);
22       } else {
23         this.$.contactWidget.removeParameter("account");
24       }
25     },
26     attributesChanged: function (inSender, inEvent) {
27       this.inherited(arguments);
28       this.accountChanged();
29     },
30     controlValueChanged: function (inSender, inEvent) {
31       this.inherited(arguments);
32       if (inEvent.originator.name === 'accountWidget') {
33         this.accountChanged();
34       }
35     }
36   };
37
38   /**
39     Handles Address change with prompts.
40   */
41   XV.WorkspaceAddressMixin = {
42     accountChanged: function () {
43       var account = this.getAccount();
44       this.$.addressWidget.setAccount(account);
45     },
46     attributesChanged: function (inSender, inEvent) {
47       this.inherited(arguments);
48       this.accountChanged();
49     },
50     controlValueChanged: function (inSender, inEvent) {
51       this.inherited(arguments);
52       if (inEvent.originator.name === 'accountWidget' ||
53           inEvent.originator.name === 'customerWidget') {
54         this.accountChanged();
55       }
56     },
57     getAccount: function () {
58       var model = this.getValue();
59       return model ? (model.get('account') || model.get('customer')) : undefined;
60     }
61   };
62
63   /**
64     Abstract workspace to be used for objects that are attached to models subclassed
65       from `AccountDocument`.
66     Must be subclassed.
67   */
68   enyo.kind({
69     name: "XV.AccountDocumentWorkspace",
70     kind: "XV.Workspace",
71     handlers: {
72       onError: "errorNotify"
73     },
74     published: {
75       // The natural key is the number, not the UUID
76       existingNumber: ""
77     },
78     accountConvert: function (inEvent) {
79       this.value.convertFromAccount(this.existingNumber);
80       this._popupDone = true;
81       this.$.findExistingAccountPopup.hide();
82     },
83     errorNotify: function (inSender, inEvent) {
84       // Handle existing
85       if (inEvent.error.code === 'xt1008') {
86         this.existingNumber = inEvent.error.params.response.id;
87         this._popupDone = false;
88         this.$.findExistingAccountPopup.show();
89         return true;
90       }
91     },
92     accountCancel: function () {
93       this._popupDone = true;
94       this.$.findExistingAccountPopup.hide();
95     },
96     popupHidden: function () {
97       if (!this._popupDone) {
98         this.$.findExistingAccountPopup.show();
99         return true;
100       }
101     }
102   });
103
104   // ..........................................................
105   // EMAIL PROFILE
106   //
107
108   /**
109     An abstract workspace for email profiles.
110   */
111   enyo.kind({
112     name: "XV.EmailProfileWorkspace",
113     kind: "XV.Workspace",
114     headerAttrs: ["name", "-", "description"],
115     components: [
116       {kind: "Panels", arrangerKind: "CarouselArranger",
117         fit: true, components: [
118         {kind: "XV.Groupbox", name: "mainPanel", components: [
119           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
120           {kind: "XV.ScrollableGroupbox", name: "mainGroup", fit: true,
121             classes: "in-panel", components: [
122             {kind: "XV.InputWidget", attr: "name"},
123             {kind: "XV.InputWidget", attr: "description"},
124             {kind: "onyx.GroupboxHeader", content: "_header".loc()},
125             {kind: "XV.InputWidget", attr: "from"},
126             {kind: "XV.InputWidget", attr: "replyTo"},
127             {kind: "XV.InputWidget", attr: "to"},
128             {kind: "XV.InputWidget", attr: "cc"},
129             {kind: "XV.InputWidget", attr: "bcc"},
130             {kind: "XV.InputWidget", attr: "subject"},
131             {kind: "onyx.GroupboxHeader", content: "_body".loc()},
132             {kind: "XV.TextArea", attr: "body", classes: "max-height"}
133           ]}
134         ]}
135       ]}
136     ]
137   });
138
139   // ..........................................................
140   // BASE CLASS
141   //
142
143   enyo.kind({
144     name: "XV.OrderedReferenceWorkspace",
145     kind: "XV.Workspace",
146     components: [
147       {kind: "Panels", arrangerKind: "CarouselArranger",
148         fit: true, components: [
149         {kind: "XV.Groupbox", name: "mainPanel", components: [
150           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
151           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
152             classes: "in-panel", components: [
153             {kind: "XV.InputWidget", attr: "name"},
154             {kind: "XV.InputWidget", attr: "description"},
155             // these order fields are integers, so setting a maxlength
156             // to prevent exceeding integer's max value
157             {kind: "XV.NumberWidget", attr: "order", maxlength: 9, formatting: false}
158           ]}
159         ]}
160       ]}
161     ]
162   });
163
164   // ..........................................................
165   // ACCOUNT
166   //
167
168   enyo.kind({
169     name: "XV.AccountWorkspace",
170     kind: "XV.Workspace",
171     title: "_account".loc(),
172     headerAttrs: ["number", "-", "name"],
173     model: "XM.Account",
174     handlers: {
175       onSavePrompt: "savePrompt"
176     },
177     components: [
178       {kind: "Panels", arrangerKind: "CarouselArranger",
179         fit: true, components: [
180         {kind: "XV.Groupbox", name: "mainPanel", components: [
181           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
182           {kind: "XV.ScrollableGroupbox", name: "mainGroup", fit: true,
183             classes: "in-panel", components: [
184             {kind: "XV.InputWidget", attr: "number"},
185             {kind: "XV.CheckboxWidget", attr: "isActive"},
186             {kind: "XV.InputWidget", attr: "name"},
187             {kind: "XV.AccountTypePicker", attr: "accountType"},
188             {kind: "XV.AccountWidget", attr: "parent", label: "_parent".loc()},
189             {kind: "XV.UserAccountWidget", attr: "owner"},
190             {kind: "onyx.GroupboxHeader", content: "_primaryContact".loc()},
191             {kind: "XV.ContactWidget", attr: "primaryContact",
192               showAddress: true, label: "_name".loc()},
193             {kind: "onyx.GroupboxHeader", content: "_secondaryContact".loc()},
194             {kind: "XV.ContactWidget", attr: "secondaryContact",
195               showAddress: true, label: "_name".loc()},
196             {kind: "XV.AccountCharacteristicsWidget", attr: "characteristics"},
197             {kind: "onyx.GroupboxHeader", content: "_notes".loc()},
198             {kind: "XV.TextArea", attr: "notes", fit: true}
199           ]}
200         ]},
201         {kind: "XV.AccountCommentBox", attr: "comments"},
202         {kind: "XV.Groupbox", name: "rolesPanel", title: "_roles".loc(),
203           components: [
204           {kind: "onyx.GroupboxHeader", content: "_roles".loc()},
205           {kind: "XV.ScrollableGroupbox", name: "rolesGroup", fit: true,
206             classes: "in-panel", components: []
207           }
208         ]},
209         {kind: "XV.AccountDocumentsBox", attr: "documents"},
210         {kind: "XV.AccountContactsBox", attr: "contactRelations"}
211       ]},
212       {kind: "onyx.Popup", name: "savePromptPopup", centered: true,
213         modal: true, floating: true, scrim: true,
214         onHide: "popupHidden", components: [
215         {content: "_mustSave".loc() },
216         {content: "_saveYourWork?".loc() },
217         {tag: "br"},
218         {kind: "onyx.Button", content: "_cancel".loc(), ontap: "savePromptCancel",
219           classes: "xv-popup-button"},
220         {kind: "onyx.Button", content: "_save".loc(), ontap: "savePromptSave",
221           classes: "onyx-blue xv-popup-button"}
222       ]}
223     ],
224     create: function () {
225       this.inherited(arguments);
226       var K = XM.Account.prototype,
227         roles = K.roleAttributes.sort(),
228         that = this;
229
230       // Loop and add a role checkbox for each role attribute found on the model
231       _.each(roles, function (role) {
232         that.createComponent({
233           kind: XV.AccountRoleCheckboxWidget,
234           name: role + "Control",
235           label: ("_" + role).loc(),
236           attr: role,
237           container: that.$.rolesGroup,
238           owner: that
239         });
240       });
241
242     },
243     savePrompt: function (inSender, inEvent) {
244       this._popupDone = false;
245       this._inEvent = inEvent;
246       this.$.savePromptPopup.show();
247     },
248     savePromptCancel: function () {
249       this._popupDone = true;
250       this._inEvent.callback(false);
251       this.$.savePromptPopup.hide();
252     },
253     savePromptSave: function () {
254       var that = this,
255         options = {};
256       options.success = function () {
257         that._inEvent.callback(true);
258       };
259       this._popupDone = true;
260       this.$.savePromptPopup.hide();
261       this.save(options);
262     }
263   });
264
265   XV.registerModelWorkspace("XM.AccountRelation", "XV.AccountWorkspace");
266   XV.registerModelWorkspace("XM.AccountListItem", "XV.AccountWorkspace");
267
268   // ..........................................................
269   // BANK ACCOUNT
270   //
271
272   enyo.kind({
273     name: "XV.BankAccountWorkspace",
274     kind: "XV.Workspace",
275     title: "_bankAccount".loc(),
276     model: "XM.BankAccount",
277     components: [
278       {kind: "Panels", arrangerKind: "CarouselArranger",
279         fit: true, components: [
280         {kind: "XV.Groupbox", name: "mainPanel", components: [
281           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
282           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
283             classes: "in-panel", fit: true, components: [
284             {kind: "XV.InputWidget", attr: "name"},
285             {kind: "XV.InputWidget", attr: "description"},
286             {kind: "XV.InputWidget", attr: "bankName"},
287             {kind: "XV.InputWidget", attr: "accountNumber"},
288             {kind: "XV.BankAccountTypePicker", attr: "bankAccountType"},
289             {kind: "XV.CurrencyPicker", attr: "currency"},
290             {kind: "XV.CheckboxWidget", attr: "isUsedByBilling"},
291             {kind: "XV.CheckboxWidget", attr: "isUsedByPayments"},
292             {kind: "onyx.GroupboxHeader", content: "_notes".loc()},
293             {kind: "XV.TextArea", attr: "notes"}
294           ]}
295         ]}
296       ]}
297     ]
298   });
299
300   XV.registerModelWorkspace("XM.BankAccountRelation", "XV.BankAccountWorkspace");
301
302   // ..........................................................
303   // CHARACTERISTIC
304   //
305
306   enyo.kind({
307     name: "XV.CharacteristicWorkspace",
308     kind: "XV.Workspace",
309     title: "_characteristic".loc(),
310     model: "XM.Characteristic",
311     components: [
312       {kind: "Panels", arrangerKind: "CarouselArranger",
313         fit: true, components: [
314         {kind: "XV.Groupbox", name: "mainPanel", components: [
315           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
316           {kind: "XV.ScrollableGroupbox", name: "mainGroup", fit: true,
317             classes: "in-panel", components: [
318             {kind: "XV.InputWidget", attr: "name"},
319             {kind: "XV.CharacteristicTypePicker", name: "typePicker", attr: "characteristicType"},
320             {kind: "XV.CheckboxWidget", attr: "isSearchable"},
321             {kind: "onyx.GroupboxHeader", content: "_notes".loc()},
322             {kind: "XV.TextArea", attr: "notes", fit: true, name: "notesHeader"},
323             {name: "advancedPanel", showing: false, components: [
324               {kind: "onyx.GroupboxHeader", content: "_advanced".loc()},
325               {kind: "XV.InputWidget", attr: "mask"},
326               {kind: "XV.InputWidget", attr: "validator"}
327             ]}
328           ]}
329         ]},
330         {kind: "XV.Groupbox", name: "rolesPanel", title: "_roles".loc(),
331           components: [
332           {kind: "onyx.GroupboxHeader", content: "_roles".loc()},
333           {kind: "XV.ScrollableGroupbox", name: "rolesGroup", fit: true,
334             classes: "in-panel", components: [
335             {kind: "XV.ToggleButtonWidget", attr: "isAccounts", label: "_accounts".loc()},
336             {kind: "XV.ToggleButtonWidget", attr: "isAddresses", label: "_addresses".loc()},
337             {kind: "XV.ToggleButtonWidget", attr: "isContacts", label: "_contacts".loc()},
338             {kind: "XV.ToggleButtonWidget", attr: "isCustomers", label: "_customers".loc()},
339             {kind: "XV.ToggleButtonWidget", attr: "isEmployees", label: "_employees".loc()},
340             {kind: "XV.ToggleButtonWidget", attr: "isIncidents", label: "_incidents".loc()},
341             {kind: "XV.ToggleButtonWidget", attr: "isInvoices", label: "_invoices".loc()},
342             {kind: "XV.ToggleButtonWidget", attr: "isItems", label: "_items".loc()},
343             {kind: "XV.ToggleButtonWidget", attr: "isOpportunities", label: "_opportunities".loc()},
344             {kind: "XV.ToggleButtonWidget", attr: "isSalesOrders", label: "_salesOrders".loc()},
345           ]}
346         ]},
347         {kind: "XV.CharacteristicOptionBox", name: "optionsPanel",
348           attr: "options", showing: false}
349       ]}
350     ],
351     /**
352       After the controls are updated by the model, determine visibility of panels.
353      */
354     attributesChanged: function (model, options) {
355       this.inherited(arguments);
356       if (this.getValue().getStatus() === XM.Model.READY_CLEAN ||
357         this.getValue().getStatus() === XM.Model.READY_NEW) {
358         this.typeValueChanged(model);
359       }
360     },
361
362     /**
363       Function to determine visibility of "advanced" and "options" panels based
364         on the characteristicType
365      */
366     typeValueChanged: function (model) {
367       var type = model ? model.get('characteristicType') : null;
368       var isText = type === XM.Characteristic.TEXT;
369       var isList = type === XM.Characteristic.LIST;
370       this.$.advancedPanel.setShowing(isText);
371       this.$.optionsPanel.setShowing(isList);
372       if (isList) {
373         this.$.optionsPanel.render();
374       } else if (isText) {
375         this.$.advancedPanel.render();
376       }
377       // signal to workspace container that the menu needs to re-render
378       this.doMenuChange();
379     }
380   });
381
382   XV.registerModelWorkspace("XM.Characteristic", "XV.CharacteristicWorkspace");
383
384   // ..........................................................
385   // CLASS CODE
386   //
387
388   enyo.kind({
389     name: "XV.ClassCodeWorkspace",
390     kind: "XV.Workspace",
391     title: "_classCode".loc(),
392     model: "XM.ClassCode",
393     components: [
394       {kind: "Panels", arrangerKind: "CarouselArranger",
395         fit: true, components: [
396         {kind: "XV.Groupbox", name: "mainPanel", components: [
397           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
398           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
399             classes: "in-panel", components: [
400             {kind: "XV.InputWidget", attr: "code"},
401             {kind: "XV.InputWidget", attr: "description"}
402           ]}
403         ]}
404       ]}
405     ]
406   });
407
408   XV.registerModelWorkspace("XM.ClassCode", "XV.ClassCodeWorkspace");
409
410   // ..........................................................
411   // CONFIGURE
412   //
413
414   enyo.kind({
415     name: "XV.DatabaseInformationWorkspace",
416     kind: "XV.Workspace",
417     title: "_database".loc() + " " + "_information".loc(),
418     model: "XM.DatabaseInformation",
419     components: [
420       {kind: "Panels", arrangerKind: "CarouselArranger",
421         fit: true, components: [
422         {kind: "XV.Groupbox", name: "mainPanel", components: [
423           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
424           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
425             classes: "in-panel", components: [
426             {kind: "XV.InputWidget", attr: "DatabaseName",
427               label: "_name".loc()},
428             {kind: "XV.InputWidget", attr: "ServerVersion",
429                 label: "_version".loc()},
430             {kind: "onyx.GroupboxHeader", content: "_notes".loc()},
431             {kind: "XV.TextArea", attr: "DatabaseComments"}
432           ]}
433         ]},
434         {kind: "XV.Groupbox",
435           title: "_commandCenter".loc(), name: "commandPanel", components: [
436           {kind: "XV.ScrollableGroupbox",
437             classes: "in-panel", components: [
438             {kind: "onyx.GroupboxHeader", content: "_installExtension".loc()},
439             {kind: "XV.InputWidget", name: "extensionName", label: "_extensionName".loc()},
440             {kind: "FittableColumns", classes: "xv-buttons center", components: [
441               {kind: "onyx.Button", name: "extensionButton", classes: "icon-ok", ontap: "installExtension"},
442             ]},
443           ]}
444         ]}
445       ]}
446     ],
447     create: function () {
448       this.inherited(arguments);
449       var hasPriv = XT.session.privileges.get("InstallExtension");
450       this.$.extensionName.setDisabled(!hasPriv);
451       this.$.extensionButton.setDisabled(!hasPriv);
452     },
453     installExtension: function () {
454       var that = this,
455         callback = function (response) {
456           if (!response.answer) {
457             return;
458           }
459
460           XT.dataSource.callRoute("install-extension",
461             {
462               extensionName: that.$.extensionName.getValue()
463             },
464             {
465               success: function (message) {
466                 that.doNotify({message: message && message.loc()});
467               },
468               error: function (error) {
469                 that.doNotify({message: error.message ? error.message() : error});
470               }
471             }
472           );
473         };
474
475       if (!this.$.extensionName.getValue()) {
476         this.doNotify({
477           type: XM.Model.WARNING,
478           message: "_attributeIsRequired".loc().replace("{attr}", "_extensionName".loc())
479         });
480         return;
481       }
482
483       this.doNotify({
484         type: XM.Model.QUESTION,
485         message: "_installExtensionWarning".loc() + "_confirmAction".loc(),
486         callback: callback
487       });
488     }
489   });
490
491   enyo.kind({
492     name: "XV.SystemConfigurationWorkspace",
493     kind: "XV.Workspace",
494     title: "_systemConfiguration".loc(),
495     model: "XM.System",
496     components: [
497       {kind: "Panels", arrangerKind: "CarouselArranger",
498         fit: true, components: [
499         {kind: "XV.Groupbox", name: "mainPanel", title: "_creditCard".loc(),
500           components: [
501           {kind: "onyx.GroupboxHeader", content: "_default".loc()},
502           {kind: "XV.PriorityPicker", attr: "DefaultPriority",
503             label: "_priority".loc()},
504           {kind: "onyx.GroupboxHeader", content: "_creditCard".loc()},
505           {kind: "XV.ScrollableGroupbox", name: "mainGroup", classes: "in-panel", components: [
506             {kind: "XV.CreditCardGatewayCombobox", attr: "CCCompany",
507                 label: "_gateway".loc()},
508             {kind: "XV.InputWidget", attr: "CCLogin",
509               label: "_login".loc()},
510             {kind: "XV.InputWidget", attr: "CCPassword",
511                 label: "_transactionKey".loc()},
512             {kind: "XV.ToggleButtonWidget", attr: "CCTest",
513                 label: "_testMode".loc()},
514             {kind: "XV.ToggleButtonWidget", attr: "CCRequireCCV",
515                 label: "_requireCCV".loc()}
516           ]}
517         ]}
518       ]}
519     ]
520   });
521
522   enyo.kind({
523     name: "XV.UserPreferenceWorkspace",
524     kind: "XV.Workspace",
525     title: "_userPreferences".loc(),
526     model: "XM.UserPreference",
527     published: {
528       singletonModel: "XT.session.preferences"
529     },
530     components: [
531       {kind: "Panels", arrangerKind: "CarouselArranger",
532         fit: true, components: [
533         {kind: "XV.Groupbox", name: "mainPanel", components: [
534           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
535           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
536             classes: "in-panel", components: [
537           ]}
538         ]}
539       ]}
540     ]
541   });
542
543   // ..........................................................
544   // CONTACT
545   //
546
547   hash = {
548     name: "XV.ContactWorkspace",
549     kind: "XV.Workspace",
550     title: "_contact".loc(),
551     model: "XM.Contact",
552     headerAttrs: ["firstName", "lastName"],
553     handlers: {
554       onError: "errorNotify"
555     },
556     components: [
557       {kind: "Panels", arrangerKind: "CarouselArranger",
558         fit: true, components: [
559         {kind: "XV.Groupbox", name: "mainPanel", components: [
560           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
561           {kind: "XV.ScrollableGroupbox", name: "mainGroup", fit: true,
562             classes: "in-panel", components: [
563             {kind: "XV.InputWidget", attr: "number"},
564             {kind: "XV.CheckboxWidget", attr: "isActive"},
565             {kind: "onyx.GroupboxHeader", content: "_name".loc()},
566             {kind: "XV.HonorificCombobox", attr: "honorific"},
567             {kind: "XV.InputWidget", attr: "firstName"},
568             {kind: "XV.InputWidget", attr: "middleName"},
569             {kind: "XV.InputWidget", attr: "lastName"},
570             {kind: "XV.InputWidget", attr: "suffix"},
571             {kind: "onyx.GroupboxHeader", content: "_relationships".loc()},
572             {kind: "XV.UserAccountWidget", attr: "owner"},
573             {kind: "XV.AccountWidget", attr: "account"},
574             {kind: "onyx.GroupboxHeader", content: "_address".loc()},
575             {kind: "XV.AddressWidget", attr: "address"},
576             {kind: "onyx.GroupboxHeader", content: "_information".loc()},
577             {kind: "XV.InputWidget", attr: "jobTitle"},
578             {kind: "XV.ComboboxWidget", attr: "primaryEmail",
579               keyAttribute: "email"},
580             {kind: "XV.InputWidget", attr: "phone"},
581             {kind: "XV.InputWidget", attr: "alternate"},
582             {kind: "XV.InputWidget", attr: "fax"},
583             {kind: "XV.InputWidget", attr: "webAddress"},
584             {kind: "XV.ContactCharacteristicsWidget", attr: "characteristics"},
585             {kind: "onyx.GroupboxHeader", content: "_notes".loc()},
586             {kind: "XV.TextArea", attr: "notes"}
587           ]}
588         ]},
589         {kind: "XV.ContactCommentBox", attr: "comments"},
590         {kind: "XV.ContactDocumentsBox", attr: "documents"},
591         {kind: "XV.ContactEmailBox", attr: "email"}
592       ]}
593     ]
594   };
595   hash = enyo.mixin(hash, XV.WorkspaceAddressMixin);
596   enyo.kind(hash);
597
598   XV.registerModelWorkspace("XM.ContactRelation", "XV.ContactWorkspace");
599   XV.registerModelWorkspace("XM.ContactListItem", "XV.ContactWorkspace");
600
601   // ..........................................................
602   // COST CATEGORY
603   //
604
605   enyo.kind({
606     name: "XV.CostCategoryWorkspace",
607     kind: "XV.Workspace",
608     title: "_costCategory".loc(),
609     model: "XM.CostCategory",
610     components: [
611       {kind: "Panels", arrangerKind: "CarouselArranger",
612         fit: true, components: [
613         {kind: "XV.Groupbox", name: "mainPanel", components: [
614           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
615           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
616             classes: "in-panel", components: [
617             {kind: "XV.InputWidget", attr: "code"},
618             {kind: "XV.InputWidget", attr: "description"}
619           ]}
620         ]}
621       ]}
622     ]
623   });
624
625   XV.registerModelWorkspace("XM.CostCategory", "XV.CostCategoryWorkspace");
626
627
628   // ..........................................................
629   // COUNTRY
630   //
631
632   enyo.kind({
633     name: "XV.CountryWorkspace",
634     kind: "XV.Workspace",
635     title: "_country".loc(),
636     model: "XM.Country",
637     components: [
638       {kind: "Panels", arrangerKind: "CarouselArranger",
639         fit: true, components: [
640         {kind: "XV.Groupbox", name: "mainPanel", components: [
641           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
642           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
643             classes: "in-panel", components: [
644             {kind: "XV.InputWidget", attr: "abbreviation"},
645             {kind: "XV.InputWidget", attr: "name"},
646             {kind: "XV.InputWidget", attr: "currencyName"},
647             {kind: "XV.InputWidget", attr: "currencySymbol"},
648             {kind: "XV.InputWidget", attr: "currencyAbbreviation"},
649             {kind: "XV.InputWidget", attr: "currencyNumber"}
650           ]}
651         ]}
652       ]}
653     ]
654   });
655
656   XV.registerModelWorkspace("XM.Country", "XV.CountryWorkspace");
657
658   // ..........................................................
659   // CURRENCY
660   //
661
662   enyo.kind({
663     name: "XV.CurrencyWorkspace",
664     kind: "XV.Workspace",
665     title: "_currency".loc(),
666     model: "XM.Currency",
667     components: [
668       {kind: "Panels", arrangerKind: "CarouselArranger",
669         fit: true, components: [
670         {kind: "XV.Groupbox", name: "mainPanel", components: [
671           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
672           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
673             classes: "in-panel", components: [
674             {kind: "XV.InputWidget", attr: "abbreviation"},
675             {kind: "XV.InputWidget", attr: "name"},
676             {kind: "XV.InputWidget", attr: "symbol"},
677             {kind: "XV.CheckboxWidget", attr: "isBase", name: "isBase"}
678           ]}
679         ]}
680       ]}
681     ]
682   });
683
684   XV.registerModelWorkspace("XM.Currency", "XV.CurrencyWorkspace");
685
686   // ..........................................................
687   // CUSTOMER
688   //
689
690   enyo.kind({
691     name: "XV.CustomerWorkspace",
692     kind: "XV.Workspace",
693     title: "_customer".loc(),
694     model: "XM.Customer",
695     headerAttrs: ["number", "-", "name"],
696     handlers: {
697       onError: "errorNotify"
698     },
699     published: {
700       // The natural key is Number, not UUID
701       existingNumber: ""
702     },
703     components: [
704       {kind: "Panels", arrangerKind: "CarouselArranger",
705         fit: true, components: [
706         {kind: "XV.Groupbox", name: "mainPanel", components: [
707           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
708           {kind: "XV.ScrollableGroupbox", name: "mainGroup", fit: true,
709             classes: "in-panel", components: [
710             {kind: "XV.InputWidget", attr: "number"},
711             {kind: "XV.InputWidget", attr: "name"},
712             {kind: "XV.CustomerTypePicker", attr: "customerType"},
713             {kind: "XV.CheckboxWidget", attr: "isActive"},
714             {kind: "onyx.GroupboxHeader", content: "_billingContact".loc()},
715             {kind: "XV.ContactWidget", attr: "billingContact",
716               showAddress: true, label: "_name".loc()},
717             {kind: "onyx.GroupboxHeader", content: "_correspondenceContact".loc()},
718             {kind: "XV.ContactWidget", attr: "correspondenceContact",
719               showAddress: true, label: "_name".loc()},
720             {kind: "XV.CustomerCharacteristicsWidget", attr: "characteristics"},
721             {kind: "onyx.GroupboxHeader", content: "_notes".loc()},
722             {kind: "XV.TextArea", attr: "notes"}
723           ]}
724         ]},
725         {kind: "XV.Groupbox", name: "settingsPanel", title: "_settings".loc(), components: [
726           {kind: "onyx.GroupboxHeader", content: "_settings".loc()},
727           {kind: "XV.ScrollableGroupbox", name: "settingsGroup", fit: true,
728             classes: "in-panel", components: [
729             {kind: "XV.SalesRepPicker", attr: "salesRep"},
730             {kind: "XV.PercentWidget", attr: "commission"},
731             {kind: "XV.ShipViaCombobox", attr: "shipVia"},
732             {kind: "XV.ShippingChargePicker", attr: "shipCharge"},
733             {kind: "XV.CustomerEmailProfilePicker", attr: "emailProfile"},
734             {kind: "XV.CheckboxWidget", attr: "backorder"},
735             {kind: "XV.CheckboxWidget", attr: "partialShip"},
736             {kind: "XV.CheckboxWidget", attr: "isFreeFormShipto", label: "_freeFormShip".loc()},
737             {kind: "XV.CheckboxWidget", attr: "isFreeFormBillto", label: "_freeFormBill".loc()},
738             {kind: "onyx.GroupboxHeader", content: "_terms".loc()},
739             {kind: "XV.BillingTermsPicker", attr: "terms"},
740             {kind: "XV.PercentWidget", attr: "discount"},
741             {kind: "XV.CreditStatusPicker", attr: "creditStatus"},
742             {kind: "XV.CheckboxWidget", attr: "usesPurchaseOrders"},
743             {kind: "XV.CheckboxWidget", attr: "blanketPurchaseOrders"},
744             {kind: "XV.BalanceMethodPicker", attr: "balanceMethod"},
745             {kind: "XV.NumberWidget", attr: "creditLimit"},
746             {kind: "XV.InputWidget", attr: "creditRating"},
747             {kind: "XV.NumberWidget", attr: "graceDays"},
748             {kind: "onyx.GroupboxHeader", content: "_tax".loc()},
749             {kind: "XV.TaxZonePicker", attr: "taxZone", label: "_defaultTaxZone".loc()}
750           ]}
751         ]},
752         {kind: "XV.CustomerShipToBox", attr: "shiptos"},
753         {kind: "XV.CustomerCommentBox", attr: "comments"},
754         {kind: "XV.CreditCardsBox", attr: "creditCards"},
755         {kind: "XV.TaxRegistrationBox", attr: "taxRegistration"},
756         {kind: "XV.CustomerDocumentsBox", attr: "documents"},
757         {kind: "XV.CustomerQuoteListRelationsBox", attr: "quoteRelations"},
758         {kind: "XV.CustomerSalesOrderListRelationsBox", attr: "salesOrderRelations"},
759       ]},
760       // TODO: move this to notify system
761       {kind: "onyx.Popup", name: "findExistingCustomerPopup", centered: true,
762         modal: true, floating: true, scrim: true, onShow: "popupShown",
763         onHide: "popupHidden", components: [
764         {name: "exists"},
765         {name: "whatToDo"},
766         {tag: "br"},
767         {kind: "onyx.Button", name: "ok", content: "_ok".loc(), ontap: "customerConvert",
768           classes: "onyx-blue xv-popup-button", type: ""},
769         {kind: "onyx.Button", name: "cancel", content: "_cancel".loc(), ontap: "customerCancel",
770           classes: "xv-popup-button", type: ""}
771       ]}
772     ],
773     customerConvert: function (inEvent) {
774       this._popupDone = true;
775       this.$.findExistingCustomerPopup.hide();
776       if (inEvent.type === "prospect") {
777         this.value.convertFromProspect(this.existingNumber);
778       } else if (inEvent.type === "account") {
779         this.value.convertFromAccount(this.existingNumber);
780       }
781     },
782     errorNotify: function (inSender, inEvent) {
783       // Handle customer existing as prospect
784       if (inEvent.error.code === 'xt1008') {
785         var type = inEvent.error.params.response.type;
786         this.existingNumber = inEvent.error.params.response.id;
787         if (type === 'P') { // Prospect
788           this._popupDone = false;
789           this.$.exists.setContent("_prospectExists".loc());
790           this.$.whatToDo.setContent("_convertProspect".loc());
791           this.$.ok.type = "prospect";
792           this.$.findExistingCustomerPopup.show();
793           return true;
794         } else if (type === 'A') { // Existing Account
795           this._popupDone = false;
796           this.$.exists.setContent("_accountExists".loc());
797           this.$.whatToDo.setContent("_convertAccount".loc());
798           this.$.ok.type = "account";
799           this.$.findExistingCustomerPopup.show();
800           return true;
801         }
802       }
803     },
804     customerCancel: function () {
805       this._popupDone = true;
806       this.$.findExistingCustomerPopup.hide();
807       return true;
808     },
809     popupHidden: function () {
810       if (!this._popupDone) {
811         this.$.findExistingCustomerPopup.show();
812         return true;
813       }
814     }
815   });
816
817   XV.registerModelWorkspace("XM.CustomerRelation", "XV.CustomerWorkspace");
818   XV.registerModelWorkspace("XM.CustomerListItem", "XV.CustomerWorkspace");
819   XV.registerModelWorkspace("XM.CustomerProspectListItem", "XV.CustomerWorkspace");
820
821   // ..........................................................
822   // CUSTOMER EMAIL PROFILE
823   //
824
825   enyo.kind({
826     name: "XV.CustomerEmailProfileWorkspace",
827     kind: "XV.EmailProfileWorkspace",
828     title: "_customerEmailProfile".loc(),
829     model: "XM.CustomerEmailProfile",
830   });
831
832   XV.registerModelWorkspace("XM.CustomerEmailProfile", "XV.CustomerEmailProfileWorkspace");
833
834   // ..........................................................
835   // CUSTOMER GROUP
836   //
837
838   enyo.kind({
839     name: "XV.CustomerGroupWorkspace",
840     kind: "XV.Workspace",
841     title: "_customerGroup".loc(),
842     model: "XM.CustomerGroup",
843     components: [
844       {kind: "Panels", arrangerKind: "CarouselArranger",
845         fit: true, components: [
846         {kind: "XV.Groupbox", name: "mainPanel", components: [
847           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
848           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
849             classes: "in-panel", components: [
850             {kind: "XV.InputWidget", attr: "name"},
851             {kind: "XV.InputWidget", attr: "description"}
852           ]}
853         ]},
854         {kind: "XV.CustomerGroupCustomerBox", attr: "customers"}
855       ]}
856     ]
857   });
858
859   XV.registerModelWorkspace("XM.CustomerGroup", "XV.CustomerGroupWorkspace");
860
861   // ..........................................................
862   // CUSTOMER TYPE
863   //
864
865   enyo.kind({
866     name: "XV.CustomerTypeWorkspace",
867     kind: "XV.Workspace",
868     title: "_customerType".loc(),
869     model: "XM.CustomerType",
870     components: [
871       {kind: "Panels", arrangerKind: "CarouselArranger",
872         fit: true, components: [
873         {kind: "XV.Groupbox", name: "mainPanel", components: [
874           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
875           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
876             classes: "in-panel", components: [
877             {kind: "XV.InputWidget", attr: "code"},
878             {kind: "XV.InputWidget", attr: "description"}
879           ]}
880         ]}
881       ]}
882     ]
883   });
884
885   XV.registerModelWorkspace("XM.CustomerType", "XV.CustomerTypeWorkspace");
886
887   // ..........................................................
888   // CLASS CODE
889   //
890
891   enyo.kind({
892     name: "XV.ExpenseCategoryWorkspace",
893     kind: "XV.Workspace",
894     title: "_expenseCategory".loc(),
895     model: "XM.ExpenseCategory",
896     components: [
897       {kind: "Panels", arrangerKind: "CarouselArranger",
898         fit: true, components: [
899         {kind: "XV.Groupbox", name: "mainPanel", components: [
900           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
901           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
902             classes: "in-panel", components: [
903             {kind: "XV.InputWidget", attr: "code"},
904             {kind: "XV.InputWidget", attr: "description"}
905           ]}
906         ]}
907       ]}
908     ]
909   });
910
911   XV.registerModelWorkspace("XM.ExpenseCategory", "XV.ExpenseCategoryWorkspace");
912
913   // ..........................................................
914   // DEPARTMENT
915   //
916
917   enyo.kind({
918     name: "XV.DepartmentWorkspace",
919     kind: "XV.Workspace",
920     title: "_department".loc(),
921     model: "XM.Department",
922     components: [
923       {kind: "Panels", arrangerKind: "CarouselArranger",
924         fit: true, components: [
925         {kind: "XV.Groupbox", name: "mainPanel", components: [
926           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
927           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
928             classes: "in-panel", components: [
929             {kind: "XV.InputWidget", attr: "number"},
930             {kind: "XV.InputWidget", attr: "name"}
931           ]}
932         ]}
933       ]}
934     ]
935   });
936
937   XV.registerModelWorkspace("XM.Department", "XV.DepartmentWorkspace");
938
939   // ..........................................................
940   // EMPLOYEE
941   //
942
943   enyo.kind({
944     name: "XV.EmployeeWorkspace",
945     kind: "XV.AccountDocumentWorkspace",
946     title: "_employee".loc(),
947     model: "XM.Employee",
948     headerAttrs: ["number", "-", "name"],
949     components: [
950       {kind: "Panels", arrangerKind: "CarouselArranger",
951         fit: true, components: [
952         {kind: "XV.Groupbox", name: "mainPanel", components: [
953           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
954           {kind: "XV.ScrollableGroupbox", name: "mainGroup", fit: true,
955             classes: "in-panel", components: [
956             {kind: "XV.InputWidget", attr: "code"},
957             {kind: "XV.InputWidget", attr: "number"},
958             {kind: "XV.InputWidget", attr: "name"},
959             {kind: "XV.CheckboxWidget", attr: "isActive"},
960             {kind: "onyx.GroupboxHeader", content: "_contact".loc()},
961             {kind: "XV.ContactWidget", attr: "contact",
962               showAddress: true, label: "_name".loc()},
963             {kind: "XV.EmployeeCharacteristicsWidget", attr: "characteristics"},
964             {kind: "onyx.GroupboxHeader", content: "_notes".loc()},
965             {kind: "XV.TextArea", attr: "notes"}
966           ]}
967         ]},
968         {kind: "XV.Groupbox", name: "detailPanel", title: "_detail".loc(),
969           components: [
970           {kind: "onyx.GroupboxHeader", content: "_detail".loc()},
971           {kind: "XV.ScrollableGroupbox", name: "detailGroup", fit: true,
972             classes: "in-panel", components: [
973             {kind: "XV.DateWidget", attr: "startDate"},
974             {kind: "XV.SitePicker", attr: "site"},
975             {kind: "XV.DepartmentWidget", attr: "department"},
976             {kind: "XV.EmployeeWidget", attr: "manager"},
977             {kind: "XV.ShiftWidget", attr: "shift"},
978             {kind: "onyx.GroupboxHeader", content: "_financials".loc()},
979             {kind: "XV.WageTypePicker", attr: "wageType"},
980             {kind: "XV.MoneyWidget",
981               attr: {localValue: "wage", currency: "wageCurrency"},
982               currencyDisabled: true},
983             {kind: "XV.WagePeriodPicker", attr: "wagePeriod", label: "_period".loc()},
984             {kind: "XV.MoneyWidget",
985               attr: {localValue: "billingRate", currency: "billingCurrency"},
986               currencyDisabled: true},
987             {kind: "XV.WagePeriodPicker", attr: "billingPeriod", label: "_period".loc()}
988           ]}
989         ]},
990         {kind: "XV.EmployeeCommentBox", attr: "comments"},
991         {kind: "XV.EmployeeGroupGroupBox", attr: "groups"}
992       ]},
993       {kind: "onyx.Popup", name: "findExistingAccountPopup", centered: true,
994         modal: true, floating: true, scrim: true, onShow: "popupShown",
995         onHide: "popupHidden", components: [
996         {content: "_accountExists".loc()},
997         {name: "whatToDo", content: "_convertAccountEmployee".loc()},
998         {tag: "br"},
999         {kind: "onyx.Button", name: "convert", content: "_ok".loc(), ontap: "accountConvert",
1000           classes: "onyx-blue xv-popup-button"},
1001         {kind: "onyx.Button", name: "cancel", content: "_cancel".loc(), ontap: "accountCancel",
1002           classes: "xv-popup-button"}
1003       ]}
1004     ]
1005   });
1006
1007   XV.registerModelWorkspace("XM.EmployeeRelation", "XV.EmployeeWorkspace");
1008   XV.registerModelWorkspace("XM.EmployeeListItem", "XV.EmployeeWorkspace");
1009
1010   // ..........................................................
1011   // EMPLOYEE GROUP
1012   //
1013
1014   enyo.kind({
1015     name: "XV.EmployeeGroupWorkspace",
1016     kind: "XV.Workspace",
1017     title: "_employeeGroup".loc(),
1018     model: "XM.EmployeeGroup",
1019     components: [
1020       {kind: "Panels", arrangerKind: "CarouselArranger",
1021         fit: true, components: [
1022         {kind: "XV.Groupbox", name: "mainPanel", components: [
1023           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
1024           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
1025             classes: "in-panel", components: [
1026             {kind: "XV.InputWidget", attr: "name"},
1027             {kind: "XV.InputWidget", attr: "description"}
1028           ]}
1029         ]},
1030         {kind: "XV.EmployeeGroupEmployeeBox", attr: "employees"}
1031       ]}
1032     ]
1033   });
1034
1035   XV.registerModelWorkspace("XM.EmployeeGroup", "XV.EmployeeGroupWorkspace");
1036
1037   // ..........................................................
1038   // FILE
1039   //
1040
1041   enyo.kind({
1042     name: "XV.FileWorkspace",
1043     kind: "XV.Workspace",
1044     title: "_file".loc(),
1045     model: "XM.File",
1046     components: [
1047       {kind: "Panels", arrangerKind: "CarouselArranger",
1048         fit: true, components: [
1049         {kind: "XV.Groupbox", name: "mainPanel", components: [
1050           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
1051           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
1052             classes: "in-panel", components: [
1053             {kind: "XV.InputWidget", attr: "name", name: "name"},
1054             {kind: "XV.InputWidget", attr: "description", name: "description" },
1055             {kind: "XV.FileInput", name: "file", attr: "data"}
1056           ]}
1057         ]}
1058       ]}
1059     ],
1060
1061     /**
1062       When a file is uploaded we want the filename to overwrite
1063       the name and description fields.
1064      */
1065     controlValueChanged: function (inSender, inEvent) {
1066       var filename = inEvent.filename;
1067       this.inherited(arguments);
1068
1069       if (filename) {
1070         this.$.name.setValue(filename);
1071         this.$.description.setValue(filename);
1072       }
1073     },
1074     /**
1075       We want the description to be always disabled, which means we have
1076       to go in after the attributesChanged method, which, as it's defined
1077       in the superkind, will reset the disabled status based on permissions etc.
1078      */
1079     attributesChanged: function (model, options) {
1080       this.inherited(arguments);
1081       this.$.description.setDisabled(true);
1082     }
1083   });
1084
1085   XV.registerModelWorkspace("XM.FileRelation", "XV.FileWorkspace");
1086
1087   // ..........................................................
1088   // FREIGHT CLASS
1089   //
1090
1091   enyo.kind({
1092     name: "XV.FreightClassWorkspace",
1093     kind: "XV.Workspace",
1094     title: "_freightClass".loc(),
1095     model: "XM.FreightClass",
1096     components: [
1097       {kind: "Panels", arrangerKind: "CarouselArranger",
1098         fit: true, components: [
1099         {kind: "XV.Groupbox", name: "mainPanel", components: [
1100           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
1101           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
1102             classes: "in-panel", components: [
1103             {kind: "XV.InputWidget", attr: "code"},
1104             {kind: "XV.InputWidget", attr: "description"}
1105           ]}
1106         ]}
1107       ]}
1108     ]
1109   });
1110
1111   XV.registerModelWorkspace("XM.FreightClass", "XV.FreightClassWorkspace");
1112
1113   // ..........................................................
1114   // HONORIFIC
1115   //
1116
1117   enyo.kind({
1118     name: "XV.HonorificWorkspace",
1119     kind: "XV.Workspace",
1120     title: "_honorific".loc(),
1121     model: "XM.Honorific",
1122     components: [
1123       {kind: "Panels", arrangerKind: "CarouselArranger",
1124         fit: true, components: [
1125         {kind: "XV.Groupbox", name: "mainPanel", components: [
1126           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
1127           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
1128             classes: "in-panel", components: [
1129             {kind: "XV.InputWidget", attr: "code"}
1130           ]}
1131         ]}
1132       ]}
1133     ]
1134   });
1135
1136   XV.registerModelWorkspace("XM.Honorific", "XV.HonorificWorkspace");
1137
1138   // ..........................................................
1139   // INCIDENT
1140   //
1141
1142   var incidentHash = {
1143     name: "XV.IncidentWorkspace",
1144     kind: "XV.Workspace",
1145     title: "_incident".loc(),
1146     headerAttrs: ["number", "-", "description"],
1147     model: "XM.Incident",
1148     components: [
1149       {kind: "Panels", arrangerKind: "CarouselArranger",
1150         fit: true, components: [
1151         {kind: "XV.Groupbox", name: "mainPanel", components: [
1152           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
1153           {kind: "XV.ScrollableGroupbox", name: "mainGroup", fit: true,
1154             classes: "in-panel", components: [
1155             {kind: "XV.InputWidget", attr: "number"},
1156             {kind: "XV.InputWidget", attr: "description"},
1157             {kind: "XV.CheckboxWidget", attr: "isPublic", name: "isPublic"},
1158             {kind: "XV.AccountWidget", attr: "account"},
1159             {kind: "XV.ContactWidget", attr: "contact"},
1160             {kind: "XV.IncidentCategoryPicker", attr: "category"},
1161             {kind: "onyx.GroupboxHeader", content: "_status".loc()},
1162             {kind: "XV.IncidentStatusPicker", attr: "status"},
1163             {kind: "XV.PriorityPicker", attr: "priority"},
1164             {kind: "XV.IncidentSeverityPicker", attr: "severity"},
1165             {kind: "XV.IncidentResolutionPicker", attr: "resolution"},
1166             {kind: "onyx.GroupboxHeader", content: "_userAccounts".loc()},
1167             {kind: "XV.UserAccountWidget", attr: "owner"},
1168             {kind: "XV.UserAccountWidget", attr: "assignedTo"},
1169             {kind: "XV.IncidentCharacteristicsWidget", attr: "characteristics"},
1170             {kind: "onyx.GroupboxHeader", content: "_notes".loc()},
1171             {kind: "XV.TextArea", attr: "notes", fit: true},
1172             {kind: "onyx.GroupboxHeader", content: "_relationships".loc()},
1173             {kind: "XV.ItemWidget", attr: "item"}
1174           ]}
1175         ]},
1176         {kind: "XV.IncidentCommentBox", attr: "comments"},
1177         {kind: "XV.IncidentDocumentsBox", attr: "documents"},
1178         {kind: "XV.IncidentHistoryRelationsBox", attr: "history"}
1179       ]}
1180     ],
1181     create: function () {
1182       this.inherited(arguments);
1183       var settings = XT.session.getSettings();
1184       this.$.isPublic.setShowing(settings.get('IncidentsPublicPrivate'));
1185     }
1186   };
1187
1188   incidentHash = enyo.mixin(incidentHash, XV.accountNotifyContactMixin);
1189   enyo.kind(incidentHash);
1190
1191   XV.registerModelWorkspace("XM.Incident", "XV.IncidentWorkspace");
1192   XV.registerModelWorkspace("XM.IncidentRelation", "XV.IncidentWorkspace");
1193   XV.registerModelWorkspace("XM.IncidentListItem", "XV.IncidentWorkspace");
1194
1195   // ..........................................................
1196   // INCIDENT EMAIL PROFILE
1197   //
1198
1199   enyo.kind({
1200     name: "XV.IncidentEmailProfileWorkspace",
1201     kind: "XV.EmailProfileWorkspace",
1202     title: "_incidentEmailProfile".loc(),
1203     model: "XM.IncidentEmailProfile",
1204   });
1205
1206   XV.registerModelWorkspace("XM.IncidentEmailProfile", "XV.IncidentEmailProfileWorkspace");
1207
1208   // ..........................................................
1209   // INCIDENT CATEGORY
1210   //
1211
1212   enyo.kind({
1213     name: "XV.IncidentCategoryWorkspace",
1214     kind: "XV.OrderedReferenceWorkspace",
1215     title: "_incidentCategory".loc(),
1216     model: "XM.IncidentCategory",
1217     components: [
1218       {kind: "Panels", arrangerKind: "CarouselArranger",
1219         fit: true, components: [
1220         {kind: "XV.Groupbox", name: "mainPanel", components: [
1221           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
1222           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
1223             classes: "in-panel", components: [
1224             {kind: "XV.InputWidget", attr: "name"},
1225             {kind: "XV.InputWidget", attr: "description"},
1226             {kind: "XV.NumberWidget", attr: "order"},
1227             {kind: "XV.IncidentEmailProfilePicker", attr: "emailProfile"}
1228           ]}
1229         ]}
1230       ]}
1231     ]
1232   });
1233
1234   XV.registerModelWorkspace("XM.IncidentCategory", "XV.IncidentCategoryWorkspace");
1235
1236   // ..........................................................
1237   // INCIDENT RESOLUTION
1238   //
1239
1240   enyo.kind({
1241     name: "XV.IncidentResolutionWorkspace",
1242     kind: "XV.OrderedReferenceWorkspace",
1243     title: "_incidentResolution".loc(),
1244     model: "XM.IncidentResolution"
1245   });
1246
1247   XV.registerModelWorkspace("XM.IncidentResolution", "XV.IncidentResolutionWorkspace");
1248
1249   // ..........................................................
1250   // INCIDENT SEVERITY
1251   //
1252
1253   enyo.kind({
1254     name: "XV.IncidentSeverityWorkspace",
1255     kind: "XV.OrderedReferenceWorkspace",
1256     title: "_incidentSeverity".loc(),
1257     model: "XM.IncidentSeverity"
1258   });
1259
1260   XV.registerModelWorkspace("XM.IncidentSeverity", "XV.IncidentSeverityWorkspace");
1261
1262   // ..........................................................
1263   // INVOICE
1264   //
1265   hash = {
1266     name: "XV.InvoiceWorkspace",
1267     kind: "XV.Workspace",
1268     title: "_invoice".loc(),
1269     model: "XM.Invoice",
1270     actions: [{
1271       name: "print",
1272       isViewMethod: true,
1273       label: "_print".loc(),
1274       privilege: "PrintInvoices",
1275       prerequisite: "isReadyClean"
1276     },
1277     {
1278       name: "email",
1279       isViewMethod: true,
1280       label: "_email".loc(),
1281       privilege: "PrintInvoices",
1282       prerequisite: "isReadyClean"
1283     }],
1284     components: [
1285       {kind: "Panels", arrangerKind: "CarouselArranger",
1286         fit: true, components: [
1287         {kind: "XV.Groupbox", name: "mainPanel", components: [
1288           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
1289           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
1290               classes: "in-panel", fit: true, components: [
1291             {name: "mainSubgroup", components: [ // not a scroller, so we can addBefore
1292               {kind: "XV.InputWidget", attr: "number"},
1293               {kind: "XV.DateWidget", attr: "invoiceDate"},
1294               {kind: "XV.CheckboxWidget", name: "isPosted", attr: "isPosted"},
1295               {kind: "XV.CheckboxWidget", name: "isVoid", attr: "isVoid"},
1296               {kind: "onyx.GroupboxHeader", content: "_billTo".loc()},
1297               {kind: "XV.BillingCustomerWidget", attr: "customer",
1298                  name: "customerWidget", showAddress: true,
1299                  label: "_customer".loc(), nameAttribute: ""
1300               },
1301               {kind: "XV.AddressFieldsWidget",
1302                 name: "addressWidget", attr:
1303                 {name: "billtoName", line1: "billtoAddress1",
1304                   line2: "billtoAddress2", line3: "billtoAddress3",
1305                   city: "billtoCity", state: "billtoState",
1306                   postalCode: "billtoPostalCode", country: "billtoCountry"}
1307               },
1308               {kind: "onyx.GroupboxHeader", content: "_notes".loc(), name: "notesHeader"},
1309               {kind: "XV.TextArea", attr: "notes", fit: true}
1310             ]}
1311           ]}
1312         ]},
1313         {kind: "XV.Groupbox", name: "settingsPanel", title: "_settings".loc(),
1314           components: [
1315           {kind: "onyx.GroupboxHeader", content: "_settings".loc()},
1316           {kind: "XV.ScrollableGroupbox", name: "settingsGroup", fit: true,
1317             classes: "in-panel", components: [
1318             {kind: "XV.CurrencyPicker", attr: "currency"},
1319             {kind: "XV.BillingTermsPicker", attr: "terms"},
1320             {kind: "XV.SalesRepPicker", attr: "salesRep"},
1321             {kind: "XV.PercentWidget", attr: "commission"},
1322             {kind: "XV.SaleTypePicker", attr: "saleType"},
1323             {kind: "XV.InputWidget", attr: "customerPurchaseOrderNumber",
1324               label: "_custPO".loc()},
1325             {kind: "XV.TaxZonePicker", attr: "taxZone"},
1326           ]}
1327         ]},
1328         {kind: "XV.InvoiceAllocationsBox", attr: "allocations", title: "_allocatedCredit".loc()},
1329         // TODO: nest the next two items in a groupbox
1330         {kind: "XV.InvoiceTaxBox", attr: "taxes", title: "_taxes".loc()},
1331         {kind: "XV.InvoiceTaxAdjustmentBox", attr: "taxAdjustments", title: "_taxAdjustments".loc()},
1332         {kind: "XV.InvoiceDocumentsBox", attr: "documents"}
1333       ]}
1334     ],
1335     create: function () {
1336       this.inherited(arguments);
1337       if (enyo.platform.touch) {
1338         this.$.panels.createComponents([
1339           {kind: "XV.InvoiceLineItemBox", name: "lineItemBox", attr: "lineItems",
1340             title: "_lineItems".loc(), addBefore: this.$.settingsPanel, classes: "medium-panel"}
1341         ], {owner: this});
1342       } else {
1343         this.$.panels.createComponents([
1344           {kind: "XV.InvoiceLineItemGridBox", name: "lineItemBox", title: "_lineItems".loc(),
1345             attr: "lineItems", addBefore: this.$.settingsPanel}
1346         ], {owner: this});
1347       }
1348       this.processExtensions(true);
1349     }
1350   };
1351   hash = enyo.mixin(hash, XV.WorkspaceAddressMixin);
1352   enyo.kind(hash);
1353
1354   XV.registerModelWorkspace("XM.InvoiceListItem", "XV.InvoiceWorkspace");
1355
1356   enyo.kind({
1357     name: "XV.InvoiceLineWorkspace",
1358     kind: "XV.ChildWorkspace",
1359     title: "_invoiceLine".loc(),
1360     model: "XM.InvoiceLine",
1361     published: {
1362       currencyKey: "invoice.currency",
1363       effectiveKey: "invoice.invoiceDate"
1364     },
1365     components: [
1366       {kind: "Panels", arrangerKind: "CarouselArranger",
1367         fit: true, components: [
1368         {kind: "XV.Groupbox", name: "mainPanel", components: [
1369           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
1370           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
1371             classes: "in-panel", fit: true, components: [
1372             {kind: "XV.NumberWidget", attr: "lineNumber"},
1373             {kind: "XV.CheckboxWidget", attr: "isMiscellaneous"},
1374             {kind: "XV.ItemSiteWidget", attr: {item: "item", site: "site"},
1375               name: "itemSiteWidget",
1376               query: {parameters: [
1377               {attribute: "item.isSold", value: true},
1378               {attribute: "item.isActive", value: true},
1379               {attribute: "isSold", value: true},
1380               {attribute: "isActive", value: true}
1381             ]}},
1382             {kind: "XV.SalesPriceWidget", attr: "item.listPrice", label: "_listPrice".loc()},
1383             {kind: "XV.SalesPriceWidget", attr: "item.wholesalePrice",
1384               label: "_wholesalePrice".loc()},
1385             {kind: "XV.InputWidget", attr: "customerPartNumber"},
1386             {kind: "XV.InputWidget", attr: "itemNumber"},
1387             {kind: "XV.InputWidget", attr: "itemDescription"},
1388             {kind: "XV.SalesCategoryPicker", attr: "salesCategory"},
1389           ]}
1390         ]},
1391         {kind: "XV.Groupbox", name: "pricePanel", title: "_price".loc(), components: [
1392           {kind: "onyx.GroupboxHeader", content: "_price".loc()},
1393           {kind: "XV.ScrollableGroupbox", name: "priceGroup",
1394               classes: "in-panel", fit: true, components: [
1395             {kind: "XV.QuantityWidget", attr: "quantity", label: "_ordered".loc()},
1396             {kind: "XV.QuantityWidget", attr: "billed"},
1397             {kind: "XV.UnitPicker", name: "quantityUnitPicker",
1398               attr: "quantityUnit"},
1399             {kind: "XV.MoneyWidget", attr:
1400               {localValue: "price", currency: ""},
1401               label: "_price".loc(), currencyDisabled: true,
1402               scale: XT.SALES_PRICE_SCALE},
1403             {kind: "XV.UnitPicker", name: "priceUnitPicker",
1404               attr: "priceUnit"},
1405             {kind: "XV.MoneyWidget", attr:
1406               {localValue: "extendedPrice", currency: ""},
1407               label: "_extendedPrice".loc(), currencyDisabled: true,
1408               scale: XT.EXTENDED_PRICE_SCALE}
1409           ]}
1410         ]},
1411         {kind: "XV.Groupbox", name: "detailsPanel", title: "_detail".loc(),
1412           components: [
1413           {kind: "onyx.GroupboxHeader", content: "_detail".loc()},
1414           {kind: "XV.ScrollableGroupbox", name: "detailGroup",
1415             classes: "in-panel", fit: true, components: [
1416             {kind: "XV.MoneyWidget", attr: {baseValue: "item.standardCost"},
1417               label: "_unitCost".loc(), isEditableProperty: "baseValue",
1418               currencyDisabled: true},
1419             {kind: "XV.MoneyWidget", attr: {localValue: "customerPrice"},
1420               label: "_customerPrice".loc(), scale: XT.SALES_PRICE_SCALE,
1421               currencyDisabled: true},
1422             {kind: "onyx.GroupboxHeader", content: "_tax".loc()},
1423             {kind: "XV.TaxTypePicker", attr: "taxType"},
1424             {kind: "XV.MoneyWidget", attr: {localValue: "taxTotal"},
1425               label: "_tax".loc(), currencyDisabled: true},
1426             {kind: "onyx.GroupboxHeader", content: "_notes".loc()},
1427             {kind: "XV.TextArea", attr: "notes", fit: true}
1428           ]}
1429         ]},
1430         {kind: "XV.InvoiceLineTaxBox", attr: "taxes"}
1431       ]}
1432     ],
1433     create: function () {
1434       this.inherited(arguments);
1435       var effectiveKey = this.getEffectiveKey(),
1436         currencyKey = this.getCurrencyKey();
1437
1438       // Set currency and effective attributes on money widgets
1439       this.getComponents().forEach(function (ctl) {
1440         if (ctl.kind === "XV.MoneyWidget") {
1441           ctl.attr.currency = currencyKey;
1442           ctl.attr.effective = effectiveKey;
1443         }
1444       });
1445     }
1446   });
1447   // ..........................................................
1448   // INVOICE ALLOCATION
1449   //
1450
1451   /*
1452   enyo.kind({
1453     name: "XV.InvoiceAllocationWorkspace",
1454     kind: "XV.Workspace",
1455     title: "_allocation".loc(),
1456     model: "XM.InvoiceAllocation",
1457     components: [
1458       {kind: "Panels", arrangerKind: "CarouselArranger",
1459         fit: true, components: [
1460         {kind: "XV.Groupbox", name: "mainPanel", components: [
1461           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
1462           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
1463             classes: "in-panel", components: [
1464             {kind: "XV.InputWidget", attr: "code"}
1465           ]}
1466         ]}
1467       ]}
1468     ]
1469   });
1470
1471   XV.registerModelWorkspace("XM.InvoiceAllocation", "XV.InvoiceAllocationWorkspace");
1472   */
1473   // ..........................................................
1474   // ITEM
1475   //
1476
1477   enyo.kind({
1478     name: "XV.ItemWorkspace",
1479     kind: "XV.Workspace",
1480     title: "_item".loc(),
1481     model: "XM.Item",
1482     headerAttrs: ["number", "-", "description1"],
1483     components: [
1484       {kind: "Panels", arrangerKind: "CarouselArranger",
1485         fit: true, components: [
1486         {kind: "XV.Groupbox", name: "mainPanel", components: [
1487           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
1488           {kind: "XV.ScrollableGroupbox", name: "mainGroup", fit: true,
1489             classes: "in-panel", components: [
1490             {kind: "XV.InputWidget", attr: "number"},
1491             {kind: "XV.CheckboxWidget", attr: "isActive"},
1492             {kind: "XV.InputWidget", attr: "description1"},
1493             {kind: "XV.InputWidget", attr: "description2"},
1494             {kind: "XV.ItemTypePicker", attr: "itemType", showNone: false},
1495             {kind: "XV.ClassCodePicker", attr: "classCode"},
1496             {kind: "XV.UnitPicker", attr: "inventoryUnit"},
1497             {kind: "XV.CheckboxWidget", attr: "isFractional"},
1498             {kind: "XV.ItemCharacteristicsWidget", attr: "characteristics"},
1499             {kind: "onyx.GroupboxHeader",
1500               content: "_extendedDescription".loc()},
1501             {kind: "XV.TextArea", attr: "extendedDescription"},
1502             {kind: "onyx.GroupboxHeader", content: "_notes".loc()},
1503             {kind: "XV.TextArea", attr: "notes", fit: true}
1504           ]}
1505         ]},
1506         {kind: "XV.Groupbox", name: "settingsPanel", title: "_settings".loc(),
1507           components: [
1508           {kind: "XV.ScrollableGroupbox", name: "settingsGroup", fit: true,
1509             classes: "in-panel", components: [
1510             {kind: "onyx.GroupboxHeader", content: "_settings".loc()},
1511             {kind: "XV.CheckboxWidget", attr: "isSold"},
1512             {kind: "XV.ProductCategoryPicker", attr: "productCategory",
1513               label: "_category".loc()},
1514             {kind: "XV.SalesPriceWidget", attr: "listPrice"},
1515             {kind: "XV.SalesPriceWidget", attr: "wholesalePrice"},
1516             {kind: "XV.UnitPicker", attr: "priceUnit"},
1517             {kind: "XV.CheckboxWidget", attr: "isExclusive"}
1518           ]}
1519         ]},
1520         {kind: "XV.ItemCommentBox", attr: "comments"},
1521         {kind: "XV.ItemDocumentsBox", attr: "documents"},
1522         {kind: "XV.ItemAliasBox", attr: "aliases"}
1523       ]}
1524     ]
1525   });
1526
1527   XV.registerModelWorkspace("XM.ItemRelation", "XV.ItemWorkspace");
1528   XV.registerModelWorkspace("XM.ItemListItem", "XV.ItemWorkspace");
1529
1530   // ..........................................................
1531   // ITEM GROUP
1532   //
1533
1534   enyo.kind({
1535     name: "XV.ItemGroupWorkspace",
1536     kind: "XV.Workspace",
1537     title: "_itemGroup".loc(),
1538     model: "XM.ItemGroup",
1539     components: [
1540       {kind: "Panels", arrangerKind: "CarouselArranger",
1541         fit: true, components: [
1542         {kind: "XV.Groupbox", name: "mainPanel", components: [
1543           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
1544           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
1545             classes: "in-panel", components: [
1546             {kind: "XV.InputWidget", attr: "name"},
1547             {kind: "XV.InputWidget", attr: "description"}
1548           ]}
1549         ]},
1550         {kind: "XV.ItemGroupItemBox", attr: "items"}
1551       ]}
1552     ]
1553   });
1554
1555   XV.registerModelWorkspace("XM.ItemGroup", "XV.ItemGroupWorkspace");
1556   XV.registerModelWorkspace("XM.ItemGroupRelation", "XV.ItemGroupWorkspace");
1557   XV.registerModelWorkspace("XM.ItemGroupItem", "XV.ItemGroupWorkspace");
1558
1559   // ..........................................................
1560   // ITEM SITE
1561   //
1562
1563   enyo.kind({
1564     name: "XV.ItemSiteWorkspace",
1565     kind: "XV.Workspace",
1566     title: "_itemSite".loc(),
1567     model: "XM.ItemSite",
1568     headerAttrs: ["item.number", "-", "site.code"],
1569     components: [
1570       {kind: "Panels", arrangerKind: "CarouselArranger",
1571         fit: true, components: [
1572         {kind: "XV.Groupbox", name: "mainPanel", components: [
1573           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
1574           {kind: "XV.ScrollableGroupbox", name: "mainGroup", fit: true,
1575             classes: "in-panel", components: [
1576             {kind: "XV.ItemWidget", attr: "item"},
1577             {kind: "XV.OptionalSitePicker", attr: "site"},
1578             {kind: "XV.CheckboxWidget", attr: "isActive"},
1579             {kind: "XV.PlannerCodePicker", attr: "plannerCode"},
1580             {kind: "XV.CostCategoryPicker", attr: "costCategory"},
1581             {kind: "XV.CheckboxWidget", attr: "isSold"},
1582             {kind: "XV.NumberSpinnerWidget", attr: "soldRanking"},
1583             {kind: "onyx.GroupboxHeader", content: "_notes".loc()},
1584             {kind: "XV.TextArea", attr: "notes", fit: true}
1585           ]}
1586         ]},
1587         {kind: "XV.ItemSiteCommentBox", attr: "comments"}
1588       ]}
1589     ]
1590   });
1591
1592   XV.registerModelWorkspace("XM.ItemSiteRelation", "XV.ItemSiteWorkspace");
1593   XV.registerModelWorkspace("XM.ItemItemSiteRelation", "XV.ItemSiteWorkspace");
1594   XV.registerModelWorkspace("XM.ItemSiteListItem", "XV.ItemSiteWorkspace");
1595
1596   // ..........................................................
1597   // OPPORTUNITY
1598   //
1599
1600   var opportunityHash = {
1601     name: "XV.OpportunityWorkspace",
1602     kind: "XV.Workspace",
1603     title: "_opportunity".loc(),
1604     headerAttrs: ["number", "-", "name"],
1605     model: "XM.Opportunity",
1606     components: [
1607       {kind: "Panels", arrangerKind: "CarouselArranger",
1608         fit: true, components: [
1609         {kind: "XV.Groupbox", name: "mainPanel", components: [
1610           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
1611           {kind: "XV.ScrollableGroupbox", name: "mainGroup", fit: true,
1612             classes: "in-panel", components: [
1613             {kind: "XV.InputWidget", attr: "number"},
1614             {kind: "XV.CheckboxWidget", attr: "isActive"},
1615             {kind: "XV.InputWidget", attr: "name"},
1616             {kind: "XV.AccountWidget", attr: "account"},
1617             {kind: "XV.ContactWidget", attr: "contact"},
1618             {kind: "XV.MoneyWidget",
1619               attr: {localValue: "amount", currency: "currency"},
1620               label: "_amount".loc()},
1621             {kind: "XV.NumberWidget", attr: "probability"},
1622             {kind: "onyx.GroupboxHeader", content: "_status".loc()},
1623             {kind: "XV.OpportunityStagePicker", attr: "opportunityStage",
1624               label: "_stage".loc()},
1625             {kind: "XV.PriorityPicker", attr: "priority"},
1626             {kind: "XV.OpportunityTypePicker", attr: "opportunityType",
1627               label: "_type".loc()},
1628             {kind: "XV.OpportunitySourcePicker", attr: "opportunitySource",
1629               label: "_source".loc()},
1630             {kind: "onyx.GroupboxHeader", content: "_schedule".loc()},
1631             {kind: "XV.DateWidget", attr: "targetClose"},
1632             {kind: "XV.DateWidget", attr: "startDate"},
1633             {kind: "XV.DateWidget", attr: "assignDate"},
1634             {kind: "XV.DateWidget", attr: "actualClose"},
1635             {kind: "onyx.GroupboxHeader", content: "_userAccounts".loc()},
1636             {kind: "XV.UserAccountWidget", attr: "owner"},
1637             {kind: "XV.UserAccountWidget", attr: "assignedTo"},
1638             {kind: "XV.OpportunityCharacteristicsWidget", attr: "characteristics"},
1639             {kind: "onyx.GroupboxHeader", content: "_notes".loc()},
1640             {kind: "XV.TextArea", attr: "notes", fit: true}
1641           ]}
1642         ]},
1643         {kind: "XV.OpportunityCommentBox", attr: "comments"},
1644         {kind: "XV.OpportunityDocumentsBox", attr: "documents"}
1645       ]}
1646     ]
1647   };
1648
1649   opportunityHash = enyo.mixin(opportunityHash, XV.accountNotifyContactMixin);
1650   enyo.kind(opportunityHash);
1651
1652   XV.registerModelWorkspace("XM.Opportunity", "XV.OpportunityWorkspace");
1653   XV.registerModelWorkspace("XM.OpportunityRelation", "XV.OpportunityWorkspace");
1654   XV.registerModelWorkspace("XM.OpportunityListItem", "XV.OpportunityWorkspace");
1655
1656   // ..........................................................
1657   // OPPORTUNITY SOURCE
1658   //
1659
1660   enyo.kind({
1661     name: "XV.OpportunitySourceWorkspace",
1662     kind: "XV.Workspace",
1663     title: "_opportunitySource".loc(),
1664     model: "XM.OpportunitySource"
1665   });
1666
1667   XV.registerModelWorkspace("XM.OpportunitySource", "XV.OpportunitySourceWorkspace");
1668
1669   // ..........................................................
1670   // OPPORTUNITY STAGE
1671   //
1672
1673   enyo.kind({
1674     name: "XV.OpportunityStageWorkspace",
1675     kind: "XV.Workspace",
1676     title: "_opportunityStage".loc(),
1677     model: "XM.OpportunityStage",
1678     components: [
1679       {kind: "Panels", arrangerKind: "CarouselArranger",
1680         fit: true, components: [
1681         {kind: "XV.Groupbox", name: "mainPanel", components: [
1682           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
1683           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
1684             classes: "in-panel", components: [
1685             {kind: "XV.InputWidget", attr: "name"},
1686             {kind: "XV.InputWidget", attr: "description"},
1687             {kind: "XV.CheckboxWidget", attr: "deactivate"}
1688           ]}
1689         ]}
1690       ]}
1691     ]
1692   });
1693
1694   XV.registerModelWorkspace("XM.OpportunityStage", "XV.OpportunityStageWorkspace");
1695
1696   // ..........................................................
1697   // OPPORTUNITY TYPE
1698   //
1699
1700   enyo.kind({
1701     name: "XV.OpportunityTypeWorkspace",
1702     kind: "XV.Workspace",
1703     title: "_opportunityType".loc(),
1704     model: "XM.OpportunityType"
1705   });
1706
1707   XV.registerModelWorkspace("XM.OpportunityType", "XV.OpportunityTypeWorkspace");
1708
1709   // ..........................................................
1710   // PLANNER CODE
1711   //
1712
1713   enyo.kind({
1714     name: "XV.PlannerCodeWorkspace",
1715     kind: "XV.Workspace",
1716     title: "_plannerCode".loc(),
1717     model: "XM.PlannerCode",
1718     components: [
1719       {kind: "Panels", arrangerKind: "CarouselArranger",
1720         fit: true, components: [
1721         {kind: "XV.Groupbox", name: "mainPanel", components: [
1722           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
1723           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
1724             classes: "in-panel", components: [
1725             {kind: "XV.InputWidget", attr: "code"},
1726             {kind: "XV.InputWidget", attr: "name"}
1727           ]}
1728         ]}
1729       ]}
1730     ]
1731   });
1732
1733   XV.registerModelWorkspace("XM.PlannerCode", "XV.PlannerCodeWorkspace");
1734
1735   // ..........................................................
1736   // PRIORITY
1737   //
1738
1739   enyo.kind({
1740     name: "XV.PriorityWorkspace",
1741     kind: "XV.OrderedReferenceWorkspace",
1742     title: "_priority".loc(),
1743     model: "XM.Priority"
1744   });
1745
1746   XV.registerModelWorkspace("XM.Priority", "XV.PriorityWorkspace");
1747
1748   // ..........................................................
1749   // PRODUCT CATEGORY
1750   //
1751
1752   enyo.kind({
1753     name: "XV.ProductCategoryWorkspace",
1754     kind: "XV.Workspace",
1755     title: "_productCategory".loc(),
1756     model: "XM.ProductCategory",
1757     components: [
1758       {kind: "Panels", arrangerKind: "CarouselArranger",
1759         fit: true, components: [
1760         {kind: "XV.Groupbox", name: "mainPanel", components: [
1761           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
1762           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
1763             classes: "in-panel", components: [
1764             {kind: "XV.InputWidget", attr: "code"},
1765             {kind: "XV.InputWidget", attr: "description"}
1766           ]}
1767         ]}
1768       ]}
1769     ]
1770   });
1771
1772   XV.registerModelWorkspace("XM.ProductCategory", "XV.ProductCategoryWorkspace");
1773
1774   // ..........................................................
1775   // PROSPECT
1776   //
1777
1778   enyo.kind({
1779     name: "XV.ProspectWorkspace",
1780     kind: "XV.AccountDocumentWorkspace",
1781     title: "_prospect".loc(),
1782     model: "XM.Prospect",
1783     headerAttrs: ["number", "-", "name"],
1784     components: [
1785       {kind: "Panels", arrangerKind: "CarouselArranger",
1786         fit: true, components: [
1787         {kind: "XV.Groupbox", name: "mainPanel", components: [
1788           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
1789           {kind: "XV.ScrollableGroupbox", name: "mainGroup", fit: true,
1790             classes: "in-panel", components: [
1791             {kind: "XV.InputWidget", attr: "number"},
1792             {kind: "XV.InputWidget", attr: "name"},
1793             {kind: "XV.CheckboxWidget", attr: "isActive"},
1794             {kind: "XV.SalesRepPicker", attr: "salesRep"},
1795             {kind: "XV.TaxZonePicker", attr: "taxZone"},
1796             {kind: "onyx.GroupboxHeader", content: "_contact".loc()},
1797             {kind: "XV.ContactWidget", attr: "contact",
1798               showAddress: true, label: "_name".loc()},
1799             {kind: "onyx.GroupboxHeader", content: "_notes".loc()},
1800             {kind: "XV.TextArea", attr: "notes"}
1801           ]}
1802         ]},
1803         {kind: "XV.ProspectQuoteListRelationsBox", attr: "quoteRelations"}
1804       ]},
1805       // TODO: use standard notify mechanism
1806       {kind: "onyx.Popup", name: "findExistingAccountPopup", centered: true,
1807         modal: true, floating: true, scrim: true, onShow: "popupShown",
1808         onHide: "popupHidden", components: [
1809         {content: "_accountExists".loc()},
1810         {name: "whatToDo", content: "_convertAccountProspect".loc()},
1811         {tag: "br"},
1812         {kind: "onyx.Button", name: "convert", content: "_ok".loc(), ontap: "accountConvert",
1813           classes: "onyx-blue xv-popup-button"},
1814         {kind: "onyx.Button", name: "cancel", content: "_cancel".loc(), ontap: "accountCancel",
1815           classes: "xv-popup-button"}
1816       ]}
1817     ]
1818   });
1819
1820   XV.registerModelWorkspace("XM.ProspectRelation", "XV.ProspectWorkspace");
1821   XV.registerModelWorkspace("XM.ProspectListItem", "XV.ProspectWorkspace");
1822
1823   // ..........................................................
1824   // RETURN
1825   //
1826   hash = {
1827     name: "XV.ReturnWorkspace",
1828     kind: "XV.Workspace",
1829     title: "_return".loc(),
1830     model: "XM.Return",
1831     components: [
1832       {kind: "Panels", arrangerKind: "CarouselArranger",
1833         fit: true, components: [
1834         {kind: "XV.Groupbox", name: "mainPanel", components: [
1835           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
1836           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
1837               classes: "in-panel", fit: true, components: [
1838             {name: "mainSubgroup", components: [ // not a scroller, so we can addBefore
1839               {kind: "XV.InputWidget", attr: "number"},
1840               {kind: "XV.DateWidget", attr: "returnDate"},
1841               {kind: "XV.CheckboxWidget", name: "isPosted", attr: "isPosted"},
1842               {kind: "XV.CheckboxWidget", name: "isVoid", attr: "isVoid"},
1843               {kind: "onyx.GroupboxHeader", content: "_billTo".loc()},
1844               {kind: "XV.BillingCustomerWidget", attr: "customer",
1845                  name: "customerWidget", showAddress: true,
1846                  label: "_customer".loc(), nameAttribute: ""
1847               },
1848               {kind: "XV.AddressFieldsWidget",
1849                 name: "addressWidget", attr:
1850                 {name: "billtoName", line1: "billtoAddress1",
1851                   line2: "billtoAddress2", line3: "billtoAddress3",
1852                   city: "billtoCity", state: "billtoState",
1853                   postalCode: "billtoPostalCode", country: "billtoCountry"}
1854               },
1855               {kind: "onyx.GroupboxHeader", content: "_notes".loc(), name: "notesHeader"},
1856               {kind: "XV.TextArea", attr: "notes", fit: true}
1857             ]}
1858           ]}
1859         ]},
1860         {kind: "XV.Groupbox", name: "settingsPanel", title: "_settings".loc(),
1861           components: [
1862           {kind: "onyx.GroupboxHeader", content: "_settings".loc()},
1863           {kind: "XV.ScrollableGroupbox", name: "settingsGroup", fit: true,
1864             classes: "in-panel", components: [
1865             {kind: "XV.CurrencyPicker", attr: "currency"},
1866             {kind: "XV.SalesRepPicker", attr: "salesRep"},
1867             {kind: "XV.PercentWidget", attr: "commission"},
1868             {kind: "XV.SaleTypePicker", attr: "saleType"},
1869             {kind: "XV.InputWidget", attr: "customerPurchaseOrderNumber",
1870               label: "_custPO".loc()},
1871             {kind: "XV.TaxZonePicker", attr: "taxZone"},
1872           ]}
1873         ]},
1874         //{kind: "XV.ReturnAllocationsBox", attr: "allocations", title: "_allocatedCredit".loc()},
1875         // TODO: nest the next two items in a groupbox
1876         {kind: "XV.ReturnTaxBox", attr: "taxes", title: "_taxes".loc()},
1877         {kind: "XV.ReturnTaxAdjustmentBox", attr: "taxAdjustments", title: "_taxAdjustments".loc()}
1878         //{kind: "XV.ReturnDocumentsBox", attr: "documents"}
1879       ]}
1880     ],
1881     create: function () {
1882       this.inherited(arguments);
1883       if (enyo.platform.touch) {
1884         this.$.panels.createComponents([
1885           {kind: "XV.ReturnLineItemBox", name: "lineItemBox",
1886             attr: "lineItems", title: "_lineItems".loc(),
1887               addBefore: this.$.settingsPanel, classes: "medium-panel"}
1888         ], {owner: this});
1889       } else {
1890         this.$.panels.createComponents([
1891           {kind: "XV.ReturnLineItemGridBox", name: "lineItemBox",
1892             title: "_lineItems".loc(), attr: "lineItems", addBefore: this.$.settingsPanel}
1893         ], {owner: this});
1894       }
1895       this.processExtensions(true);
1896     }
1897   };
1898   hash = enyo.mixin(hash, XV.WorkspaceAddressMixin);
1899   enyo.kind(hash);
1900
1901   XV.registerModelWorkspace("XM.ReturnListItem", "XV.ReturnWorkspace");
1902
1903   enyo.kind({
1904     name: "XV.ReturnLineWorkspace",
1905     kind: "XV.ChildWorkspace",
1906     title: "_returnLine".loc(),
1907     model: "XM.ReturnLine",
1908     published: {
1909       currencyKey: "return.currency",
1910       effectiveKey: "return.returnDate"
1911     },
1912     components: [
1913       {kind: "Panels", arrangerKind: "CarouselArranger",
1914         fit: true, components: [
1915         {kind: "XV.Groupbox", name: "mainPanel", components: [
1916           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
1917           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
1918             classes: "in-panel", fit: true, components: [
1919             {kind: "XV.NumberWidget", attr: "lineNumber"},
1920             {kind: "XV.ItemSiteWidget", attr: {item: "item", site: "site"},
1921               name: "itemSiteWidget",
1922               query: {parameters: [
1923               {attribute: "item.isSold", value: true},
1924               {attribute: "item.isActive", value: true},
1925               {attribute: "isSold", value: true},
1926               {attribute: "isActive", value: true}
1927             ]}},
1928             {kind: "XV.SalesPriceWidget", attr: "item.listPrice", label: "_listPrice".loc()},
1929             {kind: "XV.SalesPriceWidget", attr: "item.wholesalePrice",
1930               label: "_wholesalePrice".loc()}
1931           ]}
1932         ]},
1933         {kind: "XV.Groupbox", name: "pricePanel", title: "_price".loc(), components: [
1934           {kind: "onyx.GroupboxHeader", content: "_price".loc()},
1935           {kind: "XV.ScrollableGroupbox", name: "priceGroup",
1936               classes: "in-panel", fit: true, components: [
1937             {kind: "XV.QuantityWidget", attr: "quantity", label: "_ordered".loc()},
1938             {kind: "XV.QuantityWidget", attr: "credited"},
1939             {kind: "XV.UnitPicker", name: "quantityUnitPicker",
1940               attr: "quantityUnit"},
1941             {kind: "XV.MoneyWidget", attr:
1942               {localValue: "price", currency: ""},
1943               label: "_price".loc(), currencyDisabled: true,
1944               scale: XT.SALES_PRICE_SCALE},
1945             {kind: "XV.UnitPicker", name: "priceUnitPicker",
1946               attr: "priceUnit"},
1947             {kind: "XV.MoneyWidget", attr:
1948               {localValue: "extendedPrice", currency: ""},
1949               label: "_extendedPrice".loc(), currencyDisabled: true,
1950               scale: XT.EXTENDED_PRICE_SCALE}
1951           ]}
1952         ]},
1953         {kind: "XV.Groupbox", name: "detailsPanel", title: "_detail".loc(),
1954           components: [
1955           {kind: "onyx.GroupboxHeader", content: "_detail".loc()},
1956           {kind: "XV.ScrollableGroupbox", name: "detailGroup",
1957             classes: "in-panel", fit: true, components: [
1958             {kind: "XV.MoneyWidget", attr: {baseValue: "item.standardCost"},
1959               label: "_unitCost".loc(), isEditableProperty: "baseValue",
1960               currencyDisabled: true},
1961             {kind: "onyx.GroupboxHeader", content: "_tax".loc()},
1962             {kind: "XV.TaxTypePicker", attr: "taxType"},
1963             {kind: "XV.MoneyWidget", attr: {localValue: "taxTotal"},
1964               label: "_taxTotal".loc(), currencyDisabled: true},
1965             {kind: "onyx.GroupboxHeader", content: "_notes".loc()},
1966             {kind: "XV.TextArea", attr: "notes", fit: true}
1967           ]}
1968         ]},
1969         {kind: "XV.InvoiceLineTaxBox", attr: "taxes"}
1970       ]}
1971     ],
1972     create: function () {
1973       this.inherited(arguments);
1974       var effectiveKey = this.getEffectiveKey(),
1975         currencyKey = this.getCurrencyKey();
1976
1977       // Set currency and effective attributes on money widgets
1978       this.getComponents().forEach(function (ctl) {
1979         if (ctl.kind === "XV.MoneyWidget") {
1980           ctl.attr.currency = currencyKey;
1981           ctl.attr.effective = effectiveKey;
1982         }
1983       });
1984     }
1985   });
1986   // ..........................................................
1987   // SALES ORDER BASE
1988   //
1989
1990   /**
1991     This is the base kind for Quote and Sales order. This should include all common components
1992     and functions.
1993   */
1994   enyo.kind({
1995     name: "XV.SalesOrderBase",
1996     kind: "XV.Workspace",
1997     printOnSaveSetting: "DefaultPrintSOOnSave",
1998     headerAttrs: ["number", "-", "billtoName"],
1999     published: {
2000       effectiveKey: "orderDate"
2001     },
2002     handlers: {
2003       ontap: "copyBilltoToShipto"
2004     },
2005     create: function () {
2006       this.inherited(arguments);
2007
2008       var effectiveKey = this.getEffectiveKey(),
2009         settings = this.$.settingsGroup.children[0].children,
2010         last = settings[settings.length - 1];
2011
2012       this.getComponents().forEach(function (ctl) {
2013         if (ctl.kind === "XV.MoneyWidget") {
2014           // XXX #refactor -- what does this do?
2015           ctl.getAttr().effective = effectiveKey; // append this property onto the object
2016         }
2017       });
2018
2019       this.$.billtoAddress.$.buttonColumns.createComponent({
2020         kind: "onyx.Button",
2021         classes: "icon-copy",
2022         name: "copyAddressButton",
2023         ontap: "copyBilltoToShipto"
2024       });
2025
2026       // If this is the relationships header, and nothing was added by extensions
2027       // then just hide it.
2028       if (last instanceof onyx.GroupboxHeader) { last.hide(); }
2029     },
2030     customerChanged: function () {
2031       var customer = this.$.customerWidget.getValue(),
2032         id = customer ? customer.get("account") : -1;
2033
2034       this.$.billtoContact.addParameter({attribute: "account", value: id}, true);
2035       this.$.shiptoContact.addParameter({attribute: "account", value: id}, true);
2036       if (customer) {
2037         this.$.customerShiptoWidget.setDisabled(false);
2038         this.$.customerShiptoWidget.addParameter({
2039           attribute: "customer.number",
2040           value: customer.id
2041         });
2042         if (this.$.creditCardWidget) {
2043           this.$.creditCardWidget.addParameter({attribute: "customer", value: customer.id});
2044         }
2045       } else {
2046         this.$.customerShiptoWidget.setDisabled(true);
2047       }
2048     },
2049     attributesChanged: function () {
2050       this.inherited(arguments);
2051       var model = this.getValue(),
2052         customer = model ? model.get("customer") : false,
2053         isFreeFormShipto = customer ? customer.get("isFreeFormShipto") : true,
2054         button = this.$.billtoAddress.$.buttonColumns.$.copyAddressButton;
2055
2056       button.setDisabled(!isFreeFormShipto);
2057       this.customerChanged();
2058     },
2059     controlValueChanged: function (inSender, inEvent) {
2060       this.inherited(arguments);
2061       if (inEvent.originator.name === 'customerWidget') {
2062         this.customerChanged();
2063       }
2064     },
2065     copyBilltoToShipto: function (inSender, inEvent) {
2066       if (inEvent.originator.name === "copyAddressButton") {
2067         this.getValue().copyBilltoToShipto();
2068         return true;
2069       }
2070     }
2071   });
2072
2073   // ..........................................................
2074   // QUOTE
2075   //
2076   enyo.kind({
2077     name: "XV.QuoteWorkspace",
2078     kind: "XV.SalesOrderBase",
2079     title: "_quote".loc(),
2080     model: "XM.Quote",
2081     effectiveKey: "quoteDate",
2082     components: [
2083       {kind: "Panels", arrangerKind: "CarouselArranger",
2084         fit: true, components: [
2085         {kind: "XV.Groupbox", name: "mainPanel", components: [
2086           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2087           {kind: "XV.ScrollableGroupbox", name: "mainGroup", fit: true,
2088             classes: "in-panel", components: [
2089             {kind: "XV.InputWidget", attr: "number"},
2090             {kind: "XV.DateWidget", name: "dateField", attr: "quoteDate",
2091               label: "_quoteDate".loc()},
2092             {kind: "XV.DateWidget", attr: "scheduleDate"},
2093             {kind: "XV.DateWidget", attr: "expireDate"},
2094             {kind: "XV.InputWidget", attr: "formatStatus",
2095               label: "_status".loc()},
2096             {kind: "onyx.GroupboxHeader", content: "_billTo".loc()},
2097             {kind: "XV.CustomerProspectWidget", attr: "customer",
2098               name: "customerWidget", showAddress: true,
2099               label: "_customer".loc(), nameAttribute: ""
2100             },
2101             {kind: "XV.AddressFieldsWidget",
2102               name: "billtoAddress", attr:
2103               {name: "billtoName", line1: "billtoAddress1",
2104                 line2: "billtoAddress2", line3: "billtoAddress3",
2105                 city: "billtoCity", state: "billtoState",
2106                 postalCode: "billtoPostalCode", country: "billtoCountry"}
2107             },
2108             {kind: "XV.ContactWidget", attr: "billtoContact",
2109               name: "billtoContact"},
2110             {kind: "onyx.GroupboxHeader", content: "_shipTo".loc()},
2111             {kind: "XV.CustomerShiptoWidget", attr: "shipto",
2112               showAddress: true, label: "_number".loc(),
2113               nameAttribute: ""},
2114             {kind: "XV.AddressFieldsWidget",
2115               name: "shiptoAddress", disabled: true,
2116               attr: {name: "shiptoName", line1: "shiptoAddress1",
2117                 line2: "shiptoAddress2", line3: "shiptoAddress3",
2118                 city: "shiptoCity", state: "shiptoState",
2119                 postalCode: "shiptoPostalCode", country: "shiptoCountry"}
2120             },
2121             {kind: "XV.ContactWidget", attr: "shiptoContact",
2122               name: "shiptoContact"},
2123             {kind: "onyx.GroupboxHeader", content: "_orderNotes".loc()},
2124             {kind: "XV.TextArea", attr: "orderNotes", fit: true},
2125             {kind: "onyx.GroupboxHeader", content: "_shippingNotes".loc()},
2126             {kind: "XV.TextArea", attr: "shipNotes", fit: true}
2127           ]}
2128         ]},
2129         {kind: "XV.Groupbox", name: "settingsPanel", title: "_settings".loc(),
2130           components: [
2131           {kind: "onyx.GroupboxHeader", content: "_settings".loc()},
2132           {kind: "XV.ScrollableGroupbox", name: "settingsGroup", fit: true,
2133             classes: "in-panel", components: [
2134             {kind: "XV.BillingTermsPicker", attr: "terms"},
2135             {kind: "XV.SalesRepPicker", attr: "salesRep"},
2136             {kind: "XV.PercentWidget", attr: "commission"},
2137             {kind: "XV.TaxZonePicker", attr: "taxZone"},
2138             {kind: "XV.SaleTypePicker", attr: "saleType"},
2139             {kind: "onyx.GroupboxHeader", content: "_shipping".loc()},
2140             {kind: "XV.SitePicker", attr: "site"},
2141             {kind: "XV.DateWidget", attr: "packDate"},
2142             {kind: "XV.InputWidget", attr: "fob"},
2143             {kind: "XV.InputWidget", attr: "customerPurchaseOrderNumber",
2144              label: "_custPO".loc()},
2145             {kind: "XV.ShipViaCombobox", attr: "shipVia"},
2146             {kind: "XV.ShipZonePicker", attr: "shipZone"},
2147             {kind: "onyx.GroupboxHeader", content: "_relationships".loc()}
2148           ]}
2149         ]},
2150         {kind: "XV.QuoteCommentBox", attr: "comments"},
2151         {kind: "XV.QuoteDocumentsBox", attr: "documents"}
2152       ]}
2153     ],
2154     create: function () {
2155       this.inherited(arguments);
2156
2157       if (enyo.platform.touch) {
2158         this.$.panels.createComponents([
2159           {kind: "XV.QuoteLineItemBox", attr: "lineItems", title: "_lineItems".loc(),
2160             addBefore: this.$.settingsPanel, classes: "medium-panel"}
2161         ], {owner: this});
2162       } else {
2163         this.$.panels.createComponents([
2164           {kind: "XV.QuoteLineItemGridBox", attr: "lineItems", title: "_lineItems".loc(),
2165             addBefore: this.$.settingsPanel}
2166         ], {owner: this});
2167       }
2168     }
2169   });
2170
2171   XV.registerModelWorkspace("XM.QuoteRelation", "XV.QuoteWorkspace");
2172   XV.registerModelWorkspace("XM.QuoteListItem", "XV.QuoteWorkspace");
2173
2174   // ..........................................................
2175   // LINE ITEM
2176   //
2177   var lineItem = {
2178     kind: "XV.Workspace",
2179     modelAmnesty: true,
2180     handlers: {
2181       onBarcodeCapture: "handleBarcodeCapture"
2182     },
2183     components: [
2184       {kind: "Panels", arrangerKind: "CarouselArranger",
2185         fit: true, components: [
2186         {kind: "XV.Groupbox", name: "mainPanel", components: [
2187           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2188           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
2189             classes: "in-panel", fit: true, components: [
2190             {kind: "XV.NumberWidget", attr: "lineNumber"},
2191             {kind: "XV.ItemSiteWidget", attr: {item: "item", site: "site"},
2192               name: "itemSiteWidget",
2193               query: {parameters: [
2194               {attribute: "item.isSold", value: true},
2195               {attribute: "item.isActive", value: true},
2196               {attribute: "isSold", value: true},
2197               {attribute: "isActive", value: true}
2198             ]}},
2199             {kind: "XV.InputWidget", attr: "customerPartNumber"},
2200             {kind: "XV.QuantityWidget", attr: "quantity"},
2201             {kind: "XV.UnitPicker", name: "quantityUnitPicker",
2202               attr: "quantityUnit"},
2203             {kind: "XV.PercentWidget", name: "discount", attr: "discount"},
2204             {kind: "XV.MoneyWidget", attr:
2205               {localValue: "price", currency: ""},
2206               label: "_price".loc(), currencyDisabled: true,
2207               scale: XT.SALES_PRICE_SCALE},
2208             {kind: "XV.UnitPicker", name: "priceUnitPicker",
2209               attr: "priceUnit"},
2210             {kind: "XV.MoneyWidget", attr:
2211               {localValue: "extendedPrice", currency: ""},
2212               label: "_extendedPrice".loc(), currencyDisabled: true,
2213               scale: XT.EXTENDED_PRICE_SCALE},
2214             {kind: "onyx.GroupboxHeader", content: "_delivery".loc()},
2215             {kind: "XV.DateWidget", attr: "scheduleDate"},
2216             {kind: "XV.DateWidget", attr: "promiseDate", showing: false,
2217               name: "promiseDate"},
2218             {kind: "XV.PurchaseOrderLineCharacteristicsWidget",
2219               attr: "characteristics"}
2220           ]}
2221         ]},
2222         {kind: "XV.Groupbox", name: "detailsPanel", title: "_detail".loc(),
2223           components: [
2224           {kind: "onyx.GroupboxHeader", content: "_detail".loc()},
2225           {kind: "XV.ScrollableGroupbox", name: "detailGroup",
2226             classes: "in-panel", fit: true, components: [
2227             {kind: "XV.MoneyWidget", attr: {baseValue: "unitCost"},
2228               label: "_unitCost".loc(), isEditableProperty: "baseValue",
2229               currencyDisabled: true},
2230             {kind: "XV.MoneyWidget", attr: {baseValue: "listPrice"},
2231               label: "_listPrice".loc(), scale: XT.SALES_PRICE_SCALE,
2232               isEditableProperty: "baseValue", currencyDisabled: true},
2233             {kind: "XV.MoneyWidget", attr: {localValue: "customerPrice"},
2234               label: "_customerPrice".loc(), scale: XT.SALES_PRICE_SCALE,
2235               currencyDisabled: true},
2236             {kind: "XV.PercentWidget", attr: "listPriceDiscount"},
2237             {kind: "XV.PercentWidget", attr: "markup"},
2238             {kind: "XV.MoneyWidget", attr: {localValue: "margin"},
2239               label: "_margin".loc(), scale: XT.EXTENDED_PRICE_SCALE,
2240               currencyDisabled: true},
2241             {kind: "onyx.GroupboxHeader", content: "_tax".loc()},
2242             {kind: "XV.TaxTypePicker", attr: "taxType"},
2243             {kind: "XV.MoneyWidget", attr: {localValue: "tax"},
2244               label: "_tax".loc(), currencyDisabled: true},
2245             {kind: "onyx.GroupboxHeader", content: "_notes".loc()},
2246             {kind: "XV.TextArea", attr: "notes", fit: true}
2247           ]}
2248         ]}
2249       ]}
2250     ],
2251     create: function () {
2252       this.inherited(arguments);
2253       var effectiveKey = this.getEffectiveKey(),
2254         currencyKey = this.getCurrencyKey(),
2255         comments = this.getCommentBox();
2256
2257       // Show/Hide promise date
2258       this.$.promiseDate.setShowing(XT.session.settings.get("UsePromiseDate"));
2259
2260       // Set currency and effective attributes on money widgets
2261       this.getComponents().forEach(function (ctl) {
2262         if (ctl.kind === "XV.MoneyWidget") {
2263           ctl.attr.currency = currencyKey;
2264           ctl.attr.effective = effectiveKey;
2265         }
2266       });
2267
2268       // Add the Comment Box to Panels
2269       this.$.panels.createComponents([comments], {owner: this});
2270     },
2271     handleBarcodeCapture: function (inSender, inEvent) {
2272       this.$.itemSiteWidget.$.privateItemSiteWidget.$.input.setValue(inEvent.data);
2273       this.$.itemSiteWidget.$.privateItemSiteWidget.autocomplete();
2274     }
2275   };
2276   enyo.mixin(lineItem, XV.LineMixin);
2277
2278   // ..........................................................
2279   // QUOTE LINE ITEM
2280   //
2281   var quoteLineItem = {
2282     name: "XV.QuoteLineWorkspace",
2283     title: "_quoteLine".loc(),
2284     model: "XM.QuoteLine",
2285     published: {
2286       currencyKey: "quote.currency",
2287       effectiveKey: "quote.quoteDate",
2288       commentBox: {kind: "XV.QuoteLineCommentBox", attr: "comments"}
2289     }
2290   };
2291   enyo.mixin(quoteLineItem, XV.QuoteLineMixin);
2292   enyo.mixin(quoteLineItem, lineItem);
2293   enyo.kind(quoteLineItem);
2294
2295   // ..........................................................
2296   // SALES ORDER LINE ITEM
2297   //
2298   var salesOrderLineItem = {
2299     name: "XV.SalesOrderLineWorkspace",
2300     title: "_salesOrderLine".loc(),
2301     model: "XM.SalesOrderLine",
2302     published: {
2303       currencyKey: "salesOrder.currency",
2304       effectiveKey: "salesOrder.orderDate",
2305       commentBox: {kind: "XV.SalesOrderLineCommentBox", attr: "comments"}
2306     }
2307   };
2308   _.extend(salesOrderLineItem, XV.SalesOrderLineMixin, lineItem, {
2309     destroy: function () {
2310       this.bind("off");
2311       this.inherited(arguments);
2312     }
2313   });
2314
2315   enyo.kind(salesOrderLineItem);
2316
2317   // ..........................................................
2318   // SALES ORDER
2319   //
2320
2321   enyo.kind({
2322     name: "XV.SalesOrderWorkspace",
2323     kind: "XV.SalesOrderBase",
2324     title: "_salesOrder".loc(),
2325     handlers: {
2326       onMagstripeCapture: "handleMagstripeCapture",
2327       onPaymentPosted: 'handlePaymentPosted',
2328     },
2329     model: "XM.SalesOrder",
2330     printOnSaveSetting: "DefaultPrintPOOnSave",
2331     actions: [{
2332       name: "print",
2333       isViewMethod: true,
2334       label: "_print".loc(),
2335       privilege: "ViewSalesOrders",
2336       prerequisite: "isReadyClean"
2337     },
2338     {name: "email",
2339       isViewMethod: true,
2340       label: "_email".loc(),
2341       privilege: "ViewSalesOrders",
2342       prerequisite: "isReadyClean"
2343     }],
2344     components: [
2345       {kind: "Panels", arrangerKind: "CarouselArranger",
2346         fit: true, components: [
2347         {kind: "XV.Groupbox", name: "mainPanel", components: [
2348           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2349           {kind: "XV.ScrollableGroupbox", name: "mainGroup", fit: true,
2350             classes: "in-panel", components: [
2351             {kind: "XV.InputWidget", attr: "number"},
2352             {kind: "XV.DateWidget", name: "dateField", attr: "orderDate",
2353               label: "_orderDate".loc()},
2354             {kind: "XV.DateWidget", attr: "scheduleDate"},
2355             {kind: "XV.DateWidget", attr: "packDate"},
2356             {kind: "XV.InputWidget", attr: "formatStatus",
2357               label: "_status".loc()},
2358             {kind: "XV.CheckboxWidget", attr: "printOnSaveSetting",
2359               label: "_printOnSave".loc()},
2360             {kind: "onyx.GroupboxHeader", content: "_billTo".loc()},
2361             {kind: "XV.SalesCustomerWidget", attr: "customer",
2362                name: "customerWidget", showAddress: true,
2363                label: "_customer".loc(), nameAttribute: ""
2364             },
2365             {kind: "XV.AddressFieldsWidget",
2366               name: "billtoAddress", attr:
2367               {name: "billtoName", line1: "billtoAddress1",
2368                 line2: "billtoAddress2", line3: "billtoAddress3",
2369                 city: "billtoCity", state: "billtoState",
2370                 postalCode: "billtoPostalCode", country: "billtoCountry"}
2371             },
2372             {kind: "XV.ContactWidget", attr: "billtoContact",
2373               name: "billtoContact"},
2374             {kind: "onyx.GroupboxHeader", content: "_shipTo".loc()},
2375             {kind: "XV.CustomerShiptoWidget", attr: "shipto",
2376               showAddress: true, label: "_number".loc(),
2377               nameAttribute: ""},
2378             {kind: "XV.AddressFieldsWidget",
2379               name: "shiptoAddress",
2380               disabled: true,
2381               attr: {name: "shiptoName", line1: "shiptoAddress1",
2382                 line2: "shiptoAddress2", line3: "shiptoAddress3",
2383                 city: "shiptoCity", state: "shiptoState",
2384                 postalCode: "shiptoPostalCode", country: "shiptoCountry"}
2385             },
2386             {kind: "XV.ContactWidget", attr: "shiptoContact",
2387               name: "shiptoContact"},
2388             {kind: "XV.SalesOrderCharacteristicsWidget", attr: "characteristics"},
2389             {kind: "onyx.GroupboxHeader", content: "_orderNotes".loc()},
2390             {kind: "XV.TextArea", attr: "orderNotes", fit: true},
2391             {kind: "onyx.GroupboxHeader", content: "_shippingNotes".loc()},
2392             {kind: "XV.TextArea", attr: "shipNotes", fit: true}
2393           ]}
2394         ]},
2395         {kind: "XV.Groupbox", name: "settingsPanel", title: "_settings".loc(),
2396           components: [
2397           {kind: "onyx.GroupboxHeader", content: "_settings".loc()},
2398           {kind: "XV.ScrollableGroupbox", name: "settingsGroup", fit: true,
2399             classes: "in-panel", components: [
2400             {kind: "XV.BillingTermsPicker", attr: "terms"},
2401             {kind: "XV.SalesRepPicker", attr: "salesRep"},
2402             {kind: "XV.PercentWidget", attr: "commission"},
2403             {kind: "XV.TaxZonePicker", attr: "taxZone"},
2404             {kind: "XV.SaleTypePicker", attr: "saleType"},
2405             {kind: "XV.HoldTypePicker", attr: "holdType"},
2406             {kind: "onyx.GroupboxHeader", content: "_shipping".loc()},
2407             {kind: "XV.CheckboxWidget", attr: "shipComplete"},
2408             {kind: "XV.HeavyweightSitePicker", attr: "site"},
2409             {kind: "XV.InputWidget", attr: "fob"},
2410             {kind: "XV.InputWidget", attr: "customerPurchaseOrderNumber",
2411              label: "_custPO".loc()},
2412             {kind: "XV.ShipViaCombobox", attr: "shipVia"},
2413             {kind: "XV.ShipZonePicker", attr: "shipZone"},
2414             {kind: "XV.ShippingChargePicker", attr: "shipCharge"},
2415             {kind: "onyx.GroupboxHeader", content: "_relationships".loc()}
2416           ]}
2417         ]},
2418         {kind: "XV.SalesOrderCommentBox", name: "salesOrderCommentBox",
2419           attr: "comments"},
2420         {kind: "XV.SalesOrderDocumentsBox", attr: "documents"}
2421       ]}
2422     ],
2423     /**
2424      * When the printOnSaveSetting checkbox is changed,
2425      * also change the workspace setting.
2426      */
2427     controlValueChanged: function (inSender, inEvent) {
2428       this.inherited(arguments);
2429       if (inEvent.originator.attr === 'printOnSaveSetting') {
2430         this.printOnSaveSetting = inEvent.originator.value;
2431       }
2432     },
2433     /**
2434      * @listens onPaymentPosted
2435      */
2436     handlePaymentPosted: function (inSender, inEvent) {
2437       this.requery();
2438     },
2439
2440     valueChanged: function () {
2441       this.inherited(arguments);
2442       if (this.$.salesOrderPaymentBox && this.value) {
2443         this.$.salesOrderPaymentBox.setSalesOrder(this.value);
2444       }
2445     },
2446     create: function () {
2447       this.inherited(arguments);
2448
2449       if (XV.SalesOrderPaymentBox && XT.session.privileges.get('PostCashReceipts')) {
2450         this.$.panels.createComponent({kind: "XV.SalesOrderPaymentBox", title: "_payment".loc(), addBefore: this.$.salesOrderCommentBox},
2451           {owner: this});
2452         if (this.value) {
2453           this.$.salesOrderPaymentBox.setSalesOrder(this.value);
2454         }
2455       }
2456
2457       if (XT.session.privileges.get("ProcessCreditCards") &&
2458           XT.session.settings.get("CCCompany") === "Authorize.Net") {
2459         this.$.panels.createComponent(
2460           {kind: "XV.CreditCardBox", name: "creditCardBox", attr: "customer.creditCards",
2461             addBefore: this.$.salesOrderCommentBox},
2462           {owner: this}
2463         );
2464
2465         // XXX altering this line will break the New button. if I add this to
2466         // paymentPanel, I get 'object has no method getValue' when I click
2467         // 'New' -tjw
2468         this.$.creditCardBox.parent.parent = this;
2469       }
2470
2471       if (enyo.platform.touch) {
2472         this.$.panels.createComponents([
2473           {kind: "XV.SalesOrderLineItemBox", name: "salesOrderLineItemBox",
2474             attr: "lineItems", addBefore: this.$.settingsPanel, classes: "medium-panel"},
2475         ], {owner: this});
2476         this.$.panels.createComponents([
2477           {kind: "XV.SalesOrderWorkflowBox", attr: "workflow", title: "_workflow".loc(),
2478             addBefore: this.$.salesOrderCommentBox, classes: "medium-panel"}
2479         ], {owner: this});
2480       } else {
2481         this.$.panels.createComponents([
2482           {kind: "XV.SalesOrderLineItemGridBox", name: "salesOrderLineItemBox",
2483             attr: "lineItems", addBefore: this.$.settingsPanel},
2484         ], {owner: this});
2485         this.$.panels.createComponents([
2486           {kind: "XV.SalesOrderWorkflowGridBox", attr: "workflow", title: "_workflow".loc(),
2487             addBefore: this.$.salesOrderCommentBox}
2488         ], {owner: this});
2489       }
2490     },
2491     handleHotKey: function (keyCode) {
2492       switch (String.fromCharCode(keyCode)) {
2493       case "L":
2494         if (!this.$.salesOrderLineItemGridBox.disabled) {
2495           this.$.salesOrderLineItemGridBox.newItem();
2496         }
2497         return;
2498       }
2499     },
2500     handleMagstripeCapture: function (inSender, inEvent) {
2501       if (this.$.creditCardBox && !this.$.creditCardBox.$.newButton.disabled) { // XXX sloppy
2502         this.$.salesPanels.setIndex(this.$.salesPanels.getPanels().length);
2503         this.$.creditCardBox.newItemWithData(inEvent.data);
2504       }
2505     },
2506     /**
2507       Reset the grid-box back to its read-only state in the case of "apply"
2508      */
2509     save: function (options) {
2510       this.inherited(arguments);
2511       var gridBox = this.$.salesOrderLineItemGridBox;
2512       // XXX hack to prevent screen from hanging in the case of invalid data
2513       if (gridBox && !XT.app.$.postbooks.$.notifyPopup.getShowing()) {
2514         gridBox.setEditableIndex(null);
2515         gridBox.valueChanged();
2516         gridBox.$.editableGridRow.setShowing(false);
2517       }
2518     }
2519   });
2520
2521   XV.registerModelWorkspace("XM.SalesOrder", "XV.SalesOrderWorkspace");
2522   XV.registerModelWorkspace("XM.SalesOrderWorkflow", "XV.SalesOrderWorkspace");
2523   XV.registerModelWorkspace("XM.SalesOrderRelation", "XV.SalesOrderWorkspace");
2524   XV.registerModelWorkspace("XM.SalesOrderListItem", "XV.SalesOrderWorkspace");
2525
2526   // ..........................................................
2527   // SALES ORDER WORKFLOW
2528   //
2529
2530   enyo.kind({
2531     name: "XV.SalesOrderWorkflowWorkspace",
2532     kind: "XV.ChildWorkspace",
2533     title: "_salesOrderWorkflow".loc(),
2534     model: "XM.SalesOrderWorkflow",
2535     components: [
2536       {kind: "Panels", arrangerKind: "CarouselArranger",
2537         classes: "xv-top-panel", fit: true, components: [
2538         {kind: "XV.Groupbox", name: "mainPanel", components: [
2539           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2540           {kind: "XV.ScrollableGroupbox", name: "mainGroup", fit: true,
2541             classes: "in-panel", components: [
2542             {kind: "XV.InputWidget", attr: "name"},
2543             {kind: "XV.InputWidget", attr: "description"},
2544             {kind: "XV.SalesOrderWorkflowTypePicker", attr: "workflowType"},
2545             {kind: "XV.WorkflowStatusPicker", attr: "status"},
2546             {kind: "XV.PriorityPicker", attr: "priority", showNone: false},
2547             {kind: "XV.NumberSpinnerWidget", attr: "sequence"},
2548             {kind: "onyx.GroupboxHeader", content: "_schedule".loc()},
2549             {kind: "XV.DateWidget", attr: "dueDate"},
2550             {kind: "XV.DateWidget", attr: "startDate"},
2551             {kind: "XV.DateWidget", attr: "assignDate"},
2552             {kind: "XV.DateWidget", attr: "completeDate"},
2553             {kind: "onyx.GroupboxHeader", content: "_userAccounts".loc()},
2554             {kind: "XV.UserAccountWidget", attr: "owner"},
2555             {kind: "XV.UserAccountWidget", attr: "assignedTo"},
2556             {kind: "onyx.GroupboxHeader", content: "_notes".loc()},
2557             {kind: "XV.TextArea", attr: "notes", fit: true}
2558           ]}
2559         ]},
2560         {kind: "XV.Groupbox", name: "onCompletedPanel", title: "_completionActions".loc(),
2561           components: [
2562           {kind: "onyx.GroupboxHeader", content: "_onCompletion".loc()},
2563           {kind: "XV.ScrollableGroupbox", name: "completionGroup", fit: true,
2564             classes: "in-panel", components: [
2565             {kind: "XV.CreditStatusPicker", attr: "completedParentStatus",
2566               noneText: "_noChange".loc(), label: "_nextStatus".loc()},
2567             {kind: "XV.DependenciesWidget",
2568               attr: {workflow: "parent.workflow", successors: "completedSuccessors"}}
2569           ]}
2570         ]},
2571         {kind: "XV.Groupbox", name: "onDeferredPanel", title: "_deferredActions".loc(),
2572           components: [
2573           {kind: "onyx.GroupboxHeader", content: "_onDeferred".loc()},
2574           {kind: "XV.ScrollableGroupbox", name: "deferredGroup", fit: true,
2575             classes: "in-panel", components: [
2576             {kind: "XV.CreditStatusPicker", attr: "deferredParentStatus",
2577               noneText: "_noChange".loc(), label: "_nextStatus".loc()},
2578             {kind: "XV.DependenciesWidget",
2579               attr: {workflow: "parent.workflow", successors: "deferredSuccessors"}}
2580           ]}
2581         ]}
2582       ]}
2583     ]
2584   });
2585   // ..........................................................
2586   // REASON CODE
2587   //
2588
2589   enyo.kind({
2590     name: "XV.ReasonCodeWorkspace",
2591     kind: "XV.Workspace",
2592     title: "_reasonCode".loc(),
2593     model: "XM.ReasonCode",
2594     components: [
2595       {kind: "Panels", arrangerKind: "CarouselArranger",
2596         fit: true, components: [
2597         {kind: "XV.Groupbox", name: "mainPanel", components: [
2598           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2599           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
2600             classes: "in-panel", components: [
2601             {kind: "XV.InputWidget", attr: "code"},
2602             {kind: "XV.InputWidget", attr: "description"},
2603             {kind: "XV.ReasonCodeDocumentTypePicker", attr: "documentType"}
2604           ]}
2605         ]}
2606       ]}
2607     ]
2608   });
2609
2610   XV.registerModelWorkspace("XM.ReasonCode", "XV.ReasonCodeWorkspace");
2611
2612   // ..........................................................
2613   // SALES EMAIL PROFILE
2614   //
2615
2616   enyo.kind({
2617     name: "XV.SalesEmailProfileWorkspace",
2618     kind: "XV.EmailProfileWorkspace",
2619     title: "_salesEmailProfile".loc(),
2620     model: "XM.SalesEmailProfile",
2621   });
2622
2623   XV.registerModelWorkspace("XM.SalesEmailProfile", "XV.SalesEmailProfileWorkspace");
2624
2625   // ..........................................................
2626   // SALES REP
2627   //
2628
2629   enyo.kind({
2630     name: "XV.SalesRepWorkspace",
2631     kind: "XV.AccountDocumentWorkspace",
2632     title: "_salesRep".loc(),
2633     model: "XM.SalesRep",
2634     components: [
2635       {kind: "Panels", arrangerKind: "CarouselArranger",
2636         fit: true, components: [
2637         {kind: "XV.Groupbox", name: "mainPanel", components: [
2638           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2639           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
2640             classes: "in-panel", components: [
2641             {kind: "XV.InputWidget", attr: "number"},
2642             {kind: "XV.CheckboxWidget", attr: "isActive"},
2643             {kind: "XV.InputWidget", attr: "name"},
2644             {kind: "XV.PercentWidget", attr: "commission"}
2645           ]}
2646         ]}
2647       ]},
2648       {kind: "onyx.Popup", name: "findExistingAccountPopup", centered: true,
2649         modal: true, floating: true, scrim: true, onShow: "popupShown",
2650         onHide: "popupHidden", components: [
2651         {content: "_accountExists".loc()},
2652         {name: "whatToDo", content: "_convertAccountSalesRep".loc()},
2653         {tag: "br"},
2654         {kind: "onyx.Button", name: "convert", content: "_ok".loc(), ontap: "accountConvert",
2655           classes: "onyx-blue xv-popup-button"},
2656         {kind: "onyx.Button", name: "cancel", content: "_cancel".loc(), ontap: "accountCancel",
2657           classes: "xv-popup-button"}
2658       ]}
2659     ]
2660   });
2661
2662   XV.registerModelWorkspace("XM.SalesRep", "XV.SalesRepWorkspace");
2663
2664   // ..........................................................
2665   // SALE TYPE
2666   //
2667
2668   enyo.kind({
2669     name: "XV.SaleTypeWorkspace",
2670     kind: "XV.Workspace",
2671     title: "_saleType".loc(),
2672     model: "XM.SaleType",
2673     components: [
2674       {kind: "Panels", arrangerKind: "CarouselArranger",
2675         fit: true, components: [
2676         {kind: "XV.Groupbox", name: "mainPanel", components: [
2677           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2678           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
2679             classes: "in-panel", components: [
2680             {kind: "XV.InputWidget", attr: "code"},
2681             {kind: "XV.InputWidget", attr: "description"},
2682             {kind: "XV.SalesEmailProfilePicker", attr: "emailProfile"},
2683             {kind: "XV.HoldTypePicker", attr: "defaultHoldType"},
2684             {kind: "XV.SaleTypeCharacteristicsWidget", attr: "characteristics"}
2685           ]}
2686         ]},
2687         {kind: "XV.SaleTypeWorkflowBox", attr: "workflow"}
2688       ]}
2689     ]
2690   });
2691
2692   XV.registerModelWorkspace("XM.SaleType", "XV.SaleTypeWorkspace");
2693
2694   // ..........................................................
2695   // SHIFT
2696   //
2697
2698   enyo.kind({
2699     name: "XV.ShiftWorkspace",
2700     kind: "XV.Workspace",
2701     title: "_shift".loc(),
2702     model: "XM.Shift",
2703     components: [
2704       {kind: "Panels", arrangerKind: "CarouselArranger",
2705         fit: true, components: [
2706         {kind: "XV.Groupbox", name: "mainPanel", components: [
2707           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2708           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
2709             classes: "in-panel", components: [
2710             {kind: "XV.InputWidget", attr: "number"},
2711             {kind: "XV.InputWidget", attr: "name"}
2712           ]}
2713         ]}
2714       ]}
2715     ]
2716   });
2717
2718   XV.registerModelWorkspace("XM.Shift", "XV.ShiftWorkspace");
2719
2720   // ..........................................................
2721   // SHIP VIA
2722   //
2723
2724   enyo.kind({
2725     name: "XV.ShipViaWorkspace",
2726     kind: "XV.Workspace",
2727     title: "_shipVia".loc(),
2728     model: "XM.ShipVia",
2729     components: [
2730       {kind: "Panels", arrangerKind: "CarouselArranger",
2731         fit: true, components: [
2732         {kind: "XV.Groupbox", name: "mainPanel", components: [
2733           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2734           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
2735             classes: "in-panel", components: [
2736             {kind: "XV.InputWidget", attr: "code"},
2737             {kind: "XV.InputWidget", attr: "description"}
2738           ]}
2739         ]}
2740       ]}
2741     ]
2742   });
2743
2744   XV.registerModelWorkspace("XM.ShipVia", "XV.ShipViaWorkspace");
2745
2746   // ..........................................................
2747   // SHIP ZONE
2748   //
2749
2750   enyo.kind({
2751     name: "XV.ShipZoneWorkspace",
2752     kind: "XV.Workspace",
2753     title: "_shipZone".loc(),
2754     model: "XM.ShipZone",
2755     components: [
2756       {kind: "Panels", arrangerKind: "CarouselArranger",
2757         fit: true, components: [
2758         {kind: "XV.Groupbox", name: "mainPanel", components: [
2759           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2760           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
2761             classes: "in-panel", components: [
2762             {kind: "XV.InputWidget", attr: "name"},
2763             {kind: "XV.InputWidget", attr: "description"}
2764           ]}
2765         ]}
2766       ]}
2767     ]
2768   });
2769
2770   XV.registerModelWorkspace("XM.ShipZone", "XV.ShipZoneWorkspace");
2771
2772   // ..........................................................
2773   // SITE
2774   //
2775
2776   enyo.kind({
2777     name: "XV.SiteWorkspace",
2778     kind: "XV.Workspace",
2779     title: "_site".loc(),
2780     model: "XM.Site",
2781     components: [
2782       {kind: "Panels", arrangerKind: "CarouselArranger",
2783         fit: true, components: [
2784         {kind: "XV.Groupbox", name: "mainPanel", components: [
2785           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2786           {kind: "XV.ScrollableGroupbox", name: "mainGroup", fit: true,
2787             classes: "in-panel", components: [
2788             {kind: "XV.InputWidget", attr: "code"},
2789             {kind: "XV.CheckboxWidget", attr: "isActive"},
2790             {kind: "XV.SiteTypePicker", attr: "siteType"},
2791             {kind: "XV.InputWidget", attr: "description"},
2792             {kind: "XV.ContactWidget", attr: "contact"},
2793             {kind: "XV.AddressWidget", attr: "address"},
2794             {kind: "XV.TaxZonePicker", attr: "taxZone"},
2795             {kind: "XV.InputWidget", attr: "incoterms"},
2796             {kind: "onyx.GroupboxHeader", content: "_notes".loc()},
2797             {kind: "XV.TextArea", attr: "notes", fit: true}
2798           ]}
2799         ]},
2800         {kind: "XV.SiteCommentBox", attr: "comments"}
2801       ]}
2802     ]
2803   });
2804
2805   XV.registerModelWorkspace("XM.SiteRelation", "XV.SiteWorkspace");
2806   XV.registerModelWorkspace("XM.SiteListItem", "XV.SiteWorkspace");
2807
2808   // ..........................................................
2809   // SITE TYPE
2810   //
2811
2812   enyo.kind({
2813     name: "XV.SiteTypeWorkspace",
2814     kind: "XV.Workspace",
2815     title: "_siteType".loc(),
2816     model: "XM.SiteType"
2817   });
2818
2819   XV.registerModelWorkspace("XM.SiteType", "XV.SiteTypeWorkspace");
2820
2821   // ..........................................................
2822   // STATE
2823   //
2824
2825   enyo.kind({
2826     name: "XV.StateWorkspace",
2827     kind: "XV.Workspace",
2828     title: "_state".loc(),
2829     model: "XM.State",
2830     components: [
2831       {kind: "Panels", arrangerKind: "CarouselArranger",
2832         fit: true, components: [
2833         {kind: "XV.Groupbox", name: "mainPanel", components: [
2834           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2835           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
2836             classes: "in-panel", components: [
2837             {kind: "XV.InputWidget", attr: "abbreviation"},
2838             {kind: "XV.InputWidget", attr: "name"},
2839             {kind: "XV.CountryPicker", attr: "country"}
2840           ]}
2841         ]}
2842       ]}
2843     ]
2844   });
2845
2846   XV.registerModelWorkspace("XM.State", "XV.StateWorkspace");
2847
2848   // ..........................................................
2849   // TAX ASSIGNMENT
2850   //
2851
2852   enyo.kind({
2853     name: "XV.TaxAssignmentWorkspace",
2854     kind: "XV.Workspace",
2855     title: "_taxAssignment".loc(),
2856     model: "XM.TaxAssignment",
2857     components: [
2858       {kind: "Panels", arrangerKind: "CarouselArranger",
2859         fit: true, components: [
2860         {kind: "XV.Groupbox", name: "mainPanel", components: [
2861           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2862           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
2863             classes: "in-panel", components: [
2864               {kind: "XV.TaxCodePicker", label: "_taxCode".loc(), attr: "tax"},
2865               {kind: "XV.TaxZonePicker", label: "_taxZone".loc(), attr: "taxZone"},
2866               {kind: "XV.TaxTypePicker", label: "_taxType".loc(), attr: "taxType"}
2867             ]}
2868           ]}
2869         ]}
2870       ]
2871     });
2872
2873   XV.registerModelWorkspace("XM.TaxAssignment", "XV.TaxAssignmentWorkspace");
2874
2875   // ..........................................................
2876   // TAX AUTHORITY
2877   //
2878
2879   hash = {
2880     name: "XV.TaxAuthorityWorkspace",
2881     kind: "XV.AccountDocumentWorkspace",
2882     title: "_taxAuthority".loc(),
2883     model: "XM.TaxAuthority",
2884     headerAttrs: ["code", "-", "name"],
2885     handlers: {
2886       onError: "errorNotify"
2887     },
2888     components: [
2889       {kind: "Panels", arrangerKind: "CarouselArranger",
2890         fit: true, components: [
2891         {kind: "XV.Groupbox", name: "mainPanel", components: [
2892           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2893           {kind: "XV.ScrollableGroupbox", name: "mainGroup", fit: true,
2894             classes: "in-panel", components: [
2895             {kind: "XV.InputWidget", attr: "code"},
2896             {kind: "XV.InputWidget", attr: "name"},
2897             {kind: "XV.InputWidget", attr: "externalReference"},
2898             {kind: "XV.CurrencyPicker", attr: "currency"},
2899             {kind: "XV.InputWidget", attr: "county"},
2900             {kind: "onyx.GroupboxHeader", content: "_address".loc()},
2901             {kind: "XV.AddressWidget", attr: "address"}
2902           ]}
2903         ]}
2904       ]},
2905       {kind: "onyx.Popup", name: "findExistingAccountPopup", centered: true,
2906         modal: true, floating: true, scrim: true, onShow: "popupShown",
2907         onHide: "popupHidden", components: [
2908         {content: "_accountExists".loc()},
2909         {name: "whatToDo", content: "_convertAccountTaxAuthority".loc()},
2910         {tag: "br"},
2911         {kind: "onyx.Button", name: "convert", content: "_ok".loc(), ontap: "accountConvert",
2912           classes: "onyx-blue xv-popup-button"},
2913         {kind: "onyx.Button", name: "cancel", content: "_cancel".loc(), ontap: "accountCancel",
2914           classes: "xv-popup-button"}
2915       ]}
2916     ]
2917   };
2918
2919   hash = enyo.mixin(hash, XV.WorkspaceAddressMixin);
2920   enyo.kind(hash);
2921
2922   XV.registerModelWorkspace("XM.TaxAuthority", "XV.TaxAuthorityWorkspace");
2923   XV.registerModelWorkspace("XM.TaxAuthorityRelation", "XV.TaxAuthorityWorkspace");
2924
2925   // ..........................................................
2926   // TAX CODE
2927   //
2928
2929   enyo.kind({
2930     name: "XV.TaxCodeWorkspace",
2931     kind: "XV.Workspace",
2932     title: "_taxCode".loc(),
2933     model: "XM.TaxCode",
2934     components: [
2935       {kind: "Panels", arrangerKind: "CarouselArranger",
2936         fit: true, components: [
2937         {kind: "XV.Groupbox", name: "mainPanel", components: [
2938           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2939           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
2940             classes: "in-panel", components: [
2941             {kind: "XV.InputWidget", attr: "code"},
2942             {kind: "XV.InputWidget", attr: "description"},
2943             {kind: "XV.TaxClassPicker", attr: "class", label: "_taxClass".loc()},
2944             {kind: "XV.TaxAuthorityPicker", attr: "authority", label: "_taxAuthority".loc()},
2945             {kind: "XV.TaxCodePicker", attr: "basis"}
2946           ]}
2947         ]}
2948       ]}
2949     ]
2950   });
2951
2952   XV.registerModelWorkspace("XM.TaxCode", "XV.TaxCodeWorkspace");
2953
2954   // ..........................................................
2955   // TAX CLASS
2956   //
2957
2958   enyo.kind({
2959     name: "XV.TaxClassWorkspace",
2960     kind: "XV.Workspace",
2961     title: "_taxClass".loc(),
2962     model: "XM.TaxClass",
2963     components: [
2964       {kind: "Panels", arrangerKind: "CarouselArranger",
2965         fit: true, components: [
2966         {kind: "XV.Groupbox", name: "mainPanel", components: [
2967           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2968           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
2969             classes: "in-panel", components: [
2970             {kind: "XV.InputWidget", attr: "code"},
2971             {kind: "XV.InputWidget", attr: "description"},
2972             {kind: "XV.NumberWidget", attr: "sequence"}
2973           ]}
2974         ]}
2975       ]}
2976     ]
2977   });
2978
2979   XV.registerModelWorkspace("XM.TaxClass", "XV.TaxClassWorkspace");
2980
2981   // ..........................................................
2982   // TAX RATE
2983   //
2984
2985   enyo.kind({
2986     name: "XV.TaxRateWorkspace",
2987     kind: "XV.Workspace",
2988     title: "_taxRate".loc(),
2989     model: "XM.TaxRate",
2990     components: [
2991       {kind: "Panels", arrangerKind: "CarouselArranger",
2992         fit: true, components: [
2993         {kind: "XV.Groupbox", name: "mainPanel", components: [
2994           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2995           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
2996             classes: "in-panel", components: [
2997               {kind: "XV.TaxCodePicker", label: "_taxCode".loc(), attr: "tax"},
2998               {kind: "XV.PercentWidget", label: "_percent".loc(), attr: "percent"},
2999               {kind: "XV.MoneyWidget", attr: {localValue: "amount", currency: "currency",
3000                 effective: "effectiveDate"}, label: "_amount".loc()},
3001               {kind: "XV.DateWidget", label: "_effective".loc(), attr: "effectiveDate"},
3002               {kind: "XV.DateWidget", label: "_expires".loc(), attr: "expirationDate"}
3003             ]}
3004           ]}
3005         ]}
3006       ]
3007     });
3008
3009   XV.registerModelWorkspace("XM.TaxRate", "XV.TaxRateWorkspace");
3010
3011   // ..........................................................
3012   // TAX TYPE
3013   //
3014
3015   enyo.kind({
3016     name: "XV.TaxTypeWorkspace",
3017     kind: "XV.Workspace",
3018     title: "_taxType".loc(),
3019     model: "XM.TaxType",
3020     components: [
3021       {kind: "Panels", arrangerKind: "CarouselArranger",
3022         fit: true, components: [
3023         {kind: "XV.Groupbox", name: "mainPanel", components: [
3024           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
3025           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
3026             classes: "in-panel", components: [
3027             {kind: "XV.InputWidget", attr: "name"},
3028             {kind: "XV.InputWidget", attr: "description"},
3029             {kind: "XV.CheckboxWidget", attr: "isSystem"}
3030           ]}
3031         ]}
3032       ]}
3033     ]
3034   });
3035
3036   XV.registerModelWorkspace("XM.TaxType", "XV.TaxTypeWorkspace");
3037
3038   // ..........................................................
3039   // TAX ZONE
3040   //
3041
3042   enyo.kind({
3043     name: "XV.TaxZoneWorkspace",
3044     kind: "XV.Workspace",
3045     title: "_taxZone".loc(),
3046     model: "XM.TaxZone",
3047     components: [
3048       {kind: "Panels", arrangerKind: "CarouselArranger",
3049         fit: true, components: [
3050         {kind: "XV.Groupbox", name: "mainPanel", components: [
3051           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
3052           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
3053             classes: "in-panel", components: [
3054             {kind: "XV.InputWidget", attr: "code"},
3055             {kind: "XV.InputWidget", attr: "description"}
3056           ]}
3057         ]}
3058       ]}
3059     ]
3060   });
3061
3062   XV.registerModelWorkspace("XM.TaxZone", "XV.TaxZoneWorkspace");
3063
3064   // ..........................................................
3065   // TERMS
3066   //
3067
3068   enyo.kind({
3069     name: "XV.TermsWorkspace",
3070     kind: "XV.Workspace",
3071     title: "_terms".loc(),
3072     model: "XM.Terms",
3073     components: [
3074       {kind: "Panels", arrangerKind: "CarouselArranger",
3075         fit: true, components: [
3076         {kind: "XV.Groupbox", name: "mainPanel", components: [
3077           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
3078           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
3079             classes: "in-panel", components: [
3080             {kind: "XV.InputWidget", attr: "code"},
3081             {kind: "XV.InputWidget", attr: "description"},
3082             {kind: "XV.TermsTypePicker", attr: "termsType"},
3083             {kind: "XV.NumberSpinnerWidget", name: "dueDays", attr: "dueDays"},
3084             {kind: "XV.NumberSpinnerWidget", name: "discountDays", attr: "discountDays"},
3085             {kind: "XV.NumberSpinnerWidget", name: "cutOffDay", attr: "cutOffDay"},
3086             {kind: "XV.CheckboxWidget", attr: "isUsedByBilling"},
3087             {kind: "XV.CheckboxWidget", attr: "isUsedByPayments"}
3088           ]}
3089         ]}
3090       ]}
3091     ],
3092     // XXX would be better if we only responded to changes to termstype specifically
3093     attributesChanged: function () {
3094       var termsType = this.getValue().get("termsType");
3095       this.inherited(arguments);
3096
3097       this.$.cutOffDay.setShowing(termsType === XM.Terms.PROXIMO);
3098
3099       if (termsType === XM.Terms.DAYS) {
3100         this.$.dueDays.setLabel("_dueDays".loc());
3101         this.$.discountDays.setLabel("_discountDays".loc());
3102       } else if (termsType === XM.Terms.PROXIMO) {
3103         this.$.dueDays.setLabel("_dueDay".loc());
3104         this.$.discountDays.setLabel("_discountDay".loc());
3105       }
3106     }
3107   });
3108
3109   XV.registerModelWorkspace("XM.Terms", "XV.TermsWorkspace");
3110
3111   // ..........................................................
3112   // TO DO
3113   //
3114
3115   var toDoHash = {
3116     name: "XV.ToDoWorkspace",
3117     kind: "XV.Workspace",
3118     title: "_toDo".loc(),
3119     headerAttrs: ["name"],
3120     model: "XM.ToDo",
3121     components: [
3122       {kind: "Panels", arrangerKind: "CarouselArranger",
3123         fit: true, components: [
3124         {kind: "XV.Groupbox", name: "mainPanel", components: [
3125           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
3126           {kind: "XV.ScrollableGroupbox", name: "mainGroup", fit: true,
3127             classes: "in-panel", components: [
3128             {kind: "XV.CheckboxWidget", attr: "isActive"},
3129             {kind: "XV.InputWidget", attr: "name"},
3130             {kind: "XV.InputWidget", attr: "description"},
3131             {kind: "XV.PriorityPicker", attr: "priority"},
3132             {kind: "XV.ToDoStatusPicker", label: "_status".loc(), attr: "statusProxy"},
3133             {kind: "onyx.GroupboxHeader", content: "_schedule".loc()},
3134             {kind: "XV.DateWidget", attr: "dueDate"},
3135             {kind: "XV.DateWidget", attr: "startDate"},
3136             {kind: "XV.DateWidget", attr: "assignDate"},
3137             {kind: "XV.DateWidget", attr: "completeDate"},
3138             {kind: "onyx.GroupboxHeader", content: "_userAccounts".loc()},
3139             {kind: "XV.UserAccountWidget", attr: "owner"},
3140             {kind: "XV.UserAccountWidget", attr: "assignedTo"},
3141             {kind: "onyx.GroupboxHeader", content: "_notes".loc()},
3142             {kind: "XV.TextArea", attr: "notes", fit: true},
3143             {kind: "onyx.GroupboxHeader", content: "_relationships".loc()},
3144             {kind: "XV.AccountWidget", attr: "account"},
3145             {kind: "XV.ContactWidget", attr: "contact"}
3146           ]}
3147         ]},
3148         {kind: "XV.ToDoCommentBox", attr: "comments"},
3149         {kind: "XV.ToDoDocumentsBox", attr: "documents"}
3150       ]}
3151     ]
3152   };
3153   toDoHash = enyo.mixin(toDoHash, XV.accountNotifyContactMixin);
3154   enyo.kind(toDoHash);
3155   XV.registerModelWorkspace("XM.ToDo", "XV.ToDoWorkspace");
3156   XV.registerModelWorkspace("XM.ToDoRelation", "XV.ToDoWorkspace");
3157   XV.registerModelWorkspace("XM.ToDoListItem", "XV.ToDoWorkspace");
3158
3159   // ..........................................................
3160   // URL
3161   //
3162
3163   enyo.kind({
3164     name: "XV.UrlWorkspace",
3165     kind: "XV.Workspace",
3166     title: "_url".loc(),
3167     model: "XM.Url",
3168     components: [
3169       {kind: "Panels", arrangerKind: "CarouselArranger",
3170         fit: true, components: [
3171         {kind: "XV.Groupbox", name: "mainPanel", components: [
3172           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
3173           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
3174             classes: "in-panel", components: [
3175             {kind: "XV.InputWidget", attr: "name"},
3176             {kind: "XV.InputWidget", attr: "path", label: "_address".loc()}
3177           ]}
3178         ]}
3179       ]}
3180     ]
3181   });
3182
3183   XV.registerModelWorkspace("XM.Url", "XV.UrlWorkspace");
3184
3185   // ..........................................................
3186   // UNIT
3187   //
3188
3189   enyo.kind({
3190     name: "XV.UnitWorkspace",
3191     kind: "XV.Workspace",
3192     title: "_unit".loc(),
3193     model: "XM.Unit",
3194     components: [
3195       {kind: "Panels", arrangerKind: "CarouselArranger",
3196         fit: true, components: [
3197         {kind: "XV.Groupbox", name: "mainPanel", components: [
3198           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
3199           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
3200             classes: "in-panel", components: [
3201             {kind: "XV.InputWidget", attr: "name"},
3202             {kind: "XV.InputWidget", attr: "description"},
3203             {kind: "XV.CheckboxWidget", attr: "isItemWeight"}
3204           ]}
3205         ]}
3206       ]}
3207     ]
3208   });
3209
3210   XV.registerModelWorkspace("XM.Unit", "XV.UnitWorkspace");
3211
3212   // ..........................................................
3213   // USER ACCOUNT
3214   //
3215
3216   enyo.kind({
3217     name: "XV.UserAccountWorkspace",
3218     kind: "XV.Workspace",
3219     title: "_userAccount".loc(),
3220     model: "XM.UserAccount",
3221     handlers: {
3222       onRefreshPrivileges: "refreshPrivileges"
3223     },
3224     components: [
3225       {kind: "Panels", arrangerKind: "CarouselArranger",
3226         fit: true, components: [
3227         {kind: "XV.Groupbox", name: "mainPanel", components: [
3228           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
3229           {kind: "XV.ScrollableGroupbox", name: "mainGroup", fit: true,
3230             classes: "in-panel", components: [
3231             {kind: "XV.InputWidget", attr: "username"},
3232             {kind: "XV.InputWidget", type: "password", attr: "password"},
3233             {kind: "XV.InputWidget", type: "password", name: "passwordCheck",
3234               label: "_reEnterPassword".loc()},
3235             {kind: "XV.LocalePicker", attr: "locale"},
3236             {kind: "XV.CheckboxWidget", attr: "isActive"},
3237             {kind: "XV.InputWidget", attr: "properName"},
3238             {kind: "XV.InputWidget", attr: "initials"},
3239             {kind: "XV.InputWidget", attr: "email"},
3240             {kind: "XV.CheckboxWidget", attr: "useEnhancedAuth"},
3241             {kind: "XV.CheckboxWidget", attr: "disableExport"},
3242             {kind: "XV.CheckboxWidget", attr: "isAgent"},
3243             {kind: "onyx.GroupboxHeader", content: "_extensions".loc()},
3244             {kind: "XV.UserAccountExtensionAssignmentBox", attr: "grantedExtensions",
3245               name: "grantedExtensions" },
3246             {kind: "onyx.GroupboxHeader", content: "_roles".loc()},
3247             {kind: "XV.UserAccountRoleAssignmentBox", attr: "grantedUserAccountRoles",
3248               name: "grantedRoles" },
3249           ]}
3250         ]},
3251         {kind: "XV.Groupbox", name: "privilegePanel", title: "_privileges".loc(),
3252           classes: "xv-assignment-box", components: [
3253           {kind: "onyx.GroupboxHeader", content: "_privileges".loc()},
3254           {kind: "XV.ScrollableGroupbox", fit: true,
3255             classes: "in-panel", components: [
3256             {kind: "XV.UserAccountPrivilegeAssignmentBox", attr: "grantedPrivileges",
3257               name: "grantedPrivileges"}
3258           ]}
3259         ]}
3260       ]}
3261     ],
3262     /*
3263       Ensure that the passwordCheck field is wiped out. This would not happen otherwise
3264       because it's not an attribute of the model.
3265     */
3266     attributesChanged: function (model, options) {
3267       this.inherited(arguments);
3268       if (this.value.getStatus() === XM.Model.READY_CLEAN) {
3269         this.$.passwordCheck.setValue("");
3270       }
3271     },
3272     /**
3273       The passwordCheck field is not on the model. Pipe to a hidden field.
3274      */
3275     controlValueChanged: function (inSender, inEvent) {
3276       if (inEvent.originator.name === 'passwordCheck') {
3277         this.value._passwordCheck = inEvent.originator.value;
3278         return true;
3279       }
3280       this.inherited(arguments);
3281     },
3282
3283     /**
3284       Inject awareness of privileges earned by role into the privilege box when prompted
3285      */
3286     refreshPrivileges: function (inSender, inEvent) {
3287       this.$.grantedPrivileges.mapIds(this.$.grantedRoles.getAssignedCollection().models);
3288       this.$.grantedPrivileges.tryToRender();
3289       this.$.grantedExtensions.mapIds(this.$.grantedRoles.getAssignedCollection().models);
3290       this.$.grantedExtensions.tryToRender();
3291     },
3292
3293     /**
3294       Inject awareness of privileges earned by role into the privilege box
3295         at the start of the model loading
3296      */
3297     statusChanged: function (model, status, options) {
3298       this.inherited(arguments);
3299       if (model.getStatus() & XM.Model.READY) {
3300         this.$.grantedPrivileges.mapIds(this.getValue().get("grantedUserAccountRoles").models);
3301         this.$.grantedPrivileges.tryToRender();
3302         this.$.grantedExtensions.mapIds(this.getValue().get("grantedUserAccountRoles").models);
3303         this.$.grantedExtensions.tryToRender();
3304       }
3305     }
3306   });
3307
3308   XV.registerModelWorkspace("XM.UserAccountRelation", "XV.UserAccountWorkspace");
3309   XV.registerModelWorkspace("XM.UserAccountListItem", "XV.UserAccountWorkspace");
3310
3311   // ..........................................................
3312   // USER ACCOUNT ROLE
3313   //
3314
3315   enyo.kind({
3316     name: "XV.UserAccountRoleWorkspace",
3317     kind: "XV.Workspace",
3318     title: "_userAccountRole".loc(),
3319     model: "XM.UserAccountRole",
3320     components: [
3321       {kind: "Panels", arrangerKind: "CarouselArranger",
3322         fit: true, classes: "xv-top-panel", components: [
3323         {kind: "XV.Groupbox", name: "mainPanel", components: [
3324           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
3325           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
3326             classes: "in-panel", components: [
3327             {kind: "XV.InputWidget", attr: "name"},
3328             {kind: "XV.InputWidget", attr: "description"},
3329             {kind: "onyx.GroupboxHeader", content: "_extensions".loc()},
3330             {kind: "XV.UserAccountRoleExtensionAssignmentBox", attr: "grantedExtensions",
3331               name: "grantedExtensions" }
3332           ]}
3333         ]},
3334         {kind: "XV.Groupbox", name: "privilegePanel", classes: "xv-assignment-box",
3335             title: "_privileges".loc(), components: [
3336           {kind: "onyx.GroupboxHeader", content: "_privileges".loc()},
3337           {kind: "XV.UserAccountRolePrivilegeAssignmentBox", attr: "grantedPrivileges",
3338             name: "grantedPrivileges" }
3339         ]}
3340       ]}
3341     ]
3342   });
3343
3344   XV.registerModelWorkspace("XM.UserAccountRole", "XV.UserAccountRoleWorkspace");
3345   XV.registerModelWorkspace("XM.UserAccountRoleRelation", "XV.UserAccountRoleWorkspace");
3346   XV.registerModelWorkspace("XM.UserAccountRoleListItem", "XV.UserAccountRoleWorkspace");
3347
3348
3349 }());