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