mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Invoice Cancellation (#3605)
* Ensure release build sets correct file and directory permissions * Invoice Cancellation * Invoice Cancellations
This commit is contained in:
parent
bfc434071f
commit
213df6451f
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@ -30,7 +30,9 @@ jobs:
|
|||||||
php artisan optimize
|
php artisan optimize
|
||||||
php artisan storage:link
|
php artisan storage:link
|
||||||
sudo php artisan cache:clear
|
sudo php artisan cache:clear
|
||||||
|
sudo find ./ -type f -exec chmod 644 {} \;
|
||||||
|
sudo find ./ -type d -exec chmod 755 {} \;
|
||||||
|
|
||||||
- name: Prepare JS/CSS assets
|
- name: Prepare JS/CSS assets
|
||||||
run: |
|
run: |
|
||||||
npm i
|
npm i
|
||||||
|
@ -677,6 +677,11 @@ class InvoiceController extends BaseController
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'cancel':
|
case 'cancel':
|
||||||
|
$invoice = $invoice->service()->handleCancellation()->save();
|
||||||
|
|
||||||
|
if(!$bulk){
|
||||||
|
$this->itemResponse($invoice);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 'reverse':
|
case 'reverse':
|
||||||
$invoice = $invoice->service()->handleReversal()->save();
|
$invoice = $invoice->service()->handleReversal()->save();
|
||||||
|
59
app/Services/Invoice/HandleCancellation.php
Normal file
59
app/Services/Invoice/HandleCancellation.php
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://opensource.org/licenses/AAL
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Services\Invoice;
|
||||||
|
|
||||||
|
use App\Events\Payment\PaymentWasCreated;
|
||||||
|
use App\Factory\CreditFactory;
|
||||||
|
use App\Factory\InvoiceItemFactory;
|
||||||
|
use App\Factory\PaymentFactory;
|
||||||
|
use App\Helpers\Invoice\InvoiceSum;
|
||||||
|
use App\Models\Client;
|
||||||
|
use App\Models\Invoice;
|
||||||
|
use App\Models\Payment;
|
||||||
|
use App\Models\Paymentable;
|
||||||
|
use App\Services\AbstractService;
|
||||||
|
use App\Services\Client\ClientService;
|
||||||
|
use App\Services\Payment\PaymentService;
|
||||||
|
use App\Utils\Traits\GeneratesCounter;
|
||||||
|
|
||||||
|
class HandleCancellation extends AbstractService
|
||||||
|
{
|
||||||
|
use GeneratesCounter;
|
||||||
|
|
||||||
|
private $invoice;
|
||||||
|
|
||||||
|
public function __construct(Invoice $invoice)
|
||||||
|
{
|
||||||
|
$this->invoice = $invoice;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function run()
|
||||||
|
{
|
||||||
|
/* Check again!! */
|
||||||
|
if(!$this->invoice->invoiceCancellable($this->invoice))
|
||||||
|
return $this->invoice;
|
||||||
|
|
||||||
|
$adjustment = $this->invoice->balance*-1;
|
||||||
|
//set invoice balance to 0
|
||||||
|
$this->invoice->ledger()->updateInvoiceBalance($adjustment, "Invoice cancellation");
|
||||||
|
|
||||||
|
$this->invoice->balance = 0;
|
||||||
|
$this->invoice = $this->invoice->service()->setStatus(Invoice::STATUS_CANCELLED)->save();
|
||||||
|
|
||||||
|
//adjust client balance
|
||||||
|
$this->invoice->client->service()->updateBalance($adjustment)->save();
|
||||||
|
|
||||||
|
return $this->invoice;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -90,7 +90,7 @@ class HandleReversal extends AbstractService
|
|||||||
$credit->service()->markSent()->save();
|
$credit->service()->markSent()->save();
|
||||||
}
|
}
|
||||||
/* Set invoice balance to 0 */
|
/* Set invoice balance to 0 */
|
||||||
$this->invoice->ledger()->updateInvoiceBalance($balance_remaining, $notes)->save();
|
$this->invoice->ledger()->updateInvoiceBalance($balance_remaining*-1, $notes)->save();
|
||||||
|
|
||||||
$this->invoice->balance= 0;
|
$this->invoice->balance= 0;
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ use App\Services\Invoice\ApplyNumber;
|
|||||||
use App\Services\Invoice\ApplyPayment;
|
use App\Services\Invoice\ApplyPayment;
|
||||||
use App\Services\Invoice\CreateInvitations;
|
use App\Services\Invoice\CreateInvitations;
|
||||||
use App\Services\Invoice\GetInvoicePdf;
|
use App\Services\Invoice\GetInvoicePdf;
|
||||||
|
use App\Services\Invoice\HandleCancellation;
|
||||||
use App\Services\Invoice\HandleReversal;
|
use App\Services\Invoice\HandleReversal;
|
||||||
use App\Services\Invoice\MarkInvoicePaid;
|
use App\Services\Invoice\MarkInvoicePaid;
|
||||||
use App\Services\Invoice\MarkSent;
|
use App\Services\Invoice\MarkSent;
|
||||||
@ -123,6 +124,14 @@ class InvoiceService
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function handleCancellation()
|
||||||
|
{
|
||||||
|
$this->invoice = (new HandleCancellation($this->invoice))->run();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public function markViewed()
|
public function markViewed()
|
||||||
{
|
{
|
||||||
$this->invoice->last_viewed = Carbon::now()->format('Y-m-d H:i');
|
$this->invoice->last_viewed = Carbon::now()->format('Y-m-d H:i');
|
||||||
|
@ -28,7 +28,7 @@ trait ActionsInvoice
|
|||||||
public function invoiceCancellable($invoice) :bool
|
public function invoiceCancellable($invoice) :bool
|
||||||
{
|
{
|
||||||
|
|
||||||
if($invoice->status_id == Invoice::STATUS_PARTIAL && $invoice->is_deleted == false && $invoice->deleted_at == NULL)
|
if(($invoice->status_id == Invoice::STATUS_SENT || $invoice->status_id == Invoice::STATUS_PARTIAL) && $invoice->is_deleted == false && $invoice->deleted_at == NULL)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
72
tests/Feature/CancelInvoiceTest.php
Normal file
72
tests/Feature/CancelInvoiceTest.php
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Feature;
|
||||||
|
|
||||||
|
use App\Factory\CreditFactory;
|
||||||
|
use App\Factory\InvoiceItemFactory;
|
||||||
|
use App\Helpers\Invoice\InvoiceSum;
|
||||||
|
use App\Listeners\Credit\CreateCreditInvitation;
|
||||||
|
use App\Models\Client;
|
||||||
|
use App\Models\Credit;
|
||||||
|
use App\Models\Invoice;
|
||||||
|
use App\Models\Payment;
|
||||||
|
use App\Models\Paymentable;
|
||||||
|
use App\Utils\Traits\MakesHash;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Illuminate\Foundation\Testing\WithFaker;
|
||||||
|
use Illuminate\Routing\Middleware\ThrottleRequests;
|
||||||
|
use Illuminate\Support\Carbon;
|
||||||
|
use Illuminate\Validation\ValidationException;
|
||||||
|
use Tests\MockAccountData;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @covers App\Services\Invoice\HandleCancellation
|
||||||
|
*/
|
||||||
|
|
||||||
|
class CancelInvoiceTest extends TestCase
|
||||||
|
{
|
||||||
|
use MakesHash;
|
||||||
|
use DatabaseTransactions;
|
||||||
|
use MockAccountData;
|
||||||
|
|
||||||
|
public function setUp() :void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->withoutMiddleware(
|
||||||
|
ThrottleRequests::class
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->faker = \Faker\Factory::create();
|
||||||
|
|
||||||
|
Model::reguard();
|
||||||
|
|
||||||
|
$this->makeTestData();
|
||||||
|
|
||||||
|
$this->withoutExceptionHandling();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCancelInvoice()
|
||||||
|
{
|
||||||
|
$this->assertTrue($this->invoice->invoiceCancellable($this->invoice));
|
||||||
|
|
||||||
|
$client_balance = $this->client->balance;
|
||||||
|
$invoice_balance = $this->invoice->balance;
|
||||||
|
|
||||||
|
$this->assertEquals(Invoice::STATUS_SENT, $this->invoice->status_id);
|
||||||
|
|
||||||
|
$this->invoice->service()->handleCancellation()->save();
|
||||||
|
|
||||||
|
$this->assertEquals(0, $this->invoice->balance);
|
||||||
|
$this->assertEquals($this->client->balance, ($client_balance - $invoice_balance));
|
||||||
|
$this->assertNotEquals($client_balance, $this->client->balance);
|
||||||
|
$this->assertEquals(Invoice::STATUS_CANCELLED, $this->invoice->status_id);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -29,7 +29,7 @@ class InvoiceActionsTest extends TestCase
|
|||||||
{
|
{
|
||||||
$this->assertTrue($this->invoiceDeletable($this->invoice));
|
$this->assertTrue($this->invoiceDeletable($this->invoice));
|
||||||
$this->assertTrue($this->invoiceReversable($this->invoice));
|
$this->assertTrue($this->invoiceReversable($this->invoice));
|
||||||
$this->assertFalse($this->invoiceCancellable($this->invoice));
|
$this->assertTrue($this->invoiceCancellable($this->invoice));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testInvoiceIsReversable()
|
public function testInvoiceIsReversable()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user