1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-10 13:12:50 +01:00

Merge pull request #6593 from turbo124/v5-develop

Add withTrashed() when searching for MultiDB users
This commit is contained in:
David Bomba 2021-09-07 16:01:06 +10:00 committed by GitHub
commit c9ea2ceaf3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 490 additions and 69 deletions

View File

@ -67,18 +67,6 @@ class InvoiceFilters extends QueryFilters
return $this->builder;
}
public function client_id(string $client_id = '') :Builder
{
if (strlen($client_id) == 0) {
return $this->builder;
}
$this->builder->where('client_id', $this->decodePrimaryKey($client_id));
return $this->builder;
}
public function number(string $number) :Builder
{
return $this->builder->where('number', $number);

View File

@ -12,6 +12,7 @@
namespace App\Filters;
//use Illuminate\Database\Query\Builder;
use App\Utils\Traits\MakesHash;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
@ -20,6 +21,8 @@ use Illuminate\Http\Request;
*/
abstract class QueryFilters
{
use MakesHash;
/**
* active status.
*/
@ -177,6 +180,18 @@ abstract class QueryFilters
}
public function client_id(string $client_id = '') :Builder
{
if (strlen($client_id) == 0) {
return $this->builder;
}
$this->builder->where('client_id', $this->decodePrimaryKey($client_id));
return $this->builder;
}
public function filter_deleted_clients($value)
{

View File

@ -34,11 +34,6 @@ class SystemLogFilters extends QueryFilters
return $this->builder->where('event_id', $event_id);
}
public function client_id(int $client_id) :Builder
{
return $this->builder->where('client_id', $client_id);
}
/**
* Filter based on search text.
*

View File

@ -30,7 +30,7 @@ class InvoiceSum
public $invoice_item;
public $total_taxes;
public $total_taxes = 0;
private $total;

View File

@ -20,7 +20,9 @@ use App\Utils\Number;
use App\Utils\TempFile;
use App\Utils\Traits\MakesDates;
use App\Utils\Traits\MakesHash;
use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
use Illuminate\View\View;
use ZipStream\Option\Archive;
use ZipStream\ZipStream;
@ -86,6 +88,10 @@ class InvoiceController extends Controller
->with('message', ctrans('texts.no_action_provided'));
}
/**
* @param array $ids
* @return Factory|View|RedirectResponse
*/
private function makePayment(array $ids)
{
$invoices = Invoice::whereIn('id', $ids)
@ -119,8 +125,8 @@ class InvoiceController extends Controller
//format data
$invoices->map(function ($invoice) {
$invoice->service()->removeUnpaidGatewayFees()->save();
$invoice->balance = Number::formatValue($invoice->balance, $invoice->client->currency());
$invoice->partial = Number::formatValue($invoice->partial, $invoice->client->currency());
$invoice->balance = $invoice->balance > 0 ? Number::formatValue($invoice->balance, $invoice->client->currency()) : 0;
$invoice->partial = $invoice->partial > 0 ? Number::formatValue($invoice->partial, $invoice->client->currency()) : 0;
return $invoice;
});

View File

@ -60,6 +60,8 @@ class StoreUserRequest extends Request
//unique user rule - check company_user table for user_id / company_id / account_id if none exist we can add the user. ELSE return false
if(array_key_exists('email', $input))
$input['email'] = trim($input['email']);
if (isset($input['company_user'])) {
if (! isset($input['company_user']['is_admin'])) {

View File

@ -45,6 +45,8 @@ class UpdateUserRequest extends Request
{
$input = $this->all();
if(array_key_exists('email', $input))
$input['email'] = trim($input['email']);
$this->replace($input);
}

View File

@ -62,7 +62,7 @@ class BaseTransformer
public function getClient($client_name, $client_email) {
$clients = $this->maps['company']->clients;
$clients = $clients->where( 'name', $client_name );
$clients = $clients->where( 'id_number', $client_name );
if ( $clients->count() >= 1 ) {
return $clients->first()->id;

View File

@ -42,7 +42,7 @@ class ClientTransformer extends BaseTransformer {
'work_phone' => $this->getString( $data, 'Phone' ),
'private_notes' => $this->getString( $data, 'Notes' ),
'website' => $this->getString( $data, 'Website' ),
'id_number' => $this->getString( $data, 'Customer ID'),
'address1' => $this->getString( $data, 'Billing Address' ),
'address2' => $this->getString( $data, 'Billing Street2' ),
'city' => $this->getString( $data, 'Billing City' ),

View File

@ -38,7 +38,7 @@ class InvoiceTransformer extends BaseTransformer {
$transformed = [
'company_id' => $this->maps['company']->id,
'client_id' => $this->getClient( $this->getString( $invoice_data, 'Company Name' ), null ),
'client_id' => $this->getClient( $this->getString( $invoice_data, 'Customer ID' ), null ),
'number' => $this->getString( $invoice_data, 'Invoice Number' ),
'date' => isset( $invoice_data['Invoice Date'] ) ? date( 'Y-m-d', strtotime( $invoice_data['Invoice Date'] ) ) : null,
'due_date' => isset( $invoice_data['Due Date'] ) ? date( 'Y-m-d', strtotime( $invoice_data['Due Date'] ) ) : null,
@ -59,7 +59,7 @@ class InvoiceTransformer extends BaseTransformer {
'notes' => $this->getString( $record, 'Item Description' ),
'cost' => $this->getFloat( $record, 'Item Price' ),
'quantity' => $this->getFloat( $record, 'Quantity' ),
'discount' => $this->getFloat( $record, 'Discount Amount' ),
'discount' => $this->getString( $record, 'Discount Amount' ),
'is_amount_discount' => true,
];
}

View File

@ -56,8 +56,8 @@ class AutoBillCron
nlog($auto_bill_partial_invoices->count(). " partial invoices to auto bill");
$auto_bill_partial_invoices->cursor()->each(function ($invoice) use($db){
$this->runAutoBiller($invoice, $db);
$auto_bill_partial_invoices->cursor()->each(function ($invoice){
$this->runAutoBiller($invoice, false);
});
$auto_bill_invoices = Invoice::whereDate('due_date', '<=', now())
@ -69,8 +69,8 @@ class AutoBillCron
nlog($auto_bill_invoices->count(). " full invoices to auto bill");
$auto_bill_invoices->cursor()->each(function ($invoice) use($db){
$this->runAutoBiller($invoice, $db);
$auto_bill_invoices->cursor()->each(function ($invoice){
$this->runAutoBiller($invoice, false);
});
@ -115,8 +115,12 @@ class AutoBillCron
info("Firing autobill for {$invoice->company_id} - {$invoice->number}");
try{
MultiDB::setDB($db);
if($db)
MultiDB::setDB($db);
$invoice->service()->autoBill()->save();
}
catch(\Exception $e) {
nlog("Failed to capture payment for {$invoice->company_id} - {$invoice->number} ->" . $e->getMessage());

View File

@ -30,6 +30,7 @@ use App\Providers\MailServiceProvider;
use App\Utils\Ninja;
use App\Utils\Traits\MakesHash;
use Dacastro4\LaravelGmail\Facade\LaravelGmail;
use GuzzleHttp\Exception\ClientException;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
@ -118,10 +119,18 @@ class NinjaMailerJob implements ShouldQueue
nlog("error failed with {$e->getMessage()}");
if($this->nmo->entity)
$this->entityEmailFailed($e->getMessage());
$message = $e->getMessage();
if(Ninja::isHosted())
if($e instanceof ClientException) { //postmark specific failure
$response = $e->getResponse();
$message = $response->Message;
}
if($this->nmo->entity)
$this->entityEmailFailed($message);
if(Ninja::isHosted() && (!$e instanceof ClientException)) // Don't send postmark failures to Sentry
app('sentry')->captureException($e);
}
}
@ -241,6 +250,7 @@ class NinjaMailerJob implements ShouldQueue
private function logMailError($errors, $recipient_object)
{
SystemLogger::dispatch(
$errors,
SystemLog::CATEGORY_MAIL,
@ -249,19 +259,18 @@ class NinjaMailerJob implements ShouldQueue
$recipient_object,
$this->nmo->company
);
}
public function failed($exception = null)
{
nlog('mailer job failed');
nlog($exception->getMessage());
$job_failure = new EmailFailure($this->nmo->company->company_key);
$job_failure->string_metric5 = 'failed_email';
$job_failure->string_metric6 = substr($exception->getMessage(), 0, 150);
$job_failure->string_metric6 = substr($errors, 0, 150);
LightLogs::create($job_failure)
->batch();
}
public function failed($exception = null)
{
}
}

View File

@ -147,6 +147,8 @@ class SendRecurring implements ShouldQueue
}
//important catch all here - we should never leave contacts send_email to false incase they are permanently set to false in the future.
$this->recurring_invoice->client->contacts()->update(['send_email' => true]);
}

View File

@ -73,12 +73,12 @@ class MultiDB
public static function checkUserEmailExists($email) : bool
{
if (! config('ninja.db.multi_db_enabled'))
return User::where(['email' => $email])->exists(); // true >= 1 emails found / false -> == emails found
return User::where(['email' => $email])->withTrashed()->exists(); // true >= 1 emails found / false -> == emails found
$current_db = config('database.default');
foreach (self::$dbs as $db) {
if (User::on($db)->where(['email' => $email])->exists()) { // if user already exists, validation will fail
if (User::on($db)->where(['email' => $email])->withTrashed()->exists()) { // if user already exists, validation will fail
self::setDb($current_db);
return true;
}
@ -107,7 +107,7 @@ class MultiDB
$current_db = config('database.default');
foreach (self::$dbs as $db) {
if (User::on($db)->where(['email' => $email])->exists()) {
if (User::on($db)->where(['email' => $email])->withTrashed()->exists()) {
if (Company::on($db)->where(['company_key' => $company_key])->exists()) {
self::setDb($current_db);
return true;
@ -196,7 +196,7 @@ class MultiDB
//multi-db active
foreach (self::$dbs as $db) {
if (User::on($db)->where('email', $email)->exists()){
if (User::on($db)->where('email', $email)->withTrashed()->exists()){
self::setDb($db);
return true;
}

View File

@ -478,7 +478,7 @@ class BaseDriver extends AbstractPaymentDriver
$message,
SystemLog::CATEGORY_GATEWAY_RESPONSE,
SystemLog::EVENT_GATEWAY_FAILURE,
$this::SYSTEM_LOG_TYPE,
SystemLog::TYPE_PAYTRACE,
$this->client,
$this->client->company,
);

View File

@ -28,7 +28,8 @@ class BaseRepository
{
use MakesHash;
use SavesDocuments;
public $import_mode = false;
public $import_mode = false;
/**
* @param $entity

View File

@ -71,6 +71,7 @@ class ClientContactRepository extends BaseRepository
$client->company->client_contacts()->where('email', $update_contact->email)->update(['password' => $update_contact->password]);
}
$update_contact->email = trim($contact['email']);
$update_contact->save();
});

View File

@ -170,10 +170,10 @@ class PaymentRepository extends BaseRepository {
event( new PaymentWasCreated( $payment, $payment->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null) ) );
}
nlog("payment amount = {$payment->amount}");
nlog("payment applied = {$payment->applied}");
nlog("invoice totals = {$invoice_totals}");
nlog("credit totals = {$credit_totals}");
// nlog("payment amount = {$payment->amount}");
// nlog("payment applied = {$payment->applied}");
// nlog("invoice totals = {$invoice_totals}");
// nlog("credit totals = {$credit_totals}");
$payment->applied += ($invoice_totals - $credit_totals); //wont work because - check tests
// $payment->applied += $invoice_totals; //wont work because - check tests

View File

@ -84,25 +84,41 @@ class DeletePayment
nlog("net deletable amount - refunded = {$net_deletable}");
$paymentable_invoice->service()
->updateBalance($net_deletable)
->updatePaidToDate($net_deletable * -1)
->save();
if(!$paymentable_invoice->is_deleted)
{
$paymentable_invoice->service()
->updateBalance($net_deletable)
->updatePaidToDate($net_deletable * -1)
->save();
$paymentable_invoice->ledger()
->updateInvoiceBalance($net_deletable, "Adjusting invoice {$paymentable_invoice->number} due to deletion of Payment {$this->payment->number}")
->save();
$paymentable_invoice->ledger()
->updateInvoiceBalance($net_deletable, "Adjusting invoice {$paymentable_invoice->number} due to deletion of Payment {$this->payment->number}")
->save();
$paymentable_invoice->client
->service()
->updateBalance($net_deletable)
->updatePaidToDate($net_deletable * -1)
->save();
$paymentable_invoice->client
->service()
->updateBalance($net_deletable)
->updatePaidToDate($net_deletable * -1)
->save();
if ($paymentable_invoice->balance == $paymentable_invoice->amount) {
$paymentable_invoice->service()->setStatus(Invoice::STATUS_SENT)->save();
} else {
$paymentable_invoice->service()->setStatus(Invoice::STATUS_PARTIAL)->save();
if ($paymentable_invoice->balance == $paymentable_invoice->amount) {
$paymentable_invoice->service()->setStatus(Invoice::STATUS_SENT)->save();
} else {
$paymentable_invoice->service()->setStatus(Invoice::STATUS_PARTIAL)->save();
}
}
else {
//If the invoice is deleted we only update the meta data on the invoice
//and reduce the clients paid to date
$paymentable_invoice->service()
->updatePaidToDate($net_deletable * -1)
->save();
// $paymentable_invoice->client
// ->service()
// ->updatePaidToDate($net_deletable * -1)
// ->save();
}
});

View File

@ -271,6 +271,8 @@ trait MakesInvoiceValues
$data;
}
$locale_info = localeconv();
foreach ($items as $key => $item) {
if ($table_type == '$product' && $item->type_id != 1) {
if ($item->type_id != 4 && $item->type_id != 6 && $item->type_id != 5) {
@ -301,8 +303,10 @@ trait MakesInvoiceValues
$data[$key][$table_type . ".{$_table_type}3"] = $helpers->formatCustomFieldValue($this->client->company->custom_fields, "{$_table_type}3", $item->custom_value3, $this->client);
$data[$key][$table_type . ".{$_table_type}4"] = $helpers->formatCustomFieldValue($this->client->company->custom_fields, "{$_table_type}4", $item->custom_value4, $this->client);
$data[$key][$table_type.'.quantity'] = Number::formatValue($item->quantity, $this->client->currency());
//$data[$key][$table_type.'.quantity'] = Number::formatValue($item->quantity, $this->client->currency());
//change quantity from localized number, to decimal format with no trailing zeroes 06/09/21
$data[$key][$table_type.'.quantity'] = rtrim($item->quantity, $locale_info['decimal_point']);
$data[$key][$table_type.'.unit_cost'] = Number::formatMoney($item->cost, $this->client);
$data[$key][$table_type.'.cost'] = Number::formatMoney($item->cost, $this->client);
@ -483,7 +487,7 @@ trait MakesInvoiceValues
$output = (int)$raw - (int)$_value[1]; // 1 (:MONTH) - 4
}
if ($_operation == '/') {
if ($_operation == '/' && (int)$_value[1] != 0) {
$output = (int)$raw / (int)$_value[1]; // 1 (:MONTH) / 4
}

View File

@ -0,0 +1,376 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace Tests\Feature;
use App\Factory\CompanyTokenFactory;
use App\Factory\CompanyUserFactory;
use App\Factory\InvoiceFactory;
use App\Factory\InvoiceItemFactory;
use App\Models\Account;
use App\Models\Client;
use App\Models\ClientContact;
use App\Models\Company;
use App\Models\CompanyToken;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\User;
use App\Utils\Traits\MakesHash;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Validation\ValidationException;
use Tests\TestCase;
/**
* @test
*/
class MultiPaymentDeleteTest extends TestCase
{
use DatabaseTransactions, MakesHash;
private $faker;
public function setUp() :void
{
parent::setUp();
$this->faker = \Faker\Factory::create();
}
public function testComplexRefundDeleteScenario()
{
$account = Account::factory()->create();
$company = Company::factory()->create([
'account_id' => $account->id,
]);
$account->default_company_id = $company->id;
$account->save();
$user = User::factory()->create([
'account_id' => $account->id,
'confirmation_code' => '11',
'email' => $this->faker->unique()->safeEmail,
]);
$cu = CompanyUserFactory::create($user->id, $company->id, $account->id);
$cu->is_owner = true;
$cu->is_admin = true;
$cu->save();
$token = new CompanyToken;
$token->user_id = $user->id;
$token->company_id = $company->id;
$token->account_id = $account->id;
$token->name = 'test token';
$token->token = 'okeytokey';
$token->is_system = true;
$token->save();
$client = Client::factory()->create([
'user_id' => $user->id,
'company_id' => $company->id,
]);
ClientContact::factory()->create([
'user_id' => $user->id,
'client_id' => $client->id,
'company_id' => $company->id,
'is_primary' => 1,
]);
ClientContact::factory()->create([
'user_id' => $user->id,
'client_id' => $client->id,
'company_id' => $company->id,
]);
$invoice = Invoice::factory()->create([
'user_id' => $user->id,
'client_id' => $client->id,
'company_id' => $company->id,
'number' => (string)$this->faker->randomNumber(6),
]);
$invoice = InvoiceFactory::create($company->id,$user->id);
$invoice->client_id = $client->id;
$invoice->status_id = Invoice::STATUS_DRAFT;
$line_items = [];
$item = InvoiceItemFactory::create();
$item->quantity = 1;
$item->cost = 325;
$item->type_id = 1;
$line_items[] = $item;
$invoice->line_items = $line_items;
$invoice = $invoice->calc()->getInvoice();
$this->assertEquals(0, $client->balance);
$this->assertEquals(0, $client->paid_to_date);
$this->assertEquals(0, $invoice->balance);
//mark sent
$invoice = $invoice->service()->markSent()->save();
$invoice->fresh();
$invoice->client->fresh();
$this->assertEquals(325, $invoice->balance);
$this->assertEquals(0, $invoice->client->fresh()->paid_to_date);
$this->assertEquals(325, $invoice->client->balance);
//payment 163
//
$data = [
'amount' => 163.0,
'client_id' => $this->encodePrimaryKey($client->id),
'invoices' => [
[
'invoice_id' => $this->encodePrimaryKey($invoice->id),
'amount' => 163,
],
],
'date' => '2019/12/12',
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $token->token,
])->post('/api/v1/payments/', $data);
$arr = $response->json();
$payment_id = $arr['data']['id'];
$payment_1 = Payment::whereId($this->decodePrimaryKey($payment_id))->first();
//payment 162
$this->assertEquals(162, $invoice->fresh()->balance);
$this->assertEquals(162, $invoice->client->fresh()->balance);
$this->assertEquals(163, $invoice->client->fresh()->paid_to_date);
$data = [
'amount' => 162.0,
'client_id' => $this->encodePrimaryKey($client->id),
'invoices' => [
[
'invoice_id' => $this->encodePrimaryKey($invoice->id),
'amount' => 162,
],
],
'date' => '2019/12/12',
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $token->token,
])->post('/api/v1/payments/', $data);
$arr = $response->json();
$payment_id = $arr['data']['id'];
$payment_2 = Payment::whereId($this->decodePrimaryKey($payment_id))->first();
$this->assertEquals(0, $invoice->fresh()->balance);
$this->assertEquals(0, $invoice->client->fresh()->balance);
$this->assertEquals(325, $invoice->client->fresh()->paid_to_date);
//refund payment 2 by 63 dollars
$data = [
'id' => $this->encodePrimaryKey($payment_2->id),
'amount' => 63,
'date' => '2021/12/12',
'invoices' => [
[
'invoice_id' => $invoice->hashed_id,
'amount' => 63,
],
],
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $token->token,
])->post('/api/v1/payments/refund', $data);
$this->assertEquals(63, $invoice->fresh()->balance);
$this->assertEquals(63, $invoice->client->fresh()->balance);
$this->assertEquals(262, $invoice->client->fresh()->paid_to_date);
//delete payment 2
//
$data = [
'ids' => [$this->encodePrimaryKey($payment_2->id)],
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $token->token,
])->post('/api/v1/payments/bulk?action=delete', $data);
$this->assertEquals(162, $invoice->fresh()->balance);
$this->assertEquals(162, $invoice->client->fresh()->balance);
$this->assertEquals(163, $invoice->client->fresh()->paid_to_date);
// Pay 162 again and create payment #3
$data = [
'amount' => 162.0,
'client_id' => $this->encodePrimaryKey($client->id),
'invoices' => [
[
'invoice_id' => $this->encodePrimaryKey($invoice->id),
'amount' => 162,
],
],
'date' => '2019/12/12',
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $token->token,
])->post('/api/v1/payments/', $data);
$arr = $response->json();
$payment_id = $arr['data']['id'];
$payment_3 = Payment::whereId($this->decodePrimaryKey($payment_id))->first();
$invoice->fresh();
$invoice->client->fresh();
$this->assertEquals(0, $invoice->fresh()->balance);
$this->assertEquals(0, $invoice->client->fresh()->balance);
$this->assertEquals(325, $invoice->client->fresh()->paid_to_date);
//refund payment 3 by 63
$data = [
'id' => $this->encodePrimaryKey($payment_3->id),
'amount' => 63,
'date' => '2021/12/12',
'invoices' => [
[
'invoice_id' => $invoice->hashed_id,
'amount' => 63,
],
],
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $token->token,
])->post('/api/v1/payments/refund', $data);
$this->assertEquals(63, $invoice->fresh()->balance);
$this->assertEquals(63, $invoice->client->fresh()->balance);
$this->assertEquals(262, $invoice->client->fresh()->paid_to_date);
//payment 4 for 63
$data = [
'amount' => 63.0,
'client_id' => $this->encodePrimaryKey($client->id),
'invoices' => [
[
'invoice_id' => $this->encodePrimaryKey($invoice->id),
'amount' => 63,
],
],
'date' => '2019/12/12',
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $token->token,
])->post('/api/v1/payments/', $data);
$arr = $response->json();
$payment_id = $arr['data']['id'];
$payment_4 = Payment::whereId($this->decodePrimaryKey($payment_id))->first();
$this->assertEquals(0, $invoice->fresh()->balance);
$this->assertEquals(0, $invoice->client->fresh()->balance);
$this->assertEquals(325, $invoice->client->fresh()->paid_to_date);
// delete payment 3
//
//
$data = [
'ids' => [$this->encodePrimaryKey($payment_4->id)],
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $token->token,
])->post('/api/v1/payments/bulk?action=delete', $data);
$this->assertEquals(63, $invoice->fresh()->balance);
$this->assertEquals(63, $invoice->client->fresh()->balance);
$this->assertEquals(262, $invoice->client->fresh()->paid_to_date);
//set discount of 63 to invoice
$invoice = $invoice->fresh();
$invoice->discount = 63;
$invoice->is_amount_discount = true;
$invoice->save();
$invoice->calc()->getInvoice()->save();
$invoice->service()->updateStatus()->save();
$invoice->ledger()->updateInvoiceBalance(-63, "Update adjustment for invoice {$invoice->number}");
$invoice->client->service()->updateBalance(-63)->save();
$this->assertEquals(0, $invoice->fresh()->balance);
$this->assertEquals(0, $invoice->client->fresh()->balance);
$this->assertEquals(262, $invoice->client->fresh()->paid_to_date);
//now delete the invoice
$data = [
'ids' => [$invoice->hashed_id],
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $token->token,
])->post('/api/v1/invoices/bulk?action=delete', $data);
$this->assertEquals(0, $invoice->fresh()->balance);
$this->assertEquals(0, $invoice->client->fresh()->balance);
$this->assertEquals(0, $invoice->client->fresh()->paid_to_date);
//Delete payment 4 which is for $162
$data = [
'ids' => [$this->encodePrimaryKey($payment_1->id)],
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $token->token,
])->post('/api/v1/payments/bulk?action=delete', $data);
$this->assertEquals(0, $invoice->fresh()->balance);
$this->assertEquals(0, $invoice->client->fresh()->balance);
$this->assertEquals(0, $invoice->client->fresh()->paid_to_date);
}
}