mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-05-31 21:34:35 -04:00
Fixes for attempting to pass a negative value for a applied amount
This commit is contained in:
parent
73c73d1f5c
commit
a871e319ea
@ -78,7 +78,6 @@ class StorePaymentRequest extends Request
|
|||||||
foreach ($input['credits'] as $key => $value) {
|
foreach ($input['credits'] as $key => $value) {
|
||||||
if (array_key_exists('credit_id', $input['credits'][$key])) {
|
if (array_key_exists('credit_id', $input['credits'][$key])) {
|
||||||
$input['credits'][$key]['credit_id'] = $this->decodePrimaryKey($value['credit_id']);
|
$input['credits'][$key]['credit_id'] = $this->decodePrimaryKey($value['credit_id']);
|
||||||
|
|
||||||
$credits_total += $value['amount'];
|
$credits_total += $value['amount'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,8 @@ class UpdatePaymentRequest extends Request
|
|||||||
|
|
||||||
$rules = [
|
$rules = [
|
||||||
'invoices' => ['array', new PaymentAppliedValidAmount($this->all()), new ValidCreditsPresentRule($this->all())],
|
'invoices' => ['array', new PaymentAppliedValidAmount($this->all()), new ValidCreditsPresentRule($this->all())],
|
||||||
'invoices.*.invoice_id' => 'distinct',
|
'invoices.*.invoice_id' => 'sometimes|distinct',
|
||||||
|
'invoices.*.amount' => 'sometimes|numeric|min:0',
|
||||||
];
|
];
|
||||||
|
|
||||||
if ($this->number) {
|
if ($this->number) {
|
||||||
@ -85,7 +86,6 @@ class UpdatePaymentRequest extends Request
|
|||||||
if (isset($input['invoices']) && is_array($input['invoices']) !== false) {
|
if (isset($input['invoices']) && is_array($input['invoices']) !== false) {
|
||||||
foreach ($input['invoices'] as $key => $value) {
|
foreach ($input['invoices'] as $key => $value) {
|
||||||
if(isset($input['invoices'][$key]['invoice_id'])) {
|
if(isset($input['invoices'][$key]['invoice_id'])) {
|
||||||
// if (array_key_exists('invoice_id', $input['invoices'][$key])) {
|
|
||||||
$input['invoices'][$key]['invoice_id'] = $this->decodePrimaryKey($value['invoice_id']);
|
$input['invoices'][$key]['invoice_id'] = $this->decodePrimaryKey($value['invoice_id']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -93,7 +93,6 @@ class UpdatePaymentRequest extends Request
|
|||||||
|
|
||||||
if (isset($input['credits']) && is_array($input['credits']) !== false) {
|
if (isset($input['credits']) && is_array($input['credits']) !== false) {
|
||||||
foreach ($input['credits'] as $key => $value) {
|
foreach ($input['credits'] as $key => $value) {
|
||||||
// if (array_key_exists('credits', $input['credits'][$key])) {
|
|
||||||
if (isset($input['credits'][$key]['credit_id'])) {
|
if (isset($input['credits'][$key]['credit_id'])) {
|
||||||
$input['credits'][$key]['credit_id'] = $this->decodePrimaryKey($value['credit_id']);
|
$input['credits'][$key]['credit_id'] = $this->decodePrimaryKey($value['credit_id']);
|
||||||
}
|
}
|
||||||
|
@ -87,10 +87,6 @@ class PaymentAppliedValidAmount implements Rule
|
|||||||
|
|
||||||
$inv = $inv_collection->firstWhere('id', $invoice['invoice_id']);
|
$inv = $inv_collection->firstWhere('id', $invoice['invoice_id']);
|
||||||
|
|
||||||
nlog($inv->status_id);
|
|
||||||
nlog($inv->amount);
|
|
||||||
nlog($invoice['amount']);
|
|
||||||
|
|
||||||
if($inv->status_id == Invoice::STATUS_DRAFT && $inv->amount >= $invoice['amount']) {
|
if($inv->status_id == Invoice::STATUS_DRAFT && $inv->amount >= $invoice['amount']) {
|
||||||
|
|
||||||
} elseif ($inv->balance < $invoice['amount']) {
|
} elseif ($inv->balance < $invoice['amount']) {
|
||||||
|
@ -49,11 +49,8 @@ class ValidCreditsPresentRule implements Rule
|
|||||||
|
|
||||||
private function validCreditsPresent(): bool
|
private function validCreditsPresent(): bool
|
||||||
{
|
{
|
||||||
//todo need to ensure the clients credits are here not random ones!
|
|
||||||
|
|
||||||
if (array_key_exists('credits', $this->input) && is_array($this->input['credits']) && count($this->input['credits']) > 0) {
|
if (array_key_exists('credits', $this->input) && is_array($this->input['credits']) && count($this->input['credits']) > 0) {
|
||||||
$credit_collection = Credit::query()->whereIn('id', array_column($this->input['credits'], 'credit_id'))->count();
|
$credit_collection = Credit::query()->where('client_id', request()->input('client_id'))->whereIn('id', array_column($this->input['credits'], 'credit_id'))->count();
|
||||||
|
|
||||||
return $credit_collection == count($this->input['credits']);
|
return $credit_collection == count($this->input['credits']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,6 +62,74 @@ class PaymentTest extends TestCase
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testNegativeAppliedAmounts()
|
||||||
|
{
|
||||||
|
$p = Payment::factory()->create([
|
||||||
|
'company_id' => $this->company->id,
|
||||||
|
'user_id' => $this->user->id,
|
||||||
|
'client_id' => $this->client->id,
|
||||||
|
'status_id' => Payment::STATUS_COMPLETED,
|
||||||
|
'amount' => 100
|
||||||
|
]);
|
||||||
|
|
||||||
|
$i = Invoice::factory()->create([
|
||||||
|
'company_id' => $this->company->id,
|
||||||
|
'user_id' => $this->user->id,
|
||||||
|
'client_id' => $this->client->id,
|
||||||
|
'status_id' => Invoice::STATUS_SENT,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$i->calc()->getInvoice()->service()->markSent()->save();
|
||||||
|
|
||||||
|
$this->assertGreaterThan(0, $i->balance);
|
||||||
|
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'amount' => 5,
|
||||||
|
'client_id' => $this->client->hashed_id,
|
||||||
|
'invoices' => [
|
||||||
|
[
|
||||||
|
'invoice_id' => $this->invoice->hashed_id,
|
||||||
|
'amount' => 5,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'date' => '2020/12/11',
|
||||||
|
'idempotency_key' => \Illuminate\Support\Str::uuid()->toString()
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->postJson('/api/v1/payments/', $data);
|
||||||
|
|
||||||
|
$response->assertStatus(200);
|
||||||
|
|
||||||
|
$payment_id = $response->json()['data']['id'];
|
||||||
|
|
||||||
|
$payment = Payment::find($this->decodePrimaryKey($payment_id));
|
||||||
|
|
||||||
|
$this->assertNotNull($payment);
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'client_id' => $this->client->hashed_id,
|
||||||
|
'invoices' => [
|
||||||
|
[
|
||||||
|
'invoice_id' => $this->invoice->hashed_id,
|
||||||
|
'amount' => -5,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'date' => '2020/12/11',
|
||||||
|
'idempotency_key' => \Illuminate\Support\Str::uuid()->toString()
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->putJson('/api/v1/payments/'.$payment_id, $data);
|
||||||
|
|
||||||
|
$response->assertStatus(422);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public function testCompletedPaymentLogic()
|
public function testCompletedPaymentLogic()
|
||||||
{
|
{
|
||||||
@ -1395,8 +1463,9 @@ class PaymentTest extends TestCase
|
|||||||
$invoice_calc = new InvoiceSum($invoice);
|
$invoice_calc = new InvoiceSum($invoice);
|
||||||
$invoice_calc->build();
|
$invoice_calc->build();
|
||||||
|
|
||||||
$invoice = $invoice_calc->getInvoice();
|
$invoice = $invoice_calc->getInvoice()->service()->markSent()->save();
|
||||||
$invoice->save();
|
$this->assertEquals(10, $invoice->amount);
|
||||||
|
$this->assertEquals(10, $invoice->balance);
|
||||||
|
|
||||||
$credit = CreditFactory::create($this->company->id, $this->user->id);
|
$credit = CreditFactory::create($this->company->id, $this->user->id);
|
||||||
$credit->client_id = $client->id;
|
$credit->client_id = $client->id;
|
||||||
@ -1410,8 +1479,10 @@ class PaymentTest extends TestCase
|
|||||||
$credit_calc = new InvoiceSum($credit);
|
$credit_calc = new InvoiceSum($credit);
|
||||||
$credit_calc->build();
|
$credit_calc->build();
|
||||||
|
|
||||||
$credit = $credit_calc->getCredit();
|
$credit = $credit_calc->getCredit()->service()->markSent()->save(); //$10 credit
|
||||||
$credit->save(); //$10 credit
|
|
||||||
|
$this->assertEquals(10, $credit->amount);
|
||||||
|
$this->assertEquals(10, $credit->balance);
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'amount' => $invoice->amount,
|
'amount' => $invoice->amount,
|
||||||
|
@ -163,18 +163,11 @@ class CreditPaymentTest extends TestCase
|
|||||||
'date' => '2019/12/12',
|
'date' => '2019/12/12',
|
||||||
];
|
];
|
||||||
|
|
||||||
$response = false;
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
try {
|
'X-API-TOKEN' => $this->token,
|
||||||
$response = $this->withHeaders([
|
])->postJson('/api/v1/payments/', $data);
|
||||||
'X-API-SECRET' => config('ninja.api_secret'),
|
|
||||||
'X-API-TOKEN' => $this->token,
|
|
||||||
])->post('/api/v1/payments/', $data);
|
|
||||||
} catch (ValidationException $e) {
|
|
||||||
$message = json_decode($e->validator->getMessageBag(), 1);
|
|
||||||
nlog($e->validator->getMessageBag());
|
|
||||||
}
|
|
||||||
|
|
||||||
$response->assertStatus(200);
|
$response->assertStatus(200);
|
||||||
|
|
||||||
$arr = $response->json();
|
$arr = $response->json();
|
||||||
|
@ -63,127 +63,126 @@ class RefundTest extends TestCase
|
|||||||
public function testRefundAndAppliedAmounts()
|
public function testRefundAndAppliedAmounts()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'amount' => 500,
|
||||||
|
'client_id' => $this->client->hashed_id,
|
||||||
|
'date' => '2020/12/12',
|
||||||
|
|
||||||
$data = [
|
];
|
||||||
'amount' => 500,
|
|
||||||
'client_id' => $this->client->hashed_id,
|
|
||||||
'date' => '2020/12/12',
|
|
||||||
|
|
||||||
];
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->postJson('/api/v1/payments', $data);
|
||||||
|
|
||||||
$response = $this->withHeaders([
|
$response->assertStatus(200);
|
||||||
'X-API-SECRET' => config('ninja.api_secret'),
|
|
||||||
'X-API-TOKEN' => $this->token,
|
|
||||||
])->postJson('/api/v1/payments', $data);
|
|
||||||
|
|
||||||
$response->assertStatus(200);
|
$arr = $response->json();
|
||||||
|
|
||||||
$arr = $response->json();
|
$payment_id = $arr['data']['id'];
|
||||||
|
|
||||||
$payment_id = $arr['data']['id'];
|
$item = new InvoiceItem;
|
||||||
|
$item->cost = 300;
|
||||||
|
$item->quantity = 1;
|
||||||
|
|
||||||
$item = new InvoiceItem;
|
$i = Invoice::factory()
|
||||||
$item->cost = 300;
|
->create([
|
||||||
$item->quantity = 1;
|
'user_id' => $this->user->id,
|
||||||
|
'company_id' => $this->company->id,
|
||||||
|
'client_id' => $this->client->id,
|
||||||
|
'line_items' => [$item],
|
||||||
|
'discount' => 0,
|
||||||
|
'tax_name1' => '',
|
||||||
|
'tax_name2' => '',
|
||||||
|
'tax_name3' => '',
|
||||||
|
'tax_rate1' => 0,
|
||||||
|
'tax_rate2' => 0,
|
||||||
|
'tax_rate3' => 0,
|
||||||
|
]);
|
||||||
|
|
||||||
$i = Invoice::factory()
|
$i->calc()->getInvoice();
|
||||||
->create([
|
$i->service()->markSent()->save();
|
||||||
'user_id' => $this->user->id,
|
|
||||||
'company_id' => $this->company->id,
|
|
||||||
'client_id' => $this->client->id,
|
|
||||||
'line_items' => [$item],
|
|
||||||
'discount' => 0,
|
|
||||||
'tax_name1' => '',
|
|
||||||
'tax_name2' => '',
|
|
||||||
'tax_name3' => '',
|
|
||||||
'tax_rate1' => 0,
|
|
||||||
'tax_rate2' => 0,
|
|
||||||
'tax_rate3' => 0,
|
|
||||||
]);
|
|
||||||
|
|
||||||
$i->calc()->getInvoice();
|
$this->assertEquals(300, $i->balance);
|
||||||
$i->service()->markSent()->save();
|
|
||||||
|
|
||||||
$this->assertEquals(300, $i->balance);
|
$data = [
|
||||||
|
'client_id' => $this->client->hashed_id,
|
||||||
|
'invoices' => [
|
||||||
|
[
|
||||||
|
'invoice_id' => $i->hashed_id,
|
||||||
|
'amount' => 300
|
||||||
|
],
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
$data = [
|
$response = $this->withHeaders([
|
||||||
'client_id' => $this->client->hashed_id,
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
'invoices' => [
|
'X-API-TOKEN' => $this->token,
|
||||||
[
|
])->putJson('/api/v1/payments/'.$payment_id, $data);
|
||||||
'invoice_id' => $i->hashed_id,
|
|
||||||
'amount' => 300
|
|
||||||
],
|
|
||||||
]
|
|
||||||
];
|
|
||||||
|
|
||||||
$response = $this->withHeaders([
|
$response->assertStatus(200);
|
||||||
'X-API-SECRET' => config('ninja.api_secret'),
|
|
||||||
'X-API-TOKEN' => $this->token,
|
|
||||||
])->putJson('/api/v1/payments/'.$payment_id, $data);
|
|
||||||
|
|
||||||
$response->assertStatus(200);
|
$i = $i->fresh();
|
||||||
|
|
||||||
$i = $i->fresh();
|
$this->assertEquals(0, $i->balance);
|
||||||
|
|
||||||
$this->assertEquals(0, $i->balance);
|
$payment = Payment::find($this->decodePrimaryKey($payment_id));
|
||||||
|
|
||||||
$payment = Payment::find($this->decodePrimaryKey($payment_id));
|
$this->assertNotNull($payment);
|
||||||
|
$this->assertEquals(500, $payment->amount);
|
||||||
|
$this->assertEquals(300, $payment->applied);
|
||||||
|
$this->assertEquals(0, $payment->refunded);
|
||||||
|
|
||||||
$this->assertNotNull($payment);
|
$data = [
|
||||||
$this->assertEquals(500, $payment->amount);
|
'id' => $this->encodePrimaryKey($payment->id),
|
||||||
$this->assertEquals(300, $payment->applied);
|
'invoices' => [
|
||||||
$this->assertEquals(0, $payment->refunded);
|
[
|
||||||
|
'invoice_id' => $i->hashed_id,
|
||||||
|
'amount' => $i->amount,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'date' => '2020/12/12',
|
||||||
|
];
|
||||||
|
|
||||||
$data = [
|
$response = $this->withHeaders([
|
||||||
'id' => $this->encodePrimaryKey($payment->id),
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
'invoices' => [
|
'X-API-TOKEN' => $this->token,
|
||||||
[
|
])->postJson('/api/v1/payments/refund', $data);
|
||||||
'invoice_id' => $i->hashed_id,
|
|
||||||
'amount' => $i->amount,
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'date' => '2020/12/12',
|
|
||||||
];
|
|
||||||
|
|
||||||
$response = $this->withHeaders([
|
$response->assertStatus(200);
|
||||||
'X-API-SECRET' => config('ninja.api_secret'),
|
|
||||||
'X-API-TOKEN' => $this->token,
|
|
||||||
])->postJson('/api/v1/payments/refund', $data);
|
|
||||||
|
|
||||||
$response->assertStatus(200);
|
$payment = $payment->fresh();
|
||||||
|
$i = $i->fresh();
|
||||||
|
|
||||||
$payment = $payment->fresh();
|
$this->assertEquals(300, $payment->refunded);
|
||||||
$i = $i->fresh();
|
$this->assertEquals(300, $i->balance);
|
||||||
|
$this->assertEquals(2, $i->status_id);
|
||||||
$this->assertEquals(300, $payment->refunded);
|
|
||||||
$this->assertEquals(300, $i->balance);
|
|
||||||
$this->assertEquals(2, $i->status_id);
|
|
||||||
|
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'client_id' => $this->client->hashed_id,
|
'client_id' => $this->client->hashed_id,
|
||||||
'invoices' => [
|
'invoices' => [
|
||||||
[
|
[
|
||||||
'invoice_id' => $i->hashed_id,
|
'invoice_id' => $i->hashed_id,
|
||||||
'amount' => 200
|
'amount' => 200
|
||||||
],
|
],
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
$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,
|
||||||
])->putJson('/api/v1/payments/'.$payment_id, $data);
|
])->putJson('/api/v1/payments/'.$payment_id, $data);
|
||||||
|
|
||||||
$response->assertStatus(200);
|
$response->assertStatus(200);
|
||||||
|
|
||||||
$payment = $payment->fresh();
|
$payment = $payment->fresh();
|
||||||
$i = $i->fresh();
|
$i = $i->fresh();
|
||||||
|
|
||||||
$this->assertEquals(300, $payment->refunded);
|
$this->assertEquals(300, $payment->refunded);
|
||||||
$this->assertEquals(100, $i->balance);
|
$this->assertEquals(100, $i->balance);
|
||||||
$this->assertEquals(3, $i->status_id);
|
$this->assertEquals(3, $i->status_id);
|
||||||
$this->assertEquals(500, $payment->applied);
|
$this->assertEquals(500, $payment->applied);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user