1 /**
  2  * @author Brian Carlsen
  3  * @version 1.0.0
  4  *
  5  * Serves as a wrapper to the MINDBODY Client Service, providing 
  6  * some additional functionality.
  7  *
  8  * All Client Service methods are available returning
  9  * 1) An extracted result using the instance method with the same name
 10  * 2) The raw array response using the instance method with the same name post-fixed with 'Response'.
 11  * 		The array consists of:
 12  * 			i) 		The object represtentation of the SOAP response
 13  * 			ii) 	The raw XML SOAP response
 14  * 			iii) 	The raw header info of the SOAP response
 15  */
 16 
 17 var Promise = require( 'bluebird' );
 18 
 19 var mboService = require( './mbo_Service' );
 20 
 21 //--------------- ClientService Class ---------------------
 22 
 23 /**
 24  * Represents the MINDOBDY Client Service.
 25  *
 26  * @constructor
 27  * @param  {string} username Username of the MINDBODY client interacting with the service.
 28  * @param  {string} password Password of the MINDBODY client interacting with the service.
 29  * @return {mbo_ClientService}          Returns the Client Service.
 30  */
 31 function mbo_ClientService( username, password ) {
 32 	mboService.call( this, 'ClientService', username, password );
 33 }
 34 mbo_ClientService.prototype = Object.create( mboService.prototype );
 35 mbo_ClientService.prototype.constructor = mbo_ClientService;
 36 
 37 //------------- Methods ----------------------
 38 
 39 /**
 40  * Retrieves the number of clients.
 41  * @param  {string} [search] Search string to filter results.
 42  * @return {Promise}        An A+ Promise passed the total number of clients, 
 43  *                             matching the search string if given. 
 44  */
 45 mbo_ClientService.prototype.getClientCount = function( search ) {
 46 	var args = {
 47 		SearchText: search || '',
 48 		PageSize: 1,
 49 		XMLDetail: 'Bare'
 50 	};
 51 
 52 	return this.GetClientsResponse( args )
 53 		.spread( function( result, raw, header ) { 
 54 			return result.GetClientsResult.ResultCount;
 55 		} )
 56 		.catch( function( err ) {
 57 			throw err;
 58 		} );
 59 };
 60 
 61 /**
 62  * Retrieves all clients, filtered by search text if given.
 63  * @param  {string} [search] Search string to filter results.
 64  * @return {Promise}        An A+ Promise passed an array of the found clients.
 65  */
 66 mbo_ClientService.prototype.getAllClients = function( search, params ) {
 67 	var self = this;
 68 
 69 	return self.getClientCount()
 70 		.then( function( clientCount ) {
 71 			var args = params || {};
 72 			args.SearchText = search || '';
 73 
 74 			var pageSize = args.PageSize || self.requestDefaults.PageSize,
 75 				pages = Math.ceil( clientCount / pageSize ),
 76 				completed = 0
 77 				clientRequests = [];
 78 
 79 			for ( var p = 0; p < pages; ++p ) { // Send all requests
 80 				args.CurrentPageIndex = p;
 81 				clientRequests.push( self.GetClients( args ) );
 82 			}
 83 
 84 			return clientRequests;
 85 		} )
 86 		.then( function( clientRequests ) {
 87 			return Promise.all( clientRequests ) // Wait for all requests to complete
 88 				.then( function( clientRequests ) { 
 89 					var clients = [];
 90 
 91 					for ( var i in clientRequests ) {
 92 						clients = clients.concat( clientRequests[ i ] );
 93 					}
 94 					
 95 					return clients;
 96 				} )
 97 		} )
 98 		.catch( function( err ) {
 99 			throw err;
100 		} );
101 };
102 
103 /**
104  * Retireves the clients matching the given IDs
105  * @param  {number|number[]} ids An single or array of client IDs to retrieve.
106  * @return {Promise}     An A+ Promise passed an array of clients.
107  */
108 mbo_ClientService.prototype.getClientsById = function( ids, params ) {
109 	var args = params || {};
110 	args.ClientIDs = {
111 		string: ids
112 	};
113 
114 	// If more than 1000 clients, must break query into multiple pages
115 	var clientRequests = [],
116 		pages = Math.ceil( ids.length / 1000 );
117 
118 	for ( var p = 0; p < pages; ++p ) {
119 		args.CurrentPageIndex = p;
120 		clientRequests.push( this.GetClients( args ) );
121 	}
122 
123 	return Promise.all( clientRequests )
124 		.then( function( clientRequests ) {
125 			var clients = [];
126 
127 			for ( var i in clientRequests ) {
128 				clients = clients.concat( clientRequests[ i ] );
129 			}
130 
131 			return clients;
132 		} )
133 		.catch( function( err ) {
134 			throw err;
135 		} );
136 };
137 
138 /**
139  * Retrieves a single client by ID
140  * @param  {number} id Teh ID of the requested client
141  * @return {Promise}    An A+ Promise passed an object representing the client.
142  */
143 mbo_ClientService.prototype.getClientById = function( id ) {
144 	return this.getClientsById( id )
145 		.then( function( clients ) {
146 			if ( clients.length === 1 ) {
147 				if ( clients[ 0 ].ID ) {
148 					return clients[ 0 ];
149 				}
150 				else {
151 					return false;
152 				}
153 			}
154 			else {
155 				throw new Error( 'More than one client found with same Id.' );
156 			}
157 		} )
158 		.catch( function( err ) {
159 			throw err;
160 		} );
161 };
162 
163 /**
164  * Gets the values of Client Indexes for a client.
165  * @param  {number|string} id The client's Id
166  * @return {Promise}    Returns an A+ Promise resolved to an array of objects representing 
167  *                            the client's assigned Client Index values.
168  *                            Each object has the form { index: { id, name }, value: { id, name } }.
169  *                            Client Indexes which do not have an assigned value are excluded.
170  */
171 mbo_ClientService.prototype.getClientIndexValues = function( id ) {
172 	var params = {
173 		ClientIDs: { string: id },
174 		Fields: { string: [ 'Clients.ClientIndexes' ] }
175 	};
176 
177 	return this.GetClients( params )
178 		.then( function( client ) {
179 			var indexValues = [],
180 				indexes = client[ 0 ].ClientIndexes.ClientIndex;
181 
182 			if ( indexes ) {
183 				indexes.forEach( function( index ) {
184 					var value = index.Values.ClientIndexValue[ 0 ],
185 						indexValue = {
186 							index: {
187 								id: index.ID,
188 								name: index.Name
189 							},
190 							value: {
191 								id: value.ID,
192 								name: value.Name
193 							}
194 						};
195 
196 					indexValues.push( indexValue );
197 				} );
198 			}
199 			
200 			return indexValues;
201 		} )
202 		.catch( function( err ) {
203 			throw err;
204 		} );
205 };
206 
207 /**
208  * Retrieves Account Balances for Clients
209  * @param  {array} ids An array of Client Ids
210  * @return {Promise}     An A+ Promise resolved to an object keyed by client Id and values of thier
211  *                         Account Balance
212  */
213 mbo_ClientService.prototype.getClientAccountBalancesById = function( ids ) {
214 	var self = this,
215 		pages = Math.ceil( ids.length / self.requestDefaults.PageSize ),
216 		completed = 0
217 		balanceRequests = [];
218 
219 	for ( var p = 0; p < pages; ++p ) { // Send all requests
220 		var idStart = p * self.requestDefaults.PageSize,
221 			idEnd 	= ( p + 1 ) * self.requestDefaults.PageSize
222 			pageIds = ids.slice( idStart, idEnd );
223 
224 		var args = {
225 			XMLDetail: 'Bare',
226 			CurrentPageIndex: p,
227 			ClientIDs: { string: pageIds }
228 		};
229 
230 		balanceRequests.push( self.GetClientAccountBalances( args ) );
231 	}
232 
233 	return Promise.all( balanceRequests ) // Wait for all requests to complete
234 		.then( function( balanceRequests ) {
235 			var balances = {};
236 
237 			balanceRequests.forEach( function( balanceRequest ) {  
238 				balanceRequest.forEach( function( balance ) {
239 					balances[ balance.ID ] = parseFloat( balance.AccountBalance );
240 				} );
241 			} );
242 
243 			return balances;
244 		} )
245 		.catch( function( err ) {
246 			throw err;
247 		} );
248 };
249 
250 module.exports = mbo_ClientService;