From 3ee3f67c8c209b5252057e83ce7b28fdd93d71e8 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 6 Jan 2021 16:14:20 +1100 Subject: [PATCH] Fixes for over payments --- .../ClientPortal/PaymentController.php | 52 ++++++++++++++----- .../PayPalExpressPaymentDriver.php | 2 +- app/Services/Payment/UpdateInvoicePayment.php | 6 +++ resources/lang/en/texts.php | 1 + 4 files changed, 46 insertions(+), 15 deletions(-) diff --git a/app/Http/Controllers/ClientPortal/PaymentController.php b/app/Http/Controllers/ClientPortal/PaymentController.php index d08840e86663..86b41ec31912 100644 --- a/app/Http/Controllers/ClientPortal/PaymentController.php +++ b/app/Http/Controllers/ClientPortal/PaymentController.php @@ -83,8 +83,6 @@ class PaymentController extends Controller $gateway = CompanyGateway::find($request->input('company_gateway_id')); - //refactor from here! - /** * find invoices * @@ -95,11 +93,13 @@ class PaymentController extends Controller $invoices = Invoice::whereIn('id', $this->transformKeys($payable_invoices->pluck('invoice_id')->toArray()))->get(); /* pop non payable invoice from the $payable_invoices array */ + $payable_invoices = $payable_invoices->filter(function ($payable_invoice) use ($invoices) { return $invoices->where('hashed_id', $payable_invoice['invoice_id'])->first()->isPayable(); }); /*return early if no invoices*/ + if ($payable_invoices->count() == 0) { return redirect() ->route('client.invoices.index') @@ -108,23 +108,32 @@ class PaymentController extends Controller $settings = auth()->user()->client->getMergedSettings(); - /*iterate through invoices and add gateway fees and other payment metadata*/ - $payable_invoices = $payable_invoices->map(function ($payable_invoice) use ($invoices, $settings) { - $payable_invoice['amount'] = Number::parseFloat($payable_invoice['amount']); + /* This loop checks for under / over payments and returns the user if a check fails */ + + foreach($payable_invoices as $payable_invoice) + { + + /*Match the payable invoice to the Model Invoice*/ $invoice = $invoices->first(function ($inv) use ($payable_invoice) { return $payable_invoice['invoice_id'] == $inv->hashed_id; }); - // Check if company supports over & under payments. - // In case it doesn't this is where process should stop. + /* + * Check if company supports over & under payments. + * Determine the payable amount and the max payable. ie either partial or invoice balance + */ $payable_amount = Number::roundValue(Number::parseFloat($payable_invoice['amount']), auth()->user()->client->currency()->precision); - $invoice_balance = Number::roundValue($invoice->balance, auth()->user()->client->currency()->precision); + $invoice_balance = Number::roundValue(($invoice->partial > 0 ? $invoice->partial : $invoice->balance), auth()->user()->client->currency()->precision); + + /*If we don't allow under/over payments force the payable amount - prevents inspect element adjustments in JS*/ if ($settings->client_portal_allow_under_payment == false && $settings->client_portal_allow_over_payment == false) { $payable_invoice['amount'] = Number::roundValue(($invoice->partial > 0 ? $invoice->partial : $invoice->balance), auth()->user()->client->currency()->precision); - } // We don't allow either of these, reset the amount to default invoice (to prevent inspect element payments). + } + + /* If we DO allow under payments check the minimum amount is present else return */ if ($settings->client_portal_allow_under_payment) { if ($payable_invoice['amount'] < $settings->client_portal_under_payment_minimum) { @@ -133,23 +142,38 @@ class PaymentController extends Controller ->with('message', ctrans('texts.minimum_required_payment', ['amount' => $settings->client_portal_under_payment_minimum])); } } else { - $payable_amount = Number::roundValue(Number::parseFloat($payable_invoice['amount']), auth()->user()->client->currency()->precision); - $invoice_balance = Number::roundValue($invoice->balance, auth()->user()->client->currency()->precision); + /*Double check!!*/ if ($payable_amount < $invoice_balance) { return redirect() ->route('client.invoices.index') ->with('message', ctrans('texts.under_payments_disabled')); } - } // Make sure 'amount' from form is not lower than 'amount' from invoice. + } - if ($settings->client_portal_allow_over_payment == false) { + /* If we don't allow over payments and the amount exceeds the balance */ + + if (!$settings->client_portal_allow_over_payment) { if ($payable_amount > $invoice_balance) { return redirect() ->route('client.invoices.index') ->with('message', ctrans('texts.over_payments_disabled')); } - } // Make sure 'amount' from form is not higher than 'amount' from invoice. + } + + } + + /*Iterate through invoices and add gateway fees and other payment metadata*/ + + $payable_invoices = $payable_invoices->map(function ($payable_invoice) use ($invoices, $settings) { + $payable_invoice['amount'] = Number::parseFloat($payable_invoice['amount']); + + $invoice = $invoices->first(function ($inv) use ($payable_invoice) { + return $payable_invoice['invoice_id'] == $inv->hashed_id; + }); + + $payable_amount = Number::roundValue(Number::parseFloat($payable_invoice['amount']), auth()->user()->client->currency()->precision); + $invoice_balance = Number::roundValue($invoice->balance, auth()->user()->client->currency()->precision); $payable_invoice['due_date'] = $this->formatDate($invoice->due_date, $invoice->client->date_format()); $payable_invoice['invoice_number'] = $invoice->number; diff --git a/app/PaymentDrivers/PayPalExpressPaymentDriver.php b/app/PaymentDrivers/PayPalExpressPaymentDriver.php index f9a6b02285f1..59b972e307d8 100644 --- a/app/PaymentDrivers/PayPalExpressPaymentDriver.php +++ b/app/PaymentDrivers/PayPalExpressPaymentDriver.php @@ -192,7 +192,7 @@ class PayPalExpressPaymentDriver extends BaseDriver 'cancelUrl' => $this->client->company->domain() . '/client/invoices', 'description' => implode(',', collect($this->payment_hash->data->invoices) ->map(function ($invoice) { - return sprintf('%s: %s', ctrans('texts.invoice_number'), $invoice->number); + return sprintf('%s: %s', ctrans('texts.invoice_number'), $invoice->invoice_number); })->toArray()), 'transactionId' => $this->payment_hash->hash . '-' . time(), 'ButtonSource' => 'InvoiceNinja_SP', diff --git a/app/Services/Payment/UpdateInvoicePayment.php b/app/Services/Payment/UpdateInvoicePayment.php index f67de4a7490f..9df0b53c066a 100644 --- a/app/Services/Payment/UpdateInvoicePayment.php +++ b/app/Services/Payment/UpdateInvoicePayment.php @@ -39,6 +39,7 @@ class UpdateInvoicePayment $invoices = Invoice::whereIn('id', $this->transformKeys(array_column($paid_invoices, 'invoice_id')))->get(); collect($paid_invoices)->each(function ($paid_invoice) use ($invoices) { + $invoice = $invoices->first(function ($inv) use ($paid_invoice) { return $paid_invoice->invoice_id == $inv->hashed_id; }); @@ -49,6 +50,11 @@ class UpdateInvoicePayment $paid_amount = $paid_invoice->amount; } + /* Need to determine here is we have an OVER payment - if YES only apply the max invoice amount */ + if($paid_amount > $invoice->partial && $paid_amount > $invoice->balance) + $paid_amount = $invoice->balance; + + /* Updates the company ledger */ $this->payment ->ledger() ->updatePaymentBalance($paid_amount * -1); diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 1c850c61c839..aeaf5a0a8e03 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -3362,4 +3362,5 @@ return [ 'currency_albanian_lek' => 'Albanian Lek', 'endless' => 'Endless', + 'minimum_payment' => 'Minimum Payment', ];