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