Merge pull request #1609 from xtuple/4_5_x
[xtuple] / enyo-client / application / source / views / list.js
1 /*jshint bitwise:true, indent:2, curly:true, eqeqeq:true, immed:true,
2 latedef:true, newcap:true, noarg:true, regexp:true, undef:true,
3 trailing:true, white:true, strict: false*/
4 /*global XT:true, XM:true, XV:true, _:true, window: true, enyo:true, Globalize:true*/
5
6 (function () {
7
8   // ..........................................................
9   // EMAIL
10   //
11
12   /**
13     An abstract list to be used for email profiles
14   */
15   enyo.kind({
16     name: "XV.EmailProfileList",
17     kind: "XV.List",
18     query: {orderBy: [
19       {attribute: 'name'}
20     ]},
21     components: [
22       {kind: "XV.ListItem", components: [
23         {kind: "FittableColumns", components: [
24           {kind: "XV.ListColumn", classes: "short",
25             components: [
26             {kind: "XV.ListAttr", attr: "name", isKey: true}
27           ]},
28           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
29             {kind: "XV.ListAttr", attr: "description"}
30           ]}
31         ]}
32       ]}
33     ]
34   });
35
36   // ..........................................................
37   // ACCOUNT
38   //
39
40   enyo.kind({
41     name: "XV.AccountList",
42     kind: "XV.List",
43     label: "_accounts".loc(),
44     collection: "XM.AccountListItemCollection",
45     query: {orderBy: [
46       {attribute: 'number'}
47     ]},
48     parameterWidget: "XV.AccountListParameters",
49     components: [
50       {kind: "XV.ListItem", components: [
51         {kind: "FittableColumns", components: [
52           {kind: "XV.ListColumn", classes: "name-column", components: [
53             {kind: "XV.ListAttr", attr: "number", isKey: true},
54             {kind: "XV.ListAttr", attr: "name"}
55           ]},
56           {kind: "XV.ListColumn", classes: "right-column", components: [
57             {kind: "XV.ListAttr", attr: "primaryContact.phone", },
58             {kind: "XV.ListAttr", attr: "primaryContact.primaryEmail"}
59           ]},
60           {kind: "XV.ListColumn", fit: true, components: [
61             {kind: "XV.ListAttr", attr: "primaryContact.name",
62               placeholder: "_noContact".loc()},
63             {kind: "XV.ListAttr", attr: "primaryContact.address"}
64           ]}
65         ]}
66       ]}
67     ]
68   });
69
70   XV.registerModelList("XM.AccountRelation", "XV.AccountList");
71
72   // ..........................................................
73   // ACTIVITY
74   //
75
76   enyo.kind({
77     name: "XV.ActivityList",
78     kind: "XV.List",
79     label: "_activities".loc(),
80     collection: "XM.ActivityListItemCollection",
81     parameterWidget: "XV.ActivityListParameters",
82     published: {
83       activityActions: []
84     },
85     actions: [
86       {name: "reassignUser",
87         method: "reassignUser",
88         prerequisite: "canReassign",
89         isViewMethod: true,
90         notify: false}
91     ],
92     events: {
93       onNotify: ""
94     },
95     query: {orderBy: [
96       {attribute: 'dueDate'},
97       {attribute: 'name'},
98       {attribute: 'uuid'}
99     ]},
100     multiSelect: true,
101     components: [
102       {kind: "XV.ListItem", components: [
103         {kind: "FittableColumns", components: [
104           {kind: "XV.ListColumn", classes: "name-column", components: [
105             {kind: "XV.ListAttr", formatter: "formatName", isKey: true},
106             {kind: "XV.ListAttr", formatter: "formatDescription1"},
107           ]},
108           {kind: "XV.ListColumn", classes: "right-column", components: [
109             {kind: "XV.ListAttr", attr: "dueDate", placeholder: "_noDueDate".loc()},
110             {kind: "XV.ListAttr", attr: "getActivityStatusString"}
111           ]},
112           {kind: "XV.ListColumn", classes: "second",
113             components: [
114             {kind: "XV.ListAttr", attr: "activityType",
115               formatter: "formatType",
116               placeholder: "_noDescription".loc()},
117             {kind: "XV.ListAttr", formatter: "formatDescription2"}
118           ]},
119           {kind: "XV.ListColumn", fit: true, components: [
120             {kind: "XV.ListAttr", attr: "owner.username",
121               placeholder: "_noOwner".loc()},
122             {kind: "XV.ListAttr", attr: "assignedTo.username", name: "assignedTo",
123               placeholder: "_noAssignedTo".loc()}
124           ]}
125         ]}
126       ]}
127     ],
128     selectedModels: function () {
129       var that = this,
130         collection = this.getValue(),
131         models = [],
132         selected;
133       if (collection.length) {
134         selected = _.keys(this.getSelection().selected);
135         // Using the selected index keys, go grab the models and return them in an array
136         models.push(_.map(selected, function (index) {
137           return that.getModel(index);
138         }));
139       }
140       return models[0];
141     },
142     reassignUser: function () {
143       var callback = function (resp, optionsObj) {
144         var navigator = this.$.navigator;
145         if (!resp.answer) {
146           return;
147         } else if (!resp.componentValue) {
148           navigator.$.contentPanels.getActive().doNotify({
149             type: XM.Model.WARNING,
150             message: "_noUserSelected".loc()
151           });
152         } else {
153           // Gather selected models, assemble dispatch params object and send dispatch to server
154           var options = {},
155             params = [],
156             models = optionsObj.models,
157             assignedTo = resp.componentValue.id,
158             ids = _.map(models, function (model) {
159               return model.id;
160             });
161           // Loop through and assemble dispatch param object
162           for (var i = 0; i < ids.length; i++) {
163             params.push({
164               activityId: ids[i],
165               username: assignedTo
166             });
167           }
168
169           // TODO - dispatch error handling
170           options.success = function (resp) {
171             navigator.requery();
172             return;
173           };
174
175           // Send to server with dispath. Need to pass options.error callback for error handling
176           XM.Model.prototype.dispatch("XM.Activity", "reassignUser", params, options);
177         }
178       };
179
180       this.doNotify({
181         type: XM.Model.QUESTION,
182         callback: callback,
183         message: "_reassignSelectedActivities".loc(),
184         yesLabel: "_reassign".loc(),
185         noLabel: "_cancel".loc(),
186         component: {kind: "XV.UserPicker", name: "assignTo", label: "_assignTo".loc()},
187         options: {models: this.selectedModels()}
188       });
189     },
190     getWorkspace: function () {
191       if (!this._lastTapIndex) {
192         // don't respond to events waterfalled from other models
193         return;
194       }
195       var collection = this.getValue(),
196         model = collection.at(this._lastTapIndex),
197         recordType = "XM." + model.get("activityType");
198       return XV.getWorkspace(recordType);
199     },
200     formatName: function (value, view, model) {
201       var parent = model.get("parent");
202       if (parent) { return parent.get("name"); }
203       return model.get("name");
204     },
205     formatDescription1: function (value, view, model) {
206       var parent = model.get("parent");
207       if (parent) { return model.get("name"); }
208       return model.get("description");
209     },
210     formatDescription2: function (value, view, model) {
211       var parent = model.get("parent");
212       if (!parent) { return ""; }
213       return model.get("description");
214     },
215     formatType: function (value) {
216       return ("_" + value.slice(0, 1).toLowerCase() + value.slice(1)).loc();
217     },
218     itemTap: function (inSender, inEvent) {
219       var model = this.getModel(inEvent.index),
220         key = model.get("editorKey"),
221         oldId = model.id,
222         type = model.get("activityType"),
223         action = model.get("activityAction"),
224         actActions = this.getActivityActions(),
225         actAction = _.find(actActions, function (item) {
226           return item.activityType === type && item.activityAction === action;
227         });
228
229       if (actAction) {
230         if (!this.getToggleSelected() || inEvent.originator.isKey) {
231           inEvent.model = model;
232           actAction.method.call(this, inSender, inEvent);
233           return true;
234         }
235       } else {
236         model.id = key;
237         this._lastTapIndex = inEvent.index;
238         this.inherited(arguments);
239         model.id = oldId;
240       }
241     }
242   });
243
244   // ..........................................................
245   // ADDRESS
246   //
247
248   enyo.kind({
249     name: "XV.AddressList",
250     kind: "XV.List",
251     label: "_addresses".loc(),
252     collection: "XM.AddressInfoCollection",
253     query: {orderBy: [
254       {attribute: 'country'},
255       {attribute: 'state'},
256       {attribute: 'city'},
257       {attribute: 'line1'}
258     ]},
259     parameterWidget: "XV.AddressListParameters",
260     components: [
261       {kind: "XV.ListItem", components: [
262         {kind: "XV.ListAttr", attr: "id", formatter: "formatAddress",
263           classes: "xv-addresslist-attr", allowHtml: true}
264       ]}
265     ],
266     formatAddress: function (value, view, model) {
267       return XM.Address.format(model, true);
268     },
269     setQuery: function (query) {
270       // If account is in filter, need to switch to a model including account.
271       var account,
272         collection;
273       account = _.find(query.parameters, function (param) {
274         return param.attribute === 'account';
275       });
276       collection = account ?
277         'XM.AccountAddressListItemCollection' : 'XM.AddressInfoCollection';
278       this.setCollection(collection);
279       this.inherited(arguments);
280     }
281   });
282
283   // ..........................................................
284   // BANK ACCOUNT
285   //
286
287   enyo.kind({
288     name: "XV.BankAccountList",
289     kind: "XV.List",
290     label: "_bankAccounts".loc(),
291     collection: "XM.BankAccountRelationCollection",
292     query: {orderBy: [
293       {attribute: 'name'}
294     ]},
295     components: [
296       {kind: "XV.ListItem", components: [
297         {kind: "FittableColumns", components: [
298           {kind: "XV.ListColumn", classes: "short",
299             components: [
300             {kind: "XV.ListAttr", attr: "name", isKey: true}
301           ]},
302           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
303             {kind: "XV.ListAttr", attr: "description"}
304           ]}
305         ]}
306       ]}
307     ]
308   });
309
310   XV.registerModelList("XM.BankAccountRelation", "XV.BankAccountList");
311
312   // ..........................................................
313   // CLASS CODE
314   //
315
316   enyo.kind({
317     name: "XV.ClassCodeList",
318     kind: "XV.List",
319     label: "_classCodes".loc(),
320     collection: "XM.ClassCodeCollection",
321     query: {orderBy: [
322       {attribute: 'code'}
323     ]},
324     components: [
325       {kind: "XV.ListItem", components: [
326         {kind: "FittableColumns", components: [
327           {kind: "XV.ListColumn", classes: "short",
328             components: [
329             {kind: "XV.ListAttr", attr: "code", isKey: true}
330           ]},
331           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
332             {kind: "XV.ListAttr", attr: "description"}
333           ]}
334         ]}
335       ]}
336     ]
337   });
338
339   // ..........................................................
340   // CONFIGURE
341   //
342
343   enyo.kind({
344     name: "XV.ConfigurationsList",
345     kind: "XV.List",
346     label: "_configure".loc(),
347     collection: "XM.configurations",
348     query: {orderBy: [
349       {attribute: 'name'}
350     ]},
351     canAddNew: false,
352     components: [
353       {kind: "XV.ListItem", components: [
354         {kind: "FittableColumns", components: [
355           {kind: "XV.ListColumn", classes: "short",
356             components: [
357             {kind: "XV.ListAttr", attr: "name", classes: "bold"}
358           ]},
359           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
360             {kind: "XV.ListAttr", attr: "description"}
361           ]}
362         ]}
363       ]}
364     ],
365     collectionChanged: function () {
366       var collection = this.getCollection(),
367         obj = XT.getObjectByName(collection);
368       this.setValue(obj);
369     },
370     getWorkspace: function () {
371       return this._workspace;
372     },
373     /**
374       Configuration is a special list because it's backed by a backbone
375       model which points to an empty XM.Model in its model attribute. Don't
376       let this go up through the normal channels; handle the opening of
377       the workspace here.
378      */
379     itemTap: function (inSender, inEvent) {
380       var model = this.getValue().at(inEvent.index),
381         workspace = model.get("workspace"),
382         xmModel = XT.getObjectByName(model.get('model')),
383         canNotRead = !xmModel.getClass().canRead(),
384         id = false;
385
386       this._workspace = model.get('workspace');
387
388       // Check privileges first
389       if (canNotRead) {
390         this.showError("_insufficientViewPrivileges".loc());
391         return true;
392       }
393
394       // Bubble requset for workspace view, including the model id payload
395       if (workspace) {
396         this.doWorkspace({workspace: workspace, id: id});
397       }
398       return true;
399     },
400     fetch: function () {
401       this.fetched();
402     }
403
404   });
405
406   // ..........................................................
407   // CONTACT
408   //
409
410   enyo.kind({
411     name: "XV.ContactList",
412     kind: "XV.List",
413     label: "_contacts".loc(),
414     collection: "XM.ContactListItemCollection",
415     actions: [{
416       name: "exportContact",
417       method: "vCardExport",
418       isViewMethod: "true"
419     }],
420     query: {orderBy: [
421       {attribute: 'lastName'},
422       {attribute: 'firstName'},
423       {attribute: 'primaryEmail'}
424     ]},
425     parameterWidget: "XV.ContactListParameters",
426     components: [
427       {kind: "XV.ListItem", components: [
428         {kind: "FittableColumns", components: [
429           {kind: "XV.ListColumn", classes: "name-column", components: [
430             {kind: "FittableColumns", components: [
431               {kind: "XV.ListAttr", attr: "firstName",
432                 formatter: "formatFirstName", isKey: true},
433               {kind: "XV.ListAttr", attr: "lastName", fit: true,
434                 style: "padding-left: 0px;", isKey: true}
435             ]},
436             {kind: "XV.ListAttr", attr: "jobTitle", showPlaceholder: true}
437           ]},
438           {kind: "XV.ListColumn", classes: "right-column", components: [
439             {kind: "XV.ListAttr", attr: "phone"},
440             {kind: "XV.ListAttr", attr: "primaryEmail"}
441           ]},
442           {kind: "XV.ListColumn", fit: true, components: [
443             {kind: "XV.ListAttr", attr: "account.name", showPlaceholder: true},
444             {kind: "XV.ListAttr", attr: "address", showPlaceholder: true}
445           ]}
446         ]}
447       ]}
448     ],
449     formatFirstName: function (value, view, model) {
450       var lastName = (model.get('lastName') || "").trim(),
451         firstName = (model.get('firstName') || "").trim();
452       if (_.isEmpty(firstName) && _.isEmpty(lastName)) {
453         view.addRemoveClass("placeholder", true);
454         value = "_noName".loc();
455       } else {
456         view.addRemoveClass("bold", _.isEmpty(lastName));
457       }
458       if (this.getToggleSelected()) {
459         view.addRemoveClass("hyperlink", true);
460       }
461       return value;
462     },
463     vCardExport: function (inEvent) {
464       var imodel = inEvent.model,
465           model = imodel,
466           begin,
467           version,
468           name,
469           fullName,
470           org,
471           title,
472           phoneWork,
473           phoneHome,
474           phoneFax,
475           address = [],
476           addressWork,
477           labelWork,
478           email,
479           website,
480           revision,
481           end,
482           stringToSave;
483
484       if (model.get('lastName')) {
485         name = model.get('lastName');
486         fullName = model.get('lastName');
487       }
488       if (model.get('middleName')) {
489         name = name + ";" + model.get('middleName');
490         fullName = model.get('middleName') + " " + fullName;
491       }
492       if (model.get('firstName')) {
493         name = name + ";" + model.get('firstName');
494         fullName = model.get('firstName') + " " + fullName;
495       }
496
497       begin = "VCARD";
498       version = "3.0";
499       org = "";
500       title = model.get('jobTitle');
501       phoneWork = model.get('phone');
502       phoneHome = model.get('alternate');
503       phoneFax = model.get('fax');
504       if (isNaN(model.getValue('address.line1').charAt(0))) {
505         org = model.getValue('address.line1');
506         address[0] = model.getValue('address.line2');
507       }
508       else {
509         address[0] = model.getValue('address.line1');
510         address.push(model.getValue('address.line2'));
511       }
512       address.push(model.getValue('address.line3'));
513       address.push(model.getValue('address.city'));
514       address.push(model.getValue('address.state'));
515       address.push(model.getValue('address.postalCode'));
516       address.push(model.getValue('address.country'));
517       //for address, set address with semicolon delimiters
518       //for label, set address with ESCAPED newline delimiters
519       if (address[0]) {
520         addressWork = address[0] + ";";
521         labelWork = address[0] + "\\n";
522       }
523       for (var i = 1; i < address.length; i++) {
524         if (address[i]) {
525           addressWork = addressWork + address[i] + ";";
526           labelWork = labelWork + address[i] + "\\n";
527         }
528       }
529       email = model.get('primaryEmail');
530       website = model.get('webAddress');
531       revision = Globalize.format(new Date(), "yyyy-MM-dd");
532       end = "VCARD";
533
534       stringToSave = "BEGIN:" + begin + "%0A";
535       stringToSave = stringToSave + "VERSION:" + version + "%0A";
536       stringToSave = stringToSave + "N:" + name + "%0A";
537       stringToSave = stringToSave + "FN:" + fullName + "%0A";
538       if (org) {
539         stringToSave = stringToSave + "ORG:" + org + "%0A";
540       }
541       if (title) {
542         stringToSave = stringToSave + "TITLE:" + title + "%0A";
543       }
544       if (phoneWork) {
545         stringToSave = stringToSave + "TEL;TYPE=WORK,VOICE:" + phoneWork + "%0A";
546       }
547       if (phoneHome) {
548         stringToSave = stringToSave + "TEL;TYPE=HOME,VOICE:" + phoneHome + "%0A";
549       }
550       if (phoneFax) {
551         stringToSave = stringToSave + "TEL;TYPE=FAX:" + phoneFax + "%0A";
552       }
553       if (addressWork) {
554         stringToSave = stringToSave + "ADR;TYPE=WORK:;;" + addressWork + "%0A";
555       }
556       if (labelWork) {
557         stringToSave = stringToSave + "LABEL;TYPE=WORK:;;" + labelWork + "%0A";
558       }
559       if (email) {
560         stringToSave = stringToSave + "EMAIL;TYPE=PREF,INTERNET:" + email + "%0A";
561       }
562       if (website) {
563         stringToSave = stringToSave + "URL:" + website + "%0A";
564       }
565       stringToSave = stringToSave + "REV:" + revision + "%0A";
566       stringToSave = stringToSave + "END:" + end + "%0A";
567
568       window.open(XT.getOrganizationPath() +
569         '/%@?stringToSave=%@'
570         .f('vcfExport',
571           stringToSave),
572         '_newtab');
573     }
574   });
575
576   XV.registerModelList("XM.ContactRelation", "XV.ContactList");
577
578   // ..........................................................
579   // COST CATEGORY
580   //
581
582   enyo.kind({
583     name: "XV.CostCategoryList",
584     kind: "XV.List",
585     label: "_costCategories".loc(),
586     collection: "XM.CostCategoryCollection",
587     query: {orderBy: [
588       {attribute: 'code'}
589     ]},
590     parameterWidget: "XV.CostCategoryListParameters",
591     components: [
592       {kind: "XV.ListItem", components: [
593         {kind: "FittableColumns", components: [
594           {kind: "XV.ListColumn", classes: "short",
595             components: [
596             {kind: "XV.ListAttr", attr: "code", isKey: true}
597           ]},
598           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
599             {kind: "XV.ListAttr", attr: "description"}
600           ]}
601         ]}
602       ]}
603     ]
604   });
605
606   XV.registerModelList("XM.CostCategory", "XV.CostCategoryList");
607
608   // ..........................................................
609   // CREDIT CARD
610   //
611
612   enyo.kind({
613     name: "XV.CreditCardList",
614     kind: "XV.List",
615     label: "_creditCards".loc(),
616     collection: "XM.CreditCardCollection",
617     query: {orderBy: [
618       {attribute: 'number'}
619     ]},
620     parameterWidget: "XV.CreditCardListParameters",
621     components: [
622       {kind: "XV.ListItem", components: [
623         {kind: "FittableColumns", components: [
624           {kind: "XV.ListColumn", classes: "short",
625             components: [
626             {kind: "XV.ListAttr", attr: "number", isKey: true}
627           ]},
628           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
629             {kind: "XV.ListAttr", attr: "name"}
630           ]}
631         ]}
632       ]}
633     ]
634   });
635
636   XV.registerModelList("XM.CostCategory", "XV.CostCategoryList");
637
638   // ..........................................................
639   // CURRENCY
640   //
641
642   enyo.kind({
643     name: "XV.CurrencyList",
644     kind: "XV.List",
645     label: "_currencies".loc(),
646     collection: "XM.CurrencyCollection",
647     query: {orderBy: [
648       {attribute: 'name'}
649     ]},
650     components: [
651       {kind: "XV.ListItem", components: [
652         {kind: "FittableColumns", components: [
653           {kind: "XV.ListColumn", classes: "short",
654             components: [
655             {kind: "XV.ListAttr", attr: "abbreviation", isKey: true}
656           ]},
657           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
658             {kind: "XV.ListAttr", attr: "name"}
659           ]},
660           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
661             {kind: "XV.ListAttr", attr: "isBase"}
662           ]}
663         ]}
664       ]}
665     ]
666   });
667
668   // ..........................................................
669   // CUSTOMER
670   //
671
672   enyo.kind({
673     name: "XV.CustomerList",
674     kind: "XV.List",
675     label: "_customers".loc(),
676     collection: "XM.CustomerListItemCollection",
677     query: {orderBy: [
678       {attribute: 'number'}
679     ]},
680     multiSelect: true,
681     parameterWidget: "XV.CustomerListParameters",
682     components: [
683       {kind: "XV.ListItem", components: [
684         {kind: "FittableColumns", components: [
685           {kind: "XV.ListColumn", classes: "name-column", components: [
686             {kind: "XV.ListAttr", attr: "number", isKey: true},
687             {kind: "XV.ListAttr", attr: "name"}
688           ]},
689           {kind: "XV.ListColumn", classes: "right-column", components: [
690             {kind: "XV.ListAttr", attr: "billingContact.phone", },
691             {kind: "XV.ListAttr", attr: "billingContact.primaryEmail"}
692           ]},
693           {kind: "XV.ListColumn", fit: true, components: [
694             {kind: "XV.ListAttr", attr: "billingContact.name",
695               placeholder: "_noContact".loc()},
696             {kind: "XV.ListAttr", attr: "billingContact.address"}
697           ]}
698         ]}
699       ]}
700     ]
701   });
702   XV.registerModelList("XM.CustomerRelation", "XV.CustomerList");
703
704   // ..........................................................
705   // CUSTOMER EMAIL PROFILE
706   //
707   enyo.kind({
708     name: "XV.CustomerEmailProfileList",
709     kind: "XV.EmailProfileList",
710     label: "_customerEmailProfiles".loc(),
711     collection: "XM.CustomerEmailProfileCollection"
712   });
713
714   // ..........................................................
715   // CUSTOMER GROUP
716   //
717
718   enyo.kind({
719     name: "XV.CustomerGroupList",
720     kind: "XV.List",
721     label: "_customerGroup".loc(),
722     collection: "XM.CustomerGroupCollection",
723     parameterWidget: "XV.CustomerGroupListParameters",
724     query: {orderBy: [
725       {attribute: 'name'}
726     ]},
727     components: [
728       {kind: "XV.ListItem", components: [
729         {kind: "FittableColumns", components: [
730           {kind: "XV.ListColumn", classes: "short",
731             components: [
732             {kind: "XV.ListAttr", attr: "name", isKey: true}
733           ]},
734           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
735             {kind: "XV.ListAttr", attr: "description"}
736           ]}
737         ]}
738       ]}
739     ]
740   });
741
742   XV.registerModelList("XM.CustomerGroupRelation", "XV.CustomerGroupList");
743
744   // ..........................................................
745   // CUSTOMER PROSPECT
746   //
747
748   enyo.kind({
749     name: "XV.CustomerProspectList",
750     kind: "XV.CustomerList",
751     label: "_customerProspect".loc(),
752     collection: "XM.CustomerProspectListItemCollection",
753     components: [
754       {kind: "XV.ListItem", components: [
755         {kind: "FittableColumns", components: [
756           {kind: "XV.ListColumn", classes: "name-column", components: [
757             {kind: "XV.ListAttr", attr: "number", isKey: true},
758             {kind: "XV.ListAttr", attr: "name"}
759           ]},
760           {kind: "XV.ListColumn", classes: "right-column", components: [
761             {kind: "XV.ListAttr", attr: "contact.phone", },
762             {kind: "XV.ListAttr", attr: "contact.primaryEmail"}
763           ]},
764           {kind: "XV.ListColumn", fit: true, components: [
765             {kind: "XV.ListAttr", attr: "contact.name",
766               placeholder: "_noContact".loc()},
767             {kind: "XV.ListAttr", attr: "contact.address.formatShort"}
768           ]}
769         ]}
770       ]}
771     ],
772   });
773
774   XV.registerModelList("XM.CustomerProspectRelation", "XV.CustomerProspectList");
775
776   // ..........................................................
777   // CUSTOMER SHIPTO
778   //
779   enyo.kind({
780     name: "XV.CustomerShiptoList",
781     kind: "XV.List",
782     collection: "XM.CustomerShiptoRelationCollection",
783     parameterWidget: "XV.CustomerShiptoParameters",
784     query: {orderBy: [
785       {attribute: 'name'}
786     ]},
787     components: [
788       {kind: "XV.ListItem", components: [
789         {kind: "FittableColumns", components: [
790           {kind: "XV.ListColumn", classes: "short",
791             components: [
792             {kind: "XV.ListAttr", attr: "name", isKey: true}
793           ]},
794           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
795             {kind: "XV.ListAttr", attr: "description"}
796           ]}
797         ]}
798       ]}
799     ]
800   });
801   XV.registerModelList("XM.CustomerShiptoRelation", "XV.CustomerShiptoList");
802
803   // ..........................................................
804   // CUSTOMER TYPE LIST
805   //
806
807   enyo.kind({
808     name: "XV.CustomerTypeList",
809     kind: "XV.List",
810     label: "_customerTypes".loc(),
811     collection: "XM.CustomerTypeCollection",
812     query: {orderBy: [
813       {attribute: 'code'}
814     ]},
815     components: [
816       {kind: "XV.ListItem", components: [
817         {kind: "FittableColumns", components: [
818           {kind: "XV.ListColumn", classes: "short",
819             components: [
820             {kind: "XV.ListAttr", attr: "code", isKey: true}
821           ]},
822           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
823             {kind: "XV.ListAttr", attr: "description"}
824           ]}
825         ]}
826       ]}
827     ]
828   });
829
830   XV.registerModelList("XM.CustomerType", "XV.CustomerTypeList");
831
832   // ..........................................................
833   // DEPARTMENT
834   //
835
836   enyo.kind({
837     name: "XV.DepartmentList",
838     kind: "XV.List",
839     label: "_departments".loc(),
840     collection: "XM.DepartmentCollection",
841     parameterWidget: "XV.DepartmentListParameters",
842     query: {orderBy: [
843       {attribute: 'number'}
844     ]},
845     components: [
846       {kind: "XV.ListItem", components: [
847         {kind: "FittableColumns", components: [
848           {kind: "XV.ListColumn", classes: "short",
849             components: [
850             {kind: "XV.ListAttr", attr: "number", isKey: true}
851           ]},
852           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
853             {kind: "XV.ListAttr", attr: "name"}
854           ]}
855         ]}
856       ]}
857     ]
858   });
859
860   // ..........................................................
861   // EMPLOYEE
862   //
863
864   enyo.kind({
865     name: "XV.EmployeeList",
866     kind: "XV.List",
867     label: "_employees".loc(),
868     collection: "XM.EmployeeRelationCollection",
869     query: {orderBy: [
870       {attribute: 'code'}
871     ]},
872     parameterWidget: "XV.EmployeeListParameters",
873     components: [
874       {kind: "XV.ListItem", components: [
875         {kind: "FittableColumns", components: [
876           {kind: "XV.ListColumn", classes: "name-column", components: [
877             {kind: "XV.ListAttr", attr: "code", isKey: true},
878             {kind: "XV.ListAttr", attr: "name"}
879           ]},
880           {kind: "XV.ListColumn", classes: "right-column", components: [
881             {kind: "XV.ListAttr", attr: "contact.phone", },
882             {kind: "XV.ListAttr", attr: "contact.primaryEmail"}
883           ]},
884           {kind: "XV.ListColumn", fit: true, components: [
885             {kind: "XV.ListAttr", attr: "contact.name",
886               placeholder: "_noContact".loc()},
887             {kind: "XV.ListAttr", attr: "contact.address.formatShort"}
888           ]}
889         ]}
890       ]}
891     ]
892   });
893
894   XV.registerModelList("XM.EmployeeRelation", "XV.EmployeeList");
895
896   // ..........................................................
897   // EMPLOYEE GROUP
898   //
899
900   enyo.kind({
901     name: "XV.EmployeeGroupList",
902     kind: "XV.CustomerGroupList",
903     label: "_employeeGroup".loc(),
904     collection: "XM.EmployeeGroupCollection",
905     parameterWidget: "XV.EmployeeGroupListParameters"
906   });
907
908   XV.registerModelList("XM.EmployeeGroupRelation", "XV.EmployeeGroupList");
909
910   // ..........................................................
911   // EXPENSE CATEGORY
912   //
913
914   enyo.kind({
915     name: "XV.ExpenseCategoryList",
916     kind: "XV.List",
917     label: "_expenseCategories".loc(),
918     collection: "XM.ExpenseCategoryCollection",
919     query: {orderBy: [
920       {attribute: 'code'}
921     ]},
922     components: [
923       {kind: "XV.ListItem", components: [
924         {kind: "FittableColumns", components: [
925           {kind: "XV.ListColumn", classes: "short",
926             components: [
927             {kind: "XV.ListAttr", attr: "code", isKey: true}
928           ]},
929           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
930             {kind: "XV.ListAttr", attr: "description"}
931           ]}
932         ]}
933       ]}
934     ]
935   });
936
937   // ..........................................................
938   // FILE
939   //
940
941   enyo.kind({
942     name: "XV.FileList",
943     kind: "XV.List",
944     label: "_files".loc(),
945     collection: "XM.FileRelationCollection",
946     parameterWidget: "XV.FileListParameters",
947     query: {orderBy: [
948       {attribute: 'name'}
949     ]},
950     components: [
951       {kind: "XV.ListItem", components: [
952         {kind: "FittableColumns", components: [
953           {kind: "XV.ListColumn", classes: "short",
954             components: [
955             {kind: "XV.ListAttr", attr: "name", isKey: true}
956           ]},
957           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
958             {kind: "XV.ListAttr", attr: "description"}
959           ]}
960         ]}
961       ]}
962     ]
963   });
964
965   XV.registerModelList("XM.FileRelation", "XV.FileList");
966
967   // ..........................................................
968   // FILTER
969   //
970
971   enyo.kind({
972     name: "XV.FilterList",
973     kind: "XV.List",
974     label: "_filters".loc(),
975     collection: "XM.FilterCollection",
976     query: {
977       orderBy: [{
978         attribute: 'name'
979       }]
980     },
981     events: {
982       onListChange: ""
983     },
984     components: [
985       {kind: "XV.ListItem", components: [
986         {kind: "FittableColumns", components: [
987           {kind: "XV.ListColumn", classes: "short", fit: true,
988             components: [
989             {kind: "XV.ListAttr", attr: "name"}
990           ]},
991           {kind: "XV.ListColumn", classes: "third", components: [
992             {kind: "XV.ListAttr", attr: "shared", formatter: "formatShared"}
993           ]},
994           {kind: "XV.ListColumn",  components: [
995             {tag: "i", classes: "icon-remove list-icon", ontap: "removeRow"}
996           ]},
997           {kind: "XV.ListColumn", components: [
998             {tag: "i", classes: "icon-signout list-icon", ontap: "shareRow"}
999           ]}
1000         ]}
1001       ]}
1002     ],
1003     /**
1004       When the value of the list is changed, bind the add
1005       and remove events of this collection.
1006     */
1007     valueChanged: function () {
1008       this.inherited(arguments);
1009       // bind enyo event to add/remove on collection of models
1010       this.getValue().on("add", this.doListChange(), this);
1011       this.getValue().on("remove", this.doListChange(), this);
1012     },
1013     /**
1014       Formatting function to show the shared text instead of
1015       the boolean value.
1016     */
1017     formatShared: function (value, view, model) {
1018       var shared = model && model.get('shared') ? "_shared".loc() : "";
1019       return shared;
1020     },
1021     /**
1022       Removes the selected row when the "remove" icon is
1023       selected.
1024     */
1025     removeRow: function (inSender, inEvent) {
1026       var index = inEvent.index,
1027         value = this.getValue(),
1028         model = value.models[index],
1029         that = this;
1030       inEvent.model = model;
1031       inEvent.done = function () {
1032         inEvent.delete = true;
1033         that.doListChange(inEvent);
1034       };
1035       this.deleteItem(inEvent);
1036     },
1037     /**
1038       Sets the shared value of the current model when the
1039       "shared" icon is selected.
1040     */
1041     shareRow: function (inSender, inEvent) {
1042       var options = {},
1043         index = inEvent.index,
1044         that = this,
1045         model = this.getValue().models[index];
1046       if (!model.get("shared")) {
1047         model.set("shared", true);
1048         options.success = function (model, resp, options) {
1049           that.reset();
1050         };
1051         model.save(null, options);
1052       }
1053     }
1054   });
1055
1056   // ..........................................................
1057   // FREIGHT CLASS
1058   //
1059
1060   enyo.kind({
1061     name: "XV.FreightClassList",
1062     kind: "XV.List",
1063     label: "_freightClass".loc(),
1064     collection: "XM.FreightClassCollection",
1065     parameterWidget: "XV.FreightClassListParameters",
1066     query: {orderBy: [
1067       {attribute: 'code'}
1068     ]},
1069     components: [
1070       {kind: "XV.ListItem", components: [
1071         {kind: "FittableColumns", components: [
1072           {kind: "XV.ListColumn", classes: "short",
1073             components: [
1074             {kind: "XV.ListAttr", attr: "code", isKey: true}
1075           ]},
1076           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
1077             {kind: "XV.ListAttr", attr: "description"}
1078           ]}
1079         ]}
1080       ]}
1081     ]
1082   });
1083
1084   XV.registerModelList("XM.FreightClassRelation", "XV.FreightClassList");
1085
1086   // ..........................................................
1087   // HONORIFIC
1088   //
1089
1090   enyo.kind({
1091     name: "XV.HonorificList",
1092     kind: "XV.List",
1093     label: "_honorifics".loc(),
1094     collection: "XM.HonorificCollection",
1095     query: {orderBy: [
1096       {attribute: 'code'}
1097     ]},
1098     components: [
1099       {kind: "XV.ListItem", components: [
1100         {kind: "XV.ListColumn", classes: "last", components: [
1101           {kind: "XV.ListAttr", attr: "code", isKey: true}
1102         ]}
1103       ]}
1104     ]
1105   });
1106
1107   // ..........................................................
1108   // INCIDENT
1109   //
1110
1111   enyo.kind({
1112     name: "XV.IncidentList",
1113     kind: "XV.List",
1114     label: "_incidents".loc(),
1115     collection: "XM.IncidentListItemCollection",
1116     query: {orderBy: [
1117       {attribute: 'priorityOrder'},
1118       {attribute: 'updated', descending: true},
1119       {attribute: 'number', descending: true, numeric: true}
1120     ]},
1121     toggleSelected: false,
1122     parameterWidget: "XV.IncidentListParameters",
1123     components: [
1124       {kind: "XV.ListItem", components: [
1125         {kind: "FittableColumns", components: [
1126           {kind: "XV.ListColumn", classes: "first", components: [
1127             {kind: "FittableColumns", components: [
1128               {kind: "XV.ListAttr", attr: "number", isKey: true},
1129               {kind: "XV.ListAttr", attr: "updated", fit: true,
1130                 formatter: "formatDate",
1131                 classes: "right"}
1132             ]},
1133             {kind: "XV.ListAttr", attr: "description"}
1134           ]},
1135           {kind: "XV.ListColumn", classes: "second", components: [
1136             {kind: "XV.ListAttr", attr: "account.name"},
1137             {kind: "XV.ListAttr", attr: "contact.name"}
1138           ]},
1139           {kind: "XV.ListColumn", classes: "third", components: [
1140             {kind: "XV.ListAttr", attr: "getIncidentStatusString"},
1141             {kind: "XV.ListAttr", attr: "assignedTo.username"}
1142           ]},
1143           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
1144             {kind: "XV.ListAttr", attr: "priority.name",
1145               placeholder: "_noPriority".loc()},
1146             {kind: "XV.ListAttr", attr: "category.name",
1147               placeholder: "_noCategory".loc()}
1148           ]}
1149         ]}
1150       ]}
1151     ],
1152     formatDate: function (value, view, model) {
1153       var date = value ? XT.date.applyTimezoneOffset(value, true) : "",
1154         isToday = date ? !XT.date.compareDate(date, new Date()) : false;
1155       view.addRemoveClass("bold", isToday);
1156       return date ? Globalize.format(date, "d") : "";
1157     },
1158     getStyle: function (model) {
1159       var settings = XT.session.getSettings(),
1160         K = XM.Incident,
1161         status = model ? model.get('status') : null,
1162         background,
1163         style;
1164       switch (status)
1165       {
1166       case K.NEW:
1167         background = settings.get('IncidentNewColor');
1168         break;
1169       case K.FEEDBACK:
1170         background = settings.get('IncidentFeedbackColor');
1171         break;
1172       case K.CONFIRMED:
1173         background = settings.get('IncidentConfirmedColor');
1174         break;
1175       case K.ASSIGNED:
1176         background = settings.get('IncidentAssignedColor');
1177         break;
1178       case K.RESOLVED:
1179         background = settings.get('IncidentResolvedColor');
1180         break;
1181       case K.CLOSED:
1182         background = settings.get('IncidentClosedColor');
1183         break;
1184       }
1185       if (background) {
1186         style = "background: " + background + ";";
1187       }
1188       return style;
1189     },
1190     setupItem: function (inSender, inEvent) {
1191       this.inherited(arguments);
1192       var model = this.getValue().models[inEvent.index],
1193         style = this.getStyle(model),
1194         prop,
1195         view;
1196
1197       // Apply background color to all views.
1198       this.$.listItem.setStyle(style);
1199       for (prop in this.$) {
1200         if (this.$.hasOwnProperty(prop) && this.$[prop].getAttr) {
1201           view = this.$[prop];
1202           view.setStyle(style);
1203         }
1204       }
1205       return true;
1206     }
1207   });
1208
1209   XV.registerModelList("XM.IncidentListItem", "XV.IncidentList");
1210   XV.registerModelList("XM.IncidentRelation", "XV.IncidentList");
1211
1212   // ..........................................................
1213   // INCIDENT EMAIL PROFILE
1214   //
1215
1216   enyo.kind({
1217     name: "XV.IncidentEmailProfileList",
1218     kind: "XV.EmailProfileList",
1219     label: "_incidentEmailProfiles".loc(),
1220     collection: "XM.IncidentEmailProfileCollection"
1221   });
1222
1223   // ..........................................................
1224   // INVOICE
1225   //
1226
1227   enyo.kind({
1228     name: "XV.InvoiceList",
1229     kind: "XV.List",
1230     multiSelect: true,
1231     label: "_invoices".loc(),
1232     parameterWidget: "XV.InvoiceListParameters",
1233     collection: "XM.InvoiceListItemCollection",
1234     query: {orderBy: [
1235       {attribute: 'number'}
1236     ]},
1237     actions: [
1238       {name: "void", privilege: "VoidPostedInvoices", prerequisite: "canVoid",
1239         method: "doVoid" },
1240       {name: "post", privilege: "PostMiscInvoices", prerequisite: "canPost",
1241         method: "doPost" },
1242       {name: "print", privilege: "PrintInvoices", method: "doPrint", isViewMethod: true },
1243       {name: "email", privilege: "PrintInvoices", method: "doEmail", isViewMethod: true},
1244       {name: "download", privilege: "PrintInvoices", method: "doDownload",
1245         isViewMethod: true}
1246     ],
1247     components: [
1248       {kind: "XV.ListItem", components: [
1249         {kind: "FittableColumns", components: [
1250           {kind: "XV.ListColumn", classes: "first", components: [
1251             {kind: "FittableColumns", components: [
1252               {kind: "XV.ListAttr", attr: "number", isKey: true, fit: true},
1253               {kind: "XV.ListAttr", attr: "isPosted", fit: true,
1254                 formatter: "formatPosted", style: "padding-left: 24px"},
1255               {kind: "XV.ListAttr", name: "dateField", attr: "invoiceDate",
1256                 formatter: "formatInvoiceDate", classes: "right",
1257                 placeholder: "_noDate".loc()}
1258             ]},
1259             {kind: "FittableColumns", components: [
1260               {kind: "XV.ListAttr", attr: "customer.name"},
1261               {kind: "XV.ListAttr", attr: "total", formatter: "formatTotal",
1262                 classes: "right"}
1263             ]}
1264           ]},
1265           {kind: "XV.ListColumn", classes: "last", components: [
1266             {kind: "XV.ListAttr", formatter: "formatName"},
1267             {kind: "XV.ListAttr", formatter: "formatAddress"}
1268           ]}
1269         ]}
1270       ]}
1271     ],
1272     // some extensions may override this function (i.e. inventory)
1273     formatAddress: function (value, view, model) {
1274       var city = model.get("billtoCity"),
1275         state = model.get("billtoState"),
1276         country = model.get("billtoCountry");
1277       return XM.Address.formatShort(city, state, country);
1278     },
1279     // some extensions may override this function (i.e. inventory)
1280     formatName: function (value, view, model) {
1281       return model.get("billtoName");
1282     },
1283     formatPosted: function (value) {
1284       return value ? "_posted".loc() : "_unposted".loc();
1285     },
1286     formatInvoiceDate: function (value, view, model) {
1287       var isLate = model && !model.get("isPosted") &&
1288         model.get(model.documentDateKey) &&
1289         (XT.date.compareDate(value, new Date()) < 1);
1290       view.addRemoveClass("error", isLate);
1291       return Globalize.format(value, "d");
1292     },
1293     formatTotal: function (value, view, model) {
1294       var currency = model ? model.get("currency") : false,
1295         scale = XT.locale.moneyScale;
1296       return currency ? currency.format(value, scale) : "";
1297     },
1298   });
1299   XV.registerModelList("XM.InvoiceRelation", "XV.InvoiceList");
1300
1301   // ..........................................................
1302   // ITEM
1303   //
1304
1305   enyo.kind({
1306     name: "XV.ItemList",
1307     kind: "XV.List",
1308     label: "_items".loc(),
1309     collection: "XM.ItemListItemCollection",
1310     query: {orderBy: [
1311       {attribute: 'number'}
1312     ]},
1313     parameterWidget: "XV.ItemListParameters",
1314     components: [
1315       {kind: "XV.ListItem", components: [
1316         {kind: "FittableColumns", components: [
1317           {kind: "XV.ListColumn", classes: "first", components: [
1318             {kind: "FittableColumns", components: [
1319               {kind: "XV.ListAttr", attr: "number", isKey: true},
1320               {kind: "XV.ListAttr", attr: "inventoryUnit.name", fit: true,
1321                 classes: "right"}
1322             ]},
1323             {kind: "XV.ListAttr", formatter: "formatDescription"}
1324           ]},
1325           {kind: "XV.ListColumn", classes: "second",
1326             components: [
1327             {kind: "XV.ListAttr", attr: "formatItemType", classes: "italic"},
1328             {kind: "XV.ListAttr", attr: "classCode.code"}
1329           ]},
1330           {kind: "XV.ListColumn", classes: "third", components: [
1331             {kind: "XV.ListAttr", attr: "listPrice", formatter: "formatPrice"},
1332             {kind: "XV.ListAttr", attr: "isFractional", formatter: "formatFractional"}
1333           ]},
1334           {kind: "XV.ListColumn", fit: true, components: [
1335             {kind: "XV.ListAttr", attr: "priceUnit.name", formatter: "formatPriceUnit"},
1336             {kind: "XV.ListAttr", attr: "productCategory.code"}
1337           ]}
1338         ]}
1339       ]}
1340     ],
1341     formatFractional: function (value, view, model) {
1342       return value ? "_fractional".loc() : "";
1343     },
1344     formatPrice: function (value, view, model) {
1345       var sold = model.get("isSold");
1346       if (XT.session.privileges.get("ViewListPrices") && sold) {
1347         var scale = XT.locale.salesPriceScale;
1348         return Globalize.format(value, "c" + scale);
1349       }
1350       view.addRemoveClass("placeholder", true);
1351       if (!sold) {
1352         return "_notSold".loc();
1353       }
1354       return "--";
1355     },
1356     formatPriceUnit: function (value, view, model) {
1357       if (XT.session.privileges.get("ViewListPrices") && model.get("isSold")) {
1358         return value;
1359       }
1360       return "";
1361     },
1362     formatDescription: function (value, view, model) {
1363       var descrip1 = model.get("description1") || "",
1364         descrip2 = model.get("description2") || "",
1365         sep = descrip2 ? " - " : "";
1366       return descrip1 + sep + descrip2;
1367     }
1368   });
1369
1370   XV.registerModelList("XM.ItemRelation", "XV.ItemList");
1371
1372   // ..........................................................
1373   // ITEM GROUP
1374   //
1375
1376   enyo.kind({
1377     name: "XV.ItemGroupList",
1378     kind: "XV.List",
1379     label: "_itemGroups".loc(),
1380     collection: "XM.ItemGroupRelationCollection",
1381     query: {orderBy: [
1382       {attribute: 'name'}
1383     ]},
1384     components: [
1385       {kind: "XV.ListItem", components: [
1386         {kind: "FittableColumns", components: [
1387           {kind: "XV.ListColumn", classes: "short",
1388             components: [
1389             {kind: "XV.ListAttr", attr: "name", isKey: true}
1390           ]},
1391           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
1392             {kind: "XV.ListAttr", attr: "description"}
1393           ]}
1394         ]}
1395       ]}
1396     ]
1397   });
1398
1399   // ..........................................................
1400   // ITEM SITE
1401   //
1402
1403   enyo.kind({
1404     name: "XV.ItemSiteList",
1405     kind: "XV.List",
1406     label: "_itemSites".loc(),
1407     collection: "XM.ItemSiteListItemCollection",
1408     query: {orderBy: [
1409       {attribute: 'item.number'}
1410     ]},
1411     parameterWidget: "XV.ItemSiteListParameters",
1412     components: [
1413       {kind: "XV.ListItem", components: [
1414         {kind: "FittableColumns", components: [
1415           {kind: "XV.ListColumn", classes: "first", components: [
1416             {kind: "FittableColumns", components: [
1417               {kind: "XV.ListAttr", attr: "item.number", isKey: true},
1418               {kind: "XV.ListAttr", attr: "item.barcode", fit: true, classes: "right"}
1419             ]},
1420             {kind: "FittableColumns", components: [
1421               {kind: "XV.ListAttr", attr: "item.description1"},
1422               {kind: "XV.ListAttr", attr: "item.aliases", fit: true,
1423                 formatter: "formatAliases", classes: "right"}
1424             ]}
1425           ]},
1426           {kind: "XV.ListColumn", classes: "second",
1427             components: [
1428             {kind: "XV.ListAttr", attr: "site.code", classes: "bold"},
1429             {kind: "XV.ListAttr", attr: "site.description"}
1430           ]}
1431         ]}
1432       ]}
1433     ],
1434     formatActive: function (value, view, model) {
1435       return value ? "_active".loc() : "";
1436     },
1437     formatAliases: function (value, view, model) {
1438       return value.map(function (model) {
1439         return model.get("aliasNumber");
1440       }).join();
1441     },
1442     formatSold: function (value, view, model) {
1443       return value ? "_sold".loc() : "";
1444     }
1445   });
1446
1447   XV.registerModelList("XM.ItemSiteRelation", "XV.ItemSiteList");
1448
1449   // ..........................................................
1450   // LEDGER ACCOUNT
1451   //
1452
1453   enyo.kind({
1454     name: "XV.LedgerAccountList",
1455     kind: "XV.List",
1456     label: "_ledgerAccounts".loc(),
1457     collection: "XM.LedgerAccountRelationCollection",
1458     query: {orderBy: [
1459       {attribute: 'name'}
1460     ]},
1461     parameterWidget: "XV.LedgerAccountListParameters",
1462     components: [
1463       {kind: "XV.ListItem", components: [
1464         {kind: "FittableColumns", components: [
1465           {kind: "XV.ListColumn", classes: "first", components: [
1466             {kind: "FittableColumns", components: [
1467               {kind: "XV.ListAttr", attr: "name", isKey: true},
1468               {kind: "XV.ListAttr", attr: "getAccountTypeString", fit: true,
1469                 classes: "right"}
1470             ]},
1471             {kind: "XV.ListAttr", attr: "description"}
1472           ]},
1473           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
1474             {kind: "XV.ListAttr", attr: "externalReference"},
1475             {kind: "XV.ListAttr", attr: "isActive", formatter: "formatActive"}
1476           ]}
1477         ]}
1478       ]}
1479     ],
1480     formatActive: function (value, view, model) {
1481       return value ? "" : "_inactive".loc();
1482     }
1483   });
1484
1485   // ..........................................................
1486   // OPPORTUNITY
1487   //
1488
1489   enyo.kind({
1490     name: "XV.OpportunityList",
1491     kind: "XV.List",
1492     collection: "XM.OpportunityListItemCollection",
1493     query: {orderBy: [
1494       {attribute: 'priorityOrder'},
1495       {attribute: 'targetClose'},
1496       {attribute: 'name'},
1497       {attribute: 'number', numeric: true}
1498     ]},
1499     label: "_opportunities".loc(),
1500     parameterWidget: "XV.OpportunityListParameters",
1501     components: [
1502       {kind: "XV.ListItem", components: [
1503         {kind: "FittableColumns", components: [
1504           {kind: "XV.ListColumn", classes: "first", components: [
1505             {kind: "FittableColumns", components: [
1506               {kind: "XV.ListAttr", attr: "number", isKey: true},
1507               {kind: "XV.ListAttr", attr: "targetClose", fit: true,
1508                 placeholder: "_noCloseTarget".loc(),
1509                 classes: "right"}
1510             ]},
1511             {kind: "FittableColumns", components: [
1512               {kind: "XV.ListAttr", attr: "name"},
1513               {kind: "XV.ListAttr", attr: "amount", classes: "right",
1514                 formatter: "formatAmount"}
1515             ]}
1516           ]},
1517           {kind: "XV.ListColumn", classes: "second",
1518             components: [
1519             {kind: "XV.ListAttr", attr: "account.name",
1520               placeholder: "_noAccountName".loc()},
1521             {kind: "XV.ListAttr", attr: "contact.name"}
1522           ]},
1523           {kind: "XV.ListColumn", classes: "third",
1524             components: [
1525             {kind: "XV.ListAttr", attr: "opportunityStage.name",
1526               placeholder: "_noStage".loc()},
1527             {kind: "XV.ListAttr", attr: "assignedTo.username"}
1528           ]},
1529           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
1530             {kind: "XV.ListAttr", attr: "priority.name",
1531               placeholder: "_noPriority".loc()},
1532             {kind: "XV.ListAttr", attr: "opportunityType.name",
1533               placeholder: "_noType".loc()}
1534           ]}
1535         ]}
1536       ]}
1537     ],
1538     formatAmount: function (value, view, model) {
1539       var currency = model ? model.get("currency") : false,
1540         scale = XT.locale.moneyScale;
1541       return currency ? currency.format(value, scale) : "";
1542     }
1543   });
1544
1545   XV.registerModelList("XM.OpportunityListItem", "XV.OpportunityList");
1546   XV.registerModelList("XM.OpportunityRelation", "XV.OpportunityList");
1547
1548   // ..........................................................
1549   // PLANNER CODE
1550   //
1551
1552   enyo.kind({
1553     name: "XV.PlannerCodeList",
1554     kind: "XV.List",
1555     label: "_plannerCodes".loc(),
1556     collection: "XM.PlannerCodeCollection",
1557     query: {orderBy: [
1558       {attribute: 'code'}
1559     ]},
1560     parameterWidget: "XV.PlannerCodeListParameters",
1561     components: [
1562       {kind: "XV.ListItem", components: [
1563         {kind: "FittableColumns", components: [
1564           {kind: "XV.ListColumn", classes: "first", components: [
1565             {kind: "FittableColumns", components: [
1566               {kind: "XV.ListAttr", attr: "code", isKey: true},
1567               {kind: "XV.ListAttr", attr: "name", fit: true, classes: "right"}
1568             ]}
1569           ]}
1570         ]}
1571       ]}
1572     ]
1573   });
1574
1575   XV.registerModelList("XM.PlannerCode", "XV.PlannerCodeList");
1576
1577   // ..........................................................
1578   // PRODUCT CATEGORY
1579   //
1580
1581   enyo.kind({
1582     name: "XV.ProductCategoryList",
1583     kind: "XV.List",
1584     label: "_productCategories".loc(),
1585     collection: "XM.ProductCategoryCollection",
1586     query: {orderBy: [
1587       {attribute: 'code'}
1588     ]},
1589     components: [
1590       {kind: "XV.ListItem", components: [
1591         {kind: "FittableColumns", components: [
1592           {kind: "XV.ListColumn", classes: "short",
1593             components: [
1594             {kind: "XV.ListAttr", attr: "code", isKey: true}
1595           ]},
1596           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
1597             {kind: "XV.ListAttr", attr: "description"}
1598           ]}
1599         ]}
1600       ]}
1601     ]
1602   });
1603
1604   // ..........................................................
1605   // PROJECT
1606   //
1607
1608   enyo.kind({
1609     name: "XV.ProjectList",
1610     kind: "XV.List",
1611     label: "_projects".loc(),
1612     collection: "XM.ProjectListItemCollection",
1613     query: {orderBy: [
1614       {attribute: "number" }
1615     ]},
1616     parameterWidget: "XV.ProjectListParameters",
1617     headerComponents: [
1618       {kind: "FittableColumns", classes: "xv-list-header",
1619         components: [
1620         {kind: "XV.ListColumn", classes: "name-column", components: [
1621           {content: "_name".loc()},
1622           {content: "_description".loc()},
1623           {content: "_account".loc()}
1624         ]},
1625         {kind: "XV.ListColumn", classes: "right-column", components: [
1626           {content: "_dueDate".loc()},
1627           {content: "_priority".loc()},
1628           {content: "_complete".loc()}
1629         ]},
1630         {kind: "XV.ListColumn", classes: "short", components: [
1631           {content: "_status".loc()},
1632           {content: "_assignedTo".loc()},
1633           {content: "_type".loc()}
1634         ]},
1635         {kind: "XV.ListColumn", classes: "right-column"},
1636         {kind: "XV.ListColumn", classes: "right-column", components: [
1637           {content: "_budgeted".loc()},
1638           {content: "_actual".loc()},
1639           {content: "_balance".loc()}
1640         ]}
1641       ]}
1642     ],
1643     components: [
1644       {kind: "XV.ListItem", components: [
1645         {kind: "FittableColumns", components: [
1646           {kind: "XV.ListColumn", classes: "name-column", components: [
1647             {kind: "XV.ListAttr", attr: "number", isKey: true},
1648             {kind: "XV.ListAttr", attr: "name"},
1649             {kind: "XV.ListAttr", attr: "account.name"}
1650           ]},
1651           {kind: "XV.ListColumn", classes: "right-column", components: [
1652             {kind: "XV.ListAttr", attr: "dueDate"},
1653             {kind: "XV.ListAttr", attr: "priority.name",
1654                 placeholder: "_noPriority".loc()},
1655             {kind: "XV.ListAttr", attr: "percentComplete"}
1656           ]},
1657           {kind: "XV.ListColumn", classes: "short",
1658             components: [
1659             {kind: "XV.ListAttr", attr: "getProjectStatusString"},
1660             {kind: "XV.ListAttr", attr: "assignedTo.username",
1661               placeholder: "_noAssignedTo".loc()},
1662             {kind: "XV.ListAttr", attr: "projectType.code",
1663               placeholder: "_noProjectType".loc()},
1664           ]},
1665           {kind: "XV.ListColumn", classes: "right-column", components: [
1666             {kind: "XV.ListAttr", attr: "budgetedExpenses",
1667               classes: "text-align-right", formatter: "formatExpenses"},
1668             {kind: "XV.ListAttr", attr: "actualExpenses",
1669               classes: "text-align-right", formatter: "formatExpenses"},
1670             {kind: "XV.ListAttr", attr: "balanceExpenses",
1671               classes: "text-align-right", formatter: "formatExpenses"}
1672           ]},
1673           {kind: "XV.ListColumn", classes: "right-column", fit: true,
1674             components: [
1675             {kind: "XV.ListAttr", attr: "budgetedHours",
1676               classes: "text-align-right", formatter: "formatHours"},
1677             {kind: "XV.ListAttr", attr: "actualHours",
1678               classes: "text-align-right", formatter: "formatHours"},
1679             {kind: "XV.ListAttr", attr: "balanceHours",
1680               classes: "text-align-right", formatter: "formatHours"}
1681           ]}
1682         ]}
1683       ]}
1684     ],
1685     formatHours: function (value, view, model) {
1686       view.addRemoveClass("error", value < 0);
1687       var scale = XT.locale.quantityScale;
1688       return Globalize.format(value, "n" + scale) + " " + "_hrs".loc();
1689     },
1690     formatExpenses: function (value, view, model) {
1691       view.addRemoveClass("error", value < 0);
1692       var scale = XT.locale.currencyScale;
1693       return Globalize.format(value, "c" + scale);
1694     }
1695
1696   });
1697
1698   XV.registerModelList("XM.ProjectListItem", "XV.ProjectList");
1699   XV.registerModelList("XM.ProjectRelation", "XV.ProjectList");
1700
1701   // ..........................................................
1702   // PROSPECT
1703   //
1704
1705   enyo.kind({
1706     name: "XV.ProspectList",
1707     kind: "XV.List",
1708     label: "_prospects".loc(),
1709     collection: "XM.ProspectRelationCollection",
1710     events: {
1711       onWorkspace: ""
1712     },
1713     actions: [{
1714       name: "convert",
1715       method: "convertProspect",
1716       privilege: "MaintainCustomerMasters",
1717       isViewMethod: true
1718     }],
1719     query: {orderBy: [
1720       {attribute: 'number'}
1721     ]},
1722     parameterWidget: "XV.ProspectListParameters",
1723     components: [
1724       {kind: "XV.ListItem", components: [
1725         {kind: "FittableColumns", components: [
1726           {kind: "XV.ListColumn", classes: "first", components: [
1727             {kind: "FittableColumns", components: [
1728               {kind: "XV.ListAttr", attr: "number", isKey: true},
1729               {kind: "XV.ListAttr", attr: "contact.phone", fit: true,
1730                 classes: "right"}
1731             ]},
1732             {kind: "FittableColumns", components: [
1733               {kind: "XV.ListAttr", attr: "name"},
1734               {kind: "XV.ListAttr", attr: "contact.primaryEmail", classes: "right"}
1735             ]}
1736           ]},
1737           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
1738             {kind: "XV.ListAttr", attr: "contact.name",
1739               placeholder: "_noContact".loc()},
1740             {kind: "XV.ListAttr", attr: "contact.address"}
1741           ]}
1742         ]}
1743       ]}
1744     ],
1745     /**
1746       Convert the prospect from the list into a customer model. The way we
1747       do this is to open a customer workspace and then call the model method
1748       convertFromProspect AFTER the workspace is initialized. That way
1749       the view and the model get bound together correctly. The user will have
1750       to fill out some customer-specific fields, and when they save a new
1751       customer will be created.
1752      */
1753     convertProspect: function (inEvent) {
1754       var model = inEvent.model,
1755         modelId = model.id,
1756         success = function () {
1757           this.getValue().convertFromProspect(modelId);
1758         };
1759
1760       this.doWorkspace({
1761         workspace: "XV.CustomerWorkspace",
1762         attributes: {
1763           name: model.get("name"),
1764           number: model.get("number")
1765         },
1766         success: success,
1767         allowNew: false
1768       });
1769     }
1770   });
1771
1772   XV.registerModelList("XM.ProspectRelation", "XV.ProspectList");
1773
1774   // ..........................................................
1775   // SALES EMAIL PROFILE
1776   //
1777
1778   enyo.kind({
1779     name: "XV.SalesEmailProfileList",
1780     kind: "XV.EmailProfileList",
1781     label: "_salesEmailProfiles".loc(),
1782     collection: "XM.SalesEmailProfileCollection"
1783   });
1784
1785   // ..........................................................
1786   // SALES ORDER
1787   //
1788
1789   enyo.kind({
1790     name: "XV.SalesOrderList",
1791     kind: "XV.List",
1792     label: "_salesOrders".loc(),
1793     collection: "XM.SalesOrderListItemCollection",
1794     parameterWidget: "XV.SalesOrderListParameters",
1795     actions: [
1796       {name: "print", privilege: "ViewSalesOrders", method: "doPrint", isViewMethod: true},
1797       {name: "email", privilege: "ViewSalesOrders", method: "doEmail", isViewMethod: true}
1798     ],
1799     query: {orderBy: [
1800       {attribute: 'number'}
1801     ]},
1802     components: [
1803       {kind: "XV.ListItem", components: [
1804         {kind: "FittableColumns", components: [
1805           {kind: "FittableColumns", components: [
1806             {kind: "XV.ListColumn", classes: "name-column", components: [
1807               {kind: "XV.ListAttr", attr: "number", isKey: true},
1808               {kind: "XV.ListAttr", attr: "customer.name"}
1809             ]},
1810             {kind: "XV.ListColumn", components: [
1811               {kind: "XV.ListAttr", attr: "formatStatus"}
1812             ]},
1813             {kind: "XV.ListColumn", classes: "right-column", components: [
1814               {kind: "XV.ListAttr", attr: "scheduleDate",
1815                 placeholder: "_noSchedule".loc()},
1816               {kind: "XV.ListAttr", attr: "total", formatter: "formatTotal"}
1817             ]},
1818             {kind: "XV.ListColumn", fit: true, components: [
1819               {kind: "XV.ListAttr", formatter: "formatName"},
1820               {kind: "XV.ListAttr", formatter: "formatShiptoOrBillto"}
1821             ]}
1822           ]}
1823         ]}
1824       ]}
1825     ],
1826     formatBillto: function (value, view, model) {
1827       var city = model.get("billtoCity"),
1828         state = model.get("billtoState"),
1829         country = model.get("billtoCountry");
1830       return XM.Address.formatShort(city, state, country);
1831     },
1832     /**
1833       Returns Shipto Name if one exists, otherwise Billto Name.
1834     */
1835     formatName: function (value, view, model) {
1836       return model.get("shiptoName") || model.get("billtoName");
1837     },
1838     formatTotal: function (value, view, model) {
1839       var currency = model ? model.get("currency") : false,
1840         scale = XT.locale.moneyScale;
1841       return currency ? currency.format(value, scale) : "";
1842     },
1843
1844     formatShipto: function (value, view, model) {
1845       var city = model.get("shiptoCity"),
1846         state = model.get("shiptoState"),
1847         country = model.get("shiptoCountry");
1848       return XM.Address.formatShort(city, state, country);
1849     },
1850     /**
1851       Returns formatted Shipto City, State and Country if
1852       Shipto Name exists, otherwise Billto location.
1853     */
1854     formatShiptoOrBillto: function (value, view, model) {
1855       var hasShipto = model.get("shiptoName") ? true : false,
1856         cityAttr = hasShipto ? "shiptoCity": "billtoCity",
1857         stateAttr = hasShipto ? "shiptoState" : "billtoState",
1858         countryAttr = hasShipto ? "shiptoCountry" : "billtoCountry",
1859         city = model.get(cityAttr),
1860         state = model.get(stateAttr),
1861         country = model.get(countryAttr);
1862       return XM.Address.formatShort(city, state, country);
1863     }
1864   });
1865
1866   XV.registerModelList("XM.SalesOrderRelation", "XV.SalesOrderList");
1867
1868   // ..........................................................
1869   // QUOTE
1870   //
1871
1872   enyo.kind({
1873     name: "XV.QuoteList",
1874     kind: "XV.SalesOrderList",
1875     query: {orderBy: [
1876       {attribute: 'number'}
1877     ]},
1878     events: {
1879       onNotify: ""
1880     },
1881     actions: [{
1882       name: "convert",
1883       method: "convertQuote",
1884       privilege: "ConvertQuotes",
1885       isViewMethod: true,
1886       notify: false
1887     }],
1888     label: "_quotes".loc(),
1889     collection: "XM.QuoteListItemCollection",
1890     parameterWidget: "XV.QuoteListParameters",
1891     formatDate: function (value, view, model) {
1892       var isLate = model && model.get('expireDate') &&
1893         (XT.date.compareDate(value, new Date()) < 1);
1894       view.addRemoveClass("error", isLate);
1895       return value;
1896     },
1897     convertQuote: function (inEvent) {
1898       var model = inEvent.model,
1899         that = this,
1900         customer = model.get("customer"),
1901         K = XM.CustomerProspectRelation,
1902         attrs,
1903
1904         // In case we are converting a prospect
1905         convertToCustomer = function (resp) {
1906           if (!resp.answer) { return; }
1907
1908           that.doWorkspace({
1909             workspace: "XV.CustomerWorkspace",
1910             attributes: {
1911               number: customer.get("number"),
1912               name: customer.get("name")
1913             },
1914             success: afterCustomerCreated,
1915             callback: convertToSalesOrder,
1916             allowNew: false
1917           });
1918         },
1919
1920         afterCustomerCreated = function () {
1921           this.getValue().convertFromProspect(customer.id);
1922         },
1923
1924         convertToSalesOrder = function () {
1925           XM.SalesOrder.convertFromQuote(model.id, {
1926             success: afterQuoteConvertedSuccess
1927           });
1928         },
1929
1930         afterQuoteConvertedSuccess = function (resp) {
1931           attrs = resp;
1932           that.doWorkspace({
1933             workspace: "XV.SalesOrderWorkspace",
1934             success: afterSalesOrderCreated,
1935             allowNew: false
1936           });
1937         },
1938
1939         afterSalesOrderCreated = function () {
1940           var value = this.getValue(),
1941             gridBox = this.$.salesOrderLineItemBox;
1942
1943           value.setStatus(XM.Model.BUSY_FETCHING);
1944           value.set(attrs);
1945           value.revertStatus();
1946
1947           //Hack to force grid to refresh. Why doesn't it on its own?
1948           gridBox.valueChanged();
1949           gridBox.setDisabled(false);
1950         };
1951
1952
1953       // Get the process started one way or another
1954       if (customer.get("status") === K.PROSPECT_STATUS) {
1955         this.doNotify({
1956           type: XM.Model.QUESTION,
1957           callback: convertToCustomer,
1958           message: "_convertProspect".loc()
1959         });
1960       } else {
1961         convertToSalesOrder();
1962       }
1963     }
1964   });
1965
1966   XV.registerModelList("XM.QuoteRelation", "XV.QuoteList");
1967
1968   // ..........................................................
1969   // REASON CODE
1970   //
1971
1972   enyo.kind({
1973     name: "XV.ReasonCodeList",
1974     kind: "XV.List",
1975     label: "_reasonCodes".loc(),
1976     collection: "XM.ReasonCodeCollection",
1977     query: {orderBy: [
1978       {attribute: 'code'}
1979     ]},
1980     components: [
1981       {kind: "XV.ListItem", components: [
1982         {kind: "FittableColumns", components: [
1983           {kind: "XV.ListColumn", classes: "first",
1984             components: [
1985             {kind: "XV.ListAttr", attr: "code", isKey: true}
1986           ]},
1987           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
1988             {kind: "XV.ListAttr", attr: "description"}
1989           ]}
1990         ]}
1991       ]}
1992     ]
1993   });
1994
1995   // ..........................................................
1996   // RETURN
1997   //
1998
1999   enyo.kind({
2000     name: "XV.ReturnList",
2001     kind: "XV.InvoiceList",
2002     label: "_returns".loc(),
2003     multiSelect: false,
2004     parameterWidget: "XV.ReturnListParameters",
2005     collection: "XM.ReturnListItemCollection",
2006     actions: [
2007       {name: "void", privilege: "VoidPostedARCreditMemos",
2008         prerequisite: "canVoid", method: "doVoid" },
2009       {name: "post", privilege: "PostARDocuments",
2010         prerequisite: "canPost", method: "doPost" },
2011       {name: "print", privilege: "PrintCreditMemos",
2012         method: "doPrint" }
2013     ],
2014     create: function () {
2015       this.inherited(arguments);
2016       this.$.dateField.setAttr("returnDate");
2017     }
2018   });
2019   XV.registerModelList("XM.ReturnRelation", "XV.ReturnList");
2020
2021   // ..........................................................
2022   // SALE TYPE
2023   //
2024
2025   enyo.kind({
2026     name: "XV.SaleTypeList",
2027     kind: "XV.List",
2028     label: "_saleTypes".loc(),
2029     collection: "XM.SaleTypeCollection",
2030     parameterWidget: "XV.SaleTypeListParameters",
2031     query: {orderBy: [
2032       {attribute: 'code'}
2033     ]},
2034     components: [
2035       {kind: "XV.ListItem", components: [
2036         {kind: "FittableColumns", components: [
2037           {kind: "XV.ListColumn", classes: "short",
2038             components: [
2039             {kind: "XV.ListAttr", attr: "code", isKey: true}
2040           ]},
2041           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
2042             {kind: "XV.ListAttr", attr: "description"}
2043           ]}
2044         ]}
2045       ]}
2046     ]
2047   });
2048
2049   XV.registerModelList("XM.SaleTypeRelation", "XV.SaleTypeList");
2050
2051   // ..........................................................
2052   // SALES REP
2053   //
2054
2055   enyo.kind({
2056     name: "XV.SalesRepList",
2057     kind: "XV.List",
2058     label: "_salesRep".loc(),
2059     collection: "XM.SalesRepCollection",
2060     parameterWidget: "XV.SalesRepListParameters",
2061     query: {orderBy: [
2062       {attribute: 'number'}
2063     ]},
2064     components: [
2065       {kind: "XV.ListItem", components: [
2066         {kind: "FittableColumns", components: [
2067           {kind: "XV.ListColumn", classes: "short",
2068             components: [
2069             {kind: "XV.ListAttr", attr: "number", isKey: true}
2070           ]},
2071           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
2072             {kind: "XV.ListAttr", attr: "name"}
2073           ]}
2074         ]}
2075       ]}
2076     ]
2077   });
2078
2079   XV.registerModelList("XM.SalesRepRelation", "XV.SalesRepList");
2080
2081   // ..........................................................
2082   // SITE
2083   //
2084
2085   enyo.kind({
2086     name: "XV.SiteList",
2087     kind: "XV.List",
2088     label: "_sites".loc(),
2089     collection: "XM.SiteListItemCollection",
2090     query: {orderBy: [
2091       {attribute: 'code'}
2092     ]},
2093     parameterWidget: "XV.SiteListParameters",
2094     components: [
2095       {kind: "XV.ListItem", components: [
2096         {kind: "FittableColumns", components: [
2097           {kind: "XV.ListColumn", classes: "first", components: [
2098             {kind: "FittableColumns", components: [
2099               {kind: "XV.ListAttr", attr: "code", isKey: true},
2100               {kind: "XV.ListAttr", attr: "description", fit: true, classes: "right"}
2101             ]},
2102             {kind: "XV.ListAttr", attr: "siteType.description"}
2103           ]},
2104         ]}
2105       ]}
2106     ]
2107   });
2108
2109   XV.registerModelList("XM.SiteRelation", "XV.SiteList");
2110
2111   // ..........................................................
2112   // SHIFT
2113   //
2114
2115   enyo.kind({
2116     name: "XV.ShiftList",
2117     kind: "XV.List",
2118     label: "_shifts".loc(),
2119     collection: "XM.ShiftCollection",
2120     parameterWidget: "XV.ShiftListParameters",
2121     query: {orderBy: [
2122       {attribute: 'number'}
2123     ]},
2124     components: [
2125       {kind: "XV.ListItem", components: [
2126         {kind: "FittableColumns", components: [
2127           {kind: "XV.ListColumn", classes: "short",
2128             components: [
2129             {kind: "XV.ListAttr", attr: "number", isKey: true}
2130           ]},
2131           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
2132             {kind: "XV.ListAttr", attr: "name"}
2133           ]}
2134         ]}
2135       ]}
2136     ]
2137   });
2138
2139   // ..........................................................
2140   // SHIP VIA
2141   //
2142
2143   enyo.kind({
2144     name: "XV.ShipViaList",
2145     kind: "XV.List",
2146     label: "_shipVias".loc(),
2147     collection: "XM.ShipViaCollection",
2148     query: {orderBy: [
2149       {attribute: 'code'}
2150     ]},
2151     components: [
2152       {kind: "XV.ListItem", components: [
2153         {kind: "FittableColumns", components: [
2154           {kind: "XV.ListColumn", classes: "short",
2155             components: [
2156             {kind: "XV.ListAttr", attr: "code", isKey: true}
2157           ]},
2158           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
2159             {kind: "XV.ListAttr", attr: "description"}
2160           ]}
2161         ]}
2162       ]}
2163     ]
2164   });
2165   XV.registerModelList("XM.ShipVia", "XV.ShipViaList");
2166
2167   // ..........................................................
2168   // SHIP ZONE
2169   //
2170
2171   enyo.kind({
2172     name: "XV.ShipZoneList",
2173     kind: "XV.List",
2174     label: "_shipZones".loc(),
2175     collection: "XM.ShipZoneCollection",
2176     parameterWidget: "XV.ShipZoneListParameters",
2177     query: {orderBy: [
2178       {attribute: 'name'}
2179     ]},
2180     components: [
2181       {kind: "XV.ListItem", components: [
2182         {kind: "FittableColumns", components: [
2183           {kind: "XV.ListColumn", classes: "short",
2184             components: [
2185             {kind: "XV.ListAttr", attr: "name", isKey: true}
2186           ]},
2187           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
2188             {kind: "XV.ListAttr", attr: "description"}
2189           ]}
2190         ]}
2191       ]}
2192     ]
2193   });
2194
2195   XV.registerModelList("XM.ShipZoneRelation", "XV.ShipZoneList");
2196
2197   // ..........................................................
2198   // TAX ASSIGNMENT
2199   //
2200
2201   enyo.kind({
2202     name: "XV.TaxAssignmentList",
2203     kind: "XV.List",
2204     label: "_taxAssignment".loc(),
2205     collection: "XM.TaxAssignmentCollection",
2206     parameterWidget: "XV.TaxAssignmentListParameters",
2207     query: {orderBy: [
2208       {attribute: 'tax'}
2209     ]},
2210     components: [
2211       {kind: "XV.ListItem", components: [
2212         {kind: "FittableColumns", components: [
2213           {kind: "XV.ListColumn", classes: "short", components: [
2214             {kind: "XV.ListAttr", attr: "tax.code", isKey: true}
2215           ]},
2216           {kind: "XV.ListColumn", classes: "short", components: [
2217             {kind: "XV.ListAttr", attr: "taxType.name"}
2218           ]}
2219         ]}
2220       ]}
2221     ]
2222   });
2223
2224   // ..........................................................
2225   // TAX AUTHORITY
2226   //
2227
2228   enyo.kind({
2229     name: "XV.TaxAuthorityList",
2230     kind: "XV.List",
2231     label: "_taxAuthority".loc(),
2232     collection: "XM.TaxAuthorityCollection",
2233     parameterWidget: "XV.TaxAuthorityListParameters",
2234     query: {orderBy: [
2235       {attribute: 'code'}
2236     ]},
2237     components: [
2238       {kind: "XV.ListItem", components: [
2239         {kind: "FittableColumns", components: [
2240           {kind: "XV.ListColumn", classes: "short",
2241             components: [
2242             {kind: "XV.ListAttr", attr: "code", isKey: true}
2243           ]},
2244           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
2245             {kind: "XV.ListAttr", attr: "name"}
2246           ]}
2247         ]}
2248       ]}
2249     ]
2250   });
2251
2252   // ..........................................................
2253   // TAX CODE
2254   //
2255
2256   enyo.kind({
2257     name: "XV.TaxCodeList",
2258     kind: "XV.List",
2259     label: "_taxCode".loc(),
2260     collection: "XM.TaxCodeCollection",
2261     parameterWidget: "XV.TaxCodeListParameters",
2262     query: {orderBy: [
2263       {attribute: 'code'}
2264     ]},
2265     components: [
2266       {kind: "XV.ListItem", components: [
2267         {kind: "FittableColumns", components: [
2268           {kind: "XV.ListColumn", classes: "short",
2269             components: [
2270             {kind: "XV.ListAttr", attr: "code", isKey: true}
2271           ]},
2272           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
2273             {kind: "XV.ListAttr", attr: "description"}
2274           ]}
2275         ]}
2276       ]}
2277     ]
2278   });
2279
2280   XV.registerModelList("XM.TaxCodeRelation", "XV.TaxCodeList");
2281
2282   // ..........................................................
2283   // TAX CLASS
2284   //
2285
2286   enyo.kind({
2287     name: "XV.TaxClassList",
2288     kind: "XV.List",
2289     label: "_taxClass".loc(),
2290     collection: "XM.TaxClassCollection",
2291     parameterWidget: "XV.TaxClassListParameters",
2292     query: {orderBy: [
2293       {attribute: 'code'}
2294     ]},
2295     components: [
2296       {kind: "XV.ListItem", components: [
2297         {kind: "FittableColumns", components: [
2298           {kind: "XV.ListColumn", classes: "short",
2299             components: [
2300             {kind: "XV.ListAttr", attr: "code", isKey: true}
2301           ]},
2302           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
2303             {kind: "XV.ListAttr", attr: "description"}
2304           ]}
2305         ]}
2306       ]}
2307     ]
2308   });
2309
2310   XV.registerModelList("XM.TaxClassRelation", "XV.TaxClassList");
2311
2312   // ..........................................................
2313   // TAX RATE
2314   //
2315
2316   enyo.kind({
2317     name: "XV.TaxRateList",
2318     kind: "XV.List",
2319     label: "_taxRate".loc(),
2320     collection: "XM.TaxRateCollection",
2321     parameterWidget: "XV.TaxRateListParameters",
2322     query: {orderBy: [
2323       {attribute: 'tax'}
2324     ]},
2325     components: [
2326       {kind: "XV.ListItem", components: [
2327         {kind: "FittableColumns", components: [
2328           {kind: "XV.ListColumn", classes: "short", components: [
2329             {kind: "XV.ListAttr", attr: "tax.code", isKey: true}
2330           ]},
2331           {kind: "XV.ListColumn", classes: "short", components: [
2332             {kind: "XV.ListAttr", attr: "percent"}
2333           ]}
2334         ]}
2335       ]}
2336     ]
2337   });
2338
2339   // ..........................................................
2340   // TAX TYPE
2341   //
2342
2343   enyo.kind({
2344     name: "XV.TaxTypeList",
2345     kind: "XV.List",
2346     label: "_taxType".loc(),
2347     collection: "XM.TaxTypeCollection",
2348     parameterWidget: "XV.TaxTypeListParameters",
2349     query: {orderBy: [
2350       {attribute: 'name'}
2351     ]},
2352     components: [
2353       {kind: "XV.ListItem", components: [
2354         {kind: "FittableColumns", components: [
2355           {kind: "XV.ListColumn", classes: "short",
2356             components: [
2357             {kind: "XV.ListAttr", attr: "name", isKey: true}
2358           ]},
2359           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
2360             {kind: "XV.ListAttr", attr: "description"}
2361           ]}
2362         ]}
2363       ]}
2364     ]
2365   });
2366
2367   XV.registerModelList("XM.TaxTypeRelation", "XV.TaxTypeList");
2368
2369   // ..........................................................
2370   // TAX ZONE
2371   //
2372
2373   enyo.kind({
2374     name: "XV.TaxZoneList",
2375     kind: "XV.List",
2376     label: "_taxZone".loc(),
2377     collection: "XM.TaxZoneCollection",
2378     parameterWidget: "XV.TaxZoneListParameters",
2379     query: {orderBy: [
2380       {attribute: 'code'}
2381     ]},
2382     components: [
2383       {kind: "XV.ListItem", components: [
2384         {kind: "FittableColumns", components: [
2385           {kind: "XV.ListColumn", classes: "short",
2386             components: [
2387             {kind: "XV.ListAttr", attr: "code", isKey: true}
2388           ]},
2389           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
2390             {kind: "XV.ListAttr", attr: "description"}
2391           ]}
2392         ]}
2393       ]}
2394     ]
2395   });
2396
2397   XV.registerModelList("XM.TaxZoneRelation", "XV.TaxZoneList");
2398
2399   // ..........................................................
2400   // TERMS
2401   //
2402
2403   enyo.kind({
2404     name: "XV.TermsList",
2405     kind: "XV.List",
2406     label: "_terms".loc(),
2407     collection: "XM.TermsCollection",
2408     parameterWidget: "XV.TermsListParameters",
2409     query: {orderBy: [
2410       {attribute: 'code'}
2411     ]},
2412     components: [
2413       {kind: "XV.ListItem", components: [
2414         {kind: "FittableColumns", components: [
2415           {kind: "XV.ListColumn", classes: "short",
2416             components: [
2417             {kind: "XV.ListAttr", attr: "code", isKey: true}
2418           ]},
2419           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
2420             {kind: "XV.ListAttr", attr: "description"}
2421           ]}
2422         ]}
2423       ]}
2424     ]
2425   });
2426
2427   XV.registerModelList("XM.TermsRelation", "XV.TermsList");
2428
2429   // ..........................................................
2430   // TO DO
2431   //
2432
2433   enyo.kind({
2434     name: "XV.ToDoList",
2435     kind: "XV.List",
2436     label: "_toDo".loc(),
2437     collection: "XM.ToDoListItemCollection",
2438     parameterWidget: "XV.ToDoListParameters",
2439     query: {orderBy: [
2440       {attribute: 'priorityOrder'},
2441       {attribute: 'dueDate'},
2442       {attribute: 'name'}
2443     ]},
2444     components: [
2445       {kind: "XV.ListItem", components: [
2446         {kind: "FittableColumns", components: [
2447           {kind: "XV.ListColumn", classes: "first", components: [
2448             {kind: "FittableColumns", components: [
2449               {kind: "XV.ListAttr", attr: "name", isKey: true},
2450               {kind: "XV.ListAttr", attr: "dueDate", fit: true,
2451                 placeholder: "_noDueDate".loc(), classes: "right"}
2452             ]},
2453             {kind: "XV.ListAttr", attr: "description",
2454               placeholder: "_noDescription".loc()}
2455           ]},
2456           {kind: "XV.ListColumn", classes: "second",
2457             components: [
2458             {kind: "XV.ListAttr", attr: "account.name",
2459               placeholder: "_noAccountName".loc()},
2460             {kind: "XV.ListAttr", attr: "contact.name"}
2461           ]},
2462           {kind: "XV.ListColumn", classes: "third",
2463             components: [
2464             {kind: "XV.ListAttr", attr: "getToDoStatusString"},
2465             {kind: "XV.ListAttr", attr: "owner.username"}
2466           ]},
2467           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
2468             {kind: "XV.ListAttr", attr: "priority.name",
2469               placeholder: "_noPriority".loc()}
2470           ]}
2471         ]}
2472       ]}
2473     ]
2474   });
2475
2476   XV.registerModelList("XM.ToDoRelation", "XV.ToDoList");
2477
2478   // ..........................................................
2479   // URL
2480   //
2481
2482   enyo.kind({
2483     name: "XV.UrlList",
2484     kind: "XV.List",
2485     label: "_urls".loc(),
2486     collection: "XM.UrlCollection",
2487     parameterWidget: "XV.UrlListParameters",
2488     query: {orderBy: [
2489       {attribute: 'name'}
2490     ]},
2491     components: [
2492       {kind: "XV.ListItem", components: [
2493         {kind: "FittableColumns", components: [
2494           {kind: "XV.ListColumn", classes: "short",
2495             components: [
2496             {kind: "XV.ListAttr", attr: "name", isKey: true}
2497           ]},
2498           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
2499             {kind: "XV.ListAttr", attr: "path"}
2500           ]}
2501         ]}
2502       ]}
2503     ]
2504   });
2505
2506   XV.registerModelList("XM.Url", "XV.UrlList");
2507
2508   // ..........................................................
2509   // USER ACCOUNT
2510   //
2511
2512   enyo.kind({
2513     name: "XV.UserAccountList",
2514     kind: "XV.List",
2515     label: "_userAccounts".loc(),
2516     collection: "XM.UserAccountRelationCollection",
2517     parameterWidget: "XV.UserAccountListParameters",
2518     query: {orderBy: [
2519       {attribute: 'username'}
2520     ]},
2521     components: [
2522       {kind: "XV.ListItem", components: [
2523         {kind: "FittableColumns", components: [
2524           {kind: "XV.ListColumn", classes: "short", components: [
2525             {kind: "XV.ListAttr", attr: "username", isKey: true}
2526           ]},
2527           {kind: "XV.ListColumn", classes: "short", components: [
2528             {kind: "XV.ListAttr", attr: "propername"}
2529           ]}
2530         ]}
2531       ]}
2532     ]
2533   });
2534   
2535   XV.registerModelList("XM.UserAccountRelation", "XV.UserAccountList");
2536
2537   // ..........................................................
2538   // STATES AND COUNTRIES
2539   //
2540
2541   enyo.kind({
2542     name: "XV.AbbreviationList",
2543     kind: "XV.List",
2544     components: [
2545       {kind: "XV.ListItem", components: [
2546         {kind: "FittableColumns", components: [
2547           {kind: "XV.ListColumn", classes: "short",
2548             components: [
2549             {kind: "XV.ListAttr", attr: "abbreviation", isKey: true}
2550           ]},
2551           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
2552             {kind: "XV.ListAttr", attr: "name"}
2553           ]}
2554         ]}
2555       ]}
2556     ]
2557   });
2558
2559   enyo.kind({
2560     name: "XV.StateList",
2561     kind: "XV.AbbreviationList",
2562     label: "_states".loc(),
2563     collection: "XM.StateCollection",
2564     query: {orderBy: [{ attribute: 'abbreviation' }] }
2565   });
2566
2567   enyo.kind({
2568     name: "XV.CountryList",
2569     kind: "XV.AbbreviationList",
2570     label: "_countries".loc(),
2571     collection: "XM.CountryCollection",
2572     query: {orderBy: [
2573       {attribute: 'abbreviation'}
2574     ]}
2575   });
2576
2577   // ..........................................................
2578   // VENDOR
2579   //
2580
2581   enyo.kind({
2582     name: "XV.VendorList",
2583     kind: "XV.List",
2584     label: "_vendors".loc(),
2585     collection: "XM.VendorListItemCollection",
2586     query: {orderBy: [
2587       {attribute: 'number'}
2588     ]},
2589     parameterWidget: "XV.VendorListParameters",
2590     components: [
2591       {kind: "XV.ListItem", components: [
2592         {kind: "FittableColumns", components: [
2593           {kind: "XV.ListColumn", classes: "first", components: [
2594             {kind: "FittableColumns", components: [
2595               {kind: "XV.ListAttr", attr: "number", isKey: true},
2596               {kind: "XV.ListAttr", attr: "primaryContact.phone", fit: true,
2597                 classes: "right"}
2598             ]},
2599             {kind: "FittableColumns", components: [
2600               {kind: "XV.ListAttr", attr: "name"},
2601               {kind: "XV.ListAttr", attr: "primaryContact.primaryEmail", classes: "right"}
2602             ]}
2603           ]},
2604           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
2605             {kind: "XV.ListAttr", attr: "primaryContact.name",
2606               placeholder: "_noContact".loc()},
2607             {kind: "XV.ListAttr", attr: "address"}
2608           ]}
2609         ]}
2610       ]}
2611     ]
2612   });
2613
2614   XV.registerModelList("XM.VendorRelation", "XV.VendorList");
2615
2616   // ..........................................................
2617   // VENDOR ADDRESS
2618   //
2619
2620   enyo.kind({
2621     name: "XV.VendorAddressList",
2622     kind: "XV.List",
2623     collection: "XM.VendorAddressRelationCollection",
2624     parameterWidget: "XV.VendorAddressParameters",
2625     query: {orderBy: [
2626       {attribute: 'code'}
2627     ]},
2628     components: [
2629       {kind: "XV.ListItem", components: [
2630         {kind: "FittableColumns", components: [
2631           {kind: "XV.ListColumn", classes: "short",
2632             components: [
2633             {kind: "XV.ListAttr", attr: "code", isKey: true}
2634           ]},
2635           {kind: "XV.ListColumn", fit: true, components: [
2636             {kind: "XV.ListAttr", attr: "name"},
2637             {kind: "XV.ListAttr", formatter: "formatAddress",
2638               classes: "xv-addresslist-attr", allowHtml: true}
2639           ]}
2640         ]}
2641       ]}
2642     ],
2643     formatAddress: function (value, view, model) {
2644       var address = model.get("address");
2645       return address.format(true);
2646     }
2647   });
2648
2649   XV.registerModelList("XM.VendarAddressRelation", "XV.VendorAddressList");
2650
2651   enyo.kind({
2652     name: "XV.NameList",
2653     kind: "XV.List",
2654     query: {orderBy: [
2655       {attribute: 'name'}
2656     ]},
2657     components: [
2658       {kind: "XV.ListItem", components: [
2659         {kind: "FittableColumns", components: [
2660           {kind: "XV.ListColumn", classes: "first",
2661             components: [
2662             {kind: "XV.ListAttr", attr: "name", isKey: true}
2663           ]}
2664         ]}
2665       ]}
2666     ],
2667     /**
2668       All of these lists follow a very similar naming convention.
2669       Apply that convention unless the list overrides the label
2670       or collection attribute.
2671     */
2672     create: function () {
2673       var kindName = this.kind.substring(0, this.kind.length - 4).substring(3);
2674       if (!this.getLabel()) {
2675         this.setLabel(this.determineLabel(kindName));
2676       }
2677       if (!this.getCollection()) {
2678         this.setCollection("XM." + kindName + "Collection");
2679       }
2680       this.inherited(arguments);
2681     },
2682
2683     determineLabel: function (kindName) {
2684       return ("_" + kindName.camelize().pluralize()).loc();
2685     }
2686   });
2687
2688   // ..........................................................
2689   // INCIDENT CATEGORIES, RESOLUTIONS, SEVERITIES,
2690   // PRIORITIES,
2691   // OPPORTUNITY SOURCES, STAGES, TYPES,
2692   //
2693   // Basically anything whose rows are name and description
2694   //
2695   enyo.kind({
2696     name: "XV.NameDescriptionList",
2697     kind: "XV.NameList",
2698     components: [
2699       {kind: "XV.ListItem", components: [
2700         {kind: "FittableColumns", components: [
2701           {kind: "XV.ListColumn", classes: "short",
2702             components: [
2703             {kind: "XV.ListAttr", attr: "name", isKey: true}
2704           ]},
2705           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
2706             {kind: "XV.ListAttr", attr: "description"}
2707           ]}
2708         ]}
2709       ]}
2710     ]
2711   });
2712
2713   enyo.kind({
2714     name: "XV.IncidentCategoryList",
2715     kind: "XV.NameDescriptionList"
2716   });
2717
2718   enyo.kind({
2719     name: "XV.IncidentResolutionList",
2720     kind: "XV.NameDescriptionList"
2721   });
2722
2723   enyo.kind({
2724     name: "XV.IncidentSeverityList",
2725     kind: "XV.NameDescriptionList"
2726   });
2727
2728   enyo.kind({
2729     name: "XV.PriorityList",
2730     kind: "XV.NameDescriptionList"
2731   });
2732
2733   enyo.kind({
2734     name: "XV.UnitList",
2735     kind: "XV.NameDescriptionList"
2736   });
2737
2738   enyo.kind({
2739     name: "XV.OpportunitySourceList",
2740     kind: "XV.NameDescriptionList",
2741     published: {
2742       query: {orderBy: [{ attribute: 'name' }] }
2743     }
2744   });
2745
2746   enyo.kind({
2747     name: "XV.OpportunityStageList",
2748     kind: "XV.NameDescriptionList",
2749     published: {
2750       query: {orderBy: [{ attribute: 'name' }] }
2751     }
2752   });
2753
2754   enyo.kind({
2755     name: "XV.OpportunityTypeList",
2756     kind: "XV.NameDescriptionList",
2757     published: {
2758       query: {orderBy: [{ attribute: 'name' }] }
2759     }
2760   });
2761
2762   enyo.kind({
2763     name: "XV.SiteTypeList",
2764     kind: "XV.NameDescriptionList",
2765     collection: "XM.SiteTypeCollection"
2766   });
2767
2768   enyo.kind({
2769     name: "XV.UserAccountRoleList",
2770     kind: "XV.NameDescriptionList",
2771     collection: "XM.UserAccountRoleListItemCollection"
2772   });
2773
2774   enyo.kind({
2775     name: "XV.CharacteristicList",
2776     kind: "XV.NameList",
2777     collection: "XM.CharacteristicCollection"
2778   });
2779 }());