mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-05-24 02:14:21 -04:00
Working on invoice payments
This commit is contained in:
parent
9b86780fa3
commit
ad76169b6c
@ -22,17 +22,17 @@ class PaymentWasCreated
|
||||
use SerializesModels;
|
||||
|
||||
/**
|
||||
* @var Payment
|
||||
* @var array $data
|
||||
*/
|
||||
public $payment;
|
||||
public $data;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param Payment $payment
|
||||
* @param array $data
|
||||
*/
|
||||
public function __construct(Payment $payment)
|
||||
public function __construct(array $data)
|
||||
{
|
||||
$this->payment = $payment;
|
||||
$this->data = $data;
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ namespace App\Factory;
|
||||
use App\DataMapper\ClientSettings;
|
||||
use App\DataMapper\CompanySettings;
|
||||
use App\Models\Payment;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class PaymentFactory
|
||||
@ -31,10 +32,11 @@ class PaymentFactory
|
||||
$payment->payment_type_id = null;
|
||||
$payment->is_deleted = false;
|
||||
$payment->amount = 0;
|
||||
$payment->payment_date = null;
|
||||
$payment->payment_date = Carbon::now();
|
||||
$payment->transaction_reference = null;
|
||||
$payment->payer_id = null;
|
||||
$payment->invoice_id = 0;
|
||||
$payment->status_id = Payment::STATUS_PENDING;
|
||||
|
||||
return $payment;
|
||||
}
|
||||
|
85
app/Jobs/Invoice/ApplyPaymentToInvoice.php
Normal file
85
app/Jobs/Invoice/ApplyPaymentToInvoice.php
Normal file
@ -0,0 +1,85 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2019. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Jobs\Invoice;
|
||||
|
||||
use App\Models\Invoice;
|
||||
use App\Repositories\InvoiceRepository;
|
||||
use App\Utils\Traits\NumberFormatter;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class ApplyPaymentToInvoice implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, NumberFormatter;
|
||||
|
||||
public $invoice;
|
||||
|
||||
public $payment;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Payment $payment, Invoice $invoice)
|
||||
{
|
||||
|
||||
$this->invoice = $invoice;
|
||||
|
||||
$this->payment = $payment;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
|
||||
$adjustment = $this->payment->amount * -1;
|
||||
|
||||
$partial = max(0, $this->invoice->partial - $this->payment->amount);
|
||||
|
||||
//check if partial exists
|
||||
if($this->invoice->partial > 0)
|
||||
{
|
||||
|
||||
//if payment amount = partial
|
||||
if( $this->formatvalue($this->invoice->partial,4) == $this->formatValue($this->payment->amount,4) )
|
||||
{
|
||||
$this->invoice->partial = 0;
|
||||
}
|
||||
|
||||
//if payment amount < partial amount
|
||||
if( $this->formatvalue($this->invoice->partial,4) > $this->formatValue($this->payment->amount,4) )
|
||||
{
|
||||
//set the new partial amount to the balance
|
||||
$this->invoice->partial = $partial;
|
||||
}
|
||||
|
||||
|
||||
$this->partial_due_date = null;
|
||||
$this->due_date =
|
||||
}
|
||||
|
||||
|
||||
$this->balance = $this->balance + $adjustment;
|
||||
|
||||
|
||||
}
|
||||
}
|
78
app/Jobs/Invoice/MarkPaid.php
Normal file
78
app/Jobs/Invoice/MarkPaid.php
Normal file
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2019. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Jobs\Invoice;
|
||||
|
||||
use App\Factory\PaymentFactory;
|
||||
use App\Jobs\Invoice\ApplyPaymentToInvoice;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Repositories\InvoiceRepository;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class MarkPaid implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $invoice;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Invoice $invoice)
|
||||
{
|
||||
|
||||
$this->invoice = $invoice;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
/* Create Payment */
|
||||
$payment = new PaymentFactory($this->invoice->company_id, $this->invoice->user_id);
|
||||
|
||||
$payment->amount = $invoice->balance;
|
||||
$payment->status_id = Payment::STATUS_COMPLETED;
|
||||
$payment->client_id = $invoice->client_id;
|
||||
|
||||
$payment->save();
|
||||
|
||||
/* Create a payment relationship to the invoice entity */
|
||||
$payment->invoices()->save($this->invoice);
|
||||
|
||||
|
||||
/* Need to engineer the ability to pass an array of invoices to the activity handler*/
|
||||
$data = [
|
||||
'payment_id' => $payment->id,
|
||||
'invoice_ids' => [
|
||||
$this->invoice->id
|
||||
]
|
||||
];
|
||||
|
||||
event(new PaymentWasCreated($data));
|
||||
|
||||
/* Update Invoice balance */
|
||||
ApplyPaymentToInvoice::dispatchNow($payment, $invoice);
|
||||
|
||||
}
|
||||
}
|
@ -9,7 +9,7 @@
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Listeners\Client;
|
||||
namespace App\Listeners\Activity;
|
||||
|
||||
use App\Models\Activity;
|
||||
use App\Repositories\ActivityRepository;
|
65
app/Listeners/Activity/PaymentCreatedActivity.php
Normal file
65
app/Listeners/Activity/PaymentCreatedActivity.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2019. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Listeners\Activity;
|
||||
|
||||
use App\Models\Activity;
|
||||
use App\Models\Payment;
|
||||
use App\Repositories\ActivityRepository;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
|
||||
class PaymentCreatedActivity
|
||||
{
|
||||
protected $activityRepo;
|
||||
/**
|
||||
* Create the event listener.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(ActivityRepository $activityRepo)
|
||||
{
|
||||
$this->activityRepo = $activityRepo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param object $event
|
||||
* @return void
|
||||
*/
|
||||
public function handle($event)
|
||||
{
|
||||
$data = $event->data;
|
||||
|
||||
$payment = Payment::find($data['payment_id']);
|
||||
|
||||
$invoices = Invoice::find($data['invoice_ids']);
|
||||
|
||||
$fields = new \stdClass;
|
||||
|
||||
$fields->payment_id = $payment->id;
|
||||
$fields->user_id = $payment->user_id;
|
||||
$fields->company_id = $payment->company_id;
|
||||
$fields->activity_type_id = Activity::CREATE_PAYMENT;
|
||||
|
||||
|
||||
foreach($invoices as $invoice) //todo we may need to add additional logic if in the future we apply payments to other entity Types, not just invoices
|
||||
{
|
||||
$fields->invoice_id = $invoice->id;
|
||||
|
||||
$this->activityRepo->save($fields, $invoice);
|
||||
}
|
||||
|
||||
if( count( $invoices ) == 0 )
|
||||
$this->activityRepo->save($fields, $payment);
|
||||
}
|
||||
}
|
@ -75,6 +75,11 @@ class Invoice extends BaseModel
|
||||
return $this->morphMany(Document::class, 'documentable');
|
||||
}
|
||||
|
||||
public function payments()
|
||||
{
|
||||
return $this->morphToMany(Payment::class, 'paymentable');
|
||||
}
|
||||
|
||||
/* ---------------- */
|
||||
/* Settings getters */
|
||||
/* ---------------- */
|
||||
|
@ -19,7 +19,14 @@ class Payment extends BaseModel
|
||||
{
|
||||
use MakesHash;
|
||||
use Filterable;
|
||||
|
||||
|
||||
const STATUS_PENDING = 1;
|
||||
const STATUS_VOIDED = 2;
|
||||
const STATUS_FAILED = 3;
|
||||
const STATUS_COMPLETED = 4;
|
||||
const STATUS_PARTIALLY_REFUNDED = 5;
|
||||
const STATUS_REFUNDED = 6;
|
||||
|
||||
protected $guarded = [
|
||||
'id',
|
||||
];
|
||||
@ -43,4 +50,9 @@ class Payment extends BaseModel
|
||||
{
|
||||
return $this->morphMany(Document::class, 'documentable');
|
||||
}
|
||||
|
||||
public function invoices()
|
||||
{
|
||||
return $this->morphedByMany(Invoice::class, 'paymentable');
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ class PaymentObserver
|
||||
*/
|
||||
public function created(Payment $payment)
|
||||
{
|
||||
//
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -13,8 +13,10 @@ namespace App\Providers;
|
||||
|
||||
use App\Events\Client\ClientWasCreated;
|
||||
use App\Events\Invoice\InvoiceWasMarkedSent;
|
||||
use App\Events\Payment\PaymentWasCreated;
|
||||
use App\Events\User\UserCreated;
|
||||
use App\Listeners\Client\CreatedClientActivity;
|
||||
use App\Listeners\Activity\PaymentCreatedActivity;
|
||||
use App\Listeners\Activity\CreatedClientActivity;
|
||||
use App\Listeners\Invoice\CreateInvoiceInvitations;
|
||||
use App\Listeners\SendVerificationNotification;
|
||||
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
|
||||
@ -36,6 +38,9 @@ class EventServiceProvider extends ServiceProvider
|
||||
CreatedClientActivity::class,
|
||||
// 'App\Listeners\SubscriptionListener@createdClient',
|
||||
],
|
||||
PaymentWasCreated::class => [
|
||||
PaymentCreatedActivity::class,
|
||||
],
|
||||
'App\Events\ClientWasArchived' => [
|
||||
'App\Listeners\ActivityListener@archivedClient',
|
||||
],
|
||||
|
@ -14,9 +14,18 @@ namespace App\Repositories;
|
||||
use App\Models\Activity;
|
||||
use App\Models\Backup;
|
||||
|
||||
/**
|
||||
* Class for activity repository.
|
||||
*/
|
||||
class ActivityRepository extends BaseRepository
|
||||
{
|
||||
|
||||
/**
|
||||
* Save the Activity
|
||||
*
|
||||
* @param stdClass $fields The fields
|
||||
* @param Collection $entity The entity that you wish to have backed up (typically Invoice, Quote etc etc rather than Payment)
|
||||
*/
|
||||
public function save($fields, $entity)
|
||||
{
|
||||
$activity = new Activity();
|
||||
@ -34,6 +43,12 @@ class ActivityRepository extends BaseRepository
|
||||
$this->createBackup($entity, $activity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a backup.
|
||||
*
|
||||
* @param Collection $entity The entity
|
||||
* @param Collection $activity The activity
|
||||
*/
|
||||
public function createBackup($entity, $activity)
|
||||
{
|
||||
$backup = new Backup();
|
||||
|
@ -65,8 +65,8 @@ class InvoiceRepository extends BaseRepository
|
||||
*/
|
||||
public function markSent(Invoice $invoice) : ?Invoice
|
||||
{
|
||||
|
||||
if($invoice->status_id >= Invoice::STATUS_SENT)
|
||||
/* Return immediately if status is not draft*/
|
||||
if($invoice->status_id != Invoice::STATUS_DRAFT)
|
||||
return $invoice;
|
||||
|
||||
$invoice->status_id = Invoice::STATUS_SENT;
|
||||
|
@ -665,7 +665,7 @@ class CreateUsersTable extends Migration
|
||||
|
||||
Schema::create('payments', function ($t) {
|
||||
$t->increments('id');
|
||||
$t->unsignedInteger('invoice_id')->nullable()->index(); //todo handle payments where there is no invoice OR we are paying MULTIPLE invoices
|
||||
//$t->unsignedInteger('invoice_id')->nullable()->index(); //todo handle payments where there is no invoice OR we are paying MULTIPLE invoices
|
||||
$t->unsignedInteger('company_id')->index();
|
||||
$t->unsignedInteger('client_id')->index();
|
||||
$t->unsignedInteger('client_contact_id')->nullable();
|
||||
@ -673,6 +673,8 @@ class CreateUsersTable extends Migration
|
||||
$t->unsignedInteger('user_id')->nullable();
|
||||
$t->unsignedInteger('account_gateway_id')->nullable();
|
||||
$t->unsignedInteger('payment_type_id')->nullable();
|
||||
$t->unsignedInteger('status_id')->index();
|
||||
|
||||
$t->timestamps(6);
|
||||
$t->softDeletes();
|
||||
|
||||
@ -693,6 +695,11 @@ class CreateUsersTable extends Migration
|
||||
|
||||
});
|
||||
|
||||
Schema::create('paymentables', function ($table) {
|
||||
$table->unsignedInteger('payment_id');
|
||||
$table->unsignedInteger('paymentable_id');
|
||||
$table->string('paymentable_type');
|
||||
});
|
||||
|
||||
Schema::create('payment_libraries', function ($t) {
|
||||
$t->increments('id');
|
||||
|
Loading…
x
Reference in New Issue
Block a user