CountryPrice_OrderDOD::onInit()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
4
5
/**
6
 * Adds functionality to Order
7
 */
8
class CountryPrice_OrderDOD extends DataExtension
9
{
10
    private static $db = array(
11
        'IP' => 'Varchar(45)',
12
        'CurrencyCountry' => 'Varchar(3)',
13
        'OriginatingCountryCode' => 'Varchar(3)'
14
    );
15
16
    private static $has_one = array(
17
        'Distributor' => 'Distributor'
18
    );
19
20
    private static $searchable_fields = array(
21
        'DistributorID' => array(
22
            'title' => 'Distributor'
23
        ),
24
        'OriginatingCountryCode' => array(
25
            'field' => 'TextField',
26
            'filter' => 'PartialMatchFilter',
27
            'title' => 'Country Code (e.g. NZ)'
28
        )
29
    );
30
31
    private static $field_labels = array(
32
        'CurrencyCountry' => 'Currency Country',
33
        'OriginatingCountryCode' => 'Country'
34
    );
35
36
    private static $summary_fields = array(
37
        'Distributor.Title' => 'Distributor',
38
        'OriginatingCountryCode' => 'OriginatingCountryCode',
39
        'CurrencyUsed.Title' => 'Currency'
40
    );
41
42
    private static $_number_of_times_we_have_run_localise_order = 0;
43
44
    private static $only_allow_within_country_sales = false;
45
46
    /**
47
     * this method basically makes sure that the Order
48
     * has all the localised stuff attached to it, specifically
49
     * the right currency
50
     *
51
     * @param string|EcommerceCountry   $countryCode
0 ignored issues
show
Documentation introduced by
Should the type for parameter $countryCode not be string|EcommerceCountry|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
52
     * @param bool                      $force
53
     * @param bool                      $runAgain
54
     */
55
    public static function localise_order($countryCode = null, $force = false, $runAgain = false)
56
    {
57
        $countryCode = EcommerceCountry::get_country_from_mixed_var($countryCode, true);
58
59
        if (self::$_number_of_times_we_have_run_localise_order > 0) {
60
            $runAgain = false;
61
        }
62
        if ($runAgain) {
63
            self::$_number_of_times_we_have_run_localise_order = 0;
64
        }
65
        if (self::$_number_of_times_we_have_run_localise_order > 2) {
66
            return;
67
        }
68
69
        self::$_number_of_times_we_have_run_localise_order++;
70
        $order = ShoppingCart::current_order();
71
        if ($order && $order->exists()) {
72
            if ($order->IsSubmitted()) {
73
                return true;
74
            }
75
            if (! $countryCode) {
76
                $countryCode = $order->getCountry();
77
            }
78
            $currencyObject = CountryPrice_EcommerceCurrency::get_currency_for_country($countryCode);
0 ignored issues
show
Documentation introduced by
$countryCode is of type object<EcommerceCountry>|string, but the function expects a object<strin>.

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...
79
            if (Config::inst()->get('CountryPrice_OrderDOD', 'only_allow_within_country_sales')) {
80
                $distributor = $order->getDistributor($countryCode);
81
                $countryOptions = $distributor->Countries();
82
                if ($countryOptions && $countryOptions->count()) {
83
                    EcommerceCountry::set_for_current_order_only_show_countries($countryOptions->column('Code'));
84
                }
85
            }
86
            //check if the billing and shipping address have a country so that they will not be overridden by previous Orders
87
            //we do this to make sure that the previous address can not change the region and thereby destroy the order in the cart
88
            if ($billingAddress = $order->CreateOrReturnExistingAddress('BillingAddress')) {
89
                if (! $billingAddress->Country || $force) {
90
                    $billingAddress->Country = $countryCode;
91
                    $billingAddress->write();
92
                }
93
            }
94
            if ($shippingAddress = $order->CreateOrReturnExistingAddress('ShippingAddress')) {
95
                if (! $shippingAddress->ShippingCountry || $force) {
96
                    $shippingAddress->ShippingCountry = $countryCode;
97
                    $shippingAddress->write();
98
                }
99
            }
100
101
            //if a country code and currency has been set then all is good
102
            //from there we keep it this way
103
            if (
104
                $order->OriginatingCountryCode ==  $countryCode &&
105
                $order->CurrencyUsedID == $currencyObject->ID
106
            ) {
107
                return true;
108
            }
109
            $order->resetLocale = true;
110
            $order->write();
111
            $order = Order::get()->byID($order->ID);
112
            $orderHasBeenChanged = false;
113
114
            //check currency ...
115
            if ($order->CurrencyUsedID != $currencyObject->ID) {
116
                $order->SetCurrency($currencyObject);
117
                $orderHasBeenChanged = true;
118
            }
119
            if ($orderHasBeenChanged) {
120
                ShoppingCart::reset_order_reference();
121
                $order->write();
122
                $items = $order->OrderItems();
123
                if ($items) {
124
                    foreach ($items as $item) {
125
                        $buyable = $item->Buyable(true);
126
                        if (! $buyable->canPurchase()) {
127
                            $item->delete();
128
                        }
129
                    }
130
                }
131
                // Called after because some modifiers use the country field to calculate the values
132
                $order->calculateOrderAttributes(true);
133
            }
134
            self::localise_order($countryCode);
135
        } else {
136
            Session::set('temporary_country_order_store', $countryCode);
0 ignored issues
show
Bug introduced by
It seems like $countryCode defined by \EcommerceCountry::get_c...var($countryCode, true) on line 57 can also be of type null or object<EcommerceCountry>; however, Session::set() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
137
        }
138
    }
139
140
141
    public function onInit()
142
    {
143
        $this->setCountryDetailsForOrder();
144
    }
145
146
    public function onCalculateOrder()
147
    {
148
        $this->setCountryDetailsForOrder();
149
    }
150
151
    public function updateCMSFields(FieldList $fields)
152
    {
153
        foreach (array('IP', 'OriginatingCountryCode', 'CurrencyCountry') as $fieldName) {
154
            $field = $fields->dataFieldByName($fieldName);
155
            $field = $field->performReadonlyTransformation();
156
            $fields->addFieldToTab("Root.Country", $field);
157
            $fields->addFieldToTab(
158
                'Root.Country',
159
                DropdownField::create(
160
                    'DistributorID',
161
                     _t('Distributor.SINGULAR_NAME', 'Distributor'),
162
                    array(''=> '--- Please select ---') + Distributor::get()->map()->toArray()
163
                )
164
            );
165
        }
166
    }
167
168
    /**
169
     * Event handler called after writing to the database.
170
     */
171
    public function onAfterWrite()
172
    {
173
        if (! $this->owner->DistributorID) {
174
            if ($defaultDistributor = Distributor::get_default_distributor()) {
175
                if ($defaultDistributor->exists()) {
176
                    if ($defaultDistributor->ID) {
177
                        $this->owner->DistributorID = $defaultDistributor->ID;
0 ignored issues
show
Bug introduced by
The property DistributorID does not seem to exist in SS_Object.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
178
                        $this->owner->write();
179
                    }
180
                }
181
            }
182
        }
183
    }
184
185
    public function canView($member = null)
186
    {
187
        return $this->canEdit($member);
188
    }
189
190
    public function canEdit($member = null)
191
    {
192
        if (! $member) {
193
            $member = Member::currentUser();
194
        }
195 View Code Duplication
        if ($member) {
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...
196
            if ($distributor = $this->owner->Distributor()) {
197
                foreach ($distributor->Members() as $distributorMember) {
198
                    if ($member->ID == $distributorMember->ID) {
199
                        return true;
200
                    }
201
                }
202
            }
203
        }
204
    }
205
206
    /**
207
     * it is safer to only allow creation on the front-end...
208
     *
209
     */
210
    public function canCreate($member = null)
211
    {
212
        return false;
213
    }
214
215
    /**
216
     *
217
     * @param string (optional) $countryCode
218
     * @return Distributor | null
0 ignored issues
show
Documentation introduced by
Should the return type not be Distributor|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
219
     */
220
    public function getDistributor($countryCode = null)
221
    {
222
        if ($this->owner->DistributorID) {
223
            return Distributor::get()->byID($this->owner->DistributorID);
0 ignored issues
show
Bug introduced by
The property DistributorID does not seem to exist in SS_Object.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
224
        } else {
225
            if (!$countryCode) {
226
                $countryCode = $this->owner->getCountry();
227
            }
228
            return Distributor::get_one_for_country($countryCode);
229
        }
230
    }
231
232
    /**
233
     * this needs to run as part of the order live update
234
     *
235
     */
236
    protected function setCountryDetailsForOrder()
237
    {
238
        if ($this->owner->IsSubmitted()) {
239
            return;
240
        }
241
242
        //set IP
243
        $this->owner->IP = EcommerceCountry::get_ip();
0 ignored issues
show
Bug introduced by
The property IP does not seem to exist in SS_Object.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
244
245
        //here we need to get the REAL ORIGINAL COUNTRY
246
        $countryCode = EcommerceCountry::get_country();
247
        if (Config::inst()->get('CountryPrice_OrderDOD', 'only_allow_within_country_sales')) {
248
            $this->owner->CurrencyCountry = $countryCode;
0 ignored issues
show
Bug introduced by
The property CurrencyCountry does not seem to exist in SS_Object.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
249
            EcommerceCountry::set_for_current_order_only_show_countries(array($countryCode));
250
            $this->owner->SetCountryFields($countryCode, $billingAddress = true, $shippingAddress = true);
251
        }
252
        $this->owner->OriginatingCountryCode = $countryCode;
0 ignored issues
show
Bug introduced by
The property OriginatingCountryCode does not seem to exist in SS_Object.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
253
254
        // set currency
255
        $currencyObject = CountryPrice_EcommerceCurrency::get_currency_for_country($countryCode);
0 ignored issues
show
Documentation introduced by
$countryCode is of type string, but the function expects a object<strin>.

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...
256
        if ($currencyObject) {
257
            $this->owner->CurrencyUsedID = $currencyObject->ID;
0 ignored issues
show
Bug introduced by
The property CurrencyUsedID does not seem to exist in SS_Object.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
258
        }
259
        //the line below causes a loop!!!
260
        //$this->owner->SetCurrency($currencyObject);
261
262
        $distributor = Distributor::get_one_for_country($countryCode);
263
        if ($distributor) {
264
            $this->owner->DistributorID = $distributor->ID;
0 ignored issues
show
Bug introduced by
The property DistributorID does not seem to exist in SS_Object.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
265
        }
266
    }
267
268
269
    /**
270
     *
271
     * 1. adds distribut emails to order step emails ... if $step->SendEmailToDistributor === true
272
     *
273
     * 2. adds country specific data into arrayData that is used for search
274
     * and replace in email ...
275
     *
276
     * @param ArrayData $arrayData
277
     */
278
    public function updateReplacementArrayForEmail(ArrayData $arrayData)
279
    {
280
        $step = $this->owner->MyStep();
281
        $countryCode = $this->owner->getCountry();
282
        $countryMessageObject = null;
283
        if ($step && $countryCode) {
284
            $countryMessageObject = EcommerceOrderStepCountryData::get()
285
                ->filter(
286
                    array(
287
                        "OrderStepID" => $step->ID,
288
                        "EcommerceCountryID" => CountryPrice_EcommerceCountry::get_real_country($countryCode)->ID
289
                    )
290
                )
291
                ->first();
292
        }
293
        if ($countryMessageObject) {
294
            $arrayData->setField(
295
                "Subject",
296
                $countryMessageObject->CountrySpecificEmailSubject
297
            );
298
            $arrayData->setField(
299
                "OrderStepMessage",
300
                $countryMessageObject->CountrySpecificEmailMessage
301
            );
302
        }
303
        if ($step->SendEmailToDistributor) {
304
            if ($distributor = $this->owner->Distributor()) {
305
                $distributorEmail = $distributor->Email;
306
                if ($distributorEmail) {
307
                    $bccArray = array($distributorEmail => $distributorEmail);
308
                    foreach ($distributor->Members() as $member) {
309
                        if ($member && $member->Email) {
310
                            $bccArray[$member->Email] = $member->Email;
311
                        }
312
                    }
313
                    $arrayData->setField('BCC', implode(', ', $bccArray));
314
                }
315
            }
316
        }
317
    }
318
}
319