diff --git a/app/Services/Payment/DeletePayment.php b/app/Services/Payment/DeletePayment.php index a13697e63a..a462c23195 100644 --- a/app/Services/Payment/DeletePayment.php +++ b/app/Services/Payment/DeletePayment.php @@ -139,10 +139,12 @@ class DeletePayment // 2025-03-26 - If we are deleting a negative payment, then there is an edge case where the paid to date will be reduced further down. // for this scenario, we skip the update to the client paid to date at this point. + + //2025-08-19 - if there is an unapplied amount, we need to subtract it from the paid to date. $this->payment ->client ->service() - ->updateBalanceAndPaidToDate($net_deletable, ($net_deletable * -1) > 0 ? 0 : ($net_deletable * -1)) // if negative, set to 0, the paid to date will be reduced further down. + ->updateBalanceAndPaidToDate($net_deletable, ($net_deletable * -1) > 0 ? 0 : ($net_deletable * -1 - ($this->payment->amount - $this->payment->applied))) // if negative, set to 0, the paid to date will be reduced further down. ->save(); if (abs(floatval($paymentable_invoice->balance) - floatval($paymentable_invoice->amount)) < 0.005) { @@ -172,6 +174,7 @@ class DeletePayment $reduced_paid_to_date = $this->payment->amount < 0 ? $this->payment->amount * -1 : min(0, ($this->payment->amount - $this->payment->refunded - $this->_paid_to_date_deleted) * -1); + nlog("reduced paid to date: {$reduced_paid_to_date}"); if($reduced_paid_to_date != 0) { $this->payment ->client diff --git a/tests/Feature/CreditTest.php b/tests/Feature/CreditTest.php index ac34a0ab25..35d9cb6cfc 100644 --- a/tests/Feature/CreditTest.php +++ b/tests/Feature/CreditTest.php @@ -11,15 +11,17 @@ namespace Tests\Feature; -use App\DataMapper\InvoiceItem; use App\Models\Client; -use App\Models\ClientContact; use App\Models\Credit; -use App\Utils\Traits\MakesHash; -use Illuminate\Database\Eloquent\Model; -use Illuminate\Foundation\Testing\DatabaseTransactions; -use Illuminate\Support\Facades\Session; +use App\Models\ClientContact; +use App\DataMapper\InvoiceItem; use Tests\MockAccountData; +use App\Utils\Traits\MakesHash; +use App\Repositories\InvoiceRepository; +use App\Repositories\CreditRepository; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Facades\Session; +use Illuminate\Foundation\Testing\DatabaseTransactions; use Tests\TestCase; class CreditTest extends TestCase @@ -43,6 +45,125 @@ class CreditTest extends TestCase $this->makeTestData(); } + + public function testPartialAmountWithPartialCreditAndPaymentDeletedBalance() + { + + + $c = Client::factory()->create([ + 'company_id' => $this->company->id, + 'user_id' => $this->user->id, + 'balance' => 0, + 'paid_to_date' => 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, + ]); + + $repo = new InvoiceRepository(); + $repo->save([], $i); + + $i = $i->calc()->getInvoice(); + $i = $i->service()->markSent()->save(); + + $this->assertEquals(100, $i->balance); + $this->assertEquals(100, $i->amount); + + $cr = \App\Models\Credit::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, + ]); + + $repo = new CreditRepository(); + $repo->save([], $cr); + + $cr = $cr->calc()->getInvoice(); + $cr = $cr->service()->markSent()->save(); + + $this->assertEquals(100, $cr->balance); + $this->assertEquals(100, $cr->amount); + + + $data = [ + 'date' => '2020/12/12', + 'client_id' => $c->hashed_id, + 'amount' => 10, + 'invoices' => [ + [ + 'invoice_id' => $i->hashed_id, + 'amount' => 10 + ], + ], + 'credits' => [ + [ + 'credit_id' => $cr->hashed_id, + 'amount' => 10 + ] + ], + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->postJson('/api/v1/payments', $data); + + $response->assertStatus(200); + + $arr = $response->json(); + + $this->assertEquals(10, $arr['data']['amount']); + + $this->assertEquals(20, $c->fresh()->paid_to_date); + $this->assertEquals(90, $i->fresh()->balance); + $this->assertEquals(90, $cr->fresh()->balance); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->deleteJson('/api/v1/payments/'.$arr['data']['id']); + + $response->assertStatus(200); + + $this->assertEquals(100, $i->fresh()->balance); + $this->assertEquals(100, $cr->fresh()->balance); + $this->assertEquals(100, $c->fresh()->balance); + $this->assertEquals(0, $c->fresh()->paid_to_date); + + + } + public function testCreditReversalScenarioInvoicePartiallyPaid() { @@ -117,6 +238,8 @@ class CreditTest extends TestCase $this->assertEquals(0, $c->balance); $this->assertEquals(6, $i->status_id); + + }