Issues (89)

src/Domain/Service/Configuration.php (1 issue)

1
<?php
2
3
namespace ConferenceTools\Tickets\Domain\Service;
4
5
use ConferenceTools\Tickets\Domain\ValueObject\DiscountCode;
6
use ConferenceTools\Tickets\Domain\ValueObject\DiscountCodeMetadata;
7
use ConferenceTools\Tickets\Domain\ValueObject\Money;
8
use ConferenceTools\Tickets\Domain\ValueObject\Price;
9
use ConferenceTools\Tickets\Domain\ValueObject\TaxRate;
10
use ConferenceTools\Tickets\Domain\ValueObject\TicketMetadata;
11
use ConferenceTools\Tickets\Domain\ValueObject\TicketType;
12
use Zend\Stdlib\ArrayUtils;
13
14
class Configuration
15
{
16
    //@TODO change to private const
17
    private static $defaults = [
18
        'tickets' => [],
19
        'discountCodes' => [],
20
        'financial' => [
21
            'currency' => 'GBP',
22
            'taxRate' => 0,
23
            'displayTax' => false
24
        ]
25
    ];
26
27
    /**
28
     * Defines the currency in use across the app.
29
     *
30
     * Defaults to GBP, should be a proper currency code otherwise you will have issues with display of
31
     * currency values and creating stripe charges.
32
     *
33
     * config key: financial->currency
34
     *
35
     * @var string
36
     */
37
    private $currency;
38
39
    /**
40
     * The tax rate in use across the app.
41
     *
42
     * Defaults to 0% Will be added to all cost values for tickets. The app assumes a single tax rate for all tickets
43
     * and also assumes that tickets are for a physical event. As such the EU VATMOSS rules do not apply. If you are
44
     * selling tickets for a webinar or online conference, you should check with legal advisers if this is appropriate.
45
     *
46
     * config key: financial->taxRate
47
     *
48
     * @var TaxRate
49
     */
50
    private $taxRate;
51
52
    /**
53
     * Should VAT/sales tax be displayed in the app.
54
     *
55
     * Defaults to false. If enabled, this will display sales tax (VAT) in various points in the purchase process. You
56
     * should also update the layout template to include the relevent legal information. There are three ways this app
57
     * can deal with tax:
58
     * - if you are not vat registered or do not need to charge VAT, you can set this to false and the tax rate to 0;
59
     *   the app will not track any tax for you.
60
     * - if you set a tax rate but disable this flag, VAT will be added to purchases and tracked by the app but not
61
     *   made visible to customers. The main purpose of this is for when you have a pending VAT registration; the app
62
     *   will still track the tax and you can turn on the display of this tracking when the registration completes.
63
     *   Another use for this, if you don't need to track VAT would be to add a handling/processing fee to tickets.
64
     * - If you set a tax rate and enable this flag, VAT will be tracked and displayed to your customers at purchase
65
     *   time.
66
     *
67
     *
68
     * config key: financial->displayTax
69
     *
70
     * @var bool
71
     */
72
    private $displayTax;
73
74
    /**
75
     * An array of available ticket types. The app uses this for determining how many tickets are available and their
76
     * prices.
77
     *
78
     * If you change this config, you will need to rebuild the ticket counters projection to get the updated types in
79
     * your app.
80
     *
81
     * config key: tickets
82
     * structure: identifier => [
83
     *      'cost' => Net cost in pence/cents (eg before tax price),
84
     *      'name' => display name shown to customers,
85
     *      'available' => Number available for purchase
86
     * ]
87
     *
88
     * @var TicketType[]
89
     */
90
    private $ticketTypes;
91
92
    /**
93
     * Holds a count of avaliable tickets by type.
94
     *
95
     * @see ticketTypes for how to configure
96
     *
97
     * @var int[]
98
     */
99
    private $avaliableTickets;
100
101
    /**
102
     * An array of discount codes. The app uses this for validating and applying different codes.
103
     *
104
     * If you change this config, you will need to rebuild the discount codes projection
105
     *
106
     * configkey: discountCodes
107
     * structure: identifier => [
108
     *      'type' => The class name of the discount type eg Percentage::class,
109
     *      'name' => User friendly name for the code
110
     *      'options' => An array of options for the type you are using
111
     * ]
112
     *
113
     * @var DiscountCode[]
114
     */
115
    private $discountCodes = [];
116
117
    /**
118
     * Contains metadata about tickets eg when they are available for sale
119
     *
120
     * configkey: tickets->metadata
121
     * structure: [
122
     *      'availableFrom' => DateTime ticket is to go on sale from,
123
     *      'availableTo' => DateTime after which ticket is no longer on sale
124
     * ]
125
     *
126
     * @var TicketMetadata[]
127
     */
128
    private $ticketMetadata;
129
130
    /**
131
     * Contains metadata about discount codes eg when they are available for use
132
     *
133
     * configkey: discountCodes->metadata
134
     * structure: [
135
     *     'availableFrom' => DateTime code can be used from,
136
     *     'availableTo' => DateTime code expires at
137
     * ]
138
     *
139
     * @var DiscountCodeMetadata[]
140
     */
141
    private $discountCodeMetadata;
142
143
    private function __construct() {}
144
145
    public static function fromArray(array $settings)
146
    {
147
        /** Ensures that all the keys exist @TODO remove dependency on Zend Array Utils for this */
148
        $settings = ArrayUtils::merge(self::$defaults, $settings);
149
        $instance = new static();
150
151
        $instance->currency = (string) $settings['financial']['currency'];
152
        $instance->displayTax = (string) $settings['financial']['displayTax'];
0 ignored issues
show
Documentation Bug introduced by
The property $displayTax was declared of type boolean, but (string)$settings['financial']['displayTax'] is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
153
        $instance->taxRate = new TaxRate($settings['financial']['taxRate']);
154
155
        foreach ($settings['tickets'] as $identifier => $ticket) {
156
            $instance->addTicketInformation($ticket, $identifier);
157
        }
158
159
        foreach ($settings['discountCodes'] as $identifier => $code) {
160
            $instance->addDiscountCodeInformation($code, $identifier);
161
162
        }
163
164
        return $instance;
165
    }
166
167
    /**
168
     * @param $ticket
169
     * @param $identifier
170
     */
171
    private function addTicketInformation(array $ticket, string $identifier)
172
    {
173
        $price = Price::fromNetCost(
174
            new Money($ticket['cost'], $this->currency),
175
            $this->taxRate
176
        );
177
178
        $this->ticketTypes[$identifier] = new TicketType(
179
            $identifier,
180
            $price,
181
            $ticket['name'],
182
            $ticket['description'] ?? '',
183
            $ticket['supplementary'] ?? false
184
        );
185
186
        $this->avaliableTickets[$identifier] = $ticket['available'];
187
188
        $this->ticketMetadata[$identifier] = TicketMetadata::fromArray(
189
            $this->ticketTypes[$identifier],
190
            $ticket['metadata'] ?? []
191
        );
192
    }
193
194
    private function addDiscountCodeInformation(array $code, string $identifier)
195
    {
196
        // Be careful here; configuration object is still under constrcution at the time it is passed in
197
        // Might need to rethink this at some point
198
        $discountType = call_user_func([$code['type'], 'fromArray'], $code['options'], $this);
199
        $this->discountCodes[$identifier] = new DiscountCode(
200
            $identifier,
201
            $code['name'],
202
            $discountType
203
        );
204
205
        $this->discountCodeMetadata[$identifier] = DiscountCodeMetadata::fromArray(
206
            $this->discountCodes[$identifier],
207
            $code['metadata'] ?? []
208
        );
209
    }
210
211
    /**
212
     * @return string
213
     */
214 14
    public function getCurrency(): string
215
    {
216 14
        return $this->currency;
217
    }
218
219
    /**
220
     * @return TaxRate
221
     */
222 14
    public function getTaxRate(): TaxRate
223
    {
224 14
        return $this->taxRate;
225
    }
226
227
    /**
228
     * @return boolean
229
     */
230
    public function displayTax(): bool
231
    {
232
        return $this->displayTax;
233
    }
234
235
    /**
236
     * @return TicketType[]
237
     */
238
    public function getTicketTypes(): array
239
    {
240
        return $this->ticketTypes;
241
    }
242
243
    /**
244
     * @return TicketType
245
     */
246 3
    public function getTicketType(string $identifier): TicketType
247
    {
248 3
        return $this->ticketTypes[$identifier];
249
    }
250
251
    /**
252
     * @param string $identifier
253
     * @return int
254
     */
255
    public function getAvailableTickets(string $identifier): int
256
    {
257
        return $this->avaliableTickets[$identifier];
258
    }
259
260 4
    public function getDiscountCodes(): array
261
    {
262 4
        return $this->discountCodes;
263
    }
264
265 10
    public function getTicketMetadata(string $identifier): TicketMetadata
266
    {
267 10
        return $this->ticketMetadata[$identifier];
268
    }
269
270 4
    public function getDiscountCodeMetadata(string $code): DiscountCodeMetadata
271
    {
272 4
        return $this->discountCodeMetadata[$code];
273
    }
274
}