Passed
Push — master ( 1900ed...f72112 )
by Nic
02:54 queued 13s
created

Purchasable::getCMSValidator()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 0
dl 0
loc 6
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Dynamic\Foxy\Extension;
4
5
use Dynamic\Foxy\Model\FoxyCategory;
6
use Dynamic\Foxy\Model\ProductOption;
7
use Dynamic\Foxy\Model\Variation;
8
use Dynamic\Foxy\Model\VariationType;
9
use micschk\GroupableGridfield\GridFieldGroupable;
10
use SilverStripe\Forms\CheckboxField;
11
use SilverStripe\Forms\CurrencyField;
12
use SilverStripe\Forms\DropdownField;
13
use SilverStripe\Forms\FieldList;
14
use SilverStripe\Forms\GridField\GridField;
15
use SilverStripe\Forms\GridField\GridFieldAddExistingAutocompleter;
16
use SilverStripe\Forms\GridField\GridFieldConfig_RelationEditor;
17
use SilverStripe\Forms\GridField\GridFieldPageCount;
18
use SilverStripe\Forms\GridField\GridFieldPaginator;
19
use SilverStripe\Forms\GridField\GridFieldSortableHeader;
20
use SilverStripe\Forms\NumericField;
21
use SilverStripe\Forms\RequiredFields;
22
use SilverStripe\Forms\TextField;
23
use SilverStripe\ORM\DataExtension;
24
use SilverStripe\ORM\DataObject;
25
use SilverStripe\ORM\HasManyList;
26
use SilverStripe\ORM\ManyManyList;
27
use SilverStripe\ORM\ValidationResult;
28
use SilverStripe\Security\Permission;
29
use SilverStripe\Security\PermissionProvider;
30
use SilverStripe\Security\Security;
31
use Symbiote\GridFieldExtensions\GridFieldAddExistingSearchButton;
32
use Symbiote\GridFieldExtensions\GridFieldOrderableRows;
33
use Symbiote\GridFieldExtensions\GridFieldTitleHeader;
34
35
/**
36
 * Class Purchasable
37
 * @package Dynamic\Foxy\Extension
38
 *
39
 * @property double Price
40
 * @property string Code
41
 * @property string ReceiptTitle
42
 * @property bool Available
43
 * @property int $QuantityMax
44
 *
45
 * @property int FoxyCategoryID
46
 * @method FoxyCategory FoxyCategory()
47
 *
48
 * @method HasManyList Variations()
49
 * @method ManyManyList Options()
50
 *
51
 * @property-read DataObject|Purchasable $owner
52
 */
53
class Purchasable extends DataExtension implements PermissionProvider
54
{
55
    /**
56
     * @var array
57
     */
58
    private static $db = [
0 ignored issues
show
introduced by
The private property $db is not used, and could be removed.
Loading history...
59
        'Price' => 'Currency',
60
        'Code' => 'Varchar(100)',
61
        'ReceiptTitle' => 'HTMLVarchar(255)',
62
        'Available' => 'Boolean',
63
        'QuantityMax' => 'Int',
64
    ];
65
66
    /**
67
     * @var array
68
     */
69
    private static $has_one = [
0 ignored issues
show
introduced by
The private property $has_one is not used, and could be removed.
Loading history...
70
        'FoxyCategory' => FoxyCategory::class,
71
    ];
72
73
    /**
74
     * @var string[]
75
     */
76
    private static $has_many = [
0 ignored issues
show
introduced by
The private property $has_many is not used, and could be removed.
Loading history...
77
        'Variations' => Variation::class,
78
    ];
79
80
    /**
81
     * @var array
82
     */
83
    private static $many_many = [
0 ignored issues
show
introduced by
The private property $many_many is not used, and could be removed.
Loading history...
84
        'Options' => ProductOption::class,
85
    ];
86
87
    /**
88
     * @var array
89
     */
90
    private static $many_many_extraFields = [
0 ignored issues
show
introduced by
The private property $many_many_extraFields is not used, and could be removed.
Loading history...
91
        'Options' => [
92
            'WeightModifier' => 'Decimal(9,3)',
93
            'CodeModifier' => 'Text',
94
            'PriceModifier' => 'Currency',
95
            'WeightModifierAction' => "Enum('Add,Subtract,Set', null)",
96
            'CodeModifierAction' => "Enum('Add,Subtract,Set', null)",
97
            'PriceModifierAction' => "Enum('Add,Subtract,Set', null)",
98
            'Available' => 'Boolean',
99
            'Type' => 'Int',
100
            'OptionModifierKey' => 'Varchar(255)',
101
            'SortOrder' => 'Int',
102
        ],
103
        'OptionTypes' => [
104
            'SortOrder' => 'Int',
105
        ],
106
    ];
107
108
    /**
109
     * @var array
110
     */
111
    private static $indexes = [
0 ignored issues
show
introduced by
The private property $indexes is not used, and could be removed.
Loading history...
112
        'Code' => [
113
            'type' => 'unique',
114
            'columns' => ['Code'],
115
        ],
116
    ];
117
118
    /**
119
     * @var array
120
     */
121
    private static $defaults = [
0 ignored issues
show
introduced by
The private property $defaults is not used, and could be removed.
Loading history...
122
        'ShowInMenus' => false,
123
        'Available' => true,
124
    ];
125
126
    /**
127
     * @var array
128
     */
129
    private static $summary_fields = [
0 ignored issues
show
introduced by
The private property $summary_fields is not used, and could be removed.
Loading history...
130
        'Image.CMSThumbnail',
131
        'Title',
132
        'Code',
133
        'Price.Nice',
134
    ];
135
136
    /**
137
     * @var array
138
     */
139
    private static $searchable_fields = [
0 ignored issues
show
introduced by
The private property $searchable_fields is not used, and could be removed.
Loading history...
140
        'Title',
141
        'Code',
142
        'Available',
143
    ];
144
145
    /**
146
     * @param array $labels
147
     */
148
    public function updateFieldLabels(&$labels)
149
    {
150
        $labels['Title'] = _t(__CLASS__ . '.TitleLabel', 'Product Name');
151
        $labels['Code'] = _t(__CLASS__ . '.CodeLabel', 'Code');
152
        $labels['Price'] = _t(__CLASS__ . '.PriceLabel', 'Price');
153
        $labels['Price.Nice'] = _t(__CLASS__ . '.PriceLabel', 'Price');
154
        $labels['Available'] = _t(__CLASS__ . '.AvailableLabel', 'Available for purchase');
155
        $labels['Available.Nice'] = _t(__CLASS__ . '.AvailableLabelNice', 'Available');
156
        $labels['Image.CMSThumbnail'] = _t(__CLASS__ . '.ImageLabel', 'Image');
157
        $labels['ReceiptTitle'] = _t(__CLASS__ . '.ReceiptTitleLabel', 'Product title for receipt');
158
        $labels['FoxyCategoryID'] = _t(__CLASS__ . '.FoxyCategoryLabel', 'Foxy Category');
159
    }
160
161
    /**
162
     * @param FieldList $fields
163
     */
164
    public function updateCMSFields(FieldList $fields)
165
    {
166
        $fields->removeByName([
167
            'SKU',
168
        ]);
169
170
        $fields->addFieldsToTab(
171
            'Root.Ecommerce',
172
            [
173
                CurrencyField::create('Price')
174
                    ->setDescription(_t(
175
                        __CLASS__ . '.PriceDescription',
176
                        'Base price for this product. Can be modified using Product Options'
177
                    )),
178
                TextField::create('Code')
179
                    ->setDescription(_t(
180
                        __CLASS__ . '.CodeDescription',
181
                        'Required, must be unique. Product identifier used by FoxyCart in transactions. All leading and trailing spaces are removed on save.'
182
                    )),
183
                DropdownField::create('FoxyCategoryID')
184
                    ->setTitle($this->owner->fieldLabel('FoxyCategoryID'))
0 ignored issues
show
Bug introduced by
The method fieldLabel() does not exist on Dynamic\Foxy\Extension\Purchasable. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

184
                    ->setTitle($this->owner->/** @scrutinizer ignore-call */ fieldLabel('FoxyCategoryID'))

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...
185
                    ->setSource(FoxyCategory::get()->map())
186
                    ->setDescription(_t(
187
                        __CLASS__ . '.FoxyCategoryDescription',
188
                        'Required. Must also exist in
189
                        <a href="https://admin.foxycart.com/admin.php?ThisAction=ManageProductCategories"
190
                            target="_blank">
191
                            Foxy Categories
192
                        </a>.
193
                        Used to set category specific options like shipping and taxes. Managed in Foxy > Categories'
194
                    ))
195
                    ->setEmptyString(''),
196
                TextField::create('ReceiptTitle')
197
                    ->setDescription(_t(
198
                        __CLASS__ . '.ReceiptTitleDescription',
199
                        'Optional. Alternate title to display on order receipt'
200
                    )),
201
            ],
202
            'Content'
203
        );
204
205
        if ($this->owner->exists()) {
0 ignored issues
show
Bug introduced by
The method exists() does not exist on Dynamic\Foxy\Extension\Purchasable. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

205
        if ($this->owner->/** @scrutinizer ignore-call */ exists()) {

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...
206
            $config = GridFieldConfig_RelationEditor::create();
207
            $config
208
                ->addComponents([
209
                    new GridFieldOrderableRows('SortOrder'),
210
                    new GridFieldAddExistingSearchButton(),
211
                ])
212
                ->removeComponentsByType([
213
                    GridFieldAddExistingAutocompleter::class,
214
                ]);
215
            $options = GridField::create(
216
                'Options',
217
                'Options',
218
                $this->owner->Options()->sort('SortOrder'),
219
                $config
220
            );
221
222
            $fields->addFieldsToTab(
223
                'Root.Options',
224
                [
225
                    $options,
226
                ]
227
            );
228
229
            $variationsConfig = GridFieldConfig_RelationEditor::create()
230
                ->removeComponentsByType([
231
                    GridFieldAddExistingAutocompleter::class,
232
                    GridFieldPaginator::class,
233
                    GridFieldPageCount::class,
234
                    GridFieldSortableHeader::class,
235
                ])
236
                ->addComponents([
237
                    new GridFieldOrderableRows('SortOrder'),
238
                    new GridFieldTitleHeader(),
239
                    new GridFieldGroupable(
240
                        'VariationTypeID',    // The fieldname to set the Group
241
                        'Variation Type',   // A description of the function of the group
242
                        'none',         // A title/header for items without a group/unassigned
243
                        VariationType::get()->sort('SortOrder')->map()->toArray()
244
                    )
245
                ]);
246
247
            $fields->addFieldToTab(
248
                'Root.Variations',
249
                GridField::create(
250
                    'Variations',
251
                    'Variations',
252
                    $this->owner->Variations(),
253
                    $variationsConfig
254
                )
255
            );
256
        }
257
258
        $fields->addFieldsToTab(
259
            'Root.Inventory',
260
            [
261
                NumericField::create('QuantityMax')
262
                    ->setTitle('Maximum quantity allowed in the cart')
263
                    ->setDescription('For unlimited enter 0')
264
                    ->addExtraClass('stacked'),
265
                CheckboxField::create('Available')
266
                    ->setDescription(_t(
267
                        __CLASS__ . '.AvailableDescription',
268
                        'If unchecked, will remove "Add to Cart" form and instead display "Currently unavailable"'
269
                    )),
270
            ]
271
        );
272
    }
273
274
    /**
275
     * @return RequiredFields
276
     */
277
    public function getCMSValidator()
278
    {
279
        return new RequiredFields([
280
            "Price",
281
            "Code",
282
            "FoxyCategoryID",
283
        ]);
284
    }
285
286
    /**
287
     * @return bool
288
     */
289
    public function isAvailable()
290
    {
291
        if (!$this->owner->Available) {
292
            return false;
293
        }
294
295
        if (!$this->owner->Options()->exists()) {
296
            return true;
297
        }
298
299
        foreach ($this->owner->Options() as $option) {
300
            if ($option->Available) {
301
                return true;
302
            }
303
        }
304
305
        return false;
306
    }
307
308
    /**
309
     * @return bool
310
     */
311
    public function isProduct()
312
    {
313
        return true;
314
    }
315
316
    /**
317
     * @return array
318
     */
319
    public function providePermissions()
320
    {
321
        return [
322
            'MANAGE_FOXY_PRODUCTS' => [
323
                'name' => _t(
324
                    __CLASS__ . '.PERMISSION_MANAGE_PRODUCTS_DESCRIPTION',
325
                    'Manage products'
326
                ),
327
                'category' => _t(
328
                    __CLASS__ . '.PERMISSIONS_CATEGORY',
329
                    'Foxy'
330
                ),
331
                'help' => _t(
332
                    __CLASS__ . '.PERMISSION_MANAGE_PRODUCTS_HELP',
333
                    'Manage products and related settings'
334
                ),
335
                'sort' => 400,
336
            ],
337
        ];
338
    }
339
340
    /**
341
     * @param $member
342
     * @return bool|int|void
343
     */
344
    public function canCreate($member)
345
    {
346
        if (!$member) {
347
            $member = Security::getCurrentUser();
348
        }
349
350
        return Permission::checkMember($member, 'MANAGE_FOXY_PRODUCTS');
351
    }
352
353
    /**
354
     * @param $member
355
     * @return bool|int|void|null
356
     */
357
    public function canEdit($member)
358
    {
359
        if (!$member) {
360
            $member = Security::getCurrentUser();
361
        }
362
363
        return Permission::checkMember($member, 'MANAGE_FOXY_PRODUCTS');
364
    }
365
366
    /**
367
     * @param $member
368
     * @return bool|int|void
369
     */
370
    public function canDelete($member)
371
    {
372
        if (!$member) {
373
            $member = Security::getCurrentUser();
374
        }
375
376
        return Permission::checkMember($member, 'MANAGE_FOXY_PRODUCTS');
377
    }
378
379
    /**
380
     * @param null $member
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $member is correct as it would always require null to be passed?
Loading history...
381
     * @return bool|int
382
     */
383
    public function canUnpublish($member = null)
384
    {
385
        if (!$member) {
0 ignored issues
show
introduced by
$member is of type null, thus it always evaluated to false.
Loading history...
386
            $member = Security::getCurrentUser();
387
        }
388
389
        return Permission::checkMember($member, 'MANAGE_FOXY_PRODUCTS');
390
    }
391
392
    /**
393
     * @param $member
394
     * @return bool|int
395
     */
396
    public function canArchive($member = null)
397
    {
398
        if (!$member) {
399
            $member = Security::getCurrentUser();
400
        }
401
402
        return Permission::checkMember($member, 'MANAGE_FOXY_PRODUCTS');
403
    }
404
405
    /**
406
     *
407
     */
408
    public function onBeforeWrite()
409
    {
410
        // trim spaces and replace duplicate spaces
411
        $trimmed = trim($this->owner->Code);
412
        $this->owner->Code = preg_replace('/\s+/', ' ', $trimmed);
413
    }
414
}
415