Compare commits
No commits in common. "2deaa14dcf285560341f605553c84a9e856ff613" and "8b054bcbf00501be7cb21768e709c2f306bb2973" have entirely different histories.
2deaa14dcf
...
8b054bcbf0
|
|
@ -1,19 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum PaymentMethod: string
|
||||
{
|
||||
case CASH = 'cash';
|
||||
case CHECK = 'check';
|
||||
case CARD = 'card';
|
||||
|
||||
public function label(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::CASH => 'Cash',
|
||||
self::CHECK => 'Check',
|
||||
self::CARD => 'Card',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum PaymentStatus: string
|
||||
{
|
||||
case PENDING = 'pending';
|
||||
case COMPLETED = 'completed';
|
||||
case FAILED = 'failed';
|
||||
case REFUNDED = 'refunded';
|
||||
|
||||
public function label(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::PENDING => 'Pending',
|
||||
self::COMPLETED => 'Completed',
|
||||
self::FAILED => 'Failed',
|
||||
self::REFUNDED => 'Refunded',
|
||||
};
|
||||
}
|
||||
|
||||
public function color(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::PENDING => 'amber',
|
||||
self::COMPLETED => 'green',
|
||||
self::FAILED => 'red',
|
||||
self::REFUNDED => 'zinc',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -9,8 +9,6 @@ use Illuminate\Database\Eloquent\Model;
|
|||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
|
||||
|
||||
class Client extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
|
@ -43,11 +41,6 @@ class Client extends Model
|
|||
return $this->hasMany(Invoice::class);
|
||||
}
|
||||
|
||||
public function payments(): HasManyThrough
|
||||
{
|
||||
return $this->hasManyThrough(Payment::class, Invoice::class);
|
||||
}
|
||||
|
||||
public function secondaryContacts(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(Contact::class)
|
||||
|
|
|
|||
|
|
@ -4,18 +4,15 @@ namespace App\Models;
|
|||
|
||||
use App\Casts\MoneyCast;
|
||||
use App\Enums\InvoiceStatus;
|
||||
use App\Enums\PaymentStatus;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class Invoice extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
public static function booted(): void
|
||||
{
|
||||
static::creating(function (Invoice $invoice) {
|
||||
|
|
@ -48,8 +45,6 @@ class Invoice extends Model
|
|||
|
||||
protected $casts = [
|
||||
'total' => MoneyCast::class,
|
||||
'total_payments' => MoneyCast::class,
|
||||
'balance_due' => MoneyCast::class,
|
||||
'status' => InvoiceStatus::class,
|
||||
'invoice_date' => 'date',
|
||||
'due_date' => 'date',
|
||||
|
|
@ -77,33 +72,12 @@ class Invoice extends Model
|
|||
return $this->hasMany(InvoiceLine::class);
|
||||
}
|
||||
|
||||
public function payments(): HasMany
|
||||
{
|
||||
return $this->hasMany(Payment::class);
|
||||
}
|
||||
|
||||
public function recalculateTotal(): void
|
||||
{
|
||||
$this->attributes['total'] = $this->lines()->sum('amount');
|
||||
$this->saveQuietly();
|
||||
}
|
||||
|
||||
public function recalculateTotalPayments(): void
|
||||
{
|
||||
$this->attributes['total_payments'] = $this->payments()->where('status', PaymentStatus::COMPLETED)->sum('amount');
|
||||
$this->saveQuietly();
|
||||
|
||||
$this->refresh();
|
||||
|
||||
if ($this->status === InvoiceStatus::POSTED && $this->balance_due == 0) {
|
||||
$this->status = InvoiceStatus::PAID;
|
||||
$this->saveQuietly();
|
||||
} elseif ($this->status === InvoiceStatus::PAID && $this->balance_due != 0) {
|
||||
$this->status = InvoiceStatus::POSTED;
|
||||
$this->saveQuietly();
|
||||
}
|
||||
}
|
||||
|
||||
public function isLocked(): bool
|
||||
{
|
||||
return in_array($this->status, [InvoiceStatus::POSTED, InvoiceStatus::PAID, InvoiceStatus::VOID]);
|
||||
|
|
|
|||
|
|
@ -33,10 +33,6 @@ class InvoiceLine extends Model
|
|||
throw new InvoiceLockedException;
|
||||
}
|
||||
|
||||
if ($line->exists && $line->isDirty('invoice_id')) {
|
||||
throw new \RuntimeException('Cannot move invoice line to another invoice');
|
||||
}
|
||||
|
||||
$line->amount = $line->unit_price * $line->quantity;
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,67 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Casts\MoneyCast;
|
||||
use App\Enums\PaymentMethod;
|
||||
use App\Enums\PaymentStatus;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class Payment extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'invoice_id',
|
||||
'contact_id',
|
||||
'payment_date',
|
||||
'status',
|
||||
'payment_method',
|
||||
'reference',
|
||||
'stripe_payment_intent_id',
|
||||
'fee_amount',
|
||||
'amount',
|
||||
'notes',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'payment_date' => 'date',
|
||||
'amount' => MoneyCast::class,
|
||||
'fee_amount' => MoneyCast::class,
|
||||
'status' => PaymentStatus::class,
|
||||
'payment_method' => PaymentMethod::class,
|
||||
];
|
||||
|
||||
public static function booted(): void
|
||||
{
|
||||
static::saved(function (Payment $payment) {
|
||||
// If invoice_id changed, recalculate the old invoice too
|
||||
if ($payment->wasChanged('invoice_id')) {
|
||||
$originalInvoiceId = $payment->getOriginal('invoice_id');
|
||||
if ($originalInvoiceId) {
|
||||
Invoice::find($originalInvoiceId)?->recalculateTotalPayments();
|
||||
}
|
||||
}
|
||||
|
||||
$payment->invoice->recalculateTotalPayments();
|
||||
});
|
||||
|
||||
static::deleted(function (Payment $payment) {
|
||||
$payment->invoice->recalculateTotalPayments();
|
||||
});
|
||||
}
|
||||
|
||||
public function invoice(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Invoice::class);
|
||||
}
|
||||
|
||||
public function contact(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Contact::class);
|
||||
}
|
||||
|
||||
public function client(): BelongsTo
|
||||
{
|
||||
return $this->invoice->client();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Contact;
|
||||
use App\Models\Invoice;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('payments', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignIdFor(Invoice::class)->constrained();
|
||||
$table->foreignIdFor(Contact::class)->nullable()->constrained();
|
||||
$table->date('payment_date');
|
||||
$table->string('status')->default('pending'); // Accordint to claude,needed by Stripe. pending, completed, failed, refunded
|
||||
$table->string('payment_method');
|
||||
$table->string('reference')->nullable();
|
||||
$table->string('stripe_payment_intent_id')->nullable();
|
||||
$table->integer('fee_amount')->nullable();
|
||||
$table->integer('amount');
|
||||
$table->text('notes')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('payments');
|
||||
}
|
||||
};
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('invoices', function (Blueprint $table) {
|
||||
$table->bigInteger('total_payments')->default(0)->after('total');
|
||||
$table->bigInteger('balance_due')->storedAs('total - total_payments')->after('total_payments');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('invoices', function (Blueprint $table) {
|
||||
$table->dropColumn(['balance_due', 'total_payments']);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -1,135 +0,0 @@
|
|||
<?php
|
||||
|
||||
use App\Casts\MoneyCast;
|
||||
use App\Enums\InvoiceStatus;
|
||||
use App\Enums\PaymentMethod;
|
||||
use App\Enums\PaymentStatus;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use Illuminate\Validation\Rules\Enum;
|
||||
use Livewire\Component;
|
||||
use Livewire\Attributes\Computed;
|
||||
use Livewire\Attributes\Validate;
|
||||
|
||||
new class extends Component {
|
||||
#[Validate('required|integer|exists:invoices,id')]
|
||||
public ?int $invoice_id = null;
|
||||
|
||||
#[Validate('nullable|integer|exists:contacts,id')]
|
||||
public ?int $contact_id = null;
|
||||
|
||||
#[Validate('required|date')]
|
||||
public ?string $payment_date = null;
|
||||
|
||||
#[Validate(['required', new Enum(PaymentStatus::class)])]
|
||||
public ?PaymentStatus $status = PaymentStatus::COMPLETED;
|
||||
|
||||
#[Validate(['required', new Enum(PaymentMethod::class)])]
|
||||
public ?PaymentMethod $payment_method = PaymentMethod::CHECK;
|
||||
|
||||
#[Validate('nullable|string')]
|
||||
public ?string $reference = null;
|
||||
|
||||
#[Validate('required|numeric|min:0.01')]
|
||||
public float $amount = 0;
|
||||
|
||||
#[Validate('nullable|string')]
|
||||
public ?string $notes = null;
|
||||
|
||||
#[Computed]
|
||||
public function invoices()
|
||||
{
|
||||
return Invoice::where('status', InvoiceStatus::POSTED)->with('client')->get()->sortBy('client.abbreviation');
|
||||
}
|
||||
|
||||
#[Computed]
|
||||
public function contacts()
|
||||
{
|
||||
if (!$this->invoice_id) {
|
||||
return collect();
|
||||
}
|
||||
$invoice = Invoice::find($this->invoice_id);
|
||||
return $invoice?->client?->contacts ?? collect();
|
||||
}
|
||||
|
||||
public function updatedInvoiceId(): void
|
||||
{
|
||||
$this->contact_id = null; // Reset when invoice changes
|
||||
}
|
||||
|
||||
|
||||
public function mount(?int $invoice_id = null): void
|
||||
{
|
||||
$this->invoice_id = $invoice_id;
|
||||
$this->payment_date = now()->local()->format('Y-m-d');
|
||||
}
|
||||
|
||||
public function save()
|
||||
{
|
||||
$this->validate();
|
||||
Payment::create([
|
||||
'invoice_id' => $this->invoice_id,
|
||||
'contact_id'=> $this->contact_id,
|
||||
'payment_date' => $this->payment_date,
|
||||
'status' => $this->status,
|
||||
'payment_method' => $this->payment_method,
|
||||
'reference' => $this->reference,
|
||||
'amount' => $this->amount,
|
||||
'notes' => $this->notes,
|
||||
]);
|
||||
|
||||
$this->reset();
|
||||
Flux::modal('create-payment')->close();
|
||||
$this->dispatch('payment-created');
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
?>
|
||||
|
||||
<div>
|
||||
<flux:modal.trigger name="create-payment">
|
||||
<flux:button icon="plus" variant="primary">
|
||||
Create Payment
|
||||
</flux:button>
|
||||
</flux:modal.trigger>
|
||||
|
||||
<flux:modal name="create-payment" class="md:w-96">
|
||||
<form wire:submit="save" class="space-y-6">
|
||||
<flux:heading size="lg">Record Payment</flux:heading>
|
||||
|
||||
<flux:input wire:model="payment_date" label="Payment Date" type="date"/>
|
||||
|
||||
<flux:select wire:model.live="invoice_id" label="Invoice" placeholder="Choose an invoice...">
|
||||
<option value="">Select an invoice...</option>
|
||||
@foreach ($this->invoices as $invoice)
|
||||
<option value="{{ $invoice->id }}">{{ $invoice->client->abbreviation }}
|
||||
- {{ $invoice->invoice_number }} - Balance: {{ formatMoney($invoice->balance_due) }}</option>
|
||||
@endforeach
|
||||
</flux:select>
|
||||
|
||||
<flux:select wire:model="contact_id" label="Contact" placeholder="Choose a contact..."
|
||||
:disabled="!$this->invoice_id">
|
||||
<option value="">No contact recorded</option>
|
||||
@foreach($this->contacts as $contact)
|
||||
<flux:select.option :value="$contact->id">{{ $contact->full_name }}</flux:select.option>
|
||||
@endforeach
|
||||
</flux:select>
|
||||
|
||||
<flux:select wire:model="payment_method" label="Payment Method">
|
||||
@foreach(PaymentMethod::cases() as $method)
|
||||
<flux:select.option :value="$method">{{ $method->label() }}</flux:select.option>
|
||||
@endforeach
|
||||
</flux:select>
|
||||
|
||||
<flux:input wire:model="reference" label="Reference"/>
|
||||
|
||||
<flux:input wire:model="amount" label="Amount" type="number" step="0.01"/>
|
||||
|
||||
<flux:textarea wire:model="notes" label="Notes"/>
|
||||
|
||||
<flux:button type="submit" variant="primary">Save Payment</flux:button>
|
||||
</form>
|
||||
</flux:modal>
|
||||
|
||||
</div>
|
||||
|
|
@ -1,151 +0,0 @@
|
|||
<?php
|
||||
|
||||
use App\Enums\InvoiceStatus;
|
||||
use App\Enums\PaymentMethod;
|
||||
use App\Enums\PaymentStatus;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use Flux\Flux;
|
||||
use Illuminate\Validation\Rules\Enum;
|
||||
use Livewire\Attributes\Computed;
|
||||
use Livewire\Attributes\On;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
new class extends Component {
|
||||
public ?Payment $payment = null;
|
||||
|
||||
#[Validate('required|integer|exists:invoices,id')]
|
||||
public ?int $invoice_id = null;
|
||||
|
||||
#[Validate('nullable|integer|exists:contacts,id')]
|
||||
public ?int $contact_id = null;
|
||||
|
||||
#[Validate('required|date')]
|
||||
public ?string $payment_date = null;
|
||||
|
||||
#[Validate(['required', new Enum(PaymentStatus::class)])]
|
||||
public ?PaymentStatus $status = PaymentStatus::COMPLETED;
|
||||
|
||||
#[Validate(['required', new Enum(PaymentMethod::class)])]
|
||||
public ?PaymentMethod $payment_method = PaymentMethod::CHECK;
|
||||
|
||||
#[Validate('nullable|string')]
|
||||
public ?string $reference = null;
|
||||
|
||||
#[Validate('required|numeric|min:0.01')]
|
||||
public float $amount = 0;
|
||||
|
||||
#[Validate('nullable|string')]
|
||||
public ?string $notes = null;
|
||||
|
||||
public $contacts = [];
|
||||
|
||||
#[Computed]
|
||||
public function invoices()
|
||||
{
|
||||
return Invoice::where('status', InvoiceStatus::POSTED)->with('client')->get()->sortBy('client.abbreviation');
|
||||
}
|
||||
|
||||
public function loadContacts(): void
|
||||
{
|
||||
if (!$this->invoice_id) {
|
||||
$this->contacts = [];
|
||||
return;
|
||||
}
|
||||
$invoice = Invoice::find($this->invoice_id);
|
||||
$this->contacts = $invoice?->client?->contacts ?? collect();
|
||||
}
|
||||
|
||||
public function updatedInvoiceId(): void
|
||||
{
|
||||
$this->loadContacts();
|
||||
$this->contact_id = null;
|
||||
}
|
||||
|
||||
#[On('edit-payment')]
|
||||
public function loadPayment(int $paymentId): void
|
||||
{
|
||||
$this->payment = Payment::findOrFail($paymentId);
|
||||
|
||||
$this->invoice_id = $this->payment->invoice_id;
|
||||
$this->loadContacts();
|
||||
$this->contact_id = $this->payment->contact_id;
|
||||
$this->payment_date = $this->payment->payment_date->format('Y-m-d');
|
||||
$this->status = $this->payment->status;
|
||||
$this->payment_method = $this->payment->payment_method;
|
||||
$this->reference = $this->payment->reference;
|
||||
$this->amount = $this->payment->amount;
|
||||
$this->notes = $this->payment->notes;
|
||||
|
||||
Flux::modal('edit-payment')->show();
|
||||
}
|
||||
|
||||
public function save(): void
|
||||
{
|
||||
$this->validate();
|
||||
|
||||
$this->payment->update([
|
||||
'invoice_id' => $this->invoice_id,
|
||||
'contact_id' => $this->contact_id,
|
||||
'payment_date' => $this->payment_date,
|
||||
'status' => $this->status,
|
||||
'payment_method' => $this->payment_method,
|
||||
'reference' => $this->reference,
|
||||
'amount' => $this->amount,
|
||||
'notes' => $this->notes,
|
||||
]);
|
||||
|
||||
Flux::modal('edit-payment')->close();
|
||||
$this->dispatch('payment-updated');
|
||||
}
|
||||
};
|
||||
?>
|
||||
|
||||
<div>
|
||||
<flux:modal name="edit-payment" class="md:w-96">
|
||||
<form wire:submit="save" class="space-y-6">
|
||||
<flux:heading size="lg">Edit Payment</flux:heading>
|
||||
|
||||
<flux:input wire:model="payment_date" label="Payment Date" type="date"/>
|
||||
|
||||
<flux:select wire:model.live="invoice_id" label="Invoice" placeholder="Choose an invoice...">
|
||||
<option value="">Select an invoice...</option>
|
||||
@foreach ($this->invoices as $invoice)
|
||||
<option value="{{ $invoice->id }}">{{ $invoice->client->abbreviation }}
|
||||
- {{ $invoice->invoice_number }}</option>
|
||||
@endforeach
|
||||
</flux:select>
|
||||
|
||||
<flux:select wire:model="contact_id" label="Contact" placeholder="Choose a contact...">
|
||||
<option value="">No contact recorded</option>
|
||||
@foreach($contacts as $contact)
|
||||
<flux:select.option :value="$contact->id">{{ $contact->full_name }}</flux:select.option>
|
||||
@endforeach
|
||||
</flux:select>
|
||||
|
||||
<flux:select wire:model="status" label="Status">
|
||||
@foreach(PaymentStatus::cases() as $s)
|
||||
<flux:select.option :value="$s">{{ $s->label() }}</flux:select.option>
|
||||
@endforeach
|
||||
</flux:select>
|
||||
|
||||
<flux:select wire:model="payment_method" label="Payment Method">
|
||||
@foreach(PaymentMethod::cases() as $method)
|
||||
<flux:select.option :value="$method">{{ $method->label() }}</flux:select.option>
|
||||
@endforeach
|
||||
</flux:select>
|
||||
|
||||
<flux:input wire:model="reference" label="Reference"/>
|
||||
|
||||
<flux:input wire:model="amount" label="Amount" type="number" step="0.01"/>
|
||||
|
||||
<flux:textarea wire:model="notes" label="Notes"/>
|
||||
|
||||
<div class="flex gap-2">
|
||||
<flux:button type="submit" variant="primary">Update Payment</flux:button>
|
||||
<flux:button type="button" variant="ghost" wire:click="$dispatch('close-modal', 'edit-payment')">Cancel</flux:button>
|
||||
</div>
|
||||
</form>
|
||||
</flux:modal>
|
||||
</div>
|
||||
|
|
@ -70,15 +70,7 @@ new class extends Component {
|
|||
</flux:table.column>
|
||||
<flux:table.column sortable :sorted="$sortBy === 'total'" :direction="$sortDirection"
|
||||
wire:click="sort('total')">
|
||||
Invoice Total
|
||||
</flux:table.column>
|
||||
<flux:table.column sortable :sorted="$sortBy === 'total_payments'" :direction="$sortDirection"
|
||||
wire:click="sort('total_payments')">
|
||||
Total Payments
|
||||
</flux:table.column>
|
||||
<flux:table.column sortable :sorted="$sortBy === 'balance_due'" :direction="$sortDirection"
|
||||
wire:click="sort('balance_due')">
|
||||
Balance Due
|
||||
Total
|
||||
</flux:table.column>
|
||||
<flux:table.column></flux:table.column>
|
||||
</flux:table.columns>
|
||||
|
|
@ -112,8 +104,6 @@ new class extends Component {
|
|||
@endif
|
||||
</flux:table.cell>
|
||||
<flux:table.cell>{{ formatMoney($invoice->total) }}</flux:table.cell>
|
||||
<flux:table.cell>{{ formatMoney($invoice->total_payments) }}</flux:table.cell>
|
||||
<flux:table.cell>{{ formatMoney($invoice->balance_due) }}</flux:table.cell>
|
||||
<flux:table.cell>
|
||||
<flux:dropdown position="bottom" align="start">
|
||||
<flux:button variant="ghost" size="sm" icon="ellipsis-horizontal" inset="top bottom"></flux:button>
|
||||
|
|
|
|||
|
|
@ -1,77 +0,0 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Payment;
|
||||
use Livewire\Attributes\Computed;
|
||||
use Livewire\Attributes\On;
|
||||
use Livewire\Component;
|
||||
|
||||
new class extends Component {
|
||||
|
||||
#[Computed]
|
||||
public function payments()
|
||||
{
|
||||
return Payment::orderBy('payment_date', 'desc')->orderBy('created_at', 'desc')->get();
|
||||
}
|
||||
|
||||
#[On('payment-created')]
|
||||
#[On('payment-updated')]
|
||||
public function refresh(): void
|
||||
{
|
||||
}
|
||||
|
||||
public function deletePayment(Payment $payment): void
|
||||
{
|
||||
$payment->delete();
|
||||
}
|
||||
|
||||
};
|
||||
?>
|
||||
|
||||
<div>
|
||||
<flux:heading size="xl">Payments</flux:heading>
|
||||
|
||||
<flux:table :pagination="$this->payments">
|
||||
<flux:table.columns>
|
||||
<flux:table.column>Payment Date</flux:table.column>
|
||||
<flux:table.column>Invoice</flux:table.column>
|
||||
<flux:table.column>Contact</flux:table.column>
|
||||
<flux:table.column>Status</flux:table.column>
|
||||
<flux:table.column>Payment Method<br/>Reference</flux:table.column>
|
||||
<flux:table.column>Fee Amount</flux:table.column>
|
||||
<flux:table.column>Amount Paid</flux:table.column>
|
||||
<flux:table.column></flux:table.column>
|
||||
</flux:table.columns>
|
||||
|
||||
<flux:table.rows>
|
||||
@foreach ($this->payments as $payment)
|
||||
<flux:table.row>
|
||||
<flux:table.cell>{{ $payment->payment_date->local()->format('m/d/Y') }}</flux:table.cell>
|
||||
<flux:table.cell>{{ $payment->invoice->invoice_number }}</flux:table.cell>
|
||||
<flux:table.cell>{{ $payment->contact->full_name }}</flux:table.cell>
|
||||
<flux:table.cell>{{ $payment->status->label() }}</flux:table.cell>
|
||||
<flux:table.cell>{{ $payment->payment_method->label() }}<br>{{ $payment->reference }}</flux:table.cell>
|
||||
<flux:table.cell>{{ formatMoney($payment->fee_amount) }}</flux:table.cell>
|
||||
<flux:table.cell>{{ formatMoney($payment->amount) }}</flux:table.cell>
|
||||
<flux:table.cell>
|
||||
<flux:dropdown position="bottom" align="end">
|
||||
<flux:button variant="ghost" size="sm" icon="ellipsis-horizontal" inset="top bottom"></flux:button>
|
||||
|
||||
<flux:navmenu>
|
||||
<flux:menu.item
|
||||
wire:click="$dispatch('edit-payment', { paymentId: {{ $payment->id }} })"
|
||||
icon="pencil">Edit
|
||||
</flux:menu.item>
|
||||
<flux:menu.item
|
||||
wire:click="deletePayment({{ $payment->id }})"
|
||||
wire:confirm="Are you sure you want to delete this payment?"
|
||||
icon="trash"
|
||||
variant="danger">Delete
|
||||
</flux:menu.item>
|
||||
</flux:navmenu>
|
||||
</flux:dropdown>
|
||||
</flux:table.cell>
|
||||
</flux:table.row>
|
||||
@endforeach
|
||||
</flux:table.rows>
|
||||
</flux:table>
|
||||
</div>
|
||||
|
|
@ -67,76 +67,6 @@
|
|||
</tfoot>
|
||||
</table>
|
||||
|
||||
@php
|
||||
$completedPayments = $invoice->payments->where('status', \App\Enums\PaymentStatus::COMPLETED);
|
||||
$pendingPayments = $invoice->payments->where('status', '!=', \App\Enums\PaymentStatus::COMPLETED);
|
||||
@endphp
|
||||
|
||||
@if($completedPayments->count() > 0)
|
||||
<div class="mb-8">
|
||||
<h2 class="text-sm font-semibold text-gray-500 uppercase mb-4">Payments Received</h2>
|
||||
<table class="w-full">
|
||||
<thead>
|
||||
<tr class="border-b border-gray-300">
|
||||
<th class="text-left py-2 text-sm font-semibold text-gray-600">Date</th>
|
||||
<th class="text-left py-2 text-sm font-semibold text-gray-600">Method</th>
|
||||
<th class="text-left py-2 text-sm font-semibold text-gray-600">Reference</th>
|
||||
<th class="text-right py-2 text-sm font-semibold text-gray-600">Amount</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($completedPayments as $payment)
|
||||
<tr class="border-b border-gray-200">
|
||||
<td class="py-2 text-gray-600">{{ $payment->payment_date->format('F j, Y') }}</td>
|
||||
<td class="py-2 text-gray-600">{{ $payment->payment_method->label() }}</td>
|
||||
<td class="py-2 text-gray-600">{{ $payment->reference }}</td>
|
||||
<td class="py-2 text-right text-gray-800">{{ formatMoney($payment->amount) }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr class="border-t border-gray-300">
|
||||
<td colspan="3" class="py-2 text-right font-semibold text-gray-800">Total Payments</td>
|
||||
<td class="py-2 text-right font-semibold text-gray-800">{{ formatMoney($invoice->total_payments) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3" class="py-2 text-right font-bold text-gray-800">Balance Due</td>
|
||||
<td class="py-2 text-right font-bold text-gray-800 text-lg">{{ formatMoney($invoice->balance_due) }}</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if($pendingPayments->count() > 0)
|
||||
<div class="mb-8">
|
||||
<h2 class="text-sm font-semibold text-gray-500 uppercase mb-4">Pending Payments</h2>
|
||||
<table class="w-full">
|
||||
<thead>
|
||||
<tr class="border-b border-gray-300">
|
||||
<th class="text-left py-2 text-sm font-semibold text-gray-600">Date</th>
|
||||
<th class="text-left py-2 text-sm font-semibold text-gray-600">Method</th>
|
||||
<th class="text-left py-2 text-sm font-semibold text-gray-600">Reference</th>
|
||||
<th class="text-left py-2 text-sm font-semibold text-gray-600">Status</th>
|
||||
<th class="text-right py-2 text-sm font-semibold text-gray-600">Amount</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($pendingPayments as $payment)
|
||||
<tr class="border-b border-gray-200">
|
||||
<td class="py-2 text-gray-600">{{ $payment->payment_date->format('F j, Y') }}</td>
|
||||
<td class="py-2 text-gray-600">{{ $payment->payment_method->label() }}</td>
|
||||
<td class="py-2 text-gray-600">{{ $payment->reference }}</td>
|
||||
<td class="py-2 text-gray-600">{{ $payment->status->label() }}</td>
|
||||
<td class="py-2 text-right text-gray-800">{{ formatMoney($payment->amount) }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if($invoice->balance_due != 0)
|
||||
<div class="mb-12 p-4 bg-gray-50 rounded">
|
||||
<h2 class="text-sm font-semibold text-gray-500 uppercase mb-2">Payment</h2>
|
||||
<p class="text-gray-700">Please make payment to:</p>
|
||||
|
|
@ -144,7 +74,6 @@
|
|||
<p class="text-gray-600">540 W. Louse Ave.</p>
|
||||
<p class="text-gray-600">Vinita, OK 74301</p>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if($invoice->notes)
|
||||
<div class="border-t pt-6">
|
||||
|
|
|
|||
|
|
@ -33,10 +33,6 @@
|
|||
{{ __('Invoices') }}
|
||||
</flux:sidebar.item>
|
||||
|
||||
<flux:sidebar.item icon="currency-dollar" :href="route('payments')" :current="request()->routeIs('payments.*')" wire:navigate>
|
||||
{{ __('Payments') }}
|
||||
</flux:sidebar.item>
|
||||
|
||||
</flux:sidebar.group>
|
||||
</flux:sidebar.nav>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
<x-layouts::app :title="__('Clients')">
|
||||
<div class="max-w-7xl mx-auto space-y-4">
|
||||
<div class="flex justify-end">
|
||||
<livewire:create-payment />
|
||||
</div>
|
||||
<livewire:payment-list />
|
||||
<livewire:edit-payment />
|
||||
</div>
|
||||
</x-layouts::app>
|
||||
|
|
@ -17,7 +17,6 @@ Route::middleware(['auth', 'verified'])->group(function () {
|
|||
Route::get('invoices/{invoice}/edit',
|
||||
fn (Invoice $invoice) => view('invoices.edit', compact('invoice')))->name('invoices.edit');
|
||||
Route::get('invoices/{invoice}', CustomerInvoiceController::class)->name('invoices.show');
|
||||
Route::view('payments', 'payments.index')->name('payments');
|
||||
});
|
||||
|
||||
// Route::view('dashboard', 'dashboard')
|
||||
|
|
|
|||
Loading…
Reference in New Issue