Merge pull request #1784 from garyhgohoos/23593-2
[xtuple] / enyo-client / application / source / models / address.js
1 /*jshint indent:2, curly:true, eqeqeq:true, immed:true, latedef:true,
2 newcap:true, noarg:true, regexp:true, undef:true, strict:true, trailing:true,
3 white:true*/
4 /*global XT:true, XM:true, Backbone:true, _:true, console:true */
5
6 (function () {
7   "use strict";
8
9   // ..........................................................
10   // MIXINS
11   //
12
13   /**
14     Shared address functionality.
15   */
16   XM.AddressMixin = {
17     numberPolicy: XM.Document.AUTO_NUMBER,
18
19     // ..........................................................
20     // METHODS
21     //
22     /**
23       Set default country if applicable.
24     */
25     initialize: function (attributes, options) {
26       XM.Document.prototype.initialize.apply(this, arguments);
27       if (options && options.isNew && !this.get('country')) {
28         var settings = XT.session ? XT.session.getSettings() : null,
29           country = settings ? settings.get('DefaultAddressCountry') : null;
30         if (country) { this.set('country', country); }
31       }
32     },
33
34     /**
35       Formats the multiple lines of an address into a
36       text block separating the elements of the address by line breaks.
37
38       @params {Boolean} Is HTML - default true
39       @return {String}
40     */
41     format: function (isHtml) {
42       return XM.Address.format(this, isHtml);
43     },
44
45     /**
46       A formatted address that includes city, state and country.
47
48       @return {String}
49     */
50     formatShort: function () {
51       return XM.Address.formatShort(this);
52     },
53
54     isAllEmpty: function () {
55       return !this.get("line1") &&
56         !this.get("line2") &&
57         !this.get("line3") &&
58         !this.get("city") &&
59         !this.get("state") &&
60         !this.get("postalCode") &&
61         !this.get("country");
62     },
63
64     /**
65       Success response returns an integer from the server indicating how many times the address
66       is used by other records.
67
68       @param {Object} Options
69       @returns Receiver
70     */
71     useCount: function (options) {
72       this.dispatch('XM.Address', 'useCount', this.id, options);
73       return this;
74     },
75
76     validate: function (attributes, options) {
77       var settings = XT.session.getSettings(),
78         strict = settings.get('StrictAddressCountry'),
79         country = attributes.country,
80         found;
81
82       // Validate country if setting says to do so
83       if (country && strict) {
84         found = _.find(XM.countries.models, function (c) {
85           return c.get('name') === country;
86         });
87         if (!found) {
88           return XT.Error.clone('xt2008');
89         }
90       }
91       return XM.Document.prototype.validate.apply(this, arguments);
92     }
93
94   };
95
96   /**
97     @class
98
99     @extends XM.Document
100   */
101   XM.Country = XM.Document.extend({
102     /** @scope XM.Country.prototype */
103
104     recordType: 'XM.Country',
105
106     documentKey: "abbreviation",
107
108     // ..........................................................
109     // METHODS
110     //
111
112     validate: function (attributes) {
113       var params = {};
114
115       if (attributes.abbreviation &&
116           attributes.abbreviation.length !== 2) {
117         params.attr = "_abbreviation".loc();
118         params.length = "2";
119         return XT.Error.clone('xt1006', { params: params });
120       }
121
122       if (attributes.currencyAbbreviation &&
123           attributes.currencyAbbreviation.length !== 3) {
124         params.attr = "_currencyAbbreviation".loc();
125         params.length = "3";
126         return XT.Error.clone('xt1006', { params: params });
127       }
128
129       if (attributes.currencyNumber &&
130           attributes.currencyNumber.length !== 3) {
131         params.attr = "_currencyNumber".loc();
132         params.length = "3";
133         return XT.Error.clone('xt1006', { params: params });
134       }
135
136       return XM.Document.prototype.validate.apply(this, arguments);
137     }
138
139   });
140
141   /**
142     @class
143
144     @extends XM.Document
145   */
146   XM.State = XM.Document.extend({
147     /** @scope XM.State.prototype */
148
149     recordType: 'XM.State',
150
151     documentKey: "abbreviation",
152
153     enforceUpperKey: false
154
155   });
156
157   /**
158     @class
159
160     @extends XM.Document
161     @extends XM.AddressMixin
162   */
163   XM.Address = XM.Document.extend({
164     /** @scope XM.Address.prototype */
165
166     recordType: 'XM.Address'
167
168   });
169
170   XM.Address = XM.Address.extend(XM.AddressMixin);
171
172   // ..........................................................
173   // CLASS METHODS
174   //
175
176   _.extend(XM.Address, {
177
178     /**
179       Success response returns an address id for an address with the same fields
180       as those passed.
181
182       @param {String} Line1
183       @param {String} Line2
184       @param {String} Line3
185       @param {String} City
186       @param {String} State
187       @param {String} Postal Code
188       @param {String} Country
189       @param {Object} Options
190       @returns Receiver
191     */
192     findExisting: function (line1, line2, line3, city, state, postalcode, country, options) {
193       var params = {
194           type: 'Address',
195           line1: line1,
196           line2: line2,
197           line3: line3,
198           city: city,
199           state: state,
200           postalcode: postalcode,
201           country: country
202         };
203       XM.ModelMixin.dispatch('XM.Address', 'findExisting', params, options);
204       return this;
205     },
206
207     /**
208       This function formats the multiple lines of an address into a
209       text block separating the elements of the address by line breaks.
210
211       Address format accepts multiple argument formats:
212         XM.Address.format(address);
213         XM.Address.format(address, isHtml);
214         XM.Address.format(name, line1, line2, line3, city, state, postalcode, country);
215         XM.Address.format(name, line1, line2, line3, city, state, postalcode, country, isHtml);
216
217       Where address is an XM.Address and isHtml determines whether to
218       use HTML line breaks instead of ASCII new line characters. The
219       default for isHtml is true. The longer signatures accept string
220       components of an address.
221
222       @return {String}
223     */
224     format: function () {
225       if (!arguments[0]) { return; }
226       var fmtlines   = [],
227         name, line1, line2, line3,
228         city, state, postalcode, country,
229         breaks, result = '', csz;
230
231       if (typeof arguments[0] === 'object') {
232         name = '';
233         line1 = arguments[0].get('line1');
234         line2 = arguments[0].get('line2');
235         line3 = arguments[0].get('line3');
236         city = arguments[0].get('city');
237         state = arguments[0].get('state');
238         postalcode = arguments[0].get('postalCode');
239         country = arguments[0].get('country');
240         breaks = (arguments[1] === undefined ? true : arguments[1]) ? '<br />' : '\n';
241       } else if (typeof arguments[0] === 'string')  {
242         name = arguments[0];
243         line1 = arguments[1];
244         line2 = arguments[2];
245         line3 = arguments[3];
246         city = arguments[4];
247         state = arguments[5];
248         postalcode = arguments[6];
249         country = arguments[7];
250         breaks = (arguments[8] === undefined ? true : arguments[8]) ? '<br />' : '\n';
251       } else { return false; }
252
253       if (name) { fmtlines.push(name); }
254       if (line1) { fmtlines.push(line1); }
255       if (line2) { fmtlines.push(line2); }
256       if (line3) { fmtlines.push(line3); }
257       if (city || state || postalcode) {
258         csz = (city || '') +
259               (city && (state || postalcode) ? ', '  : '') +
260               (state || '') +
261               (state && postalcode ? ' '  : '') +
262               (postalcode || '');
263         fmtlines.push(csz);
264       }
265       if (country) { fmtlines.push(country); }
266
267       if (fmtlines.length) { result = fmtlines.join(breaks); }
268
269       return result;
270     },
271
272     /**
273       A formatted address that includes city, state and country.
274
275       short format accepts multiple argument formats:
276         XM.Address.format(address);
277         XM.Address.format(city, state, country);
278
279       @return {String}
280     */
281     formatShort: function (address) {
282       var ret,
283         city,
284         state,
285         country;
286
287       if (address && typeof address === 'object') {
288         city = address.get('city') || '';
289         state = address.get('state') || '';
290         country = address.get('country') || '';
291       } else {
292         city = arguments[0] || '';
293         state = arguments[1] || '';
294         country = arguments[2] || '';
295       }
296       ret = city + (city && state ? ', ' : '') + state;
297       ret += (ret ? ' ' : '') + country;
298       return ret;
299     },
300
301     // ..........................................................
302     // CONSTANTS
303     //
304
305     /**
306       Option to convert the existing address into a new one.
307
308       @static
309       @constant
310       @type Number
311       @default 0
312     */
313     CHANGE_ONE: 0,
314
315     /**
316       Option to change the existing address so all users of the
317       address are affected.
318
319       @static
320       @constant
321       @type Number
322       @default 1
323     */
324     CHANGE_ALL: 1
325
326   });
327
328   /**
329     @class
330
331     @extends XM.Model
332   */
333   XM.AddressComment = XM.Model.extend({
334     /** @scope XM.AddressComment.prototype */
335
336     recordType: 'XM.AddressComment'
337
338   });
339
340   /**
341     @class
342
343     @extends XM.Model
344   */
345   XM.AddressCharacteristic = XM.Model.extend({
346     /** @scope XM.AddressCharacteristic.prototype */
347
348     recordType: 'XM.AddressCharacteristic'
349
350   });
351
352   /**
353     @class
354
355     @extends XM.Model
356     @extends XM.AddressMixin
357   */
358   XM.AddressInfo = XM.Document.extend({
359     /** @scope XM.AddressInfo.prototype */
360
361     recordType: 'XM.AddressInfo',
362
363     // ..........................................................
364     // METHODS
365     //
366
367     /**
368      Return a copy of this address.
369
370      @return {XM.Address} copy of the address
371     */
372     copy: function () {
373       var attrs = _.clone(this.attributes);
374       delete attrs.id;
375       delete attrs.dataState;
376       delete attrs.number;
377       delete attrs.type;
378       delete attrs.isActive;
379       return new XM.AddressInfo(attrs, {isNew: true});
380     },
381
382     isEmpty: function () {
383       return (_.isEmpty(this.get('line1')) &&
384               _.isEmpty(this.get('line2')) &&
385               _.isEmpty(this.get('line3')) &&
386               _.isEmpty(this.get('city')) &&
387               _.isEmpty(this.get('state')) &&
388               _.isEmpty(this.get('postalCode')) &&
389               _.isEmpty(this.get('country')));
390     }
391
392   });
393
394   XM.AddressInfo = XM.AddressInfo.extend(XM.AddressMixin);
395
396   // ..........................................................
397   // COLLECTIONS
398   //
399
400   /**
401     @class
402
403     @extends XM.Collection
404   */
405   XM.AddressInfoCollection = XM.Collection.extend({
406     /** @scope XM.AddressInfoCollection.prototype */
407
408     model: XM.AddressInfo
409
410   });
411
412   /**
413     @class
414
415     @extends XM.Collection
416   */
417   XM.CountryCollection = XM.Collection.extend({
418     /** @scope XM.CountryCollection.prototype */
419
420     model: XM.Country
421
422   });
423
424   /**
425     @class
426
427     @extends XM.Collection
428   */
429   XM.StateCollection = XM.Collection.extend({
430     /** @scope XM.StateCollection.prototype */
431
432     model: XM.State
433
434   });
435
436 }());