Improvements for quote validation

This commit is contained in:
David Bomba 2024-04-15 09:14:11 +10:00
parent c472e8ce68
commit 67c80ecdd9
11 changed files with 175 additions and 16 deletions

View File

@ -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']) : [];

View File

@ -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']) : [];
}

View File

@ -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;

View File

@ -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'])) {

View File

@ -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']) : [];
}

View File

@ -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']) : [];
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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)
// {

View File

@ -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 ?: '',

View File

@ -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()