Completed
Push — master ( 613b2e...a2195f )
by Nicolaas
03:28
created

EcommerceTaskMigration::run()   F

Complexity

Conditions 22
Paths > 20000

Size

Total Lines 102
Code Lines 56

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 22
dl 0
loc 102
rs 2
c 0
b 0
f 0
eloc 56
nc 28800
nop 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
4
/**
5
 * @description: migrates older versions of e-commerce to the latest one.
6
 * This has been placed here rather than in the individual classes for the following reasons:
7
 * - not to clog up individual classes
8
 * - to get a complete overview in one class
9
 * - to be able to run parts and older and newer version without having to go through several clases to retrieve them
10
 *
11
 * @authors: Nicolaas [at] Sunny Side Up .co.nz
12
 * @package: ecommerce
13
 * @sub-package: tasks
14
 * @inspiration: Silverstripe Ltd, Jeremy
15
 * @todo: change methods to simple names f10, f20, etc... and then allow individual ones to be run.
16
 * @todo: 200 + 210 need attention.
17
 **/
18
class EcommerceTaskMigration extends BuildTask
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
19
{
20
    protected $limit = 300;
21
22
    protected $start = 0;
23
24
    protected $retrieveInfoOnly = false;
25
26
    protected $title = 'Ecommerce Migration';
27
28
    protected $description = '
29
		Migrates all the data from the oldest version of e-commerce to the current one.
30
		Any obsolete fields will be renamed like this: _obsolete_MyField, but not deleted.
31
	';
32
33
    protected $listOfMigrationTasks = array(
34
        'shopMemberToMemberTableMigration_10',
35
        'moveItemToBuyable_20',
36
        'productVersionToOrderItem_25',
37
        'productIDToBuyableID_26',
38
        'amountToCalculatedTotal_27',
39
        'currencyToMoneyFields_30',
40
        'orderShippingCost_40',
41
        'orderTax_45',
42
        'orderShippingAddress_50',
43
        'orderBillingAddress_51',
44
        'memberBillingAddress_52',
45
        'moveOrderStatus_60',
46
        'fixBadOrderStatus_68',
47
        'updateProductGroups_110',
48
        'setFixedPriceForSubmittedOrderItems_120',
49
        'moveSiteConfigToEcommerceDBConfig_140',
50
        'addClassNameToOrderItems_150',
51
        'addTermsAndConditionsMessage_160',
52
        'mergeUncompletedOrderForOneMember_170',
53
        'updateFullSiteTreeSortFieldForAllProducts_180',
54
        'updateOrderStatusLogSequentialOrderNumber_190',
55
        'resaveAllPRoducts_200',
56
        'resaveAllPRoductsVariations_210',
57
        'addConfirmationPage_250',
58
        'cleanupImages_260',
59
        'addNewPopUpManager_280',
60
        'addCurrencyCodeIDToOrders_290',
61
        'MovePaymentToEcommercePayment_300',
62
        'ecommercetaskupgradepickupordeliverymodifier_310',
63
        'ecommercetaskupgradepickupordeliverymodifier_320',
64
        'removemobilephones_330',
65
        'theEnd_9999',
66
    );
67
68
    public function run($request)
0 ignored issues
show
Coding Style introduced by
run uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
69
    {
70
        set_time_limit(1200);
71
        increase_memory_limit_to(-1);
72
        $nextGetStatement = '';
73
        //can we do the next step?
74
        //IN general yes, but if the current one is not finished yet, then we do it again.
75
        $canDoNext = true;
76
        if (isset($_REQUEST['limit'])) {
77
            $this->limit = intval($_REQUEST['limit']);
78
        }
79
        if (isset($_REQUEST['start'])) {
80
            $this->start = intval($_REQUEST['start']);
81
        }
82
83
        //what is the current step?
84
        $currentMethod = isset($_REQUEST['action']) ? $_REQUEST['action'] : '';
85
        if (in_array($currentMethod, $this->listOfMigrationTasks)) {
86
87
            //are we doing the same one for a different limti?
88
            if ($this->start) {
89
                $this->DBAlterationMessageNow('this task is broken down into baby steps - we are now starting at .... '.$this->start.' processing '.$this->limit, 'created');
90
            }
91
            $nextLimit = $this->$currentMethod();
92
            if ($canDoNext && $nextLimit) {
93
                $canDoNext = false;
94
                //NEXT OPTION 1: do again with new limit
95
                $nextGetStatement = '?action='.$currentMethod.'&start='.$nextLimit;
96
                $nextDescription = 'run next batch ...';
97
            }
98
        }
99
100
        if ($canDoNext && !$nextGetStatement) {
101
            //NEXT OPTION 2: start from the beginning
102
            $nextGetStatement = '?fullmigration=1&action='.$this->listOfMigrationTasks[0];
103
            $nextDescription = "Start Migration by clicking on <i>'Next'</i> (this link) until all tasks have been completed.";
104
        }
105
        //retrieve data...
106
        $this->retrieveInfoOnly = true;
107
        $html = '';
108
        $createListOfActions = false;
109
        if (!$currentMethod) {
110
            $createListOfActions = true;
111
        }
112
        if ($createListOfActions) {
113
            $html .= '
114
			<p>Always make a backup of your database before running any migration tasks.</p>
115
			<ul>';
116
        }
117
        foreach ($this->listOfMigrationTasks as $key => $task) {
118
            $explanation = $this->$task();
119
            $explanation = str_replace(array('<h1>', '</h1>', '<p>', '</p>'), array('<strong>', '</strong>: ', '<span style="color: grey;">', '</span>'), $explanation);
120
            if ($task == $currentMethod) {
121
                if ($canDoNext) {
122
                    $keyPlusOne = $key + 1;
123
                    if (isset($this->listOfMigrationTasks[$keyPlusOne]) && isset($_REQUEST['fullmigration'])) {
124
                        //NEXT OPTION 3: next action!
125
                        $action = $this->listOfMigrationTasks[$keyPlusOne];
126
                        $nextGetStatement = '?action='.$action;
127
                        $nextDescription = $this->$action();
128
                        $nextDescription = str_replace(array('<h1>', '</h1>', '<p>', '</p>'), array('<strong>', '</strong>: ', '<span style="color: grey;">', '</span>'), $nextDescription);
129
                    } else {
130
                        //NEXT OPTION 4: we have done all of them - no more next option...
131
                        $nextGetStatement = '';
132
                        $nextDescription = '';
133
                    }
134
                }
135
            }
136
            if ($createListOfActions) {
137
                $html .=  '<li><a href="/dev/ecommerce/ecommercetaskmigration/?action='.$task."\">$explanation </a></li>";
138
            }
139
        }
140
        if ($createListOfActions) {
141
            $html .= '</ul>';
142
        }
143
        if ($nextGetStatement) {
144
            $nextLink = '/dev/ecommerce/ecommercetaskmigration/'.$nextGetStatement;
145
            if (isset($_REQUEST['fullmigration'])) {
146
                $nextLink .= '&fullmigration=1';
147
            }
148
            echo "
149
				<hr style=\"margin-top: 50px;\"/>
150
				<h3><a href=\"$nextLink\">NEXT: $nextDescription</a></h3>";
0 ignored issues
show
Bug introduced by
The variable $nextDescription does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
151
            if ($currentMethod) {
152
                echo "
153
				<div style=\"width: 400px; height: 20px; padding-top: 20px; font-size: 11px; background: url(/ecommerce/images/loading.gif) no-repeat top left transparent\">
154
					Next step, if any - will load automatically in ten seconds.
155
				</div>
156
				<script type=\"text/javascript\">
157
					var t = window.setTimeout(
158
						function(){
159
							window.location = '$nextLink';
160
						},
161
						500
162
					);
163
				</script>
164
				<hr style=\"margin-bottom: 500px;\"/>
165
			";
166
            }
167
        }
168
        echo $html;
169
    }
170
171
    /**
172
     * Returns true if the table and field (within this table) exist.
173
     * Otherwise it returns false.
174
     *
175
     * @param string - $field - name of the field to be tested
176
     * @param string - $table - name of the table to be tested
177
     *
178
     * @return bool
179
     */
180
    protected function hasTableAndField($table, $field)
181
    {
182
        $db = DB::getConn();
0 ignored issues
show
Deprecated Code introduced by
The method DB::getConn() has been deprecated with message: since version 4.0 Use DB::get_conn instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
183
        if ($db->hasTable($table)) {
0 ignored issues
show
Deprecated Code introduced by
The method SS_Database::hasTable() has been deprecated with message: since version 4.0 Use DB::get_schema()->hasTable() instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
184
            $fieldArray = $db->fieldList($table);
0 ignored issues
show
Deprecated Code introduced by
The method SS_Database::fieldList() has been deprecated with message: since version 4.0 Use DB::field_list instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
185
            if (isset($fieldArray[$field])) {
186
                return true;
187
            }
188
        }
189
190
        return false;
191
    }
192
193
    /**
194
     * Returns true if the table and field (within this table) exist.
195
     * Otherwise it returns false.
196
     *
197
     * @param string - $field - name of the field to be tested
198
     * @param string - $table - name of the table to be tested
199
     *
200
     * @return bool
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|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...
201
     */
202
    protected function makeFieldObsolete($table, $field, $format = '')
0 ignored issues
show
Unused Code introduced by
The parameter $format 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...
203
    {
204
        if ($this->hasTableAndField($table, $field)) {
205
            $db = DB::getConn();
0 ignored issues
show
Deprecated Code introduced by
The method DB::getConn() has been deprecated with message: since version 4.0 Use DB::get_conn instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
206
            $db->dontRequireField($table, $field);
0 ignored issues
show
Deprecated Code introduced by
The method SS_Database::dontRequireField() has been deprecated with message: since version 4.0 Use DB::dont_require_field() instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
207
            $this->DBAlterationMessageNow("removed $field from $table", 'deleted');
208
        } else {
209
            $this->DBAlterationMessageNow("ERROR: could not find $field in $table so it could not be removed", 'deleted');
210
        }
211
        if ($this->hasTableAndField($table, $field)) {
212
            $this->DBAlterationMessageNow("ERROR: tried to remove $field from $table but it still seems to be there", 'deleted');
213
        }
214
    }
215
216
    protected function shopMemberToMemberTableMigration_10()
217
    {
218
        $explanation = '
219
			<h1>10. ShopMember to Member</h1>
220
			<p>In the first version of e-commerce we had the ShopMember class, then we moved this data to Member.</p>
221
		';
222
        if ($this->retrieveInfoOnly) {
223
            return $explanation;
224
        } else {
225
            echo $explanation;
226
        }
227
        if ($this->hasTableAndField('ShopMember', 'ID')) {
228
            $exist = DB::query("SHOW TABLES LIKE 'ShopMember'")->numRecords();
229
            if ($exist > 0) {
230
                DB::query("
231
					UPDATE \"Member\", \"ShopMember\"
232
					SET
233
						\"Member\".\"ClassName\" = 'Member',
234
						\"Member\".\"Address\" = \"ShopMember\".\"Address\",
235
						\"Member\".\"AddressLine2\" = \"ShopMember\".\"AddressLine2\",
236
						\"Member\".\"City\" = \"ShopMember\".\"City\",
237
						\"Member\".\"State\" = \"ShopMember\".\"State\",
238
						\"Member\".\"Country\" = \"ShopMember\".\"Country\",
239
						\"Member\".\"Notes\" = \"ShopMember\".\"Notes\"
240
					WHERE \"Member\".\"ID\" = \"ShopMember\".\"ID\"
241
				");
242
                $this->DBAlterationMessageNow('Successfully migrated ShopMember To Member.', 'created');
243
            } else {
244
                $this->DBAlterationMessageNow('No need to migrate ShopMember To Member because it does not have any records.');
245
            }
246
            DB::query('DROP TABLE "ShopMember";');
247
        } else {
248
            $this->DBAlterationMessageNow('There is no need to migrate the ShopMember table.');
249
        }
250
    }
251
252
    protected function moveItemToBuyable_20()
253
    {
254
        $explanation = '
255
			<h1>20. Move ItemID to Buyable</h1>
256
			<p>Move the Product ID in OrderItem as ItemID to a new field called BuyableID.</p>
257
		';
258
        if ($this->retrieveInfoOnly) {
259
            return $explanation;
260
        } else {
261
            echo $explanation;
262
        }
263
        if ($this->hasTableAndField('OrderItem', 'ItemID')) {
264
            DB::query('
265
				UPDATE "OrderItem"
266
				SET "OrderItem"."BuyableID" = "OrderItem"."ItemID"
267
				WHERE "BuyableID" = 0 OR "BuyableID" IS NULL
268
			');
269
            $this->makeFieldObsolete('OrderItem', 'ItemID');
270
            $this->DBAlterationMessageNow('Moved ItemID to BuyableID in OrderItem', 'created');
271
        } else {
272
            $this->DBAlterationMessageNow('There is no need to move from ItemID to BuyableID');
273
        }
274
    }
275
276
    protected function productVersionToOrderItem_25()
277
    {
278
        $explanation = '
279
			<h1>25. ProductVersion to Version</h1>
280
			<p>Move the product version in the Product_OrderItem table to the OrderItem table.</p>
281
		';
282
        if ($this->retrieveInfoOnly) {
283
            return $explanation;
284
        } else {
285
            echo $explanation;
286
        }
287
        $table = 'LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL';
288
        if ($this->hasTableAndField('Product_OrderItem', 'ProductVersion')) {
289
            $table = 'Product_OrderItem';
290
        } elseif ($this->hasTableAndField('_obsolete_Product_OrderItem', 'ProductVersion')) {
291
            $table = '_obsolete_Product_OrderItem';
292
        }
293
        if ($this->hasTableAndField($table, 'ProductVersion')) {
294
            DB::query("
295
				UPDATE \"OrderItem\", \"$table\"
296
					SET \"OrderItem\".\"Version\" = \"$table\".\"ProductVersion\"
297
				WHERE \"OrderItem\".\"ID\" = \"$table\".\"ID\"
298
			");
299
            $this->makeFieldObsolete('Product_OrderItem', 'ProductVersion');
300
            $this->DBAlterationMessageNow('Migrating Product_OrderItem.ProductVersion to OrderItem.Version.', 'created');
301
        } else {
302
            $this->DBAlterationMessageNow('There is no need to migrate Product_OrderItem.ProductVersion to OrderItem.Version.');
303
        }
304
    }
305
306
    protected function productIDToBuyableID_26()
307
    {
308
        $explanation = '
309
			<h1>26. ProductID to to BuyableID</h1>
310
			<p>Move the product ID saved as Product_OrderItem.ProductID to OrderItem.BuyableID.</p>
311
		';
312
        if ($this->retrieveInfoOnly) {
313
            return $explanation;
314
        } else {
315
            echo $explanation;
316
        }
317
        $table = 'LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL';
318
        if ($this->hasTableAndField('Product_OrderItem', 'ProductID')) {
319
            $table = 'Product_OrderItem';
320
        } elseif ($this->hasTableAndField('_obsolete_Product_OrderItem', 'ProductID')) {
321
            $table = '_obsolete_Product_OrderItem';
322
        }
323
        if ($this->hasTableAndField($table, 'ProductID')) {
324
            DB::query("
325
				UPDATE \"OrderItem\"
326
					INNER JOIN \"$table\"
327
						ON \"OrderItem\".\"ID\" = \"$table\".\"ID\"
328
				SET \"OrderItem\".\"BuyableID\" = \"$table\".\"ProductID\"
329
				WHERE \"BuyableID\" = 0 OR \"BuyableID\" IS NULL
330
			");
331
            $this->makeFieldObsolete('Product_OrderItem', 'ProductID');
332
            $this->DBAlterationMessageNow('Migrating Product_OrderItem.ProductID to OrderItem.BuyableID', 'created');
333
        } else {
334
            $this->DBAlterationMessageNow('There is no need to migrate Product_OrderItem.ProductID to OrderItem.BuyableID');
335
        }
336
        if ($this->hasTableAndField($table, 'ProductVersion')) {
337
            DB::query("
338
				UPDATE \"OrderItem\"
339
					INNER JOIN \"$table\"
340
						ON \"OrderItem\".\"ID\" = \"$table\".\"ID\"
341
				SET \"OrderItem\".\"Version\" = \"$table\".\"ProductVersion\"
342
				WHERE \"BuyableID\" = 0 OR \"BuyableID\" IS NULL
343
			");
344
            $this->makeFieldObsolete('Product_OrderItem', 'ProductID');
345
            $this->DBAlterationMessageNow('Migrating Product_OrderItem.ProductVersion to OrderItem.Version', 'created');
346
        } else {
347
            $this->DBAlterationMessageNow('There is no need to migrate Product_OrderItem.ProductVersion to OrderItem.Version');
348
        }
349
        // we must check for individual database types here because each deals with schema in a none standard way
350
        //can we use Table::has_field ???
0 ignored issues
show
Unused Code Comprehensibility introduced by
39% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
351
        if ($this->hasTableAndField('ProductVariation_OrderItem', 'ProductVariationVersion')) {
352
            DB::query('
353
				UPDATE "OrderItem", "ProductVariation_OrderItem"
354
					SET "OrderItem"."Version" = "ProductVariation_OrderItem"."ProductVariationVersion"
355
				WHERE "OrderItem"."ID" = "ProductVariation_OrderItem"."ID"
356
			');
357
            $this->makeFieldObsolete('ProductVariation_OrderItem', 'ProductVariationVersion');
358
            $this->DBAlterationMessageNow('Migrating ProductVariation_OrderItem.ProductVariationVersion to OrderItem.Version', 'created');
359
        } else {
360
            $this->DBAlterationMessageNow('No need to migrate ProductVariation_OrderItem.ProductVariationVersion');
361
        }
362
        if (class_exists('ProductVariation_OrderItem')) {
363
            if ($this->hasTableAndField('ProductVariation_OrderItem', 'ProductVariationID')) {
364
                DB::query("
365
					UPDATE \"OrderItem\", \"ProductVariation_OrderItem\"
366
						SET \"OrderItem\".\"BuyableID\" = \"ProductVariation_OrderItem\".\"ProductVariationID\",
367
								\"OrderItem\".\"BuyableClassName\" = 'ProductVariation'
368
					WHERE \"OrderItem\".\"ID\" = \"ProductVariation_OrderItem\".\"ID\"
369
				");
370
                $this->makeFieldObsolete('ProductVariation_OrderItem', 'ProductVariationID');
371
                $this->DBAlterationMessageNow('Migrating ProductVariation_OrderItem.ProductVariationID to OrderItem.BuyableID and adding BuyableClassName = ProductVariation', 'created');
372
            } else {
373
                $this->DBAlterationMessageNow('No need to migrate ProductVariation_OrderItem.ProductVariationID');
374
            }
375
        } else {
376
            $this->DBAlterationMessageNow('There are not ProductVariations in this project');
377
        }
378
    }
379
380
    protected function amountToCalculatedTotal_27()
381
    {
382
        $explanation = '
383
			<h1>27. Move OrderModifier.Amount to OrderAttribute.CalculatedTotal</h1>
384
			<p>Move the amount of the modifier in the OrderModifier.Amount field to the OrderAttribute.CalculatedTotal field.</p>
385
		';
386
        if ($this->retrieveInfoOnly) {
387
            return $explanation;
388
        } else {
389
            echo $explanation;
390
        }
391
        if ($this->hasTableAndField('OrderModifier', 'Amount')) {
392
            DB::query('
393
				UPDATE "OrderModifier"
394
					INNER JOIN "OrderAttribute"
395
						ON "OrderAttribute"."ID" = "OrderModifier"."ID"
396
				SET "OrderAttribute"."CalculatedTotal" = "OrderModifier"."Amount"
397
				WHERE "OrderAttribute"."CalculatedTotal" IS NULL OR "OrderAttribute"."CalculatedTotal" = 0
398
			');
399
            $this->makeFieldObsolete('OrderModifier', 'Amount');
400
            $this->DBAlterationMessageNow('Moved OrderModifier.Amount to OrderAttribute.CalculatedTotal', 'created');
401
        } else {
402
            $this->DBAlterationMessageNow('There is no need to move OrderModifier.Amount to OrderAttribute.CalculatedTotal');
403
        }
404
    }
405
406
    protected function currencyToMoneyFields_30()
407
    {
408
        $explanation = '
409
			<h1>30. Currency to Money Fields</h1>
410
			<p>Move the Payment Amount in the Amount field to a composite DB field (AmountAmount + AmountCurrency) </p>
411
		';
412
        if ($this->retrieveInfoOnly) {
413
            return $explanation;
414
        } else {
415
            echo $explanation;
416
        }
417
        if ($this->hasTableAndField('Payment', 'Amount')) {
418
            //ECOMMERCE PAYMENT *************************
419
            DB::query('
420
				UPDATE "Payment"
421
				SET "AmountAmount" = "Amount"
422
				WHERE
423
					"Amount" > 0
424
					AND (
425
						"AmountAmount" IS NULL OR "AmountAmount" = 0
426
					)
427
			');
428
            $countAmountChanges = DB::affectedRows();
0 ignored issues
show
Deprecated Code introduced by
The method DB::affectedRows() has been deprecated with message: since version 4.0 Use DB::affected_rows instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
429
            if ($countAmountChanges) {
430
                $this->DBAlterationMessageNow("Updated Payment.Amount field to 2.4 - $countAmountChanges rows updated", 'edited');
431
            }
432
        } else {
433
            $this->DBAlterationMessageNow('There is no need to move Payment.Amount to Payment.AmountAmount');
434
        }
435
        if ($this->hasTableAndField('Payment', 'Currency')) {
436
            DB::query("
437
				UPDATE \"Payment\"
438
				SET \"AmountCurrency\" = \"Currency\"
439
				WHERE
440
					\"Currency\" <> ''
441
					AND \"Currency\" IS NOT NULL
442
					AND (
443
						\"AmountCurrency\" IS NULL
444
						OR \"AmountCurrency\" = ''
445
					)
446
			");
447
            $countCurrencyChanges = DB::affectedRows();
0 ignored issues
show
Deprecated Code introduced by
The method DB::affectedRows() has been deprecated with message: since version 4.0 Use DB::affected_rows instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
448
            if ($countCurrencyChanges) {
449
                $this->DBAlterationMessageNow("Updated Payment.Currency field to 2.4  - $countCurrencyChanges rows updated", 'edited');
450
            }
451
            if ($countAmountChanges != $countCurrencyChanges) {
0 ignored issues
show
Bug introduced by
The variable $countAmountChanges does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
452
                $this->DBAlterationMessageNow('Potential error in Payment fields update to 2.4, please review data', 'deleted');
453
            }
454
        } else {
455
            $this->DBAlterationMessageNow('There is no need to move Payment.Currency to Payment.AmountCurrency');
456
        }
457
    }
458
459
    protected function orderShippingCost_40()
460
    {
461
        $explanation = '
462
			<h1>40. Order Shipping Cost</h1>
463
			<p>Move the shipping cost in the order to its own modifier.</p>
464
		';
465
        if ($this->retrieveInfoOnly) {
466
            return $explanation;
467
        } else {
468
            echo $explanation;
469
        }
470
        if ($this->hasTableAndField('Order', 'Shipping') && $this->hasTableAndField('Order', 'HasShippingCost')) {
471
            $orders = Order::get()
472
                ->where('"HasShippingCost" = 1 AND "Shipping" IS NOT NULL')
473
                ->limit($this->limit, $this->start);
474
            if ($orders->count()) {
475
                foreach ($orders as $order) {
476
                    $modifier1 = new SimpleShippingModifier();
477
                    $modifier1->CalculatedTotal = $shipping < 0 ? abs($shipping) : $shipping;
0 ignored issues
show
Bug introduced by
The variable $shipping does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
478
                    $modifier1->TableValue = $shipping < 0 ? abs($shipping) : $shipping;
479
                    $modifier1->OrderID = $id;
0 ignored issues
show
Bug introduced by
The variable $id does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
480
                    $modifier1->TableTitle = 'Delivery';
481
                    $modifier1->write();
482
                    $this->DBAlterationMessageNow(' ------------- Added shipping cost.', 'created');
483
                }
484
485
                return $this->start + $this->limit;
486
            } else {
487
                $this->DBAlterationMessageNow('There are no orders with HasShippingCost =1 and Shipping IS NOT NULL.');
488
            }
489
            $this->makeFieldObsolete('Order', 'HasShippingCost', 'tinyint(1)');
490
            $this->makeFieldObsolete('Order', 'Shipping', 'decimal(9,2)');
491
        } else {
492
            $this->DBAlterationMessageNow('No need to update shipping cost.');
493
        }
494
495
        return 0;
496
    }
497
498
    protected function orderTax_45()
499
    {
500
        $explanation = '
501
			<h1>45. Order Added Tax</h1>
502
			<p>Move the tax in the order to its own modifier.</p>
503
		';
504
        if ($this->retrieveInfoOnly) {
505
            return $explanation;
506
        } else {
507
            echo $explanation;
508
        }
509
        if ($this->hasTableAndField('Order', 'AddedTax')) {
510
            $this->DBAlterationMessageNow('Moving Order.AddedTax to Modifier.', 'created');
511
            $orders = Order::get()
512
                ->where('"AddedTax" > 0')
513
                ->limit($this->limit, $this->start);
514
            if ($orders->count()) {
515
                foreach ($orders as $order) {
516
                    $id = $order->ID;
517
                    $hasShippingCost = DB::query("SELECT \"AddedTax\" FROM \"Order\" WHERE \"ID\" = '$id'")->value();
0 ignored issues
show
Unused Code introduced by
$hasShippingCost is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
518
                    $addedTax = DB::query("SELECT \"AddedTax\" FROM \"Order\" WHERE \"ID\" = '$id'")->value();
519
                    if ($addedTax != null && $addedTax > 0) {
520
                        $modifier1 = new FlatTaxModifier();
521
                        $modifier1->CalculatedTotal = $addedTax < 0 ? abs($addedTax) : $addedTax;
522
                        $modifier1->TableValue = $addedTax < 0 ? abs($addedTax) : $addedTax;
523
                        $modifier1->OrderID = $id;
524
                        $modifier1->TableTitle = 'Tax';
525
                        $modifier1->write();
526
                        $this->DBAlterationMessageNow(' ------------- Added tax.', 'created');
527
                    } else {
528
                        $this->DBAlterationMessageNow(' ------------- No need to add tax even though field is present');
529
                    }
530
                }
531
532
                return $this->start + $this->limit;
533
            } else {
534
                $this->DBAlterationMessageNow('There are no orders with a AddedTax field greater than zero.');
535
            }
536
            $this->makeFieldObsolete('Order', 'AddedTax');
537
        } else {
538
            $this->DBAlterationMessageNow('No need to update taxes.');
539
        }
540
541
        return 0;
542
    }
543
544
    protected function orderShippingAddress_50()
545
    {
546
        $explanation = '
547
			<h1>50. Order Shipping Address</h1>
548
			<p>Move a shipping address from within Order to its own class.</p>
549
		';
550
        if ($this->retrieveInfoOnly) {
551
            return $explanation;
552
        } else {
553
            echo $explanation;
554
        }
555
        if ($this->hasTableAndField('Order', 'ShippingAddress')) {
556
            if ($this->hasTableAndField('Order', 'UseShippingAddress')) {
557
                $orders = Order::get()
558
                    ->where('"UseShippingAddress" = 1 AND "ShippingAddress"."ID" IS NULL')
559
                    ->leftJoin('ShippingAddress', '"Order"."ShippingAddressID" = "ShippingAddress"."ID"')
560
                    ->limit($this->limit, $this->start);
561
                if ($orders->count()) {
562
                    foreach ($orders as $order) {
563
                        if (!$order->ShippingAddressID) {
564
                            $obj = ShippingAddress::create();
565
                            if (isset($order->ShippingName)) {
566
                                $obj->ShippingName = $order->ShippingName;
0 ignored issues
show
Documentation introduced by
The property ShippingName does not exist on object<ShippingAddress>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
567
                            }
568
                            if (isset($order->ShippingAddress)) {
569
                                $obj->ShippingAddress = $order->ShippingAddress;
0 ignored issues
show
Documentation introduced by
The property ShippingAddress does not exist on object<ShippingAddress>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
570
                            }
571
                            if (isset($order->ShippingAddress2)) {
572
                                $obj->ShippingAddress2 = $order->ShippingAddress2;
0 ignored issues
show
Documentation introduced by
The property ShippingAddress2 does not exist on object<ShippingAddress>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
573
                            }
574
                            if (isset($order->ShippingCity)) {
575
                                $obj->ShippingCity = $order->ShippingCity;
0 ignored issues
show
Documentation introduced by
The property ShippingCity does not exist on object<ShippingAddress>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
576
                            }
577
                            if (isset($order->ShippingPostalCode)) {
578
                                $obj->ShippingPostalCode = $order->ShippingPostalCode;
0 ignored issues
show
Documentation introduced by
The property ShippingPostalCode does not exist on object<ShippingAddress>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
579
                            }
580
                            if (isset($order->ShippingState)) {
581
                                $obj->ShippingState = $order->ShippingState;
0 ignored issues
show
Documentation introduced by
The property ShippingState does not exist on object<ShippingAddress>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
582
                            }
583
                            if (isset($order->ShippingCountry)) {
584
                                $obj->ShippingCountry = $order->ShippingCountry;
0 ignored issues
show
Documentation introduced by
The property ShippingCountry does not exist on object<ShippingAddress>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
585
                            }
586
                            if (isset($order->ShippingPhone)) {
587
                                $obj->ShippingPhone = $order->ShippingPhone;
0 ignored issues
show
Documentation introduced by
The property ShippingPhone does not exist on object<ShippingAddress>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
588
                            }
589
                            if (isset($order->ShippingHomePhone)) {
590
                                $obj->ShippingPhone .= $order->ShippingHomePhone;
0 ignored issues
show
Documentation introduced by
The property ShippingPhone does not exist on object<ShippingAddress>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
591
                            }
592
                            if (isset($order->ShippingMobilePhone)) {
593
                                $obj->ShippingMobilePhone = $order->ShippingMobilePhone;
0 ignored issues
show
Documentation introduced by
The property ShippingMobilePhone does not exist on object<ShippingAddress>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
594
                            }
595
                            $obj->OrderID = $order->ID;
0 ignored issues
show
Documentation introduced by
The property OrderID does not exist on object<ShippingAddress>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
596
                            $obj->write();
597
                            $order->ShippingAddressID = $obj->ID;
598
                            $order->write();
599
                        } else {
600
                            $this->DBAlterationMessageNow('Strange contradiction occurred in Order with ID'.$order->ID, 'deleted');
601
                        }
602
                    }
603
604
                    return $this->start + $this->limit;
605
                } else {
606
                    $this->DBAlterationMessageNow('No orders need adjusting even though they followed the old pattern.');
607
                }
608
                $this->makeFieldObsolete('Order', 'ShippingName');
609
                $this->makeFieldObsolete('Order', 'ShippingAddress');
610
                $this->makeFieldObsolete('Order', 'ShippingAddress2');
611
                $this->makeFieldObsolete('Order', 'ShippingCity');
612
                $this->makeFieldObsolete('Order', 'ShippingPostalCode');
613
                $this->makeFieldObsolete('Order', 'ShippingState');
614
                $this->makeFieldObsolete('Order', 'ShippingCountry');
615
                $this->makeFieldObsolete('Order', 'ShippingPhone');
616
                $this->makeFieldObsolete('Order', 'ShippingHomePhone');
617
                $this->makeFieldObsolete('Order', 'ShippingMobilePhone');
618
            } else {
619
                $this->DBAlterationMessageNow('There is no UseShippingAddress field even though there is a ShippingAddress Field - this is an issue.', 'deleted');
620
            }
621
        } else {
622
            $this->DBAlterationMessageNow('Orders do not have the shipping address to migrate.');
623
        }
624
625
        return 0;
626
    }
627
628
    protected function orderBillingAddress_51()
629
    {
630
        $explanation = '
631
			<h1>51. Order Billing Address</h1>
632
			<p>Move the billing address from the order to its own class.</p>
633
		';
634
        if ($this->retrieveInfoOnly) {
635
            return $explanation;
636
        } else {
637
            echo $explanation;
638
        }
639
        if ($this->hasTableAndField('Order', 'Address')) {
640
            if ($this->hasTableAndField('Order', 'City')) {
641
                $orders = Order::get()
642
                    ->where('"BillingAddress"."ID" = 0 OR "BillingAddress"."ID" IS NULL')
643
                    ->leftJoin('BillingAddress', '"Order"."BillingAddressID" = "BillingAddress"."ID"')
644
                    ->limit($this->limit, $this->start);
645
                if ($orders->count()) {
646
                    foreach ($orders as $order) {
647
                        if (!$order->BillingAddressID) {
648
                            $obj = BillingAddress::create();
649
                            if (isset($order->Email)) {
650
                                $obj->BillingEmail = $order->Email;
0 ignored issues
show
Documentation introduced by
The property BillingEmail does not exist on object<BillingAddress>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
651
                            }
652
                            if (isset($order->Surname)) {
653
                                $obj->BillingSurname = $order->Surname;
0 ignored issues
show
Documentation introduced by
The property BillingSurname does not exist on object<BillingAddress>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
654
                            }
655
                            if (isset($order->FirstName)) {
656
                                $obj->BillingFirstName = $order->FirstName;
0 ignored issues
show
Documentation introduced by
The property BillingFirstName does not exist on object<BillingAddress>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
657
                            }
658
                            if (isset($order->Address)) {
659
                                $obj->BillingAddress = $order->Address;
0 ignored issues
show
Documentation introduced by
The property BillingAddress does not exist on object<BillingAddress>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
660
                            }
661
                            if (isset($order->AddressLine2)) {
662
                                $obj->BillingAddress2 = $order->AddressLine2;
0 ignored issues
show
Documentation introduced by
The property BillingAddress2 does not exist on object<BillingAddress>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
663
                            }
664
                            if (isset($order->Address2)) {
665
                                $obj->BillingAddress2 .= $order->Address2;
0 ignored issues
show
Documentation introduced by
The property BillingAddress2 does not exist on object<BillingAddress>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
666
                            }
667
                            if (isset($order->City)) {
668
                                $obj->BillingCity = $order->City;
0 ignored issues
show
Documentation introduced by
The property BillingCity does not exist on object<BillingAddress>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
669
                            }
670
                            if (isset($order->PostalCode)) {
671
                                $obj->BillingPostalCode = $order->PostalCode;
0 ignored issues
show
Documentation introduced by
The property BillingPostalCode does not exist on object<BillingAddress>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
672
                            }
673
                            if (isset($order->State)) {
674
                                $obj->BillingState = $order->State;
0 ignored issues
show
Documentation introduced by
The property BillingState does not exist on object<BillingAddress>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
675
                            }
676
                            if (isset($order->Country)) {
677
                                $obj->BillingCountry = $order->Country;
0 ignored issues
show
Documentation introduced by
The property BillingCountry does not exist on object<BillingAddress>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
678
                            }
679
                            if (isset($order->Phone)) {
680
                                $obj->BillingPhone = $order->Phone;
0 ignored issues
show
Documentation introduced by
The property BillingPhone does not exist on object<BillingAddress>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
681
                            }
682
                            if (isset($order->HomePhone)) {
683
                                $obj->BillingPhone .= $order->HomePhone;
0 ignored issues
show
Documentation introduced by
The property BillingPhone does not exist on object<BillingAddress>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
684
                            }
685
                            if (isset($order->MobilePhone)) {
686
                                $obj->BillingMobilePhone = $order->MobilePhone;
0 ignored issues
show
Documentation introduced by
The property BillingMobilePhone does not exist on object<BillingAddress>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
687
                            }
688
                            $obj->OrderID = $order->ID;
0 ignored issues
show
Documentation introduced by
The property OrderID does not exist on object<BillingAddress>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
689
                            $obj->write();
690
                            $order->BillingAddressID = $obj->ID;
691
                            $order->write();
692
                        } else {
693
                            $this->DBAlterationMessageNow('Strange contradiction occurred in Order with ID'.$order->ID, 'deleted');
694
                        }
695
                    }
696
697
                    return $this->start + $this->limit;
698
                } else {
699
                    $this->DBAlterationMessageNow('No orders need adjusting even though they followed the old pattern.');
700
                }
701
                $this->makeFieldObsolete('Order', 'Email');
702
                $this->makeFieldObsolete('Order', 'FirstName');
703
                $this->makeFieldObsolete('Order', 'Surname');
704
                $this->makeFieldObsolete('Order', 'Address');
705
                $this->makeFieldObsolete('Order', 'Address2');
706
                $this->makeFieldObsolete('Order', 'City');
707
                $this->makeFieldObsolete('Order', 'PostalCode');
708
                $this->makeFieldObsolete('Order', 'State');
709
                $this->makeFieldObsolete('Order', 'Country');
710
                $this->makeFieldObsolete('Order', 'Phone');
711
                $this->makeFieldObsolete('Order', 'HomePhone');
712
                $this->makeFieldObsolete('Order', 'MobilePhone');
713
            } else {
714
                $this->DBAlterationMessageNow('There is no UseBillingAddress field even though there is a BillingAddress Field - this is an issue.', 'deleted');
715
            }
716
        } else {
717
            $this->DBAlterationMessageNow('Orders do not have the Billing address to migrate.');
718
        }
719
720
        return 0;
721
    }
722
723
    protected function memberBillingAddress_52()
724
    {
725
        $explanation = '
726
			<h1>52. Member Billing Address</h1>
727
			<p>Move address details in the member table to its own class (billingaddress)</p>
728
		';
729
        if ($this->retrieveInfoOnly) {
730
            return $explanation;
731
        } else {
732
            echo $explanation;
733
        }
734
        if ($this->hasTableAndField('Member', 'Address')) {
735
            if ($this->hasTableAndField('Member', 'City')) {
736
                $orders = Order::get()
737
                    ->where('"MemberID" > 0')
738
                    ->leftJoin('BillingAddress', '"Order"."BillingAddressID" = "BillingAddress"."ID"')
739
                    ->limit($this->limit, $this->start);
740
                if ($orders->count()) {
741
                    foreach ($orders as $order) {
742
                        $member = Member::get()->byID($order->MemberID);
743
                        if ($member) {
744
                            if ($obj = $order->BillingAddress()) {
745
                                $this->DBAlterationMessageNow('Order (id = '.$order->ID.') already has a billing address');
746
                                //do nothing
747
                            } else {
748
                                $this->DBAlterationMessageNow('Order (id = '.$order->ID.') now gets its own billing address...');
749
                                $obj = BillingAddress::create();
750
                            }
751
                            if (isset($member->Email) && !$obj->Email) {
752
                                $obj->Email = $member->Email;
753
                            }
754
                            if (isset($member->FirstName) && !$obj->FirstName) {
755
                                $obj->FirstName = $member->FirstName;
756
                            }
757
                            if (isset($member->Surname) && !$obj->Surname) {
758
                                $obj->Surname = $member->Surname;
759
                            }
760
                            if (isset($member->Address) && !$obj->Address) {
761
                                $obj->Address = $member->Address;
762
                            }
763
                            if (isset($member->AddressLine2) && !$obj->Address2) {
764
                                $obj->Address2 = $member->AddressLine2;
765
                            }
766
                            if (isset($member->City) && !$obj->City) {
767
                                $obj->City = $member->City;
768
                            }
769
                            if (isset($member->PostalCode) && !$obj->PostalCode) {
770
                                $obj->PostalCode = $member->PostalCode;
771
                            }
772
                            if (isset($member->State) && !$obj->State) {
773
                                $obj->State = $member->State;
774
                            }
775
                            if (isset($member->Country) && !$obj->Country) {
776
                                $obj->Country = $member->Country;
777
                            }
778
                            if (isset($member->Phone) && !$obj->Phone) {
779
                                $obj->Phone = $member->Phone;
780
                            }
781
                            if (isset($member->HomePhone) && !$obj->HomePhone) {
782
                                $obj->HomePhone .= $member->HomePhone;
783
                            }
784
                            if (isset($member->MobilePhone) && !$obj->MobilePhone) {
785
                                $obj->MobilePhone = $member->MobilePhone;
786
                            }
787
                            $obj->OrderID = $order->ID;
788
                            $obj->write();
789
                            $this->DBAlterationMessageNow('Updated Order #'.$order->ID.' with Member details', 'created');
790
                            DB::query('Update "Order" SET "BillingAddressID" = '.$obj->ID.' WHERE "Order".ID = '.$order->ID);
791
                        } else {
792
                            $this->DBAlterationMessageNow('There is no member associated with this order '.$order->ID, 'deleted');
793
                        }
794
                    }
795
796
                    return $this->start + $this->limit;
797
                } else {
798
                    $this->DBAlterationMessageNow('No orders need adjusting even though they followed the old pattern.');
799
                }
800
            } else {
801
                $this->DBAlterationMessageNow('There is no Address2 field, but there is an Address field in Member - this might be an issue.', 'deleted');
802
            }
803
        } else {
804
            $this->DBAlterationMessageNow('Members do not have a billing address to migrate.');
805
        }
806
807
        return 0;
808
    }
809
810
    protected function moveOrderStatus_60()
811
    {
812
        $explanation = '
813
			<h1>60. Move Order Status</h1>
814
			<p>Moving order status from the enum field to Order Step.</p>
815
		';
816
        if ($this->retrieveInfoOnly) {
817
            return $explanation;
818
        } else {
819
            echo $explanation;
820
        }
821
        if ($this->hasTableAndField('Order', 'Status')) {
822
            // 2) Cancel status update
823
            $orders = Order::get()
824
                ->filter(array('Status' => 'Cancelled'))
825
                ->limit($this->limit, $this->start);
826
            if ($orders->count()) {
827
                foreach ($orders as $order) {
828
                    $order->CancelledByID = $admin->ID;
0 ignored issues
show
Bug introduced by
The variable $admin does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
829
                    $order->write();
830
                    $this->DBAlterationMessageNow('The order which status was \'Cancelled\' have been successfully changed to the status \'AdminCancelled\'', 'created');
831
                }
832
833
                return $this->start + $this->limit;
834
            } else {
835
                $this->DBAlterationMessageNow('There are no orders that are cancelled');
836
            }
837
            $rows = DB::query('SELECT "ID", "Status" FROM "Order"');
838
            if ($rows) {
839
                $cartObject = null;
840
                $unpaidObject = null;
841
                $paidObject = null;
842
                $sentObject = null;
0 ignored issues
show
Unused Code introduced by
$sentObject is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
843
                $adminCancelledObject = null;
844
                $memberCancelledObject = null;
845
                foreach ($rows as $row) {
846
                    switch ($row['Status']) {
847
                        case 'Cart':
848
                            if (!$cartObject) {
849
                                $cartObject = OrderStep::get()
850
                                    ->Filter(array('Code' => 'CREATED'))
851
                                    ->First();
852
                                if ($cartObject) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
853
                                    //do nothing
854
                                } else {
855
                                    $this->DBAlterationMessageNow('Creating default steps', 'created');
856
                                    singleton('OrderStep')->requireDefaultRecords();
857
                                }
858
                            }
859
                            $cartObject = OrderStep::get()
860
                                ->Filter(array('Code' => 'CREATED'))
861
                                ->First();
862
                            if ($cartObject) {
863
                                DB::query('UPDATE "Order" SET "StatusID" = '.$cartObject->ID.' WHERE "Order"."ID" = '.$row['ID'].' AND ("StatusID" = 0 OR "StatusID" IS NULL)');
864
                            } else {
865
                                $this->DBAlterationMessageNow('Could not find CREATED status', 'deleted');
866
                            }
867
                            break;
868
                        case 'Query':
869
                        case 'Unpaid':
870
                            if (!$unpaidObject) {
871
                                $unpaidObject = OrderStep::get()
872
                                    ->Filter(array('Code' => 'SUBMITTED'))
873
                                    ->First();
874
                                if ($unpaidObject) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
875
                                    //do nothing
876
                                } else {
877
                                    $this->DBAlterationMessageNow('Creating default steps', 'created');
878
                                    singleton('OrderStep')->requireDefaultRecords();
879
                                }
880
                            }
881
                            $unpaidObject = OrderStep::get()
882
                                ->Filter(array('Code' => 'SUBMITTED'))
883
                                ->First();
884
                            if ($unpaidObject) {
885
                                DB::query('UPDATE "Order" SET "StatusID" = '.$unpaidObject->ID.' WHERE "Order"."ID" = '.$row['ID'].' AND ("StatusID" = 0 OR "StatusID" IS NULL)');
886
                            } else {
887
                                $this->DBAlterationMessageNow('Could not find SUBMITTED status', 'deleted');
888
                            }
889
                            break;
890
                        case 'Processing':
891
                        case 'Paid':
892
                            if (!$paidObject) {
893
                                $paidObject = OrderStep::get()
894
                                    ->Filter(array('Code' => 'PAID'))
895
                                    ->First();
896
                                if ($paidObject) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
897
                                    //do nothing
898
                                } else {
899
                                    $this->DBAlterationMessageNow('Creating default steps', 'created');
900
                                    singleton('OrderStep')->requireDefaultRecords();
901
                                }
902
                            }
903
                            $paidObject = OrderStep::get()
904
                                ->Filter(array('Code' => 'PAID'))
905
                                ->First();
906
                            if ($paidObject) {
907
                                DB::query('UPDATE "Order" SET "StatusID" = '.$paidObject->ID.' WHERE "Order"."ID" = '.$row['ID'].' AND ("StatusID" = 0 OR "StatusID" IS NULL)');
908
                                $this->DBAlterationMessageNow('Updating to PAID status', 'created');
909
                            } else {
910
                                $this->DBAlterationMessageNow('Could not find new status', 'deleted');
911
                            }
912
                            break;
913
                        case 'Sent':
914
                        case 'Complete':
915
                            //CHECK PAID VS SENT!
916
                            if (!$paidObject) {
917
                                $sentObject = OrderStep::get()
918
                                    ->Filter(array('Code' => 'SENT'))
919
                                    ->First();
920
                                if ($sentObject) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
921
                                    //do nothing
922
                                } else {
923
                                    $this->DBAlterationMessageNow('Creating default steps', 'created');
924
                                    singleton('OrderStep')->requireDefaultRecords();
925
                                }
926
                            }
927
                            $sentObject = OrderStep::get()
928
                                ->Filter(array('Code' => 'SENT'))
929
                                ->First();
930
                            if ($sentObject) {
931
                                $this->DBAlterationMessageNow('Updating to SENT status', 'created');
932
                                DB::query('UPDATE "Order" SET "StatusID" = '.$sentObject->ID.' WHERE "Order"."ID" = '.$row['ID'].' AND ("StatusID" = 0 OR "StatusID" IS NULL)');
933
                            } elseif ($archivedObject = OrderStep::get()->Filter(array('Code' => 'ARCHIVED'))->First()) {
934
                                $this->DBAlterationMessageNow('Updating to ARCHIVED status', 'created');
935
                                DB::query('UPDATE "Order" SET "StatusID" = '.$archivedObject->ID.' WHERE "Order"."ID" = '.$row['ID'].' AND ("StatusID" = 0 OR "StatusID" IS NULL)');
936
                            } else {
937
                                $this->DBAlterationMessageNow('Could not find new status', 'deleted');
938
                            }
939
                            break;
940
                        case 'AdminCancelled':
941
                            if (!$adminCancelledObject) {
942
                                $adminCancelledObject = OrderStep::get()
943
                                    ->Filter(array('Code' => 'SENT'))
944
                                    ->First();
945
                                if ($adminCancelledObject) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
946
                                    //do nothing
947
                                } else {
948
                                    singleton('OrderStep')->requireDefaultRecords();
949
                                }
950
                            }
951
                            $adminID = Member::currentUserID();
952
                            if (!$adminID) {
953
                                $adminID = 1;
954
                            }
955
                            $this->DBAlterationMessageNow('Updating to Admin Cancelled', 'created');
956
                            DB::query('UPDATE "Order" SET "StatusID" = '.$adminCancelledObject->ID.', "CancelledByID" = '.$adminID.' WHERE "Order"."ID" = '.$row['ID'].' AND ("StatusID" = 0 OR "StatusID" IS NULL)');
957
                            break;
958
                        case 'MemberCancelled':
959
                            if (!$memberCancelledObject) {
960
                                $memberCancelledObject = OrderStep::get()
961
                                    ->Filter(array('Code' => 'SENT'))
962
                                    ->First();
963
                                if ($memberCancelledObject) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
964
                                    //do nothing
965
                                } else {
966
                                    singleton('OrderStep')->requireDefaultRecords();
967
                                }
968
                            }
969
                            $this->DBAlterationMessageNow('Updating to MemberCancelled', 'created');
970
                            DB::query('UPDATE "Order" SET "StatusID" = '.$memberCancelledObject->ID.', "CancelledByID" = "MemberID" WHERE "Order"."ID" = '.$row['ID'].' AND ("StatusID" = 0 OR "StatusID" IS NULL)');
971
                            break;
972
                        default:
973
                            $this->DBAlterationMessageNow('Unexpected status', 'deleted');
974
                    }
975
                }
976
            } else {
977
                $this->DBAlterationMessageNow('No orders could be found.');
978
            }
979
            $this->makeFieldObsolete('Order', 'Status');
980
        } else {
981
            $this->DBAlterationMessageNow('There is no Status field in the Order Table.');
982
        }
983
984
        return 0;
985
    }
986
987
    protected function fixBadOrderStatus_68()
988
    {
989
        $explanation = '
990
			<h1>68. Fix Bad Order Status</h1>
991
			<p>Fixing any orders with an StatusID that is not in use...</p>
992
		';
993
        if ($this->retrieveInfoOnly) {
994
            return $explanation;
995
        } else {
996
            echo $explanation;
997
        }
998
        $firstOption = OrderStep::get()->First();
999
        if ($firstOption) {
1000
            $badOrders = Order::get()
1001
                ->where('"StatusID" = 0 OR "StatusID" IS NULL OR "OrderStep"."ID" IS NULL')
1002
                ->leftJoin('OrderStep', '"Order"."StatusID" = "OrderStep"."ID"')
1003
                ->limit($this->limit, $this->start);
1004
            if ($badOrders->count()) {
1005
                foreach ($badOrders as $order) {
1006
                    if ($order->TotalItems() > 0) {
1007
                        DB::query('UPDATE "Order" SET "StatusID" = '.$firstOption->ID.' WHERE "Order"."ID" = '.$order->ID);
1008
                        $this->DBAlterationMessageNow('No order status for order number #'.$order->ID." reverting to: $firstOption->Name.", 'error');
1009
                    }
1010
                }
1011
1012
                return $this->start + $this->limit;
1013
            } else {
1014
                $this->DBAlterationMessageNow('There are no orders with incorrect order status.');
1015
            }
1016
        } else {
1017
            $this->DBAlterationMessageNow('No first order step.', 'error');
1018
        }
1019
1020
        return 0;
1021
    }
1022
1023
    protected function updateProductGroups_110()
1024
    {
1025
        $explanation = "
1026
			<h1>110. Update Product Groups: </h1>
1027
			<p>Set the product groups 'show products' to the default.</p>
1028
		";
1029
        if ($this->retrieveInfoOnly) {
1030
            return $explanation;
1031
        } else {
1032
            echo $explanation;
1033
        }
1034
        $checkIfAnyLevelsAreSetAtAll = DB::query('SELECT COUNT(ID) FROM "ProductGroup" WHERE "LevelOfProductsToShow" <> 0 AND "LevelOfProductsToShow" IS NOT NULL')->value();
1035
        $productGroupDefaults = Config::inst()->get('ProductGroup', 'defaults');
1036
        if ($checkIfAnyLevelsAreSetAtAll == 0 && $productGroupDefaults['LevelOfProductsToShow'] != 0) {
1037
            //level of products to show
1038
            DB::query('
1039
				UPDATE "ProductGroup"
1040
				SET "LevelOfProductsToShow" = '.$productGroupDefaults['LevelOfProductsToShow'].'
1041
				WHERE "LevelOfProductsToShow" = 0 OR "LevelOfProductsToShow" IS NULL '
1042
            );
1043
            DB::query('
1044
				UPDATE "ProductGroup_Live"
1045
				SET "LevelOfProductsToShow" = '.$productGroupDefaults['LevelOfProductsToShow'].'
1046
				WHERE "LevelOfProductsToShow" = 0 OR "LevelOfProductsToShow"  IS NULL '
1047
            );
1048
            $this->DBAlterationMessageNow("resetting product 'show' levels", 'created');
1049
            //default sort order
1050
            DB::query('
1051
				UPDATE "ProductGroup"
1052
				SET "DefaultSortOrder" = '.$productGroupDefaults['DefaultSortOrder']."
1053
				WHERE \"DefaultSortOrder\" = 0 OR  \"DefaultSortOrder\" = '' OR  \"DefaultSortOrder\" IS NULL "
1054
            );
1055
            DB::query('
1056
				UPDATE "ProductGroup_Live"
1057
				SET "DefaultSortOrder" = '.$productGroupDefaults['DefaultSortOrder']."
1058
				WHERE \"DefaultSortOrder\" = 0 OR  \"DefaultSortOrder\" = '' OR  \"DefaultSortOrder\" IS NULL "
1059
            );
1060
            $this->DBAlterationMessageNow('resetting product default sort order', 'created');
1061
            //default filter
1062
            DB::query('
1063
				UPDATE "ProductGroup"
1064
				SET "DefaultFilter" = '.$productGroupDefaults['DefaultFilter']."
1065
				WHERE \"DefaultFilter\" = 0 OR  \"DefaultFilter\" = '' OR  \"DefaultFilter\" IS NULL "
1066
            );
1067
            DB::query('
1068
				UPDATE "ProductGroup_Live"
1069
				SET "DefaultFilter" = '.$productGroupDefaults['DefaultFilter']."
1070
				WHERE \"DefaultFilter\" = 0 OR  \"DefaultFilter\" = '' OR  \"DefaultFilter\" IS NULL "
1071
            );
1072
            $this->DBAlterationMessageNow('resetting product default filter', 'created');
1073
        } else {
1074
            $this->DBAlterationMessageNow("there is no need for resetting product 'show' levels");
1075
        }
1076
1077
        return 0;
1078
    }
1079
1080
    protected function setFixedPriceForSubmittedOrderItems_120()
1081
    {
1082
        $explanation = '
1083
			<h1>120. Set Fixed Price for Submitted Order Items: </h1>
1084
			<p>Migration task to fix the price for submitted order items.</p>
1085
		';
1086
        if ($this->retrieveInfoOnly) {
1087
            return $explanation;
1088
        } else {
1089
            echo $explanation;
1090
        }
1091
        if ($this->hasTableAndField('OrderModifier', 'CalculationValue')) {
1092
            DB::query('
1093
				UPDATE "OrderAttribute"
1094
				INNER JOIN "OrderModifier"
1095
					ON "OrderAttribute"."ID" = "OrderModifier"."ID"
1096
				SET "OrderAttribute"."CalculatedTotal" = "OrderModifier"."CalculationValue"
1097
				WHERE "OrderAttribute"."CalculatedTotal" = 0'
1098
            );
1099
            $this->makeFieldObsolete('OrderModifier', 'CalculationValue');
1100
            $this->DBAlterationMessageNow('Moving values from OrderModifier.CalculationValue to OrderAttribute.CalculatedTotal', 'created');
1101
        } else {
1102
            $this->DBAlterationMessageNow('There is no need to move values from OrderModifier.CalculationValue to OrderAttribute.CalculatedTotal');
1103
        }
1104
        /////////////////////////////////
1105
        ///////// We should not include the code below
1106
        ///////// Because it may affect past orders badly.
1107
        /////////////////////////////////
1108
        /////////////////////////////////
1109
        return;
1110
        $orderItems = Order::get()
0 ignored issues
show
Unused Code introduced by
$orderItems = \Order::ge...->limit, $this->start); does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
1111
            ->where('"Quantity" <> 0 AND "OrderAttribute"."CalculatedTotal" = 0')
1112
            ->sort('"Created" ASC')
1113
            ->limit($this->limit, $this->start);
1114
        $count = 0;
1115
        if ($orderItems->count()) {
1116
            foreach ($orderItems as $orderItem) {
1117
                if ($orderItem->Order()) {
1118
                    if ($orderItem->Order()->IsSubmitted()) {
1119
                        //TO DO: WHAT THE HELL IS THAT (true)
1120
                        $unitPrice = $orderItem->UnitPrice($recalculate = true);
1121
                        if ($unitPrice) {
1122
                            $orderItem->CalculatedTotal = $unitPrice * $orderItem->Quantity;
1123
                            $orderItem->write();
1124
                            ++$count;
1125
                            $this->DBAlterationMessageNow('RECALCULATING: '.$orderItem->UnitPrice($recalculate = true).' * '.$orderItem->Quantity.' = '.$orderItem->CalculatedTotal.' for OrderItem #'.$orderItem->ID, 'created');
1126
                        }
1127
                    } else {
1128
                        $this->DBAlterationMessageNow('OrderItem is part of not-submitted order.');
1129
                    }
1130
                } else {
1131
                    $this->DBAlterationMessageNow('OrderItem does not have an order! (OrderItemID: '.$orderItem->ID.')', 'deleted');
1132
                }
1133
            }
1134
        } else {
1135
            $this->DBAlterationMessageNow('All order items have a calculated total....');
1136
        }
1137
        if ($count) {
1138
            $this->DBAlterationMessageNow("Fixed price for all submmitted orders without a fixed one - affected: $count order items", 'created');
1139
        }
1140
1141
        return 0;
1142
    }
1143
1144
    protected function moveSiteConfigToEcommerceDBConfig_140()
1145
    {
1146
        $explanation = '
1147
			<h1>140. Move Site Config fields to Ecommerce DB Config</h1>
1148
			<p>Moving the general config fields from the SiteConfig to the EcommerceDBConfig.</p>
1149
		';
1150
        if ($this->retrieveInfoOnly) {
1151
            return $explanation;
1152
        } else {
1153
            echo $explanation;
1154
        }
1155
        $fields = array(
1156
            'ShopClosed',
1157
            'ShopPricesAreTaxExclusive',
1158
            'ShopPhysicalAddress',
1159
            'ReceiptEmail',
1160
            'PostalCodeURL',
1161
            'PostalCodeLabel',
1162
            'NumberOfProductsPerPage',
1163
            'OnlyShowProductsThatCanBePurchased',
1164
            'ProductsHaveWeight',
1165
            'ProductsHaveModelNames',
1166
            'ProductsHaveQuantifiers',
1167
            'ProductsAlsoInOtherGroups',
1168
            //"ProductsHaveVariations",
1169
            'EmailLogoID',
1170
            'DefaultProductImageID',
1171
        );
1172
        $ecomConfig = EcommerceDBConfig::get()->First();
1173
        if (!$ecomConfig) {
1174
            $ecomConfig = EcommerceDBConfig::create();
1175
            $ecomConfig->write();
1176
        }
1177
        $sc = SiteConfig::current_site_config();
1178
        if ($ecomConfig && $sc) {
1179
            foreach ($fields as $field) {
1180
                if ($this->hasTableAndField('SiteConfig', $field)) {
1181
                    if (!$this->hasTableAndField('EcommerceDBConfig', $field)) {
1182
                        $this->DBAlterationMessageNow("Could not find EcommerceDBConfig.$field - this is unexpected!", 'deleted');
1183
                    } else {
1184
                        $this->DBAlterationMessageNow("Migrated SiteConfig.$field", 'created');
1185
                        $ecomConfig->$field = DB::query("SELECT \"$field\" FROM \"SiteConfig\" WHERE \"ID\" = ".$sc->ID)->value();
1186
                        $ecomConfig->write();
1187
                        $this->makeFieldObsolete('SiteConfig', $field);
1188
                    }
1189
                } else {
1190
                    $this->DBAlterationMessageNow("SiteConfig.$field has been moved");
1191
                }
1192
            }
1193
        } else {
1194
            $this->DBAlterationMessageNow('ERROR: SiteConfig or EcommerceDBConfig are not available', 'deleted');
1195
        }
1196
1197
        return 0;
1198
    }
1199
1200
    public function addClassNameToOrderItems_150()
1201
    {
1202
        $explanation = '
1203
			<h1>150. Add a class name to all buyables.</h1>
1204
			<p>ClassNames used to be implied, this is now saved as OrderItem.BuyableClassName.</p>
1205
		';
1206
        if ($this->retrieveInfoOnly) {
1207
            return $explanation;
1208
        } else {
1209
            echo $explanation;
1210
        }
1211
        $rows = DB::query("
1212
			SELECT \"OrderAttribute\".\"ID\", \"ClassName\"
1213
			FROM \"OrderAttribute\"
1214
				INNER JOIN \"OrderItem\" ON \"OrderItem\".\"ID\" = \"OrderAttribute\".\"ID\"
1215
			WHERE \"BuyableClassName\" = '' OR \"BuyableClassName\" IS NULL;
1216
		");
1217
        if ($rows) {
1218
            foreach ($rows as $row) {
1219
                $orderItemPostFix = '_OrderItem';
1220
                $id = $row['ID'];
1221
                $className = str_replace($orderItemPostFix, '', $row['ClassName']);
1222
                if (class_exists($className) && ClassInfo::is_subclass_of($className, 'DataObject')) {
0 ignored issues
show
Bug introduced by
The method is_subclass_of() does not seem to exist on object<ClassInfo>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
1223
                    DB::query("
1224
						UPDATE \"OrderItem\"
1225
						SET \"BuyableClassName\" = '$className'
1226
						WHERE \"ID\" = $id;
1227
					");
1228
                    $this->DBAlterationMessageNow("Updating Order.BuyableClassName ( ID = $id ) to $className.", 'created');
1229
                } else {
1230
                    $this->DBAlterationMessageNow("Order Item with ID = $id does not have a valid class name. This needs investigation.", 'deleted');
1231
                }
1232
            }
1233
        } else {
1234
            $this->DBAlterationMessageNow('No order items could be found that need updating.');
1235
        }
1236
1237
        return 0;
1238
    }
1239
1240
    public function addTermsAndConditionsMessage_160()
1241
    {
1242
        $explanation = '
1243
			<h1>160. Add checkout message TermsAndConditionsMessage message.</h1>
1244
			<p>Adds TermsAndConditionsMessage if there is a terms page.</p>
1245
		';
1246
        if ($this->retrieveInfoOnly) {
1247
            return $explanation;
1248
        } else {
1249
            echo $explanation;
1250
        }
1251
        $checkoutPage = CheckoutPage::get()->First();
1252
        if ($checkoutPage) {
1253
            if ($checkoutPage->TermsPageID) {
1254
                if (!$checkoutPage->TermsAndConditionsMessage) {
1255
                    $checkoutPageDefaults = Config::inst()->get('CheckoutPage', 'defaults');
1256
                    $checkoutPage->TermsAndConditionsMessage = $checkoutPageDefaults['TermsAndConditionsMessage'];
1257
                    $checkoutPage->writeToStage('Stage');
1258
                    $checkoutPage->publish('Stage', 'Live');
1259
                    $this->DBAlterationMessageNow('Added TermsAndConditionsMessage', 'created');
1260
                } else {
1261
                    $this->DBAlterationMessageNow('There was no need to add a terms and conditions message because there was already a message.');
1262
                }
1263
            } else {
1264
                $this->DBAlterationMessageNow('There was no need to add a terms and conditions message because there is no terms and conditions page.');
1265
            }
1266
        } else {
1267
            $this->DBAlterationMessageNow('There was no need to add a terms and conditions message because there is no checkout page', 'deleted');
1268
        }
1269
1270
        return 0;
1271
    }
1272
1273
    public function mergeUncompletedOrderForOneMember_170()
1274
    {
1275
        $explanation = '
1276
			<h1>170. Merge uncompleted orders into one.</h1>
1277
			<p>Merges uncompleted orders by the same user into one.</p>
1278
		';
1279
        if ($this->retrieveInfoOnly) {
1280
            return $explanation;
1281
        } else {
1282
            echo $explanation;
1283
        }
1284
        $orders = Order::get()
1285
            ->filter(array('MemberID:GreaterThan' => 0))
1286
            ->sort(array(
1287
                'MemberID' => 'ASC',
1288
                '"Order"."Created"' => 'DESC',
1289
            ))
1290
            ->innerJoin('Member', '"Order"."MemberID" = "Member"."ID"')
1291
            ->limit($this->limit, $this->start);
1292
        $count = 0;
1293
        $previousOrderMemberID = 0;
1294
        $lastOrderFromMember = null;
1295
        if ($orders->count()) {
1296
            foreach ($orders as $order) {
1297
                //crucial ONLY for non-submitted orders...
1298
                if ($order->IsSubmitted()) {
1299
                    //do nothing!
1300
                    ++$count;
1301
                } else {
1302
                    $memberID = $order->MemberID;
1303
                    //recurring member
1304
                    if ($previousOrderMemberID == $memberID && $lastOrderFromMember) {
1305
                        $this->DBAlterationMessageNow('We have a duplicate order for a member: '.$order->Member()->Email, 'created');
1306
                        $orderAttributes = OrderAttribute::get()
1307
                            ->filter(array('OrderID' => $order->ID));
1308
                        if ($orderAttributes->count()) {
1309
                            foreach ($orderAttributes as $orderAttribute) {
1310
                                $this->DBAlterationMessageNow('Moving attribute #'.$orderAttribute->ID, 'created');
1311
                                DB::query('UPDATE "OrderAttribute" SET "OrderID" = '.$lastOrderFromMember->ID.' WHERE "ID" = '.$orderAttribute->ID);
1312
                            }
1313
                        } else {
1314
                            $this->DBAlterationMessageNow('There are no attributes for this order');
1315
                        }
1316
                        $orderStatusLogs = OrderStatusLog::get()->filter(array('OrderID' => $order->ID));
1317
                        if ($orderStatusLogs->count()) {
1318
                            foreach ($orderStatusLogs as $orderStatusLog) {
1319
                                $this->DBAlterationMessageNow('Moving order status log #'.$orderStatusLog->ID, 'created');
1320
                                DB::query('UPDATE "OrderStatusLog" SET "OrderID" = '.$lastOrderFromMember->ID.' WHERE "ID" = '.$orderStatusLog->ID);
1321
                            }
1322
                        } else {
1323
                            $this->DBAlterationMessageNow('There are no order status logs for this order');
1324
                        }
1325
                        $orderEmailRecords = OrderEmailRecord::get()->filter(array('OrderID' => $order->ID));
1326
                        if ($orderEmailRecords->count()) {
1327
                            foreach ($orderEmailRecords as $orderEmailRecord) {
1328
                                DB::query('UPDATE "OrderEmailRecord" SET "OrderID" = '.$lastOrderFromMember->ID.' WHERE "ID" = '.$orderEmailRecord->ID);
1329
                                $this->DBAlterationMessageNow('Moving email #'.$orderEmailRecord->ID, 'created');
1330
                            }
1331
                        } else {
1332
                            $this->DBAlterationMessageNow('There are no emails for this order.');
1333
                        }
1334
                    }
1335
                    //new member
1336
                    else {
1337
                        $previousOrderMemberID = $order->MemberID;
1338
                        $lastOrderFromMember = $order;
1339
                        $this->DBAlterationMessageNow('Found last order from member.');
1340
                    }
1341
                    if ($order->BillingAddressID && !$lastOrderFromMember->BillingAddressID) {
1342
                        $this->DBAlterationMessageNow('Moving Billing Address.');
1343
                        DB::query('UPDATE "Order" SET "BillingAddressID" = '.$order->BillingAddressID.' WHERE "ID" = '.$lastOrderFromMember->ID);
1344
                        DB::query('UPDATE "BillingAddress" SET "OrderID" = '.$lastOrderFromMember->ID.' WHERE "ID" = '.$order->BillingAddressID);
1345
                    }
1346
                    if ($order->ShippingAddressID && !$lastOrderFromMember->ShippingAddressID) {
1347
                        $this->DBAlterationMessageNow('Moving Shipping Address.');
1348
                        DB::query('UPDATE "Order" SET "ShippingAddressID" = '.$order->ShippingAddressID.' WHERE "ID" = '.$lastOrderFromMember->ID);
1349
                        DB::query('UPDATE "ShippingAddress" SET "OrderID" = '.$lastOrderFromMember->ID.' WHERE "ID" = '.$order->ShippingAddressID);
1350
                    }
1351
                    $order->delete();
1352
                }
1353
            }
1354
            $this->DBAlterationMessageNow("Ignored $count Orders that have already been submitted.");
1355
1356
            return $this->start + $this->limit;
1357
        } else {
1358
            $this->DBAlterationMessageNow('There were no orders at all to work through.');
1359
        }
1360
1361
        return 0;
1362
    }
1363
1364
    public function updateFullSiteTreeSortFieldForAllProducts_180()
1365
    {
1366
        $explanation = '
1367
			<h1>180. Set starting value Product.FullSiteTreeSort Field.</h1>
1368
			<p>Sets a starting value for a new field: FullSiteTreeSortField.</p>
1369
		';
1370
        if ($this->retrieveInfoOnly) {
1371
            return $explanation;
1372
        } else {
1373
            echo $explanation;
1374
        }
1375
        //level 10
1376
        $task = new EcommerceTaskCleanupProducts();
1377
        $task->setDeleteFirst(false);
1378
        $task->run(null);
1379
1380
        return 0;
1381
    }
1382
1383
    public function updateOrderStatusLogSequentialOrderNumber_190()
1384
    {
1385
        $explanation = '
1386
			<h1>190. Set sequential order numbers</h1>
1387
			<p>Prepopulates old orders for OrderStatusLog_Submitted.SequentialOrderNumber.</p>
1388
		';
1389
        if ($this->retrieveInfoOnly) {
1390
            return $explanation;
1391
        } else {
1392
            echo $explanation;
1393
        }
1394
        $submittedOrdersLog = OrderStatusLog_Submitted::get()
1395
            ->sort('Created', 'ASC')
1396
            ->limit($this->limit, $this->start);
1397
        $changes = 0;
1398
        if ($submittedOrdersLog->count()) {
1399
            foreach ($submittedOrdersLog as $submittedOrderLog) {
1400
                $old = $submittedOrderLog->SequentialOrderNumber;
1401
                $submittedOrderLog->write();
1402
                $new = $submittedOrderLog->SequentialOrderNumber;
1403
                if ($old != $new) {
1404
                    ++$changes;
1405
                    $this->DBAlterationMessageNow('Changed the SequentialOrderNumber for order #'.$submittedOrderLog->OrderID." from $old to $new ");
1406
                }
1407
            }
1408
            if (!$changes) {
1409
                $this->DBAlterationMessageNow('There were no changes in any of the OrderStatusLog_Submitted.SequentialOrderNumber fields.');
1410
            }
1411
1412
            return $this->start + $this->limit;
1413
        } else {
1414
            $this->DBAlterationMessageNow('There are no logs to update.');
1415
        }
1416
1417
        return 0;
1418
    }
1419
1420
    public function resaveAllPRoducts_200()
1421
    {
1422
        $explanation = '
1423
			<h1>200. Resave All Products to update the FullName and FullSiteTreeSort Field</h1>
1424
			<p>Saves and PUBLISHES all the products on the site. You may need to run this task several times.</p>
1425
		';
1426
        if ($this->retrieveInfoOnly) {
1427
            return $explanation;
1428
        } else {
1429
            echo $explanation;
1430
        }
1431
        $count = 0;
1432
        $products = Product::get()
1433
            ->where("\"FullName\" = '' OR \"FullName\" IS NULL")
1434
            ->sort('ID', 'ASC')
1435
            ->limit($this->limit, $this->start);
1436
        if ($products->count()) {
1437
            foreach ($products as $product) {
1438
                ++$count;
1439
                $product->writeToStage('Stage');
1440
                $product->publish('Stage', 'Live');
1441
                $this->DBAlterationMessageNow('Saving Product '.$product->Title);
1442
            }
1443
1444
            return $this->start + $this->limit;
1445
        } else {
1446
            $this->DBAlterationMessageNow('No products to update.');
1447
        }
1448
1449
        return 0;
1450
    }
1451
1452
    public function resaveAllPRoductsVariations_210()
1453
    {
1454
        $explanation = '
1455
			<h1>210. Resave All Product Variations to update the FullName and FullSiteTreeSort Field</h1>
1456
			<p>Saves all the product variations on the site. You may need to run this task several times.</p>
1457
		';
1458
        if ($this->retrieveInfoOnly) {
1459
            return $explanation;
1460
        } else {
1461
            echo $explanation;
1462
        }
1463
        $count = 0;
1464
        if (class_exists('ProductVariation')) {
1465
            ProductVariation::get()
1466
                ->where("\"FullName\" = '' OR \"FullName\" IS NULL")
1467
                ->sort('ID', 'ASC')
1468
                ->limit($this->limit, $this->start);
1469
            if ($variations->count()) {
1470
                foreach ($variations as $variation) {
0 ignored issues
show
Bug introduced by
The variable $variations does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
1471
                    ++$count;
1472
                    $variation->write();
1473
                    $this->DBAlterationMessageNow('Saving Variation '.$variation->getTitle());
1474
                }
1475
1476
                return $this->start + $this->limit;
1477
            } else {
1478
                $this->DBAlterationMessageNow('No product variations to update.');
1479
            }
1480
        } else {
1481
            $this->DBAlterationMessageNow('There are not ProductVariations in this project');
1482
        }
1483
1484
        return 0;
1485
    }
1486
1487
    public function addConfirmationPage_250()
1488
    {
1489
        $explanation = '
1490
			<h1>250. Add Confirmation Page</h1>
1491
			<p>Creates a checkout page and order confirmation page in case they do not exist.</p>
1492
		';
1493
        if ($this->retrieveInfoOnly) {
1494
            return $explanation;
1495
        } else {
1496
            echo $explanation;
1497
        }
1498
        $checkoutPage = CheckoutPage::get()->First();
1499
        if (!$checkoutPage) {
1500
            $checkoutPage = new CheckoutPage();
1501
            $this->DBAlterationMessageNow('Creating a CheckoutPage', 'created');
1502
        } else {
1503
            $this->DBAlterationMessageNow('No need to create a CheckoutPage Page');
1504
        }
1505
        if ($checkoutPage) {
1506
            $checkoutPage->HasCheckoutSteps = 1;
1507
            $checkoutPage->writeToStage('Stage');
1508
            $checkoutPage->publish('Stage', 'Live');
1509
            $orderConfirmationPage = OrderConfirmationPage::get()->First();
1510
            if ($orderConfirmationPage) {
1511
                $this->DBAlterationMessageNow('No need to create an Order Confirmation Page');
1512
            } else {
1513
                $orderConfirmationPage = new OrderConfirmationPage();
1514
                $orderConfirmationPage->ParentID = $checkoutPage->ID;
1515
                $orderConfirmationPage->writeToStage('Stage');
1516
                $orderConfirmationPage->publish('Stage', 'Live');
1517
                $this->DBAlterationMessageNow('Creating an Order Confirmation Page', 'created');
1518
            }
1519
        } else {
1520
            $this->DBAlterationMessageNow('There is no CheckoutPage available', 'deleted');
1521
        }
1522
1523
        return 0;
1524
    }
1525
1526
    public function cleanupImages_260()
1527
    {
1528
        $explanation = '
1529
			<h1>260. Cleanup Images</h1>
1530
			<p>Checks the class name of all product images and makes sure they exist.</p>
1531
		';
1532
        if ($this->retrieveInfoOnly) {
1533
            return $explanation;
1534
        } else {
1535
            echo $explanation;
1536
        }
1537
        $task = new EcommerceTaskProductImageReset();
1538
        $task->run(null);
1539
1540
        return 0;
1541
    }
1542
1543
    public function addNewPopUpManager_280()
1544
    {
1545
        $explanation = '
1546
			<h1>280. Add new pop-up manager</h1>
1547
			<p>Replaces a link to a JS Library in the config file</p>
1548
		';
1549
        if ($this->retrieveInfoOnly) {
1550
            return $explanation;
1551
        } else {
1552
            echo $explanation;
1553
        }
1554
        $oldJSLibrary = 'ecommerce/thirdparty/simpledialogue_fixed/jquery.simpledialog.0.1.js';
1555
        $newJSLibrary = 'ecommerce/thirdparty/colorbox/jquery.colorbox-min.js';
1556
        $fileArray = Config::inst()->get('EcommerceConfig', 'folder_and_file_locations');
1557
        if ($fileArray && count($fileArray)) {
1558
            foreach ($fileArray as $folderAndFileLocationWithoutBase) {
0 ignored issues
show
Bug introduced by
The expression $fileArray of type array|integer|double|string|boolean is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1559
                if ($folderAndFileLocationWithoutBase != 'ecommerce/_config/ecommerce.yml') {
1560
                    $folderAndFileLocationWithBase = Director::baseFolder().'/'.$folderAndFileLocationWithoutBase;
1561
                    if (file_exists($folderAndFileLocationWithBase)) {
1562
                        $fp = @fopen($folderAndFileLocationWithBase, 'r');
1563
                        if ($fp) {
1564
                            $oldContent = fread($fp, filesize($folderAndFileLocationWithBase));
1565
                            $newContent = str_replace($oldJSLibrary, $newJSLibrary, $oldContent);
1566
                            if ($oldContent != $newContent) {
1567
                                fclose($fp);
1568
                                $fp = fopen($folderAndFileLocationWithBase, 'w+');
1569
                                if (fwrite($fp, $newContent)) {
1570
                                    $this->DBAlterationMessageNow("file updated from $oldJSLibrary to $newJSLibrary in  $folderAndFileLocationWithoutBase", 'created');
1571
                                } else {
1572
                                    $this->DBAlterationMessageNow("Could NOT update from $oldJSLibrary to $newJSLibrary in  $folderAndFileLocationWithoutBase");
1573
                                }
1574
                                fclose($fp);
1575
                            } else {
1576
                                $this->DBAlterationMessageNow("There is no need to update $folderAndFileLocationWithBase");
1577
                            }
1578
                        } else {
1579
                            $this->DBAlterationMessageNow("it seems that $folderAndFileLocationWithBase - does not have the right permission, please change manually.", 'deleted');
1580
                        }
1581
                    } else {
1582
                        $this->DBAlterationMessageNow("Could not find $folderAndFileLocationWithBase - even though it is referenced in EcommerceConfig::\$folder_and_file_locations", 'deleted');
1583
                    }
1584
                } else {
1585
                    $this->DBAlterationMessageNow('There is no need to replace the ecommerce default file: ecommerce/_config/ecommerce.yml', 'created');
1586
                }
1587
            }
1588
        } else {
1589
            $this->DBAlterationMessageNow('Could not find any config files (most usual place: mysite/_config/ecommerce.yml)', 'deleted');
1590
        }
1591
1592
        return 0;
1593
    }
1594
1595
    public function addCurrencyCodeIDToOrders_290()
1596
    {
1597
        $explanation = '
1598
			<h1>290. Add Curenccy to Orders</h1>
1599
			<p>Sets all currencies to the default currency for all orders without a currency.</p>
1600
		';
1601
        if ($this->retrieveInfoOnly) {
1602
            return $explanation;
1603
        } else {
1604
            echo $explanation;
1605
        }
1606
        $ordersWithoutCurrencyCount = Order::get()->filter(array('CurrencyUsedID' => 0))->count();
1607
        if ($ordersWithoutCurrencyCount) {
1608
            $currencyID = EcommerceCurrency::default_currency_id();
1609
            DB::query("UPDATE \"Order\" SET \"CurrencyUsedID\" = $currencyID WHERE \"CurrencyUsedID\" = 0");
1610
            $this->DBAlterationMessageNow("All orders ($ordersWithoutCurrencyCount) have been set a currency value.", 'changed');
1611
        }
1612
1613
        return 0;
1614
    }
1615
1616
    public function MovePaymentToEcommercePayment_300()
1617
    {
1618
        $explanation = '
1619
			<h1>300. Migrating Payment to EcommercePayment</h1>
1620
			<p>We move the data from Payment to EcommercePayment.</p>
1621
		';
1622
        if ($this->retrieveInfoOnly) {
1623
            return $explanation;
1624
        } else {
1625
            echo $explanation;
1626
        }
1627
        $db = DB::getConn();
0 ignored issues
show
Deprecated Code introduced by
The method DB::getConn() has been deprecated with message: since version 4.0 Use DB::get_conn instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
1628
        $table = 'Payment';
1629
        if ($db->hasTable('_obsolete_Payment') && !$db->hasTable('Payment')) {
0 ignored issues
show
Deprecated Code introduced by
The method SS_Database::hasTable() has been deprecated with message: since version 4.0 Use DB::get_schema()->hasTable() instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
1630
            $table = '_obsolete_Payment';
1631
            $this->DBAlterationMessageNow('The table Payment has been moved to _obsolete_Payment. We are using _obsolete_Payment to fix things...', 'deleted');
1632
        }
1633
        DB::query('
1634
			INSERT IGNORE INTO EcommercePayment(
1635
				`ID`,
1636
				`ClassName`,
1637
				`Created`,
1638
				`LastEdited`,
1639
				`Status`,
1640
				`AmountAmount`,
1641
				`AmountCurrency`,
1642
				`Message`,
1643
				`IP`,
1644
				`ProxyIP`,
1645
				`OrderID`,
1646
				`ExceptionError`,
1647
				`PaidByID`
1648
			)
1649
			SELECT
1650
					`ID`,
1651
					`ClassName`,
1652
					`Created`,
1653
					`LastEdited`,
1654
					`Status`,
1655
					IF(`AmountAmount` > 0, `AmountAmount`, `Amount`),
1656
					IF(`AmountCurrency` <> \'\', `AmountCurrency`, `Currency`),
1657
					`Message`,
1658
					`IP`,
1659
					`ProxyIP`,
1660
					`OrderID`,
1661
					`ExceptionError`,
1662
					`PaidByID`
1663
				FROM '.$table.''
1664
        );
1665
        $this->DBAlterationMessageNow('Moving Payment to Ecommerce Payment', 'created');
1666
1667
        return 0;
1668
    }
1669
1670
    public function ecommercetaskupgradepickupordeliverymodifier_310()
1671
    {
1672
        $explanation = '
1673
			<h1>310.Upgrade Pick Up of Delivery Modifier</h1>
1674
			<p>Fixing data in this modifier if it exists.</p>
1675
		';
1676
        if ($this->retrieveInfoOnly) {
1677
            return $explanation;
1678
        } else {
1679
            echo $explanation;
1680
        }
1681
        if (class_exists('EcommerceTaskUpgradePickUpOrDeliveryModifier') && $this->hasTableAndField('PickUpOrDeliveryModifier', 'PickupOrDeliveryType')) {
1682
            $obj = EcommerceTaskUpgradePickUpOrDeliveryModifier::create();
1683
            $obj->run(null);
1684
        }
1685
1686
        return 0;
1687
    }
1688
1689
    public function ecommercetaskupgradepickupordeliverymodifier_320()
1690
    {
1691
        $explanation = '
1692
			<h1>320. Removing empty Order Items</h1>
1693
			<p>Removes all the order items without a buyable.</p>
1694
		';
1695
        if ($this->retrieveInfoOnly) {
1696
            return $explanation;
1697
        } else {
1698
            echo $explanation;
1699
        }
1700
        $orderItems = OrderItem::get()->filter(array('BuyableID' => 0));
1701
        $count = $orderItems->count();
1702
        if ($count > 0) {
1703
            $style = 'deleted';
1704
        } else {
1705
            $style = 'created';
1706
        }
1707
        $this->DBAlterationMessageNow('There are '.$count.' items that should be removed', $style);
1708
        foreach ($orderItems as $orderItem) {
1709
            $this->DBAlterationMessageNow('Deleting order item with ID: '.$orderItem->ID, 'deleted');
1710
            $orderItem->delete();
1711
        }
1712
1713
        return 0;
1714
    }
1715
1716
    public function removemobilephones_330()
1717
    {
1718
        $explanation = '
1719
			<h1>323. Removing mobile phones</h1>
1720
			<p>Move all Billing and Shipping Address Mobile Phone Entries to Phone.</p>
1721
		';
1722
        if ($this->retrieveInfoOnly) {
1723
            return $explanation;
1724
        } else {
1725
            echo $explanation;
1726
        }
1727
        $this->DBAlterationMessageNow('Moving Mobile Phone to Phone in Billing Address', 'created');
1728
        DB::query("UPDATE BillingAddress SET Phone = MobilePhone WHERE Phone = '' OR Phone IS NULL;");
1729
1730
        $this->DBAlterationMessageNow('Moving Mobile Phone to Phone in Shipping Address', 'created');
1731
        DB::query("UPDATE ShippingAddress SET ShippingPhone = ShippingMobilePhone WHERE ShippingPhone = '' OR ShippingPhone IS NULL;");
1732
1733
        $this->DBAlterationMessageNow('Merging Mobile Phone and Phone in Billing Address', 'created');
1734
        DB::query("UPDATE BillingAddress SET Phone = CONCAT(Phone, ' ', MobilePhone) WHERE Phone <> '' AND Phone IS NOT NULL AND MobilePhone <> '' AND MobilePhone IS NOT NULL;");
1735
1736
        $this->DBAlterationMessageNow('Merging Mobile Phone and Phone in Shipping Address', 'created');
1737
        DB::query("UPDATE ShippingAddress SET ShippingPhone = CONCAT(ShippingPhone, ' ', ShippingMobilePhone) WHERE ShippingPhone <> '' AND ShippingPhone IS NOT NULL AND ShippingMobilePhone <> '' AND ShippingMobilePhone IS NOT NULL;");
1738
1739
        //remove fields
1740
        $this->DBAlterationMessageNow('Making obsolete: BillingAddress.MobilePhone', 'deleted');
1741
        $this->makeFieldObsolete('BillingAddress', 'MobilePhone');
1742
1743
        $this->DBAlterationMessageNow('Making obsolete: ShippingAddress.ShippingMobilePhone', 'deleted');
1744
        $this->makeFieldObsolete('ShippingAddress', 'ShippingMobilePhone');
1745
1746
        return 0;
1747
    }
1748
1749
    public function theEnd_9999()
1750
    {
1751
        $explanation = '
1752
			<h1>9999. Migration Completed</h1>
1753
		';
1754
        if ($this->retrieveInfoOnly) {
1755
            return $explanation;
1756
        } else {
1757
            echo $explanation;
1758
        }
1759
1760
        return 0;
1761
    }
1762
1763
    public function DBAlterationMessageNow($message, $style = '')
1764
    {
1765
        DB::alteration_message($message, $style);
1766
        ob_end_flush();
1767
        ob_start();
1768
    }
1769
}
1770