Invoice   A
last analyzed

Complexity

Total Complexity 40

Size/Duplication

Total Lines 323
Duplicated Lines 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 40
eloc 70
c 3
b 0
f 0
dl 0
loc 323
rs 9.2

24 Methods

Rating   Name   Duplication   Size   Complexity  
A hasDiscount() 0 4 3
A rawStartingBalance() 0 4 2
A invoiceItemsByType() 0 13 4
A total() 0 3 1
A date() 0 5 2
A discount() 0 3 1
A discountIsPercentage() 0 3 2
A startingBalance() 0 3 1
A amountOff() 0 6 2
A subscriptions() 0 3 1
A download() 0 11 1
A subtotal() 0 4 1
A view() 0 5 1
A formatAmount() 0 3 1
A pdf() 0 17 3
A coupon() 0 4 2
A rawTotal() 0 3 1
A percentOff() 0 7 2
A __get() 0 3 1
A hasStartingBalance() 0 3 1
A invoiceItems() 0 3 1
A getView() 0 16 4
A asStripeInvoice() 0 3 1
A __construct() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like Invoice often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Invoice, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Phalcon\Cashier;
4
5
use DOMPDF;
6
use Carbon\Carbon;
7
use Phalcon\Mvc\View;
8
use Stripe\Invoice as StripeInvoice;
9
use Phalcon\Http\Response;
10
use Phalcon\Di\FactoryDefault;
0 ignored issues
show
Bug introduced by
The type Phalcon\Di\FactoryDefault was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
11
12
class Invoice
13
{
14
    /**
15
     * The user instance.
16
     */
17
    protected $user;
18
19
    /**
20
     * The Stripe invoice instance.
21
     *
22
     * @var \Stripe\Invoice
23
     */
24
    protected $invoice;
25
26
    /**
27
     * Create a new invoice instance.
28
     *
29
     * @param  Model           $user
0 ignored issues
show
Bug introduced by
The type Phalcon\Cashier\Model was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
30
     * @param  \Stripe\Invoice $invoice
31
     * @return void
32
     */
33
    public function __construct($user, StripeInvoice $invoice)
34
    {
35
        $this->user = $user;
36
        $this->invoice = $invoice;
37
    }
38
39
    /**
40
     * Get a Carbon date for the invoice.
41
     *
42
     * @param  \DateTimeZone|string $timezone
43
     * @return \Carbon\Carbon
44
     */
45
    public function date($timezone = null)
46
    {
47
        $carbon = Carbon::createFromTimestamp($this->invoice->date);
48
49
        return $timezone ? $carbon->setTimezone($timezone) : $carbon;
50
    }
51
52
    /**
53
     * Get the total amount that was paid (or will be paid).
54
     *
55
     * @return string
56
     */
57
    public function total()
58
    {
59
        return $this->formatAmount($this->rawTotal());
0 ignored issues
show
Bug introduced by
$this->rawTotal() of type double is incompatible with the type integer expected by parameter $amount of Phalcon\Cashier\Invoice::formatAmount(). ( Ignorable by Annotation )

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

59
        return $this->formatAmount(/** @scrutinizer ignore-type */ $this->rawTotal());
Loading history...
60
    }
61
62
    /**
63
     * Get the raw total amount that was paid (or will be paid).
64
     *
65
     * @return float
66
     */
67
    public function rawTotal()
68
    {
69
        return max(0, $this->invoice->total - ($this->rawStartingBalance() * -1));
70
    }
71
72
    /**
73
     * Get the total of the invoice (before discounts).
74
     *
75
     * @return string
76
     */
77
    public function subtotal()
78
    {
79
        return $this->formatAmount(
80
            max(0, $this->invoice->subtotal - $this->rawStartingBalance())
81
        );
82
    }
83
84
    /**
85
     * Determine if the account had a starting balance.
86
     *
87
     * @return bool
88
     */
89
    public function hasStartingBalance()
90
    {
91
        return $this->rawStartingBalance() > 0;
92
    }
93
94
    /**
95
     * Get the starting balance for the invoice.
96
     *
97
     * @return string
98
     */
99
    public function startingBalance()
100
    {
101
        return $this->formatAmount($this->rawStartingBalance());
0 ignored issues
show
Bug introduced by
$this->rawStartingBalance() of type double is incompatible with the type integer expected by parameter $amount of Phalcon\Cashier\Invoice::formatAmount(). ( Ignorable by Annotation )

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

101
        return $this->formatAmount(/** @scrutinizer ignore-type */ $this->rawStartingBalance());
Loading history...
102
    }
103
104
    /**
105
     * Determine if the invoice has a discount.
106
     *
107
     * @return bool
108
     */
109
    public function hasDiscount()
110
    {
111
        return $this->invoice->subtotal > 0 && $this->invoice->subtotal != $this->invoice->total
112
        && ! is_null($this->invoice->discount);
113
    }
114
115
    /**
116
     * Get the discount amount.
117
     *
118
     * @return string
119
     */
120
    public function discount()
121
    {
122
        return $this->formatAmount($this->invoice->subtotal - $this->invoice->total);
123
    }
124
125
    /**
126
     * Get the coupon code applied to the invoice.
127
     *
128
     * @return string|null
129
     */
130
    public function coupon()
131
    {
132
        if (isset($this->invoice->discount)) {
133
            return $this->invoice->discount->coupon->id;
134
        }
135
    }
136
137
    /**
138
     * Determine if the discount is a percentage.
139
     *
140
     * @return bool
141
     */
142
    public function discountIsPercentage()
143
    {
144
        return $this->coupon() && isset($this->invoice->discount->coupon->percent_off);
145
    }
146
147
    /**
148
     * Get the discount percentage for the invoice.
149
     *
150
     * @return int
151
     */
152
    public function percentOff()
153
    {
154
        if ($this->coupon()) {
155
            return $this->invoice->discount->coupon->percent_off;
156
        }
157
158
        return 0;
159
    }
160
161
    /**
162
     * Get the discount amount for the invoice.
163
     *
164
     * @return string
165
     */
166
    public function amountOff()
167
    {
168
        if (isset($this->invoice->discount->coupon->amount_off)) {
169
            return $this->formatAmount($this->invoice->discount->coupon->amount_off);
170
        } else {
171
            return $this->formatAmount(0);
172
        }
173
    }
174
175
    /**
176
     * Get all of the "invoice item" line items.
177
     *
178
     * @return array
179
     */
180
    public function invoiceItems()
181
    {
182
        return $this->invoiceItemsByType('invoiceitem');
183
    }
184
185
    /**
186
     * Get all of the "subscription" line items.
187
     *
188
     * @return array
189
     */
190
    public function subscriptions()
191
    {
192
        return $this->invoiceItemsByType('subscription');
193
    }
194
195
    /**
196
     * Get all of the invoie items by a given type.
197
     *
198
     * @param  string $type
199
     * @return array
200
     */
201
    public function invoiceItemsByType($type)
202
    {
203
        $lineItems = [];
204
205
        if (isset($this->lines->data)) {
0 ignored issues
show
Bug Best Practice introduced by
The property lines does not exist on Phalcon\Cashier\Invoice. Since you implemented __get, consider adding a @property annotation.
Loading history...
206
            foreach ($this->lines->data as $line) {
207
                if ($line->type == $type) {
208
                    $lineItems[] = new InvoiceItem($this->user, $line);
209
                }
210
            }
211
        }
212
213
        return $lineItems;
214
    }
215
216
    /**
217
     * Format the given amount into a string based on the user's preferences.
218
     *
219
     * @param  int $amount
220
     * @return string
221
     */
222
    protected function formatAmount($amount)
223
    {
224
        return Cashier::formatAmount($amount);
225
    }
226
227
    /**
228
     * Get the View instance for the invoice.
229
     *
230
     * @param array $data
231
     */
232
    public function view(array $data)
233
    {
234
        $data = array_merge($data, ['invoice' => $this, 'user' => $this->user]);
235
        $view = $this->getView();
236
        return $view->render('cashier/receipt', $data);
237
    }
238
239
    /**
240
     * Return a {@link \Phalcon\Mvc\View\Simple} instance
241
     *
242
     * @return \Phalcon\Mvc\View\Simple
0 ignored issues
show
Bug introduced by
The type Phalcon\Mvc\View\Simple was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
243
     */
244
    public function getView()
245
    {
246
        $di = FactoryDefault::getDefault();
247
        if (!$this->view) {
248
            $viewApp = $di->get('view');
249
            if (!($viewsDir = $di->get('config')['viewDir'])) {
250
                $viewsDir = $viewApp->getViewsDir();
251
            }
252
            $view = $di->get('\Phalcon\Mvc\View\Simple');
253
            $view->setViewsDir($viewsDir);
254
            if ($engines = $viewApp->getRegisteredEngines()) {
255
                $view->registerEngines($engines);
256
            }
257
            $this->view = $view;
0 ignored issues
show
Bug Best Practice introduced by
The property view does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
258
        }
259
        return $this->view;
260
    }
261
262
    /**
263
     * Capture the invoice as a PDF and return the raw bytes.
264
     *
265
     * @param  array $data
266
     * @return string
267
     */
268
    public function pdf(array $data)
269
    {
270
        if (! defined('DOMPDF_ENABLE_AUTOLOAD')) {
271
            define('DOMPDF_ENABLE_AUTOLOAD', false);
272
        }
273
        $path = $_SERVER['DOCUMENT_ROOT'];
274
        if (file_exists($configPath = dirname($path) . '/vendor/dompdf/dompdf/dompdf_config.inc.php')) {
275
            include_once $configPath;
276
        }
277
278
        $dompdf = new DOMPDF;
279
280
        $dompdf->load_html($this->view($data));
281
282
        $dompdf->render();
283
284
        return $dompdf->output();
285
    }
286
287
    /**
288
     * Create an invoice download response.
289
     *
290
     * @param array $data
291
     */
292
    public function download(array $data)
293
    {
294
        $filename = $data['product'].'_'.$this->date()->month.'_'.$this->date()->year.'.pdf';
295
296
        $response = new Response();
297
        $response->setHeader('Content-Description', 'File Transfer');
298
        $response->setHeader('Content-Disposition', 'attachment; filename="'.$filename.'"');
299
        $response->setStatusCode(200, 'OK');
300
        $response->setContent($this->pdf($data));
301
        $response->setContentType('application/pdf');
302
        return $response->send();
303
    }
304
305
    /**
306
     * Get the raw starting balance for the invoice.
307
     *
308
     * @return float
309
     */
310
    public function rawStartingBalance()
311
    {
312
        return isset($this->invoice->starting_balance)
313
            ? $this->invoice->starting_balance : 0;
314
    }
315
316
    /**
317
     * Get the Stripe invoice instance.
318
     *
319
     * @return \Stripe\Invoice
320
     */
321
    public function asStripeInvoice()
322
    {
323
        return $this->invoice;
324
    }
325
326
    /**
327
     * Dynamically get values from the Stripe invoice.
328
     *
329
     * @param  string $key
330
     * @return mixed
331
     */
332
    public function __get($key)
333
    {
334
        return $this->invoice->{$key};
335
    }
336
}
337