mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-06-23 20:00:33 -04:00
Improvements for quote validation
This commit is contained in:
parent
c472e8ce68
commit
67c80ecdd9
@ -94,6 +94,10 @@ class StoreCreditRequest extends Request
|
||||
$input['design_id'] = $this->decodePrimaryKey($input['design_id']);
|
||||
}
|
||||
|
||||
if(isset($input['partial']) && $input['partial'] == 0) {
|
||||
$input['partial_due_date'] = null;
|
||||
}
|
||||
|
||||
$input = $this->decodePrimaryKeys($input);
|
||||
|
||||
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
|
||||
|
@ -86,6 +86,10 @@ class UpdateCreditRequest extends Request
|
||||
|
||||
$input = $this->decodePrimaryKeys($input);
|
||||
|
||||
if(isset($input['partial']) && $input['partial'] == 0) {
|
||||
$input['partial_due_date'] = null;
|
||||
}
|
||||
|
||||
if (isset($input['line_items'])) {
|
||||
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
|
||||
}
|
||||
|
@ -93,8 +93,8 @@ class StoreInvoiceRequest extends Request
|
||||
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
|
||||
}
|
||||
|
||||
if(isset($input['partial']) && $input['partial'] == 0 && isset($input['partial_due_date'])) {
|
||||
$input['partial_due_date'] = '';
|
||||
if(isset($input['partial']) && $input['partial'] == 0) {
|
||||
$input['partial_due_date'] = null;
|
||||
}
|
||||
|
||||
$input['amount'] = 0;
|
||||
|
@ -91,8 +91,8 @@ class UpdateInvoiceRequest extends Request
|
||||
|
||||
$input['id'] = $this->invoice->id;
|
||||
|
||||
if(isset($input['partial']) && $input['partial'] == 0 && isset($input['partial_due_date'])) {
|
||||
$input['partial_due_date'] = '';
|
||||
if(isset($input['partial']) && $input['partial'] == 0) {
|
||||
$input['partial_due_date'] = null;
|
||||
}
|
||||
|
||||
if (isset($input['line_items']) && is_array($input['line_items'])) {
|
||||
|
@ -79,6 +79,10 @@ class StorePurchaseOrderRequest extends Request
|
||||
|
||||
$input = $this->decodePrimaryKeys($input);
|
||||
|
||||
if(isset($input['partial']) && $input['partial'] == 0) {
|
||||
$input['partial_due_date'] = null;
|
||||
}
|
||||
|
||||
if (isset($input['line_items']) && is_array($input['line_items'])) {
|
||||
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
|
||||
}
|
||||
|
@ -83,6 +83,10 @@ class UpdatePurchaseOrderRequest extends Request
|
||||
|
||||
$input['id'] = $this->purchase_order->id;
|
||||
|
||||
if(isset($input['partial']) && $input['partial'] == 0) {
|
||||
$input['partial_due_date'] = null;
|
||||
}
|
||||
|
||||
if (isset($input['line_items']) && is_array($input['line_items'])) {
|
||||
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ class StoreQuoteRequest extends Request
|
||||
|
||||
$rules = [];
|
||||
|
||||
$rules['client_id'] = 'required|exists:clients,id,company_id,'.$user->company()->id;
|
||||
$rules['client_id'] = ['required', 'bail', Rule::exists('clients','id')->where('company_id', $user->company()->id)];
|
||||
|
||||
if ($this->file('documents') && is_array($this->file('documents'))) {
|
||||
$rules['documents.*'] = $this->fileValidation();
|
||||
@ -64,12 +64,17 @@ class StoreQuoteRequest extends Request
|
||||
$rules['is_amount_discount'] = ['boolean'];
|
||||
$rules['exchange_rate'] = 'bail|sometimes|numeric';
|
||||
$rules['line_items'] = 'array';
|
||||
$rules['partial_due_date'] = ['bail', 'sometimes', 'exclude_if:partial,0', Rule::requiredIf(fn () => $this->partial > 0), 'date', 'before:due_date', 'after_or_equal:date'];
|
||||
$rules['due_date'] = ['bail', 'sometimes', 'nullable', 'after:partial_due_date', Rule::requiredIf(fn () => strlen($this->partial_due_date) > 1), 'date'];
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
public function prepareForValidation()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
$input = $this->all();
|
||||
|
||||
$input = $this->decodePrimaryKeys($input);
|
||||
@ -82,6 +87,19 @@ class StoreQuoteRequest extends Request
|
||||
$input['exchange_rate'] = 1;
|
||||
}
|
||||
|
||||
if(isset($input['partial']) && $input['partial'] == 0) {
|
||||
$input['partial_due_date'] = null;
|
||||
}
|
||||
|
||||
if(!isset($input['date']))
|
||||
$input['date'] = now()->addSeconds($user->company()->utc_offset())->format('Y-m-d');
|
||||
|
||||
if(isset($input['partial_due_date']) && (!isset($input['due_date']) || strlen($input['due_date']) <=1 )) {
|
||||
$client = \App\Models\Client::withTrashed()->find($input['client_id']);
|
||||
$valid_days = ($client && strlen($client->getSetting('valid_until')) >= 1) ? $client->getSetting('valid_until') : 7;
|
||||
$input['due_date'] = \Carbon\Carbon::parse($input['date'])->addDays($valid_days)->format('Y-m-d');
|
||||
}
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
}
|
||||
|
@ -56,16 +56,16 @@ class UpdateQuoteRequest extends Request
|
||||
$rules['file'] = $this->fileValidation();
|
||||
}
|
||||
|
||||
|
||||
$rules['number'] = ['bail', 'sometimes', 'nullable', Rule::unique('quotes')->where('company_id', $user->company()->id)->ignore($this->quote->id)];
|
||||
|
||||
$rules['client_id'] = ['bail', 'sometimes', Rule::in([$this->quote->client_id])];
|
||||
|
||||
$rules['line_items'] = 'array';
|
||||
$rules['discount'] = 'sometimes|numeric';
|
||||
$rules['is_amount_discount'] = ['boolean'];
|
||||
$rules['exchange_rate'] = 'bail|sometimes|numeric';
|
||||
|
||||
$rules['partial_due_date'] = ['bail', 'sometimes', 'exclude_if:partial,0', Rule::requiredIf(fn () => $this->partial > 0), 'date', 'before:due_date'];
|
||||
$rules['due_date'] = ['bail', 'sometimes', 'nullable', 'after:partial_due_date', 'after_or_equal:date', Rule::requiredIf(fn () => strlen($this->partial_due_date) > 1), 'date'];
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
@ -89,6 +89,9 @@ class UpdateQuoteRequest extends Request
|
||||
$input['exchange_rate'] = 1;
|
||||
}
|
||||
|
||||
if(isset($input['partial']) && $input['partial'] == 0) {
|
||||
$input['partial_due_date'] = null;
|
||||
}
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ use Laracasts\Presenter\PresentableTrait;
|
||||
* @property float $amount
|
||||
* @property float $balance
|
||||
* @property float|null $partial
|
||||
* @property string|null $partial_due_date
|
||||
* @property \Carbon\Carbon|null $partial_due_date
|
||||
* @property string|null $last_viewed
|
||||
* @property int|null $created_at
|
||||
* @property int|null $updated_at
|
||||
@ -195,10 +195,10 @@ class Quote extends BaseModel
|
||||
return $this->dateMutator($value);
|
||||
}
|
||||
|
||||
public function getDueDateAttribute($value)
|
||||
{
|
||||
return $value ? $this->dateMutator($value) : null;
|
||||
}
|
||||
// public function getDueDateAttribute($value)
|
||||
// {
|
||||
// return $value ? $this->dateMutator($value) : null;
|
||||
// }
|
||||
|
||||
// public function getPartialDueDateAttribute($value)
|
||||
// {
|
||||
|
@ -111,7 +111,7 @@ class QuoteTransformer extends EntityTransformer
|
||||
'reminder2_sent' => $quote->reminder2_sent ?: '',
|
||||
'reminder3_sent' => $quote->reminder3_sent ?: '',
|
||||
'reminder_last_sent' => $quote->reminder_last_sent ?: '',
|
||||
'due_date' => $quote->due_date ?: '',
|
||||
'due_date' => $quote->due_date ? $quote->due_date->format('Y-m-d') : '',
|
||||
'terms' => $quote->terms ?: '',
|
||||
'public_notes' => $quote->public_notes ?: '',
|
||||
'private_notes' => $quote->private_notes ?: '',
|
||||
@ -127,7 +127,7 @@ class QuoteTransformer extends EntityTransformer
|
||||
'is_amount_discount' => (bool) ($quote->is_amount_discount ?: false),
|
||||
'footer' => $quote->footer ?: '',
|
||||
'partial' => (float) ($quote->partial ?: 0.0),
|
||||
'partial_due_date' => $quote->partial_due_date ?: '',
|
||||
'partial_due_date' => $quote->partial_due_date ? $quote->partial_due_date->format('Y-m-d') : '',
|
||||
'custom_value1' => (string) $quote->custom_value1 ?: '',
|
||||
'custom_value2' => (string) $quote->custom_value2 ?: '',
|
||||
'custom_value3' => (string) $quote->custom_value3 ?: '',
|
||||
|
@ -54,12 +54,99 @@ class QuoteTest extends TestCase
|
||||
);
|
||||
}
|
||||
|
||||
public function testQuoteDueDateInjectionValidationLayer()
|
||||
{
|
||||
|
||||
$data = [
|
||||
'client_id' => $this->client->hashed_id,
|
||||
'partial_due_date' => now()->format('Y-m-d'),
|
||||
'partial' => 1,
|
||||
'amount' => 20,
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->postJson('/api/v1/quotes', $data);
|
||||
|
||||
$arr = $response->json();
|
||||
// nlog($arr);
|
||||
|
||||
$this->assertNotEmpty($arr['data']['due_date']);
|
||||
|
||||
}
|
||||
|
||||
public function testNullDueDates()
|
||||
{
|
||||
|
||||
$data = [
|
||||
'client_id' => $this->client->hashed_id,
|
||||
'due_date' => '',
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->postJson('/api/v1/quotes', $data);
|
||||
|
||||
$response->assertStatus(200);
|
||||
|
||||
$arr = $response->json();
|
||||
|
||||
$this->assertEmpty($arr['data']['due_date']);
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->putJson('/api/v1/quotes/'.$arr['data']['id'], $arr['data']);
|
||||
|
||||
$response->assertStatus(200);
|
||||
|
||||
$arr = $response->json();
|
||||
|
||||
$this->assertEmpty($arr['data']['due_date']);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function testNonNullDueDates()
|
||||
{
|
||||
|
||||
$data = [
|
||||
'client_id' => $this->client->hashed_id,
|
||||
'due_date' => now()->addDays(10),
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->postJson('/api/v1/quotes', $data);
|
||||
|
||||
$response->assertStatus(200);
|
||||
|
||||
$arr = $response->json();
|
||||
|
||||
$this->assertNotEmpty($arr['data']['due_date']);
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->putJson('/api/v1/quotes/'.$arr['data']['id'], $arr['data']);
|
||||
|
||||
$response->assertStatus(200);
|
||||
|
||||
$arr = $response->json();
|
||||
|
||||
$this->assertNotEmpty($arr['data']['due_date']);
|
||||
|
||||
}
|
||||
|
||||
public function testPartialDueDates()
|
||||
{
|
||||
|
||||
$data = [
|
||||
'client_id' => $this->client->hashed_id,
|
||||
'due_date' => now()->format('Y-m-d'),
|
||||
'due_date' => now()->addDay()->format('Y-m-d'),
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
@ -73,6 +160,41 @@ class QuoteTest extends TestCase
|
||||
|
||||
$this->assertNotNull($arr['data']['due_date']);
|
||||
$this->assertEmpty($arr['data']['partial_due_date']);
|
||||
|
||||
$data = [
|
||||
'client_id' => $this->client->hashed_id,
|
||||
'due_date' => now()->addDay()->format('Y-m-d'),
|
||||
'partial' => 1,
|
||||
'partial_due_date' => now()->format('Y-m-d'),
|
||||
'amount' => 20,
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->postJson('/api/v1/quotes', $data);
|
||||
|
||||
$response->assertStatus(200);
|
||||
|
||||
$arr = $response->json();
|
||||
|
||||
$this->assertEquals(now()->addDay()->format('Y-m-d'), $arr['data']['due_date']);
|
||||
$this->assertEquals(now()->format('Y-m-d'), $arr['data']['partial_due_date']);
|
||||
$this->assertEquals(1, $arr['data']['partial']);
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->putJson('/api/v1/quotes/'.$arr['data']['id'], $arr['data']);
|
||||
|
||||
$response->assertStatus(200);
|
||||
|
||||
$arr = $response->json();
|
||||
|
||||
$this->assertEquals(now()->addDay()->format('Y-m-d'), $arr['data']['due_date']);
|
||||
$this->assertEquals(now()->format('Y-m-d'), $arr['data']['partial_due_date']);
|
||||
$this->assertEquals(1, $arr['data']['partial']);
|
||||
|
||||
}
|
||||
|
||||
public function testQuoteToProjectConversion2()
|
||||
|
Loading…
x
Reference in New Issue
Block a user