mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-05 18:52:44 +01:00
Payment deletes (#3079)
* Add amount to paymentable tables to enable reversing payments gracefully * Create Test Data artisan comannd * Delete Payments + Fixes for company settings persistence
This commit is contained in:
parent
1c4b05c212
commit
ff17e3eb67
220
app/Console/Commands/CreateTestData.php
Normal file
220
app/Console/Commands/CreateTestData.php
Normal file
@ -0,0 +1,220 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\DataMapper\DefaultSettings;
|
||||
use App\Events\Invoice\InvoiceWasMarkedSent;
|
||||
use App\Events\Payment\PaymentWasCreated;
|
||||
use App\Factory\ClientFactory;
|
||||
use App\Factory\InvoiceFactory;
|
||||
use App\Factory\InvoiceItemFactory;
|
||||
use App\Factory\PaymentFactory;
|
||||
use App\Helpers\Invoice\InvoiceSum;
|
||||
use App\Jobs\Company\UpdateCompanyLedgerWithInvoice;
|
||||
use App\Jobs\Invoice\UpdateInvoicePayment;
|
||||
use App\Listeners\Invoice\CreateInvoiceInvitation;
|
||||
use App\Models\CompanyToken;
|
||||
use App\Models\Payment;
|
||||
use App\Models\PaymentType;
|
||||
use App\Models\User;
|
||||
use App\Repositories\InvoiceRepository;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateTestData extends Command
|
||||
{
|
||||
use MakesHash;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create Test Data';
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'ninja:create-test-data {count=1}';
|
||||
|
||||
protected $invoice_repo;
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(InvoiceRepository $invoice_repo)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->invoice_repo = $invoice_repo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->info(date('r').' Running CreateTestData...');
|
||||
$this->count = $this->argument('count');
|
||||
|
||||
$this->info('Warming up cache');
|
||||
|
||||
$this->warmCache();
|
||||
|
||||
|
||||
$this->info('Creating Account and Company');
|
||||
|
||||
$account = factory(\App\Models\Account::class)->create();
|
||||
$company = factory(\App\Models\Company::class)->create([
|
||||
'account_id' => $account->id,
|
||||
'domain' => 'ninja.test:8000',
|
||||
]);
|
||||
|
||||
$account->default_company_id = $company->id;
|
||||
$account->save();
|
||||
|
||||
$user = User::whereEmail('user@example.com')->first();
|
||||
|
||||
if(!$user)
|
||||
{
|
||||
$user = factory(\App\Models\User::class)->create([
|
||||
// 'account_id' => $account->id,
|
||||
'email' => 'user@example.com',
|
||||
'confirmation_code' => $this->createDbHash(config('database.default'))
|
||||
]);
|
||||
}
|
||||
|
||||
$token = \Illuminate\Support\Str::random(64);
|
||||
|
||||
$company_token = CompanyToken::create([
|
||||
'user_id' => $user->id,
|
||||
'company_id' => $company->id,
|
||||
'account_id' => $account->id,
|
||||
'name' => 'test token',
|
||||
'token' => $token,
|
||||
]);
|
||||
|
||||
$user->companies()->attach($company->id, [
|
||||
'account_id' => $account->id,
|
||||
'is_owner' => 1,
|
||||
'is_admin' => 1,
|
||||
'is_locked' => 0,
|
||||
'permissions' => json_encode([]),
|
||||
'settings' => json_encode(DefaultSettings::userSettings()),
|
||||
]);
|
||||
|
||||
|
||||
$this->info('Creating '.$this->count. ' clients');
|
||||
|
||||
|
||||
for($x=0; $x<$this->count; $x++) {
|
||||
$z = $x+1;
|
||||
$this->info("Creating client # ".$z);
|
||||
|
||||
$this->createClient($company, $user);
|
||||
}
|
||||
}
|
||||
|
||||
private function createClient($company, $user)
|
||||
{
|
||||
$client = ClientFactory::create($company->id, $user->id);
|
||||
$client->save();
|
||||
|
||||
$y = $this->count * rand(1,5);
|
||||
|
||||
$this->info("Creating {$y} invoices");
|
||||
|
||||
for($x=0; $x<$y; $x++){
|
||||
|
||||
$this->createInvoice($client);
|
||||
}
|
||||
}
|
||||
|
||||
private function createInvoice($client)
|
||||
{
|
||||
$invoice = InvoiceFactory::create($client->company->id,$client->user->id);//stub the company and user_id
|
||||
$invoice->client_id = $client->id;
|
||||
|
||||
$invoice->line_items = $this->buildLineItems();
|
||||
$invoice->uses_inclusive_Taxes = false;
|
||||
|
||||
$invoice->save();
|
||||
|
||||
$invoice_calc = new InvoiceSum($invoice);
|
||||
$invoice_calc->build();
|
||||
|
||||
$invoice = $invoice_calc->getInvoice();
|
||||
|
||||
$invoice->save();
|
||||
|
||||
event(new CreateInvoiceInvitation($invoice));
|
||||
|
||||
UpdateCompanyLedgerWithInvoice::dispatchNow($invoice, $invoice->balance);
|
||||
|
||||
$this->invoice_repo->markSent($invoice);
|
||||
|
||||
event(new InvoiceWasMarkedSent($invoice));
|
||||
|
||||
if(rand(0, 1)) {
|
||||
|
||||
$payment = PaymentFactory::create($client->company->id, $client->user->id);
|
||||
$payment->payment_date = now();
|
||||
$payment->client_id = $client->id;
|
||||
$payment->amount = $invoice->balance;
|
||||
$payment->transaction_reference = rand(0,500);
|
||||
$payment->payment_type_id = PaymentType::CREDIT_CARD_OTHER;
|
||||
$payment->status_id = Payment::STATUS_COMPLETED;
|
||||
$payment->save();
|
||||
|
||||
$payment->invoices()->save($invoice);
|
||||
|
||||
event(new PaymentWasCreated($payment));
|
||||
|
||||
UpdateInvoicePayment::dispatchNow($payment);
|
||||
}
|
||||
}
|
||||
|
||||
private function buildLineItems()
|
||||
{
|
||||
$line_items = [];
|
||||
|
||||
$item = InvoiceItemFactory::create();
|
||||
$item->quantity = 1;
|
||||
$item->cost =10;
|
||||
|
||||
$line_items[] = $item;
|
||||
|
||||
return $line_items;
|
||||
|
||||
}
|
||||
|
||||
private function warmCache()
|
||||
{
|
||||
/* Warm up the cache !*/
|
||||
$cached_tables = config('ninja.cached_tables');
|
||||
|
||||
foreach ($cached_tables as $name => $class) {
|
||||
if (! Cache::has($name)) {
|
||||
// check that the table exists in case the migration is pending
|
||||
if (! Schema::hasTable((new $class())->getTable())) {
|
||||
continue;
|
||||
}
|
||||
if ($name == 'payment_terms') {
|
||||
$orderBy = 'num_days';
|
||||
} elseif ($name == 'fonts') {
|
||||
$orderBy = 'sort_order';
|
||||
} elseif (in_array($name, ['currencies', 'industries', 'languages', 'countries', 'banks'])) {
|
||||
$orderBy = 'name';
|
||||
} else {
|
||||
$orderBy = 'id';
|
||||
}
|
||||
$tableData = $class::orderBy($orderBy)->get();
|
||||
if ($tableData->count()) {
|
||||
Cache::forever($name, $tableData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -22,6 +22,7 @@ use App\Http\Requests\Payment\ShowPaymentRequest;
|
||||
use App\Http\Requests\Payment\StorePaymentRequest;
|
||||
use App\Http\Requests\Payment\UpdatePaymentRequest;
|
||||
use App\Jobs\Entity\ActionEntity;
|
||||
use App\Jobs\Invoice\ReverseInvoicePayment;
|
||||
use App\Models\Payment;
|
||||
use App\Repositories\BaseRepository;
|
||||
use App\Repositories\PaymentRepository;
|
||||
@ -483,10 +484,12 @@ class PaymentController extends BaseController
|
||||
public function destroy(DestroyPaymentRequest $request, Payment $payment)
|
||||
{
|
||||
|
||||
ReverseInvoicePayment::dispatchNow($payment);
|
||||
|
||||
$payment->is_deleted = true;
|
||||
$payment->delete();
|
||||
|
||||
return response()->json([], 200);
|
||||
return $this->itemResponse($payment);
|
||||
|
||||
}
|
||||
|
||||
|
@ -27,4 +27,18 @@ class DestroyPaymentRequest extends Request
|
||||
return auth()->user()->can('edit', $this->payment);
|
||||
}
|
||||
|
||||
// public function rules()
|
||||
// {
|
||||
// return [
|
||||
// 'deletable'
|
||||
// ];
|
||||
// }
|
||||
|
||||
// public function messages()
|
||||
// {
|
||||
// return [
|
||||
// 'deletable' => 'Payment cannot be deleted',
|
||||
// ];
|
||||
|
||||
// }
|
||||
}
|
@ -59,8 +59,10 @@ class MarkInvoicePaid implements ShouldQueue
|
||||
$payment->transaction_reference = ctrans('texts.manual_entry');
|
||||
/* Create a payment relationship to the invoice entity */
|
||||
$payment->save();
|
||||
$payment->invoices()->save($this->invoice);
|
||||
$payment->save();
|
||||
|
||||
$payment->invoices()->attach($this->invoice->id,[
|
||||
'amount' => $payment->amount
|
||||
]);
|
||||
|
||||
$this->invoice->updateBalance($payment->amount*-1);
|
||||
|
||||
|
73
app/Jobs/Invoice/ReverseInvoicePayment.php
Normal file
73
app/Jobs/Invoice/ReverseInvoicePayment.php
Normal file
@ -0,0 +1,73 @@
|
||||
<?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\Jobs\Client\UpdateClientBalance;
|
||||
use App\Jobs\Company\UpdateCompanyLedgerWithInvoice;
|
||||
use App\Jobs\Company\UpdateCompanyLedgerWithPayment;
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Models\SystemLog;
|
||||
use App\Utils\Traits\SystemLogTrait;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class ReverseInvoicePayment implements ShouldQueue
|
||||
{
|
||||
use SystemLogTrait, Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $payment;
|
||||
/**
|
||||
* Create the event listener.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Payment $payment)
|
||||
{
|
||||
$this->payment = $payment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param object $event
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
|
||||
$invoices = $this->payment->invoices()->get();
|
||||
$client = $this->payment->client;
|
||||
|
||||
$invoices->each(function($invoice){
|
||||
|
||||
if($invoice->pivot->amount > 0)
|
||||
{
|
||||
$invoice->status_id = Invoice::STATUS_SENT;
|
||||
$invoice->balance = $invoice->pivot->amount;
|
||||
$invoice->save();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
UpdateCompanyLedgerWithPayment::dispatchNow($this->payment, ($this->payment->amount));
|
||||
|
||||
UpdateClientBalance::dispatchNow($client, $this->payment->amount);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -58,9 +58,14 @@ class UpdateInvoicePayment implements ShouldQueue
|
||||
$invoices->each(function ($invoice){
|
||||
|
||||
UpdateCompanyLedgerWithPayment::dispatchNow($this->payment, ($invoice->balance*-1));
|
||||
|
||||
$invoice->pivot->amount = $invoice->balance;
|
||||
$invoice->pivot->save();
|
||||
|
||||
$invoice->clearPartial();
|
||||
$invoice->updateBalance($invoice->balance*-1);
|
||||
|
||||
|
||||
|
||||
UpdateClientBalance::dispatchNow($this->payment->client, $invoice->balance*-1);
|
||||
|
||||
});
|
||||
@ -91,6 +96,10 @@ class UpdateInvoicePayment implements ShouldQueue
|
||||
if($invoice->hasPartial()) {
|
||||
|
||||
UpdateCompanyLedgerWithPayment::dispatchNow($this->payment, ($invoice->partial*-1));
|
||||
|
||||
$invoice->pivot->amount = $invoice->partial;
|
||||
$invoice->pivot->save();
|
||||
|
||||
$invoice->updateBalance($invoice->partial*-1);
|
||||
$invoice->clearPartial();
|
||||
$invoice->setDueDate();
|
||||
@ -102,6 +111,10 @@ class UpdateInvoicePayment implements ShouldQueue
|
||||
else
|
||||
{
|
||||
UpdateCompanyLedgerWithPayment::dispatchNow($this->payment, ($invoice->balance*-1));
|
||||
|
||||
$invoice->pivot->amount = $invoice->balance;
|
||||
$invoice->pivot->save();
|
||||
|
||||
$invoice->clearPartial();
|
||||
$invoice->updateBalance($invoice->balance*-1);
|
||||
|
||||
|
65
app/Listeners/Activity/PaymentDeletedActivity.php
Normal file
65
app/Listeners/Activity/PaymentDeletedActivity.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\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Repositories\ActivityRepository;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
|
||||
class PaymentDeletedActivity implements ShouldQueue
|
||||
{
|
||||
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)
|
||||
{
|
||||
$payment = $event->payment;
|
||||
|
||||
$invoices = $payment->invoices;
|
||||
|
||||
$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::DELETE_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);
|
||||
}
|
||||
}
|
@ -91,7 +91,7 @@ class Payment extends BaseModel
|
||||
|
||||
public function invoices()
|
||||
{
|
||||
return $this->morphedByMany(Invoice::class, 'paymentable');
|
||||
return $this->morphedByMany(Invoice::class, 'paymentable')->withPivot('amount');
|
||||
}
|
||||
|
||||
public function company_ledger()
|
||||
|
23
app/Models/Paymentable.php
Normal file
23
app/Models/Paymentable.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?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\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Relations\Pivot;
|
||||
|
||||
class Paymentable extends Pivot
|
||||
{
|
||||
// protected $guarded = ['id'];
|
||||
|
||||
// public $incrementing = true;
|
||||
|
||||
|
||||
}
|
@ -17,10 +17,12 @@ use App\Events\Invoice\InvoiceWasCreated;
|
||||
use App\Events\Invoice\InvoiceWasMarkedSent;
|
||||
use App\Events\Invoice\InvoiceWasUpdated;
|
||||
use App\Events\Payment\PaymentWasCreated;
|
||||
use App\Events\Payment\PaymentWasDeleted;
|
||||
use App\Events\User\UserLoggedIn;
|
||||
use App\Events\User\UserWasCreated;
|
||||
use App\Listeners\Activity\CreatedClientActivity;
|
||||
use App\Listeners\Activity\PaymentCreatedActivity;
|
||||
use App\Listeners\Activity\PaymentDeletedActivity;
|
||||
use App\Listeners\Contact\UpdateContactLastLogin;
|
||||
use App\Listeners\Invoice\CreateInvoiceActivity;
|
||||
use App\Listeners\Invoice\CreateInvoiceInvitation;
|
||||
@ -59,6 +61,9 @@ class EventServiceProvider extends ServiceProvider
|
||||
//UpdateInvoicePayment::class,
|
||||
UpdateInvoiceInvitations::class,
|
||||
],
|
||||
PaymentWasDeleted::class => [
|
||||
PaymentDeletedActivity::class
|
||||
],
|
||||
'App\Events\ClientWasArchived' => [
|
||||
'App\Listeners\ActivityListener@archivedClient',
|
||||
],
|
||||
|
@ -157,7 +157,7 @@ class InvoiceRepository extends BaseRepository
|
||||
*/
|
||||
$invoice = ApplyInvoiceNumber::dispatchNow($invoice, $invoice->client->getMergedSettings());
|
||||
|
||||
UpdateCompanyLedgerWithInvoice::dispatchNow($invoice, $this->balance);
|
||||
UpdateCompanyLedgerWithInvoice::dispatchNow($invoice, $invoice->balance);
|
||||
|
||||
return $invoice;
|
||||
|
||||
|
@ -43,12 +43,17 @@ trait CompanySettingsSaver
|
||||
$company_settings = CompanySettings::defaults();
|
||||
|
||||
//Iterate and set CURRENT settings
|
||||
foreach($this->settings as $key => $value)
|
||||
$company_settings->{$key} = $value;
|
||||
// foreach($this->settings as $key => $value)
|
||||
// $company_settings->{$key} = $value;
|
||||
|
||||
//Iterate and set NEW settings
|
||||
foreach($settings as $key => $value)
|
||||
$company_settings->{$key} = $value;
|
||||
foreach($settings as $key => $value) {
|
||||
|
||||
if(is_null($settings->{$key}))
|
||||
$company_settings->{$key} = '';
|
||||
else
|
||||
$company_settings->{$key} = $value;
|
||||
}
|
||||
|
||||
$entity->settings = $company_settings;
|
||||
$entity->save();
|
||||
|
@ -761,8 +761,10 @@ class CreateUsersTable extends Migration
|
||||
});
|
||||
|
||||
Schema::create('paymentables', function ($table) { //allows multiple invoices to one payment
|
||||
// $table->increments('id');
|
||||
$table->unsignedInteger('payment_id');
|
||||
$table->unsignedInteger('paymentable_id');
|
||||
$table->decimal('amount', 16, 4)->default(0);
|
||||
$table->string('paymentable_type');
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user