Working on invoice payments

This commit is contained in:
David Bomba 2019-05-13 16:18:46 +10:00
parent 9b86780fa3
commit ad76169b6c
13 changed files with 287 additions and 13 deletions

View File

@ -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;
}
}

View File

@ -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;
}

View 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;
}
}

View 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);
}
}

View File

@ -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;

View 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);
}
}

View File

@ -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 */
/* ---------------- */

View File

@ -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');
}
}

View File

@ -23,7 +23,7 @@ class PaymentObserver
*/
public function created(Payment $payment)
{
//
}
/**

View File

@ -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',
],

View File

@ -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();

View File

@ -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;

View File

@ -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');