Merge pull request #1520 from xtuple/4_5_x
[xtuple] / enyo-client / application / source / views / workspace.js
1 /*jshint bitwise:false, indent:2, curly:true, eqeqeq:true, immed:true, latedef:true,
2 newcap:true, noarg:true, regexp:true, undef:true, trailing:true, white:true,
3 strict: false*/
4 /*global XV:true, XM:true, _:true, enyo:true, XT:true, onyx:true, window:true */
5
6 (function () {
7
8   var hash;
9
10   /**
11     Used to notify change of account to contact widget if both exist on
12     the same workspace.
13   */
14   XV.accountNotifyContactMixin = {
15     accountChanged: function () {
16       var account = this.$.accountWidget.getValue();
17       if (account) {
18         this.$.contactWidget.addParameter({
19           attribute: ["account", "accountParent"],
20           value: account.id
21         }, true);
22       } else {
23         this.$.contactWidget.removeParameter("account");
24       }
25     },
26     attributesChanged: function (inSender, inEvent) {
27       this.inherited(arguments);
28       this.accountChanged();
29     },
30     controlValueChanged: function (inSender, inEvent) {
31       this.inherited(arguments);
32       if (inEvent.originator.name === 'accountWidget') {
33         this.accountChanged();
34       }
35     }
36   };
37
38   /**
39     Handles Address change with prompts.
40   */
41   XV.WorkspaceAddressMixin = {
42     accountChanged: function () {
43       var account = this.getAccount();
44       this.$.addressWidget.setAccount(account);
45     },
46     attributesChanged: function (inSender, inEvent) {
47       this.inherited(arguments);
48       this.accountChanged();
49     },
50     controlValueChanged: function (inSender, inEvent) {
51       this.inherited(arguments);
52       if (inEvent.originator.name === 'accountWidget' ||
53           inEvent.originator.name === 'customerWidget') {
54         this.accountChanged();
55       }
56     },
57     getAccount: function () {
58       var model = this.getValue();
59       return model ? (model.get('account') || model.get('customer')) : undefined;
60     }
61   };
62
63   /**
64     Abstract workspace to be used for objects that are attached to models subclassed
65       from `AccountDocument`.
66     Must be subclassed.
67   */
68   enyo.kind({
69     name: "XV.AccountDocumentWorkspace",
70     kind: "XV.Workspace",
71     handlers: {
72       onError: "errorNotify"
73     },
74     published: {
75       // The natural key is the number, not the UUID
76       existingNumber: ""
77     },
78     accountConvert: function (inEvent) {
79       this.value.convertFromAccount(this.existingNumber);
80       this._popupDone = true;
81       this.$.findExistingAccountPopup.hide();
82     },
83     errorNotify: function (inSender, inEvent) {
84       // Handle existing
85       if (inEvent.error.code === 'xt1008') {
86         this.existingNumber = inEvent.error.params.response.id;
87         this._popupDone = false;
88         this.$.findExistingAccountPopup.show();
89         return true;
90       }
91     },
92     accountCancel: function () {
93       this._popupDone = true;
94       this.$.findExistingAccountPopup.hide();
95     },
96     popupHidden: function () {
97       if (!this._popupDone) {
98         this.$.findExistingAccountPopup.show();
99         return true;
100       }
101     }
102   });
103
104   // ..........................................................
105   // EMAIL PROFILE
106   //
107
108   /**
109     An abstract workspace for email profiles.
110   */
111   enyo.kind({
112     name: "XV.EmailProfileWorkspace",
113     kind: "XV.Workspace",
114     headerAttrs: ["name", "-", "description"],
115     components: [
116       {kind: "Panels", arrangerKind: "CarouselArranger",
117         fit: true, components: [
118         {kind: "XV.Groupbox", name: "mainPanel", components: [
119           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
120           {kind: "XV.ScrollableGroupbox", name: "mainGroup", fit: true,
121             classes: "in-panel", components: [
122             {kind: "XV.InputWidget", attr: "name"},
123             {kind: "XV.InputWidget", attr: "description"},
124             {kind: "onyx.GroupboxHeader", content: "_header".loc()},
125             {kind: "XV.InputWidget", attr: "from"},
126             {kind: "XV.InputWidget", attr: "replyTo"},
127             {kind: "XV.InputWidget", attr: "to"},
128             {kind: "XV.InputWidget", attr: "cc"},
129             {kind: "XV.InputWidget", attr: "bcc"},
130             {kind: "XV.InputWidget", attr: "subject"},
131             {kind: "onyx.GroupboxHeader", content: "_body".loc()},
132             {kind: "XV.TextArea", attr: "body", classes: "max-height"}
133           ]}
134         ]}
135       ]}
136     ]
137   });
138
139   // ..........................................................
140   // BASE CLASS
141   //
142
143   enyo.kind({
144     name: "XV.OrderedReferenceWorkspace",
145     kind: "XV.Workspace",
146     components: [
147       {kind: "Panels", arrangerKind: "CarouselArranger",
148         fit: true, components: [
149         {kind: "XV.Groupbox", name: "mainPanel", components: [
150           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
151           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
152             classes: "in-panel", components: [
153             {kind: "XV.InputWidget", attr: "name"},
154             {kind: "XV.InputWidget", attr: "description"},
155             // these order fields are integers, so setting a maxlength
156             // to prevent exceeding integer's max value
157             {kind: "XV.NumberWidget", attr: "order", maxlength: 9, formatting: false}
158           ]}
159         ]}
160       ]}
161     ]
162   });
163
164   // ..........................................................
165   // ACCOUNT
166   //
167
168   enyo.kind({
169     name: "XV.AccountWorkspace",
170     kind: "XV.Workspace",
171     title: "_account".loc(),
172     headerAttrs: ["number", "-", "name"],
173     model: "XM.Account",
174     handlers: {
175       onSavePrompt: "savePrompt"
176     },
177     components: [
178       {kind: "Panels", arrangerKind: "CarouselArranger",
179         fit: true, components: [
180         {kind: "XV.Groupbox", name: "mainPanel", components: [
181           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
182           {kind: "XV.ScrollableGroupbox", name: "mainGroup", fit: true,
183             classes: "in-panel", components: [
184             {kind: "XV.InputWidget", attr: "number"},
185             {kind: "XV.CheckboxWidget", attr: "isActive"},
186             {kind: "XV.InputWidget", attr: "name"},
187             {kind: "XV.AccountTypePicker", attr: "accountType"},
188             {kind: "XV.AccountWidget", attr: "parent", label: "_parent".loc()},
189             {kind: "XV.UserAccountWidget", attr: "owner"},
190             {kind: "onyx.GroupboxHeader", content: "_primaryContact".loc()},
191             {kind: "XV.ContactWidget", attr: "primaryContact",
192               showAddress: true, label: "_name".loc()},
193             {kind: "onyx.GroupboxHeader", content: "_secondaryContact".loc()},
194             {kind: "XV.ContactWidget", attr: "secondaryContact",
195               showAddress: true, label: "_name".loc()},
196             {kind: "XV.AccountCharacteristicsWidget", attr: "characteristics"},
197             {kind: "onyx.GroupboxHeader", content: "_notes".loc()},
198             {kind: "XV.TextArea", attr: "notes", fit: true}
199           ]}
200         ]},
201         {kind: "XV.AccountCommentBox", attr: "comments"},
202         {kind: "XV.Groupbox", name: "rolesPanel", title: "_roles".loc(),
203           components: [
204           {kind: "onyx.GroupboxHeader", content: "_roles".loc()},
205           {kind: "XV.ScrollableGroupbox", name: "rolesGroup", fit: true,
206             classes: "in-panel", components: []
207           }
208         ]},
209         {kind: "XV.AccountDocumentsBox", attr: "documents"},
210         {kind: "XV.AccountContactsBox", attr: "contactRelations"}
211       ]},
212       {kind: "onyx.Popup", name: "savePromptPopup", centered: true,
213         modal: true, floating: true, scrim: true,
214         onHide: "popupHidden", components: [
215         {content: "_mustSave".loc() },
216         {content: "_saveYourWork?".loc() },
217         {tag: "br"},
218         {kind: "onyx.Button", content: "_cancel".loc(), ontap: "savePromptCancel",
219           classes: "xv-popup-button"},
220         {kind: "onyx.Button", content: "_save".loc(), ontap: "savePromptSave",
221           classes: "onyx-blue xv-popup-button"}
222       ]}
223     ],
224     create: function () {
225       this.inherited(arguments);
226       var K = XM.Account.prototype,
227         roles = K.roleAttributes.sort(),
228         that = this;
229
230       // Loop and add a role checkbox for each role attribute found on the model
231       _.each(roles, function (role) {
232         that.createComponent({
233           kind: XV.AccountRoleCheckboxWidget,
234           name: role + "Control",
235           label: ("_" + role).loc(),
236           attr: role,
237           container: that.$.rolesGroup,
238           owner: that
239         });
240       });
241
242     },
243     savePrompt: function (inSender, inEvent) {
244       this._popupDone = false;
245       this._inEvent = inEvent;
246       this.$.savePromptPopup.show();
247     },
248     savePromptCancel: function () {
249       this._popupDone = true;
250       this._inEvent.callback(false);
251       this.$.savePromptPopup.hide();
252     },
253     savePromptSave: function () {
254       var that = this,
255         options = {};
256       options.success = function () {
257         that._inEvent.callback(true);
258       };
259       this._popupDone = true;
260       this.$.savePromptPopup.hide();
261       this.save(options);
262     }
263   });
264
265   XV.registerModelWorkspace("XM.AccountRelation", "XV.AccountWorkspace");
266   XV.registerModelWorkspace("XM.AccountListItem", "XV.AccountWorkspace");
267
268   // ..........................................................
269   // BANK ACCOUNT
270   //
271
272   enyo.kind({
273     name: "XV.BankAccountWorkspace",
274     kind: "XV.Workspace",
275     title: "_bankAccount".loc(),
276     model: "XM.BankAccount",
277     components: [
278       {kind: "Panels", arrangerKind: "CarouselArranger",
279         fit: true, components: [
280         {kind: "XV.Groupbox", name: "mainPanel", components: [
281           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
282           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
283             classes: "in-panel", fit: true, components: [
284             {kind: "XV.InputWidget", attr: "name"},
285             {kind: "XV.InputWidget", attr: "description"},
286             {kind: "XV.InputWidget", attr: "bankName"},
287             {kind: "XV.InputWidget", attr: "accountNumber"},
288             {kind: "XV.BankAccountTypePicker", attr: "bankAccountType"},
289             {kind: "XV.CurrencyPicker", attr: "currency"},
290             {kind: "XV.CheckboxWidget", attr: "isUsedByBilling"},
291             {kind: "XV.CheckboxWidget", attr: "isUsedByPayments"},
292             {kind: "onyx.GroupboxHeader", content: "_notes".loc()},
293             {kind: "XV.TextArea", attr: "notes"}
294           ]}
295         ]}
296       ]}
297     ]
298   });
299
300   XV.registerModelWorkspace("XM.BankAccountRelation", "XV.BankAccountWorkspace");
301
302   // ..........................................................
303   // CHARACTERISTIC
304   //
305
306   enyo.kind({
307     name: "XV.CharacteristicWorkspace",
308     kind: "XV.Workspace",
309     title: "_characteristic".loc(),
310     model: "XM.Characteristic",
311     components: [
312       {kind: "Panels", arrangerKind: "CarouselArranger",
313         fit: true, components: [
314         {kind: "XV.Groupbox", name: "mainPanel", components: [
315           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
316           {kind: "XV.ScrollableGroupbox", name: "mainGroup", fit: true,
317             classes: "in-panel", components: [
318             {kind: "XV.InputWidget", attr: "name"},
319             {kind: "XV.CharacteristicTypePicker", name: "typePicker", attr: "characteristicType"},
320             {kind: "XV.CheckboxWidget", attr: "isSearchable"},
321             {kind: "onyx.GroupboxHeader", content: "_notes".loc()},
322             {kind: "XV.TextArea", attr: "notes", fit: true, name: "notesHeader"},
323             {name: "advancedPanel", showing: false, components: [
324               {kind: "onyx.GroupboxHeader", content: "_advanced".loc()},
325               {kind: "XV.InputWidget", attr: "mask"},
326               {kind: "XV.InputWidget", attr: "validator"}
327             ]}
328           ]}
329         ]},
330         {kind: "XV.Groupbox", name: "rolesPanel", title: "_roles".loc(),
331           components: [
332           {kind: "onyx.GroupboxHeader", content: "_roles".loc()},
333           {kind: "XV.ScrollableGroupbox", name: "rolesGroup", fit: true,
334             classes: "in-panel", components: [
335             {kind: "XV.ToggleButtonWidget", attr: "isAccounts", label: "_accounts".loc()},
336             {kind: "XV.ToggleButtonWidget", attr: "isAddresses", label: "_addresses".loc()},
337             {kind: "XV.ToggleButtonWidget", attr: "isContacts", label: "_contacts".loc()},
338             {kind: "XV.ToggleButtonWidget", attr: "isCustomers", label: "_customers".loc()},
339             {kind: "XV.ToggleButtonWidget", attr: "isEmployees", label: "_employees".loc()},
340             {kind: "XV.ToggleButtonWidget", attr: "isIncidents", label: "_incidents".loc()},
341             {kind: "XV.ToggleButtonWidget", attr: "isInvoices", label: "_invoices".loc()},
342             {kind: "XV.ToggleButtonWidget", attr: "isItems", label: "_items".loc()},
343             {kind: "XV.ToggleButtonWidget", attr: "isOpportunities", label: "_opportunities".loc()},
344             {kind: "XV.ToggleButtonWidget", attr: "isSalesOrders", label: "_salesOrders".loc()},
345           ]}
346         ]},
347         {kind: "XV.CharacteristicOptionBox", name: "optionsPanel",
348           attr: "options", showing: false}
349       ]}
350     ],
351     /**
352       After the controls are updated by the model, determine visibility of panels.
353      */
354     attributesChanged: function (model, options) {
355       this.inherited(arguments);
356       if (this.getValue().getStatus() === XM.Model.READY_CLEAN ||
357         this.getValue().getStatus() === XM.Model.READY_NEW) {
358         this.typeValueChanged(model);
359       }
360     },
361
362     /**
363       Function to determine visibility of "advanced" and "options" panels based
364         on the characteristicType
365      */
366     typeValueChanged: function (model) {
367       var type = model ? model.get('characteristicType') : null;
368       var isText = type === XM.Characteristic.TEXT;
369       var isList = type === XM.Characteristic.LIST;
370       this.$.advancedPanel.setShowing(isText);
371       this.$.optionsPanel.setShowing(isList);
372       if (isList) {
373         this.$.optionsPanel.render();
374       } else if (isText) {
375         this.$.advancedPanel.render();
376       }
377       // signal to workspace container that the menu needs to re-render
378       this.doMenuChange();
379     }
380   });
381
382   XV.registerModelWorkspace("XM.Characteristic", "XV.CharacteristicWorkspace");
383
384   // ..........................................................
385   // CLASS CODE
386   //
387
388   enyo.kind({
389     name: "XV.ClassCodeWorkspace",
390     kind: "XV.Workspace",
391     title: "_classCode".loc(),
392     model: "XM.ClassCode",
393     components: [
394       {kind: "Panels", arrangerKind: "CarouselArranger",
395         fit: true, components: [
396         {kind: "XV.Groupbox", name: "mainPanel", components: [
397           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
398           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
399             classes: "in-panel", components: [
400             {kind: "XV.InputWidget", attr: "code"},
401             {kind: "XV.InputWidget", attr: "description"}
402           ]}
403         ]}
404       ]}
405     ]
406   });
407
408   XV.registerModelWorkspace("XM.ClassCode", "XV.ClassCodeWorkspace");
409
410   // ..........................................................
411   // CONFIGURE
412   //
413
414   enyo.kind({
415     name: "XV.DatabaseInformationWorkspace",
416     kind: "XV.Workspace",
417     title: "_database".loc() + " " + "_information".loc(),
418     model: "XM.DatabaseInformation",
419     components: [
420       {kind: "Panels", arrangerKind: "CarouselArranger",
421         fit: true, components: [
422         {kind: "XV.Groupbox", name: "mainPanel", components: [
423           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
424           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
425             classes: "in-panel", components: [
426             {kind: "XV.InputWidget", attr: "DatabaseName",
427               label: "_name".loc()},
428             {kind: "XV.InputWidget", attr: "ServerVersion",
429                 label: "_version".loc()},
430             {kind: "onyx.GroupboxHeader", content: "_notes".loc()},
431             {kind: "XV.TextArea", attr: "DatabaseComments"}
432           ]}
433         ]}
434       ]}
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     printOnSaveSetting: "DefaultPrintPOOnSave",
2278     actions: [{
2279       name: "print",
2280       isViewMethod: true,
2281       label: "_print".loc(),
2282       privilege: "ViewSalesOrders",
2283       prerequisite: "isReadyClean"
2284     },
2285     {name: "email",
2286       isViewMethod: true,
2287       label: "_email".loc(),
2288       privilege: "ViewSalesOrders",
2289       prerequisite: "isReadyClean"
2290     }],
2291     components: [
2292       {kind: "Panels", arrangerKind: "CarouselArranger",
2293         fit: true, components: [
2294         {kind: "XV.Groupbox", name: "mainPanel", components: [
2295           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2296           {kind: "XV.ScrollableGroupbox", name: "mainGroup", fit: true,
2297             classes: "in-panel", components: [
2298             {kind: "XV.InputWidget", attr: "number"},
2299             {kind: "XV.DateWidget", name: "dateField", attr: "orderDate",
2300               label: "_orderDate".loc()},
2301             {kind: "XV.DateWidget", attr: "scheduleDate"},
2302             {kind: "XV.DateWidget", attr: "packDate"},
2303             {kind: "XV.InputWidget", attr: "formatStatus",
2304               label: "_status".loc()},
2305             {kind: "XV.CheckboxWidget", attr: "printOnSaveSetting",
2306               label: "_printOnSave".loc()},
2307             {kind: "onyx.GroupboxHeader", content: "_billTo".loc()},
2308             {kind: "XV.SalesCustomerWidget", attr: "customer",
2309                name: "customerWidget", showAddress: true,
2310                label: "_customer".loc(), nameAttribute: ""
2311             },
2312             {kind: "XV.AddressFieldsWidget",
2313               name: "billtoAddress", attr:
2314               {name: "billtoName", line1: "billtoAddress1",
2315                 line2: "billtoAddress2", line3: "billtoAddress3",
2316                 city: "billtoCity", state: "billtoState",
2317                 postalCode: "billtoPostalCode", country: "billtoCountry"}
2318             },
2319             {kind: "XV.ContactWidget", attr: "billtoContact",
2320               name: "billtoContact"},
2321             {kind: "onyx.GroupboxHeader", content: "_shipTo".loc()},
2322             {kind: "XV.CustomerShiptoWidget", attr: "shipto",
2323               showAddress: true, label: "_number".loc(),
2324               nameAttribute: ""},
2325             {kind: "XV.AddressFieldsWidget",
2326               name: "shiptoAddress",
2327               disabled: true,
2328               attr: {name: "shiptoName", line1: "shiptoAddress1",
2329                 line2: "shiptoAddress2", line3: "shiptoAddress3",
2330                 city: "shiptoCity", state: "shiptoState",
2331                 postalCode: "shiptoPostalCode", country: "shiptoCountry"}
2332             },
2333             {kind: "XV.ContactWidget", attr: "shiptoContact",
2334               name: "shiptoContact"},
2335             {kind: "XV.SalesOrderCharacteristicsWidget", attr: "characteristics"},
2336             {kind: "onyx.GroupboxHeader", content: "_orderNotes".loc()},
2337             {kind: "XV.TextArea", attr: "orderNotes", fit: true},
2338             {kind: "onyx.GroupboxHeader", content: "_shippingNotes".loc()},
2339             {kind: "XV.TextArea", attr: "shipNotes", fit: true}
2340           ]}
2341         ]},
2342         {kind: "XV.Groupbox", name: "settingsPanel", title: "_settings".loc(),
2343           components: [
2344           {kind: "onyx.GroupboxHeader", content: "_settings".loc()},
2345           {kind: "XV.ScrollableGroupbox", name: "settingsGroup", fit: true,
2346             classes: "in-panel", components: [
2347             {kind: "XV.BillingTermsPicker", attr: "terms"},
2348             {kind: "XV.SalesRepPicker", attr: "salesRep"},
2349             {kind: "XV.PercentWidget", attr: "commission"},
2350             {kind: "XV.TaxZonePicker", attr: "taxZone"},
2351             {kind: "XV.SaleTypePicker", attr: "saleType"},
2352             {kind: "XV.HoldTypePicker", attr: "holdType"},
2353             {kind: "onyx.GroupboxHeader", content: "_shipping".loc()},
2354             {kind: "XV.CheckboxWidget", attr: "shipComplete"},
2355             {kind: "XV.HeavyweightSitePicker", attr: "site"},
2356             {kind: "XV.InputWidget", attr: "fob"},
2357             {kind: "XV.InputWidget", attr: "customerPurchaseOrderNumber",
2358              label: "_custPO".loc()},
2359             {kind: "XV.ShipViaCombobox", attr: "shipVia"},
2360             {kind: "XV.ShipZonePicker", attr: "shipZone"},
2361             {kind: "XV.ShippingChargePicker", attr: "shipCharge"},
2362             {kind: "onyx.GroupboxHeader", content: "_relationships".loc()}
2363           ]}
2364         ]},
2365         {kind: "XV.SalesOrderCommentBox", name: "salesOrderCommentBox",
2366           attr: "comments"},
2367         {kind: "XV.SalesOrderDocumentsBox", attr: "documents"}
2368       ]}
2369     ],
2370     /**
2371      * When the printOnSaveSetting checkbox is changed,
2372      * also change the workspace setting.
2373      */
2374     controlValueChanged: function (inSender, inEvent) {
2375       this.inherited(arguments);
2376       if (inEvent.originator.attr === 'printOnSaveSetting') {
2377         this.printOnSaveSetting = inEvent.originator.value;
2378       }
2379     },
2380     /**
2381      * @listens onPaymentPosted
2382      */
2383     handlePaymentPosted: function (inSender, inEvent) {
2384       this.requery();
2385     },
2386
2387     valueChanged: function () {
2388       this.inherited(arguments);
2389       if (this.$.salesOrderPaymentBox && this.value) {
2390         this.$.salesOrderPaymentBox.setSalesOrder(this.value);
2391       }
2392     },
2393     create: function () {
2394       this.inherited(arguments);
2395
2396       if (XV.SalesOrderPaymentBox && XT.session.privileges.get('PostCashReceipts')) {
2397         this.$.panels.createComponent({kind: "XV.SalesOrderPaymentBox", title: "_payment".loc(), addBefore: this.$.salesOrderCommentBox},
2398           {owner: this});
2399         if (this.value) {
2400           this.$.salesOrderPaymentBox.setSalesOrder(this.value);
2401         }
2402       }
2403
2404       if (XT.session.privileges.get("ProcessCreditCards") &&
2405           XT.session.settings.get("CCCompany") === "Authorize.Net") {
2406         this.$.panels.createComponent(
2407           {kind: "XV.CreditCardBox", name: "creditCardBox", attr: "customer.creditCards",
2408             addBefore: this.$.salesOrderCommentBox},
2409           {owner: this}
2410         );
2411
2412         // XXX altering this line will break the New button. if I add this to
2413         // paymentPanel, I get 'object has no method getValue' when I click
2414         // 'New' -tjw
2415         this.$.creditCardBox.parent.parent = this;
2416       }
2417
2418       if (enyo.platform.touch) {
2419         this.$.panels.createComponents([
2420           {kind: "XV.SalesOrderLineItemBox", name: "salesOrderLineItemBox",
2421             attr: "lineItems", addBefore: this.$.settingsPanel, classes: "medium-panel"},
2422         ], {owner: this});
2423         this.$.panels.createComponents([
2424           {kind: "XV.SalesOrderWorkflowBox", attr: "workflow", title: "_workflow".loc(),
2425             addBefore: this.$.salesOrderCommentBox, classes: "medium-panel"}
2426         ], {owner: this});
2427       } else {
2428         this.$.panels.createComponents([
2429           {kind: "XV.SalesOrderLineItemGridBox", name: "salesOrderLineItemBox",
2430             attr: "lineItems", addBefore: this.$.settingsPanel},
2431         ], {owner: this});
2432         this.$.panels.createComponents([
2433           {kind: "XV.SalesOrderWorkflowGridBox", attr: "workflow", title: "_workflow".loc(),
2434             addBefore: this.$.salesOrderCommentBox}
2435         ], {owner: this});
2436       }
2437     },
2438     handleHotKey: function (keyCode) {
2439       switch (String.fromCharCode(keyCode)) {
2440       case "L":
2441         if (!this.$.salesOrderLineItemGridBox.disabled) {
2442           this.$.salesOrderLineItemGridBox.newItem();
2443         }
2444         return;
2445       }
2446     },
2447     handleMagstripeCapture: function (inSender, inEvent) {
2448       if (this.$.creditCardBox && !this.$.creditCardBox.$.newButton.disabled) { // XXX sloppy
2449         this.$.salesPanels.setIndex(this.$.salesPanels.getPanels().length);
2450         this.$.creditCardBox.newItemWithData(inEvent.data);
2451       }
2452     },
2453     /**
2454       Reset the grid-box back to its read-only state in the case of "apply"
2455      */
2456     save: function (options) {
2457       this.inherited(arguments);
2458       var gridBox = this.$.salesOrderLineItemGridBox;
2459       // XXX hack to prevent screen from hanging in the case of invalid data
2460       if (gridBox && !XT.app.$.postbooks.$.notifyPopup.getShowing()) {
2461         gridBox.setEditableIndex(null);
2462         gridBox.valueChanged();
2463         gridBox.$.editableGridRow.setShowing(false);
2464       }
2465     }
2466   });
2467
2468   XV.registerModelWorkspace("XM.SalesOrder", "XV.SalesOrderWorkspace");
2469   XV.registerModelWorkspace("XM.SalesOrderWorkflow", "XV.SalesOrderWorkspace");
2470   XV.registerModelWorkspace("XM.SalesOrderRelation", "XV.SalesOrderWorkspace");
2471   XV.registerModelWorkspace("XM.SalesOrderListItem", "XV.SalesOrderWorkspace");
2472
2473   // ..........................................................
2474   // SALES ORDER WORKFLOW
2475   //
2476
2477   enyo.kind({
2478     name: "XV.SalesOrderWorkflowWorkspace",
2479     kind: "XV.ChildWorkspace",
2480     title: "_salesOrderWorkflow".loc(),
2481     model: "XM.SalesOrderWorkflow",
2482     components: [
2483       {kind: "Panels", arrangerKind: "CarouselArranger",
2484         classes: "xv-top-panel", fit: true, components: [
2485         {kind: "XV.Groupbox", name: "mainPanel", components: [
2486           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2487           {kind: "XV.ScrollableGroupbox", name: "mainGroup", fit: true,
2488             classes: "in-panel", components: [
2489             {kind: "XV.InputWidget", attr: "name"},
2490             {kind: "XV.InputWidget", attr: "description"},
2491             {kind: "XV.SalesOrderWorkflowTypePicker", attr: "workflowType"},
2492             {kind: "XV.WorkflowStatusPicker", attr: "status"},
2493             {kind: "XV.PriorityPicker", attr: "priority", showNone: false},
2494             {kind: "XV.NumberSpinnerWidget", attr: "sequence"},
2495             {kind: "onyx.GroupboxHeader", content: "_schedule".loc()},
2496             {kind: "XV.DateWidget", attr: "dueDate"},
2497             {kind: "XV.DateWidget", attr: "startDate"},
2498             {kind: "XV.DateWidget", attr: "assignDate"},
2499             {kind: "XV.DateWidget", attr: "completeDate"},
2500             {kind: "onyx.GroupboxHeader", content: "_userAccounts".loc()},
2501             {kind: "XV.UserAccountWidget", attr: "owner"},
2502             {kind: "XV.UserAccountWidget", attr: "assignedTo"},
2503             {kind: "onyx.GroupboxHeader", content: "_notes".loc()},
2504             {kind: "XV.TextArea", attr: "notes", fit: true}
2505           ]}
2506         ]},
2507         {kind: "XV.Groupbox", name: "onCompletedPanel", title: "_completionActions".loc(),
2508           components: [
2509           {kind: "onyx.GroupboxHeader", content: "_onCompletion".loc()},
2510           {kind: "XV.ScrollableGroupbox", name: "completionGroup", fit: true,
2511             classes: "in-panel", components: [
2512             {kind: "XV.CreditStatusPicker", attr: "completedParentStatus",
2513               noneText: "_noChange".loc(), label: "_nextStatus".loc()},
2514             {kind: "XV.DependenciesWidget",
2515               attr: {workflow: "parent.workflow", successors: "completedSuccessors"}}
2516           ]}
2517         ]},
2518         {kind: "XV.Groupbox", name: "onDeferredPanel", title: "_deferredActions".loc(),
2519           components: [
2520           {kind: "onyx.GroupboxHeader", content: "_onDeferred".loc()},
2521           {kind: "XV.ScrollableGroupbox", name: "deferredGroup", fit: true,
2522             classes: "in-panel", components: [
2523             {kind: "XV.CreditStatusPicker", attr: "deferredParentStatus",
2524               noneText: "_noChange".loc(), label: "_nextStatus".loc()},
2525             {kind: "XV.DependenciesWidget",
2526               attr: {workflow: "parent.workflow", successors: "deferredSuccessors"}}
2527           ]}
2528         ]}
2529       ]}
2530     ]
2531   });
2532   // ..........................................................
2533   // REASON CODE
2534   //
2535
2536   enyo.kind({
2537     name: "XV.ReasonCodeWorkspace",
2538     kind: "XV.Workspace",
2539     title: "_reasonCode".loc(),
2540     model: "XM.ReasonCode",
2541     components: [
2542       {kind: "Panels", arrangerKind: "CarouselArranger",
2543         fit: true, components: [
2544         {kind: "XV.Groupbox", name: "mainPanel", components: [
2545           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2546           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
2547             classes: "in-panel", components: [
2548             {kind: "XV.InputWidget", attr: "code"},
2549             {kind: "XV.InputWidget", attr: "description"},
2550             {kind: "XV.ReasonCodeDocumentTypePicker", attr: "documentType"}
2551           ]}
2552         ]}
2553       ]}
2554     ]
2555   });
2556
2557   XV.registerModelWorkspace("XM.ReasonCode", "XV.ReasonCodeWorkspace");
2558
2559   // ..........................................................
2560   // SALES EMAIL PROFILE
2561   //
2562
2563   enyo.kind({
2564     name: "XV.SalesEmailProfileWorkspace",
2565     kind: "XV.EmailProfileWorkspace",
2566     title: "_salesEmailProfile".loc(),
2567     model: "XM.SalesEmailProfile",
2568   });
2569
2570   XV.registerModelWorkspace("XM.SalesEmailProfile", "XV.SalesEmailProfileWorkspace");
2571
2572   // ..........................................................
2573   // SALES REP
2574   //
2575
2576   enyo.kind({
2577     name: "XV.SalesRepWorkspace",
2578     kind: "XV.AccountDocumentWorkspace",
2579     title: "_salesRep".loc(),
2580     model: "XM.SalesRep",
2581     components: [
2582       {kind: "Panels", arrangerKind: "CarouselArranger",
2583         fit: true, components: [
2584         {kind: "XV.Groupbox", name: "mainPanel", components: [
2585           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2586           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
2587             classes: "in-panel", components: [
2588             {kind: "XV.InputWidget", attr: "number"},
2589             {kind: "XV.CheckboxWidget", attr: "isActive"},
2590             {kind: "XV.InputWidget", attr: "name"},
2591             {kind: "XV.PercentWidget", attr: "commission"}
2592           ]}
2593         ]}
2594       ]},
2595       {kind: "onyx.Popup", name: "findExistingAccountPopup", centered: true,
2596         modal: true, floating: true, scrim: true, onShow: "popupShown",
2597         onHide: "popupHidden", components: [
2598         {content: "_accountExists".loc()},
2599         {name: "whatToDo", content: "_convertAccountSalesRep".loc()},
2600         {tag: "br"},
2601         {kind: "onyx.Button", name: "convert", content: "_ok".loc(), ontap: "accountConvert",
2602           classes: "onyx-blue xv-popup-button"},
2603         {kind: "onyx.Button", name: "cancel", content: "_cancel".loc(), ontap: "accountCancel",
2604           classes: "xv-popup-button"}
2605       ]}
2606     ]
2607   });
2608
2609   XV.registerModelWorkspace("XM.SalesRep", "XV.SalesRepWorkspace");
2610
2611   // ..........................................................
2612   // SALE TYPE
2613   //
2614
2615   enyo.kind({
2616     name: "XV.SaleTypeWorkspace",
2617     kind: "XV.Workspace",
2618     title: "_saleType".loc(),
2619     model: "XM.SaleType",
2620     components: [
2621       {kind: "Panels", arrangerKind: "CarouselArranger",
2622         fit: true, components: [
2623         {kind: "XV.Groupbox", name: "mainPanel", components: [
2624           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2625           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
2626             classes: "in-panel", components: [
2627             {kind: "XV.InputWidget", attr: "code"},
2628             {kind: "XV.InputWidget", attr: "description"},
2629             {kind: "XV.SalesEmailProfilePicker", attr: "emailProfile"},
2630             {kind: "XV.HoldTypePicker", attr: "defaultHoldType"},
2631             {kind: "XV.SaleTypeCharacteristicsWidget", attr: "characteristics"}
2632           ]}
2633         ]},
2634         {kind: "XV.SaleTypeWorkflowBox", attr: "workflow"}
2635       ]}
2636     ]
2637   });
2638
2639   XV.registerModelWorkspace("XM.SaleType", "XV.SaleTypeWorkspace");
2640
2641   // ..........................................................
2642   // SHIFT
2643   //
2644
2645   enyo.kind({
2646     name: "XV.ShiftWorkspace",
2647     kind: "XV.Workspace",
2648     title: "_shift".loc(),
2649     model: "XM.Shift",
2650     components: [
2651       {kind: "Panels", arrangerKind: "CarouselArranger",
2652         fit: true, components: [
2653         {kind: "XV.Groupbox", name: "mainPanel", components: [
2654           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2655           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
2656             classes: "in-panel", components: [
2657             {kind: "XV.InputWidget", attr: "number"},
2658             {kind: "XV.InputWidget", attr: "name"}
2659           ]}
2660         ]}
2661       ]}
2662     ]
2663   });
2664
2665   XV.registerModelWorkspace("XM.Shift", "XV.ShiftWorkspace");
2666
2667   // ..........................................................
2668   // SHIP VIA
2669   //
2670
2671   enyo.kind({
2672     name: "XV.ShipViaWorkspace",
2673     kind: "XV.Workspace",
2674     title: "_shipVia".loc(),
2675     model: "XM.ShipVia",
2676     components: [
2677       {kind: "Panels", arrangerKind: "CarouselArranger",
2678         fit: true, components: [
2679         {kind: "XV.Groupbox", name: "mainPanel", components: [
2680           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2681           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
2682             classes: "in-panel", components: [
2683             {kind: "XV.InputWidget", attr: "code"},
2684             {kind: "XV.InputWidget", attr: "description"}
2685           ]}
2686         ]}
2687       ]}
2688     ]
2689   });
2690
2691   XV.registerModelWorkspace("XM.ShipVia", "XV.ShipViaWorkspace");
2692
2693   // ..........................................................
2694   // SHIP ZONE
2695   //
2696
2697   enyo.kind({
2698     name: "XV.ShipZoneWorkspace",
2699     kind: "XV.Workspace",
2700     title: "_shipZone".loc(),
2701     model: "XM.ShipZone",
2702     components: [
2703       {kind: "Panels", arrangerKind: "CarouselArranger",
2704         fit: true, components: [
2705         {kind: "XV.Groupbox", name: "mainPanel", components: [
2706           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2707           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
2708             classes: "in-panel", components: [
2709             {kind: "XV.InputWidget", attr: "name"},
2710             {kind: "XV.InputWidget", attr: "description"}
2711           ]}
2712         ]}
2713       ]}
2714     ]
2715   });
2716
2717   XV.registerModelWorkspace("XM.ShipZone", "XV.ShipZoneWorkspace");
2718
2719   // ..........................................................
2720   // SITE
2721   //
2722
2723   enyo.kind({
2724     name: "XV.SiteWorkspace",
2725     kind: "XV.Workspace",
2726     title: "_site".loc(),
2727     model: "XM.Site",
2728     components: [
2729       {kind: "Panels", arrangerKind: "CarouselArranger",
2730         fit: true, components: [
2731         {kind: "XV.Groupbox", name: "mainPanel", components: [
2732           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2733           {kind: "XV.ScrollableGroupbox", name: "mainGroup", fit: true,
2734             classes: "in-panel", components: [
2735             {kind: "XV.InputWidget", attr: "code"},
2736             {kind: "XV.CheckboxWidget", attr: "isActive"},
2737             {kind: "XV.SiteTypePicker", attr: "siteType"},
2738             {kind: "XV.InputWidget", attr: "description"},
2739             {kind: "XV.ContactWidget", attr: "contact"},
2740             {kind: "XV.AddressWidget", attr: "address"},
2741             {kind: "XV.TaxZonePicker", attr: "taxZone"},
2742             {kind: "XV.InputWidget", attr: "incoterms"},
2743             {kind: "onyx.GroupboxHeader", content: "_notes".loc()},
2744             {kind: "XV.TextArea", attr: "notes", fit: true}
2745           ]}
2746         ]},
2747         {kind: "XV.SiteCommentBox", attr: "comments"}
2748       ]}
2749     ]
2750   });
2751
2752   XV.registerModelWorkspace("XM.SiteRelation", "XV.SiteWorkspace");
2753   XV.registerModelWorkspace("XM.SiteListItem", "XV.SiteWorkspace");
2754
2755   // ..........................................................
2756   // SITE TYPE
2757   //
2758
2759   enyo.kind({
2760     name: "XV.SiteTypeWorkspace",
2761     kind: "XV.Workspace",
2762     title: "_siteType".loc(),
2763     model: "XM.SiteType"
2764   });
2765
2766   XV.registerModelWorkspace("XM.SiteType", "XV.SiteTypeWorkspace");
2767
2768   // ..........................................................
2769   // STATE
2770   //
2771
2772   enyo.kind({
2773     name: "XV.StateWorkspace",
2774     kind: "XV.Workspace",
2775     title: "_state".loc(),
2776     model: "XM.State",
2777     components: [
2778       {kind: "Panels", arrangerKind: "CarouselArranger",
2779         fit: true, components: [
2780         {kind: "XV.Groupbox", name: "mainPanel", components: [
2781           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2782           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
2783             classes: "in-panel", components: [
2784             {kind: "XV.InputWidget", attr: "abbreviation"},
2785             {kind: "XV.InputWidget", attr: "name"},
2786             {kind: "XV.CountryPicker", attr: "country"}
2787           ]}
2788         ]}
2789       ]}
2790     ]
2791   });
2792
2793   XV.registerModelWorkspace("XM.State", "XV.StateWorkspace");
2794
2795   // ..........................................................
2796   // TAX ASSIGNMENT
2797   //
2798
2799   enyo.kind({
2800     name: "XV.TaxAssignmentWorkspace",
2801     kind: "XV.Workspace",
2802     title: "_taxAssignment".loc(),
2803     model: "XM.TaxAssignment",
2804     components: [
2805       {kind: "Panels", arrangerKind: "CarouselArranger",
2806         fit: true, components: [
2807         {kind: "XV.Groupbox", name: "mainPanel", components: [
2808           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2809           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
2810             classes: "in-panel", components: [
2811               {kind: "XV.TaxCodePicker", label: "_taxCode".loc(), attr: "tax"},
2812               {kind: "XV.TaxZonePicker", label: "_taxZone".loc(), attr: "taxZone"},
2813               {kind: "XV.TaxTypePicker", label: "_taxType".loc(), attr: "taxType"}
2814             ]}
2815           ]}
2816         ]}
2817       ]
2818     });
2819
2820   XV.registerModelWorkspace("XM.TaxAssignment", "XV.TaxAssignmentWorkspace");
2821
2822   // ..........................................................
2823   // TAX AUTHORITY
2824   //
2825
2826   hash = {
2827     name: "XV.TaxAuthorityWorkspace",
2828     kind: "XV.AccountDocumentWorkspace",
2829     title: "_taxAuthority".loc(),
2830     model: "XM.TaxAuthority",
2831     headerAttrs: ["code", "-", "name"],
2832     handlers: {
2833       onError: "errorNotify"
2834     },
2835     components: [
2836       {kind: "Panels", arrangerKind: "CarouselArranger",
2837         fit: true, components: [
2838         {kind: "XV.Groupbox", name: "mainPanel", components: [
2839           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2840           {kind: "XV.ScrollableGroupbox", name: "mainGroup", fit: true,
2841             classes: "in-panel", components: [
2842             {kind: "XV.InputWidget", attr: "code"},
2843             {kind: "XV.InputWidget", attr: "name"},
2844             {kind: "XV.InputWidget", attr: "externalReference"},
2845             {kind: "XV.CurrencyPicker", attr: "currency"},
2846             {kind: "XV.InputWidget", attr: "county"},
2847             {kind: "onyx.GroupboxHeader", content: "_address".loc()},
2848             {kind: "XV.AddressWidget", attr: "address"}
2849           ]}
2850         ]}
2851       ]},
2852       {kind: "onyx.Popup", name: "findExistingAccountPopup", centered: true,
2853         modal: true, floating: true, scrim: true, onShow: "popupShown",
2854         onHide: "popupHidden", components: [
2855         {content: "_accountExists".loc()},
2856         {name: "whatToDo", content: "_convertAccountTaxAuthority".loc()},
2857         {tag: "br"},
2858         {kind: "onyx.Button", name: "convert", content: "_ok".loc(), ontap: "accountConvert",
2859           classes: "onyx-blue xv-popup-button"},
2860         {kind: "onyx.Button", name: "cancel", content: "_cancel".loc(), ontap: "accountCancel",
2861           classes: "xv-popup-button"}
2862       ]}
2863     ]
2864   };
2865
2866   hash = enyo.mixin(hash, XV.WorkspaceAddressMixin);
2867   enyo.kind(hash);
2868
2869   XV.registerModelWorkspace("XM.TaxAuthority", "XV.TaxAuthorityWorkspace");
2870   XV.registerModelWorkspace("XM.TaxAuthorityRelation", "XV.TaxAuthorityWorkspace");
2871
2872   // ..........................................................
2873   // TAX CODE
2874   //
2875
2876   enyo.kind({
2877     name: "XV.TaxCodeWorkspace",
2878     kind: "XV.Workspace",
2879     title: "_taxCode".loc(),
2880     model: "XM.TaxCode",
2881     components: [
2882       {kind: "Panels", arrangerKind: "CarouselArranger",
2883         fit: true, components: [
2884         {kind: "XV.Groupbox", name: "mainPanel", components: [
2885           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2886           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
2887             classes: "in-panel", components: [
2888             {kind: "XV.InputWidget", attr: "code"},
2889             {kind: "XV.InputWidget", attr: "description"},
2890             {kind: "XV.TaxClassPicker", attr: "class", label: "_taxClass".loc()},
2891             {kind: "XV.TaxAuthorityPicker", attr: "authority", label: "_taxAuthority".loc()},
2892             {kind: "XV.TaxCodePicker", attr: "basis"}
2893           ]}
2894         ]}
2895       ]}
2896     ]
2897   });
2898
2899   XV.registerModelWorkspace("XM.TaxCode", "XV.TaxCodeWorkspace");
2900
2901   // ..........................................................
2902   // TAX CLASS
2903   //
2904
2905   enyo.kind({
2906     name: "XV.TaxClassWorkspace",
2907     kind: "XV.Workspace",
2908     title: "_taxClass".loc(),
2909     model: "XM.TaxClass",
2910     components: [
2911       {kind: "Panels", arrangerKind: "CarouselArranger",
2912         fit: true, components: [
2913         {kind: "XV.Groupbox", name: "mainPanel", components: [
2914           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2915           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
2916             classes: "in-panel", components: [
2917             {kind: "XV.InputWidget", attr: "code"},
2918             {kind: "XV.InputWidget", attr: "description"},
2919             {kind: "XV.NumberWidget", attr: "sequence"}
2920           ]}
2921         ]}
2922       ]}
2923     ]
2924   });
2925
2926   XV.registerModelWorkspace("XM.TaxClass", "XV.TaxClassWorkspace");
2927
2928   // ..........................................................
2929   // TAX RATE
2930   //
2931
2932   enyo.kind({
2933     name: "XV.TaxRateWorkspace",
2934     kind: "XV.Workspace",
2935     title: "_taxRate".loc(),
2936     model: "XM.TaxRate",
2937     components: [
2938       {kind: "Panels", arrangerKind: "CarouselArranger",
2939         fit: true, components: [
2940         {kind: "XV.Groupbox", name: "mainPanel", components: [
2941           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2942           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
2943             classes: "in-panel", components: [
2944               {kind: "XV.TaxCodePicker", label: "_taxCode".loc(), attr: "tax"},
2945               {kind: "XV.PercentWidget", label: "_percent".loc(), attr: "percent"},
2946               {kind: "XV.MoneyWidget", attr: {localValue: "amount", currency: "currency",
2947                 effective: "effectiveDate"}, label: "_amount".loc()},
2948               {kind: "XV.DateWidget", label: "_effective".loc(), attr: "effectiveDate"},
2949               {kind: "XV.DateWidget", label: "_expires".loc(), attr: "expirationDate"}
2950             ]}
2951           ]}
2952         ]}
2953       ]
2954     });
2955
2956   XV.registerModelWorkspace("XM.TaxRate", "XV.TaxRateWorkspace");
2957
2958   // ..........................................................
2959   // TAX TYPE
2960   //
2961
2962   enyo.kind({
2963     name: "XV.TaxTypeWorkspace",
2964     kind: "XV.Workspace",
2965     title: "_taxType".loc(),
2966     model: "XM.TaxType",
2967     components: [
2968       {kind: "Panels", arrangerKind: "CarouselArranger",
2969         fit: true, components: [
2970         {kind: "XV.Groupbox", name: "mainPanel", components: [
2971           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2972           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
2973             classes: "in-panel", components: [
2974             {kind: "XV.InputWidget", attr: "name"},
2975             {kind: "XV.InputWidget", attr: "description"},
2976             {kind: "XV.CheckboxWidget", attr: "isSystem"}
2977           ]}
2978         ]}
2979       ]}
2980     ]
2981   });
2982
2983   XV.registerModelWorkspace("XM.TaxType", "XV.TaxTypeWorkspace");
2984
2985   // ..........................................................
2986   // TAX ZONE
2987   //
2988
2989   enyo.kind({
2990     name: "XV.TaxZoneWorkspace",
2991     kind: "XV.Workspace",
2992     title: "_taxZone".loc(),
2993     model: "XM.TaxZone",
2994     components: [
2995       {kind: "Panels", arrangerKind: "CarouselArranger",
2996         fit: true, components: [
2997         {kind: "XV.Groupbox", name: "mainPanel", components: [
2998           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
2999           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
3000             classes: "in-panel", components: [
3001             {kind: "XV.InputWidget", attr: "code"},
3002             {kind: "XV.InputWidget", attr: "description"}
3003           ]}
3004         ]}
3005       ]}
3006     ]
3007   });
3008
3009   XV.registerModelWorkspace("XM.TaxZone", "XV.TaxZoneWorkspace");
3010
3011   // ..........................................................
3012   // TERMS
3013   //
3014
3015   enyo.kind({
3016     name: "XV.TermsWorkspace",
3017     kind: "XV.Workspace",
3018     title: "_terms".loc(),
3019     model: "XM.Terms",
3020     components: [
3021       {kind: "Panels", arrangerKind: "CarouselArranger",
3022         fit: true, components: [
3023         {kind: "XV.Groupbox", name: "mainPanel", components: [
3024           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
3025           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
3026             classes: "in-panel", components: [
3027             {kind: "XV.InputWidget", attr: "code"},
3028             {kind: "XV.InputWidget", attr: "description"},
3029             {kind: "XV.TermsTypePicker", attr: "termsType"},
3030             {kind: "XV.NumberSpinnerWidget", name: "dueDays", attr: "dueDays"},
3031             {kind: "XV.NumberSpinnerWidget", name: "discountDays", attr: "discountDays"},
3032             {kind: "XV.NumberSpinnerWidget", name: "cutOffDay", attr: "cutOffDay"},
3033             {kind: "XV.CheckboxWidget", attr: "isUsedByBilling"},
3034             {kind: "XV.CheckboxWidget", attr: "isUsedByPayments"}
3035           ]}
3036         ]}
3037       ]}
3038     ],
3039     // XXX would be better if we only responded to changes to termstype specifically
3040     attributesChanged: function () {
3041       var termsType = this.getValue().get("termsType");
3042       this.inherited(arguments);
3043
3044       this.$.cutOffDay.setShowing(termsType === XM.Terms.PROXIMO);
3045
3046       if (termsType === XM.Terms.DAYS) {
3047         this.$.dueDays.setLabel("_dueDays".loc());
3048         this.$.discountDays.setLabel("_discountDays".loc());
3049       } else if (termsType === XM.Terms.PROXIMO) {
3050         this.$.dueDays.setLabel("_dueDay".loc());
3051         this.$.discountDays.setLabel("_discountDay".loc());
3052       }
3053     }
3054   });
3055
3056   XV.registerModelWorkspace("XM.Terms", "XV.TermsWorkspace");
3057
3058   // ..........................................................
3059   // TO DO
3060   //
3061
3062   var toDoHash = {
3063     name: "XV.ToDoWorkspace",
3064     kind: "XV.Workspace",
3065     title: "_toDo".loc(),
3066     headerAttrs: ["name"],
3067     model: "XM.ToDo",
3068     components: [
3069       {kind: "Panels", arrangerKind: "CarouselArranger",
3070         fit: true, components: [
3071         {kind: "XV.Groupbox", name: "mainPanel", components: [
3072           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
3073           {kind: "XV.ScrollableGroupbox", name: "mainGroup", fit: true,
3074             classes: "in-panel", components: [
3075             {kind: "XV.CheckboxWidget", attr: "isActive"},
3076             {kind: "XV.InputWidget", attr: "name"},
3077             {kind: "XV.InputWidget", attr: "description"},
3078             {kind: "XV.PriorityPicker", attr: "priority"},
3079             {kind: "XV.ToDoStatusPicker", label: "_status".loc(), attr: "statusProxy"},
3080             {kind: "onyx.GroupboxHeader", content: "_schedule".loc()},
3081             {kind: "XV.DateWidget", attr: "dueDate"},
3082             {kind: "XV.DateWidget", attr: "startDate"},
3083             {kind: "XV.DateWidget", attr: "assignDate"},
3084             {kind: "XV.DateWidget", attr: "completeDate"},
3085             {kind: "onyx.GroupboxHeader", content: "_userAccounts".loc()},
3086             {kind: "XV.UserAccountWidget", attr: "owner"},
3087             {kind: "XV.UserAccountWidget", attr: "assignedTo"},
3088             {kind: "onyx.GroupboxHeader", content: "_notes".loc()},
3089             {kind: "XV.TextArea", attr: "notes", fit: true},
3090             {kind: "onyx.GroupboxHeader", content: "_relationships".loc()},
3091             {kind: "XV.AccountWidget", attr: "account"},
3092             {kind: "XV.ContactWidget", attr: "contact"}
3093           ]}
3094         ]},
3095         {kind: "XV.ToDoCommentBox", attr: "comments"},
3096         {kind: "XV.ToDoDocumentsBox", attr: "documents"}
3097       ]}
3098     ]
3099   };
3100   toDoHash = enyo.mixin(toDoHash, XV.accountNotifyContactMixin);
3101   enyo.kind(toDoHash);
3102   XV.registerModelWorkspace("XM.ToDo", "XV.ToDoWorkspace");
3103   XV.registerModelWorkspace("XM.ToDoRelation", "XV.ToDoWorkspace");
3104   XV.registerModelWorkspace("XM.ToDoListItem", "XV.ToDoWorkspace");
3105
3106   // ..........................................................
3107   // URL
3108   //
3109
3110   enyo.kind({
3111     name: "XV.UrlWorkspace",
3112     kind: "XV.Workspace",
3113     title: "_url".loc(),
3114     model: "XM.Url",
3115     components: [
3116       {kind: "Panels", arrangerKind: "CarouselArranger",
3117         fit: true, components: [
3118         {kind: "XV.Groupbox", name: "mainPanel", components: [
3119           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
3120           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
3121             classes: "in-panel", components: [
3122             {kind: "XV.InputWidget", attr: "name"},
3123             {kind: "XV.InputWidget", attr: "path", label: "_address".loc()}
3124           ]}
3125         ]}
3126       ]}
3127     ]
3128   });
3129
3130   XV.registerModelWorkspace("XM.Url", "XV.UrlWorkspace");
3131
3132   // ..........................................................
3133   // UNIT
3134   //
3135
3136   enyo.kind({
3137     name: "XV.UnitWorkspace",
3138     kind: "XV.Workspace",
3139     title: "_unit".loc(),
3140     model: "XM.Unit",
3141     components: [
3142       {kind: "Panels", arrangerKind: "CarouselArranger",
3143         fit: true, components: [
3144         {kind: "XV.Groupbox", name: "mainPanel", components: [
3145           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
3146           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
3147             classes: "in-panel", components: [
3148             {kind: "XV.InputWidget", attr: "name"},
3149             {kind: "XV.InputWidget", attr: "description"},
3150             {kind: "XV.CheckboxWidget", attr: "isItemWeight"}
3151           ]}
3152         ]}
3153       ]}
3154     ]
3155   });
3156
3157   XV.registerModelWorkspace("XM.Unit", "XV.UnitWorkspace");
3158
3159   // ..........................................................
3160   // USER ACCOUNT
3161   //
3162
3163   enyo.kind({
3164     name: "XV.UserAccountWorkspace",
3165     kind: "XV.Workspace",
3166     title: "_userAccount".loc(),
3167     model: "XM.UserAccount",
3168     handlers: {
3169       onRefreshPrivileges: "refreshPrivileges"
3170     },
3171     components: [
3172       {kind: "Panels", arrangerKind: "CarouselArranger",
3173         fit: true, components: [
3174         {kind: "XV.Groupbox", name: "mainPanel", components: [
3175           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
3176           {kind: "XV.ScrollableGroupbox", name: "mainGroup", fit: true,
3177             classes: "in-panel", components: [
3178             {kind: "XV.InputWidget", attr: "username"},
3179             {kind: "XV.InputWidget", type: "password", attr: "password"},
3180             {kind: "XV.InputWidget", type: "password", name: "passwordCheck",
3181               label: "_reEnterPassword".loc()},
3182             {kind: "XV.LocalePicker", attr: "locale"},
3183             {kind: "XV.CheckboxWidget", attr: "isActive"},
3184             {kind: "XV.InputWidget", attr: "properName"},
3185             {kind: "XV.InputWidget", attr: "initials"},
3186             {kind: "XV.InputWidget", attr: "email"},
3187             {kind: "XV.CheckboxWidget", attr: "useEnhancedAuth"},
3188             {kind: "XV.CheckboxWidget", attr: "disableExport"},
3189             {kind: "XV.CheckboxWidget", attr: "isAgent"},
3190             {kind: "onyx.GroupboxHeader", content: "_extensions".loc()},
3191             {kind: "XV.UserAccountExtensionAssignmentBox", attr: "grantedExtensions",
3192               name: "grantedExtensions" },
3193             {kind: "onyx.GroupboxHeader", content: "_roles".loc()},
3194             {kind: "XV.UserAccountRoleAssignmentBox", attr: "grantedUserAccountRoles",
3195               name: "grantedRoles" },
3196           ]}
3197         ]},
3198         {kind: "XV.Groupbox", name: "privilegePanel", title: "_privileges".loc(),
3199           classes: "xv-assignment-box", components: [
3200           {kind: "onyx.GroupboxHeader", content: "_privileges".loc()},
3201           {kind: "XV.ScrollableGroupbox", fit: true,
3202             classes: "in-panel", components: [
3203             {kind: "XV.UserAccountPrivilegeAssignmentBox", attr: "grantedPrivileges",
3204               name: "grantedPrivileges"}
3205           ]}
3206         ]}
3207       ]}
3208     ],
3209     /*
3210       Ensure that the passwordCheck field is wiped out. This would not happen otherwise
3211       because it's not an attribute of the model.
3212     */
3213     attributesChanged: function (model, options) {
3214       this.inherited(arguments);
3215       if (this.value.getStatus() === XM.Model.READY_CLEAN) {
3216         this.$.passwordCheck.setValue("");
3217       }
3218     },
3219     /**
3220       The passwordCheck field is not on the model. Pipe to a hidden field.
3221      */
3222     controlValueChanged: function (inSender, inEvent) {
3223       if (inEvent.originator.name === 'passwordCheck') {
3224         this.value._passwordCheck = inEvent.originator.value;
3225         return true;
3226       }
3227       this.inherited(arguments);
3228     },
3229
3230     /**
3231       Inject awareness of privileges earned by role into the privilege box when prompted
3232      */
3233     refreshPrivileges: function (inSender, inEvent) {
3234       this.$.grantedPrivileges.mapIds(this.$.grantedRoles.getAssignedCollection().models);
3235       this.$.grantedPrivileges.tryToRender();
3236       this.$.grantedExtensions.mapIds(this.$.grantedRoles.getAssignedCollection().models);
3237       this.$.grantedExtensions.tryToRender();
3238     },
3239
3240     /**
3241       Inject awareness of privileges earned by role into the privilege box
3242         at the start of the model loading
3243      */
3244     statusChanged: function (model, status, options) {
3245       this.inherited(arguments);
3246       if (model.getStatus() & XM.Model.READY) {
3247         this.$.grantedPrivileges.mapIds(this.getValue().get("grantedUserAccountRoles").models);
3248         this.$.grantedPrivileges.tryToRender();
3249         this.$.grantedExtensions.mapIds(this.getValue().get("grantedUserAccountRoles").models);
3250         this.$.grantedExtensions.tryToRender();
3251       }
3252     }
3253   });
3254
3255   XV.registerModelWorkspace("XM.UserAccountRelation", "XV.UserAccountWorkspace");
3256   XV.registerModelWorkspace("XM.UserAccountListItem", "XV.UserAccountWorkspace");
3257
3258   // ..........................................................
3259   // USER ACCOUNT ROLE
3260   //
3261
3262   enyo.kind({
3263     name: "XV.UserAccountRoleWorkspace",
3264     kind: "XV.Workspace",
3265     title: "_userAccountRole".loc(),
3266     model: "XM.UserAccountRole",
3267     components: [
3268       {kind: "Panels", arrangerKind: "CarouselArranger",
3269         fit: true, classes: "xv-top-panel", components: [
3270         {kind: "XV.Groupbox", name: "mainPanel", components: [
3271           {kind: "onyx.GroupboxHeader", content: "_overview".loc()},
3272           {kind: "XV.ScrollableGroupbox", name: "mainGroup",
3273             classes: "in-panel", components: [
3274             {kind: "XV.InputWidget", attr: "name"},
3275             {kind: "XV.InputWidget", attr: "description"},
3276             {kind: "onyx.GroupboxHeader", content: "_extensions".loc()},
3277             {kind: "XV.UserAccountRoleExtensionAssignmentBox", attr: "grantedExtensions",
3278               name: "grantedExtensions" }
3279           ]}
3280         ]},
3281         {kind: "XV.Groupbox", name: "privilegePanel", classes: "xv-assignment-box",
3282             title: "_privileges".loc(), components: [
3283           {kind: "onyx.GroupboxHeader", content: "_privileges".loc()},
3284           {kind: "XV.UserAccountRolePrivilegeAssignmentBox", attr: "grantedPrivileges",
3285             name: "grantedPrivileges" }
3286         ]}
3287       ]}
3288     ]
3289   });
3290
3291   XV.registerModelWorkspace("XM.UserAccountRole", "XV.UserAccountRoleWorkspace");
3292   XV.registerModelWorkspace("XM.UserAccountRoleRelation", "XV.UserAccountRoleWorkspace");
3293   XV.registerModelWorkspace("XM.UserAccountRoleListItem", "XV.UserAccountRoleWorkspace");
3294
3295
3296 }());