From 21bcd43972b4112ce9d222ebfb592ca2572b14cb Mon Sep 17 00:00:00 2001 From: Matt Young Date: Wed, 28 Jan 2026 16:54:47 -0600 Subject: [PATCH] Initial payment setup --- app/Enums/PaymentMethod.php | 19 ++++++++ app/Enums/PaymentStatus.php | 31 ++++++++++++ app/Models/Client.php | 7 +++ app/Models/Payment.php | 48 +++++++++++++++++++ ...026_01_28_221112_create_payments_table.php | 39 +++++++++++++++ 5 files changed, 144 insertions(+) create mode 100644 app/Enums/PaymentMethod.php create mode 100644 app/Enums/PaymentStatus.php create mode 100644 app/Models/Payment.php create mode 100644 database/migrations/2026_01_28_221112_create_payments_table.php diff --git a/app/Enums/PaymentMethod.php b/app/Enums/PaymentMethod.php new file mode 100644 index 0000000..7d1a635 --- /dev/null +++ b/app/Enums/PaymentMethod.php @@ -0,0 +1,19 @@ + 'Cash', + self::CHECK => 'Check', + self::CARD => 'Card', + }; + } +} \ No newline at end of file diff --git a/app/Enums/PaymentStatus.php b/app/Enums/PaymentStatus.php new file mode 100644 index 0000000..d6ab8e2 --- /dev/null +++ b/app/Enums/PaymentStatus.php @@ -0,0 +1,31 @@ + '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', + }; + } +} \ No newline at end of file diff --git a/app/Models/Client.php b/app/Models/Client.php index 1bdfa0c..918bdc1 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -9,6 +9,8 @@ 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; @@ -41,6 +43,11 @@ 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) diff --git a/app/Models/Payment.php b/app/Models/Payment.php new file mode 100644 index 0000000..55b9b71 --- /dev/null +++ b/app/Models/Payment.php @@ -0,0 +1,48 @@ + 'date', + 'amount' => MoneyCast::class, + 'fee_amount' => MoneyCast::class, + 'status' => PaymentStatus::class, + 'payment_method' => PaymentMethod::class, + ]; + + 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(); + } +} diff --git a/database/migrations/2026_01_28_221112_create_payments_table.php b/database/migrations/2026_01_28_221112_create_payments_table.php new file mode 100644 index 0000000..f8977e0 --- /dev/null +++ b/database/migrations/2026_01_28_221112_create_payments_table.php @@ -0,0 +1,39 @@ +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'); + } +};