Passed
Push — master ( 45eca1...10ee28 )
by Thomas
07:06
created

InvoiceController::store()   D

Complexity

Conditions 15
Paths 240

Size

Total Lines 117
Code Lines 79

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 15
eloc 79
c 3
b 0
f 0
nc 240
nop 1
dl 0
loc 117
rs 4.1247

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace App\Http\Controllers;
4
5
use App\Interfaces\InvoicingInterface;
6
use App\Models\Book;
7
use App\Models\Comment;
8
use App\Models\Config;
9
use App\Models\Discount;
10
use App\Models\Enrollment;
11
use App\Models\Fee;
12
use App\Models\Invoice;
13
use App\Models\InvoiceDetail;
14
use App\Models\InvoiceType;
15
use App\Models\Payment;
16
use App\Models\Paymentmethod;
17
use App\Models\ScheduledPayment;
18
use App\Models\Tax;
19
use Carbon\Carbon;
20
use Exception;
21
use Illuminate\Http\Request;
22
use Illuminate\Support\Facades\App;
23
use Illuminate\Support\Facades\Log;
24
use LaravelDaily\Invoices\Classes\Buyer;
25
use LaravelDaily\Invoices\Classes\InvoiceItem;
26
use LaravelDaily\Invoices\Invoice as InvoiceAlias;
27
28
class InvoiceController extends Controller
29
{
30
    public function __construct(public InvoicingInterface $invoicingService)
31
    {
32
        parent::__construct();
33
        $this->middleware(['permission:enrollments.edit']);
34
    }
35
36
    public function accountingServiceStatus()
37
    {
38
        return $this->invoicingService->status();
39
    }
40
41
    public function create()
42
    {
43
        $data = ['enrollment' => null, 'products' => [], 'invoicetypes' => InvoiceType::all(), 'clients' => [], 'availableBooks' => Book::all(), 'availableFees' => Fee::all(), 'availableDiscounts' => Discount::all(), 'availableTaxes' => Tax::all(), 'availablePaymentMethods' => Paymentmethod::all()];
44
        if (config('invoicing.price_categories_enabled')) {
45
            abort(403, 'Unable to create an invoice because price categories are enabled in your setup.');
46
        }
47
48
        return view('carts.show', $data);
49
    }
50
51
    /**
52
     * Create a invoice based on the cart contents for the specified user
53
     * Receive in the request: the user ID + the invoice data.
54
     */
55
    public function store(Request $request)
56
    {
57
58
        // receive the client data and create a invoice with status = pending
59
        $invoice = Invoice::create([
60
            'client_name' => $request->client_name,
61
            'client_idnumber' => $request->client_idnumber,
62
            'client_address' => $request->client_address,
63
            'client_email' => $request->client_email,
64
            'client_phone' => $request->client_phone,
65
            'invoice_type_id' => $request->invoicetype,
66
            'receipt_number' => $request->receiptnumber,
67
            'date' => $request->has('date') ? Carbon::parse($request->date) : Carbon::now(),
68
        ]);
69
70
        $invoice->setNumber(); // TODO extract this to model events.
71
72
        // persist the products
73
        foreach ($request->products as $f => $product) {
74
            $productType = match ($product['type']) {
75
                'enrollment' => Enrollment::class,
76
                'scheduledPayment' => ScheduledPayment::class,
77
                'fee' => Fee::class,
78
                'book' => Book::class,
79
            };
80
81
            $productFinalPrice = 0; // used to compute the final price with taxes and discounts
82
83
            $productFinalPrice += $product['price'] * ($product['quantity'] ?? 1) * 100;
84
85
            // The front end sends the discounts value as percent, but  for the invoice we want to store their actual value relative to the product they were applied on
86
            if (isset($product['discounts'])) {
87
                foreach ($product['discounts'] as $d => $discount) {
88
                    InvoiceDetail::create([
89
                        'invoice_id' => $invoice->id,
90
                        'product_name' => $discount['name'],
91
                        'product_id' => $discount['id'],
92
                        'product_type' => Discount::class,
93
                        'price' => -($discount['value'] / 100) * $product['price'] * ($product['quantity'] ?? 1),
94
                    ]);
95
96
                    $productFinalPrice -= (($discount['value']) * $product['price']) * ($product['quantity'] ?? 1); // no need to multiply by 100 because discount is in %
97
                }
98
            }
99
100
            if (isset($product['taxes'])) {
101
                foreach ($product['taxes'] as $d => $tax) {
102
                    $productFinalPrice += (($tax['value']) * $product['price']) * ($product['quantity'] ?? 1); // no need to multiply by 100 because discount is in %
103
104
                    InvoiceDetail::create([
105
                        'invoice_id' => $invoice->id,
106
                        'product_name' => $tax['name'],
107
                        'product_id' => $tax['id'],
108
                        'product_type' => Tax::class,
109
                        'price' => $product['price'] * ($tax['value'] / 100) * ($product['quantity'] ?? 1),
110
                    ]);
111
                }
112
            }
113
114
            InvoiceDetail::create([
115
                'invoice_id' => $invoice->id,
116
                'product_name' => $product['name'],
117
                'product_code' => $product['product_code'],
118
                'product_id' => $product['id'],
119
                'product_type' => $productType,
120
                'price' => $product['price'],
121
                'final_price' => $productFinalPrice,
122
                'quantity' => $product['quantity'] ?? 1,
123
            ]);
124
        }
125
126
        foreach ($request->payments as $p => $payment) {
127
            Payment::create([
128
                'responsable_id' => backpack_user()->id,
0 ignored issues
show
Bug introduced by
Accessing id on the interface Illuminate\Contracts\Auth\Authenticatable suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
129
                'invoice_id' => $invoice->id,
130
                'payment_method' => $payment['method'] ?? null,
131
                'value' => $payment['value'],
132
                'date' => isset($payment['date']) ? Carbon::parse($payment['date']) : Carbon::now(),
133
            ]);
134
        }
135
136
        // send the details to Accounting
137
        // and receive and store the invoice number
138
        if ($request->sendinvoice == true && config('invoicing.invoicing_system')) {
139
            try {
140
                $invoiceNumber = $this->invoicingService->saveInvoice($invoice);
141
                Log::info($invoiceNumber);
142
                if ($invoiceNumber !== null) {
143
                    $invoice->receipt_number = $invoiceNumber;
144
                    $invoice->save();
145
                    $success = true;
146
                } else {
147
                    Invoice::where('id', $invoice->id)->delete();
148
                    abort(500);
149
                }
150
            } catch (Exception $exception) {
151
                Log::error('Data could not be sent to accounting');
152
                Log::error($exception);
153
            }
154
        } else {
155
            $success = true;
156
        }
157
        if (isset($success)) {
158
159
            $this->ifTheInvoiceIsFullyPaidMarkItsProductsAsSuch($invoice);
160
161
            if (isset($request->comment)) {
162
                Comment::create([
163
                    'commentable_id' => $invoice->id,
164
                    'commentable_type' => Invoice::class,
165
                    'body' => $request->comment,
166
                    'author_id' => backpack_user()->id,
167
                ]);
168
            }
169
        } else {
170
            Invoice::where('id', $invoice->id)->delete();
171
            abort(500);
172
        }
173
    }
174
175
    public function download(Invoice $invoice)
176
    {
177
        App::setLocale(config('app.locale'));
178
179
        $customer = new Buyer([
180
            'name'          => $invoice->client_name,
181
            'custom_fields' => [
182
                'nif/cif' => $invoice->client_idnumber,
183
                'domicilio' => $invoice->client_address,
184
                'email' => $invoice->client_email,
185
            ],
186
        ]);
187
188
        $notes = $invoice->invoiceType->notes;
189
190
        $currencyFormat = config('app.currency_position') === 'before' ? '{SYMBOL}{VALUE}' : '{VALUE}{SYMBOL}';
191
        $generatedInvoice = InvoiceAlias::make()
192
            ->buyer($customer)
193
            ->series($invoice->invoice_series)
194
            ->sequence($invoice->invoice_number ?? $invoice->id)
195
            ->dateFormat('d/m/Y')
196
            ->date($invoice->date)
0 ignored issues
show
Bug introduced by
$invoice->date of type string is incompatible with the type Carbon\Carbon expected by parameter $date of LaravelDaily\Invoices\Invoice::date(). ( Ignorable by Annotation )

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

196
            ->date(/** @scrutinizer ignore-type */ $invoice->date)
Loading history...
197
            ->logo(storage_path('logo2.png'))
198
            ->currencySymbol(config('app.currency_symbol'))
199
            ->currencyCode(config('app.currency_code'))
200
            ->currencyFormat($currencyFormat)
201
            ->notes($notes ?? '');
202
203
        foreach ($invoice->invoiceDetails as $product) {
204
            $item = (new InvoiceItem())->title($product->product_name)->pricePerUnit($product->price)->quantity($product->quantity);
205
206
            $generatedInvoice->addItem($item);
207
        }
208
209
        $generatedInvoice->footer = Config::firstWhere('name', 'invoice_footer')->value ?? '';
0 ignored issues
show
Bug introduced by
The property footer does not seem to exist on LaravelDaily\Invoices\Invoice.
Loading history...
210
211
        return $generatedInvoice->stream();
212
    }
213
214
    public function savePayments(Request $request, Invoice $invoice)
215
    {
216
        $invoice->payments()->delete();
217
218
        foreach ($request->payments as $payment) {
219
            $invoice->payments()->create([
220
                'payment_method' => $payment['payment_method'] ?? null,
221
                'value' => $payment['value'],
222
                'date' => isset($payment['date']) ? Carbon::parse($payment['date']) : Carbon::now(),
223
                'status' => $payment['status'] ?? 1,
224
                'responsable_id' => backpack_user()->id,
0 ignored issues
show
Bug introduced by
Accessing id on the interface Illuminate\Contracts\Auth\Authenticatable suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
225
            ]);
226
        }
227
228
        $this->ifTheInvoiceIsFullyPaidMarkItsProductsAsSuch($invoice);
229
230
        return $invoice->fresh()->payments;
231
    }
232
233
    private function ifTheInvoiceIsFullyPaidMarkItsProductsAsSuch(Invoice $invoice): void
234
    {
235
        foreach ($invoice->scheduledPayments as $scheduledPayment) {
236
            if ($invoice->totalPrice() === $invoice->paidTotal()) {
237
                $scheduledPayment->product->markAsPaid();
238
239
                $relatedEnrollment = $scheduledPayment->product->enrollment;
240
                if ($relatedEnrollment && $relatedEnrollment->scheduledPayments->where('status', '!==', 2)->count() === 0) {
241
                    $relatedEnrollment->markAsPaid();
242
                }
243
            }
244
        }
245
246
        foreach ($invoice->enrollments as $enrollment) {
247
            if ($invoice->totalPrice() === $invoice->paidTotal()) {
248
                $enrollment->product->markAsPaid();
249
            }
250
        }
251
    }
252
}
253