SalesReport   A
last analyzed

Complexity

Total Complexity 34

Size/Duplication

Total Lines 226
Duplicated Lines 43.36 %

Coupling/Cohesion

Components 4
Dependencies 2

Importance

Changes 0
Metric Value
wmc 34
lcom 4
cbo 2
dl 98
loc 226
rs 9.2
c 0
b 0
f 0

12 Methods

Rating   Name   Duplication   Size   Complexity  
A set_full_export_select_statement() 0 4 1
A get_full_export_select_statement() 9 9 3
A set_full_export_join_statement() 0 4 1
A get_full_export_join_statement() 9 9 3
A set_full_export_file_name() 0 4 1
A get_full_export_file_name() 9 9 3
A getReportField() 0 52 1
A getCustomQuery() 11 11 2
A getExportFields() 0 7 2
A getExportQuery() 10 10 2
C statistic() 42 42 13
A processform() 8 8 2

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
/**
3
 * @author Nicolaas [at] sunnysideup.co.nz
4
 * @package ecommercextras
5
 */
6
class SalesReport extends SS_Report
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...
7
{
8
    protected static $full_export_select_statement =
9
        array(
10
            "`Order`.`ID`" => "Order number",
11
            "`Order`.`Created`" => "Order date and time",
12
            "GROUP_CONCAT(`Payment`.`Message` SEPARATOR ', ')" => "payment gateway reference",
13
            "GROUP_CONCAT(`Payment`.`ID` SEPARATOR ', ')" =>  "Transaction Id",
14
            "SUM(IF(Payment.Status = 'Success',`Payment`.`Amount`, 0)) RealPayments" => "Total Order Amount",
15
            "IF(ProductVariationsForVariations.Title IS NOT NULL, CONCAT(ProductSiteTreeForVariations.Title,' : ', ProductVariationsForVariations.Title), IF(SiteTreeForProducts.Title IS NOT NULL, SiteTreeForProducts.Title, OrderAttribute.ClassName)) ProductOrModifierName" => "Product Name",
16
            "IF(OrderItem.Quantity IS NOT NULL, OrderItem.Quantity, 1)  ProductOrModifierQuantity" => "Quantity",
17
            "IF(ProductSiteTreeForVariations.ID IS NOT NULL, ProductVariationsForVariations.Price, 0) +  IF(ProductForProducts.Price IS NOT NULL, ProductForProducts.Price, 0) + IF(OrderModifier.Amount IS NOT NULL, OrderModifier.Amount, 0) ProductOrModifierPrice" => "Amount",
18
            "IF(ProductForProducts.Price IS NOT NULL, ProductForProducts.Price, 0) ProductOrModifierPriceProduct" => "Product Amount",
19
            "IF(ProductSiteTreeForVariations.ID IS NOT NULL, ProductVariationsForVariations.Price, 0) ProductOrModifierPriceVariation" => "Variation Amount",
20
            "IF(OrderModifier.Amount IS NOT NULL, OrderModifier.Amount, 0) ProductOrModifierPriceModifier" => "Modifier Amount",
21
            "CONCAT(Member.Address, ' ', Member.AddressLine2,' ', Member.City, ' ', Member.Country,' ', Member.HomePhone,' ', Member.MobilePhone,' ', Member.Notes,' ', Member.Notes ) MemberContactDetails" => "Customer contact details",
22
            "IF(`Order`.`UseShippingAddress`, CONCAT(`Order`.`ShippingName`, ' ',`Order`.`ShippingAddress`, ' ',`Order`.`ShippingAddress2`, ' ',`Order`.`ShippingCity`, ' ',`Order`.`ShippingCountry`), 'no alternative delivery address') MemberShippingDetailsAddress" => "Costumer delivery",
23
            "`Order`.`Status`" => "Order status",
24
            "GROUP_CONCAT(`OrderStatusLogWithDetails`.`DispatchTicket` SEPARATOR ', ')" => "Dispatch ticket code",
25
            "GROUP_CONCAT(`OrderStatusLogWithDetails`.`DispatchedOn` SEPARATOR ', ')" => "Dispatch date",
26
            "GROUP_CONCAT(`OrderStatusLogWithDetails`.`DispatchedBy` SEPARATOR ', ')" => "Dispatched by",
27
            "GROUP_CONCAT(`OrderStatusLog`.`Note` SEPARATOR ', ')" => "Dispatch notes"
28
        );
29
    public static function set_full_export_select_statement($v)
30
    {
31
        self::$full_export_select_statement = $v;
32
    }
33 View Code Duplication
    public static function get_full_export_select_statement()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
34
    {
35
        $bt = defined('DB::USE_ANSI_SQL') ? "\"" : "`";
36
        if ($bt == '"') {
37
            return str_replace('`', '"', self::$full_export_select_statement);
38
        } else {
39
            return self::$full_export_select_statement;
40
        }
41
    }
42
43
    protected static $full_export_join_statement = '
44
			INNER JOIN `Order` ON `Order`.`ID` = `OrderAttribute`.`OrderID`
45
			INNER JOIN `Member` On `Order`.`MemberID` = `Member`.`ID`
46
			LEFT JOIN `Payment` ON `Payment`.`OrderID` = `Order`.`ID`
47
			LEFT JOIN `Product_versions` ProductForProducts ON `Product_OrderItem`.`ProductID` = ProductForProducts.`RecordID` AND `Product_OrderItem`.`ProductVersion` = ProductForProducts.`Version`
48
			LEFT JOIN `SiteTree_versions` SiteTreeForProducts ON SiteTreeForProducts.`RecordID` = `Product_OrderItem`.`ProductID` AND `Product_OrderItem`.`ProductVersion` = SiteTreeForProducts.`Version`
49
			LEFT JOIN `ProductVariation_versions` ProductVariationsForVariations ON `ProductVariation_OrderItem`.`ProductVariationID` = ProductVariationsForVariations.`RecordID` AND `ProductVariation_OrderItem`.`ProductVariationVersion` = ProductVariationsForVariations.`Version`
50
			LEFT JOIN `SiteTree_versions` ProductSiteTreeForVariations ON `ProductSiteTreeForVariations`.`RecordID` = `Product_OrderItem`.`ProductID` AND `Product_OrderItem`.`ProductVersion` = `ProductSiteTreeForVariations`.`Version`
51
			LEFT JOIN `OrderStatusLog` ON `OrderStatusLog`.`OrderID` = `Order`.`ID`
52
			LEFT JOIN `OrderStatusLogWithDetails` ON `OrderStatusLogWithDetails`.`ID` = `OrderStatusLog`.`ID`';
53
    public static function set_full_export_join_statement($v)
54
    {
55
        self::$full_export_join_statement = $v;
56
    }
57 View Code Duplication
    public static function get_full_export_join_statement()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
58
    {
59
        $bt = defined('DB::USE_ANSI_SQL') ? "\"" : "`";
60
        if ($bt == '"') {
61
            return str_replace('`', '"', self::$full_export_join_statement);
62
        } else {
63
            return self::$full_export_join_statement;
64
        }
65
    }
66
67
    protected static $full_export_file_name = "SalesExport";
68
    public static function set_full_export_file_name($v)
69
    {
70
        self::$full_export_file_name = $v;
71
    }
72 View Code Duplication
    public static function get_full_export_file_name()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
73
    {
74
        $bt = defined('DB::USE_ANSI_SQL') ? "\"" : "`";
75
        if ($bt == '"') {
76
            return str_replace('`', '"', self::$full_export_file_name);
77
        } else {
78
            return self::$full_export_file_name;
79
        }
80
    }
81
82
    protected $title = 'All Orders';
83
84
    protected static $sales_array = array();
85
86
    protected $description = 'Search Orders';
87
88
    /**
89
     * Return a {@link ComplexTableField} that shows
90
     * all Order instances that are not printed. That is,
91
     * Order instances with the property "Printed" value
92
     * set to "0".
93
     *
94
     * @return ComplexTableField
95
     */
96
    public function getReportField()
97
    {
98
        // Get the fields used for the table columns
99
        Order::$summary_fields["Created"] = "Received";
100
        $fields = Order::$summary_fields;
101
102
        // Add some fields specific to this report
103
        $fields['Invoice'] = '';
104
        $fields['PackingSlip'] = '';
105
        $fields['ChangeStatus'] = '';
106
107
        $table = new TableListField(
108
            'Orders',
109
            'Order',
110
            $fields
111
        );
112
113
        // Customise the SQL query for Order, because we don't want it querying
114
        // all the fields. Invoice and Printed are dummy fields that just have some
115
        // text in them, which would be automatically queried if we didn't specify
116
        // a custom query.
117
118
        $table->setCustomQuery($this->getCustomQuery());
119
120
        // Set the links to the Invoice and Print fields allowing a user to view
121
        // another template for viewing an Order instance
122
        $table->setFieldFormatting(array(
123
            'Invoice' => '<a href=\"OrderReportWithLog_Popup/invoice/$ID\" class=\"makeIntoPopUp\">Invoice and Update</a>',
124
            'PackingSlip' => '<a href=\"OrderReport_Popup/packingslip/$ID\" class=\"makeIntoPopUp\">Packing Slip</a>',
125
            'ChangeStatus' => '<a href=\"#\" class=\"statusDropdownChange\" rel=\"$ID\">$Status</a><span class=\"outcome\"></span>'
126
        ));
127
128
        $table->setFieldCasting(array(
129
            'Total' => 'Currency->Nice'
130
        ));
131
132
        $table->setPermissions(array(
133
            'edit',
134
            'show',
135
            'export'
136
        ));
137
        $table->setPageSize(250);
138
139
140
        $table->setFieldListCsv($this->getExportFields());
141
142
        $table->setCustomCsvQuery($this->getExportQuery());
143
144
        //$tableField->removeCsvHeader();
0 ignored issues
show
Unused Code Comprehensibility introduced by
84% 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...
145
146
        return $table;
147
    }
148
149 View Code Duplication
    public function getCustomQuery()
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
150
    {
151
        if ("SalesReport" == $this->class) {
152
            user_error('Please implement getCustomQuery() on ' . $this->class, E_USER_ERROR);
153
        } else {
154
            //buildSQL($filter = "", $sort = "", $limit = "", $join = "", $restrictClasses = true, $having = "")
0 ignored issues
show
Unused Code Comprehensibility introduced by
45% 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...
155
            $query = singleton('Order')->buildSQL('', 'Order.Created DESC');
156
            $query->groupby[] = 'Order.ID';
157
            return $query;
158
        }
159
    }
160
161
    public function getExportFields()
162
    {
163
        if ("SalesReport" == $this->class) {
164
            user_error('Please implement getExportFields() on ' . $this->class, E_USER_ERROR);
165
        } else {
0 ignored issues
show
Unused Code introduced by
This else statement is empty and can be removed.

This check looks for the else branches 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 else branches can be removed.

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

could be turned into

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

This is much more concise to read.

Loading history...
166
        }
167
    }
168
169 View Code Duplication
    public function getExportQuery()
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
170
    {
171
        if ("SalesReport" == $this->class) {
172
            user_error('Please implement getExportFields() on ' . $this->class, E_USER_ERROR);
173
        } else {
174
            $query = singleton('Order')->buildSQL('', 'Order.Created DESC');
175
            $query->groupby[] = 'Order.Created';
176
            return $query;
177
        }
178
    }
179
180 View Code Duplication
    protected function statistic($type)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
181
    {
182
        if (!count(self::$sales_array)) {
183
            $data = $this->getCustomQuery()->execute();
184
            if ($data) {
185
                $array = array();
0 ignored issues
show
Unused Code introduced by
$array 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...
186
                foreach ($data as $row) {
187
                    if ($row["RealPayments"]) {
188
                        self::$sales_array[] = $row["RealPayments"];
189
                    }
190
                }
191
            }
192
        }
193
        if (count(self::$sales_array)) {
194
            switch ($type) {
195
                case "count":
196
                    return count(self::$sales_array);
197
                    break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
198
                case "sum":
199
                    return array_sum(self::$sales_array);
200
                    break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
201
                case "avg":
202
                    return array_sum(self::$sales_array) / count(self::$sales_array);
203
                    break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
204
                case "min":
205
                    asort(self::$sales_array);
206
                    foreach (self::$sales_array as $item) {
207
                        return $item;
208
                    }
209
                    break;
210
                case "max":
211
                    arsort(self::$sales_array);
212
                    foreach (self::$sales_array as $item) {
213
                        return $item;
214
                    }
215
                    break;
216
                default:
217
                    user_error("Wrong statistic type speficied in SalesReport::statistic", E_USER_ERROR);
218
            }
219
        }
220
        return -1;
221
    }
222
223 View Code Duplication
    public function processform()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
Coding Style introduced by
processform 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...
224
    {
225
        if ("SalesReport" == $this->class) {
226
            user_error('Please implement processform() on ' . $this->class, E_USER_ERROR);
227
        } else {
228
            die($_REQUEST);
0 ignored issues
show
Coding Style Compatibility introduced by
The method processform() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
229
        }
230
    }
231
}
232
233
class SalesReport_Handler extends Controller
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
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...
234
{
235
    public function processform()
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
236
    {
237
        $ClassName = Director::URLParam("ID");
0 ignored issues
show
Deprecated Code introduced by
The method Director::urlParam() has been deprecated with message: 3.0 Use SS_HTTPRequest->latestParam()

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...
238
        $object = new $ClassName;
239
        return $object->processform();
240
    }
241
242
243 View Code Duplication
    public function setstatus()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
244
    {
245
        $id = $this->urlParams['ID'];
246
        if (!is_numeric($id)) {
247
            return "could not update order status";
248
        }
249
        $order = DataObject::get_by_id('Order', $id);
250
        if ($order) {
251
            $oldStatus = $order->Status;
252
            $newStatus = $this->urlParams['OtherID'];
253
            if ($oldStatus != $newStatus) {
254
                $order->Status = $newStatus;
255
                $order->write();
256
                $orderlog = new OrderStatusLog();
257
                $orderlog->OrderID = $order->ID;
258
                $orderlog->Status = "Status changed from ".$oldStatus." to ".$newStatus.".";
259
                $orderlog->Note = "Status changed from ".$oldStatus." to ".$newStatus.".";
260
                $orderlog->write();
261
            } else {
262
                return "no change";
263
            }
264
        } else {
265
            return "order not found";
266
        }
267
        return "updated to ".$newStatus;
268
    }
269
270
    public function fullsalesexport()
271
    {
272
        $exportReport = new SearchableProductSalesReport();
273
        $query = $exportReport->getCustomQuery();
274
        $query->select = null;
275
        $array = SalesReport::get_full_export_select_statement();
276
        if (is_array($array) && count($array)) {
277
            $fileData = '"row number"';
278
            foreach ($array as $sql => $name) {
279
                $query->select[] = $sql;
280
                $fileData .= ',"'.$name.'"';
281
            }
282
            $data = $query->execute();
283
            if ($data) {
284
                $i = 0;
285
                foreach ($data as $row) {
286
                    $i++;
287
                    $fileData .= "\r\n".'"'.$i.'"';
288
                    foreach ($row as $fieldName => $value) {
289
                        $fileData .= ',"'.$value.'"';
290
                    }
291
                }
292
            } else {
293
                $fileData = "no data available";
294
            }
295
        } else {
296
            $fileData = "please select fields first";
297
        }
298
        $fileName = SalesReport::get_full_export_file_name()."-".date("Y-m-d", strtotime("today")).".csv";
299
        header("Content-Type: text/csv; name=\"" . addslashes($fileName) . "\"");
300
        header("Content-Disposition: attachment; filename=\"" . addslashes($fileName) . "\"");
301
        header("Content-length: " . strlen($fileData));
302
        echo $fileData;
303
        exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The method fullsalesexport() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
304
    }
305
}
306