haxtuple placeholder for 17921
[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", maxlength: 2},
645             {kind: "XV.InputWidget", attr: "name"},
646             {kind: "XV.InputWidget", attr: "currencyName"},
647             {kind: "XV.InputWidget", attr: "currencySymbol"},
648             {kind: "XV.InputWidget", attr: "currencyAbbreviation", maxlength: 3},
649             {kind: "XV.InputWidget", attr: "currencyNumber", maxlength: 3}
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             {name: "mainSubgroup", components: [ // not a scroller, so we can addBefore
2789               {kind: "XV.InputWidget", attr: "code"},
2790               {kind: "XV.CheckboxWidget", attr: "isActive"},
2791               {kind: "XV.SiteTypePicker", attr: "siteType"},
2792               {kind: "XV.InputWidget", attr: "description"},
2793               {kind: "XV.ContactWidget", attr: "contact", name: "contactWidget"},
2794               {kind: "XV.AddressWidget", attr: "address"}
2795             ]}
2796           ]}
2797         ]},
2798         {kind: "XV.SiteCommentBox", attr: "comments", name: "commentsPanel"}
2799       ]}
2800     ]
2801   });
2802
2803   XV.registerModelWorkspace("XM.SiteRelation", "XV.SiteWorkspace");
2804   XV.registerModelWorkspace("XM.SiteListItem", "XV.SiteWorkspace");
2805
2806   // ..........................................................
2807   // SITE TYPE
2808   //
2809
2810   enyo.kind({
2811     name: "XV.SiteTypeWorkspace",
2812     kind: "XV.Workspace",
2813     title: "_siteType".loc(),
2814     model: "XM.SiteType"
2815   });
2816
2817   XV.registerModelWorkspace("XM.SiteType", "XV.SiteTypeWorkspace");
2818
2819   // ..........................................................
2820   // STATE
2821   //
2822
2823   enyo.kind({
2824     name: "XV.StateWorkspace",
2825     kind: "XV.Workspace",
2826     title: "_state".loc(),
2827     model: "XM.State",
2828     components: [
2829       {kind: "Panels", arrangerKind: "CarouselArranger",
2830         fit: true, components: [
2831         {kind: "XV.Groupbox", name: "mainPanel", components: [
2832           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2833           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
2834             classes: "in-panel", components: [
2835             {kind: "XV.InputWidget", attr: "abbreviation"},
2836             {kind: "XV.InputWidget", attr: "name"},
2837             {kind: "XV.CountryPicker", attr: "country"}
2838           ]}
2839         ]}
2840       ]}
2841     ]
2842   });
2843
2844   XV.registerModelWorkspace("XM.State", "XV.StateWorkspace");
2845
2846   // ..........................................................
2847   // TAX ASSIGNMENT
2848   //
2849
2850   enyo.kind({
2851     name: "XV.TaxAssignmentWorkspace",
2852     kind: "XV.Workspace",
2853     title: "_taxAssignment".loc(),
2854     model: "XM.TaxAssignment",
2855     components: [
2856       {kind: "Panels", arrangerKind: "CarouselArranger",
2857         fit: true, components: [
2858         {kind: "XV.Groupbox", name: "mainPanel", components: [
2859           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2860           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
2861             classes: "in-panel", components: [
2862               {kind: "XV.TaxCodePicker", label: "_taxCode".loc(), attr: "tax"},
2863               {kind: "XV.TaxZonePicker", label: "_taxZone".loc(), attr: "taxZone"},
2864               {kind: "XV.TaxTypePicker", label: "_taxType".loc(), attr: "taxType"}
2865             ]}
2866           ]}
2867         ]}
2868       ]
2869     });
2870
2871   XV.registerModelWorkspace("XM.TaxAssignment", "XV.TaxAssignmentWorkspace");
2872
2873   // ..........................................................
2874   // TAX AUTHORITY
2875   //
2876
2877   hash = {
2878     name: "XV.TaxAuthorityWorkspace",
2879     kind: "XV.AccountDocumentWorkspace",
2880     title: "_taxAuthority".loc(),
2881     model: "XM.TaxAuthority",
2882     headerAttrs: ["code", "-", "name"],
2883     handlers: {
2884       onError: "errorNotify"
2885     },
2886     components: [
2887       {kind: "Panels", arrangerKind: "CarouselArranger",
2888         fit: true, components: [
2889         {kind: "XV.Groupbox", name: "mainPanel", components: [
2890           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2891           {kind: "XV.ScrollableGroupbox", name: "mainGroup", fit: true,
2892             classes: "in-panel", components: [
2893             {kind: "XV.InputWidget", attr: "code"},
2894             {kind: "XV.InputWidget", attr: "name"},
2895             {kind: "XV.InputWidget", attr: "externalReference"},
2896             {kind: "XV.CurrencyPicker", attr: "currency"},
2897             {kind: "XV.InputWidget", attr: "county"},
2898             {kind: "onyx.GroupboxHeader", content: "_address".loc()},
2899             {kind: "XV.AddressWidget", attr: "address"}
2900           ]}
2901         ]}
2902       ]},
2903       {kind: "onyx.Popup", name: "findExistingAccountPopup", centered: true,
2904         modal: true, floating: true, scrim: true, onShow: "popupShown",
2905         onHide: "popupHidden", components: [
2906         {content: "_accountExists".loc()},
2907         {name: "whatToDo", content: "_convertAccountTaxAuthority".loc()},
2908         {tag: "br"},
2909         {kind: "onyx.Button", name: "convert", content: "_ok".loc(), ontap: "accountConvert",
2910           classes: "onyx-blue xv-popup-button"},
2911         {kind: "onyx.Button", name: "cancel", content: "_cancel".loc(), ontap: "accountCancel",
2912           classes: "xv-popup-button"}
2913       ]}
2914     ]
2915   };
2916
2917   hash = enyo.mixin(hash, XV.WorkspaceAddressMixin);
2918   enyo.kind(hash);
2919
2920   XV.registerModelWorkspace("XM.TaxAuthority", "XV.TaxAuthorityWorkspace");
2921   XV.registerModelWorkspace("XM.TaxAuthorityRelation", "XV.TaxAuthorityWorkspace");
2922
2923   // ..........................................................
2924   // TAX CODE
2925   //
2926
2927   enyo.kind({
2928     name: "XV.TaxCodeWorkspace",
2929     kind: "XV.Workspace",
2930     title: "_taxCode".loc(),
2931     model: "XM.TaxCode",
2932     components: [
2933       {kind: "Panels", arrangerKind: "CarouselArranger",
2934         fit: true, components: [
2935         {kind: "XV.Groupbox", name: "mainPanel", components: [
2936           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2937           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
2938             classes: "in-panel", components: [
2939             {kind: "XV.InputWidget", attr: "code"},
2940             {kind: "XV.InputWidget", attr: "description"},
2941             {kind: "XV.TaxClassPicker", attr: "class", label: "_taxClass".loc()},
2942             {kind: "XV.TaxAuthorityPicker", attr: "authority", label: "_taxAuthority".loc()},
2943             {kind: "XV.TaxCodePicker", attr: "basis"}
2944           ]}
2945         ]}
2946       ]}
2947     ]
2948   });
2949
2950   XV.registerModelWorkspace("XM.TaxCode", "XV.TaxCodeWorkspace");
2951
2952   // ..........................................................
2953   // TAX CLASS
2954   //
2955
2956   enyo.kind({
2957     name: "XV.TaxClassWorkspace",
2958     kind: "XV.Workspace",
2959     title: "_taxClass".loc(),
2960     model: "XM.TaxClass",
2961     components: [
2962       {kind: "Panels", arrangerKind: "CarouselArranger",
2963         fit: true, components: [
2964         {kind: "XV.Groupbox", name: "mainPanel", components: [
2965           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2966           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
2967             classes: "in-panel", components: [
2968             {kind: "XV.InputWidget", attr: "code"},
2969             {kind: "XV.InputWidget", attr: "description"},
2970             {kind: "XV.NumberWidget", attr: "sequence"}
2971           ]}
2972         ]}
2973       ]}
2974     ]
2975   });
2976
2977   XV.registerModelWorkspace("XM.TaxClass", "XV.TaxClassWorkspace");
2978
2979   // ..........................................................
2980   // TAX RATE
2981   //
2982
2983   enyo.kind({
2984     name: "XV.TaxRateWorkspace",
2985     kind: "XV.Workspace",
2986     title: "_taxRate".loc(),
2987     model: "XM.TaxRate",
2988     components: [
2989       {kind: "Panels", arrangerKind: "CarouselArranger",
2990         fit: true, components: [
2991         {kind: "XV.Groupbox", name: "mainPanel", components: [
2992           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2993           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
2994             classes: "in-panel", components: [
2995               {kind: "XV.TaxCodePicker", label: "_taxCode".loc(), attr: "tax"},
2996               {kind: "XV.PercentWidget", label: "_percent".loc(), attr: "percent"},
2997               {kind: "XV.MoneyWidget", attr: {localValue: "amount", currency: "currency",
2998                 effective: "effectiveDate"}, label: "_amount".loc()},
2999               {kind: "XV.DateWidget", label: "_effective".loc(), attr: "effectiveDate"},
3000               {kind: "XV.DateWidget", label: "_expires".loc(), attr: "expirationDate"}
3001             ]}
3002           ]}
3003         ]}
3004       ]
3005     });
3006
3007   XV.registerModelWorkspace("XM.TaxRate", "XV.TaxRateWorkspace");
3008
3009   // ..........................................................
3010   // TAX TYPE
3011   //
3012
3013   enyo.kind({
3014     name: "XV.TaxTypeWorkspace",
3015     kind: "XV.Workspace",
3016     title: "_taxType".loc(),
3017     model: "XM.TaxType",
3018     components: [
3019       {kind: "Panels", arrangerKind: "CarouselArranger",
3020         fit: true, components: [
3021         {kind: "XV.Groupbox", name: "mainPanel", components: [
3022           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
3023           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
3024             classes: "in-panel", components: [
3025             {kind: "XV.InputWidget", attr: "name"},
3026             {kind: "XV.InputWidget", attr: "description"},
3027             {kind: "XV.CheckboxWidget", attr: "isSystem"}
3028           ]}
3029         ]}
3030       ]}
3031     ]
3032   });
3033
3034   XV.registerModelWorkspace("XM.TaxType", "XV.TaxTypeWorkspace");
3035
3036   // ..........................................................
3037   // TAX ZONE
3038   //
3039
3040   enyo.kind({
3041     name: "XV.TaxZoneWorkspace",
3042     kind: "XV.Workspace",
3043     title: "_taxZone".loc(),
3044     model: "XM.TaxZone",
3045     components: [
3046       {kind: "Panels", arrangerKind: "CarouselArranger",
3047         fit: true, components: [
3048         {kind: "XV.Groupbox", name: "mainPanel", components: [
3049           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
3050           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
3051             classes: "in-panel", components: [
3052             {kind: "XV.InputWidget", attr: "code"},
3053             {kind: "XV.InputWidget", attr: "description"}
3054           ]}
3055         ]}
3056       ]}
3057     ]
3058   });
3059
3060   XV.registerModelWorkspace("XM.TaxZone", "XV.TaxZoneWorkspace");
3061
3062   // ..........................................................
3063   // TERMS
3064   //
3065
3066   enyo.kind({
3067     name: "XV.TermsWorkspace",
3068     kind: "XV.Workspace",
3069     title: "_terms".loc(),
3070     model: "XM.Terms",
3071     components: [
3072       {kind: "Panels", arrangerKind: "CarouselArranger",
3073         fit: true, components: [
3074         {kind: "XV.Groupbox", name: "mainPanel", components: [
3075           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
3076           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
3077             classes: "in-panel", components: [
3078             {kind: "XV.InputWidget", attr: "code"},
3079             {kind: "XV.InputWidget", attr: "description"},
3080             {kind: "XV.TermsTypePicker", attr: "termsType"},
3081             {kind: "XV.NumberSpinnerWidget", name: "dueDays", attr: "dueDays"},
3082             {kind: "XV.NumberSpinnerWidget", name: "discountDays", attr: "discountDays"},
3083             {kind: "XV.NumberSpinnerWidget", name: "cutOffDay", attr: "cutOffDay"},
3084             {kind: "XV.CheckboxWidget", attr: "isUsedByBilling"},
3085             {kind: "XV.CheckboxWidget", attr: "isUsedByPayments"}
3086           ]}
3087         ]}
3088       ]}
3089     ],
3090     // XXX would be better if we only responded to changes to termstype specifically
3091     attributesChanged: function () {
3092       var termsType = this.getValue().get("termsType");
3093       this.inherited(arguments);
3094
3095       this.$.cutOffDay.setShowing(termsType === XM.Terms.PROXIMO);
3096
3097       if (termsType === XM.Terms.DAYS) {
3098         this.$.dueDays.setLabel("_dueDays".loc());
3099         this.$.discountDays.setLabel("_discountDays".loc());
3100       } else if (termsType === XM.Terms.PROXIMO) {
3101         this.$.dueDays.setLabel("_dueDay".loc());
3102         this.$.discountDays.setLabel("_discountDay".loc());
3103       }
3104     }
3105   });
3106
3107   XV.registerModelWorkspace("XM.Terms", "XV.TermsWorkspace");
3108
3109   // ..........................................................
3110   // TO DO
3111   //
3112
3113   var toDoHash = {
3114     name: "XV.ToDoWorkspace",
3115     kind: "XV.Workspace",
3116     title: "_toDo".loc(),
3117     headerAttrs: ["name"],
3118     model: "XM.ToDo",
3119     components: [
3120       {kind: "Panels", arrangerKind: "CarouselArranger",
3121         fit: true, components: [
3122         {kind: "XV.Groupbox", name: "mainPanel", components: [
3123           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
3124           {kind: "XV.ScrollableGroupbox", name: "mainGroup", fit: true,
3125             classes: "in-panel", components: [
3126             {kind: "XV.CheckboxWidget", attr: "isActive"},
3127             {kind: "XV.InputWidget", attr: "name"},
3128             {kind: "XV.InputWidget", attr: "description"},
3129             {kind: "XV.PriorityPicker", attr: "priority"},
3130             {kind: "XV.ToDoStatusPicker", label: "_status".loc(), attr: "statusProxy"},
3131             {kind: "onyx.GroupboxHeader", content: "_schedule".loc()},
3132             {kind: "XV.DateWidget", attr: "dueDate"},
3133             {kind: "XV.DateWidget", attr: "startDate"},
3134             {kind: "XV.DateWidget", attr: "assignDate"},
3135             {kind: "XV.DateWidget", attr: "completeDate"},
3136             {kind: "onyx.GroupboxHeader", content: "_userAccounts".loc()},
3137             {kind: "XV.UserAccountWidget", attr: "owner"},
3138             {kind: "XV.UserAccountWidget", attr: "assignedTo"},
3139             {kind: "onyx.GroupboxHeader", content: "_notes".loc()},
3140             {kind: "XV.TextArea", attr: "notes", fit: true},
3141             {kind: "onyx.GroupboxHeader", content: "_relationships".loc()},
3142             {kind: "XV.AccountWidget", attr: "account"},
3143             {kind: "XV.ContactWidget", attr: "contact"}
3144           ]}
3145         ]},
3146         {kind: "XV.ToDoCommentBox", attr: "comments"},
3147         {kind: "XV.ToDoDocumentsBox", attr: "documents"}
3148       ]}
3149     ]
3150   };
3151   toDoHash = enyo.mixin(toDoHash, XV.accountNotifyContactMixin);
3152   enyo.kind(toDoHash);
3153   XV.registerModelWorkspace("XM.ToDo", "XV.ToDoWorkspace");
3154   XV.registerModelWorkspace("XM.ToDoRelation", "XV.ToDoWorkspace");
3155   XV.registerModelWorkspace("XM.ToDoListItem", "XV.ToDoWorkspace");
3156
3157   // ..........................................................
3158   // URL
3159   //
3160
3161   enyo.kind({
3162     name: "XV.UrlWorkspace",
3163     kind: "XV.Workspace",
3164     title: "_url".loc(),
3165     model: "XM.Url",
3166     components: [
3167       {kind: "Panels", arrangerKind: "CarouselArranger",
3168         fit: true, components: [
3169         {kind: "XV.Groupbox", name: "mainPanel", components: [
3170           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
3171           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
3172             classes: "in-panel", components: [
3173             {kind: "XV.InputWidget", attr: "name"},
3174             {kind: "XV.InputWidget", attr: "path", label: "_address".loc()}
3175           ]}
3176         ]}
3177       ]}
3178     ]
3179   });
3180
3181   XV.registerModelWorkspace("XM.Url", "XV.UrlWorkspace");
3182
3183   // ..........................................................
3184   // UNIT
3185   //
3186
3187   enyo.kind({
3188     name: "XV.UnitWorkspace",
3189     kind: "XV.Workspace",
3190     title: "_unit".loc(),
3191     model: "XM.Unit",
3192     components: [
3193       {kind: "Panels", arrangerKind: "CarouselArranger",
3194         fit: true, components: [
3195         {kind: "XV.Groupbox", name: "mainPanel", components: [
3196           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
3197           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
3198             classes: "in-panel", components: [
3199             {kind: "XV.InputWidget", attr: "name"},
3200             {kind: "XV.InputWidget", attr: "description"},
3201             {kind: "XV.CheckboxWidget", attr: "isItemWeight"}
3202           ]}
3203         ]}
3204       ]}
3205     ]
3206   });
3207
3208   XV.registerModelWorkspace("XM.Unit", "XV.UnitWorkspace");
3209
3210   // ..........................................................
3211   // USER ACCOUNT
3212   //
3213
3214   enyo.kind({
3215     name: "XV.UserAccountWorkspace",
3216     kind: "XV.Workspace",
3217     title: "_userAccount".loc(),
3218     model: "XM.UserAccount",
3219     handlers: {
3220       onRefreshPrivileges: "refreshPrivileges"
3221     },
3222     components: [
3223       {kind: "Panels", arrangerKind: "CarouselArranger",
3224         fit: true, components: [
3225         {kind: "XV.Groupbox", name: "mainPanel", components: [
3226           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
3227           {kind: "XV.ScrollableGroupbox", name: "mainGroup", fit: true,
3228             classes: "in-panel", components: [
3229             {kind: "XV.InputWidget", attr: "username"},
3230             {kind: "XV.InputWidget", type: "password", attr: "password"},
3231             {kind: "XV.InputWidget", type: "password", name: "passwordCheck",
3232               label: "_reEnterPassword".loc()},
3233             {kind: "XV.LocalePicker", attr: "locale"},
3234             {kind: "XV.CheckboxWidget", attr: "isActive"},
3235             {kind: "XV.InputWidget", attr: "properName"},
3236             {kind: "XV.InputWidget", attr: "initials"},
3237             {kind: "XV.InputWidget", attr: "email"},
3238             {kind: "XV.CheckboxWidget", attr: "useEnhancedAuth"},
3239             {kind: "XV.CheckboxWidget", attr: "disableExport"},
3240             {kind: "XV.CheckboxWidget", attr: "isAgent"},
3241             {kind: "onyx.GroupboxHeader", content: "_extensions".loc()},
3242             {kind: "XV.UserAccountExtensionAssignmentBox", attr: "grantedExtensions",
3243               name: "grantedExtensions" },
3244             {kind: "onyx.GroupboxHeader", content: "_roles".loc()},
3245             {kind: "XV.UserAccountRoleAssignmentBox", attr: "grantedUserAccountRoles",
3246               name: "grantedRoles" },
3247           ]}
3248         ]},
3249         {kind: "XV.Groupbox", name: "privilegePanel", title: "_privileges".loc(),
3250           classes: "xv-assignment-box", components: [
3251           {kind: "onyx.GroupboxHeader", content: "_privileges".loc()},
3252           {kind: "XV.ScrollableGroupbox", fit: true,
3253             classes: "in-panel", components: [
3254             {kind: "XV.UserAccountPrivilegeAssignmentBox", attr: "grantedPrivileges",
3255               name: "grantedPrivileges"}
3256           ]}
3257         ]}
3258       ]}
3259     ],
3260     /*
3261       Ensure that the passwordCheck field is wiped out. This would not happen otherwise
3262       because it's not an attribute of the model.
3263     */
3264     attributesChanged: function (model, options) {
3265       this.inherited(arguments);
3266       if (this.value.getStatus() === XM.Model.READY_CLEAN) {
3267         this.$.passwordCheck.setValue("");
3268       }
3269     },
3270     /**
3271       The passwordCheck field is not on the model. Pipe to a hidden field.
3272      */
3273     controlValueChanged: function (inSender, inEvent) {
3274       if (inEvent.originator.name === 'passwordCheck') {
3275         this.value._passwordCheck = inEvent.originator.value;
3276         return true;
3277       }
3278       this.inherited(arguments);
3279     },
3280
3281     /**
3282       Inject awareness of privileges earned by role into the privilege box when prompted
3283      */
3284     refreshPrivileges: function (inSender, inEvent) {
3285       this.$.grantedPrivileges.mapIds(this.$.grantedRoles.getAssignedCollection().models);
3286       this.$.grantedPrivileges.tryToRender();
3287       this.$.grantedExtensions.mapIds(this.$.grantedRoles.getAssignedCollection().models);
3288       this.$.grantedExtensions.tryToRender();
3289     },
3290
3291     /**
3292       Inject awareness of privileges earned by role into the privilege box
3293         at the start of the model loading
3294      */
3295     statusChanged: function (model, status, options) {
3296       this.inherited(arguments);
3297       if (model.getStatus() & XM.Model.READY) {
3298         this.$.grantedPrivileges.mapIds(this.getValue().get("grantedUserAccountRoles").models);
3299         this.$.grantedPrivileges.tryToRender();
3300         this.$.grantedExtensions.mapIds(this.getValue().get("grantedUserAccountRoles").models);
3301         this.$.grantedExtensions.tryToRender();
3302       }
3303     }
3304   });
3305
3306   XV.registerModelWorkspace("XM.UserAccountRelation", "XV.UserAccountWorkspace");
3307   XV.registerModelWorkspace("XM.UserAccountListItem", "XV.UserAccountWorkspace");
3308
3309   // ..........................................................
3310   // USER ACCOUNT ROLE
3311   //
3312
3313   enyo.kind({
3314     name: "XV.UserAccountRoleWorkspace",
3315     kind: "XV.Workspace",
3316     title: "_userAccountRole".loc(),
3317     model: "XM.UserAccountRole",
3318     components: [
3319       {kind: "Panels", arrangerKind: "CarouselArranger",
3320         fit: true, classes: "xv-top-panel", components: [
3321         {kind: "XV.Groupbox", name: "mainPanel", components: [
3322           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
3323           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
3324             classes: "in-panel", components: [
3325             {kind: "XV.InputWidget", attr: "name"},
3326             {kind: "XV.InputWidget", attr: "description"},
3327             {kind: "onyx.GroupboxHeader", content: "_extensions".loc()},
3328             {kind: "XV.UserAccountRoleExtensionAssignmentBox", attr: "grantedExtensions",
3329               name: "grantedExtensions" }
3330           ]}
3331         ]},
3332         {kind: "XV.Groupbox", name: "privilegePanel", classes: "xv-assignment-box",
3333             title: "_privileges".loc(), components: [
3334           {kind: "onyx.GroupboxHeader", content: "_privileges".loc()},
3335           {kind: "XV.UserAccountRolePrivilegeAssignmentBox", attr: "grantedPrivileges",
3336             name: "grantedPrivileges" }
3337         ]}
3338       ]}
3339     ]
3340   });
3341
3342   XV.registerModelWorkspace("XM.UserAccountRole", "XV.UserAccountRoleWorkspace");
3343   XV.registerModelWorkspace("XM.UserAccountRoleRelation", "XV.UserAccountRoleWorkspace");
3344   XV.registerModelWorkspace("XM.UserAccountRoleListItem", "XV.UserAccountRoleWorkspace");
3345
3346 }());