Passed
Push — master ( 26caeb...0e28f7 )
by Thomas
11:18
created

InvoiceController::savePayments()   A

Complexity

Conditions 6
Paths 6

Size

Total Lines 22
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 12
c 1
b 0
f 0
nc 6
nop 2
dl 0
loc 22
rs 9.2222
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
            'total_price' => $request->total_price,
66
            'invoice_type_id' => $request->invoicetype,
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' => Enrollment::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
            if ($product['type'] === 'enrollment') {
84
                Enrollment::find($product['id'])->invoices()->attach($invoice, ['scheduled_payment_id' => $request->scheduled_payment_id ?? null]);
85
            }
86
87
            if ($product['type'] === 'scheduledPayment') {
88
                $scheduledPayment = ScheduledPayment::find($product['id']);
89
                $scheduledPayment->enrollment->invoices()->attach($invoice, ['scheduled_payment_id' => $product['id']]);
90
                // Reset status to default value
91
                $scheduledPayment->update(['status' => null]);
92
            }
93
94
            $productFinalPrice += $product['price'] * ($product['quantity'] ?? 1) * 100;
95
96
            if (isset($product['discounts'])) {
97
                foreach ($product['discounts'] as $d => $discount) {
98
                    $productFinalPrice -= (($discount['value']) * $product['price']) * ($product['quantity'] ?? 1); // no need to multiply by 100 because discount is in %
99
100
                    InvoiceDetail::create([
101
                        'invoice_id' => $invoice->id,
102
                        'product_name' => $discount['name'],
103
                        'product_id' => $discount['id'],
104
                        'product_type' => Discount::class,
105
                        'price' => -$discount['value'] * ($product['quantity'] ?? 1),
106
                    ]);
107
                }
108
            }
109
110
            if (isset($product['taxes'])) {
111
                foreach ($product['taxes'] as $d => $tax) {
112
                    $productFinalPrice += (($tax['value']) * $product['price']) * ($product['quantity'] ?? 1); // no need to multiply by 100 because discount is in %
113
114
                    InvoiceDetail::create([
115
                        'invoice_id' => $invoice->id,
116
                        'product_name' => $tax['name'],
117
                        'product_id' => $tax['id'],
118
                        'product_type' => Tax::class,
119
                        'price' => $product['price'] * ($tax['value'] / 100) * ($product['quantity'] ?? 1),
120
                    ]);
121
                }
122
            }
123
124
            InvoiceDetail::create([
125
                'invoice_id' => $invoice->id,
126
                'product_name' => $product['name'],
127
                'product_code' => $product['product_code'],
128
                'product_id' => $product['id'],
129
                'product_type' => $productType,
130
                'price' => $product['price'],
131
                'final_price' => $productFinalPrice,
132
                'quantity' => $product['quantity'] ?? 1,
133
                //'tax_rate' => collect($product['taxes'] ?? [])->sum('value'),
134
            ]);
135
        }
136
137
        foreach ($request->payments as $p => $payment) {
138
            Payment::create([
139
                'responsable_id' => backpack_user()->id,
140
                'invoice_id' => $invoice->id,
141
                'payment_method' => $payment['method'] ?? null,
142
                'value' => $payment['value'],
143
                'date' => isset($payment['date']) ? Carbon::parse($payment['date']) : Carbon::now(),
144
            ]);
145
        }
146
147
        // send the details to Accounting
148
        // and receive and store the invoice number
149
        if ($request->sendinvoice == true && config('invoicing.invoicing_system')) {
150
            try {
151
                $invoiceNumber = $this->invoicingService->saveInvoice($invoice);
152
                Log::info($invoiceNumber);
153
                if ($invoiceNumber !== null) {
154
                    $invoice->receipt_number = $invoiceNumber;
155
                    $invoice->save();
156
                    $success = true;
157
                } else {
158
                    Invoice::where('id', $invoice->id)->delete();
159
                    abort(500);
160
                }
161
            } catch (Exception $exception) {
162
                Log::error('Data could not be sent to accounting');
163
                Log::error($exception);
164
            }
165
        } else {
166
            $success = true;
167
        }
168
169
        if (isset($success)) {
170
            // if the value of payments matches the total due price,
171
            // mark the invoice and associated enrollments as paid.
172
            foreach ($invoice->enrollments as $enrollment) {
173
                if ($enrollment->price == $invoice->paidTotal()) {
174
                    $enrollment->markAsPaid();
175
                } elseif ($enrollment->scheduledPayments->where('computed_status', '!==', 2)->count() === 0) {
176
                    $enrollment->markAsPaid();
177
                }
178
                if (isset($request->comment)) {
179
                    Comment::create([
180
                        'commentable_id' => $enrollment->id,
181
                        'commentable_type' => Enrollment::class,
182
                        'body' => $request->comment,
183
                        'author_id' => backpack_user()->id,
184
                    ]);
185
                }
186
            }
187
        } else {
188
            Invoice::where('id', $invoice->id)->delete();
189
            abort(500);
190
        }
191
    }
192
193
    public function edit(Invoice $invoice)
194
    {
195
        return view('invoices.edit', compact('invoice'));
196
    }
197
198
    public function download(Invoice $invoice)
199
    {
200
        App::setLocale(config('app.locale'));
201
202
        $customer = new Buyer([
203
            'name'          => $invoice->client_name,
204
            'custom_fields' => [
205
                'nif/cif' => $invoice->client_idnumber,
206
                'domicilio' => $invoice->client_address,
207
                'email' => $invoice->client_email,
208
            ],
209
        ]);
210
211
        $notes = $invoice->invoiceType->notes;
212
213
        $currencyFormat = config('app.currency_position') === 'before' ? '{SYMBOL}{VALUE}' : '{VALUE}{SYMBOL}';
214
        $generatedInvoice = InvoiceAlias::make()
215
            ->buyer($customer)
216
            ->series($invoice->invoice_series)
217
            ->sequence($invoice->invoice_number ?? $invoice->id)
218
            ->dateFormat('d/m/Y')
219
            ->date($invoice->date)
220
            ->logo(storage_path('logo2.png'))
221
            ->currencySymbol(config('app.currency_symbol'))
222
            ->currencyCode(config('app.currency_code'))
223
            ->currencyFormat($currencyFormat)
224
            ->notes($notes ?? '');
225
226
        //$taxIsGlobal = $invoice->products->pluck('tax_rate')->unique()->count() === 1;
227
        //$taxRate = $invoice->taxes->pluck('tax_rate')->unique()->first();
228
229
        foreach ($invoice->invoiceDetails as $product) {
230
            $item = (new InvoiceItem())->title($product->product_name)->pricePerUnit($product->price)->quantity($product->quantity);
231
232
            /*if (!$taxIsGlobal)
233
            {
234
                $item->taxByPercent($product->tax_rate);
235
            }*/
236
237
            $generatedInvoice->addItem($item);
238
        }
239
240
        /*if ($taxRate > 0 && $taxIsGlobal)
241
        {
242
            $generatedInvoice->taxRate($taxRate);
243
        }*/
244
245
        $generatedInvoice->footer = Config::firstWhere('name', 'invoice_footer')->value ?? '';
246
247
        return $generatedInvoice->stream();
248
    }
249
250
    public function savePayments(Request $request, Invoice $invoice)
251
    {
252
        $invoice->payments()->delete();
253
254
        foreach ($request->payments as $payment) {
255
            $invoice->payments()->create([
256
                'payment_method' => $payment['payment_method'] ?? null,
257
                'value' => $payment['value'],
258
                'date' => isset($payment['date']) ? Carbon::parse($payment['date']) : Carbon::now(),
259
                'status' => $payment['status'] ?? 1,
260
                'responsable_id' => backpack_user()->id,
261
            ]);
262
        }
263
264
        // if the payments match the enrollment price, mark as paid.
265
        foreach ($invoice->enrollments as $enrollment) {
266
            if ($invoice->total_price == $invoice->paidTotal() && $invoice->payments->where('status', '!==', 2)->count() === 0) {
267
                $enrollment->markAsPaid();
268
            }
269
        }
270
271
        return $invoice->fresh()->payments;
272
    }
273
}
274