diff --git a/app/Helpers/Invoice/InvoiceItemSum.php b/app/Helpers/Invoice/InvoiceItemSum.php index dc946874f0..ef2cddb0ef 100644 --- a/app/Helpers/Invoice/InvoiceItemSum.php +++ b/app/Helpers/Invoice/InvoiceItemSum.php @@ -29,6 +29,7 @@ class InvoiceItemSum use Discounter; use Taxer; + //@phpstan-ignore-next-line private array $eu_tax_jurisdictions = [ 'AT', // Austria 'BE', // Belgium @@ -170,7 +171,7 @@ class InvoiceItemSum private function shouldCalculateTax(): self { - if (!$this->invoice->company?->calculate_taxes || $this->invoice->company->account->isFreeHostedClient()) { + if (!$this->invoice->company?->calculate_taxes || $this->invoice->company->account->isFreeHostedClient()) { //@phpstan-ignore-line $this->calc_tax = false; return $this; } diff --git a/app/Helpers/Invoice/InvoiceItemSumInclusive.php b/app/Helpers/Invoice/InvoiceItemSumInclusive.php index 88416f6c86..eacb13afb9 100644 --- a/app/Helpers/Invoice/InvoiceItemSumInclusive.php +++ b/app/Helpers/Invoice/InvoiceItemSumInclusive.php @@ -27,7 +27,7 @@ class InvoiceItemSumInclusive use Discounter; use Taxer; - + //@phpstan-ignore-next-line private array $eu_tax_jurisdictions = [ 'AT', // Austria 'BE', // Belgium @@ -98,6 +98,7 @@ class InvoiceItemSumInclusive private $total_taxes; + /** @phpstan-ignore-next-line */ private $item; private $line_items; @@ -399,7 +400,7 @@ class InvoiceItemSumInclusive private function shouldCalculateTax(): self { - if (!$this->invoice->company->calculate_taxes || $this->invoice->company->account->isFreeHostedClient()) { + if (!$this->invoice->company?->calculate_taxes || $this->invoice->company->account->isFreeHostedClient()) {//@phpstan-ignore-line $this->calc_tax = false; return $this; } diff --git a/app/Http/Controllers/MigrationController.php b/app/Http/Controllers/MigrationController.php index 85fec16ab0..11123bf830 100644 --- a/app/Http/Controllers/MigrationController.php +++ b/app/Http/Controllers/MigrationController.php @@ -280,186 +280,172 @@ class MigrationController extends BaseController } } - if (app()->environment() === 'local') { - } - - try { - return response()->json([ - '_id' => Str::uuid(), - 'method' => config('queue.default'), - 'started_at' => now(), - ], 200); - } finally { - // Controller logic here - - foreach ($companies as $company) { - if (! is_array($company)) { - continue; - } - - $company = (array) $company; - - $user = auth()->user(); - - $company_count = $user->account->companies()->count(); - $fresh_company = false; - - // Look for possible existing company (based on company keys). - $existing_company = Company::query()->whereRaw('BINARY `company_key` = ?', [$company['company_key']])->first(); - - App::forgetInstance('translator'); - $t = app('translator'); - $t->replace(Ninja::transformTranslations($user->account->companies()->first()->settings)); - App::setLocale($user->account->companies()->first()->getLocale()); - - if (! $existing_company && $company_count >= 10) { - $nmo = new NinjaMailerObject(); - $nmo->mailable = new MaxCompanies($user->account->companies()->first()); - $nmo->company = $user->account->companies()->first(); - $nmo->settings = $user->account->companies()->first()->settings; - $nmo->to_user = $user; - - if(!$this->silent_migration) { - NinjaMailerJob::dispatch($nmo, true); - } - - return; - } elseif ($existing_company && $company_count > 10) { - $nmo = new NinjaMailerObject(); - $nmo->mailable = new MaxCompanies($user->account->companies()->first()); - $nmo->company = $user->account->companies()->first(); - $nmo->settings = $user->account->companies()->first()->settings; - $nmo->to_user = $user; - - if(!$this->silent_migration) { - NinjaMailerJob::dispatch($nmo, true); - } - - return; - } - - $checks = [ - 'existing_company' => $existing_company ? (bool) 1 : false, - 'force' => array_key_exists('force', $company) ? (bool) $company['force'] : false, - ]; - - // If there's existing company and ** no ** force is provided - skip migration. - if ($checks['existing_company'] == true && $checks['force'] == false) { - nlog('Migrating: Existing company without force. (CASE_01)'); - - $nmo = new NinjaMailerObject(); - $nmo->mailable = new ExistingMigration($existing_company); - $nmo->company = $user->account->companies()->first(); - $nmo->settings = $user->account->companies()->first(); - $nmo->to_user = $user; - - if(!$this->silent_migration) { - NinjaMailerJob::dispatch($nmo, true); - } - - return response()->json([ - '_id' => Str::uuid(), - 'method' => config('queue.default'), - 'started_at' => now(), - ], 200); - } - - // If there's existing company and force ** is provided ** - purge the company and migrate again. - if ($checks['existing_company'] == true && $checks['force'] == true) { - nlog('purging the existing company here'); - $this->purgeCompanyWithForceFlag($existing_company); - - $account = auth()->user()->account; - $fresh_company = (new ImportMigrations())->getCompany($account); - $fresh_company->is_disabled = true; - $fresh_company->save(); - - $account->default_company_id = $fresh_company->id; - $account->save(); - - $fresh_company_token = new CompanyToken(); - $fresh_company_token->user_id = $user->id; - $fresh_company_token->company_id = $fresh_company->id; - $fresh_company_token->account_id = $account->id; - $fresh_company_token->name = $request->token_name ?? Str::random(12); - $fresh_company_token->token = $request->token ?? Str::random(64); - $fresh_company_token->is_system = true; - $fresh_company_token->save(); - - /** @var \App\Models\User $user */ - $user->companies()->attach($fresh_company->id, [ - 'account_id' => $account->id, - 'is_owner' => 1, - 'is_admin' => 1, - 'is_locked' => 0, - 'notifications' => CompanySettings::notificationDefaults(), - 'permissions' => '', - 'settings' => null, - ]); - } - - // If there's no existing company migrate just normally. - if ($checks['existing_company'] == false) { - nlog('creating fresh company'); - - $account = auth()->user()->account; - $fresh_company = (new ImportMigrations())->getCompany($account); - - $fresh_company->is_disabled = true; - $fresh_company->save(); - - $fresh_company_token = new CompanyToken(); - $fresh_company_token->user_id = $user->id; - $fresh_company_token->company_id = $fresh_company->id; - $fresh_company_token->account_id = $account->id; - $fresh_company_token->name = $request->token_name ?? Str::random(12); - $fresh_company_token->token = $request->token ?? Str::random(64); - $fresh_company_token->is_system = true; - - $fresh_company_token->save(); - - /** @var \App\Models\User $user */ - $user->companies()->attach($fresh_company->id, [ - 'account_id' => $account->id, - 'is_owner' => 1, - 'is_admin' => 1, - 'is_locked' => 0, - 'notifications' => CompanySettings::notificationDefaults(), - 'permissions' => '', - 'settings' => null, - ]); - } - - $migration_file = $request->file($company['company_index']) - ->storeAs( - 'migrations', - $request->file($company['company_index'])->getClientOriginalName(), - 'public' - ); - - if (app()->environment() == 'testing') { - nlog('environment is testing = bailing out now'); - - return; - } - - nlog('starting migration job'); - nlog($migration_file); - - if (Ninja::isHosted()) { - StartMigration::dispatch($migration_file, $user, $fresh_company, $this->silent_migration)->onQueue('migration'); - } else { - StartMigration::dispatch($migration_file, $user, $fresh_company, $this->silent_migration); - } + foreach ($companies as $company) { + if (! is_array($company)) { + continue; } - return response()->json([ - '_id' => Str::uuid(), - 'method' => config('queue.default'), - 'started_at' => now(), - ], 200); + $company = (array) $company; + $user = auth()->user(); + + $company_count = $user->account->companies()->count(); + $fresh_company = false; + + // Look for possible existing company (based on company keys). + $existing_company = Company::query()->whereRaw('BINARY `company_key` = ?', [$company['company_key']])->first(); + + App::forgetInstance('translator'); + $t = app('translator'); + $t->replace(Ninja::transformTranslations($user->account->companies()->first()->settings)); + App::setLocale($user->account->companies()->first()->getLocale()); + + if (! $existing_company && $company_count >= 10) { + $nmo = new NinjaMailerObject(); + $nmo->mailable = new MaxCompanies($user->account->companies()->first()); + $nmo->company = $user->account->companies()->first(); + $nmo->settings = $user->account->companies()->first()->settings; + $nmo->to_user = $user; + + if(!$this->silent_migration) { + NinjaMailerJob::dispatch($nmo, true); + } + + return; + } elseif ($existing_company && $company_count > 10) { + $nmo = new NinjaMailerObject(); + $nmo->mailable = new MaxCompanies($user->account->companies()->first()); + $nmo->company = $user->account->companies()->first(); + $nmo->settings = $user->account->companies()->first()->settings; + $nmo->to_user = $user; + + if(!$this->silent_migration) { + NinjaMailerJob::dispatch($nmo, true); + } + + return; + } + + $checks = [ + 'existing_company' => $existing_company ? (bool) 1 : false, + 'force' => array_key_exists('force', $company) ? (bool) $company['force'] : false, + ]; + + // If there's existing company and ** no ** force is provided - skip migration. + if ($checks['existing_company'] == true && $checks['force'] == false) { + nlog('Migrating: Existing company without force. (CASE_01)'); + + $nmo = new NinjaMailerObject(); + $nmo->mailable = new ExistingMigration($existing_company); + $nmo->company = $user->account->companies()->first(); + $nmo->settings = $user->account->companies()->first(); + $nmo->to_user = $user; + + if(!$this->silent_migration) { + NinjaMailerJob::dispatch($nmo, true); + } + + return response()->json([ + '_id' => Str::uuid(), + 'method' => config('queue.default'), + 'started_at' => now(), + ], 200); + } + + // If there's existing company and force ** is provided ** - purge the company and migrate again. + if ($checks['existing_company'] == true && $checks['force'] == true) { + nlog('purging the existing company here'); + $this->purgeCompanyWithForceFlag($existing_company); + + $account = auth()->user()->account; + $fresh_company = (new ImportMigrations())->getCompany($account); + $fresh_company->is_disabled = true; + $fresh_company->save(); + + $account->default_company_id = $fresh_company->id; + $account->save(); + + $fresh_company_token = new CompanyToken(); + $fresh_company_token->user_id = $user->id; + $fresh_company_token->company_id = $fresh_company->id; + $fresh_company_token->account_id = $account->id; + $fresh_company_token->name = $request->token_name ?? Str::random(12); + $fresh_company_token->token = $request->token ?? Str::random(64); + $fresh_company_token->is_system = true; + $fresh_company_token->save(); + + /** @var \App\Models\User $user */ + $user->companies()->attach($fresh_company->id, [ + 'account_id' => $account->id, + 'is_owner' => 1, + 'is_admin' => 1, + 'is_locked' => 0, + 'notifications' => CompanySettings::notificationDefaults(), + 'permissions' => '', + 'settings' => null, + ]); + } + + // If there's no existing company migrate just normally. + if ($checks['existing_company'] == false) { + nlog('creating fresh company'); + + $account = auth()->user()->account; + $fresh_company = (new ImportMigrations())->getCompany($account); + + $fresh_company->is_disabled = true; + $fresh_company->save(); + + $fresh_company_token = new CompanyToken(); + $fresh_company_token->user_id = $user->id; + $fresh_company_token->company_id = $fresh_company->id; + $fresh_company_token->account_id = $account->id; + $fresh_company_token->name = $request->token_name ?? Str::random(12); + $fresh_company_token->token = $request->token ?? Str::random(64); + $fresh_company_token->is_system = true; + + $fresh_company_token->save(); + + /** @var \App\Models\User $user */ + $user->companies()->attach($fresh_company->id, [ + 'account_id' => $account->id, + 'is_owner' => 1, + 'is_admin' => 1, + 'is_locked' => 0, + 'notifications' => CompanySettings::notificationDefaults(), + 'permissions' => '', + 'settings' => null, + ]); + } + + $migration_file = $request->file($company['company_index']) + ->storeAs( + 'migrations', + $request->file($company['company_index'])->getClientOriginalName(), + 'public' + ); + + if (app()->environment() == 'testing') { + nlog('environment is testing = bailing out now'); + + return; + } + + nlog('starting migration job'); + nlog($migration_file); + + if (Ninja::isHosted()) { + StartMigration::dispatch($migration_file, $user, $fresh_company, $this->silent_migration)->onQueue('migration'); + } else { + StartMigration::dispatch($migration_file, $user, $fresh_company, $this->silent_migration); + } } + return response()->json([ + '_id' => Str::uuid(), + 'method' => config('queue.default'), + 'started_at' => now(), + ], 200); + } } diff --git a/app/Http/Controllers/OneTimeTokenController.php b/app/Http/Controllers/OneTimeTokenController.php index 6e3f54f2e9..1d4773b530 100644 --- a/app/Http/Controllers/OneTimeTokenController.php +++ b/app/Http/Controllers/OneTimeTokenController.php @@ -22,8 +22,6 @@ use Illuminate\Support\Str; class OneTimeTokenController extends BaseController { - private $contexts = [ - ]; public function __construct() { diff --git a/app/Http/Controllers/PostMarkController.php b/app/Http/Controllers/PostMarkController.php index ec8a481c38..e2974f9983 100644 --- a/app/Http/Controllers/PostMarkController.php +++ b/app/Http/Controllers/PostMarkController.php @@ -19,7 +19,6 @@ use Illuminate\Http\Request; */ class PostMarkController extends BaseController { - private $invitation; public function __construct() { diff --git a/app/Http/Controllers/SetupController.php b/app/Http/Controllers/SetupController.php index f30831855c..7c91dc91a0 100644 --- a/app/Http/Controllers/SetupController.php +++ b/app/Http/Controllers/SetupController.php @@ -234,24 +234,6 @@ class SetupController extends Controller } } - private function testPhantom() - { - try { - $key = config('ninja.phantomjs_key'); - $url = 'https://www.invoiceninja.org/'; - - $phantom_url = "https://phantomjscloud.com/api/browser/v2/{$key}/?request=%7Burl:%22{$url}%22,renderType:%22pdf%22%7D"; - $pdf = CurlUtils::get($phantom_url); - - Storage::disk(config('filesystems.default'))->put('test.pdf', $pdf); - Storage::disk('local')->put('test.pdf', $pdf); - - return response(['url' => Storage::disk('local')->url('test.pdf')], 200); - } catch (Exception $e) { - return response([], 500); - } - } - public function clearCompiledCache() { $cacheCompiled = base_path('bootstrap/cache/compiled.php'); diff --git a/app/Import/Transformer/Invoice2Go/InvoiceTransformer.php b/app/Import/Transformer/Invoice2Go/InvoiceTransformer.php index cf7771d637..28d8d530aa 100644 --- a/app/Import/Transformer/Invoice2Go/InvoiceTransformer.php +++ b/app/Import/Transformer/Invoice2Go/InvoiceTransformer.php @@ -277,7 +277,7 @@ class InvoiceTransformer extends BaseTransformer if($key == 0) { continue; } - + /** @var array $row */ if(is_array($row[5])) { $csv = str_getcsv($row[5][0], ";"); $row[5] = array_combine(explode(",", $csv[0]), explode(",", $csv[1])); diff --git a/app/Jobs/Entity/CreateRawPdf.php b/app/Jobs/Entity/CreateRawPdf.php index aa10bdf019..4d1936c85f 100644 --- a/app/Jobs/Entity/CreateRawPdf.php +++ b/app/Jobs/Entity/CreateRawPdf.php @@ -40,7 +40,7 @@ class CreateRawPdf public Invoice | Credit | Quote | RecurringInvoice | PurchaseOrder $entity; - public $company; + public \App\Models\Company $company; public $contact; @@ -55,6 +55,7 @@ class CreateRawPdf { $this->invitation = $invitation; + $this->company = $invitation->company; if ($invitation instanceof InvoiceInvitation) { $this->entity = $invitation->invoice; @@ -115,7 +116,14 @@ class CreateRawPdf } if ($this->entity_string == "invoice" && $this->entity->client->getSetting("merge_e_invoice_to_pdf")) { $pdf = (new MergeEDocument($this->entity, $pdf))->handle(); + } + + $merge_docs = $this->entity->client ? $this->entity->client->getSetting('embed_documents') : $this->company->getSetting('embed_documents'); + + if($merge_docs && ($this->entity->documents()->where('is_public', true)->count() > 0 || $this->company->documents()->where('is_public', true)->count() > 0)) { + $pdf = $this->entity->documentMerge($pdf); } + return $pdf; } diff --git a/app/Jobs/Invoice/CreateUbl.php b/app/Jobs/Invoice/CreateUbl.php index 167018736f..cd018b4ed9 100644 --- a/app/Jobs/Invoice/CreateUbl.php +++ b/app/Jobs/Invoice/CreateUbl.php @@ -192,7 +192,7 @@ class CreateUbl implements ShouldQueue /** * @param $item * @param $invoice_total - * @return float|int + * @return float */ private function getItemTaxable($item, $invoice_total) { diff --git a/app/Models/User.php b/app/Models/User.php index 102c4625c5..e3c03c63eb 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -60,7 +60,7 @@ use Laracasts\Presenter\PresentableTrait; * @property bool $is_deleted * @property string|null $last_login * @property string|null $signature - * @property string $password + * @property string|null $password * @property string $language_id * @property string|null $remember_token * @property string|null $custom_value1 diff --git a/app/PaymentDrivers/Braintree/CreditCard.php b/app/PaymentDrivers/Braintree/CreditCard.php index 654890cf06..8ba6e0154f 100644 --- a/app/PaymentDrivers/Braintree/CreditCard.php +++ b/app/PaymentDrivers/Braintree/CreditCard.php @@ -53,7 +53,7 @@ class CreditCard * Credit card payment page. * * @param array $data - * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View + * @return array */ private function threeDParameters(array $data) diff --git a/app/PaymentDrivers/PayPal/PayPalWebhook.php b/app/PaymentDrivers/PayPal/PayPalWebhook.php index e4ba19d442..2840e5ccb4 100644 --- a/app/PaymentDrivers/PayPal/PayPalWebhook.php +++ b/app/PaymentDrivers/PayPal/PayPalWebhook.php @@ -307,7 +307,7 @@ class PayPalWebhook implements ShouldQueue }); - return $gateway ?? false; + return $gateway ?? null; } //--------------------------------------------------------------------------------------// diff --git a/app/PaymentDrivers/PayPalPPCPPaymentDriver.php b/app/PaymentDrivers/PayPalPPCPPaymentDriver.php index 1803265d4b..14fd0ace2d 100644 --- a/app/PaymentDrivers/PayPalPPCPPaymentDriver.php +++ b/app/PaymentDrivers/PayPalPPCPPaymentDriver.php @@ -284,6 +284,10 @@ class PayPalPPCPPaymentDriver extends PayPalBasePaymentDriver $r = $this->gatewayRequest('/v2/checkout/orders', 'post', $order); + if(!isset($r->json()['id'])) { + $this->handleProcessingFailure($r->json()); + } + $this->payment_hash->withData("orderID", $r->json()['id']); return $r->json()['id']; diff --git a/app/Repositories/PaymentRepository.php b/app/Repositories/PaymentRepository.php index f0924ec7b5..e7879ec8dd 100644 --- a/app/Repositories/PaymentRepository.php +++ b/app/Repositories/PaymentRepository.php @@ -59,7 +59,7 @@ class PaymentRepository extends BaseRepository * @param Payment $payment The $payment entity * @return Payment The updated/created payment object */ - private function applyPayment(array $data, Payment $payment): ?Payment + private function applyPayment(array $data, Payment $payment): Payment { $is_existing_payment = true; $client = false; diff --git a/app/Services/Credit/CreditService.php b/app/Services/Credit/CreditService.php index 9474058f85..927bc3c16a 100644 --- a/app/Services/Credit/CreditService.php +++ b/app/Services/Credit/CreditService.php @@ -219,27 +219,6 @@ class CreditService public function deletePdf() { - $this->credit->invitations->each(function ($invitation) { - // (new UnlinkFile(config('filesystems.default'), $this->credit->client->credit_filepath($invitation).$this->credit->numberFormatter().'.pdf'))->handle(); - - //30-06-2023 - try { - // if (Storage::disk(config('filesystems.default'))->exists($this->invoice->client->invoice_filepath($invitation).$this->invoice->numberFormatter().'.pdf')) { - Storage::disk(config('filesystems.default'))->delete($this->credit->client->credit_filepath($invitation).$this->credit->numberFormatter().'.pdf'); - // } - - // if (Ninja::isHosted() && Storage::disk('public')->exists($this->invoice->client->invoice_filepath($invitation).$this->invoice->numberFormatter().'.pdf')) { - if (Ninja::isHosted()) { - Storage::disk('public')->delete($this->credit->client->credit_filepath($invitation).$this->credit->numberFormatter().'.pdf'); - } - } catch (\Exception $e) { - nlog($e->getMessage()); - } - - - - }); - return $this; } @@ -273,11 +252,14 @@ class CreditService public function deleteCredit() { + $paid_to_date = $this->credit->invoice_id ? $this->credit->balance : 0; + $this->credit - ->client - ->service() - ->adjustCreditBalance($this->credit->balance * -1) - ->save(); + ->client + ->service() + ->updatePaidToDate($paid_to_date) + ->adjustCreditBalance($this->credit->balance * -1) + ->save(); return $this; } @@ -285,9 +267,13 @@ class CreditService public function restoreCredit() { + + $paid_to_date = $this->credit->invoice_id ? $this->credit->balance : 0; + $this->credit ->client ->service() + ->updatePaidToDate($paid_to_date * -1) ->adjustCreditBalance($this->credit->balance) ->save(); diff --git a/app/Services/EDocument/Standards/RoEInvoice.php b/app/Services/EDocument/Standards/RoEInvoice.php index ebee4d1fcd..016f810d4e 100644 --- a/app/Services/EDocument/Standards/RoEInvoice.php +++ b/app/Services/EDocument/Standards/RoEInvoice.php @@ -405,7 +405,7 @@ class RoEInvoice extends AbstractService /** * @param $item * @param $invoice_total - * @return float|int + * @return float */ private function getItemTaxable($item, $invoice_total) { diff --git a/app/Services/Email/EmailDefaults.php b/app/Services/Email/EmailDefaults.php index 068ad85c77..25a7d0204b 100644 --- a/app/Services/Email/EmailDefaults.php +++ b/app/Services/Email/EmailDefaults.php @@ -406,7 +406,7 @@ class EmailDefaults * @param string $markdown The body to convert * @return string The parsed markdown response */ - private function parseMarkdownToHtml(string $markdown): ?string + private function parseMarkdownToHtml(string $markdown): string { $converter = new CommonMarkConverter([ 'allow_unsafe_links' => false, diff --git a/app/Utils/Traits/CleanLineItems.php b/app/Utils/Traits/CleanLineItems.php index 9cd45a73f5..e82623bd3f 100644 --- a/app/Utils/Traits/CleanLineItems.php +++ b/app/Utils/Traits/CleanLineItems.php @@ -110,8 +110,6 @@ trait CleanLineItems $total += ($item['cost'] * $item['quantity']); } - nlog($total); - return $total; } } diff --git a/tests/Feature/CreditTest.php b/tests/Feature/CreditTest.php index 28aba2164b..40e4409e08 100644 --- a/tests/Feature/CreditTest.php +++ b/tests/Feature/CreditTest.php @@ -43,6 +43,102 @@ class CreditTest extends TestCase $this->makeTestData(); } + public function testPaidToDateAdjustments() + { + + $c = Client::factory()->create([ + 'company_id' => $this->company->id, + 'user_id' => $this->user->id, + 'balance' => 0, + ]); + + $ii = new InvoiceItem(); + $ii->cost = 100; + $ii->quantity = 1; + $ii->product_key = 'xx'; + $ii->notes = 'yy'; + + $i = \App\Models\Invoice::factory()->create([ + 'company_id' => $this->company->id, + 'user_id' => $this->user->id, + 'client_id' => $c->id, + 'tax_name1' => '', + 'tax_name2' => '', + 'tax_name3' => '', + 'tax_rate1' => 0, + 'tax_rate2' => 0, + 'tax_rate3' => 0, + 'discount' => 0, + 'line_items' => [ + $ii + ], + 'status_id' => 1, + ]); + + $i = $i->calc()->getInvoice(); + + $this->assertEquals(0, $i->balance); + $this->assertEquals(100, $i->amount); + + $i->service()->markSent()->save(); + + $this->assertEquals(100, $i->balance); + + $i->service()->markPaid()->save(); + $i = $i->fresh(); + $c = $c->fresh(); + + $this->assertEquals(0, $i->balance); + $this->assertEquals(0, $c->balance); + + $this->assertEquals(100, $c->paid_to_date); + + $i->service()->handleReversal()->save(); + + + $data = $i->toArray(); + $data['invoice_id'] = $i->hashed_id; + $data['user_id'] = $this->encodePrimaryKey($i->user_id); + $data['client_id'] = $this->encodePrimaryKey($i->client_id); + $data['status_id'] = 1; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->postJson("/api/v1/credits?mark_sent=true", $data); + + $response->assertStatus(200); + $arr = $response->json(); + + $cr_id = $arr['data']['id']; + + $i = $i->fresh(); + $c = $c->fresh(); + + $credit = $i->credits()->first(); + + $this->assertNotNull($credit); + + $this->assertEquals(0, $i->balance); + $this->assertEquals(100, $c->credit_balance); + $this->assertEquals(0, $c->paid_to_date); + + $credit->service()->deleteCredit()->save(); + + $c = $c->fresh(); + + $this->assertEquals(100, $c->paid_to_date); + $this->assertEquals(0, $c->credit_balance); + + $credit->service()->restoreCredit()->save(); + + $c = $c->fresh(); + + $this->assertEquals(0, $c->paid_to_date); + $this->assertEquals(100, $c->credit_balance); + + } + public function testCreditPaymentsPaidToDates() { $c = Client::factory()->create([