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