Completed
Push — master ( 43e406...3aef2a )
by Antony
09:00
created

Order::matchCustomerAddress()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 11
rs 9.4285
cc 3
eloc 6
nc 3
nop 2
1
<?php namespace SilvershopUnleashed\Model;
2
3
use DataExtension;
4
use ShopConfig;
5
use Member;
6
use HasGroupPricing;
7
use DateTime;
8
use UnleashedAPI;
9
use Utils;
10
use SS_Datetime;
11
use SS_Log;
12
13
class Order extends DataExtension
14
{
15
    /**
16
     * Record when an order is sent to Unleashed
17
     */
18
    private static $db = [
0 ignored issues
show
Unused Code introduced by
The property $db is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
19
        'OrderSentToUnleashed' => 'SS_Datetime'
20
    ];
21
22
    /**
23
     * Enable sending of Sales Orders to Unleashed
24
     * @var boolean
25
     */
26
    private static $send_sales_orders_to_unleashed = false;
0 ignored issues
show
Unused Code introduced by
The property $send_sales_orders_to_unleashed is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
27
28
    /**
29
     * Declare the tax modifier used in Silvershop
30
     *
31
     * @example  'FlatTaxModifier'
32
     * @var string
33
     */
34
    private static $tax_modifier_class_name = '';
0 ignored issues
show
Unused Code introduced by
The property $tax_modifier_class_name is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
35
36
    /**
37
     * Days following payment that delivery is expected
38
     * @var int
39
     */
40
    private static $expected_days_to_deliver = 0;
0 ignored issues
show
Unused Code introduced by
The property $expected_days_to_deliver is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
41
42
    /**
43
     * Default CreatedBy
44
     * @var string
45
     */
46
    private static $default_created_by = '';
0 ignored issues
show
Unused Code introduced by
The property $default_created_by is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
47
48
    /**
49
     * Default payment term
50
     * @var string
51
     */
52
    private static $default_payment_term = 'Same Day';
0 ignored issues
show
Unused Code introduced by
The property $default_payment_term is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
53
54
    /**
55
     * Default Customer Type
56
     * @var string
57
     */
58
    private static $default_customer_type = '';
0 ignored issues
show
Unused Code introduced by
The property $default_customer_type is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
59
60
    /**
61
     * Default Sales Order Group
62
     * @var string
63
     */
64
    private static $default_sales_order_group = '';
0 ignored issues
show
Unused Code introduced by
The property $default_sales_order_group is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
65
66
    /**
67
     * Default Sales Person
68
     * @var string
69
     */
70
    private static $default_sales_person = '';
0 ignored issues
show
Unused Code introduced by
The property $default_sales_person is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
71
72
    /**
73
     * Default Source Id
74
     * @var string
75
     */
76
    private static $default_source_id = '';
0 ignored issues
show
Unused Code introduced by
The property $default_source_id is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
77
78
    /**
79
     * Apply Guid if absent
80
     */
81
    public function onBeforeWrite()
82
    {
83
        parent::onBeforeWrite();
84
        if (!$this->owner->getField("Guid")) {
85
            $this->owner->Guid = (string) Utils::createGuid();
86
        }
87
    }
88
89
90
    /**
91
     * Return the Address Name
92
     * @return string
93
     */
94
    public function getAddressName($address)
95
    {
96
        if (is_array($address)) {
97
            $address_name = $address['StreetAddress'];
98
            if ($address['StreetAddress2']) {
99
                $address_name .= ' ' . $address['StreetAddress2'];
100
            }
101
            $address_name .= ' ' . $address['City'];
102
        } else {
103
            $address_name = $address->Address;
104
            if ($address->AddressLine2) {
105
                $address_name .= ' ' . $address->AddressLine2;
106
            }
107
            $address_name .= ' ' . $address->City;
108
        }
109
        return $address_name;
110
    }
111
112
    /**
113
     * Match the order's shipping address to items returned from Unleashed
114
     * @return boolean
115
     */
116
    public function matchCustomerAddress($items, $shipping_address)
117
    {
118
        // Obtain the delivery address
119
        $address = $items[0]['Addresses'][0];
120
        if ($address['AddressType'] != "Physical") {
121
            if (isset($items[0]['Addresses'][1])) {
122
                $address = $items[0]['Addresses'][1];
123
            }
124
        }
125
        return strtoupper($this->getAddressName($shipping_address)) == strtoupper($this->getAddressName($address));
126
    }
127
128
    /**
129
     * Send a sales order to Unleashed upon paid status
130
     *
131
     * Note: create Customer first
132
     */
133
    public function onAfterWrite()
134
    {
135
        parent::onAfterWrite();
136
        $config = $this->owner->config();
137
138
        if ($config->send_sales_orders_to_unleashed
139
            && $this->owner->Status == 'Paid'
140
            && !$this->owner->OrderSentToUnleashed) {
141
            // Definitions
142
            $order = $this->owner;
143
            $billing_address = $order->BillingAddress();
144
            $shipping_address = $order->ShippingAddress();
145
            $member = $order->Member();
146
            $comments = $order->Notes;
147
            $countries = ShopConfig::config()->iso_3166_country_codes;
148
            $subtotal = $order->Total();
149
            $sell_price_tier = ShopConfig::current()->CustomerGroup()->Title;
150
            $taxable = false;
151
            $tax_code = '';
152
            $tax_total = 0;
153
            $tax_class_name = $config->tax_modifier_class_name;
154
            $modifiers = $order->Modifiers();
155
            $tax_modifier = $order->getModifier($config->tax_modifier_class_name);
156
            $shipping_method = '';
157
            $sales_order_lines = [];
158
            $line_number = 0;
159
160
            // Customer
161
            if (!$member->exists()) {  // Create Member for Guests
162
                $member = Member::create();
163
                $member->FirstName = $order->FirstName;
164
                $member->Surname = $order->Surname;
165
                $member->Email = $order->getLatestEmail();
166
            }
167
168
            // Selling Price Tier if Customer set with a different pricecard using the Extended Pricing Module
169
            if (class_exists('HasGroupPricing') && $member->Groups()->exists()) {
170
                $levels = HasGroupPricing::get_levels();
171
                foreach ($member->Groups() as $group) {
172
                    if (array_key_exists($group->Code, $levels)) {
173
                        // Assign member specific group
174
                        $sell_price_tier = $group->Title;
175
                    }
176
                }
177
            }
178
179
            // Taxation (e.g. Sales Tax/GST)
180
            if (!empty($tax_modifier)) {
181
                $subtotal -= $tax_modifier->Amount;
182
                $taxable = true;
183
                $tax_code = $tax_modifier::config()->name;
184
                $tax_total = floatval($tax_modifier->Amount);
185
            }
186
187
            // Define Customer Code/Name (use Company field of BillingAddress to allow for B2B eCommerce sites)
188
            if ($billing_address->Company) {
189
                $customer_code_and_name = $billing_address->Company;    // use Organisation name
190
            } else {
191
                $customer_code_and_name = $order->getName();  // use Contact full name instead
192
            }
193
194
            if (!$member->Guid) {  // See if New Customer/Guest has previously purchased
195
                $response = UnleashedAPI::sendCall(
196
                    'GET',
197
                    'https://api.unleashedsoftware.com/Customers?contactEmail=' .  $member->Email
198
                );
199
200
                if ($response->getStatusCode() == '200') {
201
                    $contents = json_decode($response->getBody()->getContents(), true);
202
                    $items = $contents['Items'];
203
                    if ($items) {
204
                        // Email address exists
205
                        $member->Guid = $items[0]['Guid'];
206
                    } else {
207
                        // A Customer is not returned so we have a unique email address.
208
                        // Check to see if the Customer Code exists (we cannot double up on the Customer Code)
209
                        $response = UnleashedAPI::sendCall(
210
                            'GET',
211
                            'https://api.unleashedsoftware.com/Customers?customerCode=' .  $customer_code_and_name
212
                        );
213
214
                        if ($response->getStatusCode() == '200') {
215
                            $contents = json_decode($response->getBody()->getContents(), true);
216
                            $items = $contents['Items'];
217
                            if ($items) {
218
                                // A Customer Code already exists (and the email address is unique).
219
                                // If the address is the same then this is the Customer
220
                                if ($this->matchCustomerAddress($items, $shipping_address)) {
221
                                    $member->Guid = $items[0]['Guid'];
222
223
                                    //Note the existing email address in the Comment
224
                                    //PUT Customer is not available in Unleashed
225
                                    if ($comments) {
226
                                        $comments .= PHP_EOL;
227
                                    }
228
                                    $comments .= _t(
229
                                        'UnleashedAPI.addEmailToCustomerComment',
230
                                        'Add email to Customer: {email_address}',
231
                                        '',
232
                                        ['email_address' => $member->Email]
0 ignored issues
show
Documentation introduced by
array('email_address' => $member->Email) is of type array<string,?,{"email_address":"?"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
233
                                    );
234
                                } else {
235
                                    // The Customer Code already exists, we have a unique email address, but
236
                                    // the delivery address is new
237
                                    // We need to create a new Customer with a unique Customer Code
238
                                    $customer_code_and_name .= rand(10000000, 99999999);
239
                                }
240
                            }
241
                        }
242
                    }
243
                }
244
            }
245
246
            if (!$member->Guid) {
247
                // The Customer Code does not exists in Unleashed and the email address is unique
248
                // Create in Unleashed
249
                $member->Guid = (string) Utils::createGuid();
250
                $address_name_postal_new_customer = $this->getAddressName($billing_address);
251
                $address_name_physical_new_customer = $this->getAddressName($shipping_address);
252
253
                $body = [
254
                    'Addresses' => [
255
                        [
256
                            'AddressName' => $address_name_postal_new_customer,
257
                            'AddressType' => 'Postal',
258
                            'City' => $billing_address->City,
259
                            'Country' => $countries[$billing_address->Country],
260
                            'PostalCode' => $billing_address->PostalCode,
261
                            'Region' => $billing_address->State,
262
                            'StreetAddress' => $billing_address->Address,
263
                            'StreetAddress2' => $billing_address->AddressLine2
264
                        ],
265
                        [
266
                            'AddressName' => $address_name_physical_new_customer,
267
                            'AddressType' => 'Physical',
268
                            'City' => $shipping_address->City,
269
                            'Country' => $countries[$shipping_address->Country],
270
                            'PostalCode' => $shipping_address->PostalCode,
271
                            'Region' => $shipping_address->State,
272
                            'StreetAddress' => $shipping_address->Address,
273
                            'StreetAddress2' => $shipping_address->AddressLine2
274
                        ]
275
                    ],
276
                    'Currency' =>[
277
                        'CurrencyCode' => $order->Currency()
278
                    ],
279
                    'CustomerCode' => $customer_code_and_name,
280
                    'CustomerName' => $customer_code_and_name,
281
                    'ContactFirstName' => $member->FirstName,
282
                    'ContactLastName' => $member->Surname,
283
                    'Email' => $member->Email,
284
                    'Guid' => $member->Guid,
285
                    'PaymentTerm' => $config->default_payment_term,
286
                    'PrintPackingSlipInsteadOfInvoice' => true,
287
                    'SellPriceTier' => $sell_price_tier
288
                ];
289
290
                if ($taxable) {
291
                    $body['Taxable'] = $taxable;
292
                }
293
294
                if ($config->default_created_by) {
295
                    $body['CreatedBy'] = $config->default_created_by;
296
                }
297
298
                if ($config->default_customer_type) {
299
                    $body['CustomerType'] = $config->default_customer_type;
300
                }
301
302
                if ($billing_address->Phone) {  // add phone number if available
303
                    $body['PhoneNumber'] = $billing_address->Phone;
304
                }
305
306
                $response = UnleashedAPI::sendCall(
307
                    'POST',
308
                    'https://api.unleashedsoftware.com/Customers/' . $member->Guid,
309
                    ['json' => $body ]
310
                );
311
312
                if ($response->getReasonPhrase() == 'Created' && $order->Member()->exists()) {
313
                    $member->write();
314
                }
315
            }
316
317
318
            // Prepare Sales Order data
319
            if ($member->Guid) {  // Skip if previous calls to Customer have failed and the Guid has not been set
320
321
                // Dates
322
                $date_placed = new DateTime($order->Placed);
323
                $date_paid = new DateTime($order->Paid);
324
                $date_required = new DateTime($order->Paid);
325
                if ($config->expected_days_to_deliver) {
326
                    $date_required->modify('+' . $config->expected_days_to_deliver . 'day');
327
                }
328
329
                // Sales Order Lines
330
                foreach ($order->Items()->getIterator() as $item) {
331
                    // Definitions
332
                    $product = $item->Product();
333
                    $line_number += 1;
334
335
                    $sales_order_line = [
336
                        'DiscountRate' => 0,
337
                        'Guid' => $item->Guid,
338
                        'LineNumber' => $line_number,
339
                        'LineType' => null,
340
                        'LineTotal' => round(floatval($item->Total()), $config->rounding_precision),
341
                        'OrderQuantity' => (int) $item->Quantity,
342
                        'Product' => [
343
                            'Guid' => $product->Guid
344
                        ],
345
                        'UnitPrice' => round(floatval($product->getPrice()), $config->rounding_precision)
346
                    ];
347 View Code Duplication
                    if ($tax_class_name) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
348
                        $tax_calculator = new $tax_class_name;
349
                        $sales_order_line['LineTax'] = round(
350
                            $tax_calculator->value($item->Total()),
351
                            $config->rounding_precision
352
                        );
353
                        $sales_order_line['LineTaxCode'] = $tax_code;
354
                    }
355
                    $sales_order_lines[] = $sales_order_line;
356
                }
357
358
                // Add Modifiers that have an associated product_code
359
                foreach ($modifiers->sort('Sort')->getIterator() as $modifier) {
360
                    if ($modifier::config()->product_code &&
361
                        $modifier->Type !== 'Ignored' &&
362
                        $modifier->value()
363
                    ) {
364
                        $line_number += 1;
365
                        $sales_order_line = [
366
                            'DiscountRate' => 0,
367
                            'Guid' => $modifier->Guid,
368
                            'LineNumber' => $line_number,
369
                            'LineTotal' => round(floatval($modifier->Amount), $config->rounding_precision),
370
                            'LineType' => null,
371
                            'OrderQuantity' => 1,
372
                            'Product' => [
373
                                'ProductCode' => $modifier::config()->product_code,
374
                            ],
375
                            'UnitPrice' => round(floatval($modifier->Amount), $config->rounding_precision)
376
                        ];
377 View Code Duplication
                        if ($tax_class_name) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
378
                            $tax_calculator = new $tax_class_name;
379
                            $sales_order_line['LineTax'] = round(
380
                                $tax_calculator->value($modifier->Amount),
381
                                $config->rounding_precision
382
                            );
383
                            $sales_order_line['LineTaxCode'] = $tax_code;
384
                        }
385
                        $sales_order_lines[] = $sales_order_line;
386
                    }
387
                }
388
389
                // Shipping Module
390
                if (class_exists('ShippingMethod')) {
391
                    $name = $order->ShippingMethod()->Name;
392
                    if ($name) {
393
                        $shipping_method = $name;
394
                    }
395
                }
396
397
                $body = [
398
                    'Comments' => $comments,
399
                    'Currency' =>[
400
                        'CurrencyCode' => $order->Currency()
401
                    ],
402
                    'Customer' => [
403
                        'Guid' => $member->Guid
404
                    ],
405
                    'DeliveryCity' => $shipping_address->City,
406
                    'DeliveryCountry' => $countries[$shipping_address->Country],
407
                    'DeliveryPostCode' => $shipping_address->PostalCode,
408
                    'DeliveryRegion' => $shipping_address->State,
409
                    'DeliveryStreetAddress' => $shipping_address->Address,
410
                    'DeliveryStreetAddress2' => $shipping_address->AddressLine2,
411
                    'DiscountRate' => 0,
412
                    'Guid' => $order->Guid,
413
                    'OrderDate' => $date_placed->format('Y-m-d\TH:i:s'),
414
                    'OrderNumber' => $order->Reference,
415
                    'OrderStatus' => 'Parked',
416
                    'PaymentDueDate' => $date_paid->format('Y-m-d\TH:i:s'),
417
                    'ReceivedDate' => $date_placed->format('Y-m-d\TH:i:s'),
418
                    'RequiredDate' => $date_required->format('Y-m-d\TH:i:s'),
419
                    'SalesOrderLines' => $sales_order_lines,
420
                    'SubTotal' => $subtotal,
421
                    'Tax' => [
422
                        'TaxCode' => $tax_code
423
                    ],
424
                    'TaxTotal' => $tax_total,
425
                    'Total' => round(floatval($order->Total()), $config->rounding_precision)
426
                ];
427
428
                if ($shipping_method) {
429
                    $body['DeliveryMethod'] = $shipping_method;
430
                    $body['DeliveryName'] = $shipping_method;
431
                }
432
433
                if ($config->default_sales_order_group) {
434
                    $body['SalesOrderGroup'] = $config->default_sales_order_group;
435
                }
436
437
                if ($config->default_sales_person) {
438
                    $body['SalesPerson'] = $config->default_sales_person;
439
                }
440
441
                if ($config->default_source_id) {
442
                    $body['SourceId'] = $config->default_source_id;
443
                }
444
445
                $this->owner->extend('updateUnleashedSalesOrder', $body);
446
447
                $response = UnleashedAPI::sendCall(
448
                    'POST',
449
                    'https://api.unleashedsoftware.com/SalesOrders/' . $order->Guid,
450
                    ['json' => $body]
451
                );
452
                if ($response->getReasonPhrase() == 'Created') {
453
                    $this->owner->OrderSentToUnleashed = SS_Datetime::now()->Rfc2822();
454
                    $this->owner->write();
455
                }
456
            }
457
        }
458
    }
459
}
460