InvoiceController::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 4
rs 10
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
        if (config('invoicing.price_categories_enabled')) {
44
            abort(403, 'Unable to create an invoice because price categories are enabled in your setup.');
45
        }
46
47
        return view('carts.show', ['enrollment' => null,
48
            'products' => [],
49
            'invoicetypes' => InvoiceType::all(),
50
            'clients' => [],
51
            'availableBooks' => Book::all(),
52
            'availableFees' => Fee::all(),
53
            'availableDiscounts' => Discount::all(),
54
            'availableTaxes' => Tax::all(),
55
            'availablePaymentMethods' => Paymentmethod::all(), ]);
56
    }
57
58
    /**
59
     * Create a invoice based on the cart contents for the specified user
60
     * Receive in the request: the user ID + the invoice data.
61
     */
62
    public function store(Request $request)
63
    {
64
65
        // receive the client data and create a invoice with status = pending
66
        $invoice = Invoice::create([
67
            'client_name' => $request->client_name,
68
            'client_idnumber' => $request->client_idnumber,
69
            'client_address' => $request->client_address,
70
            'client_email' => $request->client_email,
71
            'client_phone' => $request->client_phone,
72
            'invoice_type_id' => $request->invoicetype,
73
            'receipt_number' => $request->receiptnumber,
74
            'date' => $request->has('date') ? Carbon::parse($request->date) : Carbon::now(),
75
        ]);
76
77
        $invoice->setNumber(); // TODO extract this to model events.
78
79
        // persist the products
80
        foreach ($request->products as $f => $product) {
81
            $productType = match ($product['type']) {
82
                'enrollment' => Enrollment::class,
83
                'scheduledPayment' => ScheduledPayment::class,
84
                'fee' => Fee::class,
85
                'book' => Book::class,
86
            };
87
88
            $productFinalPrice = 0; // used to compute the final price with taxes and discounts
89
90
            $productFinalPrice += $product['price'] * ($product['quantity'] ?? 1) * 100;
91
92
            // 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
93
            if (isset($product['discounts'])) {
94
                foreach ($product['discounts'] as $d => $discount) {
95
                    InvoiceDetail::create([
96
                        'invoice_id' => $invoice->id,
97
                        'product_name' => $discount['name'],
98
                        'product_id' => $discount['id'],
99
                        'product_type' => Discount::class,
100
                        'price' => -($discount['value'] / 100) * $product['price'] * ($product['quantity'] ?? 1),
101
                    ]);
102
103
                    $productFinalPrice -= (($discount['value']) * $product['price']) * ($product['quantity'] ?? 1); // no need to multiply by 100 because discount is in %
104
                }
105
            }
106
107
            if (isset($product['taxes'])) {
108
                foreach ($product['taxes'] as $d => $tax) {
109
                    $productFinalPrice += (($tax['value']) * $product['price']) * ($product['quantity'] ?? 1); // no need to multiply by 100 because discount is in %
110
111
                    InvoiceDetail::create([
112
                        'invoice_id' => $invoice->id,
113
                        'product_name' => $tax['name'],
114
                        'product_id' => $tax['id'],
115
                        'product_type' => Tax::class,
116
                        'price' => $product['price'] * ($tax['value'] / 100) * ($product['quantity'] ?? 1),
117
                    ]);
118
                }
119
            }
120
121
            InvoiceDetail::create([
122
                'invoice_id' => $invoice->id,
123
                'product_name' => $product['name'],
124
                'product_code' => $product['product_code'],
125
                'product_id' => $product['id'],
126
                'product_type' => $productType,
127
                'price' => $product['price'],
128
                'final_price' => $productFinalPrice,
129
                'quantity' => $product['quantity'] ?? 1,
130
            ]);
131
        }
132
133
        foreach ($request->payments as $p => $payment) {
134
            Payment::create([
135
                '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...
136
                'invoice_id' => $invoice->id,
137
                'payment_method' => $payment['method'] ?? null,
138
                'value' => $payment['value'],
139
                'date' => isset($payment['date']) ? Carbon::parse($payment['date']) : Carbon::now(),
140
            ]);
141
        }
142
143
        // send the details to Accounting
144
        // and receive and store the invoice number
145
        if ($request->sendinvoice == true && config('invoicing.invoicing_system')) {
146
            try {
147
                $invoiceNumber = $this->invoicingService->saveInvoice($invoice);
148
                Log::info($invoiceNumber);
149
                if ($invoiceNumber !== null) {
150
                    $invoice->receipt_number = $invoiceNumber;
151
                    $invoice->save();
152
                    $success = true;
153
                } else {
154
                    Invoice::where('id', $invoice->id)->delete();
155
                    abort(500);
156
                }
157
            } catch (Exception $exception) {
158
                Log::error('Data could not be sent to accounting');
159
                Log::error($exception);
160
            }
161
        } else {
162
            $success = true;
163
        }
164
        if (isset($success)) {
165
            $this->ifTheInvoiceIsFullyPaidMarkItsProductsAsSuch($invoice);
166
167
            if (isset($request->comment)) {
168
                Comment::create([
169
                    'commentable_id' => $invoice->id,
170
                    'commentable_type' => Invoice::class,
171
                    'body' => $request->comment,
172
                    'author_id' => backpack_user()->id,
173
                ]);
174
            }
175
        } else {
176
            Invoice::where('id', $invoice->id)->delete();
177
            abort(500);
178
        }
179
    }
180
181
    public function download(Invoice $invoice)
182
    {
183
        App::setLocale(config('app.locale'));
184
185
        $customer = new Buyer([
186
            'name' => $invoice->client_name,
187
            'custom_fields' => [
188
                'nif/cif' => $invoice->client_idnumber,
189
                'domicilio' => $invoice->client_address,
190
                'email' => $invoice->client_email,
191
            ],
192
        ]);
193
194
        $notes = $invoice->invoiceType->notes;
195
196
        $currencyFormat = config('app.currency_position') === 'before' ? '{SYMBOL}{VALUE}' : '{VALUE}{SYMBOL}';
197
        $generatedInvoice = InvoiceAlias::make()
198
            ->buyer($customer)
199
            ->series($invoice->invoice_series)
200
            ->sequence($invoice->invoice_number ?? $invoice->id)
201
            ->dateFormat('d/m/Y')
202
            ->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

202
            ->date(/** @scrutinizer ignore-type */ $invoice->date)
Loading history...
203
            ->logo(storage_path('logo2.png'))
204
            ->currencySymbol(config('app.currency_symbol'))
205
            ->currencyCode(config('app.currency_code'))
206
            ->currencyFormat($currencyFormat)
207
            ->notes($notes ?? '');
208
209
        foreach ($invoice->invoiceDetails as $product) {
210
            $item = (new InvoiceItem())->title($product->product_name)->pricePerUnit($product->price)->quantity($product->quantity);
211
212
            $generatedInvoice->addItem($item);
213
        }
214
215
        $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...
216
217
        return $generatedInvoice->stream();
218
    }
219
220
    public function savePayments(Request $request, Invoice $invoice)
221
    {
222
        $invoice->payments()->delete();
223
224
        foreach ($request->payments as $payment) {
225
            $invoice->payments()->create([
226
                'payment_method' => $payment['payment_method'] ?? null,
227
                'value' => $payment['value'],
228
                'date' => isset($payment['date']) ? Carbon::parse($payment['date']) : Carbon::now(),
229
                'status' => $payment['status'] ?? 1,
230
                '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...
231
            ]);
232
        }
233
234
        $this->ifTheInvoiceIsFullyPaidMarkItsProductsAsSuch($invoice);
235
236
        return $invoice->fresh()->payments;
237
    }
238
239
    private function ifTheInvoiceIsFullyPaidMarkItsProductsAsSuch(Invoice $invoice): void
240
    {
241
        foreach ($invoice->scheduledPayments as $scheduledPayment) {
242
            if ($invoice->totalPrice() === $invoice->paidTotal()) {
243
                $scheduledPayment->product->markAsPaid();
244
245
                /** @var Enrollment $relatedEnrollment */
246
                $relatedEnrollment = $scheduledPayment->product->enrollment;
247
                if ($relatedEnrollment && $relatedEnrollment->scheduledPayments->where('status', '!==', 2)->count() === 0) {
248
                    $relatedEnrollment->markAsPaid();
249
                }
250
            }
251
        }
252
253
        foreach ($invoice->enrollments as $enrollment) {
254
            if ($invoice->totalPrice() === $invoice->paidTotal() && $enrollment->product->price == $enrollment->product->total_paid_price) {
255
                $enrollment->product->markAsPaid();
256
            }
257
        }
258
    }
259
}
260