Completed
Push — master ( 2e277e...a1c438 )
by Bram
02:51
created

Discount::onBeforeWrite()   C

Complexity

Conditions 8
Paths 16

Size

Total Lines 25
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 25
rs 5.3846
c 0
b 0
f 0
cc 8
eloc 13
nc 16
nop 0
1
<?php
2
/**
3
 * Discount.php
4
 *
5
 * @author Bram de Leeuw
6
 * Date: 30/03/17
7
 */
8
9
namespace Broarm\EventTickets;
10
11
use CalendarEvent;
12
use Currency;
13
use DateField;
14
use DropdownField;
15
use Group;
16
use ManyManyList;
17
use Member;
18
use NumericField;
19
use ReadonlyField;
20
use SS_Datetime;
21
use TagField;
22
use TextareaField;
23
use TextField;
24
25
/**
26
 * Class Discount
27
 *
28
 * @property string Code
29
 * @property string ValidFrom
30
 * @property string ValidTill
31
 * @property float  Amount
32
 * @property int    Uses
33
 * @property bool   Used
34
 * @property string DiscountType
35
 * @method ManyManyList Groups()
36
 * @method ManyManyList Events()
37
 * @method ManyManyList Reservations()
38
 */
39
class Discount extends PriceModifier
40
{
41
    const PRICE = 'PRICE';
42
    const PERCENTAGE = 'PERCENTAGE';
43
44
    private static $singular_name = 'Discount';
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $singular_name is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
45
46
    private static $db = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $db is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
47
        'Description' => 'Text',
48
        'Amount' => 'Decimal',
49
        'Uses' => 'Int',
50
        'DiscountType' => 'Enum("PRICE,PERCENTAGE","PRICE")',
51
        'Code' => 'Varchar(255)',
52
        'ValidFrom' => 'SS_Datetime',
53
        'ValidTill' => 'SS_Datetime'
54
    );
55
56
    private static $default_sort = "ValidFrom DESC";
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $default_sort is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
57
58
    private static $many_many = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $many_many is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
59
        'Groups' => 'Group',
60
        'Events' => 'CalendarEvent'
61
    );
62
63
    private static $indexes = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $indexes is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
64
        'Code' => 'unique("Code")'
65
    );
66
67
    private static $summary_fields = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $summary_fields is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
68
        'Code' => 'Code',
69
        'Description' => 'Description',
70
        'ValidFrom.Nice' => 'Valid from',
71
        'ValidTill.Nice' => 'Valid till',
72
        'Reservations.Count' => 'Uses'
73
    );
74
75
    private static $defaults = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $defaults is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
76
        'Uses' => 1
77
    );
78
79
    /**
80
     * Create the needed cms fields
81
     *
82
     * @return \FieldList
83
     */
84
    public function getCMSFields()
85
    {
86
        $fields = parent::getCMSFields();
87
88
        $types = $this->dbObject('DiscountType')->enumValues();
89
90
        $fields->addFieldsToTab('Root.Main', array(
91
            $code = TextField::create('Code', 'Code'),
92
            TextareaField::create('Description', 'Description')->setDescription('The description is only visible in the cms'),
93
            DropdownField::create('DiscountType', _t('Discount.TYPE', 'Type of discount'), $types),
94
            NumericField::create('Amount', _t('Discount.AMOUNT', 'Amount')),
95
            NumericField::create('Uses', _t('Discount.USES', 'Maximum number of uses')),
96
            $validFrom = DateField::create('ValidFrom', _t('Discount.VALID_FROM', 'Valid from')),
97
            $validTill = DateField::create('ValidTill', _t('Discount.VALID_TILL', 'Valid till')),
98
            TagField::create('Groups', _t('Discount.GROUPS', 'Constrain to groups'), Group::get()),
99
            TagField::create('Events', _t('Discount.EVENTS', 'Constrain to events'), CalendarEvent::get())
100
        ));
101
102
        $code->setDescription(
103
            _t('Discount.CODE_HELP', 'The code is generated after saving')
104
        );
105
106
        $validFrom
107
            ->setConfig('showcalendar', true)
108
            ->setDescription(_t('Discount.VALID_FROM_HELP', 'If no date is set the current date is used'));
109
        $validTill
110
            ->setConfig('showcalendar', true)
111
            ->setDescription(_t('Discount.VALID_TILL_HELP', 'If no date is set the current date + 1 year is used'));
112
113
        $fields->removeByName(array('Title'));
114
        return $fields;
115
    }
116
117
    public function onBeforeWrite()
118
    {
119
        // Generate or validate the set code
120
        if (empty($this->Code)) {
121
            $this->Code = $this->generateCode();
122
        } elseif (empty($this->Title) && $codes = self::get()->filter('Code:PartialMatch', $this->Code)) {
123
            if ($codes->count() >= 1) {
124
                $this->Code .= "-{$codes->count()}";
125
            }
126
        }
127
128
        // Set the title
129
        if (empty($this->Title)) {
130
            $this->Title = $this->Code;
131
        }
132
133
        // Set the default dates
134
        if (empty($this->ValidFrom) && empty($this->ValidTill)) {
135
            $format = 'Y-m-d';
136
            $this->ValidFrom = $start = date($format);
137
            $this->ValidTill = date($format, strtotime("$start + 1 year"));
138
        }
139
140
        parent::onBeforeWrite();
141
    }
142
143
    /**
144
     * Return the table title
145
     *
146
     * @return string
147
     */
148
    public function getTableTitle()
149
    {
150
        return _t('Discount.DISCOUNT', 'Discount');
151
    }
152
153
    /**
154
     * Check if the discount exceeded the maximum uses
155
     *
156
     * @return bool
157
     */
158
    public function validateUses()
159
    {
160
        return $this->Reservations()->count() <= $this->Uses;
161
    }
162
163
    /**
164
     * Calculate the discount
165
     *
166
     * @param $total
167
     */
168
    public function updateTotal(&$total)
169
    {
170
        switch ($this->DiscountType) {
171
            case self::PERCENTAGE:
172
                $discount = ($total / 100 * $this->Amount);
173
                $total -= $discount;
174
                break;
175
            default: // case price
176
                $discount = $this->Amount;
177
                $total -= $discount;
178
                $total = $total > 0 ? $total : 0;
179
                break;
180
        }
181
182
        // save the modification on the join
183
        $this->setPriceModification($discount);
184
    }
185
186
    /**
187
     * Check if the from and till dates are in the past and future
188
     *
189
     * @return bool
190
     */
191
    public function validateDate()
192
    {
193
        /** @var SS_Datetime $from */
194
        $from = $this->dbObject('ValidFrom');
195
        /** @var SS_Datetime $till */
196
        $till = $this->dbObject('ValidTill');
197
198
        return (bool)($from->InPast() && $till->InFuture());
199
    }
200
201
    /**
202
     * Validate the given member with the allowed groups
203
     *
204
     * @param Member $member
0 ignored issues
show
Documentation introduced by
Should the type for parameter $member not be null|Member?

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

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

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

Loading history...
205
     *
206
     * @return bool
207
     */
208
    public function validateGroups(Member $member = null)
209
    {
210
        // If groups are attached to the discount, check if valid
211
        if ($this->Groups()->exists()) {
212
            if (empty($member)) {
213
                return false;
214
            } else {
215
                $validGroups = $this->Groups()->column('ID');
216
                $groupMembers = Member::get()->filter('Groups.ID:ExactMatchMulti', $validGroups);
217
                return (bool)$groupMembers->find('ID', $member->ID);
218
            }
219
        }
220
221
        return true;
222
    }
223
224
    /**
225
     * Validate if the given event is in the group of allowed events
226
     *
227
     * @param CalendarEvent $event
228
     *
229
     * @return bool
230
     */
231
    public function validateEvents(CalendarEvent $event)
232
    {
233
        // If events are attached to the discount, check if valid
234
        if ($this->Events()->exists()) {
235
            if (empty($event)) {
236
                return false;
237
            } else {
238
                $validEvents = $this->Events()->column('ID');
239
                return in_array($event->ID, $validEvents);
240
            }
241
        }
242
243
        return true;
244
    }
245
246
    /**
247
     * Generate a unique coupon code
248
     *
249
     * @return string
250
     */
251
    public function generateCode()
252
    {
253
        return uniqid($this->ID);
254
    }
255
}
256