Working on integrating multiple invoice payments

This commit is contained in:
David Bomba 2024-07-02 17:09:46 +10:00
parent 73c1f1b767
commit 01312996d8
9 changed files with 87 additions and 52 deletions

View File

@ -78,6 +78,7 @@ class InvoiceController extends Controller
'key' => $invitation ? $invitation->key : false, 'key' => $invitation ? $invitation->key : false,
'hash' => $hash, 'hash' => $hash,
'variables' => $variables, 'variables' => $variables,
'invoices' => [$invoice->hashed_id],
]; ];
if ($request->query('mode') === 'fullscreen') { if ($request->query('mode') === 'fullscreen') {

View File

@ -27,6 +27,8 @@ class PaymentMethod extends Component
public $isLoading = true; public $isLoading = true;
public $amount = 0;
public function placeholder() public function placeholder()
{ {
return <<<'HTML' return <<<'HTML'
@ -43,14 +45,16 @@ class PaymentMethod extends Component
{ {
$this->invoice = $this->context['invoice']; $this->invoice = $this->context['invoice'];
$this->variables = $this->context['variables']; $invoice_amount = $this->invoice->partial > 0 ? $this->invoice->partial : $this->invoice->balance;
$this->variables = $this->context['variables'];
$this->amount = isset($this->context['payable_invoices']) ? array_sum(array_column($this->context['payable_invoices'], 'amount')) : $invoice_amount;
MultiDB::setDb($this->invoice->company->db); MultiDB::setDb($this->invoice->company->db);
$this->methods = $this->invoice->client->service()->getPaymentMethods($this->invoice->balance); $this->methods = $this->invoice->client->service()->getPaymentMethods($this->amount);
if(count($this->methods) == 1) { if(count($this->methods) == 1) {
$this->dispatch('singlePaymentMethodFound', company_gateway_id: $this->methods[0]['company_gateway_id'], gateway_type_id: $this->methods[0]['gateway_type_id'], amount: $this->invoice->balance); $this->dispatch('singlePaymentMethodFound', company_gateway_id: $this->methods[0]['company_gateway_id'], gateway_type_id: $this->methods[0]['gateway_type_id'], amount: $this->amount);
} }
else { else {
$this->isLoading = false; $this->isLoading = false;
@ -61,6 +65,6 @@ class PaymentMethod extends Component
public function render() public function render()
{ {
//If there is only one payment method, skip display and push straight to the form!! //If there is only one payment method, skip display and push straight to the form!!
return render('components.livewire.payment_method-flow2', ['methods' => $this->methods, 'amount' => $this->invoice->balance]); return render('components.livewire.payment_method-flow2', ['methods' => $this->methods]);
} }
} }

View File

@ -34,9 +34,6 @@ class ProcessPayment extends Component
public function mount() public function mount()
{ {
nlog($this->isLoading);
nlog("inside Process Payment");
MultiDB::setDb($this->context['invoice']->company->db); MultiDB::setDb($this->context['invoice']->company->db);
$invitation = InvoiceInvitation::find($this->context['invitation_id']); $invitation = InvoiceInvitation::find($this->context['invitation_id']);

View File

@ -28,21 +28,29 @@ class UnderOverPayment extends Component
public $errors = ''; public $errors = '';
public $payableInvoices = [];
public function mount() public function mount()
{ {
$invoice = $this->context['invoice'];
$this->invoice_amount = $invoice->partial > 0 ? $invoice->partial : $invoice->balance; $this->invoice_amount = array_sum(array_column($this->context['payable_invoices'], 'amount'));
$this->currency = $invoice->client->currency(); $this->currency = $this->context['invitation']->contact->client->currency();
$this->payableAmount = Number::formatValue($this->invoice_amount, $this->currency); $this->payableInvoices = $this->context['payable_invoices'];
} }
public function checkValue($value) public function checkValue(array $payableInvoices)
{ {
nlog($payableInvoices);
$this->errors = ''; $this->errors = '';
$settings = $this->context['settings']; $settings = $this->context['settings'];
$input_amount = Number::parseFloat($value); $input_amount = 0;
foreach($payableInvoices as $invoice)
$input_amount += Number::parseFloat($invoice['formatted_amount']);
nlog($input_amount);
if($settings->client_portal_allow_under_payment && $settings->client_portal_under_payment_minimum != 0) if($settings->client_portal_allow_under_payment && $settings->client_portal_under_payment_minimum != 0)
{ {
@ -60,12 +68,11 @@ class UnderOverPayment extends Component
} }
if(!$this->errors) if(!$this->errors)
$this->dispatch('payable-amount', payable_amount: $value ); $this->dispatch('payable-amount', payable_amount: $input_amount );
} }
public function render() public function render()
{ {
return render('components.livewire.under-over-payments',[ return render('components.livewire.under-over-payments',[
'settings' => $this->context['settings'], 'settings' => $this->context['settings'],
]); ]);

View File

@ -23,12 +23,22 @@ use Livewire\Attributes\Reactive;
use App\Livewire\Flow2\PaymentMethod; use App\Livewire\Flow2\PaymentMethod;
use App\Livewire\Flow2\ProcessPayment; use App\Livewire\Flow2\ProcessPayment;
use App\Livewire\Flow2\UnderOverPayment; use App\Livewire\Flow2\UnderOverPayment;
use App\Models\Invoice;
use App\Utils\Number; use App\Utils\Number;
use App\Utils\Traits\MakesDates;
use App\Utils\Traits\MakesHash;
class InvoicePay extends Component class InvoicePay extends Component
{ {
use MakesDates;
use MakesHash;
public $invitation_id; public $invitation_id;
public $invoices;
public $variables;
public $db; public $db;
public $settings; public $settings;
@ -86,7 +96,7 @@ class InvoicePay extends Component
#[On('payment-method-selected')] #[On('payment-method-selected')]
public function paymentMethodSelected($company_gateway_id, $gateway_type_id, $amount) public function paymentMethodSelected($company_gateway_id, $gateway_type_id, $amount)
{ {
nlog("payment method selected inside InvoicePay"); //@TODO only handles single invoice scenario
$this->payment_method_accepted = true; $this->payment_method_accepted = true;
@ -95,11 +105,11 @@ class InvoicePay extends Component
$this->context['amount'] = $amount; $this->context['amount'] = $amount;
$this->context['pre_payment'] = false; $this->context['pre_payment'] = false;
$this->context['is_recurring'] = false; $this->context['is_recurring'] = false;
$this->context['payable_invoices'] = ['invoice_id' => $this->context['invoice']->hashed_id, 'amount' => $this->context['invoice']->balance];
// $this->context['payable_invoices'] = ['invoice_id' => $this->context['invoice']->hashed_id, 'amount' => $amount];
$this->context['invitation_id'] = $this->invitation_id; $this->context['invitation_id'] = $this->invitation_id;
// $this->invite = \App\Models\InvoiceInvitation::withTrashed()->find($this->invitation_id)->withoutRelations();
$this->component();
} }
@ -136,12 +146,23 @@ class InvoicePay extends Component
MultiDB::setDb($this->db); MultiDB::setDb($this->db);
// @phpstan-ignore-next-line // @phpstan-ignore-next-line
$invite = \App\Models\InvoiceInvitation::with('invoice','contact.client','company')->withTrashed()->find($this->invitation_id); $invite = \App\Models\InvoiceInvitation::with('contact.client','company')->withTrashed()->find($this->invitation_id);
$client = $invite->contact->client; $client = $invite->contact->client;
$variables = ($invite && auth()->guard('contact')->user()->client->getSetting('show_accept_invoice_terms')) ? (new HtmlEngine($invite))->generateLabelsAndValues() : false;
$settings = $client->getMergedSettings(); $settings = $client->getMergedSettings();
$this->context['settings'] = $settings; $this->context['settings'] = $settings;
$invoices = Invoice::find($this->transformKeys($this->invoices));
$invoices = $invoices->filter(function ($i){
$i = $i->service()
->markSent()
->removeUnpaidGatewayFees()
->save();
return $i->isPayable();
});
//under-over / payment //under-over / payment
//required fields //required fields
@ -151,11 +172,20 @@ class InvoicePay extends Component
$this->under_over_payment = $settings->client_portal_allow_over_payment || $settings->client_portal_allow_under_payment; $this->under_over_payment = $settings->client_portal_allow_over_payment || $settings->client_portal_allow_under_payment;
$this->required_fields = false; $this->required_fields = false;
$this->context['variables'] = $variables; $this->context['variables'] = $this->variables;
$this->context['invoice'] = $invite->invoice; $this->context['invoices'] = $invoices;
$this->context['settings'] = $settings; $this->context['settings'] = $settings;
$this->context['invitation'] = $invite;
$this->context['payable_invoices'] = ['invoice_id' => $this->context['invoice']->hashed_id, 'amount' => $invite->invoice->partial > 0 ? $invite->invoice->partial : $invite->invoice->balance]; $this->context['payable_invoices'] = $invoices->map(function ($i){
return [
'invoice_id' => $i->hashed_id,
'amount' => $i->partial > 0 ? $i->partial : $i->balance,
'formatted_amount' => Number::formatValue($i->partial > 0 ? $i->partial : $i->balance, $i->client->currency()),
'number' => $i->number,
'date' => $i->translateDate($i->date, $i->client->date_format(), $i->client->locale())
];
})->toArray();
} }

View File

@ -22,7 +22,8 @@ class InvoiceSummary extends Component
public function mount() public function mount()
{ {
$this->invoice = $this->context['invoice']; //@TODO for a single invoice - show all details, for multi-invoices, only show the summaries
$this->invoice = $this->context['invitation']->invoice;
} }
public function render() public function render()

View File

@ -102,17 +102,7 @@ class LivewireInstantPayment
$invoices = Invoice::query() $invoices = Invoice::query()
->whereIn('id', $this->transformKeys($payable_invoices->pluck('invoice_id')->toArray())) ->whereIn('id', $this->transformKeys($payable_invoices->pluck('invoice_id')->toArray()))
->withTrashed() ->withTrashed()
->get() ->get();
->filter(function ($invoice){
$invoice =
$invoice->service()
->markSent()
->removeUnpaidGatewayFees()
->save();
return $invoice->isPayable();
});
/* pop non payable invoice from the $payable_invoices array */ /* pop non payable invoice from the $payable_invoices array */
$payable_invoices = $payable_invoices->filter(function ($payable_invoice) use ($invoices) { $payable_invoices = $payable_invoices->filter(function ($payable_invoice) use ($invoices) {

View File

@ -1,35 +1,40 @@
<div x-data="{ payableAmount: '{{ $payableAmount }}', errors: @entangle('errors') }" class="px-4 py-5 bg-white sm:gap-4 sm:px-6"> <div x-data="{ payableInvoices: @entangle('payableInvoices'), errors: @entangle('errors') }" class="px-4 py-5 bg-white sm:gap-4 sm:px-6">
<dt class="text-sm font-medium leading-5 text-gray-500"> <dt class="text-sm font-medium leading-5 text-gray-500">
{{ ctrans('texts.payment_amount') }} {{ ctrans('texts.payment_amount') }}
</dt> </dt>
<dd class="text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2 flex flex-col"> <dd class="text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2 flex flex-col">
<template x-for="(invoice, index) in payableInvoices" :key="index">
<div class="flex items-center"> <div class="flex items-center">
<label> <label>
<span class="mt-2">{{ $currency->code }} ({{ $currency->symbol }})</span> <span x-text="'{{ ctrans('texts.invoice') }} ' + invoice.number" class="mt-2"></span>
<span>{{ $currency->code }} ({{ $currency->symbol }})</span>
<input <input
type="text" type="text"
class="input mt-0 mr-4 relative" class="input mt-0 mr-4 relative"
name="payable_amount" name="payable_invoices[]"
x-model="payableAmount" x-model="payableInvoices[index].formatted_amount"
value="{{ $payableAmount }}"/> />
</label> </label>
</div> </div>
</template>
<template x-if="errors.length > 0"> <template x-if="errors.length > 0">
<div x-text="errors" class="alert alert-failure mb-4"></div> <div x-text="errors" class="alert alert-failure mb-4"></div>
</template> </template>
@if($settings->client_portal_allow_under_payment) @if($settings->client_portal_allow_under_payment)
<span class="mt-1 text-sm text-gray-800">{{ ctrans('texts.minimum_payment') }}: {{ $settings->client_portal_under_payment_minimum }}</span> <span class="mt-1 text-sm text-gray-800">{{ ctrans('texts.minimum_payment') }}: {{ $settings->client_portal_under_payment_minimum }}</span>
@endif @endif
</dd> </dd>
<div class="bg-white px-4 py-5 flex w-full justify-end"> <div class="bg-white px-4 py-5 flex w-full justify-end">
<button <button
class="button button-primary bg-primary payment-method flex items-center justify-center relative py-4" class="button button-primary bg-primary payment-method flex items-center justify-center relative py-4"
wire:click="checkValue(payableAmount)"> wire:click="checkValue(payableInvoices)">
<svg class="animate-spin h-5 w-5 text-white hidden" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"> <svg class="animate-spin h-5 w-5 text-white hidden" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle> <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path> <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>

View File

@ -17,7 +17,7 @@
@endif @endif
@if($invoice->isPayable()) @if($invoice->isPayable())
@livewire('invoice-pay', ['invitation_id' => $invitation->id, 'db' => $invoice->company->db]) @livewire('invoice-pay', ['invoices' => $invoices, 'invitation_id' => $invitation->id, 'db' => $invoice->company->db, 'variables' => $variables])
@endif @endif
@include('portal.ninja2020.components.entity-documents', ['entity' => $invoice]) @include('portal.ninja2020.components.entity-documents', ['entity' => $invoice])