Completed
Push — master ( a85a3b...a9e055 )
by Justin
11:13
created

ash_ups   D

Complexity

Total Complexity 116

Size/Duplication

Total Lines 878
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6
Metric Value
wmc 116
lcom 1
cbo 6
dl 0
loc 878
rs 4.4444

22 Methods

Rating   Name   Duplication   Size   Complexity  
A __autoload() 0 3 1
A getId() 0 3 1
A setId() 0 4 1
A getName() 0 3 1
A getInternalName() 0 3 1
A _is_large() 0 10 4
F _buildRateRequest() 0 169 19
F getQuote() 0 156 27
A get_item_shipping() 0 2 1
A _setServiceURL() 0 11 3
B _includeUPSData() 0 37 1
D getForm() 0 176 14
A submit_form() 0 21 4
B array2xml() 0 19 5
A _insured_value() 0 12 3
A _declared_value() 0 6 1
B _build_shipment() 0 31 2
A _makeRateRequest() 0 13 1
A futureDate() 0 21 4
D _parseQuote() 0 102 16
B _formatTable() 0 27 5
A __construct() 0 11 1

How to fix   Complexity   

Complex Class

Complex classes like ash_ups often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ash_ups, and based on these observations, apply Extract Interface, too.

1
<?php
2
/* Author : Greg Gullett and Instinct.co.uk
3
 * SVN : UPS Trunk :
4
 * Version : 1.1.0 : December 21, 2010
5
 */
6
class ash_ups {
0 ignored issues
show
Coding Style introduced by
Class name "ash_ups" is not in camel caps format
Loading history...
7
	public $internal_name;
8
	public $name;
9
	public $service_url = '';
10
	public $Services = array();
11
	public $singular_shipping = false;
12
	public $shipment;
13
	public $is_external = true;
14
	public $requires_curl = true;
15
	public $requires_weight = true;
16
	public $needs_zipcode = true;
17
18
	public $drop_types = array();
19
	public $cust_types = array();
20
21
	public function __construct() {
22
		$this->internal_name = 'ups';
23
		$this->name = _x( 'UPS', 'Shipping Module', 'wp-e-commerce' );
24
		$this->is_external = true;
25
		$this->requires_curl = true;
26
		$this->requires_weight = true;
27
		$this->needs_zipcode = true;
28
		$this->_setServiceURL();
29
		$this->_includeUPSData();
30
		return true;
0 ignored issues
show
Bug introduced by
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
31
	}
32
33
	function __autoload ( $name ) {
34
		include_once( '../wpsc-includes/shipping.helper.php' );
35
	}
36
37
	function getId () {
38
		// return $this->usps_id;
39
	}
40
41
	function setId ( $id ) {
0 ignored issues
show
Unused Code introduced by
The parameter $id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
42
		// $usps_id = $id;
43
		// return true;
44
	}
45
46
	private function _setServiceURL () {
47
		global $wpdb;
48
		$wpsc_ups_settings = get_option( 'wpsc_ups_settings' );
49
		$wpsc_ups_environment = ( array_key_exists( 'upsenvironment', (array) $wpsc_ups_settings ) ) ? $wpsc_ups_settings['upsenvironment'] : '0'; //(1) testing, else (0) production.
50
51
		if ( '1' == $wpsc_ups_environment ){
52
			$this->service_url = 'https://wwwcie.ups.com/ups.app/xml/Rate';
53
		} else {
54
			$this->service_url = 'https://www.ups.com/ups.app/xml/Rate';
55
		}
56
	}
57
58
	function getName () {
59
		return $this->name;
60
	}
61
62
	function getInternalName () {
63
		return $this->internal_name;
64
	}
65
66
	private function _includeUPSData () {
67
		$this->drop_types = array(
68
			'01' => __( 'Daily Pickup', 'wp-e-commerce' ),
69
			'03' => __( 'Customer Counter', 'wp-e-commerce' ),
70
			'06' => __( 'One Time Pickup', 'wp-e-commerce' ),
71
			'07' => __( 'On Call Air', 'wp-e-commerce' ),
72
			'19' => __( 'Letter Center', 'wp-e-commerce' ),
73
			'20' => __( 'Air Service Center', 'wp-e-commerce' ),
74
			'11' => __( 'Suggested Retail Rates (Advanced Config)', 'wp-e-commerce' ),
75
		);
76
77
		$this->cust_types = array(
78
			'01' => __( 'Daily Pickup, with UPS Account', 'wp-e-commerce' ),
79
			'03' => __( 'No Daily Pickup, with No or Other Account', 'wp-e-commerce' ),
80
			'04' => __( 'Retail Outlet (Only US origin shipments)', 'wp-e-commerce' )
81
		);
82
83
		$this->Services = array(
84
			'14' => __( 'Next Day Air Early AM', 'wp-e-commerce' ),
85
			'01' => __( 'Next Day Air', 'wp-e-commerce' ),
86
			'13' => __( 'Next Day Air Saver', 'wp-e-commerce' ),
87
			'59' => __( '2nd Day Air AM', 'wp-e-commerce' ),
88
			'02' => __( '2nd Day Air', 'wp-e-commerce' ),
89
			'12' => __( '3 Day Select', 'wp-e-commerce' ),
90
			'03' => __( 'Ground', 'wp-e-commerce' ),
91
			'11' => __( 'Standard', 'wp-e-commerce' ),
92
			'07' => __( 'Worldwide Express', 'wp-e-commerce' ),
93
			'54' => __( 'Worldwide Express Plus', 'wp-e-commerce' ),
94
			'08' => __( 'Worldwide Expedited', 'wp-e-commerce' ),
95
			'65' => __( 'Saver', 'wp-e-commerce' ),
96
			'82' => __( 'UPS Today Standard', 'wp-e-commerce' ),
97
			'83' => __( 'UPS Today Dedicated Courier', 'wp-e-commerce' ),
98
			'84' => __( 'UPS Today Intercity', 'wp-e-commerce' ),
99
			'85' => __( 'UPS Today Express', 'wp-e-commerce' ),
100
			'86' => __( 'UPS Today Express Saver', 'wp-e-commerce' )
101
		);
102
	}
103
104
	function getForm () {
105
		if ( ! isset( $this->Services ) ) {
106
			$this->_includeUPSData();
107
		}
108
		//__('Your Packaging', 'wp-e-commerce');  <-- use to translate
109
		$wpsc_ups_settings = get_option( 'wpsc_ups_settings' );
110
		$wpsc_ups_services = get_option( 'wpsc_ups_services' );
111
		// Defined on page 41 in UPS API documentation RSS_Tool_06_10_09.pdf
112
		/*$packaging_options['00'] = __('**UNKNOWN**', 'wp-e-commerce');*/
113
		$packaging_options['01'] = __( 'UPS Letter', 'wp-e-commerce' );
0 ignored issues
show
Coding Style Comprehensibility introduced by
$packaging_options was never initialized. Although not strictly required by PHP, it is generally a good practice to add $packaging_options = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
114
		$packaging_options['02'] = __( 'Your Packaging', 'wp-e-commerce' );
115
		$packaging_options['03'] = __( 'UPS Tube', 'wp-e-commerce' );
116
		$packaging_options['04'] = __( 'UPS Pak', 'wp-e-commerce' );
117
		$packaging_options['21'] = __( 'UPS Express Box', 'wp-e-commerce' );
118
		$packaging_options['2a'] = __( 'UPS Express Box - Small', 'wp-e-commerce' );
119
		$packaging_options['2b'] = __( 'UPS Express Box - Medium', 'wp-e-commerce' );
120
		$packaging_options['2c'] = __( 'UPS Express Box - Large', 'wp-e-commerce' );
121
122
		ob_start();
123
		?>
124
		<tr><td><table class='form-table'>
125
			<tr>
126
				<td><?php _e( 'Destination Type', 'wp-e-commerce' ); ?></td>
127
				<td>
128
					<label>
129
						<input type='radio' <?php checked( '2' != $wpsc_ups_settings['49_residential'] ); ?> value='1' name='wpsc_ups_settings[49_residential]' />
130
						<?php _e( 'Residential Address', 'wp-e-commerce' ); ?>
131
					</label><br />
132
					<label><input type='radio' <?php checked( '2' == $wpsc_ups_settings['49_residential'] ); ?>value='2' name='wpsc_ups_settings[49_residential]' />
133
						<?php _e( 'Commercial Address', 'wp-e-commerce' )?>
134
					</label>
135
				</td>
136
			</tr>
137
138
			<?php
139
			$sel2_drop = '';
140
			if ( empty( $wpsc_ups_settings['DropoffType'] ) ) {
141
				$sel2_drop = '01';
142
			} else {
143
				$sel2_drop = $wpsc_ups_settings['DropoffType'];
144
			}
145
			?>
146
			<tr>
147
				<td><?php _e( 'Dropoff Type', 'wp-e-commerce' ); ?></td>
148
				<td>
149
					<script type="text/javascript">
150
						function checkDropValue () {
151
							var val = jQuery("#drop_type option:selected").val();
152
							if (val == "11"){
153
								jQuery("#cust_type").removeAttr("disabled");
154
							} else {
155
								jQuery("#cust_type").attr("disabled", true);
156
							}
157
						}
158
					</script>
159
					<select id='drop_type' name='wpsc_ups_settings[DropoffType]' onChange='checkDropValue()' >
160
						<?php foreach ( array_keys( (array) $this->drop_types ) as $dkey ): ?>
161
							<option value="<?php echo esc_attr( $dkey ); ?>" <?php selected( $dkey, $sel2_drop ); ?> >
162
								<?php echo( $this->drop_types[$dkey] ); ?>
163
							</option>
164
						<?php endforeach; ?>
165
					</select>
166
				</td>
167
			</tr>
168
169
			<?php
170
				$sel3_drop = '';
171
				if ( empty( $wpsc_ups_settings['CustomerType'] ) ) {
172
					$sel3_drop = '01';
173
				} else {
174
					$sel3_drop = $wpsc_ups_settings['CustomerType'];
175
				}
176
			?>
177
			<tr>
178
				<td><?php _e( 'Customer Type', 'wp-e-commerce' ); ?></td>
179
				<td>
180
					<select id='cust_type' name='wpsc_ups_settings[CustomerType]' <?php disabled( $wpsc_ups_settings['DropoffType'] != '11' ); ?> >
181
						<?php foreach( array_keys( $this->cust_types ) as $dkey ): ?>
182
							<option value="<?php echo esc_attr( $dkey ); ?>" <?php selected( $sel3_drop == $dkey ); ?> >
183
								<?php echo( $this->cust_types[$dkey] ); ?>
184
							</option>
185
						<?php endforeach; ?>
186
					</select>
187
				</td>
188
			</tr>
189
190
			<tr>
191
				<td><?php _e( 'Packaging', 'wp-e-commerce' ); ?></td>
192
				<td>
193
					<select name='wpsc_ups_settings[48_container]'>
194
						<?php foreach ( $packaging_options as $key => $name ): ?>
195
							<option value='<?php echo esc_attr( $key ); ?>' <?php selected( $key == $wpsc_ups_settings['48_container'] );?>>
196
								<?php echo esc_html( $name ); ?>
197
							</option>
198
						<?php endforeach; ?>
199
					</select>
200
				</td>
201
			</tr>
202
203
			<tr>
204
				<td><?php _e( 'Shipping Settings', 'wp-e-commerce' ); ?></td>
205
				<td>
206
					<input type="checkbox" id="ups_env_test" name="wpsc_ups_settings[upsenvironment]" value="1" <?php checked( 1, isset( $wpsc_ups_settings['upsenvironment'] ) && (bool) $wpsc_ups_settings['upsenvironment'] ); ?> />
207
					<label for="ups_env_test" ><?php _e( 'Use Testing Environment', 'wp-e-commerce' ); ?></label>
208
					<br />
209
					<input type="checkbox" id="ups_negotiated_rates" name="wpsc_ups_settings[ups_negotiated_rates]" value="1" <?php checked( 1, isset( $wpsc_ups_settings['ups_negotiated_rates'] ) && (bool) $wpsc_ups_settings['ups_negotiated_rates'] ); ?> />
210
					<label for="ups_negotiated_rates" ><?php _e( 'Show UPS negotiated rates', 'wp-e-commerce' ); ?> *</label>
211
					<br />
212
					<input type="checkbox" id="ups_insured_shipment" name="wpsc_ups_settings[insured_shipment]" value="1" <?php checked( 1, isset( $wpsc_ups_settings['insured_shipment'] ) && (bool) $wpsc_ups_settings['insured_shipment'] ); ?> />
213
					<label for="ups_insured_shipment" ><?php _e( 'Insure shipment against cart total', 'wp-e-commerce' ); ?> *</label>
214
					<br />
215
					<input type="checkbox" id="ups_singular_shipping" name="wpsc_ups_settings[singular_shipping]" value="1" <?php checked( 1, isset( $wpsc_ups_settings['singular_shipping'] ) && (bool) $wpsc_ups_settings['singular_shipping'] ); ?> />
216
					<label for="ups_singular_shipping" ><?php _e( 'Singular Shipping', 'wp-e-commerce' ); ?> *</label>
217
					<p class='description'><?php _e( 'Rate each quantity of items in a cart as its own package using dimensions on product', 'wp-e-commerce' ); ?></p>
218
					<input type="checkbox" id="ups_intl_rate" name="wpsc_ups_settings[intl_rate]" value="1" <?php checked( 1, isset( $wpsc_ups_settings['intl_rate'] ) && (bool) $wpsc_ups_settings['intl_rate'] ); ?> />
219
					<label for="ups_intl_rate" ><?php _e( 'Disable International Shipping', 'wp-e-commerce' ); ?></label>
220
					<p class='description'><?php _e( 'No shipping rates will be displayed if the shipment destination country is different than your base country/region.', 'wp-e-commerce' ); ?></p>
221
				</td>
222
			</tr>
223
224
			<?php
225
				ksort( $this->Services );
226
				$first = false;
227
			?>
228
			<tr>
229
				<td><?php _e( 'UPS Preferred Services', 'wp-e-commerce' ); ?></td>
230
				<td>
231
					<div class="ui-widget-content multiple-select">
232
						<?php foreach( array_keys( $this->Services ) as $service ): ?>
233
							<input type="checkbox" id="wps_ups_srv_<?php echo esc_attr( $service ); ?>" name="wpsc_ups_services[]" value="<?php echo esc_attr( $service ); ?>" <?php checked( is_array( $wpsc_ups_services ) && ( array_search( $service, $wpsc_ups_services ) !== false ) ); ?> />
234
							<label for="wps_ups_srv_<?php echo esc_attr( $service ); ?>"><?php echo esc_html( $this->Services[ $service ] ); ?></label><br />
235
						<?php endforeach; ?>
236
					</div>
237
					<p class='description'><?php _e( 'Note: All services used if no services selected', 'wp-e-commerce' ); ?></p>
238
				</td>
239
			</tr>
240
241
			<tr>
242
				<td colspan='2'><strong><?php _e( 'My UPS', 'wp-e-commerce' ); ?></strong></td>
243
			</tr>
244
			<tr>
245
				<td><?php _e( 'Account Number', 'wp-e-commerce' ); ?> *</td>
246
				<td>
247
					<input type="text" name='wpsc_ups_settings[upsaccount]' value="<?php echo esc_attr( $wpsc_ups_settings['upsaccount'] ); ?>" />
248
				</td>
249
			</tr>
250
			<tr>
251
				<td><?php _e( 'Username', 'wp-e-commerce' ); ?></td>
252
				<td>
253
					<input type="text" name='wpsc_ups_settings[upsusername]' value="<?php echo esc_attr( base64_decode( $wpsc_ups_settings['upsusername'] ) ); ?>" />
254
				</td>
255
			</tr>
256
			<tr>
257
				<td><?php _e( 'Password', 'wp-e-commerce' ); ?></td>
258
				<td>
259
					<input type="password" name='wpsc_ups_settings[upspassword]' value="<?php echo esc_attr( base64_decode( $wpsc_ups_settings['upspassword'] ) ); ?>" />
260
				</td>
261
			</tr>
262
			<tr>
263
				<td><?php _e( 'UPS XML API Key', 'wp-e-commerce' ); ?></td>
264
				<td>
265
					<input type="text" name='wpsc_ups_settings[upsid]' value="<?php echo esc_attr( base64_decode( $wpsc_ups_settings['upsid'] ) ); ?>" />
266
					<p class='description'><?php printf( __( "Don't have an API login/ID? <a href='%s' target='_blank'>Sign up for My UPS</a>", 'wp-e-commerce' ), esc_url( "https://www.ups.com/upsdeveloperkit?loc=en_US" ) ); ?></p>
267
				</td>
268
			</tr>
269
			<tr>
270
				<td colspan='2'>
271
					<p class='description'><?php _e( '* For Negotiated rates, you must enter a UPS account number and select "Show UPS negotiated rates" ', 'wp-e-commerce' ); ?></p>
272
					<p class='description'><?php printf( __( "For more help configuring UPS, please <a href='%s'>read our documentation</a>", 'wp-e-commerce' ), esc_url( 'http://docs.wpecommerce.org/wiki/documentation/shipping/ups' ) ); ?></p>
273
				</td>
274
			</tr>
275
		</table></td></tr>
276
		<?php
277
		// End new Code
278
		return ob_get_clean();
279
	}
280
281
	function submit_form() {
282
		/* This function is called when the user hit "submit" in the
283
		 * UPS settings area under Shipping to update the setttings.
284
		 */
285
		if (isset( $_POST['wpsc_ups_settings'] ) && !empty( $_POST['wpsc_ups_settings'] ) ) {
286
			if ( isset( $_POST['wpsc_ups_services'] ) ) {
287
				$wpsc_ups_services = stripslashes_deep( $_POST['wpsc_ups_services'] );
288
				update_option('wpsc_ups_services',$wpsc_ups_services);
289
			}
290
			$temp = stripslashes_deep( $_POST['wpsc_ups_settings'] );
291
			// base64_encode the information so it isnt stored as plaintext.
292
			// base64 is by no means secure but without knowing if the server
293
			// has mcrypt installed what can you do really?
294
			$temp['upsusername'] = base64_encode( $temp['upsusername'] );
295
			$temp['upspassword'] = base64_encode( $temp['upspassword'] );
296
			$temp['upsid'] = base64_encode( $temp['upsid'] );
297
298
			update_option('wpsc_ups_settings', $temp);
299
		}
300
		return true;
301
	}
302
303
	function array2xml( $data ) {
304
		$xml = "";
305
		if ( is_array( $data ) ) {
306
			foreach( $data as $key => $value ) {
307
				$xml .= "<" . trim( $key ) . ">";
308
				$xml .= $this->array2xml($value);
309
				$xml .= "</" . trim( $key ) . ">";
310
			}
311
		} else if ( is_bool( $data ) ) {
312
			if ( $data ){
313
				$xml = "true\n";
314
			} else {
315
				$xml = "false\n";
316
			}
317
		} else {
318
			$xml = trim( $data ) . "\n";
319
		}
320
		return $xml;
321
	}
322
323
	private function _is_large( &$pack, $package ) {
324
		$maximum = 165; // in inches
325
		$large_floor = 130; // in inches
326
		$calc_total=(round($package->length)+(2*round($package->width))+(2*round($package->height))); //see http://www.ups.com/content/us/en/resources/prepare/oversize.html.
327
		if ( $calc_total > $maximum || round( $package->length ) > 108 ) { //see http://www.ups.com/content/us/en/resources/prepare/oversize.html.
328
			throw new Exception( "Package dimensions exceed non-freight limits" );
329
		} elseif ( $calc_total > $large_floor ) {
330
			$pack["LargePackageIndicator"] = "";
331
		}
332
	}
333
334
	private function _insured_value( &$pack, $package, $args ){
335
		$monetary_value = $package->value;
336
		if ( $package->insurance === TRUE ){
0 ignored issues
show
Coding Style introduced by
TRUE, FALSE and NULL must be lowercase; expected true, but found TRUE.
Loading history...
337
			if ( $package->insured_amount ) {
338
				$monetary_value = $package->insured_amount;
339
			}
340
			$pack["PackageServiceOptions"]["InsuredValue"] = array(
341
				"CurrencyCode" => $args["currency"],
342
				"MonetaryValue" => $package->insured_amount
343
			);
344
		}
345
	}
346
347
	private function _declared_value( &$pack, $package, $args ){
348
		$pack["PackageServiceOptions"]["DeclaredValue"] = array(
349
			"CurrencyCode" => $args["currency"],
350
			"MonetaryValue" => $package->value //Per package value, not total value
351
		);
352
	}
353
354
	private function _build_shipment( &$Shipment, $args ){
355
356
		$cart_shipment = apply_filters( 'wpsc_the_shipment', $this->shipment, $args ); //Filter to allow reprocessing shipment packages.
357
358
		foreach ( $cart_shipment->packages as $package ) {
359
			$pack = array(
360
				"PackagingType" => array(
361
					"Code" => "02"
362
				),
363
				"Dimensions" => array(
364
					"UnitOfMeasurement" => array(
365
						"Code" => "IN"
366
					),
367
					"Length" => $package->length,
368
					"Width" => $package->width,
369
					"Height" => $package->height
370
				),
371
				"PackageWeight"=>array(
372
					"UnitOfMeasurement"=>array(
373
						"Code" => "LBS"
374
					),
375
					"Weight" => $package->weight
376
				)
377
			); // End Package
378
			// handle if the package is "large" or not (UPS standard)
379
			$this->_is_large($pack, $package);
380
			$this->_insured_value($pack, $package, $args);
381
			$this->_declared_value($pack, $package, $args);
382
			$Shipment .= $this->array2xml(array("Package"=>$pack));
383
		} // End for each package in shipment
384
	}
385
386
	private function _buildRateRequest( $args ){
387
		// Vars is an array
388
		// $RateRequest, $RatePackage, $RateCustomPackage, $RateRequestEnd
389
		// Are defined in ups_data.php that is included below if not
390
		// done so by instantiating class ... shouldnt ever need to
391
		// Always start of with this, it includes the auth block
392
		$REQUEST = "<?xml version=\"1.0\"?>\n
393
		<AccessRequest xml:lang=\"en-US\">\n";
394
395
		$access = array(
396
			"AccessLicenseNumber" => base64_decode( $args['api_id'] ),   // UPS API ID#
397
			"UserId" => base64_decode( $args['username'] ), // UPS API Username
398
			"Password" => base64_decode( $args['password'] )  // UPS API Password
399
		);
400
401
		$REQUEST .= $this->array2xml( $access );
402
		$REQUEST .= "</AccessRequest>\n";
403
		$REQUEST .= "<RatingServiceSelectionRequest xml:lang=\"en-US\">\n";
404
405
		// By Default we will shop. Shop when you do not have a service type
406
		// and you want to get a set of services and rates back!
407
		$RequestType = "Shop";
408
		// If service type is set we cannot shop so instead we Rate!
409
		if ( isset( $args["service"] ) ) {
410
			$RequestType = "Rate";
411
		}
412
413
		$RatingServiceRequest = array(
414
			"Request" => array(
415
				"TransactionReference" => array(
416
					"CustomerContext" => "Rate Request",
417
					"XpciVersion" => "1.0001"
418
				),
419
				"RequestAction" => "Rate",
420
				"RequestOption" => $RequestType
421
			)
422
		);
423
424
		// Set the dropoff code
425
		$dropCode = ( array_key_exists( 'DropoffType', $args ) ) ? $args['DropoffType'] : '01';
426
		$PickupType = array(
427
			"PickupType" => array(
428
				"Code" => $dropCode
429
			)
430
		);
431
432
		$REQUEST .= $this->array2xml( $PickupType );
433
434
		if ( $dropCode == "11" && $args['shipr_ccode'] == "US" ) {
435
			// Set the request code
436
			$CustCode = ( array_key_exists( 'CustomerType', $args ) ) ? $args['CustomerType'] : '01';
437
			$CustomerType = array(
438
				"CustomerClassification" => array(
439
					"Code"=>$CustCode
440
				)
441
			);
442
			$REQUEST .= $this->array2xml( $CustomerType );
443
		}
444
445
		// Set up Shipment Node
446
		$Shipment = "";
447
448
		// Shipper Address (billing)
449
		$Shipper = array(
450
			"Address" => array(
451
				"StateProvinceCode" => $args['shipr_state'],
452
				"PostalCode" => $args['shipr_pcode'], // The shipper Postal Code
453
				"CountryCode" => $args['shipr_ccode']
454
			)
455
		);
456
457
		// Negotiated Rates
458
		if ( array_key_exists( 'negotiated_rates', $args ) ) {
459
			if ( $args['negotiated_rates'] == '1' && ! empty( $args['account_number'] ) ) {
460
				$Shipper["ShipperNumber"] = $args['account_number'];
461
			}
462
		}
463
464
		// If the city is configured use it
465
		if ( array_key_exists( 'shipr_city', $args ) ) {
466
			if ( ! empty( $args['shipr_city'] ) ) {
467
				$Shipper["Address"]["City"] = $args["shipr_city"];
468
			}
469
		}
470
471
		$Shipment .= $this->array2xml( array("Shipper" => $Shipper ) );
472
473
		// The physical address the shipment is from (normally the same as billing)
474
		$ShipFrom = array(
475
			"Address" => array(
476
				"StateProvinceCode" => $args['shipf_state'],
477
				"PostalCode" => $args['shipf_pcode'], // The shipper Postal Code
478
				"CountryCode" => $args['shipf_ccode']
479
			));
480
481
		// If the city is configured use it
482
		if ( array_key_exists( 'shipf_city', $args ) ) {
483
			if ( ! empty( $args['shipf_city'] ) ) {
484
				$ShipFrom["Address"]["City"] = $args["shipf_city"];
485
			}
486
		}
487
488
		$Shipment .= $this->array2xml( array( "ShipFrom" => $ShipFrom ) );
489
490
		$ShipTo = array(
491
			"Address" => array(
492
				"StateProvinceCode" => $args['dest_state'], // The Destination State
493
				"PostalCode" => $args['dest_pcode'], // The Destination Postal Code
494
				"CountryCode" => $args['dest_ccode'], // The Destination Country
495
				//"ResidentialAddress"=>"1"
496
			)
497
		);
498
499
		if ( $args['residential'] == '1' ) { //ResidentialAddressIndicator orig - Indicator
500
			$ShipTo["Address"]["ResidentialAddressIndicator"] = "1";
501
		}
502
503
		$Shipment .= $this->array2xml( array( "ShipTo" => $ShipTo ) );
504
505
		// If there is a specific service being requested then
506
		// we want to pass the service into the XML
507
		if ( isset( $args["service"] ) ) {
508
		   $Shipment .= $this->array2xml( array( "Service" => array( "Code" => $args['service'] ) ) );
509
		}
510
511
		// Include this only if you want negotiated rates
512
		if ( array_key_exists( 'negotiated_rates', $args ) ){
513
			if ( $args['negotiated_rates'] == "1" ) {
514
				$Shipment .= $this->array2xml( array( "RateInformation" => array( "NegotiatedRatesIndicator" => "" ) ) );
515
			}
516
		}
517
518
		if ( (boolean) $args["singular_shipping"] ) {
519
			$this->_build_shipment( $Shipment, $args );
520
		} else {
521
			$package = array(
522
				"Package" => array(
523
					"PackagingType" => array(
524
						"Code" => $args['packaging']
525
					),
526
					"PackageWeight" => array(
527
						"UnitOfMeasurement" => array(
528
							"Code" => $args['units']
529
						),
530
						"Weight" => $args["weight"]
531
					)
532
				)
533
			);
534
			if ( (boolean) $args["insured_shipment"] ) {
535
				$package["PackageServiceOptions"] = array(
536
					"InsuredValue"=> array(
537
						"CurrencyCode" => $args["currency"],
538
						"MonetaryValue" => $args["cart_total"]
539
					)
540
				);
541
			}
542
543
			$Shipment .= $this->array2xml( $package );
544
		}
545
546
		// Set the structure for the Shipment Node
547
		$RatingServiceRequest["Shipment"] = $Shipment;
548
549
		$REQUEST .= $this->array2xml( $RatingServiceRequest );
550
		$REQUEST .= "</RatingServiceSelectionRequest>";
551
552
		// Return the final XML document as a string to be used by _makeRateRequest
553
		return $REQUEST;
554
	}
555
556
	private function _makeRateRequest( $message ){
557
		// Make the XML request to the server and retrieve the response
558
		$ch = curl_init();
0 ignored issues
show
introduced by
Using cURL functions is highly discouraged within VIP context. Check (Fetching Remote Data) on VIP Documentation.
Loading history...
559
560
		curl_setopt( $ch, CURLOPT_URL, $this->service_url );
0 ignored issues
show
introduced by
Using cURL functions is highly discouraged within VIP context. Check (Fetching Remote Data) on VIP Documentation.
Loading history...
561
		curl_setopt( $ch, CURLOPT_POST, 1 );
0 ignored issues
show
introduced by
Using cURL functions is highly discouraged within VIP context. Check (Fetching Remote Data) on VIP Documentation.
Loading history...
562
		curl_setopt( $ch, CURLOPT_POSTFIELDS, $message );
0 ignored issues
show
introduced by
Using cURL functions is highly discouraged within VIP context. Check (Fetching Remote Data) on VIP Documentation.
Loading history...
563
		curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1);
0 ignored issues
show
introduced by
Using cURL functions is highly discouraged within VIP context. Check (Fetching Remote Data) on VIP Documentation.
Loading history...
564
565
		$data = curl_exec( $ch );
0 ignored issues
show
introduced by
Using cURL functions is highly discouraged within VIP context. Check (Fetching Remote Data) on VIP Documentation.
Loading history...
566
		curl_close( $ch );
0 ignored issues
show
introduced by
Using cURL functions is highly discouraged within VIP context. Check (Fetching Remote Data) on VIP Documentation.
Loading history...
567
		return $data;
568
	}
569
570
	public function futureDate( $interval ){
571
		//Wed Apr 7
572
		date_default_timezone_set( 'America/Los_Angeles' );
0 ignored issues
show
introduced by
Using date_default_timezone_set() and similar isn’t allowed, instead use WP internal timezone support.
Loading history...
573
		$timestamp = date( 'c' );
574
		$hour = date( "G" );
575
		if ( (int) $hour >= 3 ) {
576
			$interval += 1;
577
		}
578
579
		$date = date( "Y-m-d" );
580
		$interval = " +$interval day";
581
		$final = date( "D M j", strtotime( date( "Y-m-d", strtotime( $date ) ) . $interval ) );
582
		$test = explode(" ", $final);
583
584
		if ( $test[0] == "Sat" ) {
585
			return $this->futureDate( $interval + 2 );
586
		} else if ( $test[0] == "Sun" ) {
587
			return $this->futureDate( $interval + 1 );
588
		}
589
		return $final;
590
	}
591
592
	private function _parseQuote( $raw ){
593
		global $wpdb;
594
595
		$config = get_option( 'wpsc_ups_settings', array() );
596
		$debug = ( array_key_exists( 'upsenvironment', $config) ) ? $config['upsenvironment'] : "";
597
598
		$rate_table = array();
599
		$wpsc_ups_services = get_option( "wpsc_ups_services" );
600
		// Initialize a DOM using the XML passed in!
601
		$objDOM = new DOMDocument();
602
		if ( $raw != '' ) {
603
			$objDOM->loadXML( $raw );
604
605
			// Get the <ResponseStatusCode> from the UPS XML
606
			$getStatusNode = $objDOM->getElementsByTagName( "ResponseStatusCode" );
607
			// Get the value of the error code, 1 == No Error, 0 == Error !!!
608
			$statusCode = $getStatusNode->item( 0 )->nodeValue;
609
610
			if ( $statusCode == "0" ) {
611
				// Usually I dont leave debug stuff in but this is handy stuff!!
612
				// it will print out the error message returned by UPS!
613
				if ( $debug == "1" ) {
614
					$getErrorDescNode = $objDOM->getElementsByTagName( "ErrorDescription" );
615
					$ErrorDesc = $getErrorDescNode->item( 0 )->nodeValue;
616
					echo "<br />Error : " . $ErrorDesc . "<br />";
617
				}
618
				return false;
619
			} else {
620
				$RateBlocks = $objDOM->getElementsByTagName( 'RatedShipment' );
621
				foreach ( $RateBlocks as $rate_block ) {
622
					// Get the <Service> Node from the XML chunk
623
					$getServiceNode = $rate_block->getElementsByTagName( 'Service' );
624
					$serviceNode = $getServiceNode->item( 0 );
625
626
					// Get the <Code> Node from the <Service> chunk
627
					$getServiceCodeNode = $serviceNode->getElementsByTagName( 'Code' );
628
					// Get the value from <Code>
629
					$serviceCode = $getServiceCodeNode->item( 0 )->nodeValue;
630
					$go = true;
631
					$price = '';
632
					$time = '';
633
634
					//if (array_key_exists('ups_negotiated_rates', $config)){
635
					$getNegotiatedRateNode = $rate_block->getElementsByTagName( 'NegotiatedRates' );
636
					if ( $getNegotiatedRateNode ) {
637
						$negotiatedRateNode = $getNegotiatedRateNode->item( 0 );
638
						if ( $negotiatedRateNode ) {
639
							$getNetSummaryNode = $negotiatedRateNode->getElementsByTagName( 'NetSummaryCharges' );
640
							$netSummaryNode = $getNetSummaryNode->item( 0 );
641
642
							$getGrandTotalNode = $netSummaryNode->getElementsByTagName( 'GrandTotal' );
643
							$grandTotalNode = $getGrandTotalNode->item( 0 );
644
645
							$getMonetaryNode = $grandTotalNode->getElementsByTagName( 'MonetaryValue' );
646
							$monetaryNode = $getMonetaryNode->item( 0 )->nodeValue;
647
							if ( ! empty( $monetaryNode ) ) {
648
								$go = false;
649
								$price = $monetaryNode;
650
							}
651
						}
652
					}
653
654
					// Get the <TotalCharges> Node from the XML chunk
655
					$getChargeNodes = $rate_block->getElementsByTagName( 'TotalCharges' );
656
					$chargeNode     = $getChargeNodes->item( 0 );
657
658
					// Get the <CurrencyCode> from the <TotalCharge> chunk
659
					$getCurrNode = $chargeNode->getElementsByTagName( 'CurrencyCode' );
660
661
					// Get the value of <CurrencyCode>
662
					$currCode = $getCurrNode->item( 0 )->nodeValue;
663
664
					if ( $go == true ){
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
665
						// Get the <MonetaryValue> from the <TotalCharge> chunk
666
						$getMonetaryNode = $chargeNode->getElementsByTagName( 'MonetaryValue' );
667
						// Get the value of <MonetaryValue>
668
						$price = $getMonetaryNode->item( 0 )->nodeValue;
669
					}
670
					// If there are any services specified in the admin area
671
					// this will check that list and pass on adding any services that
672
					// are not explicitly defined.
673
					if ( ! empty( $wpsc_ups_services ) ) {
674
						if ( is_array( $wpsc_ups_services ) ) {
675
							if ( array_search( $serviceCode, (array) $wpsc_ups_services ) === false ) {
676
								continue;
677
							}
678
						} else if ( $wpsc_ups_services != $serviceCode ) {
679
							continue;
680
						}
681
					}
682
					if ( array_key_exists( $serviceCode, (array) $this->Services ) ) {
683
						$rate_table[ $this->Services[ $serviceCode ] ] = array( $currCode, $price );
684
					}
685
				} // End foreach rated shipment block
686
			}
687
		}
688
		// Reverse sort the rate selection so it is cheapest first!
689
		if ( ! empty( $rate_table ) ) {
690
			asort( $rate_table );
691
		}
692
		return $rate_table;
693
	}
694
695
	private function _formatTable( $services, $currency = false ) {
696
		/* The checkout template expects the array to be in a certain
697
		 * format. This function will iterate through the provided
698
		 * services array and format it for use. During the loop
699
		 * we take advantage of the loop and translate the currency
700
		 * if necessary based off of what UPS tells us they are giving us
701
		 * for currency and what is set for the main currency in the settings
702
		 * area
703
		 */
704
		$converter = null;
705
		if ( $currency ) {
706
			$converter = new CURRENCYCONVERTER();
707
		}
708
		$finalTable = array();
709
		foreach ( array_keys( $services ) as $service ) {
710
			if ( $currency != false && $currency != $services[ $service ][0] ) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison !== instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
711
				$temp = $services[ $service ][1];
712
				$services[ $service ][1] = $converter->convert(
713
					$services[ $service ][1],
714
					$currency,
715
					$services[ $service ][0]
716
				);
717
			}
718
			$finalTable[ $service ] = $services[ $service ][1];
719
		}
720
		return $finalTable;
721
	}
722
723
	function getQuote() {
724
		global $wpdb, $wpec_ash, $wpsc_cart, $wpec_ash_tools;
725
		// Arguments array for various functions to use
726
		$args = array();
727
728
		$args['dest_ccode'] = wpsc_get_customer_meta( 'shippingcountry' );
729
730
		// Get the ups settings from the ups account info page (Shipping tab)
731
		$wpsc_ups_settings = get_option( 'wpsc_ups_settings', array() );
732
733
		//Disable International Shipping. Default: Enabled, as it currently is.
734
		$args['intl_rate'] = isset( $wpsc_ups_settings['intl_rate'] ) && ! empty( $wpsc_ups_settings['intl_rate'] ) ? FALSE : TRUE;
0 ignored issues
show
Coding Style introduced by
TRUE, FALSE and NULL must be lowercase; expected false, but found FALSE.
Loading history...
Coding Style introduced by
TRUE, FALSE and NULL must be lowercase; expected true, but found TRUE.
Loading history...
735
		if ( ! $args['intl_rate'] && $args['dest_ccode'] != get_option( 'base_country' ) ) {
736
			return array();
737
		}
738
739
		// Destination zip code
740
		$args['dest_pcode'] = (string) wpsc_get_customer_meta( 'shippingpostcode' );
741
742
		if ( ! is_object( $wpec_ash_tools ) ) {
743
			$wpec_ash_tools = new ASHTools();
744
		}
745
746
		if ( empty( $args['dest_pcode'] ) && $wpec_ash_tools->needs_post_code( $args['dest_ccode'] ) ) {
747
			// We cannot get a quote without a zip code so might as well return!
748
			return array();
749
		}
750
751
		// Get the total weight from the shopping cart
752
		$args['weight'] = wpsc_cart_weight_total();
753
		if ( empty( $args['weight'] ) ) {
754
			return array();
755
		}
756
757
		$args['dest_state'] = '';
758
759
		$wpsc_country = new WPSC_Country( wpsc_get_customer_meta( 'shippingcountry' ) );
760
761
		if ( $wpsc_country->has_regions() ) {
762
			$wpsc_region = $wpsc_country->get_region( wpsc_get_customer_meta( 'shippingregion' ) );
763
			if ( is_a( $wpsc_region, 'WPSC_Region' ) ) {
764
				$args['dest_state'] = $wpsc_region->get_code();
0 ignored issues
show
Bug introduced by
The method get_code cannot be called on $wpsc_region (of type string|false).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
765
			}
766
		}
767
768
		if ( empty( $args['dest_state'] ) ) {
769
			$args['dest_state'] = wpsc_get_customer_meta( 'shippingstate' );
770
		}
771
772
		if ( ! is_object( $wpec_ash ) ) {
773
			$wpec_ash = new ASH();
774
		}
775
776
		$shipping_cache_check['state']   = $args['dest_state']; //The destination is needed for cached shipment check.
0 ignored issues
show
Coding Style Comprehensibility introduced by
$shipping_cache_check was never initialized. Although not strictly required by PHP, it is generally a good practice to add $shipping_cache_check = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
777
		$shipping_cache_check['country'] = $args['dest_ccode'];
778
		$shipping_cache_check['zipcode'] = $args['dest_pcode'];
779
780
		$this->shipment = $wpec_ash->get_shipment();
781
		$this->shipment->set_destination( $this->internal_name, $shipping_cache_check ); //Set this shipment's destination.
782
		$this->shipment->rates_expire = date( 'Y-m-d' );
783
		$args['shipper'] = $this->internal_name;
784
785
		$args['singular_shipping'] = ( array_key_exists( 'singular_shipping', $wpsc_ups_settings ) ) ? $wpsc_ups_settings['singular_shipping']    : '0';
786
		if ( $args['weight'] > 150 && ! (boolean) $args['singular_shipping'] ) { // This is where shipping breaks out of UPS if weight is higher than 150 LBS
787
				$over_weight_txt = apply_filters(
788
						'wpsc_shipment_over_weight',
789
						__( 'Your order exceeds the standard shipping weight limit. Please contact us to quote other shipping alternatives.', 'wp-e-commerce' ),
790
						$args
791
					);
792
				$shipping_quotes[$over_weight_txt] = 0; // yes, a constant.
0 ignored issues
show
Coding Style Comprehensibility introduced by
$shipping_quotes was never initialized. Although not strictly required by PHP, it is generally a good practice to add $shipping_quotes = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
793
				$wpec_ash->cache_results( $this->internal_name, array( $shipping_quotes ), $this->shipment ); //Update shipment cache.
794
				return array( $shipping_quotes );
795
		}
796
797
		$cache = $wpec_ash->check_cache( $this->internal_name, $this->shipment ); //And now, we're ready to check cache.
798
799
		// We do not want to spam UPS (and slow down our process) if we already
800
		// have a shipping quote!
801
		if ( count( $cache['rate_table'] ) >= 1 ) {
802
			return $cache['rate_table'];
803
		}
804
805
		// Final rate table
806
		$rate_table = array();
807
808
		// API Auth settings //
809
		$args['username']          = ( array_key_exists( 'upsaccount',           $wpsc_ups_settings ) ) ? $wpsc_ups_settings['upsusername']          : '';
810
		$args['password']          = ( array_key_exists( 'upspassword',          $wpsc_ups_settings ) ) ? $wpsc_ups_settings['upspassword']          : '';
811
		$args['api_id']            = ( array_key_exists( 'upsid',                $wpsc_ups_settings ) ) ? $wpsc_ups_settings['upsid']                : '';
812
		$args['account_number']    = ( array_key_exists( 'upsaccount',           $wpsc_ups_settings ) ) ? $wpsc_ups_settings['upsaccount']           : '';
813
		$args['negotiated_rates']  = ( array_key_exists( 'ups_negotiated_rates', $wpsc_ups_settings ) ) ? $wpsc_ups_settings['ups_negotiated_rates'] : '';
814
		$args['residential']       = $wpsc_ups_settings['49_residential'];
815
		$args['insured_shipment']  = ( array_key_exists( 'insured_shipment',     $wpsc_ups_settings ) ) ? $wpsc_ups_settings['insured_shipment']     : '0';
816
		// What kind of pickup service do you use ?
817
		$args['DropoffType']       = $wpsc_ups_settings['DropoffType'];
818
		$args['packaging']         = $wpsc_ups_settings['48_container'];
819
		// Preferred Currency to display
820
		$currency_data = WPSC_Countries::get_currency_code( get_option( 'currency_type' ) );
821
		if ( ! empty( $currency_data ) ) {
822
			$args['currency'] = $currency_data;
823
		} else {
824
			$args['currency'] = 'USD';
825
		}
826
		// Shipping billing / account address
827
		$region = new WPSC_Region( get_option( 'base_country' ), get_option( 'base_region' ) );
828
829
		$args['shipr_state'] = $region->get_code();
830
		$args['shipr_city']  = get_option( 'base_city' );
831
		$args['shipr_ccode'] = get_option( 'base_country' );
832
		$args['shipr_pcode'] = get_option( 'base_zipcode' );
833
834
		// Physical Shipping address being shipped from
835
		$args['shipf_state'] = $args['shipr_state'];
836
		$args['shipf_city']  = $args['shipr_city'];
837
		$args['shipf_ccode'] = $args['shipr_ccode'];
838
		$args['shipf_pcode'] = $args['shipr_pcode'];
839
		$args['units']       = 'LBS';
840
841
		$args['cart_total'] = $wpsc_cart->calculate_subtotal( true );
842
		$args = apply_filters( 'wpsc_shipment_data', $args, $this->shipment );
843
		if ( isset( $args['stop'] ) ) { //Do not get rates.
844
			return array();
845
		}
846
		// Build the XML request
847
		$request = $this->_buildRateRequest( $args );
848
		// Now that we have the message to send ... Send it!
849
		$raw_quote = $this->_makeRateRequest( $request );
850
		// Now we have the UPS response .. unfortunately its not ready
851
		// to be viewed by normal humans ...
852
		$quotes = $this->_parseQuote( $raw_quote );
853
		// If we actually have rates back from UPS we can use em!
854
		if ( $quotes != false ) {
855
			$rate_table = apply_filters( 'wpsc_rates_table', $this->_formatTable( $quotes, $args['currency'] ), $args, $this->shipment );
856
		} else {
857
			if ( isset( $wpsc_ups_settings['upsenvironment'] ) ) {
858
				echo '<strong>:: GetQuote ::DEBUG OUTPUT::</strong><br />';
859
				echo 'Arguments sent to UPS';
860
				print_r( $args );
0 ignored issues
show
introduced by
The use of function print_r() is discouraged
Loading history...
861
				echo '<hr />';
862
				print $request;
863
				echo '<hr />';
864
				echo 'Response from UPS';
865
				echo $raw_quote;
866
				echo '</strong>:: GetQuote ::End DEBUG OUTPUT::';
867
			}
868
		}
869
870
		$wpec_ash->cache_results(
871
			$this->internal_name,
872
			$rate_table,
873
			$this->shipment
874
		);
875
876
		// return the final formatted array !
877
		return $rate_table;
878
	}
879
880
	// Empty Function, this exists just b/c it is prototyped elsewhere
881
	function get_item_shipping() {
882
	}
883
}
884
$ash_ups = new ash_ups();
885
$wpsc_shipping_modules[ $ash_ups->getInternalName() ] = $ash_ups;
886