Passed
Pull Request — master (#110)
by Jason
02:32
created

Purchasable::setIsAvailable()   B

Complexity

Conditions 8
Paths 8

Size

Total Lines 25
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 8
eloc 13
c 1
b 0
f 0
nc 8
nop 0
dl 0
loc 25
rs 8.4444
1
<?php
2
3
namespace Dynamic\Foxy\Extension;
4
5
use Dynamic\Foxy\Model\FoxyCategory;
6
use Dynamic\Foxy\Model\Variation;
7
use Dynamic\Foxy\Model\VariationType;
8
use micschk\GroupableGridfield\GridFieldGroupable;
9
use SilverStripe\Dev\Deprecation;
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\Security\Permission;
27
use SilverStripe\Security\PermissionProvider;
28
use SilverStripe\Security\Security;
29
use Symbiote\GridFieldExtensions\GridFieldOrderableRows;
30
use Symbiote\GridFieldExtensions\GridFieldTitleHeader;
31
32
/**
33
 * Class Purchasable
34
 * @package Dynamic\Foxy\Extension
35
 *
36
 * @property double Price
37
 * @property string Code
38
 * @property string ReceiptTitle
39
 * @property bool Available
40
 * @property int $QuantityMax
41
 *
42
 * @property int FoxyCategoryID
43
 * @method FoxyCategory FoxyCategory()
44
 *
45
 * @method HasManyList Variations()
46
 *
47
 * @property-read DataObject|Purchasable $owner
48
 */
49
class Purchasable extends DataExtension implements PermissionProvider
50
{
51
    /**
52
     * @var
53
     */
54
    private $is_available;
55
56
    /**
57
     * @var array
58
     */
59
    private static $db = [
0 ignored issues
show
introduced by
The private property $db is not used, and could be removed.
Loading history...
60
        'Price' => 'Currency',
61
        'Code' => 'Varchar(100)',
62
        'ReceiptTitle' => 'HTMLVarchar(255)',
63
        'Available' => 'Boolean',
64
        'QuantityMax' => 'Int',
65
    ];
66
67
    /**
68
     * @var array
69
     */
70
    private static $has_one = [
0 ignored issues
show
introduced by
The private property $has_one is not used, and could be removed.
Loading history...
71
        'FoxyCategory' => FoxyCategory::class,
72
    ];
73
74
    /**
75
     * @var string[]
76
     */
77
    private static $has_many = [
0 ignored issues
show
introduced by
The private property $has_many is not used, and could be removed.
Loading history...
78
        'Variations' => Variation::class,
79
    ];
80
81
    /**
82
     * @var array
83
     */
84
    private static $indexes = [
0 ignored issues
show
introduced by
The private property $indexes is not used, and could be removed.
Loading history...
85
        'Code' => [
86
            'type' => 'unique',
87
            'columns' => ['Code'],
88
        ],
89
    ];
90
91
    /**
92
     * @var array
93
     */
94
    private static $defaults = [
0 ignored issues
show
introduced by
The private property $defaults is not used, and could be removed.
Loading history...
95
        'ShowInMenus' => false,
96
        'Available' => true,
97
    ];
98
99
    /**
100
     * @var array
101
     */
102
    private static $summary_fields = [
0 ignored issues
show
introduced by
The private property $summary_fields is not used, and could be removed.
Loading history...
103
        'Image.CMSThumbnail',
104
        'Title',
105
        'Code',
106
        'Price.Nice',
107
    ];
108
109
    /**
110
     * @var array
111
     */
112
    private static $searchable_fields = [
0 ignored issues
show
introduced by
The private property $searchable_fields is not used, and could be removed.
Loading history...
113
        'Title',
114
        'Code',
115
        'Available',
116
    ];
117
118
    /**
119
     * @param array $labels
120
     */
121
    public function updateFieldLabels(&$labels)
122
    {
123
        $labels['Title'] = _t(__CLASS__ . '.TitleLabel', 'Product Name');
124
        $labels['Code'] = _t(__CLASS__ . '.CodeLabel', 'Code');
125
        $labels['Price'] = _t(__CLASS__ . '.PriceLabel', 'Price');
126
        $labels['Price.Nice'] = _t(__CLASS__ . '.PriceLabel', 'Price');
127
        $labels['Available'] = _t(__CLASS__ . '.AvailableLabel', 'Available for purchase');
128
        $labels['Available.Nice'] = _t(__CLASS__ . '.AvailableLabelNice', 'Available');
129
        $labels['Image.CMSThumbnail'] = _t(__CLASS__ . '.ImageLabel', 'Image');
130
        $labels['ReceiptTitle'] = _t(__CLASS__ . '.ReceiptTitleLabel', 'Product title for receipt');
131
        $labels['FoxyCategoryID'] = _t(__CLASS__ . '.FoxyCategoryLabel', 'Foxy Category');
132
    }
133
134
    /**
135
     * @param FieldList $fields
136
     */
137
    public function updateCMSFields(FieldList $fields)
138
    {
139
        $fields->removeByName([
140
            'SKU',
141
        ]);
142
143
        $fields->addFieldsToTab(
144
            'Root.Ecommerce',
145
            [
146
                CurrencyField::create('Price')
147
                    ->setDescription(_t(
148
                        __CLASS__ . '.PriceDescription',
149
                        'Base price for this product. Can be modified using Product variations'
150
                    )),
151
                TextField::create('Code')
152
                    ->setDescription(_t(
153
                        __CLASS__ . '.CodeDescription',
154
                        'Required, must be unique. Product identifier used by FoxyCart in transactions. All leading and trailing spaces are removed on save.'
155
                    )),
156
                DropdownField::create('FoxyCategoryID')
157
                    ->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

157
                    ->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...
158
                    ->setSource(FoxyCategory::get()->map())
159
                    ->setDescription(_t(
160
                        __CLASS__ . '.FoxyCategoryDescription',
161
                        'Required. Must also exist in
162
                        <a href="https://admin.foxycart.com/admin.php?ThisAction=ManageProductCategories"
163
                            target="_blank">
164
                            Foxy Categories
165
                        </a>.
166
                        Used to set category specific options like shipping and taxes. Managed in Foxy > Categories'
167
                    ))
168
                    ->setEmptyString(''),
169
                TextField::create('ReceiptTitle')
170
                    ->setDescription(_t(
171
                        __CLASS__ . '.ReceiptTitleDescription',
172
                        'Optional. Alternate title to display on order receipt'
173
                    )),
174
            ],
175
            'Content'
176
        );
177
178
        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

178
        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...
179
            $variationsConfig = GridFieldConfig_RelationEditor::create()
180
                ->removeComponentsByType([
181
                    GridFieldAddExistingAutocompleter::class,
182
                    GridFieldPaginator::class,
183
                    GridFieldPageCount::class,
184
                    GridFieldSortableHeader::class,
185
                ])
186
                ->addComponents([
187
                    new GridFieldOrderableRows('SortOrder'),
188
                    new GridFieldTitleHeader(),
189
                    new GridFieldGroupable(
190
                        'VariationTypeID',    // The fieldname to set the Group
191
                        'Variation Type',   // A description of the function of the group
192
                        'none',         // A title/header for items without a group/unassigned
193
                        VariationType::get()->sort('SortOrder')->map()->toArray()
194
                    )
195
                ]);
196
197
            $fields->addFieldToTab(
198
                'Root.Variations',
199
                GridField::create(
200
                    'Variations',
201
                    'Variations',
202
                    $this->owner->Variations(),
203
                    $variationsConfig
204
                )
205
            );
206
        }
207
208
        $fields->addFieldsToTab(
209
            'Root.Inventory',
210
            [
211
                NumericField::create('QuantityMax')
212
                    ->setTitle('Maximum quantity allowed in the cart')
213
                    ->setDescription('For unlimited enter 0')
214
                    ->addExtraClass('stacked'),
215
                CheckboxField::create('Available')
216
                    ->setDescription(_t(
217
                        __CLASS__ . '.AvailableDescription',
218
                        'If unchecked, will remove "Add to Cart" form and instead display "Currently unavailable"'
219
                    )),
220
            ]
221
        );
222
    }
223
224
    /**
225
     * @return RequiredFields
226
     */
227
    public function getCMSValidator()
228
    {
229
        return new RequiredFields([
230
            "Price",
231
            "Code",
232
            "FoxyCategoryID",
233
        ]);
234
    }
235
236
    /**
237
     * @return $this
238
     */
239
    public function setIsAvailable()
240
    {
241
        $available = true;
242
243
        if (!$this->owner->Available) {
244
            $available = false;
245
        }
246
247
        if ($available && !$this->owner->Variations()->count()) {
248
            $available = true;
249
        }
250
251
        if ($available && $this->owner->Variations()->count()) {
252
            $available = false;
253
            foreach ($this->owner->Variations() as $variation) {
254
                if ($variation->Available) {
255
                    $available = true;
256
                }
257
            }
258
        }
259
260
        $this->owner->extend('updateSetIsAvailable', $available);
0 ignored issues
show
Bug introduced by
The method extend() 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

260
        $this->owner->/** @scrutinizer ignore-call */ 
261
                      extend('updateSetIsAvailable', $available);

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...
261
262
        $this->is_available = $available;
263
        return $this;
264
    }
265
266
    /**
267
     * @return mixed
268
     */
269
    public function getIsAvailable()
270
    {
271
        if (!isset($this->is_available)) {
272
            $this->setIsAvailable();
273
        }
274
        return $this->is_available;
275
    }
276
277
    /**
278
     * @return mixed
279
     */
280
    public function isAvailable()
281
    {
282
        Deprecation::notice('1.4', 'Use getIsAvailable() instead');
283
        return $this->getIsAvailable();
284
    }
285
286
    /**
287
     * @return bool
288
     */
289
    public function isProduct()
290
    {
291
        return true;
292
    }
293
294
    /**
295
     * @return array
296
     */
297
    public function providePermissions()
298
    {
299
        return [
300
            'MANAGE_FOXY_PRODUCTS' => [
301
                'name' => _t(
302
                    __CLASS__ . '.PERMISSION_MANAGE_PRODUCTS_DESCRIPTION',
303
                    'Manage products'
304
                ),
305
                'category' => _t(
306
                    __CLASS__ . '.PERMISSIONS_CATEGORY',
307
                    'Foxy'
308
                ),
309
                'help' => _t(
310
                    __CLASS__ . '.PERMISSION_MANAGE_PRODUCTS_HELP',
311
                    'Manage products and related settings'
312
                ),
313
                'sort' => 400,
314
            ],
315
        ];
316
    }
317
318
    /**
319
     * @param $member
320
     * @return bool|int|void
321
     */
322
    public function canCreate($member)
323
    {
324
        if (!$member) {
325
            $member = Security::getCurrentUser();
326
        }
327
328
        return Permission::checkMember($member, 'MANAGE_FOXY_PRODUCTS');
329
    }
330
331
    /**
332
     * @param $member
333
     * @return bool|int|void|null
334
     */
335
    public function canEdit($member)
336
    {
337
        if (!$member) {
338
            $member = Security::getCurrentUser();
339
        }
340
341
        return Permission::checkMember($member, 'MANAGE_FOXY_PRODUCTS');
342
    }
343
344
    /**
345
     * @param $member
346
     * @return bool|int|void
347
     */
348
    public function canDelete($member)
349
    {
350
        if (!$member) {
351
            $member = Security::getCurrentUser();
352
        }
353
354
        return Permission::checkMember($member, 'MANAGE_FOXY_PRODUCTS');
355
    }
356
357
    /**
358
     * @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...
359
     * @return bool|int
360
     */
361
    public function canUnpublish($member = null)
362
    {
363
        if (!$member) {
0 ignored issues
show
introduced by
$member is of type null, thus it always evaluated to false.
Loading history...
364
            $member = Security::getCurrentUser();
365
        }
366
367
        return Permission::checkMember($member, 'MANAGE_FOXY_PRODUCTS');
368
    }
369
370
    /**
371
     * @param $member
372
     * @return bool|int
373
     */
374
    public function canArchive($member = null)
375
    {
376
        if (!$member) {
377
            $member = Security::getCurrentUser();
378
        }
379
380
        return Permission::checkMember($member, 'MANAGE_FOXY_PRODUCTS');
381
    }
382
383
    /**
384
     *
385
     */
386
    public function onBeforeWrite()
387
    {
388
        // trim spaces and replace duplicate spaces
389
        $trimmed = trim($this->owner->Code);
390
        $this->owner->Code = preg_replace('/\s+/', ' ', $trimmed);
391
    }
392
}
393