Merge pull request #1 from shackbarth/keith1
[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", classes: "descr", components: [
694             {kind: "XV.ListAttr", attr: "billingContact.name",
695               placeholder: "_noContact".loc()},
696             {kind: "XV.ListAttr", attr: "billingContact.address"}
697           ]},
698           {kind: "XV.ListColumn", fit: true, components: [
699             {kind: "XV.ListAttr", attr: "customerType.code"}
700           ]}
701         ]}
702       ]}
703     ]
704   });
705   XV.registerModelList("XM.CustomerRelation", "XV.CustomerList");
706
707   // ..........................................................
708   // CUSTOMER EMAIL PROFILE
709   //
710   enyo.kind({
711     name: "XV.CustomerEmailProfileList",
712     kind: "XV.EmailProfileList",
713     label: "_customerEmailProfiles".loc(),
714     collection: "XM.CustomerEmailProfileCollection"
715   });
716
717   // ..........................................................
718   // CUSTOMER GROUP
719   //
720
721   enyo.kind({
722     name: "XV.CustomerGroupList",
723     kind: "XV.List",
724     label: "_customerGroup".loc(),
725     collection: "XM.CustomerGroupCollection",
726     parameterWidget: "XV.CustomerGroupListParameters",
727     query: {orderBy: [
728       {attribute: 'name'}
729     ]},
730     components: [
731       {kind: "XV.ListItem", components: [
732         {kind: "FittableColumns", components: [
733           {kind: "XV.ListColumn", classes: "short",
734             components: [
735             {kind: "XV.ListAttr", attr: "name", isKey: true}
736           ]},
737           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
738             {kind: "XV.ListAttr", attr: "description"}
739           ]}
740         ]}
741       ]}
742     ]
743   });
744
745   XV.registerModelList("XM.CustomerGroupRelation", "XV.CustomerGroupList");
746
747   // ..........................................................
748   // CUSTOMER PROSPECT
749   //
750
751   enyo.kind({
752     name: "XV.CustomerProspectList",
753     kind: "XV.CustomerList",
754     label: "_customerProspect".loc(),
755     collection: "XM.CustomerProspectListItemCollection",
756     components: [
757       {kind: "XV.ListItem", components: [
758         {kind: "FittableColumns", components: [
759           {kind: "XV.ListColumn", classes: "name-column", components: [
760             {kind: "XV.ListAttr", attr: "number", isKey: true},
761             {kind: "XV.ListAttr", attr: "name"}
762           ]},
763           {kind: "XV.ListColumn", classes: "right-column", components: [
764             {kind: "XV.ListAttr", attr: "contact.phone", },
765             {kind: "XV.ListAttr", attr: "contact.primaryEmail"}
766           ]},
767           {kind: "XV.ListColumn", fit: true, components: [
768             {kind: "XV.ListAttr", attr: "contact.name",
769               placeholder: "_noContact".loc()},
770             {kind: "XV.ListAttr", attr: "contact.address.formatShort"}
771           ]}
772         ]}
773       ]}
774     ],
775   });
776
777   XV.registerModelList("XM.CustomerProspectRelation", "XV.CustomerProspectList");
778
779   // ..........................................................
780   // CUSTOMER SHIPTO
781   //
782   enyo.kind({
783     name: "XV.CustomerShiptoList",
784     kind: "XV.List",
785     collection: "XM.CustomerShiptoRelationCollection",
786     parameterWidget: "XV.CustomerShiptoParameters",
787     query: {orderBy: [
788       {attribute: 'name'}
789     ]},
790     components: [
791       {kind: "XV.ListItem", components: [
792         {kind: "FittableColumns", components: [
793           {kind: "XV.ListColumn", classes: "short",
794             components: [
795             {kind: "XV.ListAttr", attr: "name", isKey: true}
796           ]},
797           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
798             {kind: "XV.ListAttr", attr: "description"}
799           ]}
800         ]}
801       ]}
802     ]
803   });
804   XV.registerModelList("XM.CustomerShiptoRelation", "XV.CustomerShiptoList");
805
806   // ..........................................................
807   // CUSTOMER TYPE LIST
808   //
809
810   enyo.kind({
811     name: "XV.CustomerTypeList",
812     kind: "XV.List",
813     label: "_customerTypes".loc(),
814     collection: "XM.CustomerTypeCollection",
815     query: {orderBy: [
816       {attribute: 'code'}
817     ]},
818     components: [
819       {kind: "XV.ListItem", components: [
820         {kind: "FittableColumns", components: [
821           {kind: "XV.ListColumn", classes: "short",
822             components: [
823             {kind: "XV.ListAttr", attr: "code", isKey: true}
824           ]},
825           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
826             {kind: "XV.ListAttr", attr: "description"}
827           ]}
828         ]}
829       ]}
830     ]
831   });
832
833   XV.registerModelList("XM.CustomerType", "XV.CustomerTypeList");
834
835   // ..........................................................
836   // DEPARTMENT
837   //
838
839   enyo.kind({
840     name: "XV.DepartmentList",
841     kind: "XV.List",
842     label: "_departments".loc(),
843     collection: "XM.DepartmentCollection",
844     parameterWidget: "XV.DepartmentListParameters",
845     query: {orderBy: [
846       {attribute: 'number'}
847     ]},
848     components: [
849       {kind: "XV.ListItem", components: [
850         {kind: "FittableColumns", components: [
851           {kind: "XV.ListColumn", classes: "short",
852             components: [
853             {kind: "XV.ListAttr", attr: "number", isKey: true}
854           ]},
855           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
856             {kind: "XV.ListAttr", attr: "name"}
857           ]}
858         ]}
859       ]}
860     ]
861   });
862
863   // ..........................................................
864   // EMPLOYEE
865   //
866
867   enyo.kind({
868     name: "XV.EmployeeList",
869     kind: "XV.List",
870     label: "_employees".loc(),
871     collection: "XM.EmployeeRelationCollection",
872     query: {orderBy: [
873       {attribute: 'code'}
874     ]},
875     parameterWidget: "XV.EmployeeListParameters",
876     components: [
877       {kind: "XV.ListItem", components: [
878         {kind: "FittableColumns", components: [
879           {kind: "XV.ListColumn", classes: "name-column", components: [
880             {kind: "XV.ListAttr", attr: "code", isKey: true},
881             {kind: "XV.ListAttr", attr: "name"}
882           ]},
883           {kind: "XV.ListColumn", classes: "right-column", components: [
884             {kind: "XV.ListAttr", attr: "contact.phone", },
885             {kind: "XV.ListAttr", attr: "contact.primaryEmail"}
886           ]},
887           {kind: "XV.ListColumn", fit: true, components: [
888             {kind: "XV.ListAttr", attr: "contact.name",
889               placeholder: "_noContact".loc()},
890             {kind: "XV.ListAttr", attr: "contact.address.formatShort"}
891           ]}
892         ]}
893       ]}
894     ]
895   });
896
897   XV.registerModelList("XM.EmployeeRelation", "XV.EmployeeList");
898
899   // ..........................................................
900   // EMPLOYEE GROUP
901   //
902
903   enyo.kind({
904     name: "XV.EmployeeGroupList",
905     kind: "XV.CustomerGroupList",
906     label: "_employeeGroup".loc(),
907     collection: "XM.EmployeeGroupCollection",
908     parameterWidget: "XV.EmployeeGroupListParameters"
909   });
910
911   XV.registerModelList("XM.EmployeeGroupRelation", "XV.EmployeeGroupList");
912
913   // ..........................................................
914   // EXPENSE CATEGORY
915   //
916
917   enyo.kind({
918     name: "XV.ExpenseCategoryList",
919     kind: "XV.List",
920     label: "_expenseCategories".loc(),
921     collection: "XM.ExpenseCategoryCollection",
922     query: {orderBy: [
923       {attribute: 'code'}
924     ]},
925     components: [
926       {kind: "XV.ListItem", components: [
927         {kind: "FittableColumns", components: [
928           {kind: "XV.ListColumn", classes: "short",
929             components: [
930             {kind: "XV.ListAttr", attr: "code", isKey: true}
931           ]},
932           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
933             {kind: "XV.ListAttr", attr: "description"}
934           ]}
935         ]}
936       ]}
937     ]
938   });
939
940   // ..........................................................
941   // FILE
942   //
943
944   enyo.kind({
945     name: "XV.FileList",
946     kind: "XV.List",
947     label: "_files".loc(),
948     collection: "XM.FileRelationCollection",
949     parameterWidget: "XV.FileListParameters",
950     query: {orderBy: [
951       {attribute: 'name'}
952     ]},
953     components: [
954       {kind: "XV.ListItem", components: [
955         {kind: "FittableColumns", components: [
956           {kind: "XV.ListColumn", classes: "short",
957             components: [
958             {kind: "XV.ListAttr", attr: "name", isKey: true}
959           ]},
960           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
961             {kind: "XV.ListAttr", attr: "description"}
962           ]}
963         ]}
964       ]}
965     ]
966   });
967
968   XV.registerModelList("XM.FileRelation", "XV.FileList");
969
970   // ..........................................................
971   // FILTER
972   //
973
974   enyo.kind({
975     name: "XV.FilterList",
976     kind: "XV.List",
977     label: "_filters".loc(),
978     collection: "XM.FilterCollection",
979     query: {
980       orderBy: [{
981         attribute: 'name'
982       }]
983     },
984     events: {
985       onListChange: ""
986     },
987     components: [
988       {kind: "XV.ListItem", components: [
989         {kind: "FittableColumns", components: [
990           {kind: "XV.ListColumn", classes: "short", fit: true,
991             components: [
992             {kind: "XV.ListAttr", attr: "name"}
993           ]},
994           {kind: "XV.ListColumn", classes: "third", components: [
995             {kind: "XV.ListAttr", attr: "shared", formatter: "formatShared"}
996           ]},
997           {kind: "XV.ListColumn",  components: [
998             {tag: "i", classes: "icon-remove list-icon", ontap: "removeRow"}
999           ]},
1000           {kind: "XV.ListColumn", components: [
1001             {tag: "i", classes: "icon-signout list-icon", ontap: "shareRow"}
1002           ]}
1003         ]}
1004       ]}
1005     ],
1006     /**
1007       When the value of the list is changed, bind the add
1008       and remove events of this collection.
1009     */
1010     valueChanged: function () {
1011       this.inherited(arguments);
1012       // bind enyo event to add/remove on collection of models
1013       this.getValue().on("add", this.doListChange(), this);
1014       this.getValue().on("remove", this.doListChange(), this);
1015     },
1016     /**
1017       Formatting function to show the shared text instead of
1018       the boolean value.
1019     */
1020     formatShared: function (value, view, model) {
1021       var shared = model && model.get('shared') ? "_shared".loc() : "";
1022       return shared;
1023     },
1024     /**
1025       Removes the selected row when the "remove" icon is
1026       selected.
1027     */
1028     removeRow: function (inSender, inEvent) {
1029       var index = inEvent.index,
1030         value = this.getValue(),
1031         model = value.models[index],
1032         that = this;
1033       inEvent.model = model;
1034       inEvent.done = function () {
1035         inEvent.delete = true;
1036         that.doListChange(inEvent);
1037       };
1038       this.deleteItem(inEvent);
1039     },
1040     /**
1041       Sets the shared value of the current model when the
1042       "shared" icon is selected.
1043     */
1044     shareRow: function (inSender, inEvent) {
1045       var options = {},
1046         index = inEvent.index,
1047         that = this,
1048         model = this.getValue().models[index];
1049       if (!model.get("shared")) {
1050         model.set("shared", true);
1051         options.success = function (model, resp, options) {
1052           that.reset();
1053         };
1054         model.save(null, options);
1055       }
1056     }
1057   });
1058
1059   // ..........................................................
1060   // FREIGHT CLASS
1061   //
1062
1063   enyo.kind({
1064     name: "XV.FreightClassList",
1065     kind: "XV.List",
1066     label: "_freightClass".loc(),
1067     collection: "XM.FreightClassCollection",
1068     parameterWidget: "XV.FreightClassListParameters",
1069     query: {orderBy: [
1070       {attribute: 'code'}
1071     ]},
1072     components: [
1073       {kind: "XV.ListItem", components: [
1074         {kind: "FittableColumns", components: [
1075           {kind: "XV.ListColumn", classes: "short",
1076             components: [
1077             {kind: "XV.ListAttr", attr: "code", isKey: true}
1078           ]},
1079           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
1080             {kind: "XV.ListAttr", attr: "description"}
1081           ]}
1082         ]}
1083       ]}
1084     ]
1085   });
1086
1087   XV.registerModelList("XM.FreightClassRelation", "XV.FreightClassList");
1088
1089   // ..........................................................
1090   // HONORIFIC
1091   //
1092
1093   enyo.kind({
1094     name: "XV.HonorificList",
1095     kind: "XV.List",
1096     label: "_honorifics".loc(),
1097     collection: "XM.HonorificCollection",
1098     query: {orderBy: [
1099       {attribute: 'code'}
1100     ]},
1101     components: [
1102       {kind: "XV.ListItem", components: [
1103         {kind: "XV.ListColumn", classes: "last", components: [
1104           {kind: "XV.ListAttr", attr: "code", isKey: true}
1105         ]}
1106       ]}
1107     ]
1108   });
1109
1110   // ..........................................................
1111   // INCIDENT
1112   //
1113
1114   enyo.kind({
1115     name: "XV.IncidentList",
1116     kind: "XV.List",
1117     label: "_incidents".loc(),
1118     collection: "XM.IncidentListItemCollection",
1119     query: {orderBy: [
1120       {attribute: 'priorityOrder'},
1121       {attribute: 'updated', descending: true},
1122       {attribute: 'number', descending: true, numeric: true}
1123     ]},
1124     toggleSelected: false,
1125     parameterWidget: "XV.IncidentListParameters",
1126     components: [
1127       {kind: "XV.ListItem", components: [
1128         {kind: "FittableColumns", components: [
1129           {kind: "XV.ListColumn", classes: "first", components: [
1130             {kind: "FittableColumns", components: [
1131               {kind: "XV.ListAttr", attr: "number", isKey: true},
1132               {kind: "XV.ListAttr", attr: "updated", fit: true,
1133                 formatter: "formatDate",
1134                 classes: "right"}
1135             ]},
1136             {kind: "XV.ListAttr", attr: "description"}
1137           ]},
1138           {kind: "XV.ListColumn", classes: "second", components: [
1139             {kind: "XV.ListAttr", attr: "account.name"},
1140             {kind: "XV.ListAttr", attr: "contact.name"}
1141           ]},
1142           {kind: "XV.ListColumn", classes: "third", components: [
1143             {kind: "XV.ListAttr", attr: "getIncidentStatusString"},
1144             {kind: "XV.ListAttr", attr: "assignedTo.username"}
1145           ]},
1146           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
1147             {kind: "XV.ListAttr", attr: "priority.name",
1148               placeholder: "_noPriority".loc()},
1149             {kind: "XV.ListAttr", attr: "category.name",
1150               placeholder: "_noCategory".loc()}
1151           ]}
1152         ]}
1153       ]}
1154     ],
1155     formatDate: function (value, view, model) {
1156       var date = value ? XT.date.applyTimezoneOffset(value, true) : "",
1157         isToday = date ? !XT.date.compareDate(date, new Date()) : false;
1158       view.addRemoveClass("bold", isToday);
1159       return date ? Globalize.format(date, "d") : "";
1160     },
1161     getStyle: function (model) {
1162       var settings = XT.session.getSettings(),
1163         K = XM.Incident,
1164         status = model ? model.get('status') : null,
1165         background,
1166         style;
1167       switch (status)
1168       {
1169       case K.NEW:
1170         background = settings.get('IncidentNewColor');
1171         break;
1172       case K.FEEDBACK:
1173         background = settings.get('IncidentFeedbackColor');
1174         break;
1175       case K.CONFIRMED:
1176         background = settings.get('IncidentConfirmedColor');
1177         break;
1178       case K.ASSIGNED:
1179         background = settings.get('IncidentAssignedColor');
1180         break;
1181       case K.RESOLVED:
1182         background = settings.get('IncidentResolvedColor');
1183         break;
1184       case K.CLOSED:
1185         background = settings.get('IncidentClosedColor');
1186         break;
1187       }
1188       if (background) {
1189         style = "background: " + background + ";";
1190       }
1191       return style;
1192     },
1193     setupItem: function (inSender, inEvent) {
1194       this.inherited(arguments);
1195       var model = this.getValue().models[inEvent.index],
1196         style = this.getStyle(model),
1197         prop,
1198         view;
1199
1200       // Apply background color to all views.
1201       this.$.listItem.setStyle(style);
1202       for (prop in this.$) {
1203         if (this.$.hasOwnProperty(prop) && this.$[prop].getAttr) {
1204           view = this.$[prop];
1205           view.setStyle(style);
1206         }
1207       }
1208       return true;
1209     }
1210   });
1211
1212   XV.registerModelList("XM.IncidentListItem", "XV.IncidentList");
1213   XV.registerModelList("XM.IncidentRelation", "XV.IncidentList");
1214
1215   // ..........................................................
1216   // INCIDENT EMAIL PROFILE
1217   //
1218
1219   enyo.kind({
1220     name: "XV.IncidentEmailProfileList",
1221     kind: "XV.EmailProfileList",
1222     label: "_incidentEmailProfiles".loc(),
1223     collection: "XM.IncidentEmailProfileCollection"
1224   });
1225
1226   // ..........................................................
1227   // INVOICE
1228   //
1229
1230   enyo.kind({
1231     name: "XV.InvoiceList",
1232     kind: "XV.List",
1233     multiSelect: true,
1234     label: "_invoices".loc(),
1235     parameterWidget: "XV.InvoiceListParameters",
1236     collection: "XM.InvoiceListItemCollection",
1237     query: {orderBy: [
1238       {attribute: 'number'}
1239     ]},
1240     actions: [
1241       {name: "void", privilege: "VoidPostedInvoices", prerequisite: "canVoid",
1242         method: "doVoid" },
1243       {name: "post", privilege: "PostMiscInvoices", prerequisite: "canPost",
1244         method: "doPost" },
1245       {name: "print", privilege: "PrintInvoices", method: "doPrint", isViewMethod: true },
1246       {name: "email", privilege: "PrintInvoices", method: "doEmail", isViewMethod: true},
1247       {name: "download", privilege: "PrintInvoices", method: "doDownload",
1248         isViewMethod: true}
1249     ],
1250     components: [
1251       {kind: "XV.ListItem", components: [
1252         {kind: "FittableColumns", components: [
1253           {kind: "XV.ListColumn", classes: "first", components: [
1254             {kind: "FittableColumns", components: [
1255               {kind: "XV.ListAttr", attr: "number", isKey: true, fit: true},
1256               {kind: "XV.ListAttr", attr: "isPosted", fit: true,
1257                 formatter: "formatPosted", style: "padding-left: 24px"},
1258               {kind: "XV.ListAttr", name: "dateField", attr: "invoiceDate",
1259                 formatter: "formatInvoiceDate", classes: "right",
1260                 placeholder: "_noDate".loc()}
1261             ]},
1262             {kind: "FittableColumns", components: [
1263               {kind: "XV.ListAttr", attr: "customer.name"},
1264               {kind: "XV.ListAttr", attr: "total", formatter: "formatTotal",
1265                 classes: "right"}
1266             ]}
1267           ]},
1268           {kind: "XV.ListColumn", classes: "last", components: [
1269             {kind: "XV.ListAttr", formatter: "formatName"},
1270             {kind: "XV.ListAttr", formatter: "formatAddress"}
1271           ]}
1272         ]}
1273       ]}
1274     ],
1275     // some extensions may override this function (i.e. inventory)
1276     formatAddress: function (value, view, model) {
1277       var city = model.get("billtoCity"),
1278         state = model.get("billtoState"),
1279         country = model.get("billtoCountry");
1280       return XM.Address.formatShort(city, state, country);
1281     },
1282     // some extensions may override this function (i.e. inventory)
1283     formatName: function (value, view, model) {
1284       return model.get("billtoName");
1285     },
1286     formatPosted: function (value) {
1287       return value ? "_posted".loc() : "_unposted".loc();
1288     },
1289     formatInvoiceDate: function (value, view, model) {
1290       var isLate = model && !model.get("isPosted") &&
1291         model.get(model.documentDateKey) &&
1292         (XT.date.compareDate(value, new Date()) < 1);
1293       view.addRemoveClass("error", isLate);
1294       return Globalize.format(value, "d");
1295     },
1296     formatTotal: function (value, view, model) {
1297       var currency = model ? model.get("currency") : false,
1298         scale = XT.locale.moneyScale;
1299       return currency ? currency.format(value, scale) : "";
1300     },
1301   });
1302   XV.registerModelList("XM.InvoiceRelation", "XV.InvoiceList");
1303
1304   // ..........................................................
1305   // ITEM
1306   //
1307
1308   enyo.kind({
1309     name: "XV.ItemList",
1310     kind: "XV.List",
1311     label: "_items".loc(),
1312     collection: "XM.ItemListItemCollection",
1313     query: {orderBy: [
1314       {attribute: 'number'}
1315     ]},
1316     parameterWidget: "XV.ItemListParameters",
1317     components: [
1318       {kind: "XV.ListItem", components: [
1319         {kind: "FittableColumns", components: [
1320           {kind: "XV.ListColumn", classes: "first", components: [
1321             {kind: "FittableColumns", components: [
1322               {kind: "XV.ListAttr", attr: "number", isKey: true},
1323               {kind: "XV.ListAttr", attr: "inventoryUnit.name", fit: true,
1324                 classes: "right"}
1325             ]},
1326             {kind: "XV.ListAttr", formatter: "formatDescription"}
1327           ]},
1328           {kind: "XV.ListColumn", classes: "second",
1329             components: [
1330             {kind: "XV.ListAttr", attr: "formatItemType", classes: "italic"},
1331             {kind: "XV.ListAttr", attr: "classCode.code"}
1332           ]},
1333           {kind: "XV.ListColumn", classes: "third", components: [
1334             {kind: "XV.ListAttr", attr: "listPrice", formatter: "formatPrice"},
1335             {kind: "XV.ListAttr", attr: "isFractional", formatter: "formatFractional"}
1336           ]},
1337           {kind: "XV.ListColumn", fit: true, components: [
1338             {kind: "XV.ListAttr", attr: "priceUnit.name", formatter: "formatPriceUnit"},
1339             {kind: "XV.ListAttr", attr: "productCategory.code"}
1340           ]}
1341         ]}
1342       ]}
1343     ],
1344     formatFractional: function (value, view, model) {
1345       return value ? "_fractional".loc() : "";
1346     },
1347     formatPrice: function (value, view, model) {
1348       var sold = model.get("isSold");
1349       if (XT.session.privileges.get("ViewListPrices") && sold) {
1350         var scale = XT.locale.salesPriceScale;
1351         return Globalize.format(value, "c" + scale);
1352       }
1353       view.addRemoveClass("placeholder", true);
1354       if (!sold) {
1355         return "_notSold".loc();
1356       }
1357       return "--";
1358     },
1359     formatPriceUnit: function (value, view, model) {
1360       if (XT.session.privileges.get("ViewListPrices") && model.get("isSold")) {
1361         return value;
1362       }
1363       return "";
1364     },
1365     formatDescription: function (value, view, model) {
1366       var descrip1 = model.get("description1") || "",
1367         descrip2 = model.get("description2") || "",
1368         sep = descrip2 ? " - " : "";
1369       return descrip1 + sep + descrip2;
1370     }
1371   });
1372
1373   XV.registerModelList("XM.ItemRelation", "XV.ItemList");
1374
1375   // ..........................................................
1376   // ITEM GROUP
1377   //
1378
1379   enyo.kind({
1380     name: "XV.ItemGroupList",
1381     kind: "XV.List",
1382     label: "_itemGroups".loc(),
1383     collection: "XM.ItemGroupRelationCollection",
1384     query: {orderBy: [
1385       {attribute: 'name'}
1386     ]},
1387     components: [
1388       {kind: "XV.ListItem", components: [
1389         {kind: "FittableColumns", components: [
1390           {kind: "XV.ListColumn", classes: "short",
1391             components: [
1392             {kind: "XV.ListAttr", attr: "name", isKey: true}
1393           ]},
1394           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
1395             {kind: "XV.ListAttr", attr: "description"}
1396           ]}
1397         ]}
1398       ]}
1399     ]
1400   });
1401
1402   // ..........................................................
1403   // ITEM SITE
1404   //
1405
1406   enyo.kind({
1407     name: "XV.ItemSiteList",
1408     kind: "XV.List",
1409     label: "_itemSites".loc(),
1410     collection: "XM.ItemSiteListItemCollection",
1411     query: {orderBy: [
1412       {attribute: 'item.number'}
1413     ]},
1414     parameterWidget: "XV.ItemSiteListParameters",
1415     components: [
1416       {kind: "XV.ListItem", components: [
1417         {kind: "FittableColumns", components: [
1418           {kind: "XV.ListColumn", classes: "first", components: [
1419             {kind: "FittableColumns", components: [
1420               {kind: "XV.ListAttr", attr: "item.number", isKey: true},
1421               {kind: "XV.ListAttr", attr: "item.barcode", fit: true, classes: "right"}
1422             ]},
1423             {kind: "FittableColumns", components: [
1424               {kind: "XV.ListAttr", attr: "item.description1"},
1425               {kind: "XV.ListAttr", attr: "item.aliases", fit: true,
1426                 formatter: "formatAliases", classes: "right"}
1427             ]}
1428           ]},
1429           {kind: "XV.ListColumn", classes: "second",
1430             components: [
1431             {kind: "XV.ListAttr", attr: "site.code", classes: "bold"},
1432             {kind: "XV.ListAttr", attr: "site.description"}
1433           ]}
1434         ]}
1435       ]}
1436     ],
1437     formatActive: function (value, view, model) {
1438       return value ? "_active".loc() : "";
1439     },
1440     formatAliases: function (value, view, model) {
1441       return value.map(function (model) {
1442         return model.get("aliasNumber");
1443       }).join();
1444     },
1445     formatSold: function (value, view, model) {
1446       return value ? "_sold".loc() : "";
1447     }
1448   });
1449
1450   XV.registerModelList("XM.ItemSiteRelation", "XV.ItemSiteList");
1451
1452   // ..........................................................
1453   // LEDGER ACCOUNT
1454   //
1455
1456   enyo.kind({
1457     name: "XV.LedgerAccountList",
1458     kind: "XV.List",
1459     label: "_ledgerAccounts".loc(),
1460     collection: "XM.LedgerAccountRelationCollection",
1461     query: {orderBy: [
1462       {attribute: 'name'}
1463     ]},
1464     parameterWidget: "XV.LedgerAccountListParameters",
1465     components: [
1466       {kind: "XV.ListItem", components: [
1467         {kind: "FittableColumns", components: [
1468           {kind: "XV.ListColumn", classes: "first", components: [
1469             {kind: "FittableColumns", components: [
1470               {kind: "XV.ListAttr", attr: "name", isKey: true},
1471               {kind: "XV.ListAttr", attr: "getAccountTypeString", fit: true,
1472                 classes: "right"}
1473             ]},
1474             {kind: "XV.ListAttr", attr: "description"}
1475           ]},
1476           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
1477             {kind: "XV.ListAttr", attr: "externalReference"},
1478             {kind: "XV.ListAttr", attr: "isActive", formatter: "formatActive"}
1479           ]}
1480         ]}
1481       ]}
1482     ],
1483     formatActive: function (value, view, model) {
1484       return value ? "" : "_inactive".loc();
1485     }
1486   });
1487
1488   // ..........................................................
1489   // OPPORTUNITY
1490   //
1491
1492   enyo.kind({
1493     name: "XV.OpportunityList",
1494     kind: "XV.List",
1495     collection: "XM.OpportunityListItemCollection",
1496     query: {orderBy: [
1497       {attribute: 'priorityOrder'},
1498       {attribute: 'targetClose'},
1499       {attribute: 'name'},
1500       {attribute: 'number', numeric: true}
1501     ]},
1502     label: "_opportunities".loc(),
1503     parameterWidget: "XV.OpportunityListParameters",
1504     components: [
1505       {kind: "XV.ListItem", components: [
1506         {kind: "FittableColumns", components: [
1507           {kind: "XV.ListColumn", classes: "first", components: [
1508             {kind: "FittableColumns", components: [
1509               {kind: "XV.ListAttr", attr: "number", isKey: true},
1510               {kind: "XV.ListAttr", attr: "targetClose", fit: true,
1511                 placeholder: "_noCloseTarget".loc(),
1512                 classes: "right"}
1513             ]},
1514             {kind: "FittableColumns", components: [
1515               {kind: "XV.ListAttr", attr: "name"},
1516               {kind: "XV.ListAttr", attr: "amount", classes: "right",
1517                 formatter: "formatAmount"}
1518             ]}
1519           ]},
1520           {kind: "XV.ListColumn", classes: "second",
1521             components: [
1522             {kind: "XV.ListAttr", attr: "account.name",
1523               placeholder: "_noAccountName".loc()},
1524             {kind: "XV.ListAttr", attr: "contact.name"}
1525           ]},
1526           {kind: "XV.ListColumn", classes: "third",
1527             components: [
1528             {kind: "XV.ListAttr", attr: "opportunityStage.name",
1529               placeholder: "_noStage".loc()},
1530             {kind: "XV.ListAttr", attr: "assignedTo.username"}
1531           ]},
1532           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
1533             {kind: "XV.ListAttr", attr: "priority.name",
1534               placeholder: "_noPriority".loc()},
1535             {kind: "XV.ListAttr", attr: "opportunityType.name",
1536               placeholder: "_noType".loc()}
1537           ]}
1538         ]}
1539       ]}
1540     ],
1541     formatAmount: function (value, view, model) {
1542       var currency = model ? model.get("currency") : false,
1543         scale = XT.locale.moneyScale;
1544       return currency ? currency.format(value, scale) : "";
1545     }
1546   });
1547
1548   XV.registerModelList("XM.OpportunityListItem", "XV.OpportunityList");
1549   XV.registerModelList("XM.OpportunityRelation", "XV.OpportunityList");
1550
1551   // ..........................................................
1552   // PLANNER CODE
1553   //
1554
1555   enyo.kind({
1556     name: "XV.PlannerCodeList",
1557     kind: "XV.List",
1558     label: "_plannerCodes".loc(),
1559     collection: "XM.PlannerCodeCollection",
1560     query: {orderBy: [
1561       {attribute: 'code'}
1562     ]},
1563     parameterWidget: "XV.PlannerCodeListParameters",
1564     components: [
1565       {kind: "XV.ListItem", components: [
1566         {kind: "FittableColumns", components: [
1567           {kind: "XV.ListColumn", classes: "first", components: [
1568             {kind: "FittableColumns", components: [
1569               {kind: "XV.ListAttr", attr: "code", isKey: true},
1570               {kind: "XV.ListAttr", attr: "name", fit: true, classes: "right"}
1571             ]}
1572           ]}
1573         ]}
1574       ]}
1575     ]
1576   });
1577
1578   XV.registerModelList("XM.PlannerCode", "XV.PlannerCodeList");
1579
1580   // ..........................................................
1581   // PRODUCT CATEGORY
1582   //
1583
1584   enyo.kind({
1585     name: "XV.ProductCategoryList",
1586     kind: "XV.List",
1587     label: "_productCategories".loc(),
1588     collection: "XM.ProductCategoryCollection",
1589     query: {orderBy: [
1590       {attribute: 'code'}
1591     ]},
1592     components: [
1593       {kind: "XV.ListItem", components: [
1594         {kind: "FittableColumns", components: [
1595           {kind: "XV.ListColumn", classes: "short",
1596             components: [
1597             {kind: "XV.ListAttr", attr: "code", isKey: true}
1598           ]},
1599           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
1600             {kind: "XV.ListAttr", attr: "description"}
1601           ]}
1602         ]}
1603       ]}
1604     ]
1605   });
1606
1607   // ..........................................................
1608   // PROJECT
1609   //
1610
1611   enyo.kind({
1612     name: "XV.ProjectList",
1613     kind: "XV.List",
1614     label: "_projects".loc(),
1615     collection: "XM.ProjectListItemCollection",
1616     query: {orderBy: [
1617       {attribute: "number" }
1618     ]},
1619     parameterWidget: "XV.ProjectListParameters",
1620     headerComponents: [
1621       {kind: "FittableColumns", classes: "xv-list-header",
1622         components: [
1623         {kind: "XV.ListColumn", classes: "name-column", components: [
1624           {content: "_name".loc()},
1625           {content: "_description".loc()},
1626           {content: "_account".loc()}
1627         ]},
1628         {kind: "XV.ListColumn", classes: "right-column", components: [
1629           {content: "_dueDate".loc()},
1630           {content: "_priority".loc()},
1631           {content: "_complete".loc()}
1632         ]},
1633         {kind: "XV.ListColumn", classes: "short", components: [
1634           {content: "_status".loc()},
1635           {content: "_assignedTo".loc()},
1636           {content: "_type".loc()}
1637         ]},
1638         {kind: "XV.ListColumn", classes: "right-column"},
1639         {kind: "XV.ListColumn", classes: "right-column", components: [
1640           {content: "_budgeted".loc()},
1641           {content: "_actual".loc()},
1642           {content: "_balance".loc()}
1643         ]}
1644       ]}
1645     ],
1646     components: [
1647       {kind: "XV.ListItem", components: [
1648         {kind: "FittableColumns", components: [
1649           {kind: "XV.ListColumn", classes: "name-column", components: [
1650             {kind: "XV.ListAttr", attr: "number", isKey: true},
1651             {kind: "XV.ListAttr", attr: "name"},
1652             {kind: "XV.ListAttr", attr: "account.name"}
1653           ]},
1654           {kind: "XV.ListColumn", classes: "right-column", components: [
1655             {kind: "XV.ListAttr", attr: "dueDate"},
1656             {kind: "XV.ListAttr", attr: "priority.name",
1657                 placeholder: "_noPriority".loc()},
1658             {kind: "XV.ListAttr", attr: "percentComplete"}
1659           ]},
1660           {kind: "XV.ListColumn", classes: "short",
1661             components: [
1662             {kind: "XV.ListAttr", attr: "getProjectStatusString"},
1663             {kind: "XV.ListAttr", attr: "assignedTo.username",
1664               placeholder: "_noAssignedTo".loc()},
1665             {kind: "XV.ListAttr", attr: "projectType.code",
1666               placeholder: "_noProjectType".loc()},
1667           ]},
1668           {kind: "XV.ListColumn", classes: "right-column", components: [
1669             {kind: "XV.ListAttr", attr: "budgetedExpenses",
1670               classes: "text-align-right", formatter: "formatExpenses"},
1671             {kind: "XV.ListAttr", attr: "actualExpenses",
1672               classes: "text-align-right", formatter: "formatExpenses"},
1673             {kind: "XV.ListAttr", attr: "balanceExpenses",
1674               classes: "text-align-right", formatter: "formatExpenses"}
1675           ]},
1676           {kind: "XV.ListColumn", classes: "right-column", fit: true,
1677             components: [
1678             {kind: "XV.ListAttr", attr: "budgetedHours",
1679               classes: "text-align-right", formatter: "formatHours"},
1680             {kind: "XV.ListAttr", attr: "actualHours",
1681               classes: "text-align-right", formatter: "formatHours"},
1682             {kind: "XV.ListAttr", attr: "balanceHours",
1683               classes: "text-align-right", formatter: "formatHours"}
1684           ]}
1685         ]}
1686       ]}
1687     ],
1688     formatHours: function (value, view, model) {
1689       view.addRemoveClass("error", value < 0);
1690       var scale = XT.locale.quantityScale;
1691       return Globalize.format(value, "n" + scale) + " " + "_hrs".loc();
1692     },
1693     formatExpenses: function (value, view, model) {
1694       view.addRemoveClass("error", value < 0);
1695       var scale = XT.locale.currencyScale;
1696       return Globalize.format(value, "c" + scale);
1697     }
1698
1699   });
1700
1701   XV.registerModelList("XM.ProjectListItem", "XV.ProjectList");
1702   XV.registerModelList("XM.ProjectRelation", "XV.ProjectList");
1703
1704   // ..........................................................
1705   // PROSPECT
1706   //
1707
1708   enyo.kind({
1709     name: "XV.ProspectList",
1710     kind: "XV.List",
1711     label: "_prospects".loc(),
1712     collection: "XM.ProspectRelationCollection",
1713     events: {
1714       onWorkspace: ""
1715     },
1716     actions: [{
1717       name: "convert",
1718       method: "convertProspect",
1719       privilege: "MaintainCustomerMasters",
1720       isViewMethod: true
1721     }],
1722     query: {orderBy: [
1723       {attribute: 'number'}
1724     ]},
1725     parameterWidget: "XV.ProspectListParameters",
1726     components: [
1727       {kind: "XV.ListItem", components: [
1728         {kind: "FittableColumns", components: [
1729           {kind: "XV.ListColumn", classes: "first", components: [
1730             {kind: "FittableColumns", components: [
1731               {kind: "XV.ListAttr", attr: "number", isKey: true},
1732               {kind: "XV.ListAttr", attr: "contact.phone", fit: true,
1733                 classes: "right"}
1734             ]},
1735             {kind: "FittableColumns", components: [
1736               {kind: "XV.ListAttr", attr: "name"},
1737               {kind: "XV.ListAttr", attr: "contact.primaryEmail", classes: "right"}
1738             ]}
1739           ]},
1740           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
1741             {kind: "XV.ListAttr", attr: "contact.name",
1742               placeholder: "_noContact".loc()},
1743             {kind: "XV.ListAttr", attr: "contact.address"}
1744           ]}
1745         ]}
1746       ]}
1747     ],
1748     /**
1749       Convert the prospect from the list into a customer model. The way we
1750       do this is to open a customer workspace and then call the model method
1751       convertFromProspect AFTER the workspace is initialized. That way
1752       the view and the model get bound together correctly. The user will have
1753       to fill out some customer-specific fields, and when they save a new
1754       customer will be created.
1755      */
1756     convertProspect: function (inEvent) {
1757       var model = inEvent.model,
1758         modelId = model.id,
1759         success = function () {
1760           this.getValue().convertFromProspect(modelId);
1761         };
1762
1763       this.doWorkspace({
1764         workspace: "XV.CustomerWorkspace",
1765         attributes: {
1766           name: model.get("name"),
1767           number: model.get("number")
1768         },
1769         success: success,
1770         allowNew: false
1771       });
1772     }
1773   });
1774
1775   XV.registerModelList("XM.ProspectRelation", "XV.ProspectList");
1776
1777   // ..........................................................
1778   // SALES EMAIL PROFILE
1779   //
1780
1781   enyo.kind({
1782     name: "XV.SalesEmailProfileList",
1783     kind: "XV.EmailProfileList",
1784     label: "_salesEmailProfiles".loc(),
1785     collection: "XM.SalesEmailProfileCollection"
1786   });
1787
1788   // ..........................................................
1789   // SALES ORDER
1790   //
1791
1792   enyo.kind({
1793     name: "XV.SalesOrderList",
1794     kind: "XV.List",
1795     label: "_salesOrders".loc(),
1796     collection: "XM.SalesOrderListItemCollection",
1797     parameterWidget: "XV.SalesOrderListParameters",
1798     actions: [
1799       {name: "print", privilege: "ViewSalesOrders", method: "doPrint", isViewMethod: true},
1800       {name: "email", privilege: "ViewSalesOrders", method: "doEmail", isViewMethod: true}
1801     ],
1802     query: {orderBy: [
1803       {attribute: 'number'}
1804     ]},
1805     components: [
1806       {kind: "XV.ListItem", components: [
1807         {kind: "FittableColumns", components: [
1808           {kind: "FittableColumns", components: [
1809             {kind: "XV.ListColumn", classes: "name-column", components: [
1810               {kind: "XV.ListAttr", attr: "number", isKey: true},
1811               {kind: "XV.ListAttr", attr: "customer.name"}
1812             ]},
1813             {kind: "XV.ListColumn", components: [
1814               {kind: "XV.ListAttr", attr: "formatStatus"}
1815             ]},
1816             {kind: "XV.ListColumn", classes: "right-column", components: [
1817               {kind: "XV.ListAttr", attr: "scheduleDate",
1818                 placeholder: "_noSchedule".loc()},
1819               {kind: "XV.ListAttr", attr: "total", formatter: "formatTotal"}
1820             ]},
1821             {kind: "XV.ListColumn", fit: true, components: [
1822               {kind: "XV.ListAttr", formatter: "formatName"},
1823               {kind: "XV.ListAttr", formatter: "formatShiptoOrBillto"}
1824             ]}
1825           ]}
1826         ]}
1827       ]}
1828     ],
1829     formatBillto: function (value, view, model) {
1830       var city = model.get("billtoCity"),
1831         state = model.get("billtoState"),
1832         country = model.get("billtoCountry");
1833       return XM.Address.formatShort(city, state, country);
1834     },
1835     /**
1836       Returns Shipto Name if one exists, otherwise Billto Name.
1837     */
1838     formatName: function (value, view, model) {
1839       return model.get("shiptoName") || model.get("billtoName");
1840     },
1841     formatTotal: function (value, view, model) {
1842       var currency = model ? model.get("currency") : false,
1843         scale = XT.locale.moneyScale;
1844       return currency ? currency.format(value, scale) : "";
1845     },
1846
1847     formatShipto: function (value, view, model) {
1848       var city = model.get("shiptoCity"),
1849         state = model.get("shiptoState"),
1850         country = model.get("shiptoCountry");
1851       return XM.Address.formatShort(city, state, country);
1852     },
1853     /**
1854       Returns formatted Shipto City, State and Country if
1855       Shipto Name exists, otherwise Billto location.
1856     */
1857     formatShiptoOrBillto: function (value, view, model) {
1858       var hasShipto = model.get("shiptoName") ? true : false,
1859         cityAttr = hasShipto ? "shiptoCity": "billtoCity",
1860         stateAttr = hasShipto ? "shiptoState" : "billtoState",
1861         countryAttr = hasShipto ? "shiptoCountry" : "billtoCountry",
1862         city = model.get(cityAttr),
1863         state = model.get(stateAttr),
1864         country = model.get(countryAttr);
1865       return XM.Address.formatShort(city, state, country);
1866     }
1867   });
1868
1869   XV.registerModelList("XM.SalesOrderRelation", "XV.SalesOrderList");
1870
1871   // ..........................................................
1872   // QUOTE
1873   //
1874
1875   enyo.kind({
1876     name: "XV.QuoteList",
1877     kind: "XV.SalesOrderList",
1878     query: {orderBy: [
1879       {attribute: 'number'}
1880     ]},
1881     events: {
1882       onNotify: ""
1883     },
1884     actions: [{
1885       name: "convert",
1886       method: "convertQuote",
1887       privilege: "ConvertQuotes",
1888       isViewMethod: true,
1889       notify: false
1890     }],
1891     label: "_quotes".loc(),
1892     collection: "XM.QuoteListItemCollection",
1893     parameterWidget: "XV.QuoteListParameters",
1894     formatDate: function (value, view, model) {
1895       var isLate = model && model.get('expireDate') &&
1896         (XT.date.compareDate(value, new Date()) < 1);
1897       view.addRemoveClass("error", isLate);
1898       return value;
1899     },
1900     convertQuote: function (inEvent) {
1901       var model = inEvent.model,
1902         that = this,
1903         customer = model.get("customer"),
1904         K = XM.CustomerProspectRelation,
1905         attrs,
1906
1907         // In case we are converting a prospect
1908         convertToCustomer = function (resp) {
1909           if (!resp.answer) { return; }
1910
1911           that.doWorkspace({
1912             workspace: "XV.CustomerWorkspace",
1913             attributes: {
1914               number: customer.get("number"),
1915               name: customer.get("name")
1916             },
1917             success: afterCustomerCreated,
1918             callback: convertToSalesOrder,
1919             allowNew: false
1920           });
1921         },
1922
1923         afterCustomerCreated = function () {
1924           this.getValue().convertFromProspect(customer.id);
1925         },
1926
1927         convertToSalesOrder = function () {
1928           XM.SalesOrder.convertFromQuote(model.id, {
1929             success: afterQuoteConvertedSuccess
1930           });
1931         },
1932
1933         afterQuoteConvertedSuccess = function (resp) {
1934           attrs = resp;
1935           that.doWorkspace({
1936             workspace: "XV.SalesOrderWorkspace",
1937             success: afterSalesOrderCreated,
1938             allowNew: false
1939           });
1940         },
1941
1942         afterSalesOrderCreated = function () {
1943           var value = this.getValue(),
1944             gridBox = this.$.salesOrderLineItemBox;
1945
1946           value.setStatus(XM.Model.BUSY_FETCHING);
1947           value.set(attrs);
1948           value.revertStatus();
1949
1950           //Hack to force grid to refresh. Why doesn't it on its own?
1951           gridBox.valueChanged();
1952           gridBox.setDisabled(false);
1953         };
1954
1955
1956       // Get the process started one way or another
1957       if (customer.get("status") === K.PROSPECT_STATUS) {
1958         this.doNotify({
1959           type: XM.Model.QUESTION,
1960           callback: convertToCustomer,
1961           message: "_convertProspect".loc()
1962         });
1963       } else {
1964         convertToSalesOrder();
1965       }
1966     }
1967   });
1968
1969   XV.registerModelList("XM.QuoteRelation", "XV.QuoteList");
1970
1971   // ..........................................................
1972   // REASON CODE
1973   //
1974
1975   enyo.kind({
1976     name: "XV.ReasonCodeList",
1977     kind: "XV.List",
1978     label: "_reasonCodes".loc(),
1979     collection: "XM.ReasonCodeCollection",
1980     query: {orderBy: [
1981       {attribute: 'code'}
1982     ]},
1983     components: [
1984       {kind: "XV.ListItem", components: [
1985         {kind: "FittableColumns", components: [
1986           {kind: "XV.ListColumn", classes: "first",
1987             components: [
1988             {kind: "XV.ListAttr", attr: "code", isKey: true}
1989           ]},
1990           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
1991             {kind: "XV.ListAttr", attr: "description"}
1992           ]}
1993         ]}
1994       ]}
1995     ]
1996   });
1997
1998   // ..........................................................
1999   // RETURN
2000   //
2001
2002   enyo.kind({
2003     name: "XV.ReturnList",
2004     kind: "XV.InvoiceList",
2005     label: "_returns".loc(),
2006     multiSelect: false,
2007     parameterWidget: "XV.ReturnListParameters",
2008     collection: "XM.ReturnListItemCollection",
2009     actions: [
2010       {name: "void", privilege: "VoidPostedARCreditMemos",
2011         prerequisite: "canVoid", method: "doVoid" },
2012       {name: "post", privilege: "PostARDocuments",
2013         prerequisite: "canPost", method: "doPost" },
2014       {name: "print", privilege: "PrintCreditMemos",
2015         method: "doPrint" }
2016     ],
2017     create: function () {
2018       this.inherited(arguments);
2019       this.$.dateField.setAttr("returnDate");
2020     }
2021   });
2022   XV.registerModelList("XM.ReturnRelation", "XV.ReturnList");
2023
2024   // ..........................................................
2025   // SALE TYPE
2026   //
2027
2028   enyo.kind({
2029     name: "XV.SaleTypeList",
2030     kind: "XV.List",
2031     label: "_saleTypes".loc(),
2032     collection: "XM.SaleTypeCollection",
2033     parameterWidget: "XV.SaleTypeListParameters",
2034     query: {orderBy: [
2035       {attribute: 'code'}
2036     ]},
2037     components: [
2038       {kind: "XV.ListItem", components: [
2039         {kind: "FittableColumns", components: [
2040           {kind: "XV.ListColumn", classes: "short",
2041             components: [
2042             {kind: "XV.ListAttr", attr: "code", isKey: true}
2043           ]},
2044           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
2045             {kind: "XV.ListAttr", attr: "description"}
2046           ]}
2047         ]}
2048       ]}
2049     ]
2050   });
2051
2052   XV.registerModelList("XM.SaleTypeRelation", "XV.SaleTypeList");
2053
2054   // ..........................................................
2055   // SALES REP
2056   //
2057
2058   enyo.kind({
2059     name: "XV.SalesRepList",
2060     kind: "XV.List",
2061     label: "_salesRep".loc(),
2062     collection: "XM.SalesRepCollection",
2063     parameterWidget: "XV.SalesRepListParameters",
2064     query: {orderBy: [
2065       {attribute: 'number'}
2066     ]},
2067     components: [
2068       {kind: "XV.ListItem", components: [
2069         {kind: "FittableColumns", components: [
2070           {kind: "XV.ListColumn", classes: "short",
2071             components: [
2072             {kind: "XV.ListAttr", attr: "number", isKey: true}
2073           ]},
2074           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
2075             {kind: "XV.ListAttr", attr: "name"}
2076           ]}
2077         ]}
2078       ]}
2079     ]
2080   });
2081
2082   XV.registerModelList("XM.SalesRepRelation", "XV.SalesRepList");
2083
2084   // ..........................................................
2085   // SITE
2086   //
2087
2088   enyo.kind({
2089     name: "XV.SiteList",
2090     kind: "XV.List",
2091     label: "_sites".loc(),
2092     collection: "XM.SiteListItemCollection",
2093     query: {orderBy: [
2094       {attribute: 'code'}
2095     ]},
2096     parameterWidget: "XV.SiteListParameters",
2097     components: [
2098       {kind: "XV.ListItem", components: [
2099         {kind: "FittableColumns", components: [
2100           {kind: "XV.ListColumn", classes: "first", components: [
2101             {kind: "FittableColumns", components: [
2102               {kind: "XV.ListAttr", attr: "code", isKey: true},
2103               {kind: "XV.ListAttr", attr: "description", fit: true, classes: "right"}
2104             ]},
2105             {kind: "XV.ListAttr", attr: "siteType.description"}
2106           ]},
2107         ]}
2108       ]}
2109     ]
2110   });
2111
2112   XV.registerModelList("XM.SiteRelation", "XV.SiteList");
2113
2114   // ..........................................................
2115   // SHIFT
2116   //
2117
2118   enyo.kind({
2119     name: "XV.ShiftList",
2120     kind: "XV.List",
2121     label: "_shifts".loc(),
2122     collection: "XM.ShiftCollection",
2123     parameterWidget: "XV.ShiftListParameters",
2124     query: {orderBy: [
2125       {attribute: 'number'}
2126     ]},
2127     components: [
2128       {kind: "XV.ListItem", components: [
2129         {kind: "FittableColumns", components: [
2130           {kind: "XV.ListColumn", classes: "short",
2131             components: [
2132             {kind: "XV.ListAttr", attr: "number", isKey: true}
2133           ]},
2134           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
2135             {kind: "XV.ListAttr", attr: "name"}
2136           ]}
2137         ]}
2138       ]}
2139     ]
2140   });
2141
2142   // ..........................................................
2143   // SHIP VIA
2144   //
2145
2146   enyo.kind({
2147     name: "XV.ShipViaList",
2148     kind: "XV.List",
2149     label: "_shipVias".loc(),
2150     collection: "XM.ShipViaCollection",
2151     query: {orderBy: [
2152       {attribute: 'code'}
2153     ]},
2154     components: [
2155       {kind: "XV.ListItem", components: [
2156         {kind: "FittableColumns", components: [
2157           {kind: "XV.ListColumn", classes: "short",
2158             components: [
2159             {kind: "XV.ListAttr", attr: "code", isKey: true}
2160           ]},
2161           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
2162             {kind: "XV.ListAttr", attr: "description"}
2163           ]}
2164         ]}
2165       ]}
2166     ]
2167   });
2168   XV.registerModelList("XM.ShipVia", "XV.ShipViaList");
2169
2170   // ..........................................................
2171   // SHIP ZONE
2172   //
2173
2174   enyo.kind({
2175     name: "XV.ShipZoneList",
2176     kind: "XV.List",
2177     label: "_shipZones".loc(),
2178     collection: "XM.ShipZoneCollection",
2179     parameterWidget: "XV.ShipZoneListParameters",
2180     query: {orderBy: [
2181       {attribute: 'name'}
2182     ]},
2183     components: [
2184       {kind: "XV.ListItem", components: [
2185         {kind: "FittableColumns", components: [
2186           {kind: "XV.ListColumn", classes: "short",
2187             components: [
2188             {kind: "XV.ListAttr", attr: "name", isKey: true}
2189           ]},
2190           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
2191             {kind: "XV.ListAttr", attr: "description"}
2192           ]}
2193         ]}
2194       ]}
2195     ]
2196   });
2197
2198   XV.registerModelList("XM.ShipZoneRelation", "XV.ShipZoneList");
2199
2200   // ..........................................................
2201   // TAX ASSIGNMENT
2202   //
2203
2204   enyo.kind({
2205     name: "XV.TaxAssignmentList",
2206     kind: "XV.List",
2207     label: "_taxAssignment".loc(),
2208     collection: "XM.TaxAssignmentCollection",
2209     parameterWidget: "XV.TaxAssignmentListParameters",
2210     query: {orderBy: [
2211       {attribute: 'tax'}
2212     ]},
2213     components: [
2214       {kind: "XV.ListItem", components: [
2215         {kind: "FittableColumns", components: [
2216           {kind: "XV.ListColumn", classes: "short", components: [
2217             {kind: "XV.ListAttr", attr: "tax.code", isKey: true}
2218           ]},
2219           {kind: "XV.ListColumn", classes: "short", components: [
2220             {kind: "XV.ListAttr", attr: "taxType.name"}
2221           ]}
2222         ]}
2223       ]}
2224     ]
2225   });
2226
2227   // ..........................................................
2228   // TAX AUTHORITY
2229   //
2230
2231   enyo.kind({
2232     name: "XV.TaxAuthorityList",
2233     kind: "XV.List",
2234     label: "_taxAuthority".loc(),
2235     collection: "XM.TaxAuthorityCollection",
2236     parameterWidget: "XV.TaxAuthorityListParameters",
2237     query: {orderBy: [
2238       {attribute: 'code'}
2239     ]},
2240     components: [
2241       {kind: "XV.ListItem", components: [
2242         {kind: "FittableColumns", components: [
2243           {kind: "XV.ListColumn", classes: "short",
2244             components: [
2245             {kind: "XV.ListAttr", attr: "code", isKey: true}
2246           ]},
2247           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
2248             {kind: "XV.ListAttr", attr: "name"}
2249           ]}
2250         ]}
2251       ]}
2252     ]
2253   });
2254
2255   // ..........................................................
2256   // TAX CODE
2257   //
2258
2259   enyo.kind({
2260     name: "XV.TaxCodeList",
2261     kind: "XV.List",
2262     label: "_taxCode".loc(),
2263     collection: "XM.TaxCodeCollection",
2264     parameterWidget: "XV.TaxCodeListParameters",
2265     query: {orderBy: [
2266       {attribute: 'code'}
2267     ]},
2268     components: [
2269       {kind: "XV.ListItem", components: [
2270         {kind: "FittableColumns", components: [
2271           {kind: "XV.ListColumn", classes: "short",
2272             components: [
2273             {kind: "XV.ListAttr", attr: "code", isKey: true}
2274           ]},
2275           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
2276             {kind: "XV.ListAttr", attr: "description"}
2277           ]}
2278         ]}
2279       ]}
2280     ]
2281   });
2282
2283   XV.registerModelList("XM.TaxCodeRelation", "XV.TaxCodeList");
2284
2285   // ..........................................................
2286   // TAX CLASS
2287   //
2288
2289   enyo.kind({
2290     name: "XV.TaxClassList",
2291     kind: "XV.List",
2292     label: "_taxClass".loc(),
2293     collection: "XM.TaxClassCollection",
2294     parameterWidget: "XV.TaxClassListParameters",
2295     query: {orderBy: [
2296       {attribute: 'code'}
2297     ]},
2298     components: [
2299       {kind: "XV.ListItem", components: [
2300         {kind: "FittableColumns", components: [
2301           {kind: "XV.ListColumn", classes: "short",
2302             components: [
2303             {kind: "XV.ListAttr", attr: "code", isKey: true}
2304           ]},
2305           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
2306             {kind: "XV.ListAttr", attr: "description"}
2307           ]}
2308         ]}
2309       ]}
2310     ]
2311   });
2312
2313   XV.registerModelList("XM.TaxClassRelation", "XV.TaxClassList");
2314
2315   // ..........................................................
2316   // TAX RATE
2317   //
2318
2319   enyo.kind({
2320     name: "XV.TaxRateList",
2321     kind: "XV.List",
2322     label: "_taxRate".loc(),
2323     collection: "XM.TaxRateCollection",
2324     parameterWidget: "XV.TaxRateListParameters",
2325     query: {orderBy: [
2326       {attribute: 'tax'}
2327     ]},
2328     components: [
2329       {kind: "XV.ListItem", components: [
2330         {kind: "FittableColumns", components: [
2331           {kind: "XV.ListColumn", classes: "short", components: [
2332             {kind: "XV.ListAttr", attr: "tax.code", isKey: true}
2333           ]},
2334           {kind: "XV.ListColumn", classes: "short", components: [
2335             {kind: "XV.ListAttr", attr: "percent"}
2336           ]}
2337         ]}
2338       ]}
2339     ]
2340   });
2341
2342   // ..........................................................
2343   // TAX TYPE
2344   //
2345
2346   enyo.kind({
2347     name: "XV.TaxTypeList",
2348     kind: "XV.List",
2349     label: "_taxType".loc(),
2350     collection: "XM.TaxTypeCollection",
2351     parameterWidget: "XV.TaxTypeListParameters",
2352     query: {orderBy: [
2353       {attribute: 'name'}
2354     ]},
2355     components: [
2356       {kind: "XV.ListItem", components: [
2357         {kind: "FittableColumns", components: [
2358           {kind: "XV.ListColumn", classes: "short",
2359             components: [
2360             {kind: "XV.ListAttr", attr: "name", isKey: true}
2361           ]},
2362           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
2363             {kind: "XV.ListAttr", attr: "description"}
2364           ]}
2365         ]}
2366       ]}
2367     ]
2368   });
2369
2370   XV.registerModelList("XM.TaxTypeRelation", "XV.TaxTypeList");
2371
2372   // ..........................................................
2373   // TAX ZONE
2374   //
2375
2376   enyo.kind({
2377     name: "XV.TaxZoneList",
2378     kind: "XV.List",
2379     label: "_taxZone".loc(),
2380     collection: "XM.TaxZoneCollection",
2381     parameterWidget: "XV.TaxZoneListParameters",
2382     query: {orderBy: [
2383       {attribute: 'code'}
2384     ]},
2385     components: [
2386       {kind: "XV.ListItem", components: [
2387         {kind: "FittableColumns", components: [
2388           {kind: "XV.ListColumn", classes: "short",
2389             components: [
2390             {kind: "XV.ListAttr", attr: "code", isKey: true}
2391           ]},
2392           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
2393             {kind: "XV.ListAttr", attr: "description"}
2394           ]}
2395         ]}
2396       ]}
2397     ]
2398   });
2399
2400   XV.registerModelList("XM.TaxZoneRelation", "XV.TaxZoneList");
2401
2402   // ..........................................................
2403   // TERMS
2404   //
2405
2406   enyo.kind({
2407     name: "XV.TermsList",
2408     kind: "XV.List",
2409     label: "_terms".loc(),
2410     collection: "XM.TermsCollection",
2411     parameterWidget: "XV.TermsListParameters",
2412     query: {orderBy: [
2413       {attribute: 'code'}
2414     ]},
2415     components: [
2416       {kind: "XV.ListItem", components: [
2417         {kind: "FittableColumns", components: [
2418           {kind: "XV.ListColumn", classes: "short",
2419             components: [
2420             {kind: "XV.ListAttr", attr: "code", isKey: true}
2421           ]},
2422           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
2423             {kind: "XV.ListAttr", attr: "description"}
2424           ]}
2425         ]}
2426       ]}
2427     ]
2428   });
2429
2430   XV.registerModelList("XM.TermsRelation", "XV.TermsList");
2431
2432   // ..........................................................
2433   // TO DO
2434   //
2435
2436   enyo.kind({
2437     name: "XV.ToDoList",
2438     kind: "XV.List",
2439     label: "_toDo".loc(),
2440     collection: "XM.ToDoListItemCollection",
2441     parameterWidget: "XV.ToDoListParameters",
2442     query: {orderBy: [
2443       {attribute: 'priorityOrder'},
2444       {attribute: 'dueDate'},
2445       {attribute: 'name'}
2446     ]},
2447     components: [
2448       {kind: "XV.ListItem", components: [
2449         {kind: "FittableColumns", components: [
2450           {kind: "XV.ListColumn", classes: "first", components: [
2451             {kind: "FittableColumns", components: [
2452               {kind: "XV.ListAttr", attr: "name", isKey: true},
2453               {kind: "XV.ListAttr", attr: "dueDate", fit: true,
2454                 placeholder: "_noDueDate".loc(), classes: "right"}
2455             ]},
2456             {kind: "XV.ListAttr", attr: "description",
2457               placeholder: "_noDescription".loc()}
2458           ]},
2459           {kind: "XV.ListColumn", classes: "second",
2460             components: [
2461             {kind: "XV.ListAttr", attr: "account.name",
2462               placeholder: "_noAccountName".loc()},
2463             {kind: "XV.ListAttr", attr: "contact.name"}
2464           ]},
2465           {kind: "XV.ListColumn", classes: "third",
2466             components: [
2467             {kind: "XV.ListAttr", attr: "getToDoStatusString"},
2468             {kind: "XV.ListAttr", attr: "owner.username"}
2469           ]},
2470           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
2471             {kind: "XV.ListAttr", attr: "priority.name",
2472               placeholder: "_noPriority".loc()}
2473           ]}
2474         ]}
2475       ]}
2476     ]
2477   });
2478
2479   XV.registerModelList("XM.ToDoRelation", "XV.ToDoList");
2480
2481   // ..........................................................
2482   // URL
2483   //
2484
2485   enyo.kind({
2486     name: "XV.UrlList",
2487     kind: "XV.List",
2488     label: "_urls".loc(),
2489     collection: "XM.UrlCollection",
2490     parameterWidget: "XV.UrlListParameters",
2491     query: {orderBy: [
2492       {attribute: 'name'}
2493     ]},
2494     components: [
2495       {kind: "XV.ListItem", components: [
2496         {kind: "FittableColumns", components: [
2497           {kind: "XV.ListColumn", classes: "short",
2498             components: [
2499             {kind: "XV.ListAttr", attr: "name", isKey: true}
2500           ]},
2501           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
2502             {kind: "XV.ListAttr", attr: "path"}
2503           ]}
2504         ]}
2505       ]}
2506     ]
2507   });
2508
2509   XV.registerModelList("XM.Url", "XV.UrlList");
2510
2511   // ..........................................................
2512   // USER ACCOUNT
2513   //
2514
2515   enyo.kind({
2516     name: "XV.UserAccountList",
2517     kind: "XV.List",
2518     label: "_userAccounts".loc(),
2519     collection: "XM.UserAccountRelationCollection",
2520     parameterWidget: "XV.UserAccountListParameters",
2521     query: {orderBy: [
2522       {attribute: 'username'}
2523     ]},
2524     components: [
2525       {kind: "XV.ListItem", components: [
2526         {kind: "FittableColumns", components: [
2527           {kind: "XV.ListColumn", classes: "short", components: [
2528             {kind: "XV.ListAttr", attr: "username", isKey: true}
2529           ]},
2530           {kind: "XV.ListColumn", classes: "short", components: [
2531             {kind: "XV.ListAttr", attr: "propername"}
2532           ]}
2533         ]}
2534       ]}
2535     ]
2536   });
2537
2538   XV.registerModelList("XM.UserAccountRelation", "XV.UserAccountList");
2539
2540   // ..........................................................
2541   // STATES AND COUNTRIES
2542   //
2543
2544   enyo.kind({
2545     name: "XV.AbbreviationList",
2546     kind: "XV.List",
2547     components: [
2548       {kind: "XV.ListItem", components: [
2549         {kind: "FittableColumns", components: [
2550           {kind: "XV.ListColumn", classes: "short",
2551             components: [
2552             {kind: "XV.ListAttr", attr: "abbreviation", isKey: true}
2553           ]},
2554           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
2555             {kind: "XV.ListAttr", attr: "name"}
2556           ]}
2557         ]}
2558       ]}
2559     ]
2560   });
2561
2562   enyo.kind({
2563     name: "XV.StateList",
2564     kind: "XV.AbbreviationList",
2565     label: "_states".loc(),
2566     collection: "XM.StateCollection",
2567     query: {orderBy: [{ attribute: 'abbreviation' }] }
2568   });
2569
2570   enyo.kind({
2571     name: "XV.CountryList",
2572     kind: "XV.AbbreviationList",
2573     label: "_countries".loc(),
2574     collection: "XM.CountryCollection",
2575     query: {orderBy: [
2576       {attribute: 'abbreviation'}
2577     ]}
2578   });
2579
2580   // ..........................................................
2581   // VENDOR
2582   //
2583
2584   enyo.kind({
2585     name: "XV.VendorList",
2586     kind: "XV.List",
2587     label: "_vendors".loc(),
2588     collection: "XM.VendorListItemCollection",
2589     query: {orderBy: [
2590       {attribute: 'number'}
2591     ]},
2592     parameterWidget: "XV.VendorListParameters",
2593     components: [
2594       {kind: "XV.ListItem", components: [
2595         {kind: "FittableColumns", components: [
2596           {kind: "XV.ListColumn", classes: "first", components: [
2597             {kind: "FittableColumns", components: [
2598               {kind: "XV.ListAttr", attr: "number", isKey: true},
2599               {kind: "XV.ListAttr", attr: "primaryContact.phone", fit: true,
2600                 classes: "right"}
2601             ]},
2602             {kind: "FittableColumns", components: [
2603               {kind: "XV.ListAttr", attr: "name"},
2604               {kind: "XV.ListAttr", attr: "primaryContact.primaryEmail", classes: "right"}
2605             ]}
2606           ]},
2607           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
2608             {kind: "XV.ListAttr", attr: "primaryContact.name",
2609               placeholder: "_noContact".loc()},
2610             {kind: "XV.ListAttr", attr: "address"}
2611           ]}
2612         ]}
2613       ]}
2614     ]
2615   });
2616
2617   XV.registerModelList("XM.VendorRelation", "XV.VendorList");
2618
2619   // ..........................................................
2620   // VENDOR ADDRESS
2621   //
2622
2623   enyo.kind({
2624     name: "XV.VendorAddressList",
2625     kind: "XV.List",
2626     collection: "XM.VendorAddressRelationCollection",
2627     parameterWidget: "XV.VendorAddressParameters",
2628     query: {orderBy: [
2629       {attribute: 'code'}
2630     ]},
2631     components: [
2632       {kind: "XV.ListItem", components: [
2633         {kind: "FittableColumns", components: [
2634           {kind: "XV.ListColumn", classes: "short",
2635             components: [
2636             {kind: "XV.ListAttr", attr: "code", isKey: true}
2637           ]},
2638           {kind: "XV.ListColumn", fit: true, components: [
2639             {kind: "XV.ListAttr", attr: "name"},
2640             {kind: "XV.ListAttr", formatter: "formatAddress",
2641               classes: "xv-addresslist-attr", allowHtml: true}
2642           ]}
2643         ]}
2644       ]}
2645     ],
2646     formatAddress: function (value, view, model) {
2647       var address = model.get("address");
2648       return address.format(true);
2649     }
2650   });
2651
2652   XV.registerModelList("XM.VendarAddressRelation", "XV.VendorAddressList");
2653
2654   enyo.kind({
2655     name: "XV.NameList",
2656     kind: "XV.List",
2657     query: {orderBy: [
2658       {attribute: 'name'}
2659     ]},
2660     components: [
2661       {kind: "XV.ListItem", components: [
2662         {kind: "FittableColumns", components: [
2663           {kind: "XV.ListColumn", classes: "first",
2664             components: [
2665             {kind: "XV.ListAttr", attr: "name", isKey: true}
2666           ]}
2667         ]}
2668       ]}
2669     ],
2670     /**
2671       All of these lists follow a very similar naming convention.
2672       Apply that convention unless the list overrides the label
2673       or collection attribute.
2674     */
2675     create: function () {
2676       var kindName = this.kind.substring(0, this.kind.length - 4).substring(3);
2677       if (!this.getLabel()) {
2678         this.setLabel(this.determineLabel(kindName));
2679       }
2680       if (!this.getCollection()) {
2681         this.setCollection("XM." + kindName + "Collection");
2682       }
2683       this.inherited(arguments);
2684     },
2685
2686     determineLabel: function (kindName) {
2687       return ("_" + kindName.camelize().pluralize()).loc();
2688     }
2689   });
2690
2691   // ..........................................................
2692   // INCIDENT CATEGORIES, RESOLUTIONS, SEVERITIES,
2693   // PRIORITIES,
2694   // OPPORTUNITY SOURCES, STAGES, TYPES,
2695   //
2696   // Basically anything whose rows are name and description
2697   //
2698   enyo.kind({
2699     name: "XV.NameDescriptionList",
2700     kind: "XV.NameList",
2701     components: [
2702       {kind: "XV.ListItem", components: [
2703         {kind: "FittableColumns", components: [
2704           {kind: "XV.ListColumn", classes: "short",
2705             components: [
2706             {kind: "XV.ListAttr", attr: "name", isKey: true}
2707           ]},
2708           {kind: "XV.ListColumn", classes: "last", fit: true, components: [
2709             {kind: "XV.ListAttr", attr: "description"}
2710           ]}
2711         ]}
2712       ]}
2713     ]
2714   });
2715
2716   enyo.kind({
2717     name: "XV.IncidentCategoryList",
2718     kind: "XV.NameDescriptionList"
2719   });
2720
2721   enyo.kind({
2722     name: "XV.IncidentResolutionList",
2723     kind: "XV.NameDescriptionList"
2724   });
2725
2726   enyo.kind({
2727     name: "XV.IncidentSeverityList",
2728     kind: "XV.NameDescriptionList"
2729   });
2730
2731   enyo.kind({
2732     name: "XV.PriorityList",
2733     kind: "XV.NameDescriptionList"
2734   });
2735
2736   enyo.kind({
2737     name: "XV.UnitList",
2738     kind: "XV.NameDescriptionList"
2739   });
2740
2741   enyo.kind({
2742     name: "XV.OpportunitySourceList",
2743     kind: "XV.NameDescriptionList",
2744     published: {
2745       query: {orderBy: [{ attribute: 'name' }] }
2746     }
2747   });
2748
2749   enyo.kind({
2750     name: "XV.OpportunityStageList",
2751     kind: "XV.NameDescriptionList",
2752     published: {
2753       query: {orderBy: [{ attribute: 'name' }] }
2754     }
2755   });
2756
2757   enyo.kind({
2758     name: "XV.OpportunityTypeList",
2759     kind: "XV.NameDescriptionList",
2760     published: {
2761       query: {orderBy: [{ attribute: 'name' }] }
2762     }
2763   });
2764
2765   enyo.kind({
2766     name: "XV.SiteTypeList",
2767     kind: "XV.NameDescriptionList",
2768     collection: "XM.SiteTypeCollection"
2769   });
2770
2771   enyo.kind({
2772     name: "XV.UserAccountRoleList",
2773     kind: "XV.NameDescriptionList",
2774     collection: "XM.UserAccountRoleListItemCollection"
2775   });
2776
2777   enyo.kind({
2778     name: "XV.CharacteristicList",
2779     kind: "XV.NameList",
2780     collection: "XM.CharacteristicCollection"
2781   });
2782 }());