Fixes for refunds with credits present

This commit is contained in:
David Bomba 2023-09-27 14:12:03 +10:00
parent a80f5c665d
commit 7973d7c882
8 changed files with 338 additions and 150 deletions

View File

@ -73,7 +73,10 @@ class TaskStatusController extends BaseController
*/ */
public function create(CreateTaskStatusRequest $request) public function create(CreateTaskStatusRequest $request)
{ {
$task_status = TaskStatusFactory::create(auth()->user()->company()->id, auth()->user()->id); /** @var \App\Models\User $user */
$user = auth()->user();
$task_status = TaskStatusFactory::create($user->company()->id, auth()->user()->id);
return $this->itemResponse($task_status); return $this->itemResponse($task_status);
} }
@ -87,8 +90,10 @@ class TaskStatusController extends BaseController
*/ */
public function store(StoreTaskStatusRequest $request) public function store(StoreTaskStatusRequest $request)
{ {
/** @var \App\Models\User $user */
$user = auth()->user();
$task_status = TaskStatusFactory::create(auth()->user()->company()->id, auth()->user()->id); $task_status = TaskStatusFactory::create($user->company()->id, auth()->user()->id);
$task_status->fill($request->all()); $task_status->fill($request->all());
$task_status->save(); $task_status->save();

View File

@ -29,7 +29,9 @@ class RefundPayment
private $credit_note; private $credit_note;
private $total_refund; private float $total_refund = 0;
private float $credits_used = 0;
private $gateway_refund_status; private $gateway_refund_status;
@ -45,8 +47,6 @@ class RefundPayment
$this->refund_data = $refund_data; $this->refund_data = $refund_data;
$this->total_refund = 0;
$this->gateway_refund_status = false; $this->gateway_refund_status = false;
$this->activity_repository = new ActivityRepository(); $this->activity_repository = new ActivityRepository();
@ -56,9 +56,9 @@ class RefundPayment
{ {
$this->payment = $this $this->payment = $this
->calculateTotalRefund() //sets amount for the refund (needed if we are refunding multiple invoices in one payment) ->calculateTotalRefund() //sets amount for the refund (needed if we are refunding multiple invoices in one payment)
->updateCreditables() //return the credits first
->processGatewayRefund() //process the gateway refund if needed ->processGatewayRefund() //process the gateway refund if needed
->setStatus() //sets status of payment ->setStatus() //sets status of payment
->updateCreditables() //return the credits first
->updatePaymentables() //update the paymentable items ->updatePaymentables() //update the paymentable items
->adjustInvoices() ->adjustInvoices()
->finalize() ->finalize()
@ -104,12 +104,14 @@ class RefundPayment
*/ */
private function processGatewayRefund() private function processGatewayRefund()
{ {
if ($this->refund_data['gateway_refund'] !== false && $this->total_refund > 0) { $net_refund = ($this->total_refund - $this->credits_used);
if ($this->refund_data['gateway_refund'] !== false && $net_refund > 0) {
if ($this->payment->company_gateway) { if ($this->payment->company_gateway) {
$response = $this->payment->company_gateway->driver($this->payment->client)->refund($this->payment, $this->total_refund); $response = $this->payment->company_gateway->driver($this->payment->client)->refund($this->payment, $net_refund);
if($response['amount'] ?? false) if($response['amount'] ?? false)
$this->total_refund = $response['amount']; $net_refund = $response['amount'];
if($response['voided'] ?? false) if($response['voided'] ?? false)
{ {
@ -123,7 +125,7 @@ class RefundPayment
})->toArray(); })->toArray();
} }
$this->payment->refunded += $this->total_refund; $this->payment->refunded += $net_refund;
if ($response['success'] == false) { if ($response['success'] == false) {
$this->payment->save(); $this->payment->save();
@ -132,7 +134,7 @@ class RefundPayment
} }
} }
} else { } else {
$this->payment->refunded += $this->total_refund; $this->payment->refunded += $net_refund;
} }
return $this; return $this;
@ -227,23 +229,29 @@ class RefundPayment
*/ */
private function updateCreditables() private function updateCreditables()
{ {
if ($this->payment->credits()->exists()) { if ($this->payment->credits()->exists()) {
$amount_to_refund = $this->total_refund;
//Adjust credits first!!! //Adjust credits first!!!
foreach ($this->payment->credits as $paymentable_credit) { foreach ($this->payment->credits as $paymentable_credit) {
$available_credit = $paymentable_credit->pivot->amount - $paymentable_credit->pivot->refunded; $available_credit = $paymentable_credit->pivot->amount - $paymentable_credit->pivot->refunded;
if ($available_credit > $this->total_refund) { if ($available_credit > $amount_to_refund) {
$paymentable_credit->pivot->refunded += $this->total_refund; $paymentable_credit->pivot->refunded += $amount_to_refund;
$paymentable_credit->pivot->save(); $paymentable_credit->pivot->save();
$paymentable_credit->service() $paymentable_credit->service()
->setStatus(Credit::STATUS_SENT) ->setStatus(Credit::STATUS_SENT)
->updateBalance($this->total_refund) ->adjustBalance($amount_to_refund)
->updatePaidToDate($this->total_refund * -1) ->updatePaidToDate($amount_to_refund * -1)
->save(); ->save();
$this->total_refund = 0;
$this->credits_used += $amount_to_refund;
$amount_to_refund = 0;
} else { } else {
$paymentable_credit->pivot->refunded += $available_credit; $paymentable_credit->pivot->refunded += $available_credit;
$paymentable_credit->pivot->save(); $paymentable_credit->pivot->save();
@ -254,10 +262,12 @@ class RefundPayment
->updatePaidToDate($available_credit * -1) ->updatePaidToDate($available_credit * -1)
->save(); ->save();
$this->total_refund -= $available_credit; $this->credits_used += $available_credit;
$amount_to_refund -= $available_credit;
} }
if ($this->total_refund == 0) { if ($amount_to_refund == 0) {
break; break;
} }
} }

73
composer.lock generated
View File

@ -485,16 +485,16 @@
}, },
{ {
"name": "aws/aws-sdk-php", "name": "aws/aws-sdk-php",
"version": "3.281.13", "version": "3.281.14",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/aws/aws-sdk-php.git", "url": "https://github.com/aws/aws-sdk-php.git",
"reference": "5547757d891495713aa7d5770bf04124d48a6ab5" "reference": "4b5b8aab08ef0add75f086bc03c7651799d187db"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/5547757d891495713aa7d5770bf04124d48a6ab5", "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/4b5b8aab08ef0add75f086bc03c7651799d187db",
"reference": "5547757d891495713aa7d5770bf04124d48a6ab5", "reference": "4b5b8aab08ef0add75f086bc03c7651799d187db",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -574,9 +574,9 @@
"support": { "support": {
"forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
"issues": "https://github.com/aws/aws-sdk-php/issues", "issues": "https://github.com/aws/aws-sdk-php/issues",
"source": "https://github.com/aws/aws-sdk-php/tree/3.281.13" "source": "https://github.com/aws/aws-sdk-php/tree/3.281.14"
}, },
"time": "2023-09-25T18:07:38+00:00" "time": "2023-09-26T18:15:48+00:00"
}, },
{ {
"name": "bacon/bacon-qr-code", "name": "bacon/bacon-qr-code",
@ -1353,16 +1353,16 @@
}, },
{ {
"name": "doctrine/dbal", "name": "doctrine/dbal",
"version": "3.6.7", "version": "3.7.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/doctrine/dbal.git", "url": "https://github.com/doctrine/dbal.git",
"reference": "8e0e268052b4a8974cb00215bb2892787021614f" "reference": "00d03067f07482f025d41ab55e4ba0db5eca2cdf"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/doctrine/dbal/zipball/8e0e268052b4a8974cb00215bb2892787021614f", "url": "https://api.github.com/repos/doctrine/dbal/zipball/00d03067f07482f025d41ab55e4ba0db5eca2cdf",
"reference": "8e0e268052b4a8974cb00215bb2892787021614f", "reference": "00d03067f07482f025d41ab55e4ba0db5eca2cdf",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1378,9 +1378,9 @@
"doctrine/coding-standard": "12.0.0", "doctrine/coding-standard": "12.0.0",
"fig/log-test": "^1", "fig/log-test": "^1",
"jetbrains/phpstorm-stubs": "2023.1", "jetbrains/phpstorm-stubs": "2023.1",
"phpstan/phpstan": "1.10.34", "phpstan/phpstan": "1.10.35",
"phpstan/phpstan-strict-rules": "^1.5", "phpstan/phpstan-strict-rules": "^1.5",
"phpunit/phpunit": "9.6.12", "phpunit/phpunit": "9.6.13",
"psalm/plugin-phpunit": "0.18.4", "psalm/plugin-phpunit": "0.18.4",
"slevomat/coding-standard": "8.13.1", "slevomat/coding-standard": "8.13.1",
"squizlabs/php_codesniffer": "3.7.2", "squizlabs/php_codesniffer": "3.7.2",
@ -1446,7 +1446,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/doctrine/dbal/issues", "issues": "https://github.com/doctrine/dbal/issues",
"source": "https://github.com/doctrine/dbal/tree/3.6.7" "source": "https://github.com/doctrine/dbal/tree/3.7.0"
}, },
"funding": [ "funding": [
{ {
@ -1462,7 +1462,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2023-09-19T20:15:41+00:00" "time": "2023-09-26T20:56:55+00:00"
}, },
{ {
"name": "doctrine/deprecations", "name": "doctrine/deprecations",
@ -4287,16 +4287,16 @@
}, },
{ {
"name": "laravel/framework", "name": "laravel/framework",
"version": "v10.24.0", "version": "v10.25.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/laravel/framework.git", "url": "https://github.com/laravel/framework.git",
"reference": "bcebd0a4c015d5c38aeec299d355a42451dd3726" "reference": "5132dafecbea5825af7cb4d093d4d7df57aecb61"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/bcebd0a4c015d5c38aeec299d355a42451dd3726", "url": "https://api.github.com/repos/laravel/framework/zipball/5132dafecbea5825af7cb4d093d4d7df57aecb61",
"reference": "bcebd0a4c015d5c38aeec299d355a42451dd3726", "reference": "5132dafecbea5825af7cb4d093d4d7df57aecb61",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -4314,7 +4314,7 @@
"ext-tokenizer": "*", "ext-tokenizer": "*",
"fruitcake/php-cors": "^1.2", "fruitcake/php-cors": "^1.2",
"guzzlehttp/uri-template": "^1.0", "guzzlehttp/uri-template": "^1.0",
"laravel/prompts": "^0.1", "laravel/prompts": "^0.1.9",
"laravel/serializable-closure": "^1.3", "laravel/serializable-closure": "^1.3",
"league/commonmark": "^2.2.1", "league/commonmark": "^2.2.1",
"league/flysystem": "^3.8.0", "league/flysystem": "^3.8.0",
@ -4483,20 +4483,20 @@
"issues": "https://github.com/laravel/framework/issues", "issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework" "source": "https://github.com/laravel/framework"
}, },
"time": "2023-09-19T15:25:04+00:00" "time": "2023-09-26T15:22:05+00:00"
}, },
{ {
"name": "laravel/prompts", "name": "laravel/prompts",
"version": "v0.1.8", "version": "v0.1.9",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/laravel/prompts.git", "url": "https://github.com/laravel/prompts.git",
"reference": "68dcc65babf92e1fb43cba0b3f78fc3d8002709c" "reference": "b603410e7af1040aa2d29e0a2cdca570bb63e827"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/laravel/prompts/zipball/68dcc65babf92e1fb43cba0b3f78fc3d8002709c", "url": "https://api.github.com/repos/laravel/prompts/zipball/b603410e7af1040aa2d29e0a2cdca570bb63e827",
"reference": "68dcc65babf92e1fb43cba0b3f78fc3d8002709c", "reference": "b603410e7af1040aa2d29e0a2cdca570bb63e827",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -4505,6 +4505,10 @@
"php": "^8.1", "php": "^8.1",
"symfony/console": "^6.2" "symfony/console": "^6.2"
}, },
"conflict": {
"illuminate/console": ">=10.17.0 <10.25.0",
"laravel/framework": ">=10.17.0 <10.25.0"
},
"require-dev": { "require-dev": {
"mockery/mockery": "^1.5", "mockery/mockery": "^1.5",
"pestphp/pest": "^2.3", "pestphp/pest": "^2.3",
@ -4515,6 +4519,11 @@
"ext-pcntl": "Required for the spinner to be animated." "ext-pcntl": "Required for the spinner to be animated."
}, },
"type": "library", "type": "library",
"extra": {
"branch-alias": {
"dev-main": "0.1.x-dev"
}
},
"autoload": { "autoload": {
"files": [ "files": [
"src/helpers.php" "src/helpers.php"
@ -4529,9 +4538,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/laravel/prompts/issues", "issues": "https://github.com/laravel/prompts/issues",
"source": "https://github.com/laravel/prompts/tree/v0.1.8" "source": "https://github.com/laravel/prompts/tree/v0.1.9"
}, },
"time": "2023-09-19T15:33:56+00:00" "time": "2023-09-26T13:14:20+00:00"
}, },
{ {
"name": "laravel/serializable-closure", "name": "laravel/serializable-closure",
@ -15037,16 +15046,16 @@
}, },
{ {
"name": "friendsofphp/php-cs-fixer", "name": "friendsofphp/php-cs-fixer",
"version": "v3.28.0", "version": "v3.30.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
"reference": "113e09fea3d2306319ffaa2423fe3de768b28cff" "reference": "95c64693b2f149966a2bc05a7a4981b0343ea52f"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/113e09fea3d2306319ffaa2423fe3de768b28cff", "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/95c64693b2f149966a2bc05a7a4981b0343ea52f",
"reference": "113e09fea3d2306319ffaa2423fe3de768b28cff", "reference": "95c64693b2f149966a2bc05a7a4981b0343ea52f",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -15120,7 +15129,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.28.0" "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.30.0"
}, },
"funding": [ "funding": [
{ {
@ -15128,7 +15137,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2023-09-22T20:43:40+00:00" "time": "2023-09-26T22:10:43+00:00"
}, },
{ {
"name": "hamcrest/hamcrest-php", "name": "hamcrest/hamcrest-php",

View File

@ -16,6 +16,8 @@ trait CreatesApplication
{ {
$app = require __DIR__.'/../bootstrap/app.php'; $app = require __DIR__.'/../bootstrap/app.php';
define('STDIN', fopen("php://stdin", "r"));
$app->make(Kernel::class)->bootstrap(); $app->make(Kernel::class)->bootstrap();
Hash::setRounds(4); Hash::setRounds(4);

View File

@ -1310,15 +1310,14 @@ class PaymentTest extends TestCase
]; ];
try {
$response = $this->withHeaders([ $response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'), 'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token, 'X-API-TOKEN' => $this->token,
])->postJson('/api/v1/payments?include=invoices', $data); ])->postJson('/api/v1/payments?include=invoices', $data);
} catch (ValidationException $e) {
$message = json_decode($e->validator->getMessageBag(), 1); $response->assertStatus(422);
$this->assertNotNull($message);
}
} }
public function testPaymentWithSameInvoiceMultipleTimes() public function testPaymentWithSameInvoiceMultipleTimes()

View File

@ -11,21 +11,22 @@
namespace Tests\Feature; namespace Tests\Feature;
use Tests\TestCase;
use App\Models\Credit;
use App\Models\Invoice;
use App\Models\Payment;
use Tests\MockAccountData;
use App\Models\ClientContact;
use App\Factory\ClientFactory; use App\Factory\ClientFactory;
use App\Factory\CreditFactory; use App\Factory\CreditFactory;
use App\Factory\InvoiceFactory; use App\Factory\InvoiceFactory;
use App\Helpers\Invoice\InvoiceSum;
use App\Models\ClientContact;
use App\Models\Invoice;
use App\Models\Payment;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use App\Helpers\Invoice\InvoiceSum;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Routing\Middleware\ThrottleRequests;
use Illuminate\Support\Facades\Session; use Illuminate\Support\Facades\Session;
use Illuminate\Validation\ValidationException; use Illuminate\Validation\ValidationException;
use Tests\MockAccountData; use Illuminate\Routing\Middleware\ThrottleRequests;
use Tests\TestCase; use Illuminate\Foundation\Testing\DatabaseTransactions;
/** /**
* @test * @test
@ -37,6 +38,8 @@ class RefundTest extends TestCase
use DatabaseTransactions; use DatabaseTransactions;
use MockAccountData; use MockAccountData;
public $faker;
protected function setUp() :void protected function setUp() :void
{ {
parent::setUp(); parent::setUp();
@ -53,7 +56,7 @@ class RefundTest extends TestCase
$this->makeTestData(); $this->makeTestData();
$this->withoutExceptionHandling(); // $this->withoutExceptionHandling();
} }
/** /**
@ -82,10 +85,10 @@ class RefundTest extends TestCase
$this->invoice->save(); $this->invoice->save();
$this->invoice_calc = new InvoiceSum($this->invoice); $invoice_calc = new InvoiceSum($this->invoice);
$this->invoice_calc->build(); $invoice_calc->build();
$this->invoice = $this->invoice_calc->getInvoice(); $this->invoice = $invoice_calc->getInvoice();
$this->invoice->save(); $this->invoice->save();
$data = [ $data = [
@ -119,14 +122,12 @@ class RefundTest extends TestCase
$response = false; $response = false;
try {
$response = $this->withHeaders([ $response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'), 'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token, 'X-API-TOKEN' => $this->token,
])->post('/api/v1/payments/refund', $data); ])->postJson('/api/v1/payments/refund', $data);
} catch (ValidationException $e) {
$message = json_decode($e->validator->getMessageBag(), 1);
}
$arr = $response->json(); $arr = $response->json();
@ -165,10 +166,10 @@ class RefundTest extends TestCase
$this->invoice->save(); $this->invoice->save();
$this->invoice_calc = new InvoiceSum($this->invoice); $invoice_calc = new InvoiceSum($this->invoice);
$this->invoice_calc->build(); $invoice_calc->build();
$this->invoice = $this->invoice_calc->getInvoice(); $this->invoice = $invoice_calc->getInvoice();
$this->invoice->save(); $this->invoice->save();
$this->invoice->setRelation('client', $this->client); $this->invoice->setRelation('client', $this->client);
@ -217,23 +218,12 @@ class RefundTest extends TestCase
'date' => '2020/12/12', 'date' => '2020/12/12',
]; ];
$response = false;
try {
$response = $this->withHeaders([ $response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'), 'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token, 'X-API-TOKEN' => $this->token,
])->post('/api/v1/payments/refund', $data); ])->postJson('/api/v1/payments/refund', $data);
} catch (ValidationException $e) { $response->assertStatus(422);
$message = json_decode($e->validator->getMessageBag(), 1);
$this->assertNotNull($message);
\Log::error($message);
}
if ($response) {
$response->assertStatus(302);
}
} }
/** /**
@ -262,10 +252,10 @@ class RefundTest extends TestCase
$this->invoice->save(); $this->invoice->save();
$this->invoice_calc = new InvoiceSum($this->invoice); $invoice_calc = new InvoiceSum($this->invoice);
$this->invoice_calc->build(); $invoice_calc->build();
$this->invoice = $this->invoice_calc->getInvoice(); $this->invoice = $invoice_calc->getInvoice();
$this->invoice->save(); $this->invoice->save();
$data = [ $data = [
@ -346,10 +336,10 @@ class RefundTest extends TestCase
$this->invoice->save(); $this->invoice->save();
$this->invoice_calc = new InvoiceSum($this->invoice); $invoice_calc = new InvoiceSum($this->invoice);
$this->invoice_calc->build(); $invoice_calc->build();
$this->invoice = $this->invoice_calc->getInvoice(); $this->invoice = $invoice_calc->getInvoice();
$this->invoice->save(); $this->invoice->save();
$data = [ $data = [
@ -439,10 +429,10 @@ class RefundTest extends TestCase
$this->invoice->save(); $this->invoice->save();
$this->invoice_calc = new InvoiceSum($this->invoice); $invoice_calc = new InvoiceSum($this->invoice);
$this->invoice_calc->build(); $invoice_calc->build();
$this->invoice = $this->invoice_calc->getInvoice(); $this->invoice = $invoice_calc->getInvoice();
$this->invoice->save(); $this->invoice->save();
$data = [ $data = [
@ -485,10 +475,10 @@ class RefundTest extends TestCase
$this->invoice->save(); $this->invoice->save();
$this->invoice_calc = new InvoiceSum($this->invoice); $invoice_calc = new InvoiceSum($this->invoice);
$this->invoice_calc->build(); $invoice_calc->build();
$this->invoice = $this->invoice_calc->getInvoice(); $this->invoice = $invoice_calc->getInvoice();
$this->invoice->save(); $this->invoice->save();
$data = [ $data = [
@ -553,13 +543,13 @@ class RefundTest extends TestCase
$this->invoice->line_items = $this->buildLineItems(); $this->invoice->line_items = $this->buildLineItems();
$this->invoice->uses_inclusive_taxes = false; $this->invoice->uses_inclusive_taxes = false;
$this->invoice_client_id = $client->id; $this->invoice->client_id = $client->id;
$this->invoice->save(); $this->invoice->save();
$this->invoice_calc = new InvoiceSum($this->invoice); $invoice_calc = new InvoiceSum($this->invoice);
$this->invoice_calc->build(); $invoice_calc->build();
$this->invoice = $this->invoice_calc->getInvoice(); $this->invoice = $invoice_calc->getInvoice();
$this->invoice->save(); $this->invoice->save();
$this->credit = CreditFactory::create($this->company->id, $this->user->id); $this->credit = CreditFactory::create($this->company->id, $this->user->id);
@ -650,4 +640,172 @@ class RefundTest extends TestCase
} }
/*Additional scenarios*/ /*Additional scenarios*/
public function testRefundsWhenCreditsArePresent()
{
$i = Invoice::factory()->create([
'company_id' => $this->company->id,
'user_id' => $this->user->id,
'client_id' => $this->client->id,
'status_id' => Invoice::STATUS_SENT,
'amount' => 1000,
'balance' => 1000,
]);
$c = Credit::factory()->create([
'company_id' => $this->company->id,
'user_id' => $this->user->id,
'client_id' => $this->client->id,
'status_id' => Invoice::STATUS_SENT,
'amount' => 100,
'balance' => 100,
]);
$data = [
'client_id' => $this->client->hashed_id,
'invoices' => [
[
'invoice_id' => $i->hashed_id,
'amount' => 1000,
],
],
'credits' => [
[
'credit_id' => $c->hashed_id,
'amount' => 100,
],
],
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->postJson('/api/v1/payments', $data);
$arr = $response->json();
$response->assertStatus(200);
$this->assertEquals(0, $c->fresh()->balance);
$this->assertEquals(0, $i->fresh()->balance);
$payment_id = $arr['data']['id'];
$refund = [
'id' => $payment_id,
'client_id' => $this->client->hashed_id,
'amount' => 10,
'date' => now()->format('Y-m-d'),
'invoices' => [
[
'invoice_id' => $i->hashed_id,
'amount' => 10,
],
]
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->postJson('/api/v1/payments/refund', $refund);
$response->assertStatus(200);
$arr = $response->json();
$this->assertEquals(0, $arr['data']['refunded']);
$this->assertEquals(10, $c->fresh()->balance);
$this->assertEquals(10, $i->fresh()->balance);
}
public function testRefundsWithSplitCreditAndPaymentRefund()
{
$i = Invoice::factory()->create([
'company_id' => $this->company->id,
'user_id' => $this->user->id,
'client_id' => $this->client->id,
'status_id' => Invoice::STATUS_SENT,
'amount' => 1000,
'balance' => 1000,
]);
$c = Credit::factory()->create([
'company_id' => $this->company->id,
'user_id' => $this->user->id,
'client_id' => $this->client->id,
'status_id' => Invoice::STATUS_SENT,
'amount' => 100,
'balance' => 100,
]);
$data = [
'client_id' => $this->client->hashed_id,
'invoices' => [
[
'invoice_id' => $i->hashed_id,
'amount' => 1000,
],
],
'credits' => [
[
'credit_id' => $c->hashed_id,
'amount' => 100,
],
],
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->postJson('/api/v1/payments', $data);
$arr = $response->json();
$response->assertStatus(200);
$this->assertEquals(0, $c->fresh()->balance);
$this->assertEquals(0, $i->fresh()->balance);
$payment_id = $arr['data']['id'];
$payment = Payment::find($this->decodePrimaryKey($payment_id));
$this->assertEquals(900, $payment->amount);
$this->assertEquals(900, $payment->applied);
$this->assertEquals(0, $payment->refunded);
$refund = [
'id' => $payment_id,
'client_id' => $this->client->hashed_id,
'amount' => 200,
'date' => now()->format('Y-m-d'),
'invoices' => [
[
'invoice_id' => $i->hashed_id,
'amount' => 200,
],
]
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->postJson('/api/v1/payments/refund', $refund);
$response->assertStatus(200);
$arr = $response->json();
$this->assertEquals(100, $arr['data']['refunded']);
$this->assertEquals(100, $c->fresh()->balance);
$this->assertEquals(200, $i->fresh()->balance);
$this->assertEquals(900, $payment->fresh()->amount);
$this->assertEquals(900, $payment->fresh()->applied);
$this->assertEquals(100, $payment->fresh()->refunded);
}
} }

View File

@ -43,6 +43,8 @@ class CompanyLedgerTest extends TestCase
public $account; public $account;
public $faker;
protected function setUp() :void protected function setUp() :void
{ {
parent::setUp(); parent::setUp();

View File

@ -11,57 +11,58 @@
namespace Tests; namespace Tests;
use App\DataMapper\ClientRegistrationFields; use App\Models\Task;
use App\DataMapper\ClientSettings; use App\Models\User;
use App\DataMapper\CompanySettings; use App\Models\Quote;
use App\Factory\CompanyUserFactory;
use App\Factory\CreditFactory;
use App\Factory\InvoiceFactory;
use App\Factory\InvoiceInvitationFactory;
use App\Factory\InvoiceItemFactory;
use App\Factory\InvoiceToRecurringInvoiceFactory;
use App\Factory\PurchaseOrderFactory;
use App\Helpers\Invoice\InvoiceSum;
use App\Jobs\Company\CreateCompanyTaskStatuses;
use App\Models\Account;
use App\Models\BankIntegration;
use App\Models\BankTransaction;
use App\Models\BankTransactionRule;
use App\Models\Client; use App\Models\Client;
use App\Models\ClientContact;
use App\Models\Company;
use App\Models\CompanyGateway;
use App\Models\CompanyToken;
use App\Models\Credit; use App\Models\Credit;
use App\Models\CreditInvitation; use App\Models\Vendor;
use App\Models\Account;
use App\Models\Company;
use App\Models\Expense; use App\Models\Expense;
use App\Models\ExpenseCategory;
use App\Models\GroupSetting;
use App\Models\InvoiceInvitation;
use App\Models\Payment; use App\Models\Payment;
use App\Models\Product; use App\Models\Product;
use App\Models\Project; use App\Models\Project;
use App\Models\PurchaseOrderInvitation; use App\Models\TaxRate;
use App\Models\Quote; use App\Models\Scheduler;
use App\Models\TaskStatus;
use App\Utils\TruthSource;
use App\Models\CompanyToken;
use App\Models\GroupSetting;
use App\Models\ClientContact;
use App\Models\VendorContact;
use App\Factory\CreditFactory;
use App\Models\CompanyGateway;
use App\Models\RecurringQuote;
use Illuminate\Support\Carbon;
use App\Factory\InvoiceFactory;
use App\Models\BankIntegration;
use App\Models\BankTransaction;
use App\Models\ExpenseCategory;
use App\Models\QuoteInvitation; use App\Models\QuoteInvitation;
use App\Utils\Traits\MakesHash;
use App\Models\CreditInvitation;
use App\Models\RecurringExpense; use App\Models\RecurringExpense;
use App\Models\RecurringInvoice; use App\Models\RecurringInvoice;
use App\Models\RecurringQuote; use App\Models\InvoiceInvitation;
use App\Models\Scheduler; use App\DataMapper\ClientSettings;
use App\Models\Task; use App\DataMapper\CompanySettings;
use App\Models\TaskStatus; use App\Factory\CompanyUserFactory;
use App\Models\TaxRate; use App\Factory\InvoiceItemFactory;
use App\Models\User; use App\Helpers\Invoice\InvoiceSum;
use App\Models\Vendor; use App\Models\BankTransactionRule;
use App\Models\VendorContact;
use App\Utils\Traits\GeneratesCounter;
use App\Utils\Traits\MakesHash;
use App\Utils\TruthSource;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Hash;
use App\Factory\PurchaseOrderFactory;
use Illuminate\Support\Facades\Cache;
use App\Utils\Traits\GeneratesCounter;
use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Schema;
use App\Models\PurchaseOrderInvitation;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use App\Factory\InvoiceInvitationFactory;
use App\DataMapper\ClientRegistrationFields;
use App\Jobs\Company\CreateCompanyTaskStatuses;
use App\Factory\InvoiceToRecurringInvoiceFactory;
/** /**
* Class MockAccountData. * Class MockAccountData.
@ -200,7 +201,9 @@ trait MockAccountData
/* Warm up the cache !*/ /* Warm up the cache !*/
$cached_tables = config('ninja.cached_tables'); $cached_tables = config('ninja.cached_tables');
$this->artisan('db:seed --force'); Artisan::call('db:seed', [
'--force' => true
]);
foreach ($cached_tables as $name => $class) { foreach ($cached_tables as $name => $class) {
// check that the table exists in case the migration is pending // check that the table exists in case the migration is pending